Skip to content

Commit b25477e

Browse files
committed
[lldb][RISCV] add lr/sc step tests
1 parent 2520b8d commit b25477e

File tree

7 files changed

+209
-14
lines changed

7 files changed

+209
-14
lines changed

lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,8 +1801,15 @@ RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointLocations(
18011801
return HandleAtomicSequence(*pc, status);
18021802

18031803
if (FoundStoreConditional(inst->decoded)) {
1804-
// Ill-formed program, just set a breakpoint to the current position
1805-
return {*pc};
1804+
// Ill-formed atomic sequence (SC doesn't have corresponding LR
1805+
// instruction). Consider SC instruction like a non-atomic store and set a
1806+
// breakpoint at the next instruction.
1807+
Log *log = GetLog(LLDBLog::Unwind);
1808+
LLDB_LOGF(log,
1809+
"RISCVSingleStepBreakpointLocationsPredictor::%s: can't find "
1810+
"corresponding load reserve insturuction",
1811+
__FUNCTION__);
1812+
return {*pc + 4};
18061813
}
18071814

18081815
return SingleStepBreakpointLocationsPredictor::GetBreakpointLocations(status);
@@ -1821,7 +1828,7 @@ unsigned RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointSize(
18211828
last_instr_size)
18221829
return *last_instr_size;
18231830

1824-
// Just place non-compressed software trap (EBREAK).
1831+
// Just place non-compressed software trap.
18251832
return 4;
18261833
}
18271834

@@ -1830,11 +1837,12 @@ RISCVSingleStepBreakpointLocationsPredictor::HandleAtomicSequence(
18301837
lldb::addr_t pc, Status &error) {
18311838
EmulateInstructionRISCV *riscv_emulator =
18321839
static_cast<EmulateInstructionRISCV *>(m_emulator_up.get());
1833-
// Handle instuctions between LR and SC. According to unprivilleged
1834-
// RISC-V ISA there can be at most 16 instructions in the sequence
18351840

1836-
lldb::addr_t entry_pc = pc;
1837-
pc += 4; // add LR_W, LR_D instruction size
1841+
// Handle instructions between LR and SC. According to unprivilleged
1842+
// RISC-V ISA there can be at most 16 instructions in the sequence.
1843+
1844+
lldb::addr_t entry_pc = pc; // LR instruction address
1845+
pc += 4; // add LR_W, LR_D instruction size
18381846

18391847
size_t atomic_length = 0;
18401848
std::optional<DecodeResult> inst;
@@ -1856,20 +1864,39 @@ RISCVSingleStepBreakpointLocationsPredictor::HandleAtomicSequence(
18561864
!FoundStoreConditional(inst->decoded));
18571865

18581866
if (atomic_length >= s_max_atomic_sequence_length) {
1859-
// Ill-formed program, just set a breakpoint to the current position
1867+
// Ill-formed atomic sequence (LR doesn't have corresponding SC
1868+
// instruction). In this case consider LR like a non-atomic load instruction
1869+
// and set a breakpoint at the next after LR instruction.
18601870
Log *log = GetLog(LLDBLog::Unwind);
18611871
LLDB_LOGF(log,
18621872
"RISCVSingleStepBreakpointLocationsPredictor::%s: can't find "
18631873
"corresponding store conditional insturuction",
18641874
__FUNCTION__);
1865-
return {entry_pc};
1875+
return {entry_pc + 4};
18661876
}
18671877

1868-
bp_addrs.erase(
1869-
llvm::remove_if(bp_addrs,
1870-
[pc](lldb::addr_t bp_addr) { return pc >= bp_addr; }),
1871-
bp_addrs.end());
1872-
bp_addrs.push_back(pc);
1878+
lldb::addr_t exit_pc = pc;
1879+
1880+
// Check if we have a branch to the start of the atomic sequence after SC
1881+
// instruction. If we have such branch, consider it as a part of the atomic
1882+
// sequence.
1883+
inst = riscv_emulator->ReadInstructionAt(exit_pc);
1884+
if (inst) {
1885+
B *branch = std::get_if<B>(&inst->decoded);
1886+
if (branch && (exit_pc + SignExt(branch->imm)) == entry_pc)
1887+
exit_pc += inst->is_rvc ? 2 : 4;
1888+
}
1889+
1890+
// Set breakpoints at the jump addresses of the forward branches that points
1891+
// after the end of the atomic sequence.
1892+
bp_addrs.erase(llvm::remove_if(bp_addrs,
1893+
[exit_pc](lldb::addr_t bp_addr) {
1894+
return exit_pc >= bp_addr;
1895+
}),
1896+
bp_addrs.end());
1897+
1898+
// Set breakpoint at the end of atomic sequence.
1899+
bp_addrs.push_back(exit_pc);
18731900
return bp_addrs;
18741901
}
18751902

