[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 &reg_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 &regsiter_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