[v3] gdb/aarch64: record/replay support for LRCPC3
Checks
| Context |
Check |
Description |
| linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gdb_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gdb_check--master-arm |
success
|
Test passed
|
| linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 |
success
|
Test passed
|
Commit Message
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 and LLVM.
---
Changes from v2:
* Renamed the constant to HWCAP2_LRCPC3.
* Fixed up the HWCAP bit check. It fails correctly on systems without
LRCPC3 support.
* Cleaned up the test argument lists.
Ezra
gdb/aarch64-tdep.c | 87 ++++++++
gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c | 216 +++++++++++++++++++
gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp | 190 ++++++++++++++++
gdb/testsuite/lib/gdb.exp | 59 +++++
4 files changed, 552 insertions(+)
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
create mode 100644 gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
Comments
On 5/29/26 8:01 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 and LLVM.
>
> ---
Hi Ezra!
This looks ready to go in from the record-full side. Feel free to add my tag
Approved-By: Guinevere Larsen <guinevere@redhat.com> (record-full)
You should wait for Luis's or Thiago's approval for the aarch64 parts
before pushing it, though, as I only approved the test parts and ack the
changes to the recording part
> Changes from v2:
> * Renamed the constant to HWCAP2_LRCPC3.
> * Fixed up the HWCAP bit check. It fails correctly on systems without
> LRCPC3 support.
> * Cleaned up the test argument lists.
>
> Ezra
>
> gdb/aarch64-tdep.c | 87 ++++++++
> gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c | 216 +++++++++++++++++++
> gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp | 190 ++++++++++++++++
> gdb/testsuite/lib/gdb.exp | 59 +++++
> 4 files changed, 552 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 673815c2..0cce4072 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 00000000..d61d3239
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.c
> @@ -0,0 +1,216 @@
> +/* 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 <stdalign.h>
> +#include <stdint.h>
> +#include <string.h>
> +
> +#define INITIAL_STRING "This is just some string."
> +#define BUF_SIZE sizeof (INITIAL_STRING)
> +
> +#define PREPARE_SRC_AND_PTR(OFFSET) \
> + strcpy (src, INITIAL_STRING); \
> + __asm__ volatile ("mov x21, %0" \
> + : \
> + : "r"((uint64_t *)((uint8_t *)src + (OFFSET))) \
> + : "x21")
> +
> +#define PREPARE_GPR(OFFSET) \
> + __asm__ volatile ("mov x19, #0\n" ::: "x19"); \
> + __asm__ volatile ("mov x20, #0\n" ::: "x20"); \
> + PREPARE_SRC_AND_PTR (OFFSET)
> +
> +#define PREPARE_VECTOR_REG(OFFSET) \
> + __asm__ volatile ("movi v22.2d, #0\n" ::: "v22"); \
> + PREPARE_SRC_AND_PTR (OFFSET)
> +
> +int
> +main (void)
> +{
> + alignas (16) char src[BUF_SIZE];
> +
> + PREPARE_GPR (0);
> + /* Before ldiapp-0. */
> + __asm__ volatile ("ldiapp x19, x20, [x21]\n" : : : "x19", "x20", "memory");
> + /* After ldiapp-0. */
> +
> + PREPARE_GPR (0);
> + /* Before ldiapp-1. */
> + __asm__ volatile ("ldiapp w19, w20, [x21]\n" : : : "x19", "x20", "memory");
> + /* After ldiapp-1. */
> +
> + PREPARE_GPR (0);
> + /* Before ldiapp-2. */
> + __asm__ volatile ("ldiapp x19, x20, [x21], #16\n"
> + :
> + :
> + : "x19", "x20", "x21", "memory");
> + /* After ldiapp-2. */
> +
> + PREPARE_GPR (0);
> + /* Before ldiapp-3. */
> + __asm__ volatile ("ldiapp w19, w20, [x21], #8\n"
> + :
> + :
> + : "x19", "x20", "x21", "memory");
> + /* After ldiapp-3. */
> + /* Register overlap between source and destination registers. Since there is
> + no offset, writeback is disabled. */
> +
> + PREPARE_GPR (0);
> + /* Before ldiapp-4. */
> + __asm__ volatile ("ldiapp x21, x20, [x21]\n" : : : "x20", "x21", "memory");
> + /* After ldiapp-4. */
> +
> + PREPARE_GPR (0);
> + /* Before ldiapp-5. */
> + __asm__ volatile ("ldiapp w21, w20, [x21]\n" : : : "x20", "x21", "memory");
> + /* After ldiapp-5. */
> +
> + PREPARE_GPR (0);
> + /* Before stilp-0. */
> + __asm__ volatile ("stilp x19, x20, [x21]\n" : : : "memory");
> + /* After stilp-0. */
> +
> + PREPARE_GPR (0);
> + /* Before stilp-1. */
> + __asm__ volatile ("stilp w19, w20, [x21]\n" : : : "memory");
> + /* After stilp-1. */
> +
> + PREPARE_GPR (16);
> + /* Before stilp-2. */
> + __asm__ volatile ("stilp x19, x20, [x21, #-16]!\n" : : : "x21", "memory");
> + /* After stilp-2. */
> +
> + PREPARE_GPR (8);
> + /* Before stilp-3. */
> + __asm__ volatile ("stilp w19, w20, [x21, #-8]!\n" : : : "x21", "memory");
> + /* After stilp-3. */
> + /* Register overlap. Since there is no offset, writeback is disabled. */
> +
> + PREPARE_GPR (0);
> + /* Before stilp-4. */
> + __asm__ volatile ("stilp x21, x20, [x21]\n" : : : "memory");
> + /* After stilp-4. */
> +
> + PREPARE_GPR (0);
> + /* Before stilp-5. */
> + __asm__ volatile ("stilp w21, w20, [x21]\n" : : : "memory");
> + /* After stilp-5. */
> +
> + PREPARE_GPR (0);
> + /* Before ldapr-0. */
> + __asm__ volatile ("ldapr x19, [x21], #8\n" : : : "x19", "x21", "memory");
> + /* After ldapr-0. */
> +
> + PREPARE_GPR (0);
> + /* Before ldapr-1. */
> + __asm__ volatile ("ldapr w19, [x21], #4\n" : : : "x19", "x21", "memory");
> + /* After ldapr-1. */
> +
> + PREPARE_GPR (8);
> + /* Before stlr-0. */
> + __asm__ volatile ("stlr x19, [x21, #-8]!\n" : : : "x21", "memory");
> + /* After stlr-0. */
> +
> + PREPARE_GPR (4);
> + /* Before stlr-1. */
> + __asm__ volatile ("stlr w19, [x21, #-4]!\n" : : : "x21", "memory");
> + /* After stlr-1. */
> +
> + PREPARE_VECTOR_REG (0);
> + /* Before ldap1-0. */
> + __asm__ volatile ("ldap1 {v22.d}[0], [x21]\n" : : : "v22", "memory");
> + /* After ldap1-0. */
> +
> + PREPARE_VECTOR_REG (0);
> + /* Before stl1-0. */
> + __asm__ volatile ("stl1 {v22.d}[0], [x21]\n" : : : "memory");
> + /* After stl1-0. */
> +
> + PREPARE_VECTOR_REG (0);
> + /* Before ldapur-0. */
> + __asm__ volatile ("ldapur d22, [x21]\n" : : : "v22", "memory");
> + /* After ldapur-0. */
> +
> + PREPARE_VECTOR_REG (0);
> + /* Before stlur-0. */
> + __asm__ volatile ("stlur d22, [x21]\n" : : : "memory");
> + /* After stlur-0. */
> +
> + PREPARE_VECTOR_REG (256);
> + /* Before ldapur-1. */
> + __asm__ volatile ("ldapur d22, [x21, #-256]\n" : : : "v22", "memory");
> + /* After ldapur-1. */
> +
> + PREPARE_VECTOR_REG (256);
> + /* Before stlur-1. */
> + __asm__ volatile ("stlur d22, [x21, #-256]\n" : : : "memory");
> + /* After stlur-1. */
> +
> + PREPARE_VECTOR_REG (-255);
> + /* Before ldapur-2. */
> + __asm__ volatile ("ldapur d22, [x21, #255]\n" : : : "v22", "memory");
> + /* After ldapur-2. */
> +
> + PREPARE_VECTOR_REG (-255);
> + /* Before stlur-2. */
> + __asm__ volatile ("stlur d22, [x21, #255]\n" : : : "memory");
> + /* After stlur-2. */
> +
> + PREPARE_VECTOR_REG (0);
> + /* Before ldapur-3. */
> + __asm__ volatile ("ldapur h22, [x21]\n" : : : "v22", "memory");
> + /* After ldapur-3. */
> +
> + PREPARE_VECTOR_REG (0);
> + /* Before stlur-3. */
> + __asm__ volatile ("stlur h22, [x21]\n" : : : "memory");
> + /* After stlur-3. */
> +
> + PREPARE_VECTOR_REG (0);
> + /* Before ldapur-4. */
> + __asm__ volatile ("ldapur s22, [x21]\n" : : : "v22", "memory");
> + /* After ldapur-4. */
> +
> + PREPARE_VECTOR_REG (0);
> + /* Before stlur-4. */
> + __asm__ volatile ("stlur s22, [x21]\n" : : : "memory");
> + /* After stlur-4. */
> +
> + PREPARE_VECTOR_REG (0);
> + /* Before ldapur-5. */
> + __asm__ volatile ("ldapur d22, [x21]\n" : : : "v22", "memory");
> + /* After ldapur-5. */
> +
> + PREPARE_VECTOR_REG (0);
> + /* Before stlur-5. */
> + __asm__ volatile ("stlur d22, [x21]\n" : : : "memory");
> + /* After stlur-5. */
> +
> + PREPARE_VECTOR_REG (0);
> + /* Before ldapur-6. */
> + __asm__ volatile ("ldapur q22, [x21]\n" : : : "v22", "memory");
> + /* After ldapur-6. */
> +
> + PREPARE_VECTOR_REG (0);
> + /* Before stlur-6. */
> + __asm__ volatile ("stlur q22, [x21]\n" : : : "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 00000000..b9e48147
> --- /dev/null
> +++ b/gdb/testsuite/gdb.reverse/aarch64-lrcpc3.exp
> @@ -0,0 +1,190 @@
> +# 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
> +}
> +
> +if {![runto_main]} {
> + return
> +}
> +
> +gdb_test_no_output "record full"
> +
> +proc test_single_asm {name diff_reg diff_mem same_reg same_mem} {
> + with_test_prefix $name {
> +
> + 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}\\." \
> + "$insn before instruction sequence"
> +
> + gdb_continue_to_breakpoint "about to execute instruction sequence" \
> + [multi_line ".*/aarch64-lrcpc3.c:${::decimal}" \
> + "${::decimal}\[ \t\]+__asm__ volatile \\(\"${insn} .*\".*"]
> +
> + # 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}\\." \
> + "break at prologue instruction"
> +
> + # Record the initial memory and register values.
> + set regs [concat $diff_reg $same_reg]
> + set mem [concat $diff_mem $same_mem]
> + foreach r $regs {
> + set ${r}_initial [capture_command_output "info register $r" ""]
> + }
> + 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}\\." \
> + "$insn after instruction sequence"
> + gdb_continue_to_breakpoint "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 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} .*\".*"]
> +
> + # Record the final memory and register values.
> + foreach r $regs {
> + set ${r}_final [capture_command_output "info register $r" ""]
> + }
> + foreach m $mem {
> + set ${m}_final [capture_command_output "x/x $m" ""]
> + }
> +
> + foreach v [concat $same_reg $same_mem] {
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_new]] \
> + "check $v initial value versus $v new value"
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
> + "check $v initial value versus $v final value"
> + }
> +
> + foreach v [concat $diff_reg $diff_mem] {
> + gdb_assert [string compare [set ${v}_initial] [set ${v}_new]] \
> + "check $v initial value versus $v new value"
> + gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
> + "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 } { } { x21 } { src } }
> + { ldiapp-1 { w19 w20 } { } { x21 } { src } }
> + { ldiapp-2 { x19 x20 x21 } { } { } { src } }
> + { ldiapp-3 { w19 w20 x21 } { } { } { src } }
> + { ldiapp-4 { x21 x20 } { } { } { src } }
> + { ldiapp-5 { w21 w20 } { } { } { src } }
> +}
> +
> +set stilp_cases {
> + { stilp-0 { } { src } { x19 x20 x21 } { } }
> + { stilp-1 { } { src } { w19 w20 x21 } { } }
> + { stilp-2 { x21 } { src } { x19 x20 } { } }
> + { stilp-3 { x21 } { src } { x19 x20 } { } }
> + { stilp-4 { } { src } { x20 x21 } { } }
> + { stilp-5 { } { src } { w20 x21 } { } }
> +}
> +
> +set ldapr_stlr_cases {
> + { ldapr-0 { x19 x21 } { } { } { src } }
> + { ldapr-1 { w19 x21 } { } { } { src } }
> + { stlr-0 { x21 } { src } { x19 } { } }
> + { stlr-1 { x21 } { src } { w19 } { } }
> +}
> +
> +set ldap1_stl1_cases {
> + { ldap1-0 { v22 } { } { x21 } { src } }
> + { stl1-0 { } { src } { v22 } { } }
> +}
> +
> +set ldapur_stlur_cases {
> + { ldapur-0 { v22 } { } { x21 } { src } }
> + { stlur-0 { } { src } { x21 v22 } { } }
> + { ldapur-1 { v22 } { } { x21 } { src } }
> + { stlur-1 { } { src } { x21 v22 } { } }
> + { ldapur-2 { v22 } { } { x21 } { src } }
> + { stlur-2 { } { src } { x21 v22 } { } }
> + { ldapur-3 { v22 } { } { x21 } { src } }
> + { stlur-3 { } { src } { x21 v22 } { } }
> + { ldapur-4 { v22 } { } { x21 } { src } }
> + { stlur-4 { } { src } { x21 v22 } { } }
> + { ldapur-5 { v22 } { } { x21 } { src } }
> + { stlur-5 { } { src } { x21 v22 } { } }
> + { ldapur-6 { v22 } { } { x21 } { src } }
> + { stlur-6 { } { src } { x21 v22 } { } }
> +}
> +
> +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_mem same_reg same_mem
> + test_single_asm $name $diff_reg $diff_mem $same_reg $same_mem
> +}
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index cd59ada7..07f9c8df 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -5287,6 +5287,65 @@ 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 <stdbool.h>
> + #include <sys/auxv.h>
> +
> + /* Feature check for LRCPC3. */
> + #ifndef HWCAP2_LRCPC3
> + #define HWCAP2_LRCPC3 (1ULL << 46)
> + #endif
> +
> + int main (void) {
> + bool lrcpc3_supported = getauxval (AT_HWCAP2) & HWCAP2_LRCPC3;
> +
> + /* Return success if LRCPC3 is supported. */
> + return !lrcpc3_supported;
> + }
> + }
> +
> + 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 ".*$inferior_exited_re normally.*${gdb_prompt} $" {
> + verbose -log "\n$me: lrcpc3 support detected"
> + set allow_lrcpc3_tests 1
> + }
> + -re ".*$inferior_exited_re with code 01.*${gdb_prompt} $" {
> + verbose -log "\n$me: lrcpc3 support not detected"
> + 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 the AArch64 CSSC feature.
> # Return 1 if so, 0 if it does not. Note this causes a restart of GDB.
>
Guinevere Larsen <guinevere@redhat.com> writes:
> On 5/29/26 8:01 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 and LLVM.
>>
>> ---
>
> This looks ready to go in from the record-full side. Feel free to add my tag
>
> Approved-By: Guinevere Larsen <guinevere@redhat.com> (record-full)
>
> You should wait for Luis's or Thiago's approval for the aarch64 parts before pushing it,
> though, as I only approved the test parts and ack the changes to the recording part
Thank you Guinevere for the review, and thank you Ezra for v3.
This version looks good to me.
Approved-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Thanks all for the reviews - they've all been pushed now!
Ezra
@@ -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)
new file mode 100644
@@ -0,0 +1,216 @@
+/* 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 <stdalign.h>
+#include <stdint.h>
+#include <string.h>
+
+#define INITIAL_STRING "This is just some string."
+#define BUF_SIZE sizeof (INITIAL_STRING)
+
+#define PREPARE_SRC_AND_PTR(OFFSET) \
+ strcpy (src, INITIAL_STRING); \
+ __asm__ volatile ("mov x21, %0" \
+ : \
+ : "r"((uint64_t *)((uint8_t *)src + (OFFSET))) \
+ : "x21")
+
+#define PREPARE_GPR(OFFSET) \
+ __asm__ volatile ("mov x19, #0\n" ::: "x19"); \
+ __asm__ volatile ("mov x20, #0\n" ::: "x20"); \
+ PREPARE_SRC_AND_PTR (OFFSET)
+
+#define PREPARE_VECTOR_REG(OFFSET) \
+ __asm__ volatile ("movi v22.2d, #0\n" ::: "v22"); \
+ PREPARE_SRC_AND_PTR (OFFSET)
+
+int
+main (void)
+{
+ alignas (16) char src[BUF_SIZE];
+
+ PREPARE_GPR (0);
+ /* Before ldiapp-0. */
+ __asm__ volatile ("ldiapp x19, x20, [x21]\n" : : : "x19", "x20", "memory");
+ /* After ldiapp-0. */
+
+ PREPARE_GPR (0);
+ /* Before ldiapp-1. */
+ __asm__ volatile ("ldiapp w19, w20, [x21]\n" : : : "x19", "x20", "memory");
+ /* After ldiapp-1. */
+
+ PREPARE_GPR (0);
+ /* Before ldiapp-2. */
+ __asm__ volatile ("ldiapp x19, x20, [x21], #16\n"
+ :
+ :
+ : "x19", "x20", "x21", "memory");
+ /* After ldiapp-2. */
+
+ PREPARE_GPR (0);
+ /* Before ldiapp-3. */
+ __asm__ volatile ("ldiapp w19, w20, [x21], #8\n"
+ :
+ :
+ : "x19", "x20", "x21", "memory");
+ /* After ldiapp-3. */
+ /* Register overlap between source and destination registers. Since there is
+ no offset, writeback is disabled. */
+
+ PREPARE_GPR (0);
+ /* Before ldiapp-4. */
+ __asm__ volatile ("ldiapp x21, x20, [x21]\n" : : : "x20", "x21", "memory");
+ /* After ldiapp-4. */
+
+ PREPARE_GPR (0);
+ /* Before ldiapp-5. */
+ __asm__ volatile ("ldiapp w21, w20, [x21]\n" : : : "x20", "x21", "memory");
+ /* After ldiapp-5. */
+
+ PREPARE_GPR (0);
+ /* Before stilp-0. */
+ __asm__ volatile ("stilp x19, x20, [x21]\n" : : : "memory");
+ /* After stilp-0. */
+
+ PREPARE_GPR (0);
+ /* Before stilp-1. */
+ __asm__ volatile ("stilp w19, w20, [x21]\n" : : : "memory");
+ /* After stilp-1. */
+
+ PREPARE_GPR (16);
+ /* Before stilp-2. */
+ __asm__ volatile ("stilp x19, x20, [x21, #-16]!\n" : : : "x21", "memory");
+ /* After stilp-2. */
+
+ PREPARE_GPR (8);
+ /* Before stilp-3. */
+ __asm__ volatile ("stilp w19, w20, [x21, #-8]!\n" : : : "x21", "memory");
+ /* After stilp-3. */
+ /* Register overlap. Since there is no offset, writeback is disabled. */
+
+ PREPARE_GPR (0);
+ /* Before stilp-4. */
+ __asm__ volatile ("stilp x21, x20, [x21]\n" : : : "memory");
+ /* After stilp-4. */
+
+ PREPARE_GPR (0);
+ /* Before stilp-5. */
+ __asm__ volatile ("stilp w21, w20, [x21]\n" : : : "memory");
+ /* After stilp-5. */
+
+ PREPARE_GPR (0);
+ /* Before ldapr-0. */
+ __asm__ volatile ("ldapr x19, [x21], #8\n" : : : "x19", "x21", "memory");
+ /* After ldapr-0. */
+
+ PREPARE_GPR (0);
+ /* Before ldapr-1. */
+ __asm__ volatile ("ldapr w19, [x21], #4\n" : : : "x19", "x21", "memory");
+ /* After ldapr-1. */
+
+ PREPARE_GPR (8);
+ /* Before stlr-0. */
+ __asm__ volatile ("stlr x19, [x21, #-8]!\n" : : : "x21", "memory");
+ /* After stlr-0. */
+
+ PREPARE_GPR (4);
+ /* Before stlr-1. */
+ __asm__ volatile ("stlr w19, [x21, #-4]!\n" : : : "x21", "memory");
+ /* After stlr-1. */
+
+ PREPARE_VECTOR_REG (0);
+ /* Before ldap1-0. */
+ __asm__ volatile ("ldap1 {v22.d}[0], [x21]\n" : : : "v22", "memory");
+ /* After ldap1-0. */
+
+ PREPARE_VECTOR_REG (0);
+ /* Before stl1-0. */
+ __asm__ volatile ("stl1 {v22.d}[0], [x21]\n" : : : "memory");
+ /* After stl1-0. */
+
+ PREPARE_VECTOR_REG (0);
+ /* Before ldapur-0. */
+ __asm__ volatile ("ldapur d22, [x21]\n" : : : "v22", "memory");
+ /* After ldapur-0. */
+
+ PREPARE_VECTOR_REG (0);
+ /* Before stlur-0. */
+ __asm__ volatile ("stlur d22, [x21]\n" : : : "memory");
+ /* After stlur-0. */
+
+ PREPARE_VECTOR_REG (256);
+ /* Before ldapur-1. */
+ __asm__ volatile ("ldapur d22, [x21, #-256]\n" : : : "v22", "memory");
+ /* After ldapur-1. */
+
+ PREPARE_VECTOR_REG (256);
+ /* Before stlur-1. */
+ __asm__ volatile ("stlur d22, [x21, #-256]\n" : : : "memory");
+ /* After stlur-1. */
+
+ PREPARE_VECTOR_REG (-255);
+ /* Before ldapur-2. */
+ __asm__ volatile ("ldapur d22, [x21, #255]\n" : : : "v22", "memory");
+ /* After ldapur-2. */
+
+ PREPARE_VECTOR_REG (-255);
+ /* Before stlur-2. */
+ __asm__ volatile ("stlur d22, [x21, #255]\n" : : : "memory");
+ /* After stlur-2. */
+
+ PREPARE_VECTOR_REG (0);
+ /* Before ldapur-3. */
+ __asm__ volatile ("ldapur h22, [x21]\n" : : : "v22", "memory");
+ /* After ldapur-3. */
+
+ PREPARE_VECTOR_REG (0);
+ /* Before stlur-3. */
+ __asm__ volatile ("stlur h22, [x21]\n" : : : "memory");
+ /* After stlur-3. */
+
+ PREPARE_VECTOR_REG (0);
+ /* Before ldapur-4. */
+ __asm__ volatile ("ldapur s22, [x21]\n" : : : "v22", "memory");
+ /* After ldapur-4. */
+
+ PREPARE_VECTOR_REG (0);
+ /* Before stlur-4. */
+ __asm__ volatile ("stlur s22, [x21]\n" : : : "memory");
+ /* After stlur-4. */
+
+ PREPARE_VECTOR_REG (0);
+ /* Before ldapur-5. */
+ __asm__ volatile ("ldapur d22, [x21]\n" : : : "v22", "memory");
+ /* After ldapur-5. */
+
+ PREPARE_VECTOR_REG (0);
+ /* Before stlur-5. */
+ __asm__ volatile ("stlur d22, [x21]\n" : : : "memory");
+ /* After stlur-5. */
+
+ PREPARE_VECTOR_REG (0);
+ /* Before ldapur-6. */
+ __asm__ volatile ("ldapur q22, [x21]\n" : : : "v22", "memory");
+ /* After ldapur-6. */
+
+ PREPARE_VECTOR_REG (0);
+ /* Before stlur-6. */
+ __asm__ volatile ("stlur q22, [x21]\n" : : : "memory");
+ /* After stlur-6. */
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,190 @@
+# 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
+}
+
+if {![runto_main]} {
+ return
+}
+
+gdb_test_no_output "record full"
+
+proc test_single_asm {name diff_reg diff_mem same_reg same_mem} {
+ with_test_prefix $name {
+
+ 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}\\." \
+ "$insn before instruction sequence"
+
+ gdb_continue_to_breakpoint "about to execute instruction sequence" \
+ [multi_line ".*/aarch64-lrcpc3.c:${::decimal}" \
+ "${::decimal}\[ \t\]+__asm__ volatile \\(\"${insn} .*\".*"]
+
+ # 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}\\." \
+ "break at prologue instruction"
+
+ # Record the initial memory and register values.
+ set regs [concat $diff_reg $same_reg]
+ set mem [concat $diff_mem $same_mem]
+ foreach r $regs {
+ set ${r}_initial [capture_command_output "info register $r" ""]
+ }
+ 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}\\." \
+ "$insn after instruction sequence"
+ gdb_continue_to_breakpoint "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 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} .*\".*"]
+
+ # Record the final memory and register values.
+ foreach r $regs {
+ set ${r}_final [capture_command_output "info register $r" ""]
+ }
+ foreach m $mem {
+ set ${m}_final [capture_command_output "x/x $m" ""]
+ }
+
+ foreach v [concat $same_reg $same_mem] {
+ gdb_assert ![string compare [set ${v}_initial] [set ${v}_new]] \
+ "check $v initial value versus $v new value"
+ gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
+ "check $v initial value versus $v final value"
+ }
+
+ foreach v [concat $diff_reg $diff_mem] {
+ gdb_assert [string compare [set ${v}_initial] [set ${v}_new]] \
+ "check $v initial value versus $v new value"
+ gdb_assert ![string compare [set ${v}_initial] [set ${v}_final]] \
+ "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 } { } { x21 } { src } }
+ { ldiapp-1 { w19 w20 } { } { x21 } { src } }
+ { ldiapp-2 { x19 x20 x21 } { } { } { src } }
+ { ldiapp-3 { w19 w20 x21 } { } { } { src } }
+ { ldiapp-4 { x21 x20 } { } { } { src } }
+ { ldiapp-5 { w21 w20 } { } { } { src } }
+}
+
+set stilp_cases {
+ { stilp-0 { } { src } { x19 x20 x21 } { } }
+ { stilp-1 { } { src } { w19 w20 x21 } { } }
+ { stilp-2 { x21 } { src } { x19 x20 } { } }
+ { stilp-3 { x21 } { src } { x19 x20 } { } }
+ { stilp-4 { } { src } { x20 x21 } { } }
+ { stilp-5 { } { src } { w20 x21 } { } }
+}
+
+set ldapr_stlr_cases {
+ { ldapr-0 { x19 x21 } { } { } { src } }
+ { ldapr-1 { w19 x21 } { } { } { src } }
+ { stlr-0 { x21 } { src } { x19 } { } }
+ { stlr-1 { x21 } { src } { w19 } { } }
+}
+
+set ldap1_stl1_cases {
+ { ldap1-0 { v22 } { } { x21 } { src } }
+ { stl1-0 { } { src } { v22 } { } }
+}
+
+set ldapur_stlur_cases {
+ { ldapur-0 { v22 } { } { x21 } { src } }
+ { stlur-0 { } { src } { x21 v22 } { } }
+ { ldapur-1 { v22 } { } { x21 } { src } }
+ { stlur-1 { } { src } { x21 v22 } { } }
+ { ldapur-2 { v22 } { } { x21 } { src } }
+ { stlur-2 { } { src } { x21 v22 } { } }
+ { ldapur-3 { v22 } { } { x21 } { src } }
+ { stlur-3 { } { src } { x21 v22 } { } }
+ { ldapur-4 { v22 } { } { x21 } { src } }
+ { stlur-4 { } { src } { x21 v22 } { } }
+ { ldapur-5 { v22 } { } { x21 } { src } }
+ { stlur-5 { } { src } { x21 v22 } { } }
+ { ldapur-6 { v22 } { } { x21 } { src } }
+ { stlur-6 { } { src } { x21 v22 } { } }
+}
+
+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_mem same_reg same_mem
+ test_single_asm $name $diff_reg $diff_mem $same_reg $same_mem
+}
@@ -5287,6 +5287,65 @@ 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 <stdbool.h>
+ #include <sys/auxv.h>
+
+ /* Feature check for LRCPC3. */
+ #ifndef HWCAP2_LRCPC3
+ #define HWCAP2_LRCPC3 (1ULL << 46)
+ #endif
+
+ int main (void) {
+ bool lrcpc3_supported = getauxval (AT_HWCAP2) & HWCAP2_LRCPC3;
+
+ /* Return success if LRCPC3 is supported. */
+ return !lrcpc3_supported;
+ }
+ }
+
+ 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 ".*$inferior_exited_re normally.*${gdb_prompt} $" {
+ verbose -log "\n$me: lrcpc3 support detected"
+ set allow_lrcpc3_tests 1
+ }
+ -re ".*$inferior_exited_re with code 01.*${gdb_prompt} $" {
+ verbose -log "\n$me: lrcpc3 support not detected"
+ 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 the AArch64 CSSC feature.
# Return 1 if so, 0 if it does not. Note this causes a restart of GDB.