Skip to content

Commit 20a84d0

Browse files
committed
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>
1 parent b91877f commit 20a84d0

File tree

3 files changed

+154
-0
lines changed

3 files changed

+154
-0
lines changed

gdb/arc-tdep.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,16 @@ arc_insn_get_branch_target (const struct arc_instruction &insn)
465465
instruction, hence last two bits should be truncated. */
466466
return pcrel_addr + align_down (insn.address, 4);
467467
}
468+
/* DBNZ is the only branch instruction that keeps a branch address in
469+
the second operand. It must be intercepted and treated differently. */
470+
else if (insn.insn_class == DBNZ)
471+
{
472+
CORE_ADDR pcrel_addr = arc_insn_get_operand_value_signed (insn, 1);
473+
474+
/* Offset is relative to the 4-byte aligned address of the current
475+
instruction, hence last two bits should be truncated. */
476+
return pcrel_addr + align_down (insn.address, 4);
477+
}
468478
/* B, Bcc, BL, BLcc, LP, LPcc: PC = currentPC + operand. */
469479
else if (insn.insn_class == BRANCH || insn.insn_class == LOOP)
470480
{

gdb/testsuite/gdb.arch/arc-dbnz.S

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
; This testcase is part of GDB, the GNU debugger.
2+
3+
; Copyright 2024 Free Software Foundation, Inc.
4+
5+
; This program is free software; you can redistribute it and/or modify
6+
; it under the terms of the GNU General Public License as published by
7+
; the Free Software Foundation; either version 3 of the License, or
8+
; (at your option) any later version.
9+
;
10+
; This program is distributed in the hope that it will be useful,
11+
; but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
; GNU General Public License for more details.
14+
;
15+
; You should have received a copy of the GNU General Public License
16+
; along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
18+
.section .text
19+
.align 4
20+
.global main
21+
.type main, @function
22+
23+
main:
24+
mov r0,1
25+
dbnz1:
26+
; r0 == 0 after decrementing. dbnz doesn't do branch.
27+
dbnz r0,@end
28+
29+
mov r0,5
30+
dbnz2:
31+
; r0 == 3 after decrementing and delay slot. dbnz does branch.
32+
dbnz.d r0,@dbnz3
33+
sub r0,r0,1
34+
35+
dbnz4:
36+
; r0 == 1 after decrementing. dbnz does branch.
37+
dbnz r0,@end
38+
39+
dbnz3:
40+
; r0 == 2 after decrementing. dbnz does branch.
41+
dbnz r0,@dbnz4
42+
43+
end:
44+
mov r0,0
45+
j [blink]
46+
47+
.section .note.GNU-stack,"",@progbits
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# This testcase is part of GDB, the GNU debugger.
2+
3+
# Copyright 2024 Free Software Foundation, Inc.
4+
5+
# This program is free software; you can redistribute it and/or modify
6+
# it under the terms of the GNU General Public License as published by
7+
# the Free Software Foundation; either version 3 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# This program is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU General Public License
16+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
18+
# Test dbnz instruction. It decrements its source register operand, and if
19+
# the result is non-zero it branches to the location defined by a signed
20+
# half-word displacement operand.
21+
#
22+
# It's necessary to verify these cases:
23+
#
24+
# 1. Check that dbnz does not branch and falls through if its source
25+
# register is 0 after decrementing. GDB must successfully break
26+
# on the following instruction after stepping over.
27+
# 2. Check that dbnz branches to the target correctly if its source register
28+
# is not 0 after decrementing - GDB must successfully break on the target
29+
# instruction if a forward branch is performed after stepping over.
30+
# 3. The same as point 2 but for a backward branching case.
31+
32+
require {istarget "arc*-*-*"}
33+
34+
standard_testfile .S
35+
36+
if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } {
37+
return -1
38+
}
39+
40+
if ![runto_main] {
41+
return 0
42+
}
43+
44+
gdb_test "break dbnz1" \
45+
"Breakpoint $decimal at .*" \
46+
"set breakpoint on the 1st dbnz"
47+
48+
gdb_test "break dbnz2" \
49+
"Breakpoint $decimal at .*" \
50+
"set breakpoint on the 2nd dbnz"
51+
52+
gdb_test "break dbnz3" \
53+
"Breakpoint $decimal at .*" \
54+
"set breakpoint on the 3rd dbnz"
55+
56+
gdb_test "break dbnz4" \
57+
"Breakpoint $decimal at .*" \
58+
"set breakpoint on the 4th dbnz"
59+
60+
gdb_test "break end" \
61+
"Breakpoint $decimal at .*" \
62+
"set breakpoint at the end"
63+
64+
gdb_test "continue" \
65+
"Breakpoint $decimal, dbnz1.*dbnz r0,@end" \
66+
"continue to the 1st dbnz"
67+
68+
gdb_test "x /i \$pc" \
69+
"$hex <.*>:\[ \t\]+dbnz\[ \t\]+r0,24.*" \
70+
"stayng on the 1st dbnz instruction"
71+
72+
gdb_test "stepi" \
73+
"mov r0,5" \
74+
"step over the 1st dbnz, branch is not taken"
75+
76+
gdb_test "stepi" \
77+
"Breakpoint $decimal, dbnz2.*dbnz\\.d r0,@dbnz3" \
78+
"step over r0 initialization, staying on the 2nd dbnz"
79+
80+
# Linux steps over delay slot after "stepi", but stubs with hardware stepping
81+
# like nSIM's stub may step right on delay slot. Thus use "continue" instead of
82+
# "stepi" to make this test work for all platforms.
83+
gdb_test "continue" \
84+
"Breakpoint $decimal, dbnz3.*dbnz r0,@dbnz4" \
85+
"step over the 2nd dbnz, branch is taken, staying on the 3rd dbnz"
86+
87+
gdb_test "stepi" \
88+
"Breakpoint $decimal, dbnz4.*dbnz r0,@end" \
89+
"step over the 3rd dbnz, branch is taken, staying on the 4th dbnz"
90+
91+
gdb_test "stepi" \
92+
"Breakpoint $decimal, end.*mov r0,0" \
93+
"step over the 4th dbnz, branch is taken, staying on the epilogue"
94+
95+
gdb_test "info register r0" \
96+
"r0\[ \t\]+0x1\[ \t\]+1" \
97+
"r0 contains 1 after all dbnz instructions"

0 commit comments

Comments
 (0)