lldb/test/API/riscv/step/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""
2+
Test software step-inst
3+
"""
4+
5+
import lldb
6+
from lldbsuite.test.decorators import *
7+
from lldbsuite.test.lldbtest import *
8+
from lldbsuite.test import lldbutil
9+
10+
11+
class TestSoftwareStep(TestBase):
12+
@skipIf(archs=no_match(re.compile("rv*")))
13+
def test_cas(self):
14+
self.build()
15+
(target, process, cur_thread, bkpt) = lldbutil.run_to_name_breakpoint(
16+
self, "cas"
17+
)
18+
entry_pc = cur_thread.GetFrameAtIndex(0).GetPC()
19+
20+
self.runCmd("thread step-inst")
21+
self.expect(
22+
"thread list",
23+
substrs=["stopped", "stop reason = instruction step into"],
24+
)
25+
26+
pc = cur_thread.GetFrameAtIndex(0).GetPC()
27+
self.assertTrue((pc - entry_pc) > 0x10)
28+
29+
@skipIf(archs=no_match(re.compile("rv*")))
30+
def test_branch_cas(self):
31+
self.build(dictionary={"C_SOURCES": "branch.c", "EXE": "branch.x"})
32+
(target, process, cur_thread, bkpt) = lldbutil.run_to_name_breakpoint(
33+
self, "branch_cas", exe_name="branch.x"
34+
)
35+
entry_pc = cur_thread.GetFrameAtIndex(0).GetPC()
36+
37+
self.runCmd("thread step-inst")
38+
self.expect(
39+
"thread list",
40+
substrs=["stopped", "stop reason = instruction step into"],
41+
)
42+
43+
pc = cur_thread.GetFrameAtIndex(0).GetPC()
44+
self.assertTrue((pc - entry_pc) > 0x10)
45+
46+
@skipIf(archs=no_match(re.compile("rv*")))
47+
def test_incomplete_sequence_without_lr(self):
48+
self.build(
49+
dictionary={
50+
"C_SOURCES": "incomplete_sequence_without_lr.c",
51+
"EXE": "incomplete_lr.x",
52+
}
53+
)
54+
(target, process, cur_thread, bkpt) = lldbutil.run_to_name_breakpoint(
55+
self, "incomplete_cas", exe_name="incomplete_lr.x"
56+
)
57+
entry_pc = cur_thread.GetFrameAtIndex(0).GetPC()
58+
59+
self.runCmd("thread step-inst")
60+
61+
self.expect(
62+
"thread list",
63+
substrs=["stopped", "stop reason = instruction step into"],
64+
)
65+
66+
pc = cur_thread.GetFrameAtIndex(0).GetPC()
67+
self.assertTrue((pc - entry_pc) == 0x4)
68+
69+
@skipIf(archs=no_match(re.compile("rv*")))
70+
def test_incomplete_sequence_without_sc(self):
71+
self.build(
72+
dictionary={
73+
"C_SOURCES": "incomplete_sequence_without_sc.c",
74+
"EXE": "incomplete_sc.x",
75+
}
76+
)
77+
(target, process, cur_thread, bkpt) = lldbutil.run_to_name_breakpoint(
78+
self, "incomplete_cas", exe_name="incomplete_sc.x"
79+
)
80+
entry_pc = cur_thread.GetFrameAtIndex(0).GetPC()
81+
82+
self.runCmd("thread step-inst")
83+
84+
self.expect(
85+
"thread list",
86+
substrs=["stopped", "stop reason = instruction step into"],
87+
)
88+
89+
pc = cur_thread.GetFrameAtIndex(0).GetPC()
90+
self.assertTrue((pc - entry_pc) == 0x4)

lldb/test/API/riscv/step/branch.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
void __attribute__((naked)) branch_cas(int *a, int *b) {
2+
asm volatile("1:\n\t"
3+
"lr.w a2, (a0)\n\t"
4+
"and a5, a2, a4\n\t"
5+
"bne a5, a1, 2f\n\t"
6+
"xor a5, a2, a0\n\t"
7+
"and a5, a5, a4\n\t"
8+
"xor a5, a2, a5\n\t"
9+
"sc.w a5, a1, (a3)\n\t"
10+
"beqz a5, 1b\n\t"
11+
"2:\n\t"
12+
"ret\n\t");
13+
}
14+
15+
int main() {
16+
int a = 4;
17+
int b = 2;
18+
branch_cas(&a, &b);
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
void __attribute__((naked)) incomplete_cas(int *a, int *b) {
2+
asm volatile("1:\n\t"
3+
"sc.w a5, a1, (a3)\n\t"
4+
"and a5, a2, a4\n\t"
5+
"beq a5, a1, 2f\n\t"
6+
"xor a5, a2, a0\n\t"
7+
"and a5, a5, a4\n\t"
8+
"xor a5, a2, a5\n\t"
9+
"sc.w a5, a1, (a3)\n\t"
10+
"bnez a5, 1b\n\t"
11+
"2:\n\t"
12+
"ret\n\t");
13+
}
14+
15+
int main() {
16+
int a = 4;
17+
int b = 2;
18+
incomplete_cas(&a, &b);
19+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
void __attribute__((naked)) incomplete_cas(int *a, int *b) {
2+
asm volatile("1:\n\t"
3+
"lr.w a2, (a0)\n\t"
4+
"and a5, a2, a4\n\t"
5+
"beq a5, a1, 2f\n\t"
6+
"xor a5, a2, a0\n\t"
7+
"and a5, a5, a4\n\t"
8+
"xor a5, a2, a5\n\t"
9+
"bnez a5, 1b\n\t"
10+
"2:\n\t"
11+
"ret\n\t");
12+
}
13+
14+
int main() {
15+
int a = 4;
16+
int b = 2;
17+
incomplete_cas(&a, &b);
18+
}

lldb/test/API/riscv/step/main.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
void __attribute__((naked)) cas(int *a, int *b) {
2+
asm volatile("1:\n\t"
3+
"lr.w a2, (a0)\n\t"
4+
"and a5, a2, a4\n\t"
5+
"beq a5, a1, 2f\n\t"
6+
"xor a5, a2, a0\n\t"
7+
"and a5, a5, a4\n\t"
8+
"xor a5, a2, a5\n\t"
9+
"sc.w a5, a1, (a3)\n\t"
10+
"beqz a5, 1b\n\t"
11+
"2:\n\t"
12+
"ret\n\t");
13+
}
14+
15+
int main() {
16+
int a = 4;
17+
int b = 2;
18+
cas(&a, &b);
19+
}

0 commit comments

Comments
 (0)