[Lldb-commits] [lldb] [lldb][RISCV] fix LR/SC atomic sequence handling in lldb-server (PR #127505)
via lldb-commits
lldb-commits at lists.llvm.org
Thu May 29 09:50:55 PDT 2025
https://github.com/dlav-sc updated https://github.com/llvm/llvm-project/pull/127505
>From 2520b8d7884d320ed7923685b1a3e8652a7e8f5f Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Fri, 23 May 2025 13:27:46 +0000
Subject: [PATCH 1/2] [lldb][RISCV] handle lr/sc single step
lldb-server had limited support for single-stepping through the
lr/sc atomic sequence. This patch enhances that support for all
possible atomic sequences.
---
lldb/include/lldb/Core/EmulateInstruction.h | 45 +++++++
lldb/source/Core/EmulateInstruction.cpp | 93 +++++++++++++
.../Instruction/ARM/EmulateInstructionARM.cpp | 11 ++
.../Instruction/ARM/EmulateInstructionARM.h | 18 +++
.../LoongArch/EmulateInstructionLoongArch.cpp | 122 +++++++++++-------
.../LoongArch/EmulateInstructionLoongArch.h | 2 -
.../RISCV/EmulateInstructionRISCV.cpp | 117 ++++++++++++++---
.../RISCV/EmulateInstructionRISCV.h | 37 +++++-
.../Process/FreeBSD/NativeProcessFreeBSD.cpp | 14 +-
.../NativeProcessSoftwareSingleStep.cpp | 108 +++-------------
.../Utility/NativeProcessSoftwareSingleStep.h | 2 +-
.../LoongArch/TestLoongArchEmulator.cpp | 36 +++---
12 files changed, 422 insertions(+), 183 deletions(-)
diff --git a/lldb/include/lldb/Core/EmulateInstruction.h b/lldb/include/lldb/Core/EmulateInstruction.h
index b459476883fc5..0e3564206ccb8 100644
--- a/lldb/include/lldb/Core/EmulateInstruction.h
+++ b/lldb/include/lldb/Core/EmulateInstruction.h
@@ -32,6 +32,31 @@ class RegisterValue;
class Stream;
class Target;
class UnwindPlan;
+class EmulateInstruction;
+
+using BreakpointLocations = std::vector<lldb::addr_t>;
+
+class SingleStepBreakpointLocationsPredictor {
+public:
+ SingleStepBreakpointLocationsPredictor(
+ std::unique_ptr<EmulateInstruction> emulator_up)
+ : m_emulator_up{std::move(emulator_up)} {}
+
+ virtual BreakpointLocations GetBreakpointLocations(Status &status);
+
+ virtual unsigned GetBreakpointSize(lldb::addr_t, Status &) { return 4; }
+
+ virtual ~SingleStepBreakpointLocationsPredictor() = default;
+
+protected:
+ lldb::addr_t GetSequentiallyNextInstructionPC(Status &error);
+
+ lldb::addr_t GetBreakpointLocationAddress(lldb::addr_t entry_pc,
+ Status &error);
+
+ std::unique_ptr<EmulateInstruction> m_emulator_up;
+ bool m_emulation_result = false;
+};
/// \class EmulateInstruction EmulateInstruction.h
/// "lldb/Core/EmulateInstruction.h"
@@ -497,7 +522,19 @@ class EmulateInstruction : public PluginInterface {
static uint32_t GetInternalRegisterNumber(RegisterContext *reg_ctx,
const RegisterInfo ®_info);
+ static std::unique_ptr<SingleStepBreakpointLocationsPredictor>
+ CreateBreakpointLocationPredictor(
+ std::unique_ptr<EmulateInstruction> emulator_up);
+
+ // Helper functions
+ std::optional<lldb::addr_t> ReadPC();
+ bool WritePC(lldb::addr_t addr);
+
protected:
+ using BreakpointLocationsPredictorCreator =
+ std::function<std::unique_ptr<SingleStepBreakpointLocationsPredictor>(
+ std::unique_ptr<EmulateInstruction>)>;
+
ArchSpec m_arch;
void *m_baton = nullptr;
ReadMemoryCallback m_read_mem_callback = &ReadMemoryDefault;
@@ -508,6 +545,14 @@ class EmulateInstruction : public PluginInterface {
Opcode m_opcode;
private:
+ virtual BreakpointLocationsPredictorCreator
+ GetSingleStepBreakpointLocationsPredictorCreator() {
+ return [](std::unique_ptr<EmulateInstruction> emulator_up) {
+ return std::make_unique<SingleStepBreakpointLocationsPredictor>(
+ std::move(emulator_up));
+ };
+ }
+
// For EmulateInstruction only
EmulateInstruction(const EmulateInstruction &) = delete;
const EmulateInstruction &operator=(const EmulateInstruction &) = delete;
diff --git a/lldb/source/Core/EmulateInstruction.cpp b/lldb/source/Core/EmulateInstruction.cpp
index d240b4d3b3310..3bc7beff8ac7f 100644
--- a/lldb/source/Core/EmulateInstruction.cpp
+++ b/lldb/source/Core/EmulateInstruction.cpp
@@ -588,7 +588,100 @@ EmulateInstruction::GetInternalRegisterNumber(RegisterContext *reg_ctx,
return LLDB_INVALID_REGNUM;
}
+std::unique_ptr<SingleStepBreakpointLocationsPredictor>
+EmulateInstruction::CreateBreakpointLocationPredictor(
+ std::unique_ptr<EmulateInstruction> emulator_up) {
+ auto creator =
+ emulator_up->GetSingleStepBreakpointLocationsPredictorCreator();
+ return creator(std::move(emulator_up));
+}
+
+std::optional<lldb::addr_t> EmulateInstruction::ReadPC() {
+ bool success = false;
+ auto addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
+ LLDB_INVALID_ADDRESS, &success);
+ return success ? std::optional<addr_t>(addr) : std::nullopt;
+}
+
+bool EmulateInstruction::WritePC(lldb::addr_t addr) {
+ EmulateInstruction::Context ctx;
+ ctx.type = eContextAdvancePC;
+ ctx.SetNoArgs();
+ return WriteRegisterUnsigned(ctx, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC, addr);
+}
+
bool EmulateInstruction::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) {
unwind_plan.Clear();
return false;
}
+
+BreakpointLocations
+SingleStepBreakpointLocationsPredictor::GetBreakpointLocations(Status &status) {
+ if (!m_emulator_up->ReadInstruction()) {
+ // try to get at least the size of next instruction to set breakpoint.
+ lldb::addr_t next_pc = GetSequentiallyNextInstructionPC(status);
+ return BreakpointLocations{next_pc};
+ }
+
+ auto entry_pc = m_emulator_up->ReadPC();
+ if (!entry_pc) {
+ status = Status("Can't read PC");
+ return {};
+ }
+
+ m_emulation_result = m_emulator_up->EvaluateInstruction(
+ eEmulateInstructionOptionAutoAdvancePC);
+
+ lldb::addr_t next_pc = GetBreakpointLocationAddress(*entry_pc, status);
+ return BreakpointLocations{next_pc};
+}
+
+lldb::addr_t
+SingleStepBreakpointLocationsPredictor::GetSequentiallyNextInstructionPC(
+ Status &error) {
+ auto instr_size = m_emulator_up->GetLastInstrSize();
+ if (!instr_size) {
+ error = Status("Read instruction failed!");
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ auto pc = m_emulator_up->ReadPC();
+ if (!pc) {
+ error = Status("Can't read PC");
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ lldb::addr_t next_pc = *pc + *instr_size;
+ return next_pc;
+}
+
+lldb::addr_t
+SingleStepBreakpointLocationsPredictor::GetBreakpointLocationAddress(
+ lldb::addr_t entry_pc, Status &error) {
+ auto addr = m_emulator_up->ReadPC();
+ if (!addr) {
+ error = Status("Can't read PC");
+ return LLDB_INVALID_ADDRESS;
+ }
+ lldb::addr_t pc = *addr;
+
+ if (m_emulation_result) {
+ assert(entry_pc != pc && "Emulation was successfull but PC wasn't updated");
+ return pc;
+ }
+
+ if (entry_pc == pc) {
+ // Emulate instruction failed and it haven't changed PC. Advance PC with
+ // the size of the current opcode because the emulation of all
+ // PC modifying instruction should be successful. The failure most
+ // likely caused by a not supported instruction which don't modify PC.
+ return pc + m_emulator_up->GetOpcode().GetByteSize();
+ }
+
+ // The instruction emulation failed after it modified the PC. It is an
+ // unknown error where we can't continue because the next instruction is
+ // modifying the PC but we don't know how.
+ error = Status("Instruction emulation failed unexpectedly.");
+ return LLDB_INVALID_ADDRESS;
+}
diff --git a/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp b/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp
index 32975d88af46e..ff06a4a8dd0a4 100644
--- a/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp
+++ b/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp
@@ -14471,3 +14471,14 @@ bool EmulateInstructionARM::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) {
unwind_plan.SetReturnAddressRegister(dwarf_lr);
return true;
}
+
+unsigned ARMSingleStepBreakpointLocationsPredictor::GetBreakpointSize(
+ lldb::addr_t bp_addr, Status &error) {
+ auto flags = m_emulator_up->ReadRegisterUnsigned(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_ADDRESS,
+ nullptr);
+ if (flags == LLDB_INVALID_ADDRESS)
+ error = Status("Reading flags failed!");
+
+ return (flags & 0x20) ? /* Thumb mode */ 2 : /* Arm mode */ 4;
+}
diff --git a/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h b/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h
index 9eca0288607c1..c1ed9def6e27d 100644
--- a/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h
+++ b/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.h
@@ -16,6 +16,16 @@
namespace lldb_private {
+class ARMSingleStepBreakpointLocationsPredictor
+ : public SingleStepBreakpointLocationsPredictor {
+public:
+ ARMSingleStepBreakpointLocationsPredictor(
+ std::unique_ptr<EmulateInstruction> emulator_up)
+ : SingleStepBreakpointLocationsPredictor{std::move(emulator_up)} {}
+
+ unsigned GetBreakpointSize(lldb::addr_t bp_addr, Status &error) override;
+};
+
// ITSession - Keep track of the IT Block progression.
class ITSession {
public:
@@ -770,6 +780,14 @@ class EmulateInstructionARM : public EmulateInstruction {
// B6.2.13 SUBS PC, LR and related instructions
bool EmulateSUBSPcLrEtc(const uint32_t opcode, const ARMEncoding encoding);
+ BreakpointLocationsPredictorCreator
+ GetSingleStepBreakpointLocationsPredictorCreator() override {
+ return [](std::unique_ptr<EmulateInstruction> emulator_up) {
+ return std::make_unique<ARMSingleStepBreakpointLocationsPredictor>(
+ std::move(emulator_up));
+ };
+ }
+
uint32_t m_arm_isa;
Mode m_opcode_mode;
uint32_t m_opcode_cpsr;
diff --git a/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp
index 37f08592d62b9..db32bf8a4dd51 100644
--- a/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp
+++ b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp
@@ -86,7 +86,6 @@ bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) {
uint32_t inst_size = m_opcode.GetByteSize();
uint32_t inst = m_opcode.GetOpcode32();
bool increase_pc = options & eEmulateInstructionOptionAutoAdvancePC;
- bool success = false;
Opcode *opcode_data = GetOpcodeForInstruction(inst);
if (!opcode_data)
@@ -94,9 +93,10 @@ bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) {
lldb::addr_t old_pc = 0;
if (increase_pc) {
- old_pc = ReadPC(&success);
- if (!success)
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ old_pc = *addr;
}
// Call the Emulate... function.
@@ -104,9 +104,10 @@ bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) {
return false;
if (increase_pc) {
- lldb::addr_t new_pc = ReadPC(&success);
- if (!success)
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ lldb::addr_t new_pc = *addr;
if (new_pc == old_pc && !WritePC(old_pc + inst_size))
return false;
@@ -115,13 +116,14 @@ bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) {
}
bool EmulateInstructionLoongArch::ReadInstruction() {
- bool success = false;
- m_addr = ReadPC(&success);
- if (!success) {
+ auto addr = ReadPC();
+ if (!addr) {
m_addr = LLDB_INVALID_ADDRESS;
return false;
}
+ m_addr = *addr;
+ bool success = false;
Context ctx;
ctx.type = eContextReadOpcode;
ctx.SetNoArgs();
@@ -131,19 +133,6 @@ bool EmulateInstructionLoongArch::ReadInstruction() {
return true;
}
-lldb::addr_t EmulateInstructionLoongArch::ReadPC(bool *success) {
- return ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
- LLDB_INVALID_ADDRESS, success);
-}
-
-bool EmulateInstructionLoongArch::WritePC(lldb::addr_t pc) {
- EmulateInstruction::Context ctx;
- ctx.type = eContextAdvancePC;
- ctx.SetNoArgs();
- return WriteRegisterUnsigned(ctx, eRegisterKindGeneric,
- LLDB_REGNUM_GENERIC_PC, pc);
-}
-
std::optional<RegisterInfo>
EmulateInstructionLoongArch::GetRegisterInfo(lldb::RegisterKind reg_kind,
uint32_t reg_index) {
@@ -273,9 +262,12 @@ bool EmulateInstructionLoongArch::EmulateNonJMP(uint32_t inst) { return false; }
bool EmulateInstructionLoongArch::EmulateBEQZ64(uint32_t inst) {
bool success = false;
uint32_t rj = Bits32(inst, 9, 5);
- uint64_t pc = ReadPC(&success);
- if (!success)
+
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ uint64_t pc = *addr;
+
uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16);
uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
if (!success)
@@ -293,9 +285,12 @@ bool EmulateInstructionLoongArch::EmulateBEQZ64(uint32_t inst) {
bool EmulateInstructionLoongArch::EmulateBNEZ64(uint32_t inst) {
bool success = false;
uint32_t rj = Bits32(inst, 9, 5);
- uint64_t pc = ReadPC(&success);
- if (!success)
+
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ uint64_t pc = *addr;
+
uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16);
uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
if (!success)
@@ -313,9 +308,12 @@ bool EmulateInstructionLoongArch::EmulateBNEZ64(uint32_t inst) {
bool EmulateInstructionLoongArch::EmulateBCEQZ64(uint32_t inst) {
bool success = false;
uint32_t cj = Bits32(inst, 7, 5) + fpr_fcc0_loongarch;
- uint64_t pc = ReadPC(&success);
- if (!success)
+
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ uint64_t pc = *addr;
+
uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16);
uint8_t cj_val =
(uint8_t)ReadRegisterUnsigned(eRegisterKindLLDB, cj, 0, &success);
@@ -335,9 +333,12 @@ bool EmulateInstructionLoongArch::EmulateBCEQZ64(uint32_t inst) {
bool EmulateInstructionLoongArch::EmulateBCNEZ64(uint32_t inst) {
bool success = false;
uint32_t cj = Bits32(inst, 7, 5) + fpr_fcc0_loongarch;
- uint64_t pc = ReadPC(&success);
- if (!success)
+
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ uint64_t pc = *addr;
+
uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16);
uint8_t cj_val =
(uint8_t)ReadRegisterUnsigned(eRegisterKindLLDB, cj, 0, &success);
@@ -358,9 +359,12 @@ bool EmulateInstructionLoongArch::EmulateJIRL64(uint32_t inst) {
uint32_t rj = Bits32(inst, 9, 5);
uint32_t rd = Bits32(inst, 4, 0);
bool success = false;
- uint64_t pc = ReadPC(&success);
- if (!success)
+
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ uint64_t pc = *addr;
+
EmulateInstruction::Context ctx;
if (!WriteRegisterUnsigned(ctx, eRegisterKindLLDB, rd, pc + 4))
return false;
@@ -374,10 +378,11 @@ bool EmulateInstructionLoongArch::EmulateJIRL64(uint32_t inst) {
// b offs26
// PC = PC + SignExtend({offs26, 2' b0}, GRLEN)
bool EmulateInstructionLoongArch::EmulateB64(uint32_t inst) {
- bool success = false;
- uint64_t pc = ReadPC(&success);
- if (!success)
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ uint64_t pc = *addr;
+
uint32_t offs26 = Bits32(inst, 25, 10) + (Bits32(inst, 9, 0) << 16);
uint64_t next_pc = pc + llvm::SignExtend64<28>(offs26 << 2);
return WritePC(next_pc);
@@ -387,10 +392,11 @@ bool EmulateInstructionLoongArch::EmulateB64(uint32_t inst) {
// GR[1] = PC + 4
// PC = PC + SignExtend({offs26, 2'b0}, GRLEN)
bool EmulateInstructionLoongArch::EmulateBL64(uint32_t inst) {
- bool success = false;
- uint64_t pc = ReadPC(&success);
- if (!success)
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ uint64_t pc = *addr;
+
EmulateInstruction::Context ctx;
if (!WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_loongarch, pc + 4))
return false;
@@ -406,9 +412,12 @@ bool EmulateInstructionLoongArch::EmulateBEQ64(uint32_t inst) {
bool success = false;
uint32_t rj = Bits32(inst, 9, 5);
uint32_t rd = Bits32(inst, 4, 0);
- uint64_t pc = ReadPC(&success);
- if (!success)
+
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ uint64_t pc = *addr;
+
uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
if (!success)
return false;
@@ -429,9 +438,12 @@ bool EmulateInstructionLoongArch::EmulateBNE64(uint32_t inst) {
bool success = false;
uint32_t rj = Bits32(inst, 9, 5);
uint32_t rd = Bits32(inst, 4, 0);
- uint64_t pc = ReadPC(&success);
- if (!success)
+
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ uint64_t pc = *addr;
+
uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
if (!success)
return false;
@@ -452,9 +464,12 @@ bool EmulateInstructionLoongArch::EmulateBLT64(uint32_t inst) {
bool success = false;
uint32_t rj = Bits32(inst, 9, 5);
uint32_t rd = Bits32(inst, 4, 0);
- uint64_t pc = ReadPC(&success);
- if (!success)
+
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ uint64_t pc = *addr;
+
int64_t rj_val =
(int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
if (!success)
@@ -477,9 +492,12 @@ bool EmulateInstructionLoongArch::EmulateBGE64(uint32_t inst) {
bool success = false;
uint32_t rj = Bits32(inst, 9, 5);
uint32_t rd = Bits32(inst, 4, 0);
- uint64_t pc = ReadPC(&success);
- if (!success)
+
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ uint64_t pc = *addr;
+
int64_t rj_val =
(int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
if (!success)
@@ -502,9 +520,12 @@ bool EmulateInstructionLoongArch::EmulateBLTU64(uint32_t inst) {
bool success = false;
uint32_t rj = Bits32(inst, 9, 5);
uint32_t rd = Bits32(inst, 4, 0);
- uint64_t pc = ReadPC(&success);
- if (!success)
+
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ uint64_t pc = *addr;
+
uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
if (!success)
return false;
@@ -525,9 +546,12 @@ bool EmulateInstructionLoongArch::EmulateBGEU64(uint32_t inst) {
bool success = false;
uint32_t rj = Bits32(inst, 9, 5);
uint32_t rd = Bits32(inst, 4, 0);
- uint64_t pc = ReadPC(&success);
- if (!success)
+
+ auto addr = ReadPC();
+ if (!addr)
return false;
+ uint64_t pc = *addr;
+
uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success);
if (!success)
return false;
diff --git a/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h
index 47fe454fcd88c..2a8acba42f49e 100644
--- a/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h
+++ b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h
@@ -57,8 +57,6 @@ class EmulateInstructionLoongArch : public EmulateInstruction {
std::optional<RegisterInfo> GetRegisterInfo(lldb::RegisterKind reg_kind,
uint32_t reg_num) override;
- lldb::addr_t ReadPC(bool *success);
- bool WritePC(lldb::addr_t pc);
bool IsLoongArch64() { return m_arch_subtype == llvm::Triple::loongarch64; }
bool TestExecute(uint32_t inst);
diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp
index badc7ba36f011..c0fcca4e0049f 100644
--- a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp
+++ b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp
@@ -650,9 +650,10 @@ std::optional<DecodeResult> EmulateInstructionRISCV::Decode(uint32_t inst) {
for (const InstrPattern &pat : PATTERNS) {
if ((inst & pat.type_mask) == pat.eigen &&
(inst_type & pat.inst_type) != 0) {
- LLDB_LOGF(
- log, "EmulateInstructionRISCV::%s: inst(%x at %" PRIx64 ") was decoded to %s",
- __FUNCTION__, inst, m_addr, pat.name);
+ LLDB_LOGF(log,
+ "EmulateInstructionRISCV::%s: inst(%x at %" PRIx64
+ ") was decoded to %s",
+ __FUNCTION__, inst, m_addr, pat.name);
auto decoded = is_16b ? pat.decode(try_rvc) : pat.decode(inst);
return DecodeResult{decoded, inst, is_16b, pat};
}
@@ -1649,21 +1650,6 @@ bool EmulateInstructionRISCV::ReadInstruction() {
return true;
}
-std::optional<addr_t> EmulateInstructionRISCV::ReadPC() {
- bool success = false;
- auto addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
- LLDB_INVALID_ADDRESS, &success);
- return success ? std::optional<addr_t>(addr) : std::nullopt;
-}
-
-bool EmulateInstructionRISCV::WritePC(addr_t pc) {
- EmulateInstruction::Context ctx;
- ctx.type = eContextAdvancePC;
- ctx.SetNoArgs();
- return WriteRegisterUnsigned(ctx, eRegisterKindGeneric,
- LLDB_REGNUM_GENERIC_PC, pc);
-}
-
RoundingMode EmulateInstructionRISCV::GetRoundingMode() {
bool success = false;
auto fcsr = ReadRegisterUnsigned(eRegisterKindLLDB, fpr_fcsr_riscv,
@@ -1792,4 +1778,99 @@ bool EmulateInstructionRISCV::SupportsThisArch(const ArchSpec &arch) {
return arch.GetTriple().isRISCV();
}
+BreakpointLocations
+RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointLocations(
+ Status &status) {
+ EmulateInstructionRISCV *riscv_emulator =
+ static_cast<EmulateInstructionRISCV *>(m_emulator_up.get());
+
+ auto pc = riscv_emulator->ReadPC();
+ if (!pc) {
+ status = Status("Can't read PC");
+ return {};
+ }
+
+ auto inst = riscv_emulator->ReadInstructionAt(*pc);
+ if (!inst) {
+ // Can't read instruction. Try default handler.
+ return SingleStepBreakpointLocationsPredictor::GetBreakpointLocations(
+ status);
+ }
+
+ if (FoundLoadReserve(inst->decoded))
+ return HandleAtomicSequence(*pc, status);
+
+ if (FoundStoreConditional(inst->decoded)) {
+ // Ill-formed program, just set a breakpoint to the current position
+ return {*pc};
+ }
+
+ return SingleStepBreakpointLocationsPredictor::GetBreakpointLocations(status);
+}
+
+unsigned RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointSize(
+ lldb::addr_t bp_addr, Status &error) {
+ EmulateInstructionRISCV *riscv_emulator =
+ static_cast<EmulateInstructionRISCV *>(m_emulator_up.get());
+
+ if (auto inst = riscv_emulator->ReadInstructionAt(bp_addr); inst)
+ return inst->is_rvc ? 2 : 4;
+
+ // Try last instruction size.
+ if (auto last_instr_size = riscv_emulator->GetLastInstrSize();
+ last_instr_size)
+ return *last_instr_size;
+
+ // Just place non-compressed software trap (EBREAK).
+ return 4;
+}
+
+BreakpointLocations
+RISCVSingleStepBreakpointLocationsPredictor::HandleAtomicSequence(
+ lldb::addr_t pc, Status &error) {
+ EmulateInstructionRISCV *riscv_emulator =
+ static_cast<EmulateInstructionRISCV *>(m_emulator_up.get());
+ // Handle instuctions between LR and SC. According to unprivilleged
+ // RISC-V ISA there can be at most 16 instructions in the sequence
+
+ lldb::addr_t entry_pc = pc;
+ pc += 4; // add LR_W, LR_D instruction size
+
+ size_t atomic_length = 0;
+ std::optional<DecodeResult> inst;
+ std::vector<lldb::addr_t> bp_addrs;
+ do {
+ inst = riscv_emulator->ReadInstructionAt(pc);
+ if (!inst) {
+ error = Status("Can't read instruction");
+ return {};
+ }
+
+ if (B *branch = std::get_if<B>(&inst->decoded))
+ bp_addrs.push_back(pc + SignExt(branch->imm));
+
+ unsigned addent = inst->is_rvc ? 2 : 4;
+ pc += addent;
+ atomic_length += addent;
+ } while ((atomic_length < s_max_atomic_sequence_length) &&
+ !FoundStoreConditional(inst->decoded));
+
+ if (atomic_length >= s_max_atomic_sequence_length) {
+ // Ill-formed program, just set a breakpoint to the current position
+ Log *log = GetLog(LLDBLog::Unwind);
+ LLDB_LOGF(log,
+ "RISCVSingleStepBreakpointLocationsPredictor::%s: can't find "
+ "corresponding store conditional insturuction",
+ __FUNCTION__);
+ return {entry_pc};
+ }
+
+ bp_addrs.erase(
+ llvm::remove_if(bp_addrs,
+ [pc](lldb::addr_t bp_addr) { return pc >= bp_addr; }),
+ bp_addrs.end());
+ bp_addrs.push_back(pc);
+ return bp_addrs;
+}
+
} // namespace lldb_private
diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h
index 53ac11c2e1102..662ed6c812571 100644
--- a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h
+++ b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h
@@ -20,6 +20,33 @@
namespace lldb_private {
+class RISCVSingleStepBreakpointLocationsPredictor
+ : public SingleStepBreakpointLocationsPredictor {
+public:
+ RISCVSingleStepBreakpointLocationsPredictor(
+ std::unique_ptr<EmulateInstruction> emulator)
+ : SingleStepBreakpointLocationsPredictor{std::move(emulator)} {}
+
+ BreakpointLocations GetBreakpointLocations(Status &status) override;
+
+ unsigned GetBreakpointSize(lldb::addr_t bp_addr, Status &error) override;
+
+private:
+ static bool FoundLoadReserve(const RISCVInst &inst) {
+ return std::holds_alternative<LR_W>(inst) ||
+ std::holds_alternative<LR_D>(inst);
+ }
+
+ static bool FoundStoreConditional(const RISCVInst &inst) {
+ return std::holds_alternative<SC_W>(inst) ||
+ std::holds_alternative<SC_D>(inst);
+ }
+
+ BreakpointLocations HandleAtomicSequence(lldb::addr_t pc, Status &error);
+
+ static constexpr size_t s_max_atomic_sequence_length = 64;
+};
+
class EmulateInstructionRISCV : public EmulateInstruction {
public:
static llvm::StringRef GetPluginNameStatic() { return "riscv"; }
@@ -67,9 +94,6 @@ class EmulateInstructionRISCV : public EmulateInstruction {
std::optional<RegisterInfo> GetRegisterInfo(lldb::RegisterKind reg_kind,
uint32_t reg_num) override;
- std::optional<lldb::addr_t> ReadPC();
- bool WritePC(lldb::addr_t pc);
-
std::optional<DecodeResult> ReadInstructionAt(lldb::addr_t addr);
std::optional<DecodeResult> Decode(uint32_t inst);
bool Execute(DecodeResult inst, bool ignore_cond);
@@ -98,6 +122,13 @@ class EmulateInstructionRISCV : public EmulateInstruction {
bool SetAccruedExceptions(llvm::APFloatBase::opStatus);
private:
+ BreakpointLocationsPredictorCreator
+ GetSingleStepBreakpointLocationsPredictorCreator() override {
+ return [](std::unique_ptr<EmulateInstruction> emulator_up) {
+ return std::make_unique<RISCVSingleStepBreakpointLocationsPredictor>(
+ std::move(emulator_up));
+ };
+ }
/// Last decoded instruction from m_opcode
DecodeResult m_decoded;
/// Last decoded instruction size estimate.
diff --git a/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
index bf552e19742c4..2c2af272040a3 100644
--- a/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
+++ b/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
@@ -326,11 +326,15 @@ void NativeProcessFreeBSD::MonitorSIGTRAP(lldb::pid_t pid) {
if (thread_info != m_threads_stepping_with_breakpoint.end() &&
thread_info->second == regctx.GetPC()) {
thread->SetStoppedByTrace();
- Status brkpt_error = RemoveBreakpoint(thread_info->second);
- if (brkpt_error.Fail())
- LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}",
- thread_info->first, brkpt_error);
- m_threads_stepping_with_breakpoint.erase(thread_info);
+ while (thread_info != m_threads_stepping_with_breakpoint.end() {
+ Status brkpt_error = RemoveBreakpoint(thread_info->second);
+ if (brkpt_error.Fail())
+ LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}",
+ thread_info->first, brkpt_error);
+ m_threads_stepping_with_breakpoint.erase(thread_info);
+ thread_info =
+ m_threads_stepping_with_breakpoint.find(thread->GetID());
+ }
} else
thread->SetStoppedByBreakpoint();
FixupBreakpointPCAsNeeded(*thread);
diff --git a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp
index 31d59e387d25b..d38a2c7edde5a 100644
--- a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp
+++ b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp
@@ -87,34 +87,10 @@ static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton,
return length;
}
-static lldb::addr_t ReadFlags(NativeRegisterContext ®siter_context) {
- const RegisterInfo *flags_info = regsiter_context.GetRegisterInfo(
- eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
- return regsiter_context.ReadRegisterAsUnsigned(flags_info,
- LLDB_INVALID_ADDRESS);
-}
-
-static int GetSoftwareBreakpointSize(const ArchSpec &arch,
- lldb::addr_t next_flags) {
- if (arch.GetMachine() == llvm::Triple::arm) {
- if (next_flags & 0x20)
- // Thumb mode
- return 2;
- // Arm mode
- return 4;
- }
- if (arch.IsMIPS() || arch.GetTriple().isPPC64() ||
- arch.GetTriple().isRISCV() || arch.GetTriple().isLoongArch())
- return 4;
- return 0;
-}
-
-static Status SetSoftwareBreakpointOnPC(const ArchSpec &arch, lldb::addr_t pc,
- lldb::addr_t next_flags,
- NativeProcessProtocol &process) {
- int size_hint = GetSoftwareBreakpointSize(arch, next_flags);
+static Status SetSoftwareBreakpoint(lldb::addr_t bp_addr, unsigned bp_size,
+ NativeProcessProtocol &process) {
Status error;
- error = process.SetBreakpoint(pc, size_hint, /*hardware=*/false);
+ error = process.SetBreakpoint(bp_addr, bp_size, /*hardware=*/false);
// If setting the breakpoint fails because pc is out of the address
// space, ignore it and let the debugee segfault.
@@ -136,7 +112,6 @@ Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping(
std::unique_ptr<EmulateInstruction> emulator_up(
EmulateInstruction::FindPlugin(arch, eInstructionTypePCModifying,
nullptr));
-
if (emulator_up == nullptr)
return Status::FromErrorString("Instruction emulator not found!");
@@ -147,65 +122,24 @@ Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping(
emulator_up->SetWriteMemCallback(&WriteMemoryCallback);
emulator_up->SetWriteRegCallback(&WriteRegisterCallback);
- if (!emulator_up->ReadInstruction()) {
- // try to get at least the size of next instruction to set breakpoint.
- auto instr_size = emulator_up->GetLastInstrSize();
- if (!instr_size)
- return Status::FromErrorString("Read instruction failed!");
- bool success = false;
- auto pc = emulator_up->ReadRegisterUnsigned(eRegisterKindGeneric,
- LLDB_REGNUM_GENERIC_PC,
- LLDB_INVALID_ADDRESS, &success);
- if (!success)
- return Status::FromErrorString("Reading pc failed!");
- lldb::addr_t next_pc = pc + *instr_size;
- auto result =
- SetSoftwareBreakpointOnPC(arch, next_pc, /* next_flags */ 0x0, process);
- m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
- return result;
- }
+ auto bp_locaions_predictor =
+ EmulateInstruction::CreateBreakpointLocationPredictor(
+ std::move(emulator_up));
+
+ auto bp_locations = bp_locaions_predictor->GetBreakpointLocations(error);
+ if (error.Fail())
+ return error;
+
+ for (auto &&bp_addr : bp_locations) {
+ unsigned bp_size = bp_locaions_predictor->GetBreakpointSize(bp_addr, error);
+ if (error.Fail())
+ return error;
+
+ error = SetSoftwareBreakpoint(bp_addr, bp_size, process);
+ if (error.Fail())
+ return error;
- bool emulation_result =
- emulator_up->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
-
- const RegisterInfo *reg_info_pc = register_context.GetRegisterInfo(
- eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
- const RegisterInfo *reg_info_flags = register_context.GetRegisterInfo(
- eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
-
- auto pc_it =
- baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]);
- auto flags_it = reg_info_flags == nullptr
- ? baton.m_register_values.end()
- : baton.m_register_values.find(
- reg_info_flags->kinds[eRegisterKindDWARF]);
-
- lldb::addr_t next_pc;
- lldb::addr_t next_flags;
- if (emulation_result) {
- assert(pc_it != baton.m_register_values.end() &&
- "Emulation was successfull but PC wasn't updated");
- next_pc = pc_it->second.GetAsUInt64();
-
- if (flags_it != baton.m_register_values.end())
- next_flags = flags_it->second.GetAsUInt64();
- else
- next_flags = ReadFlags(register_context);
- } else if (pc_it == baton.m_register_values.end()) {
- // Emulate instruction failed and it haven't changed PC. Advance PC with
- // the size of the current opcode because the emulation of all
- // PC modifying instruction should be successful. The failure most
- // likely caused by a not supported instruction which don't modify PC.
- next_pc = register_context.GetPC() + emulator_up->GetOpcode().GetByteSize();
- next_flags = ReadFlags(register_context);
- } else {
- // The instruction emulation failed after it modified the PC. It is an
- // unknown error where we can't continue because the next instruction is
- // modifying the PC but we don't know how.
- return Status::FromErrorString(
- "Instruction emulation failed unexpectedly.");
+ m_threads_stepping_with_breakpoint.insert({thread.GetID(), bp_addr});
}
- auto result = SetSoftwareBreakpointOnPC(arch, next_pc, next_flags, process);
- m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
- return result;
+ return error;
}
diff --git a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h
index f9435b7a84ba4..35e834454e916 100644
--- a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h
+++ b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h
@@ -23,7 +23,7 @@ class NativeProcessSoftwareSingleStep {
protected:
// List of thread ids stepping with a breakpoint with the address of
// the relevan breakpoint
- std::map<lldb::tid_t, lldb::addr_t> m_threads_stepping_with_breakpoint;
+ std::multimap<lldb::tid_t, lldb::addr_t> m_threads_stepping_with_breakpoint;
};
} // namespace lldb_private
diff --git a/lldb/unittests/Instruction/LoongArch/TestLoongArchEmulator.cpp b/lldb/unittests/Instruction/LoongArch/TestLoongArchEmulator.cpp
index f9372ded0133b..1be0ef2af3c2e 100644
--- a/lldb/unittests/Instruction/LoongArch/TestLoongArchEmulator.cpp
+++ b/lldb/unittests/Instruction/LoongArch/TestLoongArchEmulator.cpp
@@ -177,10 +177,10 @@ TEST_F(LoongArch64EmulatorTester, testJIRL) {
gpr.gpr[12] = 0x12000400;
ASSERT_TRUE(TestExecute(inst));
auto r1 = gpr.gpr[1];
- auto pc = ReadPC(&success);
- ASSERT_TRUE(success);
+ auto pc = ReadPC();
+ ASSERT_TRUE(pc);
ASSERT_EQ(r1, old_pc + 4);
- ASSERT_EQ(pc, gpr.gpr[12] + (offs16 * 4));
+ ASSERT_EQ(*pc, gpr.gpr[12] + (offs16 * 4));
}
TEST_F(LoongArch64EmulatorTester, testB) {
@@ -193,9 +193,9 @@ TEST_F(LoongArch64EmulatorTester, testB) {
uint32_t inst = 0b01010000000000000100000000000001;
uint32_t offs26 = 0x10010;
ASSERT_TRUE(TestExecute(inst));
- auto pc = ReadPC(&success);
- ASSERT_TRUE(success);
- ASSERT_EQ(pc, old_pc + (offs26 * 4));
+ auto pc = ReadPC();
+ ASSERT_TRUE(pc);
+ ASSERT_EQ(*pc, old_pc + (offs26 * 4));
}
TEST_F(LoongArch64EmulatorTester, testBL) {
@@ -209,10 +209,10 @@ TEST_F(LoongArch64EmulatorTester, testBL) {
uint32_t offs26 = 0x10010;
ASSERT_TRUE(TestExecute(inst));
auto r1 = gpr.gpr[1];
- auto pc = ReadPC(&success);
- ASSERT_TRUE(success);
+ auto pc = ReadPC();
+ ASSERT_TRUE(pc);
ASSERT_EQ(r1, old_pc + 4);
- ASSERT_EQ(pc, old_pc + (offs26 * 4));
+ ASSERT_EQ(*pc, old_pc + (offs26 * 4));
}
static void testBcondBranch(LoongArch64EmulatorTester *tester,
@@ -226,9 +226,9 @@ static void testBcondBranch(LoongArch64EmulatorTester *tester,
// b<cmp> r12, r13, (-256)
uint32_t inst = encoder(12, 13, -256);
ASSERT_TRUE(tester->TestExecute(inst));
- auto pc = tester->ReadPC(&success);
- ASSERT_TRUE(success);
- ASSERT_EQ(pc, old_pc + (branched ? (-256 * 4) : 4));
+ auto pc = tester->ReadPC();
+ ASSERT_TRUE(pc);
+ ASSERT_EQ(*pc, old_pc + (branched ? (-256 * 4) : 4));
}
static void testBZcondBranch(LoongArch64EmulatorTester *tester,
@@ -241,9 +241,9 @@ static void testBZcondBranch(LoongArch64EmulatorTester *tester,
// b<cmp>z r4, (-256)
uint32_t inst = encoder(4, -256);
ASSERT_TRUE(tester->TestExecute(inst));
- auto pc = tester->ReadPC(&success);
- ASSERT_TRUE(success);
- ASSERT_EQ(pc, old_pc + (branched ? (-256 * 4) : 4));
+ auto pc = tester->ReadPC();
+ ASSERT_TRUE(pc);
+ ASSERT_EQ(*pc, old_pc + (branched ? (-256 * 4) : 4));
}
static void testBCZcondBranch(LoongArch64EmulatorTester *tester,
@@ -256,9 +256,9 @@ static void testBCZcondBranch(LoongArch64EmulatorTester *tester,
// bc<cmp>z fcc0, 256
uint32_t inst = encoder(0, 256);
ASSERT_TRUE(tester->TestExecute(inst));
- auto pc = tester->ReadPC(&success);
- ASSERT_TRUE(success);
- ASSERT_EQ(pc, old_pc + (branched ? (256 * 4) : 4));
+ auto pc = tester->ReadPC();
+ ASSERT_TRUE(pc);
+ ASSERT_EQ(*pc, old_pc + (branched ? (256 * 4) : 4));
}
GEN_BCOND_TEST(64, BEQ, 1, 1, 0)
>From 626f439dfd1b5b0f5e445123604e5ee42f40c3fb Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Fri, 23 May 2025 13:26:17 +0000
Subject: [PATCH 2/2] [lldb][RISCV] add lr/sc step tests
---
.../RISCV/EmulateInstructionRISCV.cpp | 55 +++++++++----
lldb/test/API/riscv/step/Makefile | 3 +
lldb/test/API/riscv/step/TestSoftwareStep.py | 81 +++++++++++++++++++
lldb/test/API/riscv/step/branch.c | 19 +++++
.../step/incomplete_sequence_without_lr.c | 19 +++++
.../step/incomplete_sequence_without_sc.c | 18 +++++
lldb/test/API/riscv/step/main.c | 19 +++++
7 files changed, 200 insertions(+), 14 deletions(-)
create mode 100644 lldb/test/API/riscv/step/Makefile
create mode 100644 lldb/test/API/riscv/step/TestSoftwareStep.py
create mode 100644 lldb/test/API/riscv/step/branch.c
create mode 100644 lldb/test/API/riscv/step/incomplete_sequence_without_lr.c
create mode 100644 lldb/test/API/riscv/step/incomplete_sequence_without_sc.c
create mode 100644 lldb/test/API/riscv/step/main.c
diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp
index c0fcca4e0049f..c9722b5dacb08 100644
--- a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp
+++ b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp
@@ -1801,8 +1801,15 @@ RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointLocations(
return HandleAtomicSequence(*pc, status);
if (FoundStoreConditional(inst->decoded)) {
- // Ill-formed program, just set a breakpoint to the current position
- return {*pc};
+ // Ill-formed atomic sequence (SC doesn't have corresponding LR
+ // instruction). Consider SC instruction like a non-atomic store and set a
+ // breakpoint at the next instruction.
+ Log *log = GetLog(LLDBLog::Unwind);
+ LLDB_LOGF(log,
+ "RISCVSingleStepBreakpointLocationsPredictor::%s: can't find "
+ "corresponding load reserve insturuction",
+ __FUNCTION__);
+ return {*pc + 4};
}
return SingleStepBreakpointLocationsPredictor::GetBreakpointLocations(status);
@@ -1821,7 +1828,7 @@ unsigned RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointSize(
last_instr_size)
return *last_instr_size;
- // Just place non-compressed software trap (EBREAK).
+ // Just place non-compressed software trap.
return 4;
}
@@ -1830,11 +1837,12 @@ RISCVSingleStepBreakpointLocationsPredictor::HandleAtomicSequence(
lldb::addr_t pc, Status &error) {
EmulateInstructionRISCV *riscv_emulator =
static_cast<EmulateInstructionRISCV *>(m_emulator_up.get());
- // Handle instuctions between LR and SC. According to unprivilleged
- // RISC-V ISA there can be at most 16 instructions in the sequence
- lldb::addr_t entry_pc = pc;
- pc += 4; // add LR_W, LR_D instruction size
+ // Handle instructions between LR and SC. According to unprivilleged
+ // RISC-V ISA there can be at most 16 instructions in the sequence.
+
+ lldb::addr_t entry_pc = pc; // LR instruction address
+ pc += 4; // add LR_W, LR_D instruction size
size_t atomic_length = 0;
std::optional<DecodeResult> inst;
@@ -1856,20 +1864,39 @@ RISCVSingleStepBreakpointLocationsPredictor::HandleAtomicSequence(
!FoundStoreConditional(inst->decoded));
if (atomic_length >= s_max_atomic_sequence_length) {
- // Ill-formed program, just set a breakpoint to the current position
+ // Ill-formed atomic sequence (LR doesn't have corresponding SC
+ // instruction). In this case consider LR like a non-atomic load instruction
+ // and set a breakpoint at the next after LR instruction.
Log *log = GetLog(LLDBLog::Unwind);
LLDB_LOGF(log,
"RISCVSingleStepBreakpointLocationsPredictor::%s: can't find "
"corresponding store conditional insturuction",
__FUNCTION__);
- return {entry_pc};
+ return {entry_pc + 4};
}
- bp_addrs.erase(
- llvm::remove_if(bp_addrs,
- [pc](lldb::addr_t bp_addr) { return pc >= bp_addr; }),
- bp_addrs.end());
- bp_addrs.push_back(pc);
+ lldb::addr_t exit_pc = pc;
+
+ // Check if we have a branch to the start of the atomic sequence after SC
+ // instruction. If we have such branch, consider it as a part of the atomic
+ // sequence.
+ inst = riscv_emulator->ReadInstructionAt(exit_pc);
+ if (inst) {
+ B *branch = std::get_if<B>(&inst->decoded);
+ if (branch && (exit_pc + SignExt(branch->imm)) == entry_pc)
+ exit_pc += inst->is_rvc ? 2 : 4;
+ }
+
+ // Set breakpoints at the jump addresses of the forward branches that points
+ // after the end of the atomic sequence.
+ bp_addrs.erase(llvm::remove_if(bp_addrs,
+ [exit_pc](lldb::addr_t bp_addr) {
+ return exit_pc >= bp_addr;
+ }),
+ bp_addrs.end());
+
+ // Set breakpoint at the end of atomic sequence.
+ bp_addrs.push_back(exit_pc);
return bp_addrs;
}
diff --git a/lldb/test/API/riscv/step/Makefile b/lldb/test/API/riscv/step/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/riscv/step/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/riscv/step/TestSoftwareStep.py b/lldb/test/API/riscv/step/TestSoftwareStep.py
new file mode 100644
index 0000000000000..32bd08716c29f
--- /dev/null
+++ b/lldb/test/API/riscv/step/TestSoftwareStep.py
@@ -0,0 +1,81 @@
+"""
+Test software step-inst
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestSoftwareStep(TestBase):
+ @skipIf(archs=no_match(re.compile("rv*")))
+ def test_cas(self):
+ self.build()
+ (target, process, cur_thread, bkpt) = lldbutil.run_to_name_breakpoint(
+ self, "cas"
+ )
+ entry_pc = cur_thread.GetFrameAtIndex(0).GetPC()
+
+ self.runCmd("thread step-inst")
+ self.expect(
+ "thread list",
+ substrs=["stopped", "stop reason = instruction step into"],
+ )
+
+ pc = cur_thread.GetFrameAtIndex(0).GetPC()
+ self.assertTrue((pc - entry_pc) > 0x10)
+
+ @skipIf(archs=no_match(re.compile("rv*")))
+ def test_branch_cas(self):
+ self.build(dictionary={"C_SOURCES": "branch.c", "EXE": "branch.x"})
+ (target, process, cur_thread, bkpt) = lldbutil.run_to_name_breakpoint(
+ self, "branch_cas", exe_name="branch.x"
+ )
+ entry_pc = cur_thread.GetFrameAtIndex(0).GetPC()
+
+ self.runCmd("thread step-inst")
+ self.expect(
+ "thread list",
+ substrs=["stopped", "stop reason = instruction step into"],
+ )
+
+ pc = cur_thread.GetFrameAtIndex(0).GetPC()
+ self.assertTrue((pc - entry_pc) > 0x10)
+
+ @skipIf(archs=no_match(re.compile("rv*")))
+ def test_incomplete_sequence_without_lr(self):
+ self.build(dictionary={"C_SOURCES": "incomplete_sequence_without_lr.c", "EXE": "incomplete_lr.x"})
+ (target, process, cur_thread, bkpt) = lldbutil.run_to_name_breakpoint(
+ self, "incomplete_cas", exe_name="incomplete_lr.x"
+ )
+ entry_pc = cur_thread.GetFrameAtIndex(0).GetPC()
+
+ self.runCmd("thread step-inst")
+
+ self.expect(
+ "thread list",
+ substrs=["stopped", "stop reason = instruction step into"],
+ )
+
+ pc = cur_thread.GetFrameAtIndex(0).GetPC()
+ self.assertTrue((pc - entry_pc) == 0x4)
+
+ @skipIf(archs=no_match(re.compile("rv*")))
+ def test_incomplete_sequence_without_sc(self):
+ self.build(dictionary={"C_SOURCES": "incomplete_sequence_without_sc.c", "EXE": "incomplete_sc.x"})
+ (target, process, cur_thread, bkpt) = lldbutil.run_to_name_breakpoint(
+ self, "incomplete_cas", exe_name="incomplete_sc.x"
+ )
+ entry_pc = cur_thread.GetFrameAtIndex(0).GetPC()
+
+ self.runCmd("thread step-inst")
+
+ self.expect(
+ "thread list",
+ substrs=["stopped", "stop reason = instruction step into"],
+ )
+
+ pc = cur_thread.GetFrameAtIndex(0).GetPC()
+ self.assertTrue((pc - entry_pc) == 0x4)
+
diff --git a/lldb/test/API/riscv/step/branch.c b/lldb/test/API/riscv/step/branch.c
new file mode 100644
index 0000000000000..232ede52dd462
--- /dev/null
+++ b/lldb/test/API/riscv/step/branch.c
@@ -0,0 +1,19 @@
+void __attribute__((naked)) branch_cas(int *a, int *b) {
+ asm volatile("1:\n\t"
+ "lr.w a2, (a0)\n\t"
+ "and a5, a2, a4\n\t"
+ "bne a5, a1, 2f\n\t"
+ "xor a5, a2, a0\n\t"
+ "and a5, a5, a4\n\t"
+ "xor a5, a2, a5\n\t"
+ "sc.w a5, a1, (a3)\n\t"
+ "beqz a5, 1b\n\t"
+ "2:\n\t"
+ "ret\n\t");
+}
+
+int main() {
+ int a = 4;
+ int b = 2;
+ branch_cas(&a, &b);
+}
diff --git a/lldb/test/API/riscv/step/incomplete_sequence_without_lr.c b/lldb/test/API/riscv/step/incomplete_sequence_without_lr.c
new file mode 100644
index 0000000000000..86872df5d8ede
--- /dev/null
+++ b/lldb/test/API/riscv/step/incomplete_sequence_without_lr.c
@@ -0,0 +1,19 @@
+void __attribute__((naked)) incomplete_cas(int *a, int *b) {
+ asm volatile("1:\n\t"
+ "sc.w a5, a1, (a3)\n\t"
+ "and a5, a2, a4\n\t"
+ "beq a5, a1, 2f\n\t"
+ "xor a5, a2, a0\n\t"
+ "and a5, a5, a4\n\t"
+ "xor a5, a2, a5\n\t"
+ "sc.w a5, a1, (a3)\n\t"
+ "bnez a5, 1b\n\t"
+ "2:\n\t"
+ "ret\n\t");
+}
+
+int main() {
+ int a = 4;
+ int b = 2;
+ incomplete_cas(&a, &b);
+}
diff --git a/lldb/test/API/riscv/step/incomplete_sequence_without_sc.c b/lldb/test/API/riscv/step/incomplete_sequence_without_sc.c
new file mode 100644
index 0000000000000..06a41b17ed266
--- /dev/null
+++ b/lldb/test/API/riscv/step/incomplete_sequence_without_sc.c
@@ -0,0 +1,18 @@
+void __attribute__((naked)) incomplete_cas(int *a, int *b) {
+ asm volatile("1:\n\t"
+ "lr.w a2, (a0)\n\t"
+ "and a5, a2, a4\n\t"
+ "beq a5, a1, 2f\n\t"
+ "xor a5, a2, a0\n\t"
+ "and a5, a5, a4\n\t"
+ "xor a5, a2, a5\n\t"
+ "bnez a5, 1b\n\t"
+ "2:\n\t"
+ "ret\n\t");
+}
+
+int main() {
+ int a = 4;
+ int b = 2;
+ incomplete_cas(&a, &b);
+}
diff --git a/lldb/test/API/riscv/step/main.c b/lldb/test/API/riscv/step/main.c
new file mode 100644
index 0000000000000..a58b6ab6a0fbf
--- /dev/null
+++ b/lldb/test/API/riscv/step/main.c
@@ -0,0 +1,19 @@
+void __attribute__((naked)) cas(int *a, int *b) {
+ asm volatile("1:\n\t"
+ "lr.w a2, (a0)\n\t"
+ "and a5, a2, a4\n\t"
+ "beq a5, a1, 2f\n\t"
+ "xor a5, a2, a0\n\t"
+ "and a5, a5, a4\n\t"
+ "xor a5, a2, a5\n\t"
+ "sc.w a5, a1, (a3)\n\t"
+ "beqz a5, 1b\n\t"
+ "2:\n\t"
+ "ret\n\t");
+}
+
+int main() {
+ int a = 4;
+ int b = 2;
+ cas(&a, &b);
+}
More information about the lldb-commits
mailing list