Skip to content

Commit e559d1f

Browse files
barsolo2000Bar Soloveychik
andauthored
Reland #158161 with cmake fix (#159842)
Initial PR was reverted due failing test since the buildbots don't include the RISCV backend. --------- Co-authored-by: Bar Soloveychik <[email protected]>
1 parent d0e6e5a commit e559d1f

File tree

5 files changed

+382
-14
lines changed

5 files changed

+382
-14
lines changed

lldb/include/lldb/Core/Opcode.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,9 @@ class Opcode {
223223
int Dump(Stream *s, uint32_t min_byte_width) const;
224224

225225
const void *GetOpcodeBytes() const {
226-
return ((m_type == Opcode::eTypeBytes) ? m_data.inst.bytes : nullptr);
226+
return ((m_type == Opcode::eTypeBytes || m_type == Opcode::eType16_32Tuples)
227+
? m_data.inst.bytes
228+
: nullptr);
227229
}
228230

229231
uint32_t GetByteSize() const {

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

Lines changed: 164 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ LLDB_PLUGIN_DEFINE_ADV(EmulateInstructionRISCV, InstructionRISCV)
3333

3434
namespace lldb_private {
3535

36+
// RISC-V General Purpose Register numbers
37+
static constexpr uint32_t RISCV_GPR_SP = 2; // x2 is the stack pointer
38+
static constexpr uint32_t RISCV_GPR_FP = 8; // x8 is the frame pointer
39+
3640
/// Returns all values wrapped in Optional, or std::nullopt if any of the values
3741
/// is std::nullopt.
3842
template <typename... Ts>
@@ -108,6 +112,16 @@ static uint32_t FPREncodingToLLDB(uint32_t reg_encode) {
108112
return LLDB_INVALID_REGNUM;
109113
}
110114

115+
// Helper function to get register info from GPR encoding
116+
static std::optional<RegisterInfo>
117+
GPREncodingToRegisterInfo(EmulateInstructionRISCV &emulator,
118+
uint32_t reg_encode) {
119+
uint32_t lldb_reg = GPREncodingToLLDB(reg_encode);
120+
if (lldb_reg == LLDB_INVALID_REGNUM)
121+
return std::nullopt;
122+
return emulator.GetRegisterInfo(eRegisterKindLLDB, lldb_reg);
123+
}
124+
111125
bool Rd::Write(EmulateInstructionRISCV &emulator, uint64_t value) {
112126
uint32_t lldb_reg = GPREncodingToLLDB(rd);
113127
EmulateInstruction::Context ctx;
@@ -230,10 +244,34 @@ Load(EmulateInstructionRISCV &emulator, I inst, uint64_t (*extend)(E)) {
230244
auto addr = LoadStoreAddr(emulator, inst);
231245
if (!addr)
232246
return false;
233-
return transformOptional(
234-
emulator.ReadMem<T>(*addr),
235-
[&](T t) { return inst.rd.Write(emulator, extend(E(t))); })
236-
.value_or(false);
247+
248+
// Set up context for the load operation, similar to ARM64.
249+
EmulateInstructionRISCV::Context context;
250+
251+
// Get register info for base register
252+
std::optional<RegisterInfo> reg_info_rs1 =
253+
GPREncodingToRegisterInfo(emulator, inst.rs1.rs);
254+
255+
if (!reg_info_rs1)
256+
return false;
257+
258+
// Set context type based on whether this is a stack-based load.
259+
if (inst.rs1.rs == RISCV_GPR_SP)
260+
context.type = EmulateInstruction::eContextPopRegisterOffStack;
261+
else
262+
context.type = EmulateInstruction::eContextRegisterLoad;
263+
264+
// Set the context address information
265+
context.SetAddress(*addr);
266+
267+
// Read from memory with context and write to register.
268+
bool success = false;
269+
uint64_t value =
270+
emulator.ReadMemoryUnsigned(context, *addr, sizeof(T), 0, &success);
271+
if (!success)
272+
return false;
273+
274+
return inst.rd.Write(emulator, extend(E(T(value))));
237275
}
238276

239277
template <typename I, typename T>
@@ -242,9 +280,35 @@ Store(EmulateInstructionRISCV &emulator, I inst) {
242280
auto addr = LoadStoreAddr(emulator, inst);
243281
if (!addr)
244282
return false;
245-
return transformOptional(
246-
inst.rs2.Read(emulator),
247-
[&](uint64_t rs2) { return emulator.WriteMem<T>(*addr, rs2); })
283+
284+
// Set up context for the store operation, similar to ARM64.
285+
EmulateInstructionRISCV::Context context;
286+
287+
// Get register info for source and base registers.
288+
std::optional<RegisterInfo> reg_info_rs1 =
289+
GPREncodingToRegisterInfo(emulator, inst.rs1.rs);
290+
std::optional<RegisterInfo> reg_info_rs2 =
291+
GPREncodingToRegisterInfo(emulator, inst.rs2.rs);
292+
293+
if (!reg_info_rs1 || !reg_info_rs2)
294+
return false;
295+
296+
// Set context type based on whether this is a stack-based store.
297+
if (inst.rs1.rs == RISCV_GPR_SP)
298+
context.type = EmulateInstruction::eContextPushRegisterOnStack;
299+
else
300+
context.type = EmulateInstruction::eContextRegisterStore;
301+
302+
// Set the context to show which register is being stored to which base
303+
// register + offset.
304+
context.SetRegisterToRegisterPlusOffset(*reg_info_rs2, *reg_info_rs1,
305+
SignExt(inst.imm));
306+
307+
return transformOptional(inst.rs2.Read(emulator),
308+
[&](uint64_t rs2) {
309+
return emulator.WriteMemoryUnsigned(
310+
context, *addr, rs2, sizeof(T));
311+
})
248312
.value_or(false);
249313
}
250314

@@ -737,11 +801,44 @@ class Executor {
737801
bool operator()(SH inst) { return Store<SH, uint16_t>(m_emu, inst); }
738802
bool operator()(SW inst) { return Store<SW, uint32_t>(m_emu, inst); }
739803
bool operator()(ADDI inst) {
740-
return transformOptional(inst.rs1.ReadI64(m_emu),
741-
[&](int64_t rs1) {
742-
return inst.rd.Write(
743-
m_emu, rs1 + int64_t(SignExt(inst.imm)));
744-
})
804+
return transformOptional(
805+
inst.rs1.ReadI64(m_emu),
806+
[&](int64_t rs1) {
807+
int64_t result = rs1 + int64_t(SignExt(inst.imm));
808+
// Check if this is a stack pointer adjustment.
809+
if (inst.rd.rd == RISCV_GPR_SP &&
810+
inst.rs1.rs == RISCV_GPR_SP) {
811+
EmulateInstruction::Context context;
812+
context.type =
813+
EmulateInstruction::eContextAdjustStackPointer;
814+
context.SetImmediateSigned(SignExt(inst.imm));
815+
uint32_t sp_lldb_reg = GPREncodingToLLDB(RISCV_GPR_SP);
816+
RegisterValue registerValue;
817+
registerValue.SetUInt64(result);
818+
return m_emu.WriteRegister(context, eRegisterKindLLDB,
819+
sp_lldb_reg, registerValue);
820+
}
821+
// Check if this is setting up the frame pointer.
822+
// addi fp, sp, imm -> fp = sp + imm (frame pointer setup).
823+
if (inst.rd.rd == RISCV_GPR_FP &&
824+
inst.rs1.rs == RISCV_GPR_SP) {
825+
EmulateInstruction::Context context;
826+
context.type = EmulateInstruction::eContextSetFramePointer;
827+
auto sp_reg_info = m_emu.GetRegisterInfo(
828+
eRegisterKindLLDB, GPREncodingToLLDB(RISCV_GPR_SP));
829+
if (sp_reg_info) {
830+
context.SetRegisterPlusOffset(*sp_reg_info,
831+
SignExt(inst.imm));
832+
}
833+
uint32_t fp_lldb_reg = GPREncodingToLLDB(RISCV_GPR_FP);
834+
RegisterValue registerValue;
835+
registerValue.SetUInt64(result);
836+
return m_emu.WriteRegister(context, eRegisterKindLLDB,
837+
fp_lldb_reg, registerValue);
838+
}
839+
// Regular ADDI instruction.
840+
return inst.rd.Write(m_emu, result);
841+
})
745842
.value_or(false);
746843
}
747844
bool operator()(SLTI inst) {
@@ -1745,6 +1842,61 @@ EmulateInstructionRISCV::GetRegisterInfo(RegisterKind reg_kind,
17451842
return array[reg_index];
17461843
}
17471844

1845+
bool EmulateInstructionRISCV::SetInstruction(const Opcode &opcode,
1846+
const Address &inst_addr,
1847+
Target *target) {
1848+
// Call the base class implementation.
1849+
if (!EmulateInstruction::SetInstruction(opcode, inst_addr, target))
1850+
return false;
1851+
1852+
// Extract instruction data from the opcode.
1853+
uint32_t inst_data = 0;
1854+
const void *opcode_data = m_opcode.GetOpcodeBytes();
1855+
if (!opcode_data)
1856+
return false;
1857+
1858+
if (m_opcode.GetByteSize() == 2) {
1859+
// 16-bit compressed instruction.
1860+
const uint16_t *data = static_cast<const uint16_t *>(opcode_data);
1861+
inst_data = *data;
1862+
} else if (m_opcode.GetByteSize() == 4) {
1863+
// 32-bit instruction.
1864+
const uint32_t *data = static_cast<const uint32_t *>(opcode_data);
1865+
inst_data = *data;
1866+
} else {
1867+
return false;
1868+
}
1869+
1870+
// Decode the instruction.
1871+
auto decoded_inst = Decode(inst_data);
1872+
if (!decoded_inst)
1873+
return false;
1874+
1875+
// Store the decoded result.
1876+
m_decoded = *decoded_inst;
1877+
return true;
1878+
}
1879+
1880+
bool EmulateInstructionRISCV::CreateFunctionEntryUnwind(
1881+
UnwindPlan &unwind_plan) {
1882+
unwind_plan.Clear();
1883+
unwind_plan.SetRegisterKind(eRegisterKindLLDB);
1884+
1885+
UnwindPlan::Row row;
1886+
1887+
row.GetCFAValue().SetIsRegisterPlusOffset(gpr_sp_riscv, 0);
1888+
row.SetRegisterLocationToSame(gpr_ra_riscv, /*must_replace=*/false);
1889+
row.SetRegisterLocationToSame(gpr_fp_riscv, /*must_replace=*/false);
1890+
1891+
unwind_plan.AppendRow(std::move(row));
1892+
unwind_plan.SetSourceName("EmulateInstructionRISCV");
1893+
unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
1894+
unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
1895+
unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo);
1896+
unwind_plan.SetReturnAddressRegister(gpr_ra_riscv);
1897+
return true;
1898+
}
1899+
17481900
bool EmulateInstructionRISCV::SetTargetTriple(const ArchSpec &arch) {
17491901
return SupportsThisArch(arch);
17501902
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class EmulateInstructionRISCV : public EmulateInstruction {
6161
case eInstructionTypePCModifying:
6262
return true;
6363
case eInstructionTypePrologueEpilogue:
64+
return true;
6465
case eInstructionTypeAll:
6566
return false;
6667
}
@@ -85,6 +86,7 @@ class EmulateInstructionRISCV : public EmulateInstruction {
8586
return SupportsThisInstructionType(inst_type);
8687
}
8788

89+
bool CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) override;
8890
bool SetTargetTriple(const ArchSpec &arch) override;
8991
bool ReadInstruction() override;
9092
std::optional<uint32_t> GetLastInstrSize() override { return m_last_size; }
@@ -94,6 +96,8 @@ class EmulateInstructionRISCV : public EmulateInstruction {
9496
std::optional<RegisterInfo> GetRegisterInfo(lldb::RegisterKind reg_kind,
9597
uint32_t reg_num) override;
9698

99+
bool SetInstruction(const Opcode &opcode, const Address &inst_addr,
100+
Target *target) override;
97101
std::optional<DecodeResult> ReadInstructionAt(lldb::addr_t addr);
98102
std::optional<DecodeResult> Decode(uint32_t inst);
99103
bool Execute(DecodeResult inst, bool ignore_cond);

lldb/unittests/Instruction/CMakeLists.txt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ add_lldb_unittest(EmulatorTests
22
ARM64/TestAArch64Emulator.cpp
33
LoongArch/TestLoongArchEmulator.cpp
44
RISCV/TestRISCVEmulator.cpp
5-
5+
66
LINK_COMPONENTS
77
Support
88
LINK_LIBS
@@ -13,3 +13,25 @@ add_lldb_unittest(EmulatorTests
1313
lldbPluginInstructionLoongArch
1414
lldbPluginInstructionRISCV
1515
)
16+
17+
# Only add RISCV emulator tests if RISCV is in the list of LLVM targets to build.
18+
# This is necessary because some buildbots (e.g., X86-only machines)
19+
# do not have RISCV support enabled. Without this check, the RISCV tests would be added
20+
# causing build failures.
21+
if ("RISCV" IN_LIST LLVM_TARGETS_TO_BUILD)
22+
add_lldb_unittest(RISCVEmulatorTests
23+
RISCV/TestRiscvInstEmulation.cpp
24+
25+
LINK_COMPONENTS
26+
Support
27+
${LLVM_TARGETS_TO_BUILD}
28+
LINK_LIBS
29+
lldbCore
30+
lldbSymbol
31+
lldbTarget
32+
lldbPluginInstructionRISCV
33+
lldbPluginDisassemblerLLVMC
34+
lldbPluginUnwindAssemblyInstEmulation
35+
lldbPluginProcessUtility
36+
)
37+
endif()

0 commit comments

Comments
 (0)