Message ID | 20240510052408.2173579-6-thiago.bauermann@linaro.org |
---|---|
State | New |
Headers | show |
Series | Add support for AArch64 MOPS instructions | expand |
Thanks. Looks OK to me. On 5/10/24 06:24, Thiago Jung Bauermann wrote: > The testcase verifies that MOPS instructions are recorded and correctly > reversed. Not all variants of the copy and set instructions are tested, > since there are many and the record and replay target processes them in > the same way. > --- > gdb/testsuite/gdb.reverse/aarch64-mops.c | 71 +++++++++ > gdb/testsuite/gdb.reverse/aarch64-mops.exp | 171 +++++++++++++++++++++ > 2 files changed, 242 insertions(+) > create mode 100644 gdb/testsuite/gdb.reverse/aarch64-mops.c > create mode 100644 gdb/testsuite/gdb.reverse/aarch64-mops.exp > > No change since v1. > > diff --git a/gdb/testsuite/gdb.reverse/aarch64-mops.c b/gdb/testsuite/gdb.reverse/aarch64-mops.c > new file mode 100644 > index 000000000000..513f324b9dd6 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/aarch64-mops.c > @@ -0,0 +1,71 @@ > +/* This test program is part of GDB, the GNU debugger. > + > + Copyright 2024 Free Software Foundation, Inc. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 3 of the License, or > + (at your option) any later version. > + > + This program is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + GNU General Public License for more details. > + > + You should have received a copy of the GNU General Public License > + along with this program. If not, see <http://www.gnu.org/licenses/>. */ > + > +#define TEST_STRING "Just a test string." > +#define BUF_SIZE sizeof(TEST_STRING) > + > +int > +main (void) > +{ > + char dest[BUF_SIZE]; > + char source[BUF_SIZE] = TEST_STRING; > + register char *p asm ("x19"); > + register char *q asm ("x20"); > + register long size asm ("x21"); > + register long zero asm ("x22"); > + > + p = dest; > + size = BUF_SIZE; > + zero = 0; > + /* Before setp. */ > + /* memset implemented in MOPS instructions. */ > + __asm__ volatile ("setp [%0]!, %1!, %2\n\t" > + "setm [%0]!, %1!, %2\n\t" > + "sete [%0]!, %1!, %2\n\t" > + : "+&r"(p), "+&r"(size) > + : "r"(zero) > + : "memory"); > + > + /* After sete. */ > + p = dest; > + q = source; > + size = BUF_SIZE; > + /* Before cpyp. */ > + /* memmove implemented in MOPS instructions. */ > + __asm__ volatile ("cpyp [%0]!, [%1]!, %2!\n\t" > + "cpym [%0]!, [%1]!, %2!\n\t" > + "cpye [%0]!, [%1]!, %2!\n\t" > + : "+&r" (p), "+&r" (q), "+&r" (size) > + : > + : "memory"); > + /* After cpye. */ > + p = dest; > + q = source; > + size = BUF_SIZE; > + /* Before cpyfp. */ > + /* memcpy implemented in MOPS instructions. */ > + __asm__ volatile ("cpyfp [%0]!, [%1]!, %2!\n\t" > + "cpyfm [%0]!, [%1]!, %2!\n\t" > + "cpyfe [%0]!, [%1]!, %2!\n\t" > + : "+&r" (p), "+&r" (q), "+&r" (size) > + : > + : "memory"); > + /* After cpyfe. */ > + p = dest; > + > + return 0; > +} > diff --git a/gdb/testsuite/gdb.reverse/aarch64-mops.exp b/gdb/testsuite/gdb.reverse/aarch64-mops.exp > new file mode 100644 > index 000000000000..f9c1257e0b11 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/aarch64-mops.exp > @@ -0,0 +1,171 @@ > +# Copyright 2024 Free Software Foundation, Inc. > + > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 3 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program. If not, see <http://www.gnu.org/licenses/>. > + > +# Test instruction record for AArch64 FEAT_MOPS instructions. > +# Based on gdb.reverse/ppc_record_test_isa_3_1.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_mops_tests > + > +standard_testfile > + > +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ > + [list debug additional_flags=-march=armv9.3-a]] } { > + return -1 > +} > + > +if ![runto_main] { > + return -1 > +} > + > +gdb_test_no_output "record full" > + > +proc do_test { insn_prefix } { > + global decimal hex > + > + set before_seq [gdb_get_line_number "Before ${insn_prefix}p"] > + set after_seq [gdb_get_line_number "After ${insn_prefix}e"] > + > + with_test_prefix $insn_prefix { > + gdb_test "break $before_seq" \ > + "Breakpoint $decimal at $hex: file .*/aarch64-mops.c, line $decimal\\." \ > + "break before instruction sequence" > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + "" \ > + "Breakpoint $decimal, main \\(\\) at .*/aarch64-mops.c:$decimal" \ > + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn_prefix}p \[^\r\n\]+\""] \ > + "about to execute instruction sequence" > + > + # Record the initial register values. > + set x19_initial [capture_command_output "info register x19" ""] > + set x21_initial [capture_command_output "info register x21" ""] > + > + # The set instructions use the ZERO variable, but not Q, and the other > + # instructions are the opposite. > + if {[string compare $insn_prefix "set"] == 0} { > + set x22_initial [capture_command_output "info register x22" ""] > + } else { > + set x20_initial [capture_command_output "info register x20" ""] > + } > + > + gdb_test "break $after_seq" \ > + "Breakpoint $decimal at $hex: file .*/aarch64-mops.c, line $decimal\\." \ > + "break after instruction sequence" > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + "" \ > + "Breakpoint $decimal, main \\(\\) at .*/aarch64-mops.c:$decimal" \ > + "$decimal\[ \t\]+p = dest;"] \ > + "executed instruction sequence" > + > + # Record the new register values. > + set x19_new [capture_command_output "info register x19" ""] > + set x21_new [capture_command_output "info register x21" ""] > + > + if {[string compare $insn_prefix "set"] == 0} { > + set x22_new [capture_command_output "info register x22" ""] > + } else { > + set x20_new [capture_command_output "info register x20" ""] > + } > + > + # Execute in reverse to before the instruction sequence. > + gdb_test_no_output "set exec-direction reverse" > + > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + "" \ > + "Breakpoint $decimal, main \\(\\) at .*/aarch64-mops.c:$decimal" \ > + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn_prefix}p \[^\r\n\]+\""] \ > + "reversed execution of instruction sequence" > + > + # Record the final register values. > + set x19_final [capture_command_output "info register x19" ""] > + set x21_final [capture_command_output "info register x21" ""] > + > + if {[string compare $insn_prefix "set"] == 0} { > + set x22_final [capture_command_output "info register x22" ""] > + } else { > + set x20_final [capture_command_output "info register x20" ""] > + } > + > + # Check initial and new values of x19 are different. > + gdb_assert [string compare $x19_initial $x19_new] \ > + "check x19 initial value versus x19 new value" > + > + # Check initial and new values of x21 are different. > + gdb_assert [string compare $x21_initial $x21_new] \ > + "check x21 initial value versus x21 new value" > + > + if {[string compare $insn_prefix "set"] == 0} { > + # Check initial and new values of x22 are the same. > + # The register with the value to set shouldn't change. > + gdb_assert ![string compare $x22_initial $x22_new] \ > + "check x22 initial value versus x22 new value" > + } else { > + # Check initial and new values of x20 are different. > + gdb_assert [string compare $x20_initial $x20_new] \ > + "check x20 initial value versus x20 new value" > + } > + > + # Check initial and final values of x19 are the same. > + gdb_assert ![string compare $x19_initial $x19_final] \ > + "check x19 initial value versus x19 final value" > + > + # Check initial and final values of x21 are the same. > + gdb_assert ![string compare $x21_initial $x21_final] \ > + "check x21 initial value versus x21 final value" > + > + if {[string compare $insn_prefix "set"] == 0} { > + # Check initial and final values of x22 are the same. > + # The register with the value to set shouldn't change. > + gdb_assert ![string compare $x22_initial $x22_final] \ > + "check x22 initial value versus x22 final value" > + } else { > + # Check initial and final values of x20 are the same. > + gdb_assert ![string compare $x20_initial $x20_final] \ > + "check x20 initial value versus x20 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-mops.c:$decimal" \ > + "$decimal\[ \t\]+p = dest;"] > + } > +} > + > +do_test "set" > +do_test "cpy" > +do_test "cpyf"
On 5/10/24 02:24, Thiago Jung Bauermann wrote: > The testcase verifies that MOPS instructions are recorded and correctly > reversed. Not all variants of the copy and set instructions are tested, > since there are many and the record and replay target processes them in > the same way. Hi! Thanks for adding this test! I agree that, if the record backend treats all instructions the same, there's no need to test them all. As I mentioned in my reply to v2, I'd prefer if this was squashed to commit 4, but if you don't end up sending a new series, or feel strongly that they shouldn't be together, I'm ok with separating the commits. I have a couple questions inlined. > --- > gdb/testsuite/gdb.reverse/aarch64-mops.c | 71 +++++++++ > gdb/testsuite/gdb.reverse/aarch64-mops.exp | 171 +++++++++++++++++++++ > 2 files changed, 242 insertions(+) > create mode 100644 gdb/testsuite/gdb.reverse/aarch64-mops.c > create mode 100644 gdb/testsuite/gdb.reverse/aarch64-mops.exp > > No change since v1. > > diff --git a/gdb/testsuite/gdb.reverse/aarch64-mops.c b/gdb/testsuite/gdb.reverse/aarch64-mops.c > new file mode 100644 > index 000000000000..513f324b9dd6 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/aarch64-mops.c > @@ -0,0 +1,71 @@ > +/* This test program is part of GDB, the GNU debugger. > + > + Copyright 2024 Free Software Foundation, Inc. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 3 of the License, or > + (at your option) any later version. > + > + This program is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + GNU General Public License for more details. > + > + You should have received a copy of the GNU General Public License > + along with this program. If not, see <http://www.gnu.org/licenses/>. */ > + > +#define TEST_STRING "Just a test string." > +#define BUF_SIZE sizeof(TEST_STRING) > + > +int > +main (void) > +{ > + char dest[BUF_SIZE]; > + char source[BUF_SIZE] = TEST_STRING; > + register char *p asm ("x19"); > + register char *q asm ("x20"); > + register long size asm ("x21"); > + register long zero asm ("x22"); I remember that its not always easy to guarantee that a variable will end up in a register, so this has me slightly confused. Is this portable? Have you/could you double check that clang also does what you expect? If it isn't portable, please add the gcc requirement, so we don't get new failures in clang testing :) > + > + p = dest; > + size = BUF_SIZE; > + zero = 0; > + /* Before setp. */ > + /* memset implemented in MOPS instructions. */ > + __asm__ volatile ("setp [%0]!, %1!, %2\n\t" > + "setm [%0]!, %1!, %2\n\t" > + "sete [%0]!, %1!, %2\n\t" > + : "+&r"(p), "+&r"(size) > + : "r"(zero) > + : "memory"); > + > + /* After sete. */ > + p = dest; > + q = source; > + size = BUF_SIZE; > + /* Before cpyp. */ > + /* memmove implemented in MOPS instructions. */ > + __asm__ volatile ("cpyp [%0]!, [%1]!, %2!\n\t" > + "cpym [%0]!, [%1]!, %2!\n\t" > + "cpye [%0]!, [%1]!, %2!\n\t" > + : "+&r" (p), "+&r" (q), "+&r" (size) > + : > + : "memory"); > + /* After cpye. */ > + p = dest; > + q = source; > + size = BUF_SIZE; > + /* Before cpyfp. */ > + /* memcpy implemented in MOPS instructions. */ > + __asm__ volatile ("cpyfp [%0]!, [%1]!, %2!\n\t" > + "cpyfm [%0]!, [%1]!, %2!\n\t" > + "cpyfe [%0]!, [%1]!, %2!\n\t" > + : "+&r" (p), "+&r" (q), "+&r" (size) > + : > + : "memory"); > + /* After cpyfe. */ > + p = dest; > + > + return 0; > +} > diff --git a/gdb/testsuite/gdb.reverse/aarch64-mops.exp b/gdb/testsuite/gdb.reverse/aarch64-mops.exp > new file mode 100644 > index 000000000000..f9c1257e0b11 > --- /dev/null > +++ b/gdb/testsuite/gdb.reverse/aarch64-mops.exp > @@ -0,0 +1,171 @@ > +# Copyright 2024 Free Software Foundation, Inc. > + > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 3 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program. If not, see <http://www.gnu.org/licenses/>. > + > +# Test instruction record for AArch64 FEAT_MOPS instructions. > +# Based on gdb.reverse/ppc_record_test_isa_3_1.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_mops_tests > + > +standard_testfile > + > +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ > + [list debug additional_flags=-march=armv9.3-a]] } { > + return -1 > +} > + > +if ![runto_main] { > + return -1 > +} > + > +gdb_test_no_output "record full" > + > +proc do_test { insn_prefix } { more of a personal preference, maybe, but you could use: foreach_with_prefix insn_prefix {"set" "cpy" "cpyf"} > + global decimal hex > + > + set before_seq [gdb_get_line_number "Before ${insn_prefix}p"] > + set after_seq [gdb_get_line_number "After ${insn_prefix}e"] > + > + with_test_prefix $insn_prefix { > + gdb_test "break $before_seq" \ > + "Breakpoint $decimal at $hex: file .*/aarch64-mops.c, line $decimal\\." \ > + "break before instruction sequence" > + gdb_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + "" \ > + "Breakpoint $decimal, main \\(\\) at .*/aarch64-mops.c:$decimal" \ > + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn_prefix}p \[^\r\n\]+\""] \ > + "about to execute instruction sequence" Why did you choose to not use gdb_continue_to_breakpoint in here? Similar question to all other uses of "continue"
Hello Guinevere, Thank you for your review! Guinevere Larsen <blarsen@redhat.com> writes: > On 5/10/24 02:24, Thiago Jung Bauermann wrote: >> The testcase verifies that MOPS instructions are recorded and correctly >> reversed. Not all variants of the copy and set instructions are tested, >> since there are many and the record and replay target processes them in >> the same way. > > Hi! > > Thanks for adding this test! I agree that, if the record backend treats all instructions > the same, there's no need to test them all. Nice, thanks for confirming. > As I mentioned in my reply to v2, I'd prefer if this was squashed to commit 4, but if you > don't end up sending a new series, or feel strongly that they shouldn't be together, I'm > ok with separating the commits. No problem, makes sense to me. Though I assume you mean commit 2. Done for v4. >> +int >> +main (void) >> +{ >> + char dest[BUF_SIZE]; >> + char source[BUF_SIZE] = TEST_STRING; >> + register char *p asm ("x19"); >> + register char *q asm ("x20"); >> + register long size asm ("x21"); >> + register long zero asm ("x22"); > > I remember that its not always easy to guarantee that a variable will end up in a > register, so this has me slightly confused. Is this portable? Have you/could you double > check that clang also does what you expect? > > If it isn't portable, please add the gcc requirement, so we don't get new failures in > clang testing :) That's a good point, thanks for bringing it up. This is a GNU C extension, documented here: https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html And it's only supported for one specific case: "The only supported use for this feature is to specify registers for input and output operands when calling Extended asm (see Extended Asm - Assembler Instructions with C Expression Operands)." which is how I'm using it in the testcase. This extension isn't listed in Clang's list of supported extensions here: https://clang.llvm.org/docs/LanguageExtensions.html Though that page says: "This document describes the language extensions provided by Clang. In addition to the language extensions listed here, Clang aims to support a broad range of GCC extensions. Please see the GCC manual for more information on these extensions." I checked and Clang does what I expect and doesn't warn about it, so it is indeed supported. That said, the line numbers it generates for the asm statement are a bit strange and the testcase ends up setting a breakpoint a few instructions before the prologue instruction it is aiming for. To make the testcase support Clang, I had to make it check which instruction GDB is stopped at and stepi until it reaches the intended instruction. >> +require allow_aarch64_mops_tests >> + >> +standard_testfile >> + >> +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ >> + [list debug additional_flags=-march=armv9.3-a]] } { >> + return -1 >> +} >> + >> +if ![runto_main] { >> + return -1 >> +} >> + >> +gdb_test_no_output "record full" >> + >> +proc do_test { insn_prefix } { > > more of a personal preference, maybe, but you could use: > > foreach_with_prefix insn_prefix {"set" "cpy" "cpyf"} I agree it is better, thanks for the suggestion. Done in v4. >> + global decimal hex >> + >> + set before_seq [gdb_get_line_number "Before ${insn_prefix}p"] >> + set after_seq [gdb_get_line_number "After ${insn_prefix}e"] >> + >> + with_test_prefix $insn_prefix { >> + gdb_test "break $before_seq" \ >> + "Breakpoint $decimal at $hex: file .*/aarch64-mops.c, line $decimal\\." \ >> + "break before instruction sequence" >> + gdb_test "continue" \ >> + [multi_line \ >> + "Continuing\\." \ >> + "" \ >> + "Breakpoint $decimal, main \\(\\) at .*/aarch64-mops.c:$decimal" \ >> + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn_prefix}p \[^\r\n\]+\""] \ >> + "about to execute instruction sequence" > > Why did you choose to not use gdb_continue_to_breakpoint in here? > > Similar question to all other uses of "continue" I wasn't aware that with gdb_continue_to_breakpoint I was able to specify the location pattern that the test should match. I'm using it now in v4, thanks for the suggestion.
diff --git a/gdb/testsuite/gdb.reverse/aarch64-mops.c b/gdb/testsuite/gdb.reverse/aarch64-mops.c new file mode 100644 index 000000000000..513f324b9dd6 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/aarch64-mops.c @@ -0,0 +1,71 @@ +/* This test program is part of GDB, the GNU debugger. + + Copyright 2024 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define TEST_STRING "Just a test string." +#define BUF_SIZE sizeof(TEST_STRING) + +int +main (void) +{ + char dest[BUF_SIZE]; + char source[BUF_SIZE] = TEST_STRING; + register char *p asm ("x19"); + register char *q asm ("x20"); + register long size asm ("x21"); + register long zero asm ("x22"); + + p = dest; + size = BUF_SIZE; + zero = 0; + /* Before setp. */ + /* memset implemented in MOPS instructions. */ + __asm__ volatile ("setp [%0]!, %1!, %2\n\t" + "setm [%0]!, %1!, %2\n\t" + "sete [%0]!, %1!, %2\n\t" + : "+&r"(p), "+&r"(size) + : "r"(zero) + : "memory"); + + /* After sete. */ + p = dest; + q = source; + size = BUF_SIZE; + /* Before cpyp. */ + /* memmove implemented in MOPS instructions. */ + __asm__ volatile ("cpyp [%0]!, [%1]!, %2!\n\t" + "cpym [%0]!, [%1]!, %2!\n\t" + "cpye [%0]!, [%1]!, %2!\n\t" + : "+&r" (p), "+&r" (q), "+&r" (size) + : + : "memory"); + /* After cpye. */ + p = dest; + q = source; + size = BUF_SIZE; + /* Before cpyfp. */ + /* memcpy implemented in MOPS instructions. */ + __asm__ volatile ("cpyfp [%0]!, [%1]!, %2!\n\t" + "cpyfm [%0]!, [%1]!, %2!\n\t" + "cpyfe [%0]!, [%1]!, %2!\n\t" + : "+&r" (p), "+&r" (q), "+&r" (size) + : + : "memory"); + /* After cpyfe. */ + p = dest; + + return 0; +} diff --git a/gdb/testsuite/gdb.reverse/aarch64-mops.exp b/gdb/testsuite/gdb.reverse/aarch64-mops.exp new file mode 100644 index 000000000000..f9c1257e0b11 --- /dev/null +++ b/gdb/testsuite/gdb.reverse/aarch64-mops.exp @@ -0,0 +1,171 @@ +# Copyright 2024 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Test instruction record for AArch64 FEAT_MOPS instructions. +# Based on gdb.reverse/ppc_record_test_isa_3_1.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_mops_tests + +standard_testfile + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + [list debug additional_flags=-march=armv9.3-a]] } { + return -1 +} + +if ![runto_main] { + return -1 +} + +gdb_test_no_output "record full" + +proc do_test { insn_prefix } { + global decimal hex + + set before_seq [gdb_get_line_number "Before ${insn_prefix}p"] + set after_seq [gdb_get_line_number "After ${insn_prefix}e"] + + with_test_prefix $insn_prefix { + gdb_test "break $before_seq" \ + "Breakpoint $decimal at $hex: file .*/aarch64-mops.c, line $decimal\\." \ + "break before instruction sequence" + gdb_test "continue" \ + [multi_line \ + "Continuing\\." \ + "" \ + "Breakpoint $decimal, main \\(\\) at .*/aarch64-mops.c:$decimal" \ + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn_prefix}p \[^\r\n\]+\""] \ + "about to execute instruction sequence" + + # Record the initial register values. + set x19_initial [capture_command_output "info register x19" ""] + set x21_initial [capture_command_output "info register x21" ""] + + # The set instructions use the ZERO variable, but not Q, and the other + # instructions are the opposite. + if {[string compare $insn_prefix "set"] == 0} { + set x22_initial [capture_command_output "info register x22" ""] + } else { + set x20_initial [capture_command_output "info register x20" ""] + } + + gdb_test "break $after_seq" \ + "Breakpoint $decimal at $hex: file .*/aarch64-mops.c, line $decimal\\." \ + "break after instruction sequence" + gdb_test "continue" \ + [multi_line \ + "Continuing\\." \ + "" \ + "Breakpoint $decimal, main \\(\\) at .*/aarch64-mops.c:$decimal" \ + "$decimal\[ \t\]+p = dest;"] \ + "executed instruction sequence" + + # Record the new register values. + set x19_new [capture_command_output "info register x19" ""] + set x21_new [capture_command_output "info register x21" ""] + + if {[string compare $insn_prefix "set"] == 0} { + set x22_new [capture_command_output "info register x22" ""] + } else { + set x20_new [capture_command_output "info register x20" ""] + } + + # Execute in reverse to before the instruction sequence. + gdb_test_no_output "set exec-direction reverse" + + gdb_test "continue" \ + [multi_line \ + "Continuing\\." \ + "" \ + "Breakpoint $decimal, main \\(\\) at .*/aarch64-mops.c:$decimal" \ + "$decimal\[ \t\]+__asm__ volatile \\(\"${insn_prefix}p \[^\r\n\]+\""] \ + "reversed execution of instruction sequence" + + # Record the final register values. + set x19_final [capture_command_output "info register x19" ""] + set x21_final [capture_command_output "info register x21" ""] + + if {[string compare $insn_prefix "set"] == 0} { + set x22_final [capture_command_output "info register x22" ""] + } else { + set x20_final [capture_command_output "info register x20" ""] + } + + # Check initial and new values of x19 are different. + gdb_assert [string compare $x19_initial $x19_new] \ + "check x19 initial value versus x19 new value" + + # Check initial and new values of x21 are different. + gdb_assert [string compare $x21_initial $x21_new] \ + "check x21 initial value versus x21 new value" + + if {[string compare $insn_prefix "set"] == 0} { + # Check initial and new values of x22 are the same. + # The register with the value to set shouldn't change. + gdb_assert ![string compare $x22_initial $x22_new] \ + "check x22 initial value versus x22 new value" + } else { + # Check initial and new values of x20 are different. + gdb_assert [string compare $x20_initial $x20_new] \ + "check x20 initial value versus x20 new value" + } + + # Check initial and final values of x19 are the same. + gdb_assert ![string compare $x19_initial $x19_final] \ + "check x19 initial value versus x19 final value" + + # Check initial and final values of x21 are the same. + gdb_assert ![string compare $x21_initial $x21_final] \ + "check x21 initial value versus x21 final value" + + if {[string compare $insn_prefix "set"] == 0} { + # Check initial and final values of x22 are the same. + # The register with the value to set shouldn't change. + gdb_assert ![string compare $x22_initial $x22_final] \ + "check x22 initial value versus x22 final value" + } else { + # Check initial and final values of x20 are the same. + gdb_assert ![string compare $x20_initial $x20_final] \ + "check x20 initial value versus x20 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-mops.c:$decimal" \ + "$decimal\[ \t\]+p = dest;"] + } +} + +do_test "set" +do_test "cpy" +do_test "cpyf"