@@ -3808,10 +3808,12 @@ aarch64_displaced_step_copy_insn (struct gdbarch *gdbarch,
if (aarch64_decode_insn (insn, &inst, 1, NULL) != 0)
return NULL;
- /* Look for a Load Exclusive instruction which begins the sequence. */
- if (inst.opcode->iclass == ldstexcl && bit (insn, 22))
+ /* Look for a Load Exclusive instruction which begins the sequence,
+ or for a MOPS instruction. */
+ if ((inst.opcode->iclass == ldstexcl && bit (insn, 22))
+ || AARCH64_CPU_HAS_FEATURE (*inst.opcode->avariant, MOPS))
{
- /* We can't displaced step atomic sequences. */
+ /* We can't displaced step atomic sequences nor MOPS instructions. */
return NULL;
}
new file mode 100644
@@ -0,0 +1,73 @@
+/* This file is part of GDB, the GNU debugger.
+
+ Copyright 2024 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#define TEST_STRING "Just a test string."
+#define BUF_SIZE sizeof(TEST_STRING)
+
+int
+main (void)
+{
+ char source[BUF_SIZE] = TEST_STRING;
+ char dest[BUF_SIZE];
+ char *p, *q;
+ long size, zero;
+
+ /* Note: The prfm instruction in the asm statements below is there just
+ to allow the testcase to recognize when the PC is at the instruction
+ right after the MOPS sequence. */
+
+ p = dest;
+ size = sizeof (dest);
+ zero = 0;
+ /* Break memset. */
+ /* memset implemented in MOPS instructions. */
+ __asm__ volatile ("setp [%0]!, %1!, %2\n\t"
+ "setm [%0]!, %1!, %2\n\t"
+ "sete [%0]!, %1!, %2\n\t"
+ "prfm pldl3keep, [%0, #0]\n\t"
+ : "+&r"(p), "+&r"(size)
+ : "r"(zero)
+ : "memory");
+
+ p = dest;
+ q = source;
+ size = sizeof (dest);
+ /* Break memcpy. */
+ /* memcpy implemented in MOPS instructions. */
+ __asm__ volatile ("cpyfp [%0]!, [%1]!, %2!\n\t"
+ "cpyfm [%0]!, [%1]!, %2!\n\t"
+ "cpyfe [%0]!, [%1]!, %2!\n\t"
+ "prfm pldl3keep, [%0, #0]\n\t"
+ : "+&r" (p), "+&r" (q), "+&r" (size)
+ :
+ : "memory");
+
+ p = dest;
+ q = source;
+ size = sizeof (dest);
+ /* Break memmove. */
+ /* memmove implemented in MOPS instructions. */
+ __asm__ volatile ("cpyp [%0]!, [%1]!, %2!\n\t"
+ "cpym [%0]!, [%1]!, %2!\n\t"
+ "cpye [%0]!, [%1]!, %2!\n\t"
+ "prfm pldl3keep, [%0, #0]\n\t"
+ : "+&r" (p), "+&r" (q), "+&r" (size)
+ :
+ : "memory");
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,98 @@
+# Copyright 2024 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# This file is part of the GDB testsuite.
+
+# Test single stepping through MOPS (memory operations) instruction sequences.
+
+require allow_aarch64_mops_tests
+
+standard_testfile
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+ [list debug additional_flags=-march=armv9.3-a]] } {
+ return -1
+}
+
+# If the inferior is rescheduled to another CPU while a main or epilogue
+# instruction is executed, the OS resets the inferior back to the prologue
+# instruction, so we need to allow for that possibility.
+proc step_through_sequence { prefix } {
+ set count 0
+
+ while { [is_at_instruction ${prefix}p] == 1 && $count < 50 } {
+ incr count
+
+ # The stepi output isn't useful to detect whether we stepped over
+ # the instruction.
+ gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}p"
+ if { [is_at_instruction ${prefix}m] == 1 } {
+ pass "stepped over ${prefix}p"
+ } else {
+ fail "stepped over ${prefix}e"
+ return 0
+ }
+
+ gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}m"
+ if { [is_at_instruction ${prefix}e] == 1 } {
+ pass "stepped over ${prefix}m"
+ } elseif { [is_at_instruction ${prefix}p] == 1 } {
+ # The inferior was rescheduled to another CPU.
+ pass "${prefix}m: reset back to prologue"
+ continue
+ } else {
+ fail "stepped over ${prefix}m"
+ return 0
+ }
+
+ gdb_test "stepi" "\[^\r\n\]+" "step over ${prefix}e"
+ if { [is_at_instruction prfm] == 1 } {
+ pass "stepped over ${prefix}e"
+ return 1
+ } elseif { [is_at_instruction ${prefix}p] == 1 } {
+ # The inferior was rescheduled to another CPU.
+ pass "${prefix}e: reset back to prologue"
+ continue
+ }
+ }
+
+ fail "step through $prefix sequence"
+ return 0
+}
+
+if ![runto_main] {
+ return -1
+}
+
+gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memset"]
+gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memcpy"]
+gdb_breakpoint ${srcfile}:[gdb_get_line_number "Break memmove"]
+
+gdb_continue_to_breakpoint "memset breakpoint"
+
+if { [arrive_at_instruction setp] } {
+ step_through_sequence set
+}
+
+gdb_continue_to_breakpoint "memcpy breakpoint"
+
+if { [arrive_at_instruction cpyfp] } {
+ step_through_sequence cpyf
+}
+
+gdb_continue_to_breakpoint "memmove breakpoint"
+
+if { [arrive_at_instruction cpyp] } {
+ step_through_sequence cpy
+}
@@ -848,6 +848,44 @@ proc gdb_continue_to_breakpoint {name {location_pattern .*}} {
}]
}
+# Check whether GDB is stopped at the given instruction.
+# INSTRUCTION should be just its mnemonic, without any arguments.
+
+proc is_at_instruction { instruction } {
+ global gdb_prompt hex
+
+ set test "pc points to $instruction"
+ gdb_test_multiple {x/i $pc} $test {
+ -re -wrap "=> $hex \[^\r\n\]+:\t$instruction\t\[^\r\n\]+" {
+ return 1
+ }
+ -re "\r\n$gdb_prompt $" {
+ return 0
+ }
+ }
+
+ return 0
+}
+
+# Single-steps GDB until it arrives at the given instruction.
+# INSTRUCTION should be just its mnemonic, without any arguments.
+
+proc arrive_at_instruction { instruction } {
+ set count 0
+
+ while { [is_at_instruction $instruction] != 1 } {
+ gdb_test -nopass "stepi" "\[^\r\n\]+" \
+ "stepi #$count to reach $instruction"
+ incr count
+
+ if { $count > 50 } {
+ fail "didn't reach $instruction"
+ return 0
+ }
+ }
+
+ return 1
+}
# gdb_internal_error_resync:
#
@@ -4497,6 +4535,67 @@ proc aarch64_supports_sme_svl { length } {
return 1
}
+# Run a test on the target to see if it supports AArch64 MOPS (Memory
+# Operations) extensions. Return 1 if so, 0 if it does not. Note this
+# causes a restart of GDB.
+
+gdb_caching_proc allow_aarch64_mops_tests {} {
+ global srcdir subdir gdb_prompt inferior_exited_re
+
+ set me "allow_aarch64_mops_tests"
+
+ if { ![is_aarch64_target]} {
+ return 0
+ }
+
+ # ARMv9.3-A contains the MOPS extension. The test program doesn't use it,
+ # but take the opportunity to check whether the toolchain knows about MOPS.
+ set compile_flags "{additional_flags=-march=armv9.3-a}"
+
+ # Compile a program that tests the MOPS feature.
+ set src {
+ #include <stdbool.h>
+ #include <sys/auxv.h>
+
+ #ifndef HWCAP2_MOPS
+ #define HWCAP2_MOPS (1UL << 43)
+ #endif
+
+ int main() {
+ bool mops_supported = getauxval (AT_HWCAP2) & HWCAP2_MOPS;
+
+ return !mops_supported;
+ }
+ }
+
+ if {![gdb_simple_compile $me $src executable $compile_flags]} {
+ return 0
+ }
+
+ # Compilation succeeded so now run it via gdb.
+ clean_restart $obj
+ gdb_run_cmd
+ gdb_expect {
+ -re ".*$inferior_exited_re with code 01.*${gdb_prompt} $" {
+ verbose -log "\n$me mops support not detected"
+ set allow_mops_tests 0
+ }
+ -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
+ verbose -log "\n$me: mops support detected"
+ set allow_mops_tests 1
+ }
+ default {
+ warning "\n$me: default case taken"
+ set allow_mops_tests 0
+ }
+ }
+ gdb_exit
+ remote_file build delete $obj
+
+ verbose "$me: returning $allow_mops_tests" 2
+ return $allow_mops_tests
+}
+
# A helper that compiles a test case to see if __int128 is supported.
proc gdb_int128_helper {lang} {
return [gdb_can_simple_compile "i128-for-$lang" {