aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
authorYuriy Kolerov <kolerov93@gmail.com>2024-02-14 18:16:16 +0000
committerShahab Vahedi <shahab@synopsys.com>2024-02-21 14:15:09 +0100
commit33283d91d94e205ba34306cd1c253b9faceb4dd9 (patch)
treee1b95cde3748e56e0c51fa28e4f349efde2fd360 /gdb
parentf96127310144d360eac93444c1b6efe80497d163 (diff)
downloadgdb-33283d91d94e205ba34306cd1c253b9faceb4dd9.zip
gdb-33283d91d94e205ba34306cd1c253b9faceb4dd9.tar.gz
gdb-33283d91d94e205ba34306cd1c253b9faceb4dd9.tar.bz2
arc: Determine a branch target of DBNZ correctly
DBNZ instruction was moved from BRANCH class to a separate one - DBNZ. Thus, it must be processed separately in arc_insn_get_branch_target to correctly determine an offset for a possible branch. The testsuite for DBNZ instruction verifies these cases: 1. Check that dbnz does not branch and falls through if its source register is 0 after decrementing. GDB must successfully break on the following instruction after stepping over. 2. Check that dbnz branches to the target correctly if its source register is not 0 after decrementing - GDB must successfully break on the target instruction if a forward branch is performed after stepping over. 3. The same as point 2 but for a backward branching case. Signed-off-by: Yuriy Kolerov <kolerov93@gmail.com>
Diffstat (limited to 'gdb')
-rw-r--r--gdb/arc-tdep.c10
-rw-r--r--gdb/testsuite/gdb.arch/arc-dbnz.S47
-rw-r--r--gdb/testsuite/gdb.arch/arc-dbnz.exp97
3 files changed, 154 insertions, 0 deletions
diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
index 12d8aee..7dd43cc 100644
--- a/gdb/arc-tdep.c
+++ b/gdb/arc-tdep.c
@@ -466,6 +466,16 @@ arc_insn_get_branch_target (const struct arc_instruction &insn)
instruction, hence last two bits should be truncated. */
return pcrel_addr + align_down (insn.address, 4);
}
+ /* DBNZ is the only branch instruction that keeps a branch address in
+ the second operand. It must be intercepted and treated differently. */
+ else if (insn.insn_class == DBNZ)
+ {
+ CORE_ADDR pcrel_addr = arc_insn_get_operand_value_signed (insn, 1);
+
+ /* Offset is relative to the 4-byte aligned address of the current
+ instruction, hence last two bits should be truncated. */
+ return pcrel_addr + align_down (insn.address, 4);
+ }
/* B, Bcc, BL, BLcc, LP, LPcc: PC = currentPC + operand. */
else if (insn.insn_class == BRANCH || insn.insn_class == LOOP)
{
diff --git a/gdb/testsuite/gdb.arch/arc-dbnz.S b/gdb/testsuite/gdb.arch/arc-dbnz.S
new file mode 100644
index 0000000..45e1dfe
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arc-dbnz.S
@@ -0,0 +1,47 @@
+; This testcase 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/>.
+
+ .section .text
+ .align 4
+ .global main
+ .type main, @function
+
+main:
+ mov r0,1
+dbnz1:
+ ; r0 == 0 after decrementing. dbnz doesn't do branch.
+ dbnz r0,@end
+
+ mov r0,5
+dbnz2:
+ ; r0 == 3 after decrementing and delay slot. dbnz does branch.
+ dbnz.d r0,@dbnz3
+ sub r0,r0,1
+
+dbnz4:
+ ; r0 == 1 after decrementing. dbnz does branch.
+ dbnz r0,@end
+
+dbnz3:
+ ; r0 == 2 after decrementing. dbnz does branch.
+ dbnz r0,@dbnz4
+
+end:
+ mov r0,0
+ j [blink]
+
+ .section .note.GNU-stack,"",@progbits
diff --git a/gdb/testsuite/gdb.arch/arc-dbnz.exp b/gdb/testsuite/gdb.arch/arc-dbnz.exp
new file mode 100644
index 0000000..f1fce0e
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arc-dbnz.exp
@@ -0,0 +1,97 @@
+# This testcase 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/>.
+
+# Test dbnz instruction. It decrements its source register operand, and if
+# the result is non-zero it branches to the location defined by a signed
+# half-word displacement operand.
+#
+# It's necessary to verify these cases:
+#
+# 1. Check that dbnz does not branch and falls through if its source
+# register is 0 after decrementing. GDB must successfully break
+# on the following instruction after stepping over.
+# 2. Check that dbnz branches to the target correctly if its source register
+# is not 0 after decrementing - GDB must successfully break on the target
+# instruction if a forward branch is performed after stepping over.
+# 3. The same as point 2 but for a backward branching case.
+
+require {istarget "arc*-*-*"}
+
+standard_testfile .S
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } {
+ return -1
+}
+
+if ![runto_main] {
+ return 0
+}
+
+gdb_test "break dbnz1" \
+ "Breakpoint $decimal at .*" \
+ "set breakpoint on the 1st dbnz"
+
+gdb_test "break dbnz2" \
+ "Breakpoint $decimal at .*" \
+ "set breakpoint on the 2nd dbnz"
+
+gdb_test "break dbnz3" \
+ "Breakpoint $decimal at .*" \
+ "set breakpoint on the 3rd dbnz"
+
+gdb_test "break dbnz4" \
+ "Breakpoint $decimal at .*" \
+ "set breakpoint on the 4th dbnz"
+
+gdb_test "break end" \
+ "Breakpoint $decimal at .*" \
+ "set breakpoint at the end"
+
+gdb_test "continue" \
+ "Breakpoint $decimal, dbnz1.*dbnz r0,@end" \
+ "continue to the 1st dbnz"
+
+gdb_test "x /i \$pc" \
+ "$hex <.*>:\[ \t\]+dbnz\[ \t\]+r0,24.*" \
+ "stayng on the 1st dbnz instruction"
+
+gdb_test "stepi" \
+ "mov r0,5" \
+ "step over the 1st dbnz, branch is not taken"
+
+gdb_test "stepi" \
+ "Breakpoint $decimal, dbnz2.*dbnz\\.d r0,@dbnz3" \
+ "step over r0 initialization, staying on the 2nd dbnz"
+
+# Linux steps over delay slot after "stepi", but stubs with hardware stepping
+# like nSIM's stub may step right on delay slot. Thus use "continue" instead of
+# "stepi" to make this test work for all platforms.
+gdb_test "continue" \
+ "Breakpoint $decimal, dbnz3.*dbnz r0,@dbnz4" \
+ "step over the 2nd dbnz, branch is taken, staying on the 3rd dbnz"
+
+gdb_test "stepi" \
+ "Breakpoint $decimal, dbnz4.*dbnz r0,@end" \
+ "step over the 3rd dbnz, branch is taken, staying on the 4th dbnz"
+
+gdb_test "stepi" \
+ "Breakpoint $decimal, end.*mov r0,0" \
+ "step over the 4th dbnz, branch is taken, staying on the epilogue"
+
+gdb_test "info register r0" \
+ "r0\[ \t\]+0x1\[ \t\]+1" \
+ "r0 contains 1 after all dbnz instructions"