* [PATCH 0/4] gdb/aarch64: record/replay support for AArch64
@ 2026-04-20 21:52 Ezra.Sitorus
2026-04-20 21:52 ` [PATCH 1/4] gdb/aarch64: record/replay support for LRCPC3 Ezra.Sitorus
` (5 more replies)
0 siblings, 6 replies; 23+ messages in thread
From: Ezra.Sitorus @ 2026-04-20 21:52 UTC (permalink / raw)
To: gdb-patches; +Cc: luis.machado.foss, thiago.bauermann, Ezra Sitorus
From: Ezra Sitorus <ezra.sitorus@arm.com>
This patch series enables record/replay for the following AArch64
features:
* FEAT_LRCPC3 (+rcpc3)
* FEAT_CSSC (+cssc)
* FEAT_RPRFM (+rprfm)
* FEAT_LSE128 (+lse128)
These were tested either with QEMU or Shrinkwrap. In both cases, I seem
to have test failures that are not related to my patches (gdb.threads,
gdb.mi tests for example). In these cases, is it fair to ignore these?
Or is there some underlying issue I've missed?
In addition, whenever I used QEMU, I disabled gcs because it seems to
cause test failures - address/memory accesses seem to always cause a
fault. Am I doing something wrong?
Ezra
Ezra Sitorus (4):
gdb/aarch64: record/replay support for LRCPC3
gdb/aarch64: Test record/replay support for CSSC
gdb/aarch64: record/replay support for RPRFM and PRFM (reg)
gdb/aarch64: record/replay support for LSE128
gdb/aarch64-tdep.c | 105 ++++++
gdb/testsuite/gdb.reverse/aarch64-cssc.c | 113 +++++++
gdb/testsuite/gdb.reverse/aarch64-cssc.exp | 141 ++++++++
gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c | 319 +++++++++++++++++++
gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp | 203 ++++++++++++
gdb/testsuite/gdb.reverse/aarch64-lse128.c | 139 ++++++++
gdb/testsuite/gdb.reverse/aarch64-lse128.exp | 138 ++++++++
gdb/testsuite/lib/gdb.exp | 171 ++++++++++
8 files changed, 1329 insertions(+)
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.c
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.exp
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.c
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.exp
--
2.43.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 1/4] gdb/aarch64: record/replay support for LRCPC3
2026-04-20 21:52 [PATCH 0/4] gdb/aarch64: record/replay support for AArch64 Ezra.Sitorus
@ 2026-04-20 21:52 ` Ezra.Sitorus
2026-04-24 7:00 ` Luis
` (3 more replies)
2026-04-20 21:52 ` [PATCH 2/4] gdb/aarch64: Test record/replay support for CSSC Ezra.Sitorus
` (4 subsequent siblings)
5 siblings, 4 replies; 23+ messages in thread
From: Ezra.Sitorus @ 2026-04-20 21:52 UTC (permalink / raw)
To: gdb-patches; +Cc: luis.machado.foss, thiago.bauermann, Ezra Sitorus
From: Ezra Sitorus <ezra.sitorus@arm.com>
FEAT_LRCPC3 introduces various load/store instructions with release
consistency for cases where ordering is required. This patch teaches GDB
to decode these instructions for recording and reversing.
The gdb.reverse/aarch64-lrcpc3.exp testcase verifies that the
instructions are recorded and correctly reversed. In particular, there
are some interesting cases to note:
* ldapur/stlur are SIMD instructions, but are not decoded in the simd
function.
* There are writeback cases to cover too. These were taken from the
binutils testcases: gas/testsuite/gas/aarch64/rcpc3.s.
The full testsuite was done on aarch64-none-linux-gnu without LRCPC3.
The gdb.arch and gdb.reverse tests were run on Shrinkwrap with LRCPC3
support.
Please note:
1) There is no support for LRCPC and LRCPC2 instructions
2) LRCPC3 is gated with +rcpc3 in GCC/binutils.
---
gdb/aarch64-tdep.c | 87 +++++
gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c | 319 +++++++++++++++++++
gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp | 203 ++++++++++++
gdb/testsuite/lib/gdb.exp | 63 ++++
4 files changed, 672 insertions(+)
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 4befaa2720d..81d4f160f8f 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -5812,6 +5812,93 @@ aarch64_record_load_store (aarch64_insn_decode_record *aarch64_insn_r)
aarch64_insn_r->reg_rec_count = 1;
}
}
+ /* LRCPC3 instructions. This covers ldiapp/stilp, ldapur/stlur (FP/SIMD),
+ ldapr/stlr. */
+ else if ((insn_bits24_27 & 0x0b) == 0x09 && insn_bits28_29 == 0x01
+ && insn_bits10_11 == 0x02 && !insn_bit21)
+ {
+ /* ldapur/stlur (FP/SIMD), ldapr/stlr. We can differentiate between the
+ 2 types by checking the vector flag. */
+ if (insn_bit23 || vector_flag)
+ {
+ /* For the vector instruction, the offset comes from the imm9
+ bitfield, whereas the other can only take possible values from the
+ size bitfield. */
+ int16_t imm9_off = sbits (aarch64_insn_r->aarch64_insn, 12, 20);
+ offset = vector_flag ? imm9_off : -(1 << size_bits);
+ uint32_t regnum_offset = vector_flag ? AARCH64_V0_REGNUM : 0;
+ if (ld_flag)
+ {
+ record_buf[0] = reg_rt + regnum_offset;
+ aarch64_insn_r->reg_rec_count = 1;
+ if (!vector_flag)
+ {
+ /* The Rn register always has writeback in LRCPC3. This is
+ not the case in LRCPC. */
+ record_buf[1] = reg_rn;
+ aarch64_insn_r->reg_rec_count = 2;
+ }
+ }
+ else
+ {
+ regcache_raw_read_unsigned (aarch64_insn_r->regcache, reg_rn,
+ &address);
+ /* (vector_flag && insn_bit23) is the STLUR instruction with Q
+ register. */
+ datasize = (vector_flag && insn_bit23) ? 128 : (8 << size_bits);
+ /* LRCPC3 adds STLR with a pre-indexed offset. There is another
+ STLR variant without offset but this has a different encoding. */
+ if (!vector_flag)
+ {
+ record_buf[0] = reg_rn;
+ aarch64_insn_r->reg_rec_count = 1;
+ }
+ record_buf_mem[0] = datasize >> 3;
+ record_buf_mem[1] = address + offset;
+ aarch64_insn_r->mem_rec_count = 1;
+ }
+ }
+ else
+ {
+ /* ldiapp/stilp. */
+ uint8_t opc2 = bits (aarch64_insn_r->aarch64_insn, 12, 15);
+ reg_rt2 = bits (aarch64_insn_r->aarch64_insn, 16, 20);
+ if (ld_flag)
+ {
+ record_buf[0] = reg_rt;
+ record_buf[1] = reg_rt2;
+ aarch64_insn_r->reg_rec_count = 2;
+
+ /* If the registers don't match and there's no offset then
+ there's WB. */
+ if (reg_rn != reg_rt && reg_rn != reg_rt2 && opc2 == 0)
+ {
+ record_buf[2] = reg_rn;
+ aarch64_insn_r->reg_rec_count = 3;
+ }
+ }
+ else
+ {
+ datasize = 8 << size_bits;
+ regcache_raw_read_unsigned (aarch64_insn_r->regcache, reg_rn,
+ &address);
+ offset = (opc2 == 0) ? (2 << size_bits) : 0;
+ address -= offset;
+
+ record_buf_mem[0] = datasize >> 3;
+ record_buf_mem[1] = address;
+ record_buf_mem[2] = datasize >> 3;
+ record_buf_mem[3] = address + (datasize >> 3);
+ aarch64_insn_r->mem_rec_count = 2;
+
+ if (offset != 0)
+ {
+ record_buf[0] = reg_rn;
+ aarch64_insn_r->reg_rec_count = 1;
+ }
+ }
+ }
+ }
/* Load/store register (register offset) instructions. */
else if ((insn_bits24_27 & 0x0b) == 0x08 && insn_bits28_29 == 0x03
&& insn_bits10_11 == 0x02 && insn_bit21)
diff --git a/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
new file mode 100644
index 00000000000..9265489ff70
--- /dev/null
+++ b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
@@ -0,0 +1,319 @@
+/* This test program is part of GDB, the GNU debugger.
+
+ Copyright 2024-2026 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/>. */
+
+#include <stdint.h>
+#include <stdalign.h>
+#include <string.h>
+
+#define INITIAL_STRING "This is just some string."
+#define BUF_SIZE sizeof (INITIAL_STRING)
+
+#define PREPARE_FOR_LOAD(OFFSET) \
+ __asm__ volatile ("mov %0, #0\n" : "=r"(xa)::); \
+ __asm__ volatile ("mov %0, #0\n" : "=r"(xb)::); \
+ strcpy (src, INITIAL_STRING); \
+ ptr = (uint64_t *)((uint8_t *)src + OFFSET); \
+
+#define PREPARE_FOR_VECTOR_LOAD(OFFSET) \
+ __asm__ volatile ("movi %0.2d, #0\n" : "=w"(va)::); \
+ strcpy (src, INITIAL_STRING); \
+ ptr = (uint64_t *)((uint8_t *)src + OFFSET); \
+
+#define PREPARE_FOR_STORE(OFFSET) \
+ __asm__ volatile ("mov %0, #0\n" : "=r"(xa)::); \
+ __asm__ volatile ("mov %0, #0\n" : "=r"(xb)::); \
+ strcpy (src, INITIAL_STRING); \
+ ptr = (uint64_t *)((uint8_t *)src + OFFSET); \
+
+#define PREPARE_FOR_VECTOR_STORE(OFFSET) \
+ __asm__ volatile ("movi %0.2d, #0\n" : "=w"(va)::); \
+ strcpy (src, INITIAL_STRING); \
+ ptr = (uint64_t *)((uint8_t *)src + OFFSET); \
+
+typedef uint64_t uint64x2_t __attribute__((vector_size(16)));
+
+int
+main (void)
+{
+ alignas(16) char src[BUF_SIZE];
+
+ register uint64_t xa asm ("x19");
+ register uint32_t xb asm ("x20");
+ register uint64_t *ptr asm ("x21");
+ register uint64x2_t va asm ("v22");
+
+ PREPARE_FOR_LOAD (0);
+ /* Before ldiapp-0. */
+ __asm__ volatile ("ldiapp %0, %1, [%2]\n"
+ : "=r"(xa), "=r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After ldiapp-0. */
+
+ PREPARE_FOR_LOAD (0);
+ /* Before ldiapp-1. */
+ __asm__ volatile ("ldiapp %w0, %w1, [%2]\n"
+ : "=r"(xa), "=r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After ldiapp-1. */
+
+ PREPARE_FOR_LOAD (0);
+ /* Before ldiapp-2. */
+ __asm__ volatile ("ldiapp %0, %1, [%2], #16\n"
+ : "=r"(xa), "=r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After ldiapp-2. */
+
+ PREPARE_FOR_LOAD (0);
+ /* Before ldiapp-3. */
+ __asm__ volatile ("ldiapp %w0, %w1, [%2], #8\n"
+ : "=r"(xa), "=r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After ldiapp-3. */
+ /* Register overlap between source and destination registers. Since there is
+ no offset, writeback is disabled. */
+
+ PREPARE_FOR_LOAD (0);
+ /* Before ldiapp-4. */
+ __asm__ volatile ("ldiapp %0, %1, [%0]\n"
+ : "+r"(ptr), "=r"(xb)
+ :
+ : "memory");
+ /* After ldiapp-4. */
+
+ PREPARE_FOR_LOAD (0);
+ /* Before ldiapp-5. */
+ __asm__ volatile ("ldiapp %w0, %w1, [%0]\n"
+ : "+r"(ptr), "=r"(xb)
+ :
+ : "memory");
+ /* After ldiapp-5. */
+
+ PREPARE_FOR_STORE (0);
+ /* Before stilp-0. */
+ __asm__ volatile ("stilp %0, %1, [%2]\n"
+ :
+ : "r"(xa), "r"(xb), "r"(ptr)
+ : "memory");
+ /* After stilp-0. */
+
+ PREPARE_FOR_STORE (0);
+ /* Before stilp-1. */
+ __asm__ volatile ("stilp %w0, %w1, [%2]\n"
+ :
+ : "r"(xa), "r"(xb), "r"(ptr)
+ : "memory");
+ /* After stilp-1. */
+
+ PREPARE_FOR_STORE (16);
+ /* Before stilp-2. */
+ __asm__ volatile ("stilp %1, %2, [%0, #-16]!\n"
+ : "+r"(ptr)
+ : "r"(xa), "r"(xb)
+ : "memory");
+ /* After stilp-2. */
+
+ PREPARE_FOR_STORE (8);
+ /* Before stilp-3. */
+ __asm__ volatile ("stilp %w1, %w2, [%0, #-8]!\n"
+ : "+r"(ptr)
+ : "r"(xa), "r"(xb)
+ : "memory");
+ /* After stilp-3. */
+ /* Register overlap. Since there is no offset, writeback is disabled. */
+
+ PREPARE_FOR_STORE (0);
+ /* Before stilp-4. */
+ __asm__ volatile ("stilp %0, %1, [%0]\n"
+ :
+ : "r"(ptr), "r"(xb)
+ : "memory");
+ /* After stilp-4. */
+
+ PREPARE_FOR_STORE (0);
+ /* Before stilp-5. */
+ __asm__ volatile ("stilp %w0, %w1, [%0]\n"
+ :
+ : "r"(ptr), "r"(xb)
+ : "memory");
+ /* After stilp-5. */
+
+ PREPARE_FOR_LOAD (0);
+ /* Before ldapr-0. */
+ __asm__ volatile ("ldapr %0, [%1], #8\n"
+ : "+r"(xa), "+r"(ptr)
+ :
+ : "memory");
+ /* After ldapr-0. */
+
+ PREPARE_FOR_LOAD (0);
+ /* Before ldapr-1. */
+ __asm__ volatile ("ldapr %w0, [%1], #4\n"
+ : "+r"(xa), "+r"(ptr)
+ :
+ : "memory");
+ /* After ldapr-1. */
+
+ PREPARE_FOR_STORE (8);
+ /* Before stlr-0. */
+ __asm__ volatile ("stlr %1, [%0, #-8]!\n"
+ : "+r"(ptr)
+ : "r"(xa)
+ : "memory");
+ /* After stlr-0. */
+
+ PREPARE_FOR_STORE (4);
+ /* Before stlr-1. */
+ __asm__ volatile ("stlr %w1, [%0, #-4]!\n"
+ : "+r"(ptr)
+ : "r"(xa)
+ : "memory");
+ /* After stlr-1. */
+
+ PREPARE_FOR_VECTOR_LOAD (0);
+ /* Before ldap1-0. */
+ __asm__ volatile ("ldap1 {%0.d}[0], [%1]\n"
+ : "=w"(va)
+ : "r"(ptr)
+ : "memory");
+ /* After ldap1-0. */
+
+ PREPARE_FOR_VECTOR_STORE (0);
+ /* Before stl1-0. */
+ __asm__ volatile ("stl1 {%0.d}[0], [%1]\n"
+ :
+ : "w"(va), "r"(ptr)
+ : "memory");
+ /* After stl1-0. */
+
+ PREPARE_FOR_VECTOR_LOAD (0);
+ /* Before ldapur-0. */
+ __asm__ volatile ("ldapur %d0, [%1]\n"
+ : "=w"(va)
+ : "r"(ptr)
+ : "memory");
+ /* After ldapur-0. */
+
+ PREPARE_FOR_VECTOR_STORE (0);
+ /* Before stlur-0. */
+ __asm__ volatile ("stlur %d0, [%1]\n"
+ :
+ : "w"(va), "r"(ptr)
+ : "memory");
+ /* After stlur-0. */
+
+ PREPARE_FOR_VECTOR_LOAD (256);
+ /* Before ldapur-1. */
+ __asm__ volatile ("ldapur %d0, [%1, #-256]\n"
+ : "=w"(va)
+ : "r"(ptr)
+ : "memory");
+ /* After ldapur-1. */
+
+ PREPARE_FOR_VECTOR_STORE (256);
+ /* Before stlur-1. */
+ __asm__ volatile ("stlur %d0, [%1, #-256]\n"
+ :
+ : "w"(va), "r"(ptr)
+ : "memory");
+ /* After stlur-1. */
+
+ PREPARE_FOR_VECTOR_LOAD (-255);
+ /* Before ldapur-2. */
+ __asm__ volatile ("ldapur %d0, [%1, #255]\n"
+ : "=w"(va)
+ : "r"(ptr)
+ : "memory");
+ /* After ldapur-2. */
+
+ PREPARE_FOR_VECTOR_STORE (-255);
+ /* Before stlur-2. */
+ __asm__ volatile ("stlur %d0, [%1, #255]\n"
+ :
+ : "w"(va), "r"(ptr)
+ : "memory");
+ /* After stlur-2. */
+
+ PREPARE_FOR_VECTOR_LOAD (0);
+ /* Before ldapur-3. */
+ __asm__ volatile ("ldapur %h0, [%1]\n"
+ : "=w"(va)
+ : "r"(ptr)
+ : "memory");
+ /* After ldapur-3. */
+
+ PREPARE_FOR_VECTOR_STORE (0);
+ /* Before stlur-3. */
+ __asm__ volatile ("stlur %h0, [%1]\n"
+ :
+ : "w"(va), "r"(ptr)
+ : "memory");
+ /* After stlur-3. */
+
+ PREPARE_FOR_VECTOR_LOAD (0);
+ /* Before ldapur-4. */
+ __asm__ volatile ("ldapur %s0, [%1]\n"
+ : "=w"(va)
+ : "r"(ptr)
+ : "memory");
+ /* After ldapur-4. */
+
+ PREPARE_FOR_VECTOR_STORE (0);
+ /* Before stlur-4. */
+ __asm__ volatile ("stlur %s0, [%1]\n"
+ :
+ : "w"(va), "r"(ptr)
+ : "memory");
+ /* After stlur-4. */
+
+ PREPARE_FOR_VECTOR_LOAD (0);
+ /* Before ldapur-5. */
+ __asm__ volatile ("ldapur %d0, [%1]\n"
+ : "=w"(va)
+ : "r"(ptr)
+ : "memory");
+ /* After ldapur-5. */
+
+ PREPARE_FOR_VECTOR_STORE (0);
+ /* Before stlur-5. */
+ __asm__ volatile ("stlur %d0, [%1]\n"
+ :
+ : "w"(va), "r"(ptr)
+ : "memory");
+ /* After stlur-5. */
+
+ PREPARE_FOR_VECTOR_LOAD (0);
+ /* Before ldapur-6. */
+ __asm__ volatile ("ldapur %q0, [%1]\n"
+ : "=w"(va)
+ : "r"(ptr)
+ : "memory");
+ /* After ldapur-6. */
+
+ PREPARE_FOR_VECTOR_STORE (0);
+ /* Before stlur-6. */
+ __asm__ volatile ("stlur %q0, [%1]\n"
+ :
+ : "w"(va), "r"(ptr)
+ : "memory");
+ /* After stlur-6. */
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
new file mode 100644
index 00000000000..7d7a8c45445
--- /dev/null
+++ b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
@@ -0,0 +1,203 @@
+# Copyright 2024-2026 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/>.
+
+# Test instruction record for AArch64 FEAT_LRCPC3 instructions.
+# Based on gdb.reverse/aarch64-mops.exp
+#
+# The basic flow of the record tests are:
+# 1) Stop before executing the instructions of interest. Record
+# the initial value of the registers that the instruction will
+# change, i.e. the destination register.
+# 2) Execute the instructions. Record the new value of the
+# registers that changed.
+# 3) Reverse the direction of the execution and execute back to
+# just before the instructions of interest. Record the final
+# value of the registers of interest.
+# 4) Check that the initial and new values of the registers are
+# different, i.e. the instruction changed the registers as expected.
+# 5) Check that the initial and final values of the registers are
+# the same, i.e. GDB record restored the registers to their
+# original values.
+
+require allow_aarch64_lrcpc3_tests
+
+standard_testfile
+
+if {
+ [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+ [list debug additional_flags=-march=armv8-a+rcpc3]]
+} {
+ return -1
+}
+
+if {![runto_main]} {
+ return -1
+}
+
+gdb_test_no_output "record full"
+
+proc test_single_asm {name diff_reg diff_var diff_mem same_reg same_var same_mem} {
+ global decimal hex
+
+ set before_seq [gdb_get_line_number "Before ${name}"]
+ set after_seq [gdb_get_line_number "After ${name}"]
+
+ set insn [lindex [split $name "-"] 0]
+
+ gdb_test "break $before_seq" \
+ "Breakpoint $decimal at $hex: file .*/aarch64-lrcpc3.c, line $decimal\\." \
+ "$name: break before instruction sequence"
+ gdb_continue_to_breakpoint "$name: about to execute instruction sequence" \
+ [multi_line ".*/aarch64-lrcpc3.c:$decimal" \
+ "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
+
+ # Depending on the compiler, the line number information may put GDB a few
+ # instructions before the beginning of the asm statement.
+ arrive_at_instruction $insn
+ # Add a breakpoint that we're sure is at the prologue instruction.
+ gdb_test "break *\$pc" \
+ "Breakpoint $decimal at $hex: file .*/aarch64-lrcpc3.c, line $decimal\\." \
+ "$name: break at prologue instruction"
+
+ # Record the initial memory and register values.
+ set regs [concat $diff_reg $same_reg]
+ set vars [concat $diff_var $same_var]
+ set mem [concat $diff_mem $same_mem]
+ foreach r $regs {
+ set ${r}_initial [capture_command_output "info register $r" ""]
+ }
+ foreach v $vars {
+ set ${v}_initial [get_valueof "/x" "$v" "unable to read $v" \
+ "$name: get $v initial value"]
+ }
+ foreach m $mem {
+ set ${m}_initial [capture_command_output "x/x $m" ""]
+ }
+
+ gdb_test "break $after_seq" \
+ "Breakpoint $decimal at $hex: file .*/aarch64-lrcpc3.c, line $decimal\\." \
+ "$name: break after instruction sequence"
+ gdb_continue_to_breakpoint "$name: executed instruction sequence" \
+ [multi_line ".*/aarch64-lrcpc3.c:$decimal" ".*"]
+
+ # Record the new memory and register values.
+ foreach r $regs {
+ set ${r}_new [capture_command_output "info register $r" ""]
+ }
+ foreach v $vars {
+ set ${v}_new [get_valueof "/x" "$v" "unable to read $v" \
+ "$name: get $v new value"]
+ }
+ foreach m $mem {
+ set ${m}_new [capture_command_output "x/x $m" ""]
+ }
+
+ # Execute in reverse to before the instruction sequence.
+ gdb_test_no_output "set exec-direction reverse"
+
+ gdb_continue_to_breakpoint "reversed execution of instruction sequence" \
+ [multi_line ".*/aarch64-lrcpc3.c:$decimal" \
+ "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
+
+ # Record the final memory and register values.
+ foreach r $regs {
+ set ${r}_final [capture_command_output "info register $r" ""]
+ }
+ foreach v $vars {
+ set ${v}_final [get_valueof "/x" "$v" "unable to read $v" \
+ "$name: get $v final value"]
+ }
+ foreach m $mem {
+ set ${m}_final [capture_command_output "x/x $m" ""]
+ }
+
+ foreach v [concat $same_reg $same_var $same_mem] {
+ gdb_assert ![string compare [set ${v}_initial] [set ${v}_new]] \
+ "$name: check $v initial value versus $v new value"
+ gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
+ "$name: check $v initial value versus $v final value"
+ }
+
+ foreach v [concat $diff_reg $diff_var $diff_mem] {
+ gdb_assert [string compare [set ${v}_initial] [set ${v}_new]] \
+ "$name: check $v initial value versus $v new value"
+ gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
+ "$name: check $v initial value versus $v final value"
+ }
+
+ # Restore forward execution and go to end of recording.
+ gdb_test_no_output "set exec-direction forward"
+ gdb_test "record goto end" \
+ [multi_line \
+ "Go forward to insn number $decimal" \
+ "#0 main \\(\\) at .*/aarch64-lrcpc3.c:$decimal" \
+ ".*"]
+}
+
+set ldiapp_cases {
+ { ldiapp-0 { x19 x20 } { } { } { } { ptr } { src } }
+ { ldiapp-1 { w19 w20 } { } { } { } { ptr } { src } }
+ { ldiapp-2 { x19 x20 } { ptr } { } { } { } { src } }
+ { ldiapp-3 { w19 w20 } { ptr } { } { } { } { src } }
+ { ldiapp-4 { x21 x20 } { } { } { } { } { src } }
+ { ldiapp-5 { w21 w20 } { } { } { } { } { src } }
+}
+
+set stilp_cases {
+ { stilp-0 { } { } { src } { x19 x20 } { ptr } { } }
+ { stilp-1 { } { } { src } { w19 w20 } { ptr } { } }
+ { stilp-2 { } { ptr } { src } { x19 x20 } { } { } }
+ { stilp-3 { } { ptr } { src } { x19 x20 } { } { } }
+ { stilp-4 { } { } { src } { x20 } { ptr } { } }
+ { stilp-5 { } { } { src } { w20 } { ptr } { } }
+}
+
+set ldapr_stlr_cases {
+ { ldapr-0 { x19 } { ptr } { } { } { } { src } }
+ { ldapr-1 { w19 } { ptr } { } { } { } { src } }
+ { stlr-0 { } { ptr } { src } { x19 } { } { } }
+ { stlr-1 { } { ptr } { src } { w19 } { } { } }
+}
+
+set ldap1_stl1_cases {
+ { ldap1-0 { v22 } { } { } { ptr } { } { src } }
+ { stl1-0 { } { } { src } { v22 } { } { } }
+}
+
+set ldapur_stlur_cases {
+ { ldapur-0 { v22 } { } { } { ptr } { } { src } }
+ { stlur-0 { } { } { src } { v22 } { ptr } { } }
+ { ldapur-1 { v22 } { } { } { ptr } { } { src } }
+ { stlur-1 { } { } { src } { v22 } { ptr } { } }
+ { ldapur-2 { v22 } { } { } { ptr } { } { src } }
+ { stlur-2 { } { } { src } { v22 } { ptr } { } }
+ { ldapur-3 { v22 } { } { } { ptr } { } { src } }
+ { stlur-3 { } { } { src } { v22 } { ptr } { } }
+ { ldapur-4 { v22 } { } { } { ptr } { } { src } }
+ { stlur-4 { } { } { src } { v22 } { ptr } { } }
+ { ldapur-5 { v22 } { } { } { ptr } { } { src } }
+ { stlur-5 { } { } { src } { v22 } { ptr } { } }
+ { ldapur-6 { v22 } { } { } { ptr } { } { src } }
+ { stlur-6 { } { } { src } { v22 } { ptr } { } }
+}
+
+set all_cases [concat \
+ $ldiapp_cases $stilp_cases $ldapr_stlr_cases \
+ $ldap1_stl1_cases $ldapur_stlur_cases]
+
+foreach c $all_cases {
+ lassign $c name diff_reg diff_var diff_mem same_reg same_var same_mem
+ test_single_asm $name $diff_reg $diff_var $diff_mem $same_reg $same_var $same_mem
+}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 28709004570..c6afc556548 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -5287,6 +5287,69 @@ gdb_caching_proc allow_aarch64_fpmr_tests {} {
return $allow_fpmr_tests
}
+# Run a test on the target to see if it supports AArch64 LRCPC3 (Load-Acquire
+# RCpc instructions) extensions. Return 1 if so, 0 if it does not. Note this
+# causes a restart of GDB.
+
+gdb_caching_proc allow_aarch64_lrcpc3_tests {} {
+ global srcdir subdir gdb_prompt inferior_exited_re
+
+ set me "allow_aarch64_lrcpc3_tests"
+
+ if { ![is_aarch64_target] } {
+ return 0
+ }
+
+ # Take the opportunity to check whether the toolchain knows about LRCPC3.
+ set compile_flags "{additional_flags=-march=armv8-a+rcpc3}"
+
+ # Compile a program that tests the LRCPC3 feature.
+ set src {
+ #include <stdint.h>
+
+ int main() {
+ uint64_t a, b = 0;
+ uint64_t *ptr;
+ *ptr = 0xdeadbeef;
+
+ __asm__ volatile ("ldiapp %0, %1, [%2]\n"
+ : "=r"(a), "=r"(b)
+ : "r"(ptr)
+ : "memory");
+
+ return 0;
+ }
+ }
+
+ if {![gdb_simple_compile $me $src executable $compile_flags]} {
+ return 0
+ }
+
+ # Compilation succeeded so now run it via gdb.
+ clean_restart
+ gdb_load $obj
+ gdb_run_cmd
+ gdb_expect {
+ -re ".*Illegal instruction.*${gdb_prompt} $" {
+ verbose -log "\n$me lrcpc3 support not detected"
+ set allow_lrcpc3_tests 0
+ }
+ -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
+ verbose -log "\n$me: lrcpc3 support detected"
+ set allow_lrcpc3_tests 1
+ }
+ default {
+ warning "\n$me: default case taken"
+ set allow_lrcpc3_tests 0
+ }
+ }
+ gdb_exit
+ remote_file build delete $obj
+
+ verbose "$me: returning $allow_lrcpc3_tests" 2
+ return $allow_lrcpc3_tests
+}
+
# 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.
--
2.43.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 2/4] gdb/aarch64: Test record/replay support for CSSC
2026-04-20 21:52 [PATCH 0/4] gdb/aarch64: record/replay support for AArch64 Ezra.Sitorus
2026-04-20 21:52 ` [PATCH 1/4] gdb/aarch64: record/replay support for LRCPC3 Ezra.Sitorus
@ 2026-04-20 21:52 ` Ezra.Sitorus
2026-04-24 7:07 ` Luis
` (3 more replies)
2026-04-20 21:52 ` [PATCH 3/4] gdb/aarch64: record/replay support for RPRFM and PRFM (reg) Ezra.Sitorus
` (3 subsequent siblings)
5 siblings, 4 replies; 23+ messages in thread
From: Ezra.Sitorus @ 2026-04-20 21:52 UTC (permalink / raw)
To: gdb-patches; +Cc: luis.machado.foss, thiago.bauermann, Ezra Sitorus
From: Ezra Sitorus <ezra.sitorus@arm.com>
This patch checks that the handling of CSSC instructions is correct.
There is no explicit support for handling these instructions which is
why this patch is composed of tests.
Regression tested on aarch64-none-linux-gnu on QEMU with CSSC support.
I observe unrelated regressions that appear to be environmental rather
than caused by this patch.
---
gdb/testsuite/gdb.reverse/aarch64-cssc.c | 113 +++++++++++++++++
gdb/testsuite/gdb.reverse/aarch64-cssc.exp | 141 +++++++++++++++++++++
gdb/testsuite/lib/gdb.exp | 52 ++++++++
3 files changed, 306 insertions(+)
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.c
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.exp
diff --git a/gdb/testsuite/gdb.reverse/aarch64-cssc.c b/gdb/testsuite/gdb.reverse/aarch64-cssc.c
new file mode 100644
index 00000000000..d80d202590c
--- /dev/null
+++ b/gdb/testsuite/gdb.reverse/aarch64-cssc.c
@@ -0,0 +1,113 @@
+/* This test program is part of GDB, the GNU debugger.
+
+ Copyright 2024-2026 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/>. */
+
+#include <stdint.h>
+
+#define PREPARE_REGS(X, Y, Z) \
+ __asm__ volatile ("mov %0, %1\n" : "=r"(xa): "r"(X):); \
+ __asm__ volatile ("mov %0, %1\n" : "=r"(xb): "r"(Y):); \
+ __asm__ volatile ("mov %0, %1\n" : "=r"(xc): "r"(Z):); \
+
+int
+main (void)
+{
+ register uint64_t xa asm ("x19");
+ register uint32_t xb asm ("x20");
+ register uint64_t xc asm ("x21");
+ const uint64_t a = 0x0123456789abcdef;
+ const uint64_t b = 0xfedbca9876543210;
+ const uint64_t c = 0xdeadbeefc001face;
+
+ PREPARE_REGS (a, b, c);
+ /* Before abs. */
+ __asm__ volatile ("abs %0, %1\n"
+ : "+r"(xa)
+ : "r"(xb));
+ /* After abs. */
+
+ PREPARE_REGS (a, b, c);
+ /* Before cnt. */
+ __asm__ volatile ("cnt %0, %1\n"
+ : "+r"(xa)
+ : "r"(xb));
+ /* After cnt. */
+
+ PREPARE_REGS (a, b, c);
+ /* Before ctz. */
+ __asm__ volatile ("ctz %0, %1\n"
+ : "+r"(xa)
+ : "r"(xb));
+ /* After ctz. */
+
+ PREPARE_REGS (a, b, c);
+ /* Before smax-1. */
+ __asm__ volatile ("smax %0, %1, #10\n"
+ : "+r"(xa)
+ : "r"(xb));
+ /* After smax-1. */
+
+ PREPARE_REGS (a, b, c);
+ /* Before smax-2. */
+ __asm__ volatile ("smax %0, %1, %2\n"
+ : "+r"(xa)
+ : "r"(xb), "r"(xc));
+ /* After smax-2. */
+
+ PREPARE_REGS (a, b, c);
+ /* Before smin-1. */
+ __asm__ volatile ("smin %0, %1, #10\n"
+ : "+r"(xa)
+ : "r"(xb));
+ /* After smin-1. */
+
+ PREPARE_REGS (a, b, c);
+ /* Before smin-2. */
+ __asm__ volatile ("smin %0, %1, %2\n"
+ : "+r"(xa)
+ : "r"(xb), "r"(xc));
+ /* After smin-2. */
+
+ PREPARE_REGS (a, b, c);
+ /* Before umax-1. */
+ __asm__ volatile ("umax %0, %1, #10\n"
+ : "+r"(xa)
+ : "r"(xb));
+ /* After umax-1. */
+
+ PREPARE_REGS (a, b, c);
+ /* Before umax-2. */
+ __asm__ volatile ("umax %0, %1, %2\n"
+ : "+r"(xa)
+ : "r"(xb), "r"(xc));
+ /* After umax-2. */
+
+ PREPARE_REGS (a, b, c);
+ /* Before umin-1. */
+ __asm__ volatile ("umin %0, %1, #10\n"
+ : "+r"(xa)
+ : "r"(xb));
+ /* After umin-1. */
+
+ PREPARE_REGS (a, b, c);
+ /* Before umin-2. */
+ __asm__ volatile ("umin %0, %1, %2\n"
+ : "+r"(xa)
+ : "r"(xb), "r"(xc));
+ /* After umin-2. */
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.reverse/aarch64-cssc.exp b/gdb/testsuite/gdb.reverse/aarch64-cssc.exp
new file mode 100644
index 00000000000..640ea4ae19f
--- /dev/null
+++ b/gdb/testsuite/gdb.reverse/aarch64-cssc.exp
@@ -0,0 +1,141 @@
+# Copyright 2024-2026 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/>.
+
+# Test instruction record for AArch64 FEAT_CSSC instructions.
+# Based on gdb.reverse/aarch64-mops.exp
+#
+# The basic flow of the record tests are:
+# 1) Stop before executing the instructions of interest. Record
+# the initial value of the registers that the instruction will
+# change, i.e. the destination register.
+# 2) Execute the instructions. Record the new value of the
+# registers that changed.
+# 3) Reverse the direction of the execution and execute back to
+# just before the instructions of interest. Record the final
+# value of the registers of interest.
+# 4) Check that the initial and new values of the registers are
+# different, i.e. the instruction changed the registers as expected.
+# 5) Check that the initial and final values of the registers are
+# the same, i.e. GDB record restored the registers to their
+# original values.
+
+require allow_aarch64_cssc_tests
+standard_testfile
+
+if {
+ [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+ [list debug additional_flags=-march=armv8-a+cssc]]
+} {
+ return -1
+}
+
+if {![runto_main]} {
+ return -1
+}
+
+gdb_test_no_output "record full"
+
+proc test_single_asm { name } {
+ global decimal hex
+
+ set before_seq [gdb_get_line_number "Before ${name}"]
+ set after_seq [gdb_get_line_number "After ${name}"]
+ set regs { x19 x20 x21 }
+
+ set insn [lindex [split $name "-"] 0]
+
+ gdb_test "break $before_seq" \
+ "Breakpoint $decimal at $hex: file .*/aarch64-cssc.c, line $decimal\\." \
+ "$insn: break before instruction sequence"
+ gdb_continue_to_breakpoint "$insn: about to execute instruction sequence" \
+ [multi_line ".*/aarch64-cssc.c:$decimal" \
+ "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
+
+ # Depending on the compiler, the line number information may put GDB a few
+ # instructions before the beginning of the asm statement.
+ arrive_at_instruction $insn
+ # Add a breakpoint that we're sure is at the prologue instruction.
+ gdb_test "break *\$pc" \
+ "Breakpoint $decimal at $hex: file .*/aarch64-cssc.c, line $decimal\\." \
+ "$insn: break at prologue instruction"
+
+ # Record the initial register values.
+ foreach r $regs {
+ set ${r}_initial [capture_command_output "info register $r" ""]
+ }
+
+ gdb_test "break $after_seq" \
+ "Breakpoint $decimal at $hex: file .*/aarch64-cssc.c, line $decimal\\." \
+ "$insn: break after instruction sequence"
+ gdb_continue_to_breakpoint "$insn: executed instruction sequence" \
+ [multi_line ".*/aarch64-cssc.c:$decimal" ".*"]
+
+ # Record the new register values.
+ foreach r $regs {
+ set ${r}_new [capture_command_output "info register $r" ""]
+ }
+
+ # Execute in reverse to before the instruction sequence.
+ gdb_test_no_output "set exec-direction reverse"
+
+ gdb_continue_to_breakpoint "reversed execution of instruction sequence" \
+ [multi_line ".*/aarch64-cssc.c:$decimal" \
+ "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
+
+ # Record the final register values.
+ foreach r $regs {
+ set ${r}_final [capture_command_output "info register $r" ""]
+ }
+
+ foreach v { x19 x20 x21 } {
+ gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
+ "$insn: check $v initial value versus $v final value"
+ }
+
+ gdb_assert [string compare [set x19_initial] [set x19_new]] \
+ "$insn: check x19 initial value versus x19 new value"
+
+ foreach v { x20 x21 } {
+ gdb_assert ![string compare [set ${v}_initial] [set ${v}_new]] \
+ "$insn: check $v initial value versus $v new value"
+ }
+
+ # Restore forward execution and go to end of recording.
+ gdb_test_no_output "set exec-direction forward"
+ gdb_test "record goto end" \
+ [multi_line \
+ "Go forward to insn number $decimal" \
+ "#0 main \\(\\) at .*/aarch64-cssc.c:$decimal" \
+ ".*"]
+}
+
+set cases {
+ { abs }
+ { cnt }
+ { ctz }
+ { smax-1 }
+ { smax-2 }
+ { smin-1 }
+ { smin-2 }
+ { umax-1 }
+ { umax-2 }
+ { umin-1 }
+ { umin-2 }
+}
+
+foreach c $cases {
+ lassign $c insn
+ test_single_asm $insn
+}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index c6afc556548..7d400e9028a 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -5350,6 +5350,58 @@ gdb_caching_proc allow_aarch64_lrcpc3_tests {} {
return $allow_lrcpc3_tests
}
+# Run a test on the target to see if it supports the AArch64 CSSC feature.
+# Return 1 if so, 0 if it does not. Note this causes a restart of GDB.
+
+gdb_caching_proc allow_aarch64_cssc_tests {} {
+ global srcdir subdir gdb_prompt inferior_exited_re
+
+ set me "allow_aarch64_cssc_tests"
+
+ if { ![is_aarch64_target]} {
+ return 0
+ }
+
+ set compile_flags "{additional_flags=-march=armv8-a+cssc}"
+
+ # Compile a test program reading CSSC.
+ set src {
+ int main() {
+ asm volatile ("abs x0, x1");
+ return 0;
+ }
+ }
+
+ if {![gdb_simple_compile $me $src executable $compile_flags]} {
+ return 0
+ }
+
+ # Compilation succeeded so now run it via gdb.
+ clean_restart
+ gdb_load $obj
+ gdb_run_cmd
+
+ gdb_expect {
+ -re ".*Illegal instruction.*${gdb_prompt} $" {
+ verbose -log "\n$me cssc support not detected"
+ set allow_cssc_tests 0
+ }
+ -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
+ verbose -log "\n$me: cssc support detected"
+ set allow_cssc_tests 1
+ }
+ default {
+ warning "\n$me: default case taken"
+ set allow_cssc_tests 0
+ }
+ }
+ gdb_exit
+ remote_file build delete $obj
+
+ verbose "$me: returning $allow_cssc_tests" 2
+ return $allow_cssc_tests
+}
+
# 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.
--
2.43.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 3/4] gdb/aarch64: record/replay support for RPRFM and PRFM (reg)
2026-04-20 21:52 [PATCH 0/4] gdb/aarch64: record/replay support for AArch64 Ezra.Sitorus
2026-04-20 21:52 ` [PATCH 1/4] gdb/aarch64: record/replay support for LRCPC3 Ezra.Sitorus
2026-04-20 21:52 ` [PATCH 2/4] gdb/aarch64: Test record/replay support for CSSC Ezra.Sitorus
@ 2026-04-20 21:52 ` Ezra.Sitorus
2026-04-24 7:08 ` Luis
` (2 more replies)
2026-04-20 21:52 ` [PATCH 4/4] gdb/aarch64: record/replay support for LSE128 Ezra.Sitorus
` (2 subsequent siblings)
5 siblings, 3 replies; 23+ messages in thread
From: Ezra.Sitorus @ 2026-04-20 21:52 UTC (permalink / raw)
To: gdb-patches; +Cc: luis.machado.foss, thiago.bauermann, Ezra Sitorus
From: Ezra Sitorus <ezra.sitorus@arm.com>
The PRFM (register) instruction variant was unsupported. This is added
along with RPRFM.
No testcase has been added as these are hint instructions which don't
modify registers/memory. I'm happy to add them in if you think these are
required.
The full testsuite was done on aarch64-none-linux-gnu without RPRFM.
The gdb.arch and gdb.reverse tests were run on Shrinkwrap with RPRFM
support. I've observed unrelated regressions that appear to be due to
Shrinkwrap rather than my patch.
---
gdb/aarch64-tdep.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 81d4f160f8f..61c462979b3 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -5914,6 +5914,11 @@ aarch64_record_load_store (aarch64_insn_decode_record *aarch64_insn_r)
else
if (size_bits != 0x03)
ld_flag = 0x01;
+ else if (size_bits == 0x3 && vector_flag == 0x0 && opc == 0x2)
+ {
+ /* PRFM (register) or RPRFM */
+ return AARCH64_RECORD_SUCCESS;
+ }
else
return AARCH64_RECORD_UNKNOWN;
--
2.43.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 4/4] gdb/aarch64: record/replay support for LSE128
2026-04-20 21:52 [PATCH 0/4] gdb/aarch64: record/replay support for AArch64 Ezra.Sitorus
` (2 preceding siblings ...)
2026-04-20 21:52 ` [PATCH 3/4] gdb/aarch64: record/replay support for RPRFM and PRFM (reg) Ezra.Sitorus
@ 2026-04-20 21:52 ` Ezra.Sitorus
2026-04-24 7:11 ` Luis
` (2 more replies)
2026-04-23 17:31 ` [PATCH 0/4] gdb/aarch64: record/replay support for AArch64 Guinevere Larsen
2026-04-24 7:16 ` Luis
5 siblings, 3 replies; 23+ messages in thread
From: Ezra.Sitorus @ 2026-04-20 21:52 UTC (permalink / raw)
To: gdb-patches; +Cc: luis.machado.foss, thiago.bauermann, Ezra Sitorus
From: Ezra Sitorus <ezra.sitorus@arm.com>
FEAT_LSE128 introduces support for 128-bit atomic instructions. This
patch teaches GDB to decode these instructions for recording and
reversing.
Regression tested on aarch64-none-linux-gnu on QEMU with LSE128 support.
I observe unrelated regressions that appear to be environmental rather
than caused by this patch.
---
gdb/aarch64-tdep.c | 13 ++
gdb/testsuite/gdb.reverse/aarch64-lse128.c | 139 +++++++++++++++++++
gdb/testsuite/gdb.reverse/aarch64-lse128.exp | 138 ++++++++++++++++++
gdb/testsuite/lib/gdb.exp | 56 ++++++++
4 files changed, 346 insertions(+)
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.c
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.exp
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 61c462979b3..aff8b294bd5 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -6004,6 +6004,19 @@ aarch64_record_load_store (aarch64_insn_decode_record *aarch64_insn_r)
else if ((insn_bits24_27 & 1) == 1 && insn_bits28_29 == 1
&& insn_bits10_11 == 1 && !insn_bit21)
return aarch64_record_memcopy_memset (aarch64_insn_r);
+ /* Large System Extension 128 (LSE128) instructions. */
+ else if (vector_flag == 0 && insn_bits10_11 == 0 && insn_bit21
+ && size_bits == 0 && !bit (aarch64_insn_r->aarch64_insn, 29)
+ && bits (aarch64_insn_r->aarch64_insn, 24, 25) == 0x1)
+ {
+ regcache_raw_read_unsigned (aarch64_insn_r->regcache, reg_rn, &address);
+ record_buf_mem[0] = 128 >> 3;
+ record_buf_mem[1] = address;
+ aarch64_insn_r->mem_rec_count = 1;
+ record_buf[0] = reg_rt;
+ record_buf[1] = bits (aarch64_insn_r->aarch64_insn, 16, 20);
+ aarch64_insn_r->reg_rec_count = 2;
+ }
/* Advanced SIMD load/store instructions. */
else
return aarch64_record_asimd_load_store (aarch64_insn_r);
diff --git a/gdb/testsuite/gdb.reverse/aarch64-lse128.c b/gdb/testsuite/gdb.reverse/aarch64-lse128.c
new file mode 100644
index 00000000000..906c303d904
--- /dev/null
+++ b/gdb/testsuite/gdb.reverse/aarch64-lse128.c
@@ -0,0 +1,139 @@
+/* This test program is part of GDB, the GNU debugger.
+
+ Copyright 2024-2026 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/>. */
+
+#include <stdint.h>
+#include <stdalign.h>
+#include <string.h>
+
+#define INITIAL_STRING "This is just some string."
+#define BUF_SIZE sizeof (INITIAL_STRING)
+
+#define PREPARE_REGS(X, Y) \
+ strcpy (src, INITIAL_STRING); \
+ __asm__ volatile ("mov %0, %1\n" : "=r"(xa): "r"(X):); \
+ __asm__ volatile ("mov %0, %1\n" : "=r"(xb): "r"(Y):); \
+ ptr = (uint64_t *)src; \
+
+int
+main (void)
+{
+ alignas(16) char src[BUF_SIZE];
+
+ register uint64_t xa asm ("x19");
+ register uint64_t xb asm ("x20");
+ register uint64_t *ptr asm ("x21");
+ uint64_t a = 0x0123456789abcdef;
+ uint64_t b = 0xfedbca9876543210;
+
+ PREPARE_REGS (a, b);
+ /* Before ldclrp. */
+ __asm__ volatile ("ldclrp %0, %1, [%2]\n"
+ : "+r"(xa), "+r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After ldclrp. */
+
+ PREPARE_REGS (a, b);
+ /* Before ldclrpa. */
+ __asm__ volatile ("ldclrpa %0, %1, [%2]\n"
+ : "+r"(xa), "+r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After ldclrpa. */
+
+ PREPARE_REGS (a, b);
+ /* Before ldclrpal. */
+ __asm__ volatile ("ldclrpal %0, %1, [%2]\n"
+ : "+r"(xa), "+r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After ldclrpal. */
+
+ PREPARE_REGS (a, b);
+ /* Before ldclrpl. */
+ __asm__ volatile ("ldclrpl %0, %1, [%2]\n"
+ : "+r"(xa), "+r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After ldclrpl. */
+
+ PREPARE_REGS (a, b);
+ /* Before ldsetp. */
+ __asm__ volatile ("ldsetp %0, %1, [%2]\n"
+ : "+r"(xa), "+r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After ldsetp. */
+
+ PREPARE_REGS (a, b);
+ /* Before ldsetpa. */
+ __asm__ volatile ("ldsetpa %0, %1, [%2]\n"
+ : "+r"(xa), "+r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After ldsetpa. */
+
+ PREPARE_REGS (a, b);
+ /* Before ldsetpal. */
+ __asm__ volatile ("ldsetpal %0, %1, [%2]\n"
+ : "+r"(xa), "+r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After ldsetpal. */
+
+ PREPARE_REGS (a, b);
+ /* Before ldsetpl. */
+ __asm__ volatile ("ldsetpl %0, %1, [%2]\n"
+ : "+r"(xa), "+r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After ldsetpl. */
+
+ PREPARE_REGS (a, b);
+ /* Before swpp. */
+ __asm__ volatile ("swpp %0, %1, [%2]\n"
+ : "+r"(xa), "+r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After swpp. */
+
+ PREPARE_REGS (a, b);
+ /* Before swppa. */
+ __asm__ volatile ("swppa %0, %1, [%2]\n"
+ : "+r"(xa), "+r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After swppa. */
+
+ PREPARE_REGS (a, b);
+ /* Before swppal. */
+ __asm__ volatile ("swppal %0, %1, [%2]\n"
+ : "+r"(xa), "+r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After swppal. */
+
+ PREPARE_REGS (a, b);
+ /* Before swppl. */
+ __asm__ volatile ("swppl %0, %1, [%2]\n"
+ : "+r"(xa), "+r"(xb)
+ : "r"(ptr)
+ : "memory");
+ /* After swppl. */
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.reverse/aarch64-lse128.exp b/gdb/testsuite/gdb.reverse/aarch64-lse128.exp
new file mode 100644
index 00000000000..273e239ec66
--- /dev/null
+++ b/gdb/testsuite/gdb.reverse/aarch64-lse128.exp
@@ -0,0 +1,138 @@
+# Copyright 2024-2026 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/>.
+
+# Test instruction record for AArch64 FEAT_LSE128 instructions.
+# Based on gdb.reverse/aarch64-mops.exp
+#
+# The basic flow of the record tests are:
+# 1) Stop before executing the instructions of interest. Record
+# the initial value of the registers that the instruction will
+# change, i.e. the destination register.
+# 2) Execute the instructions. Record the new value of the
+# registers that changed.
+# 3) Reverse the direction of the execution and execute back to
+# just before the instructions of interest. Record the final
+# value of the registers of interest.
+# 4) Check that the initial and new values of the registers are
+# different, i.e. the instruction changed the registers as expected.
+# 5) Check that the initial and final values of the registers are
+# the same, i.e. GDB record restored the registers to their
+# original values.
+
+standard_testfile
+
+require allow_aarch64_lse128_tests
+
+if {
+ [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+ [list debug additional_flags=-march=armv8-a+lse128]]
+} {
+ return -1
+}
+
+if {![runto_main]} {
+ return -1
+}
+
+gdb_test_no_output "record full"
+
+proc test_single_asm {insn} {
+ global decimal hex
+
+ set before_seq [gdb_get_line_number "Before ${insn}"]
+ set after_seq [gdb_get_line_number "After ${insn}"]
+ set regs { x19 x20 }
+
+ gdb_test "break $before_seq" \
+ "Breakpoint $decimal at $hex: file .*/aarch64-lse128.c, line $decimal\\." \
+ "$insn: break before instruction sequence"
+ gdb_continue_to_breakpoint "$insn: about to execute instruction sequence" \
+ [multi_line ".*/aarch64-lse128.c:$decimal" \
+ "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
+
+ # Depending on the compiler, the line number information may put GDB a few
+ # instructions before the beginning of the asm statement.
+ arrive_at_instruction $insn
+ # Add a breakpoint that we're sure is at the prologue instruction.
+ gdb_test "break *\$pc" \
+ "Breakpoint $decimal at $hex: file .*/aarch64-lse128.c, line $decimal\\." \
+ "$insn: break at prologue instruction"
+
+ # Record the initial memory and register values.
+ foreach r $regs {
+ set ${r}_initial [capture_command_output "info register $r" ""]
+ }
+ set ptr_initial [capture_command_output "x/4gwx ptr" ""]
+
+ gdb_test "break $after_seq" \
+ "Breakpoint $decimal at $hex: file .*/aarch64-lse128.c, line $decimal\\." \
+ "$insn: break after instruction sequence"
+ gdb_continue_to_breakpoint "$insn: executed instruction sequence" \
+ [multi_line ".*/aarch64-lse128.c:$decimal" ".*"]
+
+ # Record the new memory and register values.
+ foreach r $regs {
+ set ${r}_new [capture_command_output "info register $r" ""]
+ }
+ set ptr_new [capture_command_output "x/4gwx ptr" ""]
+
+ # Execute in reverse to before the instruction sequence.
+ gdb_test_no_output "set exec-direction reverse"
+
+ gdb_continue_to_breakpoint "reversed execution of instruction sequence" \
+ [multi_line ".*/aarch64-lse128.c:$decimal" \
+ "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
+
+ # Record the final memory and register values.
+ foreach r $regs {
+ set ${r}_final [capture_command_output "info register $r" ""]
+ }
+ set ptr_final [capture_command_output "x/4gwx ptr" ""]
+
+ foreach v [concat $regs {ptr}] {
+ gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
+ "$insn: check $v initial value versus $v final value"
+ gdb_assert [string compare [set ${v}_initial] [set ${v}_new]] \
+ "$insn: check $v initial value versus $v new value"
+ }
+
+ # Restore forward execution and go to end of recording.
+ gdb_test_no_output "set exec-direction forward"
+ gdb_test "record goto end" \
+ [multi_line \
+ "Go forward to insn number $decimal" \
+ "#0 main \\(\\) at .*/aarch64-lse128.c:$decimal" \
+ ".*"]
+}
+
+set cases {
+ { ldclrp }
+ { ldclrpa }
+ { ldclrpal }
+ { ldclrpl }
+ { ldsetp }
+ { ldsetpa }
+ { ldsetpal }
+ { ldsetpl }
+ { swpp }
+ { swppa }
+ { swppal }
+ { swppl }
+}
+
+foreach c $cases {
+ lassign $c insn
+ test_single_asm $insn
+}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 7d400e9028a..d5f9008470a 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -5402,6 +5402,62 @@ gdb_caching_proc allow_aarch64_cssc_tests {} {
return $allow_cssc_tests
}
+# Run a test on the target to see if it supports the AArch64 LSE128 feature.
+# Return 1 if so, 0 if it does not. Note this causes a restart of GDB.
+
+gdb_caching_proc allow_aarch64_lse128_tests {} {
+ global srcdir subdir gdb_prompt inferior_exited_re
+
+ set me "allow_aarch64_lse128_tests"
+
+ if { ![is_aarch64_target]} {
+ return 0
+ }
+
+ set compile_flags "{additional_flags=-march=armv8-a+lse128}"
+
+ # Compile a test program reading LSE128.
+ set src {
+ #include <stdint.h>
+
+ int main() {
+ __attribute__((aligned(16))) uint64_t mem[2] = { 0x0, 0x1 };
+ uint64_t *ptr = mem;
+ __asm__ volatile ("ldclrp x0, x1, [%0]\n" :: "r"(ptr) : "x0", "x1", "memory");
+ return 0;
+ }
+ }
+
+ if {![gdb_simple_compile $me $src executable $compile_flags]} {
+ return 0
+ }
+
+ # Compilation succeeded so now run it via gdb.
+ clean_restart
+ gdb_load $obj
+ gdb_run_cmd
+
+ gdb_expect {
+ -re ".*Illegal instruction.*${gdb_prompt} $" {
+ verbose -log "\n$me lse128 support not detected"
+ set allow_lse128_tests 0
+ }
+ -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
+ verbose -log "\n$me: lse128 support detected"
+ set allow_lse128_tests 1
+ }
+ default {
+ warning "\n$me: default case taken"
+ set allow_lse128_tests 0
+ }
+ }
+ gdb_exit
+ remote_file build delete $obj
+
+ verbose "$me: returning $allow_lse128_tests" 2
+ return $allow_lse128_tests
+}
+
# 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.
--
2.43.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 0/4] gdb/aarch64: record/replay support for AArch64
2026-04-20 21:52 [PATCH 0/4] gdb/aarch64: record/replay support for AArch64 Ezra.Sitorus
` (3 preceding siblings ...)
2026-04-20 21:52 ` [PATCH 4/4] gdb/aarch64: record/replay support for LSE128 Ezra.Sitorus
@ 2026-04-23 17:31 ` Guinevere Larsen
2026-04-23 22:20 ` Ezra Sitorus
2026-04-24 5:33 ` Thiago Jung Bauermann
2026-04-24 7:16 ` Luis
5 siblings, 2 replies; 23+ messages in thread
From: Guinevere Larsen @ 2026-04-23 17:31 UTC (permalink / raw)
To: Ezra.Sitorus, gdb-patches; +Cc: luis.machado.foss, thiago.bauermann
On 4/20/26 6:52 PM, Ezra.Sitorus@arm.com wrote:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> This patch series enables record/replay for the following AArch64
> features:
> * FEAT_LRCPC3 (+rcpc3)
> * FEAT_CSSC (+cssc)
> * FEAT_RPRFM (+rprfm)
> * FEAT_LSE128 (+lse128)
>
> These were tested either with QEMU or Shrinkwrap. In both cases, I seem
> to have test failures that are not related to my patches (gdb.threads,
> gdb.mi tests for example). In these cases, is it fair to ignore these?
> Or is there some underlying issue I've missed?
gdb.threads tests are pretty often unreliable (we usually call them
racy, as in, failing due to race conditions), and unfortunately some
tests consistently fail on some configurations. My general
recommendation while you're getting used to the tests is to run the
testsuite before and after your changes, so you have a feel for what is
likely to be a regression of yours or not.
For reference, my system usually shows 4 failures, but can go up to 6
depending on what I do. If I run it with clang, I can expect between 50
and 70 failures.
>
> In addition, whenever I used QEMU, I disabled gcs because it seems to
> cause test failures - address/memory accesses seem to always cause a
> fault. Am I doing something wrong?
Unfortunately I can't help here, never tried running reverse tests on
QEMU...
I'll try to review your patches tomorrow, to get this moving :)
--
Cheers,
Guinevere Larsen
It/she
>
> Ezra
>
> Ezra Sitorus (4):
> gdb/aarch64: record/replay support for LRCPC3
> gdb/aarch64: Test record/replay support for CSSC
> gdb/aarch64: record/replay support for RPRFM and PRFM (reg)
> gdb/aarch64: record/replay support for LSE128
>
> gdb/aarch64-tdep.c | 105 ++++++
> gdb/testsuite/gdb.reverse/aarch64-cssc.c | 113 +++++++
> gdb/testsuite/gdb.reverse/aarch64-cssc.exp | 141 ++++++++
> gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c | 319 +++++++++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp | 203 ++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-lse128.c | 139 ++++++++
> gdb/testsuite/gdb.reverse/aarch64-lse128.exp | 138 ++++++++
> gdb/testsuite/lib/gdb.exp | 171 ++++++++++
> 8 files changed, 1329 insertions(+)
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.exp
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.exp
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 0/4] gdb/aarch64: record/replay support for AArch64
2026-04-23 17:31 ` [PATCH 0/4] gdb/aarch64: record/replay support for AArch64 Guinevere Larsen
@ 2026-04-23 22:20 ` Ezra Sitorus
2026-04-24 5:33 ` Thiago Jung Bauermann
1 sibling, 0 replies; 23+ messages in thread
From: Ezra Sitorus @ 2026-04-23 22:20 UTC (permalink / raw)
To: Guinevere Larsen; +Cc: gdb-patches, luis.machado.foss, thiago.bauermann
> > These were tested either with QEMU or Shrinkwrap. In both cases, I seem
> > to have test failures that are not related to my patches (gdb.threads,
> > gdb.mi tests for example). In these cases, is it fair to ignore these?
> > Or is there some underlying issue I've missed?
>
> gdb.threads tests are pretty often unreliable (we usually call them racy, as
> in, failing due to race conditions), and unfortunately some tests
> consistently fail on some configurations. My general recommendation while
> you're getting used to the tests is to run the testsuite before and after
> your changes, so you have a feel for what is likely to be a regression of
> yours or not.
>
> For reference, my system usually shows 4 failures, but can go up to 6
> depending on what I do. If I run it with clang, I can expect between 50 and
> 70 failures.
I just re-read what I sent, I should clarify that on Shrinkwrap (which is much
slower than QEMU), I can only feasibly do gdb.arch and gdb.reverse tests. On
QEMU I do a full testsuite run. That said, even on Shrinkwrap with the smaller
set of tests, I still get failures on some gdb.arch and gdb.reverse tests,
which essentially make it hard for me to compare what's happening. On the
whole, the gdb.reverse/aarch64-*.exp tests pass.
> I'll try to review your patches tomorrow, to get this moving :)
Thanks!
Ezra
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 0/4] gdb/aarch64: record/replay support for AArch64
2026-04-23 17:31 ` [PATCH 0/4] gdb/aarch64: record/replay support for AArch64 Guinevere Larsen
2026-04-23 22:20 ` Ezra Sitorus
@ 2026-04-24 5:33 ` Thiago Jung Bauermann
1 sibling, 0 replies; 23+ messages in thread
From: Thiago Jung Bauermann @ 2026-04-24 5:33 UTC (permalink / raw)
To: Guinevere Larsen; +Cc: Ezra.Sitorus, gdb-patches, luis.machado.foss
Hello,
Guinevere Larsen <guinevere@redhat.com> writes:
> On 4/20/26 6:52 PM, Ezra.Sitorus@arm.com wrote:
>> From: Ezra Sitorus <ezra.sitorus@arm.com>
>>
>> This patch series enables record/replay for the following AArch64
>> features:
>> * FEAT_LRCPC3 (+rcpc3)
>> * FEAT_CSSC (+cssc)
>> * FEAT_RPRFM (+rprfm)
>> * FEAT_LSE128 (+lse128)
Great!
>> These were tested either with QEMU or Shrinkwrap. In both cases, I seem
>> to have test failures that are not related to my patches (gdb.threads,
>> gdb.mi tests for example). In these cases, is it fair to ignore these?
>> Or is there some underlying issue I've missed?
>
> gdb.threads tests are pretty often unreliable (we usually call them racy, as in, failing
> due to race conditions), and unfortunately some tests consistently fail on some
> configurations. My general recommendation while you're getting used to the tests is to run
> the testsuite before and after your changes, so you have a feel for what is likely to be a
> regression of yours or not.
>
> For reference, my system usually shows 4 failures, but can go up to 6 depending on what I
> do. If I run it with clang, I can expect between 50 and 70 failures.
Yes, a number of gdb.threads tests are flaky. With Linux's ptrace
syscall and the /proc/<pid>/tasks/<tid>/ interfaces that GDB has to work
with, it's hard to make GDB have a race-free accurate view of the
inferior threads. For a glimpse of some of the issues, see for example
bugs 26286¹ and 31832².
Regarding the flaky MI tests, I didn't dig much into them but at least
for some I think the problem is that they generate a lot of output which
can overflow Expect's buffer.
The Linaro CI keeps an "expected failures" file containing the baseline
failures and automatically detected flaky failures. For example:
http://54.172.246.49:9090/jobs/tcwg_gdb_check--master-aarch64-build/builds/10102/archive/artifacts/sumfiles/xfails.xfail
(We're currently doing some server migration so the URL above is likely
to change at some point. CI notification emails will have the current
one.)
You can use it with GCC's
contrib/testsuite-management/validate_failures.py script to filter the
noise:
$ validate_failures.py --manifest=xfails.xfail --results=gdb.sum > failures.txt
Note that strictly speaking, the baseline failures are specific of the
machine the CI jobs run on. If you want to be precise you can replace
the "known failures" section of the file with the failures from your
testsuite run of the patch's base commit.
>> In addition, whenever I used QEMU, I disabled gcs because it seems to
>> cause test failures - address/memory accesses seem to always cause a
>> fault. Am I doing something wrong?
>
> Unfortunately I can't help here, never tried running reverse tests on QEMU...
I see those failures too. When I worked on GCS support for GDB, QEMU
didn't have GCS support itself so I was never able to run the whole
testsuite with GCS enabled. It seems I chose too small a set of tests to
run on Shrinkwrap. I'll work on these regressions in the near future.
> I'll try to review your patches tomorrow, to get this moving :)
Thank you! I'm reviewing them too.
--
Thiago
¹ https://sourceware.org/bugzilla/show_bug.cgi?id=26286
² https://sourceware.org/bugzilla/show_bug.cgi?id=31832
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/4] gdb/aarch64: record/replay support for LRCPC3
2026-04-20 21:52 ` [PATCH 1/4] gdb/aarch64: record/replay support for LRCPC3 Ezra.Sitorus
@ 2026-04-24 7:00 ` Luis
2026-04-24 15:09 ` Guinevere Larsen
` (2 subsequent siblings)
3 siblings, 0 replies; 23+ messages in thread
From: Luis @ 2026-04-24 7:00 UTC (permalink / raw)
To: Ezra.Sitorus, gdb-patches; +Cc: thiago.bauermann
On 20/04/2026 22:52, Ezra.Sitorus@arm.com wrote:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> FEAT_LRCPC3 introduces various load/store instructions with release
> consistency for cases where ordering is required. This patch teaches GDB
> to decode these instructions for recording and reversing.
>
> The gdb.reverse/aarch64-lrcpc3.exp testcase verifies that the
> instructions are recorded and correctly reversed. In particular, there
> are some interesting cases to note:
> * ldapur/stlur are SIMD instructions, but are not decoded in the simd
> function.
> * There are writeback cases to cover too. These were taken from the
> binutils testcases: gas/testsuite/gas/aarch64/rcpc3.s.
>
> The full testsuite was done on aarch64-none-linux-gnu without LRCPC3.
> The gdb.arch and gdb.reverse tests were run on Shrinkwrap with LRCPC3
> support.
>
> Please note:
> 1) There is no support for LRCPC and LRCPC2 instructions
> 2) LRCPC3 is gated with +rcpc3 in GCC/binutils.
> ---
> gdb/aarch64-tdep.c | 87 +++++
> gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c | 319 +++++++++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp | 203 ++++++++++++
> gdb/testsuite/lib/gdb.exp | 63 ++++
> 4 files changed, 672 insertions(+)
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
>
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index 4befaa2720d..81d4f160f8f 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -5812,6 +5812,93 @@ aarch64_record_load_store (aarch64_insn_decode_record *aarch64_insn_r)
> aarch64_insn_r->reg_rec_count = 1;
> }
> }
> + /* LRCPC3 instructions. This covers ldiapp/stilp, ldapur/stlur (FP/SIMD),
> + ldapr/stlr. */
> + else if ((insn_bits24_27 & 0x0b) == 0x09 && insn_bits28_29 == 0x01
> + && insn_bits10_11 == 0x02 && !insn_bit21)
> + {
> + /* ldapur/stlur (FP/SIMD), ldapr/stlr. We can differentiate between the
> + 2 types by checking the vector flag. */
> + if (insn_bit23 || vector_flag)
> + {
> + /* For the vector instruction, the offset comes from the imm9
> + bitfield, whereas the other can only take possible values from the
> + size bitfield. */
> + int16_t imm9_off = sbits (aarch64_insn_r->aarch64_insn, 12, 20);
> + offset = vector_flag ? imm9_off : -(1 << size_bits);
> + uint32_t regnum_offset = vector_flag ? AARCH64_V0_REGNUM : 0;
> + if (ld_flag)
> + {
> + record_buf[0] = reg_rt + regnum_offset;
> + aarch64_insn_r->reg_rec_count = 1;
> + if (!vector_flag)
> + {
> + /* The Rn register always has writeback in LRCPC3. This is
> + not the case in LRCPC. */
> + record_buf[1] = reg_rn;
> + aarch64_insn_r->reg_rec_count = 2;
> + }
> + }
> + else
> + {
> + regcache_raw_read_unsigned (aarch64_insn_r->regcache, reg_rn,
> + &address);
> + /* (vector_flag && insn_bit23) is the STLUR instruction with Q
> + register. */
> + datasize = (vector_flag && insn_bit23) ? 128 : (8 << size_bits);
> + /* LRCPC3 adds STLR with a pre-indexed offset. There is another
> + STLR variant without offset but this has a different encoding. */
> + if (!vector_flag)
> + {
> + record_buf[0] = reg_rn;
> + aarch64_insn_r->reg_rec_count = 1;
> + }
> + record_buf_mem[0] = datasize >> 3;
> + record_buf_mem[1] = address + offset;
> + aarch64_insn_r->mem_rec_count = 1;
> + }
> + }
> + else
> + {
> + /* ldiapp/stilp. */
> + uint8_t opc2 = bits (aarch64_insn_r->aarch64_insn, 12, 15);
> + reg_rt2 = bits (aarch64_insn_r->aarch64_insn, 16, 20);
> + if (ld_flag)
> + {
> + record_buf[0] = reg_rt;
> + record_buf[1] = reg_rt2;
> + aarch64_insn_r->reg_rec_count = 2;
> +
> + /* If the registers don't match and there's no offset then
> + there's WB. */
> + if (reg_rn != reg_rt && reg_rn != reg_rt2 && opc2 == 0)
> + {
> + record_buf[2] = reg_rn;
> + aarch64_insn_r->reg_rec_count = 3;
> + }
> + }
> + else
> + {
> + datasize = 8 << size_bits;
> + regcache_raw_read_unsigned (aarch64_insn_r->regcache, reg_rn,
> + &address);
> + offset = (opc2 == 0) ? (2 << size_bits) : 0;
> + address -= offset;
> +
> + record_buf_mem[0] = datasize >> 3;
> + record_buf_mem[1] = address;
> + record_buf_mem[2] = datasize >> 3;
> + record_buf_mem[3] = address + (datasize >> 3);
> + aarch64_insn_r->mem_rec_count = 2;
> +
> + if (offset != 0)
> + {
> + record_buf[0] = reg_rn;
> + aarch64_insn_r->reg_rec_count = 1;
> + }
> + }
> + }
> + }
> /* Load/store register (register offset) instructions. */
> else if ((insn_bits24_27 & 0x0b) == 0x08 && insn_bits28_29 == 0x03
> && insn_bits10_11 == 0x02 && insn_bit21)
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
> new file mode 100644
> index 00000000000..9265489ff70
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
> @@ -0,0 +1,319 @@
> +/* This test program is part of GDB, the GNU debugger.
> +
> + Copyright 2024-2026 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/>. */
> +
> +#include <stdint.h>
> +#include <stdalign.h>
> +#include <string.h>
> +
> +#define INITIAL_STRING "This is just some string."
> +#define BUF_SIZE sizeof (INITIAL_STRING)
> +
> +#define PREPARE_FOR_LOAD(OFFSET) \
> + __asm__ volatile ("mov %0, #0\n" : "=r"(xa)::); \
> + __asm__ volatile ("mov %0, #0\n" : "=r"(xb)::); \
> + strcpy (src, INITIAL_STRING); \
> + ptr = (uint64_t *)((uint8_t *)src + OFFSET); \
> +
> +#define PREPARE_FOR_VECTOR_LOAD(OFFSET) \
> + __asm__ volatile ("movi %0.2d, #0\n" : "=w"(va)::); \
> + strcpy (src, INITIAL_STRING); \
> + ptr = (uint64_t *)((uint8_t *)src + OFFSET); \
> +
> +#define PREPARE_FOR_STORE(OFFSET) \
> + __asm__ volatile ("mov %0, #0\n" : "=r"(xa)::); \
> + __asm__ volatile ("mov %0, #0\n" : "=r"(xb)::); \
> + strcpy (src, INITIAL_STRING); \
> + ptr = (uint64_t *)((uint8_t *)src + OFFSET); \
> +
> +#define PREPARE_FOR_VECTOR_STORE(OFFSET) \
> + __asm__ volatile ("movi %0.2d, #0\n" : "=w"(va)::); \
> + strcpy (src, INITIAL_STRING); \
> + ptr = (uint64_t *)((uint8_t *)src + OFFSET); \
> +
> +typedef uint64_t uint64x2_t __attribute__((vector_size(16)));
> +
> +int
> +main (void)
> +{
> + alignas(16) char src[BUF_SIZE];
> +
> + register uint64_t xa asm ("x19");
> + register uint32_t xb asm ("x20");
> + register uint64_t *ptr asm ("x21");
> + register uint64x2_t va asm ("v22");
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldiapp-0. */
> + __asm__ volatile ("ldiapp %0, %1, [%2]\n"
> + : "=r"(xa), "=r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldiapp-0. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldiapp-1. */
> + __asm__ volatile ("ldiapp %w0, %w1, [%2]\n"
> + : "=r"(xa), "=r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldiapp-1. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldiapp-2. */
> + __asm__ volatile ("ldiapp %0, %1, [%2], #16\n"
> + : "=r"(xa), "=r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldiapp-2. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldiapp-3. */
> + __asm__ volatile ("ldiapp %w0, %w1, [%2], #8\n"
> + : "=r"(xa), "=r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldiapp-3. */
> + /* Register overlap between source and destination registers. Since there is
> + no offset, writeback is disabled. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldiapp-4. */
> + __asm__ volatile ("ldiapp %0, %1, [%0]\n"
> + : "+r"(ptr), "=r"(xb)
> + :
> + : "memory");
> + /* After ldiapp-4. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldiapp-5. */
> + __asm__ volatile ("ldiapp %w0, %w1, [%0]\n"
> + : "+r"(ptr), "=r"(xb)
> + :
> + : "memory");
> + /* After ldiapp-5. */
> +
> + PREPARE_FOR_STORE (0);
> + /* Before stilp-0. */
> + __asm__ volatile ("stilp %0, %1, [%2]\n"
> + :
> + : "r"(xa), "r"(xb), "r"(ptr)
> + : "memory");
> + /* After stilp-0. */
> +
> + PREPARE_FOR_STORE (0);
> + /* Before stilp-1. */
> + __asm__ volatile ("stilp %w0, %w1, [%2]\n"
> + :
> + : "r"(xa), "r"(xb), "r"(ptr)
> + : "memory");
> + /* After stilp-1. */
> +
> + PREPARE_FOR_STORE (16);
> + /* Before stilp-2. */
> + __asm__ volatile ("stilp %1, %2, [%0, #-16]!\n"
> + : "+r"(ptr)
> + : "r"(xa), "r"(xb)
> + : "memory");
> + /* After stilp-2. */
> +
> + PREPARE_FOR_STORE (8);
> + /* Before stilp-3. */
> + __asm__ volatile ("stilp %w1, %w2, [%0, #-8]!\n"
> + : "+r"(ptr)
> + : "r"(xa), "r"(xb)
> + : "memory");
> + /* After stilp-3. */
> + /* Register overlap. Since there is no offset, writeback is disabled. */
> +
> + PREPARE_FOR_STORE (0);
> + /* Before stilp-4. */
> + __asm__ volatile ("stilp %0, %1, [%0]\n"
> + :
> + : "r"(ptr), "r"(xb)
> + : "memory");
> + /* After stilp-4. */
> +
> + PREPARE_FOR_STORE (0);
> + /* Before stilp-5. */
> + __asm__ volatile ("stilp %w0, %w1, [%0]\n"
> + :
> + : "r"(ptr), "r"(xb)
> + : "memory");
> + /* After stilp-5. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldapr-0. */
> + __asm__ volatile ("ldapr %0, [%1], #8\n"
> + : "+r"(xa), "+r"(ptr)
> + :
> + : "memory");
> + /* After ldapr-0. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldapr-1. */
> + __asm__ volatile ("ldapr %w0, [%1], #4\n"
> + : "+r"(xa), "+r"(ptr)
> + :
> + : "memory");
> + /* After ldapr-1. */
> +
> + PREPARE_FOR_STORE (8);
> + /* Before stlr-0. */
> + __asm__ volatile ("stlr %1, [%0, #-8]!\n"
> + : "+r"(ptr)
> + : "r"(xa)
> + : "memory");
> + /* After stlr-0. */
> +
> + PREPARE_FOR_STORE (4);
> + /* Before stlr-1. */
> + __asm__ volatile ("stlr %w1, [%0, #-4]!\n"
> + : "+r"(ptr)
> + : "r"(xa)
> + : "memory");
> + /* After stlr-1. */
> +
> + PREPARE_FOR_VECTOR_LOAD (0);
> + /* Before ldap1-0. */
> + __asm__ volatile ("ldap1 {%0.d}[0], [%1]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldap1-0. */
> +
> + PREPARE_FOR_VECTOR_STORE (0);
> + /* Before stl1-0. */
> + __asm__ volatile ("stl1 {%0.d}[0], [%1]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stl1-0. */
> +
> + PREPARE_FOR_VECTOR_LOAD (0);
> + /* Before ldapur-0. */
> + __asm__ volatile ("ldapur %d0, [%1]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-0. */
> +
> + PREPARE_FOR_VECTOR_STORE (0);
> + /* Before stlur-0. */
> + __asm__ volatile ("stlur %d0, [%1]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-0. */
> +
> + PREPARE_FOR_VECTOR_LOAD (256);
> + /* Before ldapur-1. */
> + __asm__ volatile ("ldapur %d0, [%1, #-256]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-1. */
> +
> + PREPARE_FOR_VECTOR_STORE (256);
> + /* Before stlur-1. */
> + __asm__ volatile ("stlur %d0, [%1, #-256]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-1. */
> +
> + PREPARE_FOR_VECTOR_LOAD (-255);
> + /* Before ldapur-2. */
> + __asm__ volatile ("ldapur %d0, [%1, #255]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-2. */
> +
> + PREPARE_FOR_VECTOR_STORE (-255);
> + /* Before stlur-2. */
> + __asm__ volatile ("stlur %d0, [%1, #255]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-2. */
> +
> + PREPARE_FOR_VECTOR_LOAD (0);
> + /* Before ldapur-3. */
> + __asm__ volatile ("ldapur %h0, [%1]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-3. */
> +
> + PREPARE_FOR_VECTOR_STORE (0);
> + /* Before stlur-3. */
> + __asm__ volatile ("stlur %h0, [%1]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-3. */
> +
> + PREPARE_FOR_VECTOR_LOAD (0);
> + /* Before ldapur-4. */
> + __asm__ volatile ("ldapur %s0, [%1]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-4. */
> +
> + PREPARE_FOR_VECTOR_STORE (0);
> + /* Before stlur-4. */
> + __asm__ volatile ("stlur %s0, [%1]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-4. */
> +
> + PREPARE_FOR_VECTOR_LOAD (0);
> + /* Before ldapur-5. */
> + __asm__ volatile ("ldapur %d0, [%1]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-5. */
> +
> + PREPARE_FOR_VECTOR_STORE (0);
> + /* Before stlur-5. */
> + __asm__ volatile ("stlur %d0, [%1]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-5. */
> +
> + PREPARE_FOR_VECTOR_LOAD (0);
> + /* Before ldapur-6. */
> + __asm__ volatile ("ldapur %q0, [%1]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-6. */
> +
> + PREPARE_FOR_VECTOR_STORE (0);
> + /* Before stlur-6. */
> + __asm__ volatile ("stlur %q0, [%1]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-6. */
> +
> + return 0;
> +}
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
> new file mode 100644
> index 00000000000..7d7a8c45445
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
> @@ -0,0 +1,203 @@
> +# Copyright 2024-2026 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/>.
> +
> +# Test instruction record for AArch64 FEAT_LRCPC3 instructions.
> +# Based on gdb.reverse/aarch64-mops.exp
> +#
> +# The basic flow of the record tests are:
> +# 1) Stop before executing the instructions of interest. Record
> +# the initial value of the registers that the instruction will
> +# change, i.e. the destination register.
> +# 2) Execute the instructions. Record the new value of the
> +# registers that changed.
> +# 3) Reverse the direction of the execution and execute back to
> +# just before the instructions of interest. Record the final
> +# value of the registers of interest.
> +# 4) Check that the initial and new values of the registers are
> +# different, i.e. the instruction changed the registers as expected.
> +# 5) Check that the initial and final values of the registers are
> +# the same, i.e. GDB record restored the registers to their
> +# original values.
> +
> +require allow_aarch64_lrcpc3_tests
> +
> +standard_testfile
> +
> +if {
> + [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
> + [list debug additional_flags=-march=armv8-a+rcpc3]]
> +} {
> + return -1
> +}
> +
> +if {![runto_main]} {
> + return -1
> +}
> +
> +gdb_test_no_output "record full"
> +
> +proc test_single_asm {name diff_reg diff_var diff_mem same_reg same_var same_mem} {
> + global decimal hex
> +> + set before_seq [gdb_get_line_number "Before ${name}"]
> + set after_seq [gdb_get_line_number "After ${name}"]
> +
> + set insn [lindex [split $name "-"] 0]
> +
> + gdb_test "break $before_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lrcpc3.c, line $decimal\\." \
I think we prefer to use $::decimal nowadays for a global.
> + "$name: break before instruction sequence"
> + gdb_continue_to_breakpoint "$name: about to execute instruction sequence" \
> + [multi_line ".*/aarch64-lrcpc3.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
> +
> + # Depending on the compiler, the line number information may put GDB a few
> + # instructions before the beginning of the asm statement.
> + arrive_at_instruction $insn
> + # Add a breakpoint that we're sure is at the prologue instruction.
> + gdb_test "break *\$pc" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lrcpc3.c, line $decimal\\." \
> + "$name: break at prologue instruction"
> +
> + # Record the initial memory and register values.
> + set regs [concat $diff_reg $same_reg]
> + set vars [concat $diff_var $same_var]
> + set mem [concat $diff_mem $same_mem]
> + foreach r $regs {
> + set ${r}_initial [capture_command_output "info register $r" ""]
> + }
> + foreach v $vars {
> + set ${v}_initial [get_valueof "/x" "$v" "unable to read $v" \
> + "$name: get $v initial value"]
> + }
> + foreach m $mem {
> + set ${m}_initial [capture_command_output "x/x $m" ""]
> + }
> +
> + gdb_test "break $after_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lrcpc3.c, line $decimal\\." \
> + "$name: break after instruction sequence"
> + gdb_continue_to_breakpoint "$name: executed instruction sequence" \
> + [multi_line ".*/aarch64-lrcpc3.c:$decimal" ".*"]
> +
> + # Record the new memory and register values.
> + foreach r $regs {
> + set ${r}_new [capture_command_output "info register $r" ""]
> + }
> + foreach v $vars {
> + set ${v}_new [get_valueof "/x" "$v" "unable to read $v" \
> + "$name: get $v new value"]
> + }
> + foreach m $mem {
> + set ${m}_new [capture_command_output "x/x $m" ""]
> + }
> +
> + # Execute in reverse to before the instruction sequence.
> + gdb_test_no_output "set exec-direction reverse"
> +
> + gdb_continue_to_breakpoint "reversed execution of instruction sequence" \
> + [multi_line ".*/aarch64-lrcpc3.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
> +
> + # Record the final memory and register values.
> + foreach r $regs {
> + set ${r}_final [capture_command_output "info register $r" ""]
> + }
> + foreach v $vars {
> + set ${v}_final [get_valueof "/x" "$v" "unable to read $v" \
> + "$name: get $v final value"]
> + }
> + foreach m $mem {
> + set ${m}_final [capture_command_output "x/x $m" ""]
> + }
> +
> + foreach v [concat $same_reg $same_var $same_mem] {
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_new]] \
> + "$name: check $v initial value versus $v new value"
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
> + "$name: check $v initial value versus $v final value"
> + }
> +
> + foreach v [concat $diff_reg $diff_var $diff_mem] {
> + gdb_assert [string compare [set ${v}_initial] [set ${v}_new]] \
> + "$name: check $v initial value versus $v new value"
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
> + "$name: check $v initial value versus $v final value"
> + }
> +
> + # Restore forward execution and go to end of recording.
> + gdb_test_no_output "set exec-direction forward"
> + gdb_test "record goto end" \
> + [multi_line \
> + "Go forward to insn number $decimal" \
> + "#0 main \\(\\) at .*/aarch64-lrcpc3.c:$decimal" \
> + ".*"]
> +}
> +
> +set ldiapp_cases {
> + { ldiapp-0 { x19 x20 } { } { } { } { ptr } { src } }
> + { ldiapp-1 { w19 w20 } { } { } { } { ptr } { src } }
> + { ldiapp-2 { x19 x20 } { ptr } { } { } { } { src } }
> + { ldiapp-3 { w19 w20 } { ptr } { } { } { } { src } }
> + { ldiapp-4 { x21 x20 } { } { } { } { } { src } }
> + { ldiapp-5 { w21 w20 } { } { } { } { } { src } }
> +}
> +
> +set stilp_cases {
> + { stilp-0 { } { } { src } { x19 x20 } { ptr } { } }
> + { stilp-1 { } { } { src } { w19 w20 } { ptr } { } }
> + { stilp-2 { } { ptr } { src } { x19 x20 } { } { } }
> + { stilp-3 { } { ptr } { src } { x19 x20 } { } { } }
> + { stilp-4 { } { } { src } { x20 } { ptr } { } }
> + { stilp-5 { } { } { src } { w20 } { ptr } { } }
> +}
> +
> +set ldapr_stlr_cases {
> + { ldapr-0 { x19 } { ptr } { } { } { } { src } }
> + { ldapr-1 { w19 } { ptr } { } { } { } { src } }
> + { stlr-0 { } { ptr } { src } { x19 } { } { } }
> + { stlr-1 { } { ptr } { src } { w19 } { } { } }
> +}
> +
> +set ldap1_stl1_cases {
> + { ldap1-0 { v22 } { } { } { ptr } { } { src } }
> + { stl1-0 { } { } { src } { v22 } { } { } }
> +}
> +
> +set ldapur_stlur_cases {
> + { ldapur-0 { v22 } { } { } { ptr } { } { src } }
> + { stlur-0 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-1 { v22 } { } { } { ptr } { } { src } }
> + { stlur-1 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-2 { v22 } { } { } { ptr } { } { src } }
> + { stlur-2 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-3 { v22 } { } { } { ptr } { } { src } }
> + { stlur-3 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-4 { v22 } { } { } { ptr } { } { src } }
> + { stlur-4 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-5 { v22 } { } { } { ptr } { } { src } }
> + { stlur-5 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-6 { v22 } { } { } { ptr } { } { src } }
> + { stlur-6 { } { } { src } { v22 } { ptr } { } }
> +}
> +
> +set all_cases [concat \
> + $ldiapp_cases $stilp_cases $ldapr_stlr_cases \
> + $ldap1_stl1_cases $ldapur_stlur_cases]
> +
> +foreach c $all_cases {
> + lassign $c name diff_reg diff_var diff_mem same_reg same_var same_mem
> + test_single_asm $name $diff_reg $diff_var $diff_mem $same_reg $same_var $same_mem
> +}
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 28709004570..c6afc556548 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -5287,6 +5287,69 @@ gdb_caching_proc allow_aarch64_fpmr_tests {} {
> return $allow_fpmr_tests
> }
>
> +# Run a test on the target to see if it supports AArch64 LRCPC3 (Load-Acquire
> +# RCpc instructions) extensions. Return 1 if so, 0 if it does not. Note this
> +# causes a restart of GDB.
> +
> +gdb_caching_proc allow_aarch64_lrcpc3_tests {} {
> + global srcdir subdir gdb_prompt inferior_exited_re
> +
> + set me "allow_aarch64_lrcpc3_tests"
> +
> + if { ![is_aarch64_target] } {
> + return 0
> + }
> +
> + # Take the opportunity to check whether the toolchain knows about LRCPC3.
> + set compile_flags "{additional_flags=-march=armv8-a+rcpc3}"
> +
> + # Compile a program that tests the LRCPC3 feature.
> + set src {
> + #include <stdint.h>
> +
> + int main() {
> + uint64_t a, b = 0;
> + uint64_t *ptr;
> + *ptr = 0xdeadbeef;
> +
> + __asm__ volatile ("ldiapp %0, %1, [%2]\n"
> + : "=r"(a), "=r"(b)
> + : "r"(ptr)
> + : "memory");
> +
> + return 0;
> + }
> + }
> +
> + if {![gdb_simple_compile $me $src executable $compile_flags]} {
> + return 0
> + }
> +
> + # Compilation succeeded so now run it via gdb.
> + clean_restart
> + gdb_load $obj
> + gdb_run_cmd
> + gdb_expect {
> + -re ".*Illegal instruction.*${gdb_prompt} $" {
> + verbose -log "\n$me lrcpc3 support not detected"
> + set allow_lrcpc3_tests 0
> + }
> + -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
> + verbose -log "\n$me: lrcpc3 support detected"
> + set allow_lrcpc3_tests 1
> + }
> + default {
> + warning "\n$me: default case taken"
> + set allow_lrcpc3_tests 0
> + }
> + }
> + gdb_exit
> + remote_file build delete $obj
> +
> + verbose "$me: returning $allow_lrcpc3_tests" 2
> + return $allow_lrcpc3_tests
> +}
> +
> # 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.
On a first pass, a lot of mechanical changes, but looks reasonable to me
minus the global variable nit. I'm assuming tests have passed on the FM.
I'll wait for Guinevere to chime in.
Reviewed-By: Luis Machado <luis.machado.foss@gmail.com>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 2/4] gdb/aarch64: Test record/replay support for CSSC
2026-04-20 21:52 ` [PATCH 2/4] gdb/aarch64: Test record/replay support for CSSC Ezra.Sitorus
@ 2026-04-24 7:07 ` Luis
2026-04-24 16:53 ` Guinevere Larsen
` (2 subsequent siblings)
3 siblings, 0 replies; 23+ messages in thread
From: Luis @ 2026-04-24 7:07 UTC (permalink / raw)
To: Ezra.Sitorus, gdb-patches; +Cc: thiago.bauermann
On 20/04/2026 22:52, Ezra.Sitorus@arm.com wrote:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> This patch checks that the handling of CSSC instructions is correct.
> There is no explicit support for handling these instructions which is
> why this patch is composed of tests.
>
> Regression tested on aarch64-none-linux-gnu on QEMU with CSSC support.
> I observe unrelated regressions that appear to be environmental rather
> than caused by this patch.
> ---
> gdb/testsuite/gdb.reverse/aarch64-cssc.c | 113 +++++++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-cssc.exp | 141 +++++++++++++++++++++
> gdb/testsuite/lib/gdb.exp | 52 ++++++++
> 3 files changed, 306 insertions(+)
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.exp
>
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-cssc.c b/gdb/testsuite/gdb.reverse/aarch64-cssc.c
> new file mode 100644
> index 00000000000..d80d202590c
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-cssc.c
> @@ -0,0 +1,113 @@
> +/* This test program is part of GDB, the GNU debugger.
> +
> + Copyright 2024-2026 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/>. */
> +
> +#include <stdint.h>
> +
> +#define PREPARE_REGS(X, Y, Z) \
> + __asm__ volatile ("mov %0, %1\n" : "=r"(xa): "r"(X):); \
> + __asm__ volatile ("mov %0, %1\n" : "=r"(xb): "r"(Y):); \
> + __asm__ volatile ("mov %0, %1\n" : "=r"(xc): "r"(Z):); \
> +
> +int
> +main (void)
> +{
> + register uint64_t xa asm ("x19");
> + register uint32_t xb asm ("x20");
> + register uint64_t xc asm ("x21");
> + const uint64_t a = 0x0123456789abcdef;
> + const uint64_t b = 0xfedbca9876543210;
> + const uint64_t c = 0xdeadbeefc001face;
> +
> + PREPARE_REGS (a, b, c);
> + /* Before abs. */
> + __asm__ volatile ("abs %0, %1\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After abs. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before cnt. */
> + __asm__ volatile ("cnt %0, %1\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After cnt. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before ctz. */
> + __asm__ volatile ("ctz %0, %1\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After ctz. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before smax-1. */
> + __asm__ volatile ("smax %0, %1, #10\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After smax-1. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before smax-2. */
> + __asm__ volatile ("smax %0, %1, %2\n"
> + : "+r"(xa)
> + : "r"(xb), "r"(xc));
> + /* After smax-2. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before smin-1. */
> + __asm__ volatile ("smin %0, %1, #10\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After smin-1. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before smin-2. */
> + __asm__ volatile ("smin %0, %1, %2\n"
> + : "+r"(xa)
> + : "r"(xb), "r"(xc));
> + /* After smin-2. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before umax-1. */
> + __asm__ volatile ("umax %0, %1, #10\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After umax-1. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before umax-2. */
> + __asm__ volatile ("umax %0, %1, %2\n"
> + : "+r"(xa)
> + : "r"(xb), "r"(xc));
> + /* After umax-2. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before umin-1. */
> + __asm__ volatile ("umin %0, %1, #10\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After umin-1. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before umin-2. */
> + __asm__ volatile ("umin %0, %1, %2\n"
> + : "+r"(xa)
> + : "r"(xb), "r"(xc));
> + /* After umin-2. */
> +
> + return 0;
> +}
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-cssc.exp b/gdb/testsuite/gdb.reverse/aarch64-cssc.exp
> new file mode 100644
> index 00000000000..640ea4ae19f
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-cssc.exp
> @@ -0,0 +1,141 @@
> +# Copyright 2024-2026 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/>.
> +
> +# Test instruction record for AArch64 FEAT_CSSC instructions.
> +# Based on gdb.reverse/aarch64-mops.exp
> +#
> +# The basic flow of the record tests are:
> +# 1) Stop before executing the instructions of interest. Record
> +# the initial value of the registers that the instruction will
> +# change, i.e. the destination register.
> +# 2) Execute the instructions. Record the new value of the
> +# registers that changed.
> +# 3) Reverse the direction of the execution and execute back to
> +# just before the instructions of interest. Record the final
> +# value of the registers of interest.
> +# 4) Check that the initial and new values of the registers are
> +# different, i.e. the instruction changed the registers as expected.
> +# 5) Check that the initial and final values of the registers are
> +# the same, i.e. GDB record restored the registers to their
> +# original values.
> +
Not for this series, but if we're going to repeat these entries, we
might split off the common parts of Arm record/replay testing and
include them in testcases that only have unique logic.
> +require allow_aarch64_cssc_tests
> +standard_testfile
> +
> +if {
> + [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
> + [list debug additional_flags=-march=armv8-a+cssc]]
> +} {
> + return -1
> +}
> +
> +if {![runto_main]} {
> + return -1
> +}
> +
> +gdb_test_no_output "record full"
> +
> +proc test_single_asm { name } {
> + global decimal hex
> +
> + set before_seq [gdb_get_line_number "Before ${name}"]
> + set after_seq [gdb_get_line_number "After ${name}"]
> + set regs { x19 x20 x21 }
> +
> + set insn [lindex [split $name "-"] 0]
> +
> + gdb_test "break $before_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-cssc.c, line $decimal\\." \
> + "$insn: break before instruction sequence"
> + gdb_continue_to_breakpoint "$insn: about to execute instruction sequence" \
> + [multi_line ".*/aarch64-cssc.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
> +
> + # Depending on the compiler, the line number information may put GDB a few
> + # instructions before the beginning of the asm statement.
> + arrive_at_instruction $insn
> + # Add a breakpoint that we're sure is at the prologue instruction.
> + gdb_test "break *\$pc" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-cssc.c, line $decimal\\." \
> + "$insn: break at prologue instruction"
> +
> + # Record the initial register values.
> + foreach r $regs {
> + set ${r}_initial [capture_command_output "info register $r" ""]
> + }
> +
> + gdb_test "break $after_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-cssc.c, line $decimal\\." \
> + "$insn: break after instruction sequence"
> + gdb_continue_to_breakpoint "$insn: executed instruction sequence" \
> + [multi_line ".*/aarch64-cssc.c:$decimal" ".*"]
> +
> + # Record the new register values.
> + foreach r $regs {
> + set ${r}_new [capture_command_output "info register $r" ""]
> + }
> +
> + # Execute in reverse to before the instruction sequence.
> + gdb_test_no_output "set exec-direction reverse"
> +
> + gdb_continue_to_breakpoint "reversed execution of instruction sequence" \
> + [multi_line ".*/aarch64-cssc.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
> +
> + # Record the final register values.
> + foreach r $regs {
> + set ${r}_final [capture_command_output "info register $r" ""]
> + }
> +
> + foreach v { x19 x20 x21 } {
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
> + "$insn: check $v initial value versus $v final value"
> + }
> +
> + gdb_assert [string compare [set x19_initial] [set x19_new]] \
> + "$insn: check x19 initial value versus x19 new value"
> +
> + foreach v { x20 x21 } {
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_new]] \
> + "$insn: check $v initial value versus $v new value"
> + }
> +
> + # Restore forward execution and go to end of recording.
> + gdb_test_no_output "set exec-direction forward"
> + gdb_test "record goto end" \
> + [multi_line \
> + "Go forward to insn number $decimal" \
> + "#0 main \\(\\) at .*/aarch64-cssc.c:$decimal" \
> + ".*"]
> +}
> +
> +set cases {
> + { abs }
> + { cnt }
> + { ctz }
> + { smax-1 }
> + { smax-2 }
> + { smin-1 }
> + { smin-2 }
> + { umax-1 }
> + { umax-2 }
> + { umin-1 }
> + { umin-2 }
> +}
> +
> +foreach c $cases {
> + lassign $c insn
> + test_single_asm $insn
> +}
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index c6afc556548..7d400e9028a 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -5350,6 +5350,58 @@ gdb_caching_proc allow_aarch64_lrcpc3_tests {} {
> return $allow_lrcpc3_tests
> }
>
> +# Run a test on the target to see if it supports the AArch64 CSSC feature.
> +# Return 1 if so, 0 if it does not. Note this causes a restart of GDB.
> +
> +gdb_caching_proc allow_aarch64_cssc_tests {} {
> + global srcdir subdir gdb_prompt inferior_exited_re
> +
> + set me "allow_aarch64_cssc_tests"
> +
> + if { ![is_aarch64_target]} {
> + return 0
> + }
> +
> + set compile_flags "{additional_flags=-march=armv8-a+cssc}"
> +
> + # Compile a test program reading CSSC.
> + set src {
> + int main() {
> + asm volatile ("abs x0, x1");
> + return 0;
> + }
> + }
> +
> + if {![gdb_simple_compile $me $src executable $compile_flags]} {
> + return 0
> + }
> +
> + # Compilation succeeded so now run it via gdb.
> + clean_restart
> + gdb_load $obj
> + gdb_run_cmd
> +
> + gdb_expect {
> + -re ".*Illegal instruction.*${gdb_prompt} $" {
> + verbose -log "\n$me cssc support not detected"
> + set allow_cssc_tests 0
> + }
> + -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
> + verbose -log "\n$me: cssc support detected"
> + set allow_cssc_tests 1
> + }
> + default {
> + warning "\n$me: default case taken"
> + set allow_cssc_tests 0
> + }
> + }
> + gdb_exit
> + remote_file build delete $obj
> +
> + verbose "$me: returning $allow_cssc_tests" 2
> + return $allow_cssc_tests
> +}
> +
Again, the same for this function. To avoid proliferating duplicated
content, we could have a main body that does the testing based on
arguments passed, like source file and compiler flags.
> # 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.
Looks reasonable to me, assuming these have passed on QEMU.
I'll wait for Guinevere's input.
Reviewed-By: Luis Machado <luis.machado.foss@gmail.com>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 3/4] gdb/aarch64: record/replay support for RPRFM and PRFM (reg)
2026-04-20 21:52 ` [PATCH 3/4] gdb/aarch64: record/replay support for RPRFM and PRFM (reg) Ezra.Sitorus
@ 2026-04-24 7:08 ` Luis
2026-04-24 16:56 ` Guinevere Larsen
2026-04-25 8:12 ` Thiago Jung Bauermann
2 siblings, 0 replies; 23+ messages in thread
From: Luis @ 2026-04-24 7:08 UTC (permalink / raw)
To: Ezra.Sitorus, gdb-patches; +Cc: thiago.bauermann
On 20/04/2026 22:52, Ezra.Sitorus@arm.com wrote:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> The PRFM (register) instruction variant was unsupported. This is added
> along with RPRFM.
>
> No testcase has been added as these are hint instructions which don't
> modify registers/memory. I'm happy to add them in if you think these are
> required.
>
> The full testsuite was done on aarch64-none-linux-gnu without RPRFM.
> The gdb.arch and gdb.reverse tests were run on Shrinkwrap with RPRFM
> support. I've observed unrelated regressions that appear to be due to
> Shrinkwrap rather than my patch.
> ---
> gdb/aarch64-tdep.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index 81d4f160f8f..61c462979b3 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -5914,6 +5914,11 @@ aarch64_record_load_store (aarch64_insn_decode_record *aarch64_insn_r)
> else
> if (size_bits != 0x03)
> ld_flag = 0x01;
> + else if (size_bits == 0x3 && vector_flag == 0x0 && opc == 0x2)
> + {
> + /* PRFM (register) or RPRFM */
> + return AARCH64_RECORD_SUCCESS;
> + }
> else
> return AARCH64_RECORD_UNKNOWN;
>
Thanks. This is OK.
Approved-By: Luis Machado <luis.machado.foss@gmail.com>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 4/4] gdb/aarch64: record/replay support for LSE128
2026-04-20 21:52 ` [PATCH 4/4] gdb/aarch64: record/replay support for LSE128 Ezra.Sitorus
@ 2026-04-24 7:11 ` Luis
2026-04-24 17:12 ` Guinevere Larsen
2026-04-25 8:38 ` Thiago Jung Bauermann
2 siblings, 0 replies; 23+ messages in thread
From: Luis @ 2026-04-24 7:11 UTC (permalink / raw)
To: Ezra.Sitorus, gdb-patches; +Cc: thiago.bauermann
On 20/04/2026 22:52, Ezra.Sitorus@arm.com wrote:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> FEAT_LSE128 introduces support for 128-bit atomic instructions. This
> patch teaches GDB to decode these instructions for recording and
> reversing.
>
> Regression tested on aarch64-none-linux-gnu on QEMU with LSE128 support.
> I observe unrelated regressions that appear to be environmental rather
> than caused by this patch.
> ---
> gdb/aarch64-tdep.c | 13 ++
> gdb/testsuite/gdb.reverse/aarch64-lse128.c | 139 +++++++++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-lse128.exp | 138 ++++++++++++++++++
> gdb/testsuite/lib/gdb.exp | 56 ++++++++
> 4 files changed, 346 insertions(+)
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.exp
>
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index 61c462979b3..aff8b294bd5 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -6004,6 +6004,19 @@ aarch64_record_load_store (aarch64_insn_decode_record *aarch64_insn_r)
> else if ((insn_bits24_27 & 1) == 1 && insn_bits28_29 == 1
> && insn_bits10_11 == 1 && !insn_bit21)
> return aarch64_record_memcopy_memset (aarch64_insn_r);
> + /* Large System Extension 128 (LSE128) instructions. */
> + else if (vector_flag == 0 && insn_bits10_11 == 0 && insn_bit21
> + && size_bits == 0 && !bit (aarch64_insn_r->aarch64_insn, 29)
> + && bits (aarch64_insn_r->aarch64_insn, 24, 25) == 0x1)
> + {
> + regcache_raw_read_unsigned (aarch64_insn_r->regcache, reg_rn, &address);
> + record_buf_mem[0] = 128 >> 3;
> + record_buf_mem[1] = address;
> + aarch64_insn_r->mem_rec_count = 1;
> + record_buf[0] = reg_rt;
> + record_buf[1] = bits (aarch64_insn_r->aarch64_insn, 16, 20);
> + aarch64_insn_r->reg_rec_count = 2;
> + }
> /* Advanced SIMD load/store instructions. */
> else
> return aarch64_record_asimd_load_store (aarch64_insn_r);
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-lse128.c b/gdb/testsuite/gdb.reverse/aarch64-lse128.c
> new file mode 100644
> index 00000000000..906c303d904
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-lse128.c
> @@ -0,0 +1,139 @@
> +/* This test program is part of GDB, the GNU debugger.
> +
> + Copyright 2024-2026 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/>. */
> +
> +#include <stdint.h>
> +#include <stdalign.h>
> +#include <string.h>
> +
> +#define INITIAL_STRING "This is just some string."
> +#define BUF_SIZE sizeof (INITIAL_STRING)
> +
> +#define PREPARE_REGS(X, Y) \
> + strcpy (src, INITIAL_STRING); \
> + __asm__ volatile ("mov %0, %1\n" : "=r"(xa): "r"(X):); \
> + __asm__ volatile ("mov %0, %1\n" : "=r"(xb): "r"(Y):); \
> + ptr = (uint64_t *)src; \
> +
> +int
> +main (void)
> +{
> + alignas(16) char src[BUF_SIZE];
> +
> + register uint64_t xa asm ("x19");
> + register uint64_t xb asm ("x20");
> + register uint64_t *ptr asm ("x21");
> + uint64_t a = 0x0123456789abcdef;
> + uint64_t b = 0xfedbca9876543210;
> +
> + PREPARE_REGS (a, b);
> + /* Before ldclrp. */
> + __asm__ volatile ("ldclrp %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldclrp. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldclrpa. */
> + __asm__ volatile ("ldclrpa %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldclrpa. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldclrpal. */
> + __asm__ volatile ("ldclrpal %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldclrpal. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldclrpl. */
> + __asm__ volatile ("ldclrpl %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldclrpl. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldsetp. */
> + __asm__ volatile ("ldsetp %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldsetp. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldsetpa. */
> + __asm__ volatile ("ldsetpa %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldsetpa. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldsetpal. */
> + __asm__ volatile ("ldsetpal %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldsetpal. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldsetpl. */
> + __asm__ volatile ("ldsetpl %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldsetpl. */
> +
> + PREPARE_REGS (a, b);
> + /* Before swpp. */
> + __asm__ volatile ("swpp %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After swpp. */
> +
> + PREPARE_REGS (a, b);
> + /* Before swppa. */
> + __asm__ volatile ("swppa %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After swppa. */
> +
> + PREPARE_REGS (a, b);
> + /* Before swppal. */
> + __asm__ volatile ("swppal %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After swppal. */
> +
> + PREPARE_REGS (a, b);
> + /* Before swppl. */
> + __asm__ volatile ("swppl %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After swppl. */
> +
> + return 0;
> +}
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-lse128.exp b/gdb/testsuite/gdb.reverse/aarch64-lse128.exp
> new file mode 100644
> index 00000000000..273e239ec66
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-lse128.exp
> @@ -0,0 +1,138 @@
> +# Copyright 2024-2026 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/>.
> +
> +# Test instruction record for AArch64 FEAT_LSE128 instructions.
> +# Based on gdb.reverse/aarch64-mops.exp
> +#
> +# The basic flow of the record tests are:
> +# 1) Stop before executing the instructions of interest. Record
> +# the initial value of the registers that the instruction will
> +# change, i.e. the destination register.
> +# 2) Execute the instructions. Record the new value of the
> +# registers that changed.
> +# 3) Reverse the direction of the execution and execute back to
> +# just before the instructions of interest. Record the final
> +# value of the registers of interest.
> +# 4) Check that the initial and new values of the registers are
> +# different, i.e. the instruction changed the registers as expected.
> +# 5) Check that the initial and final values of the registers are
> +# the same, i.e. GDB record restored the registers to their
> +# original values.
> +
> +standard_testfile
> +
> +require allow_aarch64_lse128_tests
> +
> +if {
> + [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
> + [list debug additional_flags=-march=armv8-a+lse128]]
> +} {
> + return -1
> +}
> +
> +if {![runto_main]} {
> + return -1
> +}
> +
> +gdb_test_no_output "record full"
> +
> +proc test_single_asm {insn} {
> + global decimal hex
> +
> + set before_seq [gdb_get_line_number "Before ${insn}"]
> + set after_seq [gdb_get_line_number "After ${insn}"]
> + set regs { x19 x20 }
> +
> + gdb_test "break $before_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lse128.c, line $decimal\\." \
> + "$insn: break before instruction sequence"
> + gdb_continue_to_breakpoint "$insn: about to execute instruction sequence" \
> + [multi_line ".*/aarch64-lse128.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
> +
> + # Depending on the compiler, the line number information may put GDB a few
> + # instructions before the beginning of the asm statement.
> + arrive_at_instruction $insn
> + # Add a breakpoint that we're sure is at the prologue instruction.
> + gdb_test "break *\$pc" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lse128.c, line $decimal\\." \
> + "$insn: break at prologue instruction"
> +
> + # Record the initial memory and register values.
> + foreach r $regs {
> + set ${r}_initial [capture_command_output "info register $r" ""]
> + }
> + set ptr_initial [capture_command_output "x/4gwx ptr" ""]
> +
> + gdb_test "break $after_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lse128.c, line $decimal\\." \
> + "$insn: break after instruction sequence"
> + gdb_continue_to_breakpoint "$insn: executed instruction sequence" \
> + [multi_line ".*/aarch64-lse128.c:$decimal" ".*"]
> +
> + # Record the new memory and register values.
> + foreach r $regs {
> + set ${r}_new [capture_command_output "info register $r" ""]
> + }
> + set ptr_new [capture_command_output "x/4gwx ptr" ""]
> +
> + # Execute in reverse to before the instruction sequence.
> + gdb_test_no_output "set exec-direction reverse"
> +
> + gdb_continue_to_breakpoint "reversed execution of instruction sequence" \
> + [multi_line ".*/aarch64-lse128.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
> +
> + # Record the final memory and register values.
> + foreach r $regs {
> + set ${r}_final [capture_command_output "info register $r" ""]
> + }
> + set ptr_final [capture_command_output "x/4gwx ptr" ""]
> +
> + foreach v [concat $regs {ptr}] {
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
> + "$insn: check $v initial value versus $v final value"
> + gdb_assert [string compare [set ${v}_initial] [set ${v}_new]] \
> + "$insn: check $v initial value versus $v new value"
> + }
> +
> + # Restore forward execution and go to end of recording.
> + gdb_test_no_output "set exec-direction forward"
> + gdb_test "record goto end" \
> + [multi_line \
> + "Go forward to insn number $decimal" \
> + "#0 main \\(\\) at .*/aarch64-lse128.c:$decimal" \
> + ".*"]
> +}
> +
> +set cases {
> + { ldclrp }
> + { ldclrpa }
> + { ldclrpal }
> + { ldclrpl }
> + { ldsetp }
> + { ldsetpa }
> + { ldsetpal }
> + { ldsetpl }
> + { swpp }
> + { swppa }
> + { swppal }
> + { swppl }
> +}
> +
> +foreach c $cases {
> + lassign $c insn
> + test_single_asm $insn
> +}
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 7d400e9028a..d5f9008470a 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -5402,6 +5402,62 @@ gdb_caching_proc allow_aarch64_cssc_tests {} {
> return $allow_cssc_tests
> }
>
> +# Run a test on the target to see if it supports the AArch64 LSE128 feature.
> +# Return 1 if so, 0 if it does not. Note this causes a restart of GDB.
> +
> +gdb_caching_proc allow_aarch64_lse128_tests {} {
> + global srcdir subdir gdb_prompt inferior_exited_re
> +
> + set me "allow_aarch64_lse128_tests"
> +
> + if { ![is_aarch64_target]} {
> + return 0
> + }
> +
> + set compile_flags "{additional_flags=-march=armv8-a+lse128}"
> +
> + # Compile a test program reading LSE128.
> + set src {
> + #include <stdint.h>
> +
> + int main() {
> + __attribute__((aligned(16))) uint64_t mem[2] = { 0x0, 0x1 };
> + uint64_t *ptr = mem;
> + __asm__ volatile ("ldclrp x0, x1, [%0]\n" :: "r"(ptr) : "x0", "x1", "memory");
> + return 0;
> + }
> + }
> +
> + if {![gdb_simple_compile $me $src executable $compile_flags]} {
> + return 0
> + }
> +
> + # Compilation succeeded so now run it via gdb.
> + clean_restart
> + gdb_load $obj
> + gdb_run_cmd
> +
> + gdb_expect {
> + -re ".*Illegal instruction.*${gdb_prompt} $" {
> + verbose -log "\n$me lse128 support not detected"
> + set allow_lse128_tests 0
> + }
> + -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
> + verbose -log "\n$me: lse128 support detected"
> + set allow_lse128_tests 1
> + }
> + default {
> + warning "\n$me: default case taken"
> + set allow_lse128_tests 0
> + }
> + }
> + gdb_exit
> + remote_file build delete $obj
> +
> + verbose "$me: returning $allow_lse128_tests" 2
> + return $allow_lse128_tests
> +}
> +
> # 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.
Looks OK to me as well. Waiting for Guinevere's input as before.
Reviewed-By: Luis Machado <luis.machado.foss@gmail.com>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 0/4] gdb/aarch64: record/replay support for AArch64
2026-04-20 21:52 [PATCH 0/4] gdb/aarch64: record/replay support for AArch64 Ezra.Sitorus
` (4 preceding siblings ...)
2026-04-23 17:31 ` [PATCH 0/4] gdb/aarch64: record/replay support for AArch64 Guinevere Larsen
@ 2026-04-24 7:16 ` Luis
5 siblings, 0 replies; 23+ messages in thread
From: Luis @ 2026-04-24 7:16 UTC (permalink / raw)
To: Ezra.Sitorus, gdb-patches; +Cc: thiago.bauermann
On 20/04/2026 22:52, Ezra.Sitorus@arm.com wrote:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> This patch series enables record/replay for the following AArch64
> features:
> * FEAT_LRCPC3 (+rcpc3)
> * FEAT_CSSC (+cssc)
> * FEAT_RPRFM (+rprfm)
> * FEAT_LSE128 (+lse128)
>
> These were tested either with QEMU or Shrinkwrap. In both cases, I seem
> to have test failures that are not related to my patches (gdb.threads,
> gdb.mi tests for example). In these cases, is it fair to ignore these?
> Or is there some underlying issue I've missed?
That's common. You'll also spot hardware watchpoint behavior differences
between QEMU and hardware/FM.
As long as things don´t regress overall, it should be fine to ignore the
flaky tests. You just need to know what they are, so that takes a bit of
time to get familiar.
>
> In addition, whenever I used QEMU, I disabled gcs because it seems to
> cause test failures - address/memory accesses seem to always cause a
> fault. Am I doing something wrong?
I wouldn´t expect that to happen, so there might be issues there.
Out of curiosity, as people use more and more LLVM, have you tried with
tests against LLVM to make sure they pass?
>
> Ezra
>
> Ezra Sitorus (4):
> gdb/aarch64: record/replay support for LRCPC3
> gdb/aarch64: Test record/replay support for CSSC
> gdb/aarch64: record/replay support for RPRFM and PRFM (reg)
> gdb/aarch64: record/replay support for LSE128
>
> gdb/aarch64-tdep.c | 105 ++++++
> gdb/testsuite/gdb.reverse/aarch64-cssc.c | 113 +++++++
> gdb/testsuite/gdb.reverse/aarch64-cssc.exp | 141 ++++++++
> gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c | 319 +++++++++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp | 203 ++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-lse128.c | 139 ++++++++
> gdb/testsuite/gdb.reverse/aarch64-lse128.exp | 138 ++++++++
> gdb/testsuite/lib/gdb.exp | 171 ++++++++++
> 8 files changed, 1329 insertions(+)
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.exp
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.exp
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/4] gdb/aarch64: record/replay support for LRCPC3
2026-04-20 21:52 ` [PATCH 1/4] gdb/aarch64: record/replay support for LRCPC3 Ezra.Sitorus
2026-04-24 7:00 ` Luis
@ 2026-04-24 15:09 ` Guinevere Larsen
2026-04-25 5:08 ` Thiago Jung Bauermann
2026-04-25 7:21 ` Thiago Jung Bauermann
3 siblings, 0 replies; 23+ messages in thread
From: Guinevere Larsen @ 2026-04-24 15:09 UTC (permalink / raw)
To: Ezra.Sitorus, gdb-patches; +Cc: luis.machado.foss, thiago.bauermann
On 4/20/26 6:52 PM, Ezra.Sitorus@arm.com wrote:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> FEAT_LRCPC3 introduces various load/store instructions with release
> consistency for cases where ordering is required. This patch teaches GDB
> to decode these instructions for recording and reversing.
>
> The gdb.reverse/aarch64-lrcpc3.exp testcase verifies that the
> instructions are recorded and correctly reversed. In particular, there
> are some interesting cases to note:
> * ldapur/stlur are SIMD instructions, but are not decoded in the simd
> function.
> * There are writeback cases to cover too. These were taken from the
> binutils testcases: gas/testsuite/gas/aarch64/rcpc3.s.
>
> The full testsuite was done on aarch64-none-linux-gnu without LRCPC3.
> The gdb.arch and gdb.reverse tests were run on Shrinkwrap with LRCPC3
> support.
>
> Please note:
> 1) There is no support for LRCPC and LRCPC2 instructions
> 2) LRCPC3 is gated with +rcpc3 in GCC/binutils.
> ---
Hi! Thanks for working on this!
I can't really comment on the aarch64-tdep bits, but I did notice a few
minor things in the test that could be improved.
> gdb/aarch64-tdep.c | 87 +++++
> gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c | 319 +++++++++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp | 203 ++++++++++++
> gdb/testsuite/lib/gdb.exp | 63 ++++
> 4 files changed, 672 insertions(+)
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
>
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index 4befaa2720d..81d4f160f8f 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -5812,6 +5812,93 @@ aarch64_record_load_store (aarch64_insn_decode_record *aarch64_insn_r)
> aarch64_insn_r->reg_rec_count = 1;
> }
> }
> + /* LRCPC3 instructions. This covers ldiapp/stilp, ldapur/stlur (FP/SIMD),
> + ldapr/stlr. */
> + else if ((insn_bits24_27 & 0x0b) == 0x09 && insn_bits28_29 == 0x01
> + && insn_bits10_11 == 0x02 && !insn_bit21)
> + {
> + /* ldapur/stlur (FP/SIMD), ldapr/stlr. We can differentiate between the
> + 2 types by checking the vector flag. */
> + if (insn_bit23 || vector_flag)
> + {
> + /* For the vector instruction, the offset comes from the imm9
> + bitfield, whereas the other can only take possible values from the
> + size bitfield. */
> + int16_t imm9_off = sbits (aarch64_insn_r->aarch64_insn, 12, 20);
> + offset = vector_flag ? imm9_off : -(1 << size_bits);
> + uint32_t regnum_offset = vector_flag ? AARCH64_V0_REGNUM : 0;
> + if (ld_flag)
> + {
> + record_buf[0] = reg_rt + regnum_offset;
> + aarch64_insn_r->reg_rec_count = 1;
> + if (!vector_flag)
> + {
> + /* The Rn register always has writeback in LRCPC3. This is
> + not the case in LRCPC. */
> + record_buf[1] = reg_rn;
> + aarch64_insn_r->reg_rec_count = 2;
> + }
> + }
> + else
> + {
> + regcache_raw_read_unsigned (aarch64_insn_r->regcache, reg_rn,
> + &address);
> + /* (vector_flag && insn_bit23) is the STLUR instruction with Q
> + register. */
> + datasize = (vector_flag && insn_bit23) ? 128 : (8 << size_bits);
> + /* LRCPC3 adds STLR with a pre-indexed offset. There is another
> + STLR variant without offset but this has a different encoding. */
> + if (!vector_flag)
> + {
> + record_buf[0] = reg_rn;
> + aarch64_insn_r->reg_rec_count = 1;
> + }
> + record_buf_mem[0] = datasize >> 3;
> + record_buf_mem[1] = address + offset;
> + aarch64_insn_r->mem_rec_count = 1;
> + }
> + }
> + else
> + {
> + /* ldiapp/stilp. */
> + uint8_t opc2 = bits (aarch64_insn_r->aarch64_insn, 12, 15);
> + reg_rt2 = bits (aarch64_insn_r->aarch64_insn, 16, 20);
> + if (ld_flag)
> + {
> + record_buf[0] = reg_rt;
> + record_buf[1] = reg_rt2;
> + aarch64_insn_r->reg_rec_count = 2;
> +
> + /* If the registers don't match and there's no offset then
> + there's WB. */
> + if (reg_rn != reg_rt && reg_rn != reg_rt2 && opc2 == 0)
> + {
> + record_buf[2] = reg_rn;
> + aarch64_insn_r->reg_rec_count = 3;
> + }
> + }
> + else
> + {
> + datasize = 8 << size_bits;
> + regcache_raw_read_unsigned (aarch64_insn_r->regcache, reg_rn,
> + &address);
> + offset = (opc2 == 0) ? (2 << size_bits) : 0;
> + address -= offset;
> +
> + record_buf_mem[0] = datasize >> 3;
> + record_buf_mem[1] = address;
> + record_buf_mem[2] = datasize >> 3;
> + record_buf_mem[3] = address + (datasize >> 3);
> + aarch64_insn_r->mem_rec_count = 2;
> +
> + if (offset != 0)
> + {
> + record_buf[0] = reg_rn;
> + aarch64_insn_r->reg_rec_count = 1;
> + }
> + }
> + }
> + }
> /* Load/store register (register offset) instructions. */
> else if ((insn_bits24_27 & 0x0b) == 0x08 && insn_bits28_29 == 0x03
> && insn_bits10_11 == 0x02 && insn_bit21)
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
> new file mode 100644
> index 00000000000..9265489ff70
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
> @@ -0,0 +1,319 @@
> +/* This test program is part of GDB, the GNU debugger.
> +
> + Copyright 2024-2026 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/>. */
> +
> +#include <stdint.h>
> +#include <stdalign.h>
> +#include <string.h>
> +
> +#define INITIAL_STRING "This is just some string."
> +#define BUF_SIZE sizeof (INITIAL_STRING)
> +
> +#define PREPARE_FOR_LOAD(OFFSET) \
> + __asm__ volatile ("mov %0, #0\n" : "=r"(xa)::); \
> + __asm__ volatile ("mov %0, #0\n" : "=r"(xb)::); \
> + strcpy (src, INITIAL_STRING); \
> + ptr = (uint64_t *)((uint8_t *)src + OFFSET); \
> +
> +#define PREPARE_FOR_VECTOR_LOAD(OFFSET) \
> + __asm__ volatile ("movi %0.2d, #0\n" : "=w"(va)::); \
> + strcpy (src, INITIAL_STRING); \
> + ptr = (uint64_t *)((uint8_t *)src + OFFSET); \
> +
> +#define PREPARE_FOR_STORE(OFFSET) \
> + __asm__ volatile ("mov %0, #0\n" : "=r"(xa)::); \
> + __asm__ volatile ("mov %0, #0\n" : "=r"(xb)::); \
> + strcpy (src, INITIAL_STRING); \
> + ptr = (uint64_t *)((uint8_t *)src + OFFSET); \
> +
> +#define PREPARE_FOR_VECTOR_STORE(OFFSET) \
> + __asm__ volatile ("movi %0.2d, #0\n" : "=w"(va)::); \
> + strcpy (src, INITIAL_STRING); \
> + ptr = (uint64_t *)((uint8_t *)src + OFFSET); \
All of these macros have the same strcpy and ptr assignment code in
them. I think it would be more clear if you made one macro for that part
and the rest either have that macro, or just be in the code directly.
> +
> +typedef uint64_t uint64x2_t __attribute__((vector_size(16)));
> +
> +int
> +main (void)
> +{
> + alignas(16) char src[BUF_SIZE];
> +
> + register uint64_t xa asm ("x19");
> + register uint32_t xb asm ("x20");
> + register uint64_t *ptr asm ("x21");
> + register uint64x2_t va asm ("v22");
The register keyword is a hint for the compiler, and has been deprecated
since c++11. I know that clang always ignores the keyword and I think
GCC is free to do so under some circumstances.
Considering that the rest of the test doesn't access the variables in
any way that isn't assembly code, you're free to write the registers
directly there. I did see that in the exp file you check for the value
of the variable, however, if you're already checking the register, there
isn't anything special that checking the variable would do, it is in
fact closer to double checking that the compiler actually did what you
told it to do. You can just drop the variable and examine only the
registers.
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldiapp-0. */
> + __asm__ volatile ("ldiapp %0, %1, [%2]\n"
> + : "=r"(xa), "=r"(xb)
> + : "r"(ptr)
> + : "memory");
For example, writing __asm__ volatile ("ldiapp x19, x20, [%0]", : : "r"
(ptr) : "memory");
> + /* After ldiapp-0. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldiapp-1. */
> + __asm__ volatile ("ldiapp %w0, %w1, [%2]\n"
> + : "=r"(xa), "=r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldiapp-1. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldiapp-2. */
> + __asm__ volatile ("ldiapp %0, %1, [%2], #16\n"
> + : "=r"(xa), "=r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldiapp-2. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldiapp-3. */
> + __asm__ volatile ("ldiapp %w0, %w1, [%2], #8\n"
> + : "=r"(xa), "=r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldiapp-3. */
> + /* Register overlap between source and destination registers. Since there is
> + no offset, writeback is disabled. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldiapp-4. */
> + __asm__ volatile ("ldiapp %0, %1, [%0]\n"
> + : "+r"(ptr), "=r"(xb)
> + :
> + : "memory");
> + /* After ldiapp-4. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldiapp-5. */
> + __asm__ volatile ("ldiapp %w0, %w1, [%0]\n"
> + : "+r"(ptr), "=r"(xb)
> + :
> + : "memory");
> + /* After ldiapp-5. */
> +
> + PREPARE_FOR_STORE (0);
> + /* Before stilp-0. */
> + __asm__ volatile ("stilp %0, %1, [%2]\n"
> + :
> + : "r"(xa), "r"(xb), "r"(ptr)
> + : "memory");
> + /* After stilp-0. */
> +
> + PREPARE_FOR_STORE (0);
> + /* Before stilp-1. */
> + __asm__ volatile ("stilp %w0, %w1, [%2]\n"
> + :
> + : "r"(xa), "r"(xb), "r"(ptr)
> + : "memory");
> + /* After stilp-1. */
> +
> + PREPARE_FOR_STORE (16);
> + /* Before stilp-2. */
> + __asm__ volatile ("stilp %1, %2, [%0, #-16]!\n"
> + : "+r"(ptr)
> + : "r"(xa), "r"(xb)
> + : "memory");
> + /* After stilp-2. */
> +
> + PREPARE_FOR_STORE (8);
> + /* Before stilp-3. */
> + __asm__ volatile ("stilp %w1, %w2, [%0, #-8]!\n"
> + : "+r"(ptr)
> + : "r"(xa), "r"(xb)
> + : "memory");
> + /* After stilp-3. */
> + /* Register overlap. Since there is no offset, writeback is disabled. */
> +
> + PREPARE_FOR_STORE (0);
> + /* Before stilp-4. */
> + __asm__ volatile ("stilp %0, %1, [%0]\n"
> + :
> + : "r"(ptr), "r"(xb)
> + : "memory");
> + /* After stilp-4. */
> +
> + PREPARE_FOR_STORE (0);
> + /* Before stilp-5. */
> + __asm__ volatile ("stilp %w0, %w1, [%0]\n"
> + :
> + : "r"(ptr), "r"(xb)
> + : "memory");
> + /* After stilp-5. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldapr-0. */
> + __asm__ volatile ("ldapr %0, [%1], #8\n"
> + : "+r"(xa), "+r"(ptr)
> + :
> + : "memory");
> + /* After ldapr-0. */
> +
> + PREPARE_FOR_LOAD (0);
> + /* Before ldapr-1. */
> + __asm__ volatile ("ldapr %w0, [%1], #4\n"
> + : "+r"(xa), "+r"(ptr)
> + :
> + : "memory");
> + /* After ldapr-1. */
> +
> + PREPARE_FOR_STORE (8);
> + /* Before stlr-0. */
> + __asm__ volatile ("stlr %1, [%0, #-8]!\n"
> + : "+r"(ptr)
> + : "r"(xa)
> + : "memory");
> + /* After stlr-0. */
> +
> + PREPARE_FOR_STORE (4);
> + /* Before stlr-1. */
> + __asm__ volatile ("stlr %w1, [%0, #-4]!\n"
> + : "+r"(ptr)
> + : "r"(xa)
> + : "memory");
> + /* After stlr-1. */
> +
> + PREPARE_FOR_VECTOR_LOAD (0);
> + /* Before ldap1-0. */
> + __asm__ volatile ("ldap1 {%0.d}[0], [%1]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldap1-0. */
> +
> + PREPARE_FOR_VECTOR_STORE (0);
> + /* Before stl1-0. */
> + __asm__ volatile ("stl1 {%0.d}[0], [%1]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stl1-0. */
> +
> + PREPARE_FOR_VECTOR_LOAD (0);
> + /* Before ldapur-0. */
> + __asm__ volatile ("ldapur %d0, [%1]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-0. */
> +
> + PREPARE_FOR_VECTOR_STORE (0);
> + /* Before stlur-0. */
> + __asm__ volatile ("stlur %d0, [%1]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-0. */
> +
> + PREPARE_FOR_VECTOR_LOAD (256);
> + /* Before ldapur-1. */
> + __asm__ volatile ("ldapur %d0, [%1, #-256]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-1. */
> +
> + PREPARE_FOR_VECTOR_STORE (256);
> + /* Before stlur-1. */
> + __asm__ volatile ("stlur %d0, [%1, #-256]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-1. */
> +
> + PREPARE_FOR_VECTOR_LOAD (-255);
> + /* Before ldapur-2. */
> + __asm__ volatile ("ldapur %d0, [%1, #255]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-2. */
> +
> + PREPARE_FOR_VECTOR_STORE (-255);
> + /* Before stlur-2. */
> + __asm__ volatile ("stlur %d0, [%1, #255]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-2. */
> +
> + PREPARE_FOR_VECTOR_LOAD (0);
> + /* Before ldapur-3. */
> + __asm__ volatile ("ldapur %h0, [%1]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-3. */
> +
> + PREPARE_FOR_VECTOR_STORE (0);
> + /* Before stlur-3. */
> + __asm__ volatile ("stlur %h0, [%1]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-3. */
> +
> + PREPARE_FOR_VECTOR_LOAD (0);
> + /* Before ldapur-4. */
> + __asm__ volatile ("ldapur %s0, [%1]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-4. */
> +
> + PREPARE_FOR_VECTOR_STORE (0);
> + /* Before stlur-4. */
> + __asm__ volatile ("stlur %s0, [%1]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-4. */
> +
> + PREPARE_FOR_VECTOR_LOAD (0);
> + /* Before ldapur-5. */
> + __asm__ volatile ("ldapur %d0, [%1]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-5. */
> +
> + PREPARE_FOR_VECTOR_STORE (0);
> + /* Before stlur-5. */
> + __asm__ volatile ("stlur %d0, [%1]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-5. */
> +
> + PREPARE_FOR_VECTOR_LOAD (0);
> + /* Before ldapur-6. */
> + __asm__ volatile ("ldapur %q0, [%1]\n"
> + : "=w"(va)
> + : "r"(ptr)
> + : "memory");
> + /* After ldapur-6. */
> +
> + PREPARE_FOR_VECTOR_STORE (0);
> + /* Before stlur-6. */
> + __asm__ volatile ("stlur %q0, [%1]\n"
> + :
> + : "w"(va), "r"(ptr)
> + : "memory");
> + /* After stlur-6. */
> +
> + return 0;
> +}
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
> new file mode 100644
> index 00000000000..7d7a8c45445
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
> @@ -0,0 +1,203 @@
> +# Copyright 2024-2026 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/>.
> +
> +# Test instruction record for AArch64 FEAT_LRCPC3 instructions.
> +# Based on gdb.reverse/aarch64-mops.exp
> +#
> +# The basic flow of the record tests are:
> +# 1) Stop before executing the instructions of interest. Record
> +# the initial value of the registers that the instruction will
> +# change, i.e. the destination register.
> +# 2) Execute the instructions. Record the new value of the
> +# registers that changed.
> +# 3) Reverse the direction of the execution and execute back to
> +# just before the instructions of interest. Record the final
> +# value of the registers of interest.
> +# 4) Check that the initial and new values of the registers are
> +# different, i.e. the instruction changed the registers as expected.
> +# 5) Check that the initial and final values of the registers are
> +# the same, i.e. GDB record restored the registers to their
> +# original values.
> +
> +require allow_aarch64_lrcpc3_tests
> +
> +standard_testfile
> +
> +if {
> + [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
> + [list debug additional_flags=-march=armv8-a+rcpc3]]
> +} {
> + return -1
> +}
> +
> +if {![runto_main]} {
> + return -1
> +}
> +
> +gdb_test_no_output "record full"
> +
> +proc test_single_asm {name diff_reg diff_var diff_mem same_reg same_var same_mem} {
> + global decimal hex
> +
> + set before_seq [gdb_get_line_number "Before ${name}"]
> + set after_seq [gdb_get_line_number "After ${name}"]
> +
> + set insn [lindex [split $name "-"] 0]
> +
> + gdb_test "break $before_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lrcpc3.c, line $decimal\\." \
> + "$name: break before instruction sequence"
> + gdb_continue_to_breakpoint "$name: about to execute instruction sequence" \
> + [multi_line ".*/aarch64-lrcpc3.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
> +
> + # Depending on the compiler, the line number information may put GDB a few
> + # instructions before the beginning of the asm statement.
> + arrive_at_instruction $insn
> + # Add a breakpoint that we're sure is at the prologue instruction.
> + gdb_test "break *\$pc" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lrcpc3.c, line $decimal\\." \
> + "$name: break at prologue instruction"
> +
> + # Record the initial memory and register values.
> + set regs [concat $diff_reg $same_reg]
> + set vars [concat $diff_var $same_var]
> + set mem [concat $diff_mem $same_mem]
> + foreach r $regs {
> + set ${r}_initial [capture_command_output "info register $r" ""]
> + }
> + foreach v $vars {
> + set ${v}_initial [get_valueof "/x" "$v" "unable to read $v" \
> + "$name: get $v initial value"]
> + }
> + foreach m $mem {
> + set ${m}_initial [capture_command_output "x/x $m" ""]
> + }
> +
> + gdb_test "break $after_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lrcpc3.c, line $decimal\\." \
> + "$name: break after instruction sequence"
> + gdb_continue_to_breakpoint "$name: executed instruction sequence" \
> + [multi_line ".*/aarch64-lrcpc3.c:$decimal" ".*"]
> +
> + # Record the new memory and register values.
> + foreach r $regs {
> + set ${r}_new [capture_command_output "info register $r" ""]
> + }
> + foreach v $vars {
> + set ${v}_new [get_valueof "/x" "$v" "unable to read $v" \
> + "$name: get $v new value"]
> + }
> + foreach m $mem {
> + set ${m}_new [capture_command_output "x/x $m" ""]
> + }
> +
> + # Execute in reverse to before the instruction sequence.
> + gdb_test_no_output "set exec-direction reverse"
> +
> + gdb_continue_to_breakpoint "reversed execution of instruction sequence" \
> + [multi_line ".*/aarch64-lrcpc3.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
> +
> + # Record the final memory and register values.
> + foreach r $regs {
> + set ${r}_final [capture_command_output "info register $r" ""]
> + }
> + foreach v $vars {
> + set ${v}_final [get_valueof "/x" "$v" "unable to read $v" \
> + "$name: get $v final value"]
> + }
> + foreach m $mem {
> + set ${m}_final [capture_command_output "x/x $m" ""]
> + }
> +
> + foreach v [concat $same_reg $same_var $same_mem] {
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_new]] \
> + "$name: check $v initial value versus $v new value"
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
> + "$name: check $v initial value versus $v final value"
> + }
> +
> + foreach v [concat $diff_reg $diff_var $diff_mem] {
> + gdb_assert [string compare [set ${v}_initial] [set ${v}_new]] \
> + "$name: check $v initial value versus $v new value"
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
> + "$name: check $v initial value versus $v final value"
> + }
> +
> + # Restore forward execution and go to end of recording.
> + gdb_test_no_output "set exec-direction forward"
> + gdb_test "record goto end" \
> + [multi_line \
> + "Go forward to insn number $decimal" \
> + "#0 main \\(\\) at .*/aarch64-lrcpc3.c:$decimal" \
> + ".*"]
> +}
> +
> +set ldiapp_cases {
> + { ldiapp-0 { x19 x20 } { } { } { } { ptr } { src } }
> + { ldiapp-1 { w19 w20 } { } { } { } { ptr } { src } }
> + { ldiapp-2 { x19 x20 } { ptr } { } { } { } { src } }
> + { ldiapp-3 { w19 w20 } { ptr } { } { } { } { src } }
> + { ldiapp-4 { x21 x20 } { } { } { } { } { src } }
> + { ldiapp-5 { w21 w20 } { } { } { } { } { src } }
> +}
> +
> +set stilp_cases {
> + { stilp-0 { } { } { src } { x19 x20 } { ptr } { } }
> + { stilp-1 { } { } { src } { w19 w20 } { ptr } { } }
> + { stilp-2 { } { ptr } { src } { x19 x20 } { } { } }
> + { stilp-3 { } { ptr } { src } { x19 x20 } { } { } }
> + { stilp-4 { } { } { src } { x20 } { ptr } { } }
> + { stilp-5 { } { } { src } { w20 } { ptr } { } }
> +}
> +
> +set ldapr_stlr_cases {
> + { ldapr-0 { x19 } { ptr } { } { } { } { src } }
> + { ldapr-1 { w19 } { ptr } { } { } { } { src } }
> + { stlr-0 { } { ptr } { src } { x19 } { } { } }
> + { stlr-1 { } { ptr } { src } { w19 } { } { } }
> +}
> +
> +set ldap1_stl1_cases {
> + { ldap1-0 { v22 } { } { } { ptr } { } { src } }
> + { stl1-0 { } { } { src } { v22 } { } { } }
> +}
> +
> +set ldapur_stlur_cases {
> + { ldapur-0 { v22 } { } { } { ptr } { } { src } }
> + { stlur-0 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-1 { v22 } { } { } { ptr } { } { src } }
> + { stlur-1 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-2 { v22 } { } { } { ptr } { } { src } }
> + { stlur-2 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-3 { v22 } { } { } { ptr } { } { src } }
> + { stlur-3 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-4 { v22 } { } { } { ptr } { } { src } }
> + { stlur-4 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-5 { v22 } { } { } { ptr } { } { src } }
> + { stlur-5 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-6 { v22 } { } { } { ptr } { } { src } }
> + { stlur-6 { } { } { src } { v22 } { ptr } { } }
> +}
> +
> +set all_cases [concat \
> + $ldiapp_cases $stilp_cases $ldapr_stlr_cases \
> + $ldap1_stl1_cases $ldapur_stlur_cases]
> +
> +foreach c $all_cases {
> + lassign $c name diff_reg diff_var diff_mem same_reg same_var same_mem
> + test_single_asm $name $diff_reg $diff_var $diff_mem $same_reg $same_var $same_mem
> +}
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 28709004570..c6afc556548 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -5287,6 +5287,69 @@ gdb_caching_proc allow_aarch64_fpmr_tests {} {
> return $allow_fpmr_tests
> }
>
> +# Run a test on the target to see if it supports AArch64 LRCPC3 (Load-Acquire
> +# RCpc instructions) extensions. Return 1 if so, 0 if it does not. Note this
> +# causes a restart of GDB.
> +
> +gdb_caching_proc allow_aarch64_lrcpc3_tests {} {
> + global srcdir subdir gdb_prompt inferior_exited_re
> +
> + set me "allow_aarch64_lrcpc3_tests"
> +
> + if { ![is_aarch64_target] } {
> + return 0
> + }
> +
> + # Take the opportunity to check whether the toolchain knows about LRCPC3.
> + set compile_flags "{additional_flags=-march=armv8-a+rcpc3}"
> +
> + # Compile a program that tests the LRCPC3 feature.
> + set src {
> + #include <stdint.h>
> +
> + int main() {
> + uint64_t a, b = 0;
> + uint64_t *ptr;
> + *ptr = 0xdeadbeef;
> +
> + __asm__ volatile ("ldiapp %0, %1, [%2]\n"
> + : "=r"(a), "=r"(b)
> + : "r"(ptr)
> + : "memory");
> +
> + return 0;
> + }
> + }
> +
> + if {![gdb_simple_compile $me $src executable $compile_flags]} {
> + return 0
> + }
> +
> + # Compilation succeeded so now run it via gdb.
> + clean_restart
> + gdb_load $obj
> + gdb_run_cmd
> + gdb_expect {
> + -re ".*Illegal instruction.*${gdb_prompt} $" {
> + verbose -log "\n$me lrcpc3 support not detected"
> + set allow_lrcpc3_tests 0
> + }
> + -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
> + verbose -log "\n$me: lrcpc3 support detected"
> + set allow_lrcpc3_tests 1
> + }
> + default {
> + warning "\n$me: default case taken"
> + set allow_lrcpc3_tests 0
> + }
> + }
> + gdb_exit
> + remote_file build delete $obj
> +
> + verbose "$me: returning $allow_lrcpc3_tests" 2
> + return $allow_lrcpc3_tests
> +}
> +
> # 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.
--
Cheers,
Guinevere Larsen
It/she
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 2/4] gdb/aarch64: Test record/replay support for CSSC
2026-04-20 21:52 ` [PATCH 2/4] gdb/aarch64: Test record/replay support for CSSC Ezra.Sitorus
2026-04-24 7:07 ` Luis
@ 2026-04-24 16:53 ` Guinevere Larsen
2026-04-25 6:10 ` Thiago Jung Bauermann
2026-04-25 7:28 ` Thiago Jung Bauermann
3 siblings, 0 replies; 23+ messages in thread
From: Guinevere Larsen @ 2026-04-24 16:53 UTC (permalink / raw)
To: Ezra.Sitorus, gdb-patches; +Cc: luis.machado.foss, thiago.bauermann
On 4/20/26 6:52 PM, Ezra.Sitorus@arm.com wrote:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> This patch checks that the handling of CSSC instructions is correct.
> There is no explicit support for handling these instructions which is
> why this patch is composed of tests.
Hi Ezra! Thank you for working on this
The way this paragraph is worded is a bit confusing to me. On a first
reading, this sounded to me like there isn't support for CSSC
instructions. However, looking at the test this is clearly not the case
since the test expects things to pass
I suggest rewording the paragraph to something like this:
GDB can handle aarch64's CSSC instructions, but there were no tests
ensuring that and ensuring no regressions would creep in. This commit
adds some tests for those instructions.
>
> Regression tested on aarch64-none-linux-gnu on QEMU with CSSC support.
> I observe unrelated regressions that appear to be environmental rather
> than caused by this patch.
I don't really think there's a need to mention regression testing, since
this commit is just adding a test, but it doesn't really harm anything
either, so it's your choice to add or remove it.
On the code changes, I only have the same feedback as I sent on patch 1:
the register keyword is deprecated and entirely ignored by clang.
Instead of creating variables, you can just use the registers directly
in the asm statements and exp file.
--
Cheers,
Guinevere Larsen
It/she
> ---
> gdb/testsuite/gdb.reverse/aarch64-cssc.c | 113 +++++++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-cssc.exp | 141 +++++++++++++++++++++
> gdb/testsuite/lib/gdb.exp | 52 ++++++++
> 3 files changed, 306 insertions(+)
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.exp
>
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-cssc.c b/gdb/testsuite/gdb.reverse/aarch64-cssc.c
> new file mode 100644
> index 00000000000..d80d202590c
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-cssc.c
> @@ -0,0 +1,113 @@
> +/* This test program is part of GDB, the GNU debugger.
> +
> + Copyright 2024-2026 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/>. */
> +
> +#include <stdint.h>
> +
> +#define PREPARE_REGS(X, Y, Z) \
> + __asm__ volatile ("mov %0, %1\n" : "=r"(xa): "r"(X):); \
> + __asm__ volatile ("mov %0, %1\n" : "=r"(xb): "r"(Y):); \
> + __asm__ volatile ("mov %0, %1\n" : "=r"(xc): "r"(Z):); \
> +
> +int
> +main (void)
> +{
> + register uint64_t xa asm ("x19");
> + register uint32_t xb asm ("x20");
> + register uint64_t xc asm ("x21");
> + const uint64_t a = 0x0123456789abcdef;
> + const uint64_t b = 0xfedbca9876543210;
> + const uint64_t c = 0xdeadbeefc001face;
> +
> + PREPARE_REGS (a, b, c);
> + /* Before abs. */
> + __asm__ volatile ("abs %0, %1\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After abs. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before cnt. */
> + __asm__ volatile ("cnt %0, %1\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After cnt. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before ctz. */
> + __asm__ volatile ("ctz %0, %1\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After ctz. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before smax-1. */
> + __asm__ volatile ("smax %0, %1, #10\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After smax-1. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before smax-2. */
> + __asm__ volatile ("smax %0, %1, %2\n"
> + : "+r"(xa)
> + : "r"(xb), "r"(xc));
> + /* After smax-2. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before smin-1. */
> + __asm__ volatile ("smin %0, %1, #10\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After smin-1. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before smin-2. */
> + __asm__ volatile ("smin %0, %1, %2\n"
> + : "+r"(xa)
> + : "r"(xb), "r"(xc));
> + /* After smin-2. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before umax-1. */
> + __asm__ volatile ("umax %0, %1, #10\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After umax-1. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before umax-2. */
> + __asm__ volatile ("umax %0, %1, %2\n"
> + : "+r"(xa)
> + : "r"(xb), "r"(xc));
> + /* After umax-2. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before umin-1. */
> + __asm__ volatile ("umin %0, %1, #10\n"
> + : "+r"(xa)
> + : "r"(xb));
> + /* After umin-1. */
> +
> + PREPARE_REGS (a, b, c);
> + /* Before umin-2. */
> + __asm__ volatile ("umin %0, %1, %2\n"
> + : "+r"(xa)
> + : "r"(xb), "r"(xc));
> + /* After umin-2. */
> +
> + return 0;
> +}
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-cssc.exp b/gdb/testsuite/gdb.reverse/aarch64-cssc.exp
> new file mode 100644
> index 00000000000..640ea4ae19f
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-cssc.exp
> @@ -0,0 +1,141 @@
> +# Copyright 2024-2026 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/>.
> +
> +# Test instruction record for AArch64 FEAT_CSSC instructions.
> +# Based on gdb.reverse/aarch64-mops.exp
> +#
> +# The basic flow of the record tests are:
> +# 1) Stop before executing the instructions of interest. Record
> +# the initial value of the registers that the instruction will
> +# change, i.e. the destination register.
> +# 2) Execute the instructions. Record the new value of the
> +# registers that changed.
> +# 3) Reverse the direction of the execution and execute back to
> +# just before the instructions of interest. Record the final
> +# value of the registers of interest.
> +# 4) Check that the initial and new values of the registers are
> +# different, i.e. the instruction changed the registers as expected.
> +# 5) Check that the initial and final values of the registers are
> +# the same, i.e. GDB record restored the registers to their
> +# original values.
> +
> +require allow_aarch64_cssc_tests
> +standard_testfile
> +
> +if {
> + [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
> + [list debug additional_flags=-march=armv8-a+cssc]]
> +} {
> + return -1
> +}
> +
> +if {![runto_main]} {
> + return -1
> +}
> +
> +gdb_test_no_output "record full"
> +
> +proc test_single_asm { name } {
> + global decimal hex
> +
> + set before_seq [gdb_get_line_number "Before ${name}"]
> + set after_seq [gdb_get_line_number "After ${name}"]
> + set regs { x19 x20 x21 }
> +
> + set insn [lindex [split $name "-"] 0]
> +
> + gdb_test "break $before_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-cssc.c, line $decimal\\." \
> + "$insn: break before instruction sequence"
> + gdb_continue_to_breakpoint "$insn: about to execute instruction sequence" \
> + [multi_line ".*/aarch64-cssc.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
> +
> + # Depending on the compiler, the line number information may put GDB a few
> + # instructions before the beginning of the asm statement.
> + arrive_at_instruction $insn
> + # Add a breakpoint that we're sure is at the prologue instruction.
> + gdb_test "break *\$pc" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-cssc.c, line $decimal\\." \
> + "$insn: break at prologue instruction"
> +
> + # Record the initial register values.
> + foreach r $regs {
> + set ${r}_initial [capture_command_output "info register $r" ""]
> + }
> +
> + gdb_test "break $after_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-cssc.c, line $decimal\\." \
> + "$insn: break after instruction sequence"
> + gdb_continue_to_breakpoint "$insn: executed instruction sequence" \
> + [multi_line ".*/aarch64-cssc.c:$decimal" ".*"]
> +
> + # Record the new register values.
> + foreach r $regs {
> + set ${r}_new [capture_command_output "info register $r" ""]
> + }
> +
> + # Execute in reverse to before the instruction sequence.
> + gdb_test_no_output "set exec-direction reverse"
> +
> + gdb_continue_to_breakpoint "reversed execution of instruction sequence" \
> + [multi_line ".*/aarch64-cssc.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
> +
> + # Record the final register values.
> + foreach r $regs {
> + set ${r}_final [capture_command_output "info register $r" ""]
> + }
> +
> + foreach v { x19 x20 x21 } {
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
> + "$insn: check $v initial value versus $v final value"
> + }
> +
> + gdb_assert [string compare [set x19_initial] [set x19_new]] \
> + "$insn: check x19 initial value versus x19 new value"
> +
> + foreach v { x20 x21 } {
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_new]] \
> + "$insn: check $v initial value versus $v new value"
> + }
> +
> + # Restore forward execution and go to end of recording.
> + gdb_test_no_output "set exec-direction forward"
> + gdb_test "record goto end" \
> + [multi_line \
> + "Go forward to insn number $decimal" \
> + "#0 main \\(\\) at .*/aarch64-cssc.c:$decimal" \
> + ".*"]
> +}
> +
> +set cases {
> + { abs }
> + { cnt }
> + { ctz }
> + { smax-1 }
> + { smax-2 }
> + { smin-1 }
> + { smin-2 }
> + { umax-1 }
> + { umax-2 }
> + { umin-1 }
> + { umin-2 }
> +}
> +
> +foreach c $cases {
> + lassign $c insn
> + test_single_asm $insn
> +}
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index c6afc556548..7d400e9028a 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -5350,6 +5350,58 @@ gdb_caching_proc allow_aarch64_lrcpc3_tests {} {
> return $allow_lrcpc3_tests
> }
>
> +# Run a test on the target to see if it supports the AArch64 CSSC feature.
> +# Return 1 if so, 0 if it does not. Note this causes a restart of GDB.
> +
> +gdb_caching_proc allow_aarch64_cssc_tests {} {
> + global srcdir subdir gdb_prompt inferior_exited_re
> +
> + set me "allow_aarch64_cssc_tests"
> +
> + if { ![is_aarch64_target]} {
> + return 0
> + }
> +
> + set compile_flags "{additional_flags=-march=armv8-a+cssc}"
> +
> + # Compile a test program reading CSSC.
> + set src {
> + int main() {
> + asm volatile ("abs x0, x1");
> + return 0;
> + }
> + }
> +
> + if {![gdb_simple_compile $me $src executable $compile_flags]} {
> + return 0
> + }
> +
> + # Compilation succeeded so now run it via gdb.
> + clean_restart
> + gdb_load $obj
> + gdb_run_cmd
> +
> + gdb_expect {
> + -re ".*Illegal instruction.*${gdb_prompt} $" {
> + verbose -log "\n$me cssc support not detected"
> + set allow_cssc_tests 0
> + }
> + -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
> + verbose -log "\n$me: cssc support detected"
> + set allow_cssc_tests 1
> + }
> + default {
> + warning "\n$me: default case taken"
> + set allow_cssc_tests 0
> + }
> + }
> + gdb_exit
> + remote_file build delete $obj
> +
> + verbose "$me: returning $allow_cssc_tests" 2
> + return $allow_cssc_tests
> +}
> +
> # 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.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 3/4] gdb/aarch64: record/replay support for RPRFM and PRFM (reg)
2026-04-20 21:52 ` [PATCH 3/4] gdb/aarch64: record/replay support for RPRFM and PRFM (reg) Ezra.Sitorus
2026-04-24 7:08 ` Luis
@ 2026-04-24 16:56 ` Guinevere Larsen
2026-04-25 8:12 ` Thiago Jung Bauermann
2 siblings, 0 replies; 23+ messages in thread
From: Guinevere Larsen @ 2026-04-24 16:56 UTC (permalink / raw)
To: Ezra.Sitorus, gdb-patches; +Cc: luis.machado.foss, thiago.bauermann
On 4/20/26 6:52 PM, Ezra.Sitorus@arm.com wrote:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> The PRFM (register) instruction variant was unsupported. This is added
> along with RPRFM.
>
> No testcase has been added as these are hint instructions which don't
> modify registers/memory. I'm happy to add them in if you think these are
> required.
>
> The full testsuite was done on aarch64-none-linux-gnu without RPRFM.
> The gdb.arch and gdb.reverse tests were run on Shrinkwrap with RPRFM
> support. I've observed unrelated regressions that appear to be due to
> Shrinkwrap rather than my patch.
Just popping by to ack that no tests are needed. I'll leave the review
to Luis
> ---
> gdb/aarch64-tdep.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index 81d4f160f8f..61c462979b3 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -5914,6 +5914,11 @@ aarch64_record_load_store (aarch64_insn_decode_record *aarch64_insn_r)
> else
> if (size_bits != 0x03)
> ld_flag = 0x01;
> + else if (size_bits == 0x3 && vector_flag == 0x0 && opc == 0x2)
> + {
> + /* PRFM (register) or RPRFM */
> + return AARCH64_RECORD_SUCCESS;
> + }
> else
> return AARCH64_RECORD_UNKNOWN;
>
--
Cheers,
Guinevere Larsen
It/she
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 4/4] gdb/aarch64: record/replay support for LSE128
2026-04-20 21:52 ` [PATCH 4/4] gdb/aarch64: record/replay support for LSE128 Ezra.Sitorus
2026-04-24 7:11 ` Luis
@ 2026-04-24 17:12 ` Guinevere Larsen
2026-04-25 8:38 ` Thiago Jung Bauermann
2 siblings, 0 replies; 23+ messages in thread
From: Guinevere Larsen @ 2026-04-24 17:12 UTC (permalink / raw)
To: Ezra.Sitorus, gdb-patches; +Cc: luis.machado.foss, thiago.bauermann
On 4/20/26 6:52 PM, Ezra.Sitorus@arm.com wrote:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> FEAT_LSE128 introduces support for 128-bit atomic instructions. This
> patch teaches GDB to decode these instructions for recording and
> reversing.
>
> Regression tested on aarch64-none-linux-gnu on QEMU with LSE128 support.
> I observe unrelated regressions that appear to be environmental rather
> than caused by this patch.
Hi Ezra! Thanks for working on this :)
Once again, my only feedback is the use of the register keyword
> ---
> gdb/aarch64-tdep.c | 13 ++
> gdb/testsuite/gdb.reverse/aarch64-lse128.c | 139 +++++++++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-lse128.exp | 138 ++++++++++++++++++
> gdb/testsuite/lib/gdb.exp | 56 ++++++++
> 4 files changed, 346 insertions(+)
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.exp
>
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index 61c462979b3..aff8b294bd5 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -6004,6 +6004,19 @@ aarch64_record_load_store (aarch64_insn_decode_record *aarch64_insn_r)
> else if ((insn_bits24_27 & 1) == 1 && insn_bits28_29 == 1
> && insn_bits10_11 == 1 && !insn_bit21)
> return aarch64_record_memcopy_memset (aarch64_insn_r);
> + /* Large System Extension 128 (LSE128) instructions. */
> + else if (vector_flag == 0 && insn_bits10_11 == 0 && insn_bit21
> + && size_bits == 0 && !bit (aarch64_insn_r->aarch64_insn, 29)
> + && bits (aarch64_insn_r->aarch64_insn, 24, 25) == 0x1)
> + {
> + regcache_raw_read_unsigned (aarch64_insn_r->regcache, reg_rn, &address);
> + record_buf_mem[0] = 128 >> 3;
> + record_buf_mem[1] = address;
> + aarch64_insn_r->mem_rec_count = 1;
> + record_buf[0] = reg_rt;
> + record_buf[1] = bits (aarch64_insn_r->aarch64_insn, 16, 20);
> + aarch64_insn_r->reg_rec_count = 2;
> + }
> /* Advanced SIMD load/store instructions. */
> else
> return aarch64_record_asimd_load_store (aarch64_insn_r);
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-lse128.c b/gdb/testsuite/gdb.reverse/aarch64-lse128.c
> new file mode 100644
> index 00000000000..906c303d904
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-lse128.c
> @@ -0,0 +1,139 @@
> +/* This test program is part of GDB, the GNU debugger.
> +
> + Copyright 2024-2026 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/>. */
> +
> +#include <stdint.h>
> +#include <stdalign.h>
> +#include <string.h>
> +
> +#define INITIAL_STRING "This is just some string."
> +#define BUF_SIZE sizeof (INITIAL_STRING)
> +
> +#define PREPARE_REGS(X, Y) \
> + strcpy (src, INITIAL_STRING); \
> + __asm__ volatile ("mov %0, %1\n" : "=r"(xa): "r"(X):); \
> + __asm__ volatile ("mov %0, %1\n" : "=r"(xb): "r"(Y):); \
> + ptr = (uint64_t *)src; \
> +
> +int
> +main (void)
> +{
> + alignas(16) char src[BUF_SIZE];
> +
> + register uint64_t xa asm ("x19");
> + register uint64_t xb asm ("x20");
> + register uint64_t *ptr asm ("x21");
> + uint64_t a = 0x0123456789abcdef;
> + uint64_t b = 0xfedbca9876543210;
> +
> + PREPARE_REGS (a, b);
> + /* Before ldclrp. */
> + __asm__ volatile ("ldclrp %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldclrp. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldclrpa. */
> + __asm__ volatile ("ldclrpa %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldclrpa. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldclrpal. */
> + __asm__ volatile ("ldclrpal %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldclrpal. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldclrpl. */
> + __asm__ volatile ("ldclrpl %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldclrpl. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldsetp. */
> + __asm__ volatile ("ldsetp %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldsetp. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldsetpa. */
> + __asm__ volatile ("ldsetpa %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldsetpa. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldsetpal. */
> + __asm__ volatile ("ldsetpal %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldsetpal. */
> +
> + PREPARE_REGS (a, b);
> + /* Before ldsetpl. */
> + __asm__ volatile ("ldsetpl %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After ldsetpl. */
> +
> + PREPARE_REGS (a, b);
> + /* Before swpp. */
> + __asm__ volatile ("swpp %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After swpp. */
> +
> + PREPARE_REGS (a, b);
> + /* Before swppa. */
> + __asm__ volatile ("swppa %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After swppa. */
> +
> + PREPARE_REGS (a, b);
> + /* Before swppal. */
> + __asm__ volatile ("swppal %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After swppal. */
> +
> + PREPARE_REGS (a, b);
> + /* Before swppl. */
> + __asm__ volatile ("swppl %0, %1, [%2]\n"
> + : "+r"(xa), "+r"(xb)
> + : "r"(ptr)
> + : "memory");
> + /* After swppl. */
> +
> + return 0;
> +}
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-lse128.exp b/gdb/testsuite/gdb.reverse/aarch64-lse128.exp
> new file mode 100644
> index 00000000000..273e239ec66
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-lse128.exp
> @@ -0,0 +1,138 @@
> +# Copyright 2024-2026 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/>.
> +
> +# Test instruction record for AArch64 FEAT_LSE128 instructions.
> +# Based on gdb.reverse/aarch64-mops.exp
> +#
> +# The basic flow of the record tests are:
> +# 1) Stop before executing the instructions of interest. Record
> +# the initial value of the registers that the instruction will
> +# change, i.e. the destination register.
> +# 2) Execute the instructions. Record the new value of the
> +# registers that changed.
> +# 3) Reverse the direction of the execution and execute back to
> +# just before the instructions of interest. Record the final
> +# value of the registers of interest.
> +# 4) Check that the initial and new values of the registers are
> +# different, i.e. the instruction changed the registers as expected.
> +# 5) Check that the initial and final values of the registers are
> +# the same, i.e. GDB record restored the registers to their
> +# original values.
> +
> +standard_testfile
> +
> +require allow_aarch64_lse128_tests
> +
> +if {
> + [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
> + [list debug additional_flags=-march=armv8-a+lse128]]
> +} {
> + return -1
> +}
> +
> +if {![runto_main]} {
> + return -1
> +}
> +
> +gdb_test_no_output "record full"
> +
> +proc test_single_asm {insn} {
> + global decimal hex
> +
> + set before_seq [gdb_get_line_number "Before ${insn}"]
> + set after_seq [gdb_get_line_number "After ${insn}"]
> + set regs { x19 x20 }
> +
> + gdb_test "break $before_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lse128.c, line $decimal\\." \
> + "$insn: break before instruction sequence"
> + gdb_continue_to_breakpoint "$insn: about to execute instruction sequence" \
> + [multi_line ".*/aarch64-lse128.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
> +
> + # Depending on the compiler, the line number information may put GDB a few
> + # instructions before the beginning of the asm statement.
> + arrive_at_instruction $insn
> + # Add a breakpoint that we're sure is at the prologue instruction.
> + gdb_test "break *\$pc" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lse128.c, line $decimal\\." \
> + "$insn: break at prologue instruction"
> +
> + # Record the initial memory and register values.
> + foreach r $regs {
> + set ${r}_initial [capture_command_output "info register $r" ""]
> + }
> + set ptr_initial [capture_command_output "x/4gwx ptr" ""]
> +
> + gdb_test "break $after_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lse128.c, line $decimal\\." \
> + "$insn: break after instruction sequence"
> + gdb_continue_to_breakpoint "$insn: executed instruction sequence" \
> + [multi_line ".*/aarch64-lse128.c:$decimal" ".*"]
> +
> + # Record the new memory and register values.
> + foreach r $regs {
> + set ${r}_new [capture_command_output "info register $r" ""]
> + }
> + set ptr_new [capture_command_output "x/4gwx ptr" ""]
> +
> + # Execute in reverse to before the instruction sequence.
> + gdb_test_no_output "set exec-direction reverse"
> +
> + gdb_continue_to_breakpoint "reversed execution of instruction sequence" \
> + [multi_line ".*/aarch64-lse128.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
> +
> + # Record the final memory and register values.
> + foreach r $regs {
> + set ${r}_final [capture_command_output "info register $r" ""]
> + }
> + set ptr_final [capture_command_output "x/4gwx ptr" ""]
> +
> + foreach v [concat $regs {ptr}] {
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
> + "$insn: check $v initial value versus $v final value"
> + gdb_assert [string compare [set ${v}_initial] [set ${v}_new]] \
> + "$insn: check $v initial value versus $v new value"
> + }
> +
> + # Restore forward execution and go to end of recording.
> + gdb_test_no_output "set exec-direction forward"
> + gdb_test "record goto end" \
> + [multi_line \
> + "Go forward to insn number $decimal" \
> + "#0 main \\(\\) at .*/aarch64-lse128.c:$decimal" \
> + ".*"]
> +}
> +
> +set cases {
> + { ldclrp }
> + { ldclrpa }
> + { ldclrpal }
> + { ldclrpl }
> + { ldsetp }
> + { ldsetpa }
> + { ldsetpal }
> + { ldsetpl }
> + { swpp }
> + { swppa }
> + { swppal }
> + { swppl }
> +}
> +
> +foreach c $cases {
> + lassign $c insn
> + test_single_asm $insn
> +}
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 7d400e9028a..d5f9008470a 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -5402,6 +5402,62 @@ gdb_caching_proc allow_aarch64_cssc_tests {} {
> return $allow_cssc_tests
> }
>
> +# Run a test on the target to see if it supports the AArch64 LSE128 feature.
> +# Return 1 if so, 0 if it does not. Note this causes a restart of GDB.
> +
> +gdb_caching_proc allow_aarch64_lse128_tests {} {
> + global srcdir subdir gdb_prompt inferior_exited_re
> +
> + set me "allow_aarch64_lse128_tests"
> +
> + if { ![is_aarch64_target]} {
> + return 0
> + }
> +
> + set compile_flags "{additional_flags=-march=armv8-a+lse128}"
> +
> + # Compile a test program reading LSE128.
> + set src {
> + #include <stdint.h>
> +
> + int main() {
> + __attribute__((aligned(16))) uint64_t mem[2] = { 0x0, 0x1 };
> + uint64_t *ptr = mem;
> + __asm__ volatile ("ldclrp x0, x1, [%0]\n" :: "r"(ptr) : "x0", "x1", "memory");
> + return 0;
> + }
> + }
> +
> + if {![gdb_simple_compile $me $src executable $compile_flags]} {
> + return 0
> + }
> +
> + # Compilation succeeded so now run it via gdb.
> + clean_restart
> + gdb_load $obj
> + gdb_run_cmd
> +
> + gdb_expect {
> + -re ".*Illegal instruction.*${gdb_prompt} $" {
> + verbose -log "\n$me lse128 support not detected"
> + set allow_lse128_tests 0
> + }
> + -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
> + verbose -log "\n$me: lse128 support detected"
> + set allow_lse128_tests 1
> + }
> + default {
> + warning "\n$me: default case taken"
> + set allow_lse128_tests 0
> + }
> + }
> + gdb_exit
> + remote_file build delete $obj
> +
> + verbose "$me: returning $allow_lse128_tests" 2
> + return $allow_lse128_tests
> +}
> +
> # 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.
--
Cheers,
Guinevere Larsen
It/she
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/4] gdb/aarch64: record/replay support for LRCPC3
2026-04-20 21:52 ` [PATCH 1/4] gdb/aarch64: record/replay support for LRCPC3 Ezra.Sitorus
2026-04-24 7:00 ` Luis
2026-04-24 15:09 ` Guinevere Larsen
@ 2026-04-25 5:08 ` Thiago Jung Bauermann
2026-04-25 7:21 ` Thiago Jung Bauermann
3 siblings, 0 replies; 23+ messages in thread
From: Thiago Jung Bauermann @ 2026-04-25 5:08 UTC (permalink / raw)
To: Ezra.Sitorus; +Cc: gdb-patches, luis.machado.foss
Hello Ezra,
<Ezra.Sitorus@arm.com> writes:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> FEAT_LRCPC3 introduces various load/store instructions with release
> consistency for cases where ordering is required. This patch teaches GDB
> to decode these instructions for recording and reversing.
>
> The gdb.reverse/aarch64-lrcpc3.exp testcase verifies that the
> instructions are recorded and correctly reversed. In particular, there
> are some interesting cases to note:
> * ldapur/stlur are SIMD instructions, but are not decoded in the simd
> function.
> * There are writeback cases to cover too. These were taken from the
> binutils testcases: gas/testsuite/gas/aarch64/rcpc3.s.
>
> The full testsuite was done on aarch64-none-linux-gnu without LRCPC3.
> The gdb.arch and gdb.reverse tests were run on Shrinkwrap with LRCPC3
> support.
>
> Please note:
> 1) There is no support for LRCPC and LRCPC2 instructions
> 2) LRCPC3 is gated with +rcpc3 in GCC/binutils.
> ---
> gdb/aarch64-tdep.c | 87 +++++
> gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c | 319 +++++++++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp | 203 ++++++++++++
> gdb/testsuite/lib/gdb.exp | 63 ++++
> 4 files changed, 672 insertions(+)
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
Thanks! I have just a few nits.
I appreciate the well-commented changes in aarch64-tdep.c. They made the
code easier to follow.
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index 4befaa2720d..81d4f160f8f 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -5812,6 +5812,93 @@ aarch64_record_load_store (aarch64_insn_decode_record *aarch64_insn_r)
> aarch64_insn_r->reg_rec_count = 1;
> }
> }
> + /* LRCPC3 instructions. This covers ldiapp/stilp, ldapur/stlur (FP/SIMD),
> + ldapr/stlr. */
> + else if ((insn_bits24_27 & 0x0b) == 0x09 && insn_bits28_29 == 0x01
> + && insn_bits10_11 == 0x02 && !insn_bit21)
> + {
> + /* ldapur/stlur (FP/SIMD), ldapr/stlr. We can differentiate between the
> + 2 types by checking the vector flag. */
> + if (insn_bit23 || vector_flag)
> + {
> + /* For the vector instruction, the offset comes from the imm9
> + bitfield, whereas the other can only take possible values from the
> + size bitfield. */
> + int16_t imm9_off = sbits (aarch64_insn_r->aarch64_insn, 12, 20);
> + offset = vector_flag ? imm9_off : -(1 << size_bits);
> + uint32_t regnum_offset = vector_flag ? AARCH64_V0_REGNUM : 0;
> + if (ld_flag)
> + {
> + record_buf[0] = reg_rt + regnum_offset;
> + aarch64_insn_r->reg_rec_count = 1;
> + if (!vector_flag)
> + {
> + /* The Rn register always has writeback in LRCPC3. This is
> + not the case in LRCPC. */
> + record_buf[1] = reg_rn;
> + aarch64_insn_r->reg_rec_count = 2;
> + }
> + }
> + else
> + {
> + regcache_raw_read_unsigned (aarch64_insn_r->regcache, reg_rn,
> + &address);
> + /* (vector_flag && insn_bit23) is the STLUR instruction with Q
> + register. */
> + datasize = (vector_flag && insn_bit23) ? 128 : (8 << size_bits);
> + /* LRCPC3 adds STLR with a pre-indexed offset. There is another
> + STLR variant without offset but this has a different encoding. */
> + if (!vector_flag)
> + {
> + record_buf[0] = reg_rn;
> + aarch64_insn_r->reg_rec_count = 1;
> + }
> + record_buf_mem[0] = datasize >> 3;
> + record_buf_mem[1] = address + offset;
> + aarch64_insn_r->mem_rec_count = 1;
> + }
> + }
> + else
> + {
> + /* ldiapp/stilp. */
> + uint8_t opc2 = bits (aarch64_insn_r->aarch64_insn, 12, 15);
> + reg_rt2 = bits (aarch64_insn_r->aarch64_insn, 16, 20);
> + if (ld_flag)
> + {
> + record_buf[0] = reg_rt;
> + record_buf[1] = reg_rt2;
> + aarch64_insn_r->reg_rec_count = 2;
> +
> + /* If the registers don't match and there's no offset then
> + there's WB. */
The line above is indented with 8 spaces which can be substituted
by a \t. git am complained about it.
> + if (reg_rn != reg_rt && reg_rn != reg_rt2 && opc2 == 0)
> + {
> + record_buf[2] = reg_rn;
> + aarch64_insn_r->reg_rec_count = 3;
> + }
> + }
> + else
> + {
> + datasize = 8 << size_bits;
> + regcache_raw_read_unsigned (aarch64_insn_r->regcache, reg_rn,
> + &address);
> + offset = (opc2 == 0) ? (2 << size_bits) : 0;
> + address -= offset;
> +
> + record_buf_mem[0] = datasize >> 3;
> + record_buf_mem[1] = address;
> + record_buf_mem[2] = datasize >> 3;
> + record_buf_mem[3] = address + (datasize >> 3);
> + aarch64_insn_r->mem_rec_count = 2;
> +
> + if (offset != 0)
> + {
> + record_buf[0] = reg_rn;
> + aarch64_insn_r->reg_rec_count = 1;
> + }
> + }
> + }
> + }
> /* Load/store register (register offset) instructions. */
> else if ((insn_bits24_27 & 0x0b) == 0x08 && insn_bits28_29 == 0x03
> && insn_bits10_11 == 0x02 && insn_bit21)
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
> new file mode 100644
> index 00000000000..9265489ff70
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
> @@ -0,0 +1,319 @@
> +/* This test program is part of GDB, the GNU debugger.
> +
> + Copyright 2024-2026 Free Software Foundation, Inc.
The copyright should start with 2026.
> diff --git a/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
> new file mode 100644
> index 00000000000..7d7a8c45445
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
> @@ -0,0 +1,203 @@
> +# Copyright 2024-2026 Free Software Foundation, Inc.
Here too, the copyright should start with 2026.
> +
> +# 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/>.
> +
> +# Test instruction record for AArch64 FEAT_LRCPC3 instructions.
> +# Based on gdb.reverse/aarch64-mops.exp
> +#
> +# The basic flow of the record tests are:
> +# 1) Stop before executing the instructions of interest. Record
> +# the initial value of the registers that the instruction will
> +# change, i.e. the destination register.
> +# 2) Execute the instructions. Record the new value of the
> +# registers that changed.
> +# 3) Reverse the direction of the execution and execute back to
> +# just before the instructions of interest. Record the final
> +# value of the registers of interest.
> +# 4) Check that the initial and new values of the registers are
> +# different, i.e. the instruction changed the registers as expected.
> +# 5) Check that the initial and final values of the registers are
> +# the same, i.e. GDB record restored the registers to their
> +# original values.
> +
> +require allow_aarch64_lrcpc3_tests
> +
> +standard_testfile
> +
> +if {
> + [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
> + [list debug additional_flags=-march=armv8-a+rcpc3]]
> +} {
The style we use in Tcl puts the { and } of the if condition in the same
line as the condition expression.
> + return -1
Top-level returns from .exp files don't need to return anything anymore.
The current style is to have a bare "return".
Applies to this return ...
> +}
> +
> +if {![runto_main]} {
> + return -1
... and this return.
gdb.reverse/aarch64-mops.exp is wrong in that regard. Sorry for the bad influence.
> +set ldiapp_cases {
> + { ldiapp-0 { x19 x20 } { } { } { } { ptr } { src } }
> + { ldiapp-1 { w19 w20 } { } { } { } { ptr } { src } }
> + { ldiapp-2 { x19 x20 } { ptr } { } { } { } { src } }
> + { ldiapp-3 { w19 w20 } { ptr } { } { } { } { src } }
> + { ldiapp-4 { x21 x20 } { } { } { } { } { src } }
> + { ldiapp-5 { w21 w20 } { } { } { } { } { src } }
> +}
> +
> +set stilp_cases {
> + { stilp-0 { } { } { src } { x19 x20 } { ptr } { } }
> + { stilp-1 { } { } { src } { w19 w20 } { ptr } { } }
> + { stilp-2 { } { ptr } { src } { x19 x20 } { } { } }
> + { stilp-3 { } { ptr } { src } { x19 x20 } { } { } }
> + { stilp-4 { } { } { src } { x20 } { ptr } { } }
> + { stilp-5 { } { } { src } { w20 } { ptr } { } }
> +}
> +
> +set ldapr_stlr_cases {
> + { ldapr-0 { x19 } { ptr } { } { } { } { src } }
> + { ldapr-1 { w19 } { ptr } { } { } { } { src } }
> + { stlr-0 { } { ptr } { src } { x19 } { } { } }
> + { stlr-1 { } { ptr } { src } { w19 } { } { } }
> +}
> +
> +set ldap1_stl1_cases {
> + { ldap1-0 { v22 } { } { } { ptr } { } { src } }
> + { stl1-0 { } { } { src } { v22 } { } { } }
> +}
> +
> +set ldapur_stlur_cases {
> + { ldapur-0 { v22 } { } { } { ptr } { } { src } }
> + { stlur-0 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-1 { v22 } { } { } { ptr } { } { src } }
> + { stlur-1 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-2 { v22 } { } { } { ptr } { } { src } }
> + { stlur-2 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-3 { v22 } { } { } { ptr } { } { src } }
> + { stlur-3 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-4 { v22 } { } { } { ptr } { } { src } }
> + { stlur-4 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-5 { v22 } { } { } { ptr } { } { src } }
> + { stlur-5 { } { } { src } { v22 } { ptr } { } }
> + { ldapur-6 { v22 } { } { } { ptr } { } { src } }
> + { stlur-6 { } { } { src } { v22 } { ptr } { } }
> +}
> +
> +set all_cases [concat \
> + $ldiapp_cases $stilp_cases $ldapr_stlr_cases \
> + $ldap1_stl1_cases $ldapur_stlur_cases]
> +
> +foreach c $all_cases {
> + lassign $c name diff_reg diff_var diff_mem same_reg same_var same_mem
> + test_single_asm $name $diff_reg $diff_var $diff_mem $same_reg $same_var $same_mem
> +}
Nice way of structuring all the cases!
--
Thiago
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 2/4] gdb/aarch64: Test record/replay support for CSSC
2026-04-20 21:52 ` [PATCH 2/4] gdb/aarch64: Test record/replay support for CSSC Ezra.Sitorus
2026-04-24 7:07 ` Luis
2026-04-24 16:53 ` Guinevere Larsen
@ 2026-04-25 6:10 ` Thiago Jung Bauermann
2026-04-25 7:28 ` Thiago Jung Bauermann
3 siblings, 0 replies; 23+ messages in thread
From: Thiago Jung Bauermann @ 2026-04-25 6:10 UTC (permalink / raw)
To: Ezra.Sitorus; +Cc: gdb-patches, luis.machado.foss
<Ezra.Sitorus@arm.com> writes:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> This patch checks that the handling of CSSC instructions is correct.
> There is no explicit support for handling these instructions which is
> why this patch is composed of tests.
>
> Regression tested on aarch64-none-linux-gnu on QEMU with CSSC support.
> I observe unrelated regressions that appear to be environmental rather
> than caused by this patch.
> ---
> gdb/testsuite/gdb.reverse/aarch64-cssc.c | 113 +++++++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-cssc.exp | 141 +++++++++++++++++++++
> gdb/testsuite/lib/gdb.exp | 52 ++++++++
> 3 files changed, 306 insertions(+)
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-cssc.exp
Following the decoding of CSSC instructions in
aarch64_record_data_proc_imm and aarch64_record_load_store made me
realize how clever the AArch64 instruction encoding is. :)
For this patch I only have the same comments I had about the testcase in
patch 1: the copyright year, the formatting of the if condition block
and the top-level return statements.
--
Thiago
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/4] gdb/aarch64: record/replay support for LRCPC3
2026-04-20 21:52 ` [PATCH 1/4] gdb/aarch64: record/replay support for LRCPC3 Ezra.Sitorus
` (2 preceding siblings ...)
2026-04-25 5:08 ` Thiago Jung Bauermann
@ 2026-04-25 7:21 ` Thiago Jung Bauermann
3 siblings, 0 replies; 23+ messages in thread
From: Thiago Jung Bauermann @ 2026-04-25 7:21 UTC (permalink / raw)
To: Ezra.Sitorus; +Cc: gdb-patches, luis.machado.foss
Hello again,
It turns out I do have a couple more comments on this.
<Ezra.Sitorus@arm.com> writes:
> +proc test_single_asm {name diff_reg diff_var diff_mem same_reg same_var same_mem} {
> + global decimal hex
> +
> + set before_seq [gdb_get_line_number "Before ${name}"]
> + set after_seq [gdb_get_line_number "After ${name}"]
> +
> + set insn [lindex [split $name "-"] 0]
> +
> + gdb_test "break $before_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lrcpc3.c, line $decimal\\." \
> + "$name: break before instruction sequence"
> + gdb_continue_to_breakpoint "$name: about to execute instruction sequence" \
> + [multi_line ".*/aarch64-lrcpc3.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
> +
> + # Depending on the compiler, the line number information may put GDB a few
> + # instructions before the beginning of the asm statement.
> + arrive_at_instruction $insn
> + # Add a breakpoint that we're sure is at the prologue instruction.
> + gdb_test "break *\$pc" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lrcpc3.c, line $decimal\\." \
> + "$name: break at prologue instruction"
> +
> + # Record the initial memory and register values.
> + set regs [concat $diff_reg $same_reg]
> + set vars [concat $diff_var $same_var]
> + set mem [concat $diff_mem $same_mem]
> + foreach r $regs {
> + set ${r}_initial [capture_command_output "info register $r" ""]
> + }
> + foreach v $vars {
> + set ${v}_initial [get_valueof "/x" "$v" "unable to read $v" \
> + "$name: get $v initial value"]
> + }
> + foreach m $mem {
> + set ${m}_initial [capture_command_output "x/x $m" ""]
> + }
> +
> + gdb_test "break $after_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-lrcpc3.c, line $decimal\\." \
> + "$name: break after instruction sequence"
> + gdb_continue_to_breakpoint "$name: executed instruction sequence" \
> + [multi_line ".*/aarch64-lrcpc3.c:$decimal" ".*"]
> +
> + # Record the new memory and register values.
> + foreach r $regs {
> + set ${r}_new [capture_command_output "info register $r" ""]
> + }
> + foreach v $vars {
> + set ${v}_new [get_valueof "/x" "$v" "unable to read $v" \
> + "$name: get $v new value"]
> + }
> + foreach m $mem {
> + set ${m}_new [capture_command_output "x/x $m" ""]
> + }
> +
> + # Execute in reverse to before the instruction sequence.
> + gdb_test_no_output "set exec-direction reverse"
This message will be the same for all calls of test_single_arm, causing
DUPLICATE errors.
> + gdb_continue_to_breakpoint "reversed execution of instruction sequence" \
> + [multi_line ".*/aarch64-lrcpc3.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
Same here.
> + # Record the final memory and register values.
> + foreach r $regs {
> + set ${r}_final [capture_command_output "info register $r" ""]
> + }
> + foreach v $vars {
> + set ${v}_final [get_valueof "/x" "$v" "unable to read $v" \
> + "$name: get $v final value"]
> + }
> + foreach m $mem {
> + set ${m}_final [capture_command_output "x/x $m" ""]
> + }
> +
> + foreach v [concat $same_reg $same_var $same_mem] {
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_new]] \
> + "$name: check $v initial value versus $v new value"
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
> + "$name: check $v initial value versus $v final value"
> + }
> +
> + foreach v [concat $diff_reg $diff_var $diff_mem] {
> + gdb_assert [string compare [set ${v}_initial] [set ${v}_new]] \
> + "$name: check $v initial value versus $v new value"
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
> + "$name: check $v initial value versus $v final value"
> + }
> +
> + # Restore forward execution and go to end of recording.
> + gdb_test_no_output "set exec-direction forward"
> + gdb_test "record goto end" \
> + [multi_line \
> + "Go forward to insn number $decimal" \
> + "#0 main \\(\\) at .*/aarch64-lrcpc3.c:$decimal" \
> + ".*"]
The two tests above will also cause DUPLICATE errors.
Instead of manually adding $name to test names to differentiate between
calls of test_single_asm, I suggest wrapping the whole body of this
function in with_test_prefix $name { ... }. This will make sure all of
them are covered.
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 28709004570..c6afc556548 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -5287,6 +5287,69 @@ gdb_caching_proc allow_aarch64_fpmr_tests {} {
> return $allow_fpmr_tests
> }
>
> +# Run a test on the target to see if it supports AArch64 LRCPC3 (Load-Acquire
> +# RCpc instructions) extensions. Return 1 if so, 0 if it does not. Note this
> +# causes a restart of GDB.
> +
> +gdb_caching_proc allow_aarch64_lrcpc3_tests {} {
> + global srcdir subdir gdb_prompt inferior_exited_re
> +
> + set me "allow_aarch64_lrcpc3_tests"
> +
> + if { ![is_aarch64_target] } {
> + return 0
> + }
> +
> + # Take the opportunity to check whether the toolchain knows about LRCPC3.
> + set compile_flags "{additional_flags=-march=armv8-a+rcpc3}"
> +
> + # Compile a program that tests the LRCPC3 feature.
> + set src {
> + #include <stdint.h>
> +
> + int main() {
> + uint64_t a, b = 0;
> + uint64_t *ptr;
> + *ptr = 0xdeadbeef;
I don't understand what the line above is intended to do. It just
crashes the test program and causes the gdb_expect below to take the
default case, before the ldiapp instruction has a chance to be executed:
(gdb) run
Starting program: /home/bauermann/.cache/builds/gdb-native/gdb/testsuite/temp/1360/allow_aarch64_lrcpc3_tests.x
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/aarch64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x0000aaaaaaaa077c in main () at /home/bauermann/.cache/builds/gdb-native/gdb/testsuite/temp/1360/allow_aarch64_lrcpc3_tests.c:7
warning: 7 /home/bauermann/.cache/builds/gdb-native/gdb/testsuite/temp/1360/allow_aarch64_lrcpc3_tests.c: No such file or directory
(gdb) WARNING:
allow_aarch64_lrcpc3_tests: default case taken
gdb_caching_proc allow_aarch64_lrcpc3_tests caused gdb_exit to be called
gdb_caching_proc allow_aarch64_lrcpc3_tests marked as called
gdb_caching_proc get_compiler_info_1 marked as called
gdb_caching_proc is_aarch32_target marked as called
gdb_caching_proc universal_compile_options_c marked as called
UNSUPPORTED: gdb.reverse/aarch64-lrcpc3.exp: require failed: allow_aarch64_lrcpc3_tests
If I make ptr point to a new variable c, then I get the expected SIGILL
when running in a machine which doesn't support LRCPC3.
> + __asm__ volatile ("ldiapp %0, %1, [%2]\n"
> + : "=r"(a), "=r"(b)
> + : "r"(ptr)
> + : "memory");
> +
> + return 0;
> + }
> + }
> +
> + if {![gdb_simple_compile $me $src executable $compile_flags]} {
> + return 0
> + }
> +
> + # Compilation succeeded so now run it via gdb.
> + clean_restart
> + gdb_load $obj
> + gdb_run_cmd
> + gdb_expect {
> + -re ".*Illegal instruction.*${gdb_prompt} $" {
> + verbose -log "\n$me lrcpc3 support not detected"
> + set allow_lrcpc3_tests 0
> + }
> + -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
> + verbose -log "\n$me: lrcpc3 support detected"
> + set allow_lrcpc3_tests 1
> + }
> + default {
> + warning "\n$me: default case taken"
> + set allow_lrcpc3_tests 0
> + }
> + }
> + gdb_exit
> + remote_file build delete $obj
> +
> + verbose "$me: returning $allow_lrcpc3_tests" 2
> + return $allow_lrcpc3_tests
> +}
--
Thiago
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 2/4] gdb/aarch64: Test record/replay support for CSSC
2026-04-20 21:52 ` [PATCH 2/4] gdb/aarch64: Test record/replay support for CSSC Ezra.Sitorus
` (2 preceding siblings ...)
2026-04-25 6:10 ` Thiago Jung Bauermann
@ 2026-04-25 7:28 ` Thiago Jung Bauermann
3 siblings, 0 replies; 23+ messages in thread
From: Thiago Jung Bauermann @ 2026-04-25 7:28 UTC (permalink / raw)
To: Ezra.Sitorus; +Cc: gdb-patches, luis.machado.foss
<Ezra.Sitorus@arm.com> writes:
> +proc test_single_asm { name } {
> + global decimal hex
> +
> + set before_seq [gdb_get_line_number "Before ${name}"]
> + set after_seq [gdb_get_line_number "After ${name}"]
> + set regs { x19 x20 x21 }
> +
> + set insn [lindex [split $name "-"] 0]
Unlike the aarch64-lrcpc3.exp test from the previous patch, this one
uses $insn rather than $name to differentiate the test names. As a
result, for instructions which are tested twice (e.g., smax-1 and
smax-2) all the tests in this function cause DUPLICATE errors.
My suggestion here is the same as the one for the previous patch: wrap
the body of the function in with_test_prefix $name { ... }.
> +
> + gdb_test "break $before_seq" \
> + "Breakpoint $decimal at $hex: file .*/aarch64-cssc.c, line $decimal\\." \
> + "$insn: break before instruction sequence"
> + gdb_continue_to_breakpoint "$insn: about to execute instruction sequence" \
> + [multi_line ".*/aarch64-cssc.c:$decimal" \
> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn} \[^\r\n\]+\""]
--
Thiago
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 3/4] gdb/aarch64: record/replay support for RPRFM and PRFM (reg)
2026-04-20 21:52 ` [PATCH 3/4] gdb/aarch64: record/replay support for RPRFM and PRFM (reg) Ezra.Sitorus
2026-04-24 7:08 ` Luis
2026-04-24 16:56 ` Guinevere Larsen
@ 2026-04-25 8:12 ` Thiago Jung Bauermann
2 siblings, 0 replies; 23+ messages in thread
From: Thiago Jung Bauermann @ 2026-04-25 8:12 UTC (permalink / raw)
To: Ezra.Sitorus; +Cc: gdb-patches, luis.machado.foss
<Ezra.Sitorus@arm.com> writes:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> The PRFM (register) instruction variant was unsupported. This is added
> along with RPRFM.
>
> No testcase has been added as these are hint instructions which don't
> modify registers/memory. I'm happy to add them in if you think these are
> required.
>
> The full testsuite was done on aarch64-none-linux-gnu without RPRFM.
> The gdb.arch and gdb.reverse tests were run on Shrinkwrap with RPRFM
> support. I've observed unrelated regressions that appear to be due to
> Shrinkwrap rather than my patch.
> ---
> gdb/aarch64-tdep.c | 5 +++++
> 1 file changed, 5 insertions(+)
Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
This patch made me notice that 1. Arm has a PRFUM instruction and 2.,
it's not supported by aarch64_record_load_store either.
Considering that there will be a v2, would you mind adding support for
it as well, for completeness? I think that would complete support for
the prefetch instructions.
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index 81d4f160f8f..61c462979b3 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -5914,6 +5914,11 @@ aarch64_record_load_store (aarch64_insn_decode_record *aarch64_insn_r)
> else
> if (size_bits != 0x03)
> ld_flag = 0x01;
> + else if (size_bits == 0x3 && vector_flag == 0x0 && opc == 0x2)
> + {
> + /* PRFM (register) or RPRFM */
> + return AARCH64_RECORD_SUCCESS;
> + }
> else
> return AARCH64_RECORD_UNKNOWN;
--
Thiago
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 4/4] gdb/aarch64: record/replay support for LSE128
2026-04-20 21:52 ` [PATCH 4/4] gdb/aarch64: record/replay support for LSE128 Ezra.Sitorus
2026-04-24 7:11 ` Luis
2026-04-24 17:12 ` Guinevere Larsen
@ 2026-04-25 8:38 ` Thiago Jung Bauermann
2 siblings, 0 replies; 23+ messages in thread
From: Thiago Jung Bauermann @ 2026-04-25 8:38 UTC (permalink / raw)
To: Ezra.Sitorus; +Cc: gdb-patches, luis.machado.foss
<Ezra.Sitorus@arm.com> writes:
> From: Ezra Sitorus <ezra.sitorus@arm.com>
>
> FEAT_LSE128 introduces support for 128-bit atomic instructions. This
> patch teaches GDB to decode these instructions for recording and
> reversing.
>
> Regression tested on aarch64-none-linux-gnu on QEMU with LSE128 support.
> I observe unrelated regressions that appear to be environmental rather
> than caused by this patch.
> ---
> gdb/aarch64-tdep.c | 13 ++
> gdb/testsuite/gdb.reverse/aarch64-lse128.c | 139 +++++++++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-lse128.exp | 138 ++++++++++++++++++
> gdb/testsuite/lib/gdb.exp | 56 ++++++++
> 4 files changed, 346 insertions(+)
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.c
> create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lse128.exp
For this patch I only have the same comments I had about the testcase in
patch 1: the copyright year, the formatting of the if condition block,
the top-level return statements and the with_test_prefix change to avoid
DUPLICATE errors.
--
Thiago
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-04-25 8:38 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-04-20 21:52 [PATCH 0/4] gdb/aarch64: record/replay support for AArch64 Ezra.Sitorus
2026-04-20 21:52 ` [PATCH 1/4] gdb/aarch64: record/replay support for LRCPC3 Ezra.Sitorus
2026-04-24 7:00 ` Luis
2026-04-24 15:09 ` Guinevere Larsen
2026-04-25 5:08 ` Thiago Jung Bauermann
2026-04-25 7:21 ` Thiago Jung Bauermann
2026-04-20 21:52 ` [PATCH 2/4] gdb/aarch64: Test record/replay support for CSSC Ezra.Sitorus
2026-04-24 7:07 ` Luis
2026-04-24 16:53 ` Guinevere Larsen
2026-04-25 6:10 ` Thiago Jung Bauermann
2026-04-25 7:28 ` Thiago Jung Bauermann
2026-04-20 21:52 ` [PATCH 3/4] gdb/aarch64: record/replay support for RPRFM and PRFM (reg) Ezra.Sitorus
2026-04-24 7:08 ` Luis
2026-04-24 16:56 ` Guinevere Larsen
2026-04-25 8:12 ` Thiago Jung Bauermann
2026-04-20 21:52 ` [PATCH 4/4] gdb/aarch64: record/replay support for LSE128 Ezra.Sitorus
2026-04-24 7:11 ` Luis
2026-04-24 17:12 ` Guinevere Larsen
2026-04-25 8:38 ` Thiago Jung Bauermann
2026-04-23 17:31 ` [PATCH 0/4] gdb/aarch64: record/replay support for AArch64 Guinevere Larsen
2026-04-23 22:20 ` Ezra Sitorus
2026-04-24 5:33 ` Thiago Jung Bauermann
2026-04-24 7:16 ` Luis
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox