new file mode 100644
@@ -0,0 +1,69 @@
+/* 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)
+{
+ const char source[BUF_SIZE] = TEST_STRING;
+ char dest[BUF_SIZE];
+ char *p = dest;
+ const char *q;
+ const long zero = 0;
+ long size = BUF_SIZE;
+
+ /* 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. */
+
+ /* 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;
+ /* 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;
+ /* 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,94 @@
+# 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) atomic 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
+}
+
+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
+}
+
+proc arrive_at_instruction { instruction } {
+ set count 0
+
+ while { [is_at_instruction $instruction] != 1 } {
+ gdb_test -nopass "nexti" ".*__asm__ volatile.*" \
+ "nexti #$count to reach $instruction"
+ incr count
+
+ if { $count > 50 } {
+ fail "didn't reach $instruction"
+ return 0
+ }
+ }
+
+ return 1
+}
+
+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] } {
+ # The nexti output isn't useful to detect whether we skipped the sequence.
+ gdb_test "nexti" "\[^\r\n\]+" "step through the memset sequence"
+ gdb_assert { [is_at_instruction prfm] == 1 } \
+ "stepped through the memset sequence"
+}
+
+gdb_continue_to_breakpoint "memcpy breakpoint"
+
+if { [arrive_at_instruction cpyfp] } {
+ # The nexti output isn't useful to detect whether we skipped the sequence.
+ gdb_test "nexti" "\[^\r\n\]+" "step through the memcpy sequence"
+ gdb_assert { [is_at_instruction prfm] == 1 } \
+ "stepped through the memcpy sequence"
+}
+
+gdb_continue_to_breakpoint "memmove breakpoint"
+
+if { [arrive_at_instruction cpyp] } {
+ # The nexti output isn't useful to detect whether we skipped the sequence.
+ gdb_test "nexti" "\[^\r\n\]+" "step through the memmove sequence"
+ gdb_assert { [is_at_instruction prfm] == 1 } \
+ "stepped through the memmove sequence"
+}