[Lldb-commits] [lldb] edc1c9f - [LLDB][RISCV] Add RVM and RVA instruction support for EmulateInstructionRISCV
via lldb-commits
lldb-commits at lists.llvm.org
Fri Sep 16 21:19:20 PDT 2022
Author: Emmmer
Date: 2022-09-17T12:19:09+08:00
New Revision: edc1c9f6108a4eae24990928f914fe5a39f57ca6
URL: https://github.com/llvm/llvm-project/commit/edc1c9f6108a4eae24990928f914fe5a39f57ca6
DIFF: https://github.com/llvm/llvm-project/commit/edc1c9f6108a4eae24990928f914fe5a39f57ca6.diff
LOG: [LLDB][RISCV] Add RVM and RVA instruction support for EmulateInstructionRISCV
Add:
- RVM and RVA instructions sets.
- corresponding unittests.
Further work:
- implement RVC, RVF, RVD, and RVV extension.
Reviewed By: DavidSpickett
Differential Revision: https://reviews.llvm.org/D133670
Added:
Modified:
lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp
lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h
lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp
Removed:
################################################################################
diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp
index 4ca017dd98bbe..bcd18ff63d11b 100644
--- a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp
+++ b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp
@@ -19,7 +19,6 @@
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/LLDBLog.h"
-#include "lldb/Utility/RegisterValue.h"
#include "lldb/Utility/Stream.h"
#include "llvm/ADT/STLExtras.h"
@@ -115,7 +114,7 @@ static bool WriteRegister(EmulateInstructionRISCV &emulator,
static bool ExecJAL(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
bool success = false;
int64_t offset = SignExt(DecodeJImm(inst));
- int64_t pc = emulator.ReadPC(&success);
+ int64_t pc = emulator.ReadPC(success);
return success && emulator.WritePC(pc + offset) &&
WriteRegister(emulator, DecodeRD(inst),
RegisterValue(uint64_t(pc + 4)));
@@ -127,7 +126,7 @@ static bool ExecJALR(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
if (!ReadRegister(emulator, DecodeRS1(inst), value))
return false;
bool success = false;
- int64_t pc = emulator.ReadPC(&success);
+ int64_t pc = emulator.ReadPC(success);
int64_t rs1 = int64_t(value.GetAsUInt64());
// JALR clears the bottom bit. According to riscv-spec:
// "The JALR instruction now clears the lowest bit of the calculated target
@@ -157,10 +156,60 @@ static bool CompareB(uint64_t rs1, uint64_t rs2, uint32_t funct3) {
}
}
+struct Wrapped {
+ RegisterValue value;
+
+ template <typename T>
+ [[nodiscard]] std::enable_if_t<std::is_unsigned_v<T>, T> trunc() const {
+ return T(value.GetAsUInt64());
+ }
+
+ template <typename T> T sext() const = delete;
+};
+
+template <> int32_t Wrapped::sext<int32_t>() const {
+ return int32_t(trunc<uint32_t>());
+}
+
+template <> int64_t Wrapped::sext<int64_t>() const {
+ return int64_t(trunc<uint64_t>());
+}
+
+template <bool hasRS2> struct RSRegs {
+private:
+ Wrapped rs1_value;
+
+public:
+ bool valid;
+
+ Wrapped &rs1() { return rs1_value; }
+};
+
+template <> struct RSRegs<true> : RSRegs<false> {
+private:
+ Wrapped rs2_value;
+
+public:
+ Wrapped &rs2() { return rs2_value; }
+};
+
+RSRegs<true> readRS1RS2(EmulateInstructionRISCV &emulator, uint32_t inst) {
+ RSRegs<true> value{};
+ value.valid = ReadRegister(emulator, DecodeRS1(inst), value.rs1().value) &&
+ ReadRegister(emulator, DecodeRS2(inst), value.rs2().value);
+ return value;
+}
+
+RSRegs<false> readRS1(EmulateInstructionRISCV &emulator, uint32_t inst) {
+ RSRegs<false> value{};
+ value.valid = ReadRegister(emulator, DecodeRS1(inst), value.rs1().value);
+ return value;
+}
+
static bool ExecB(EmulateInstructionRISCV &emulator, uint32_t inst,
bool ignore_cond) {
bool success = false;
- uint64_t pc = emulator.ReadPC(&success);
+ uint64_t pc = emulator.ReadPC(success);
if (!success)
return false;
@@ -169,14 +218,12 @@ static bool ExecB(EmulateInstructionRISCV &emulator, uint32_t inst,
if (ignore_cond)
return emulator.WritePC(target);
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, DecodeRS1(inst), value1) ||
- !ReadRegister(emulator, DecodeRS2(inst), value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
uint32_t funct3 = DecodeFunct3(inst);
- if (CompareB(value1.GetAsUInt64(), value2.GetAsUInt64(), funct3))
+ if (CompareB(rs.rs1().trunc<uint64_t>(), rs.rs2().trunc<uint64_t>(), funct3))
return emulator.WritePC(target);
return true;
@@ -193,18 +240,21 @@ static bool ExecAUIPC(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
uint32_t imm = DecodeUImm(inst);
RegisterValue value;
bool success = false;
- value.SetUInt64(SignExt(imm) + emulator.ReadPC(&success));
+ value.SetUInt64(SignExt(imm) + emulator.ReadPC(success));
return success && WriteRegister(emulator, DecodeRD(inst), value);
}
template <typename T>
-static std::enable_if_t<std::is_integral_v<T>, T>
-ReadMem(EmulateInstructionRISCV &emulator, uint64_t addr, bool *success) {
-
+static std::enable_if_t<std::is_integral_v<T>, llvm::Optional<T>>
+ReadMem(EmulateInstructionRISCV &emulator, uint64_t addr) {
EmulateInstructionRISCV::Context ctx;
ctx.type = EmulateInstruction::eContextRegisterLoad;
ctx.SetNoArgs();
- return T(emulator.ReadMemoryUnsigned(ctx, addr, sizeof(T), T(), success));
+ bool success = false;
+ T result = emulator.ReadMemoryUnsigned(ctx, addr, sizeof(T), T(), &success);
+ if (!success)
+ return {}; // aka return false
+ return result;
}
template <typename T>
@@ -219,12 +269,11 @@ static bool WriteMem(EmulateInstructionRISCV &emulator, uint64_t addr,
static uint64_t LoadStoreAddr(EmulateInstructionRISCV &emulator,
uint32_t inst) {
- auto rs1 = DecodeRS1(inst);
int32_t imm = SignExt(DecodeSImm(inst));
- RegisterValue value;
- if (!ReadRegister(emulator, rs1, value))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return LLDB_INVALID_ADDRESS;
- uint64_t addr = value.GetAsUInt64() + uint64_t(imm);
+ uint64_t addr = rs.rs1().trunc<uint64_t>() + uint64_t(imm);
return addr;
}
@@ -235,9 +284,8 @@ static bool Load(EmulateInstructionRISCV &emulator, uint32_t inst,
uint64_t addr = LoadStoreAddr(emulator, inst);
if (addr == LLDB_INVALID_ADDRESS)
return false;
- bool success = false;
- E value = E(ReadMem<T>(emulator, addr, &success));
- if (!success)
+ E value = E(ReadMem<T>(emulator, addr).value());
+ if (!value)
return false;
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value)));
}
@@ -253,6 +301,7 @@ static bool Store(EmulateInstructionRISCV &emulator, uint32_t inst) {
return WriteMem<T>(emulator, addr, value);
}
+// RV32I & RV64I (The base integer ISA) //
static bool ExecLB(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
return Load<uint8_t, int8_t>(emulator, inst, SextW);
}
@@ -298,376 +347,765 @@ static bool ExecSD(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
}
static bool ExecADDI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
int32_t imm = SignExt(DecodeIImm(inst));
- RegisterValue value;
- if (!ReadRegister(emulator, rs1, value))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = int64_t(value.GetAsUInt64()) + int64_t(imm);
+ uint64_t result = rs.rs1().sext<int64_t>() + int64_t(imm);
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSLTI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
int32_t imm = SignExt(DecodeIImm(inst));
- RegisterValue value;
- if (!ReadRegister(emulator, rs1, value))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = int64_t(value.GetAsUInt64()) < int64_t(imm);
+ uint64_t result = rs.rs1().sext<int64_t>() < int64_t(imm);
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSLTIU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
int32_t imm = SignExt(DecodeIImm(inst));
- RegisterValue value;
- if (!ReadRegister(emulator, rs1, value))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value.GetAsUInt64() < uint64_t(imm);
+ uint64_t result = rs.rs1().trunc<uint64_t>() < uint64_t(imm);
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecXORI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
int32_t imm = SignExt(DecodeIImm(inst));
- RegisterValue value;
- if (!ReadRegister(emulator, rs1, value))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value.GetAsUInt64() ^ uint64_t(imm);
+ uint64_t result = rs.rs1().trunc<uint64_t>() ^ uint64_t(imm);
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecORI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
int32_t imm = SignExt(DecodeIImm(inst));
- RegisterValue value;
- if (!ReadRegister(emulator, rs1, value))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value.GetAsUInt64() | uint64_t(imm);
+ uint64_t result = rs.rs1().trunc<uint64_t>() | uint64_t(imm);
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecANDI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
int32_t imm = SignExt(DecodeIImm(inst));
- RegisterValue value;
- if (!ReadRegister(emulator, rs1, value))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value.GetAsUInt64() & uint64_t(imm);
+ uint64_t result = rs.rs1().trunc<uint64_t>() & uint64_t(imm);
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSLLI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto shamt = DecodeSHAMT7(inst);
-
- RegisterValue value;
- if (!ReadRegister(emulator, rs1, value))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value.GetAsUInt64() << shamt;
+ uint64_t result = rs.rs1().trunc<uint64_t>() << DecodeSHAMT7(inst);
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSRLI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto shamt = DecodeSHAMT7(inst);
-
- RegisterValue value;
- if (!ReadRegister(emulator, rs1, value))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value.GetAsUInt64() >> shamt;
+ uint64_t result = rs.rs1().trunc<uint64_t>() >> DecodeSHAMT7(inst);
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSRAI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto shamt = DecodeSHAMT7(inst);
-
- RegisterValue value;
- if (!ReadRegister(emulator, rs1, value))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = int64_t(value.GetAsUInt64()) >> shamt;
+ uint64_t result = rs.rs1().sext<int64_t>() >> DecodeSHAMT7(inst);
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecADD(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value1.GetAsUInt64() + value2.GetAsUInt64();
+ uint64_t result = rs.rs1().trunc<uint64_t>() + rs.rs2().trunc<uint64_t>();
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSUB(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value1.GetAsUInt64() - value2.GetAsUInt64();
+ uint64_t result = rs.rs1().trunc<uint64_t>() - rs.rs2().trunc<uint64_t>();
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSLL(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value1.GetAsUInt64() << (value2.GetAsUInt64() & 0b111111);
+ uint64_t result = rs.rs1().trunc<uint64_t>()
+ << (rs.rs2().trunc<uint64_t>() & 0b111111);
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSLT(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = int64_t(value1.GetAsUInt64()) < int64_t(value2.GetAsUInt64());
+ uint64_t result = rs.rs1().sext<int64_t>() < rs.rs2().sext<int64_t>();
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSLTU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value1.GetAsUInt64() < value2.GetAsUInt64();
+ uint64_t result = rs.rs1().trunc<uint64_t>() < rs.rs2().trunc<uint64_t>();
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecXOR(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value1.GetAsUInt64() ^ value2.GetAsUInt64();
+ uint64_t result = rs.rs1().trunc<uint64_t>() ^ rs.rs2().trunc<uint64_t>();
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSRL(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value1.GetAsUInt64() >> (value2.GetAsUInt64() & 0b111111);
+ uint64_t result =
+ rs.rs1().trunc<uint64_t>() >> (rs.rs2().trunc<uint64_t>() & 0b111111);
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSRA(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
uint64_t result =
- int64_t(value1.GetAsUInt64()) >> (value2.GetAsUInt64() & 0b111111);
+ rs.rs1().sext<int64_t>() >> (rs.rs2().trunc<uint64_t>() & 0b111111);
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecOR(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value1.GetAsUInt64() | value2.GetAsUInt64();
+ uint64_t result = rs.rs1().trunc<uint64_t>() | rs.rs2().trunc<uint64_t>();
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecAND(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = value1.GetAsUInt64() & value2.GetAsUInt64();
+ uint64_t result = rs.rs1().trunc<uint64_t>() & rs.rs2().trunc<uint64_t>();
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecADDIW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
int32_t imm = SignExt(DecodeIImm(inst));
- RegisterValue value1;
- if (!ReadRegister(emulator, rs1, value1))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = SextW(int32_t(value1.GetAsUInt64()) + imm);
+ uint64_t result = SextW(rs.rs1().sext<int32_t>() + imm);
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSLLIW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto shamt = DecodeSHAMT5(inst);
-
- RegisterValue value1;
- if (!ReadRegister(emulator, rs1, value1))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = SextW(uint32_t(value1.GetAsUInt64()) << shamt);
+ uint64_t result = SextW(rs.rs1().trunc<uint32_t>() << DecodeSHAMT5(inst));
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSRLIW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto shamt = DecodeSHAMT5(inst);
-
- RegisterValue value1;
- if (!ReadRegister(emulator, rs1, value1))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = SextW(uint32_t(value1.GetAsUInt64()) >> shamt);
+ uint64_t result = SextW(rs.rs1().trunc<uint32_t>() >> DecodeSHAMT5(inst));
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSRAIW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto shamt = DecodeSHAMT5(inst);
-
- RegisterValue value1;
- if (!ReadRegister(emulator, rs1, value1))
+ auto rs = readRS1(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = SextW(int32_t(value1.GetAsUInt64()) >> shamt);
+ uint64_t result = SextW(rs.rs1().sext<int32_t>() >> DecodeSHAMT5(inst));
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecADDW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = SextW(value1.GetAsUInt32() + value2.GetAsUInt32());
+ uint64_t result =
+ SextW(uint32_t(rs.rs1().trunc<uint64_t>() + rs.rs2().trunc<uint64_t>()));
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSUBW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = SextW(value1.GetAsUInt32() - value2.GetAsUInt32());
+ uint64_t result =
+ SextW(uint32_t(rs.rs1().trunc<uint64_t>() - rs.rs2().trunc<uint64_t>()));
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSLLW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = SextW(uint32_t(value1.GetAsUInt64())
- << (value2.GetAsUInt64() & 0b111111));
+ uint64_t result = SextW(rs.rs1().trunc<uint32_t>()
+ << (rs.rs2().trunc<uint32_t>() & 0b11111));
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSRLW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
-
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result = SextW(uint32_t(value1.GetAsUInt64()) >>
- (value2.GetAsUInt64() & 0b111111));
+ uint64_t result = SextW(rs.rs1().trunc<uint32_t>() >>
+ (rs.rs2().trunc<uint32_t>() & 0b11111));
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
static bool ExecSRAW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
- auto rs1 = DecodeRS1(inst);
- auto rs2 = DecodeRS2(inst);
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
+ return false;
+
+ uint64_t result = SextW(rs.rs1().sext<int32_t>() >>
+ (rs.rs2().trunc<uint64_t>() & 0b111111));
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
+}
- RegisterValue value1;
- RegisterValue value2;
- if (!ReadRegister(emulator, rs1, value1) ||
- !ReadRegister(emulator, rs2, value2))
+// RV32M & RV64M (The standard integer multiplication and division extension) //
+static bool ExecMUL(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
return false;
- uint64_t result =
- SextW(int32_t(value1.GetAsUInt64()) >> (value2.GetAsUInt64() & 0b111111));
+ uint64_t result = rs.rs1().trunc<uint64_t>() * rs.rs2().trunc<uint64_t>();
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
+}
+
+static bool ExecMULH(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
+ return false;
+
+ llvm::APInt mul = llvm::APInt(128, rs.rs1().trunc<uint64_t>(), true) *
+ llvm::APInt(128, rs.rs2().trunc<uint64_t>(), true);
+
+ // signed * signed
+ auto result = mul.ashr(64).trunc(64).getZExtValue();
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
+}
+
+static bool ExecMULHSU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
+ return false;
+
+ // signed * unsigned
+ llvm::APInt mul =
+ llvm::APInt(128, rs.rs1().trunc<uint64_t>(), true).zext(128) *
+ llvm::APInt(128, rs.rs2().trunc<uint64_t>(), false);
+
+ auto result = mul.lshr(64).trunc(64).getZExtValue();
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
+}
+
+static bool ExecMULHU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
+ return false;
+
+ // unsigned * unsigned
+ llvm::APInt mul = llvm::APInt(128, rs.rs1().trunc<uint64_t>(), false) *
+ llvm::APInt(128, rs.rs2().trunc<uint64_t>(), false);
+ auto result = mul.lshr(64).trunc(64).getZExtValue();
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
+}
+
+static bool ExecDIV(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
+ return false;
+
+ int64_t dividend = rs.rs1().sext<int64_t>();
+ int64_t divisor = rs.rs2().sext<int64_t>();
+
+ if (divisor == 0)
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(UINT64_MAX));
+
+ if (dividend == INT64_MIN && divisor == -1)
+ return WriteRegister(emulator, DecodeRD(inst),
+ RegisterValue(uint64_t(dividend)));
+
+ return WriteRegister(emulator, DecodeRD(inst),
+ RegisterValue(uint64_t(dividend / divisor)));
+}
+
+static bool ExecDIVU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
+ return false;
+
+ uint64_t dividend = rs.rs1().trunc<uint64_t>();
+ uint64_t divisor = rs.rs2().trunc<uint64_t>();
+
+ if (divisor == 0)
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(UINT64_MAX));
+
+ return WriteRegister(emulator, DecodeRD(inst),
+ RegisterValue(dividend / divisor));
+}
+
+static bool ExecREM(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
+ return false;
+
+ int64_t dividend = rs.rs1().sext<int64_t>();
+ int64_t divisor = rs.rs2().sext<int64_t>();
+
+ if (divisor == 0)
+ return WriteRegister(emulator, DecodeRD(inst),
+ RegisterValue(uint64_t(dividend)));
+
+ if (dividend == INT64_MIN && divisor == -1)
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(uint64_t(0)));
+
+ return WriteRegister(emulator, DecodeRD(inst),
+ RegisterValue(uint64_t(dividend % divisor)));
+}
+
+static bool ExecREMU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
+ return false;
+
+ uint64_t dividend = rs.rs1().trunc<uint64_t>();
+ uint64_t divisor = rs.rs2().trunc<uint64_t>();
+
+ if (divisor == 0)
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(dividend));
+
+ return WriteRegister(emulator, DecodeRD(inst),
+ RegisterValue(dividend % divisor));
+}
+
+static bool ExecMULW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
+ return false;
+
+ uint64_t result = SextW(rs.rs1().sext<int32_t>() * rs.rs2().sext<int32_t>());
return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result));
}
+static bool ExecDIVW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
+ return false;
+
+ int32_t dividend = rs.rs1().sext<int32_t>();
+ int32_t divisor = rs.rs2().sext<int32_t>();
+
+ if (divisor == 0)
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(UINT64_MAX));
+
+ if (dividend == INT32_MIN && divisor == -1)
+ return WriteRegister(emulator, DecodeRD(inst),
+ RegisterValue(SextW(dividend)));
+
+ return WriteRegister(emulator, DecodeRD(inst),
+ RegisterValue(SextW(dividend / divisor)));
+}
+
+static bool ExecDIVUW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
+ return false;
+
+ uint32_t dividend = rs.rs1().trunc<uint32_t>();
+ uint32_t divisor = rs.rs2().trunc<uint32_t>();
+
+ if (divisor == 0)
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(UINT64_MAX));
+
+ return WriteRegister(emulator, DecodeRD(inst),
+ RegisterValue(SextW(dividend / divisor)));
+}
+
+static bool ExecREMW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
+ return false;
+
+ int32_t dividend = rs.rs1().sext<int32_t>();
+ int32_t divisor = rs.rs2().sext<int32_t>();
+
+ if (divisor == 0)
+ return WriteRegister(emulator, DecodeRD(inst),
+ RegisterValue(SextW(dividend)));
+
+ if (dividend == INT32_MIN && divisor == -1)
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(uint64_t(0)));
+
+ return WriteRegister(emulator, DecodeRD(inst),
+ RegisterValue(SextW(dividend % divisor)));
+}
+
+static bool ExecREMUW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ auto rs = readRS1RS2(emulator, inst);
+ if (!rs.valid)
+ return false;
+
+ uint32_t dividend = rs.rs1().trunc<uint32_t>();
+ uint32_t divisor = rs.rs2().trunc<uint32_t>();
+
+ if (divisor == 0)
+ return WriteRegister(emulator, DecodeRD(inst),
+ RegisterValue(SextW(dividend)));
+
+ return WriteRegister(emulator, DecodeRD(inst),
+ RegisterValue(SextW(dividend % divisor)));
+}
+
+// RV32A & RV64A (The standard atomic instruction extension) //
+static uint64_t AtomicAddr(EmulateInstructionRISCV &emulator, uint32_t reg,
+ unsigned int align) {
+ RegisterValue value;
+ if (!ReadRegister(emulator, reg, value))
+ return LLDB_INVALID_ADDRESS;
+
+ uint64_t addr = value.GetAsUInt64();
+ return addr % align == 0 ? addr : LLDB_INVALID_ADDRESS;
+}
+
+template <typename T>
+static bool AtomicSwap(EmulateInstructionRISCV &emulator, uint32_t inst,
+ int align, uint64_t (*extend)(T)) {
+ RegisterValue rs2;
+ if (!ReadRegister(emulator, DecodeRS2(inst), rs2))
+ return false;
+
+ uint64_t addr = AtomicAddr(emulator, DecodeRS1(inst), align);
+ if (addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ T tmp = ReadMem<T>(emulator, addr).value();
+ if (!tmp)
+ return false;
+
+ bool success =
+ WriteMem<T>(emulator, addr, RegisterValue(T(rs2.GetAsUInt64())));
+ if (!success)
+ return false;
+
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(tmp)));
+}
+
+template <typename T>
+static bool AtomicADD(EmulateInstructionRISCV &emulator, uint32_t inst,
+ int align, uint64_t (*extend)(T)) {
+ RegisterValue rs2;
+ if (!ReadRegister(emulator, DecodeRS2(inst), rs2))
+ return false;
+
+ uint64_t addr = AtomicAddr(emulator, DecodeRS1(inst), align);
+ if (addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ T value = ReadMem<T>(emulator, addr).value();
+ if (!value)
+ return false;
+
+ bool success =
+ WriteMem<T>(emulator, addr, RegisterValue(value + T(rs2.GetAsUInt64())));
+ if (!success)
+ return false;
+
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value)));
+}
+
+template <typename T>
+static bool AtomicBitOperate(EmulateInstructionRISCV &emulator, uint32_t inst,
+ int align, uint64_t (*extend)(T),
+ T (*operate)(T, T)) {
+ RegisterValue rs2;
+ if (!ReadRegister(emulator, DecodeRS2(inst), rs2))
+ return false;
+
+ uint64_t addr = AtomicAddr(emulator, DecodeRS1(inst), align);
+ if (addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ T value = ReadMem<T>(emulator, addr).value();
+ if (!value)
+ return false;
+
+ bool success = WriteMem<T>(
+ emulator, addr, RegisterValue(operate(value, T(rs2.GetAsUInt64()))));
+ if (!success)
+ return false;
+
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value)));
+}
+
+template <typename T>
+static bool AtomicCmp(EmulateInstructionRISCV &emulator, uint32_t inst,
+ int align, uint64_t (*extend)(T), T (*cmp)(T, T)) {
+ RegisterValue rs2;
+ if (!ReadRegister(emulator, DecodeRS2(inst), rs2))
+ return false;
+
+ uint64_t addr = AtomicAddr(emulator, DecodeRS1(inst), align);
+ if (addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ T value = ReadMem<T>(emulator, addr).value();
+ if (!value)
+ return false;
+
+ bool success = WriteMem<T>(emulator, addr,
+ RegisterValue(cmp(value, T(rs2.GetAsUInt64()))));
+ if (!success)
+ return false;
+
+ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value)));
+}
+
+// 00010 aq[1] rl[1] 00000 rs1[5] 010 rd[5] 0101111 LR.D
+// 00010 aq[1] rl[1] 00000 rs1[5] 011 rd[5] 0101111 LR.W
+static bool IsLR(uint32_t inst) {
+ return (inst & 0xF9F0707F) == 0x1000202F || (inst & 0xF9F0707F) == 0x1000302F;
+}
+
+// 00011 aq[1] rl[1] rs2[5] rs1[5] 010 rd[5] 0101111 SC.W
+// 00011 aq[1] rl[1] rs2[5] rs1[5] 011 rd[5] 0101111 SC.D
+static bool IsSC(uint32_t inst) {
+ return (inst & 0xF800707F) == 0x1800202F || (inst & 0xF800707F) == 0x1800302F;
+}
+
+// imm[7] rs2[5] rs1[5] 001 imm[5] 1100011 BNE
+static bool IsBNE(uint32_t inst) { return (inst & 0x707F) == 0x1063; }
+
+static bool ExecAtomicSequence(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ // The atomic sequence is always 4 instructions long:
+ // example:
+ // 110cc: 100427af lr.w a5,(s0)
+ // 110d0: 00079663 bnez a5,110dc
+ // 110d4: 1ce426af sc.w.aq a3,a4,(s0)
+ // 110d8: fe069ae3 bnez a3,110cc
+ // 110dc: ........ <next instruction>
+ bool success = false;
+ llvm::Optional<lldb::addr_t> pc = emulator.ReadPC(success);
+ if (!success || !pc)
+ return false;
+ lldb::addr_t current_pc = pc.value();
+ lldb::addr_t start_pc = current_pc;
+ llvm::Optional<uint32_t> value = inst;
+
+ // inst must be LR
+ if (!value || !IsLR(value.value()))
+ return false;
+ value = ReadMem<uint32_t>(emulator, current_pc += 4).value();
+
+ // inst must be BNE to exit
+ if (!value || !IsBNE(value.value()))
+ return false;
+ auto exit_pc = current_pc + SextW(DecodeBImm(value.value()));
+ value = ReadMem<uint32_t>(emulator, current_pc += 4).value();
+
+ // inst must be SC
+ if (!value || !IsSC(value.value()))
+ return false;
+ value = ReadMem<uint32_t>(emulator, current_pc += 4).value();
+
+ // inst must be BNE to restart
+ if (!value || !IsBNE(value.value()))
+ return false;
+ if (current_pc + SextW(DecodeBImm(value.value())) != start_pc)
+ return false;
+ current_pc += 4;
+
+ if (exit_pc != current_pc)
+ return false;
+
+ return emulator.WritePC(current_pc);
+}
+
+static bool ExecLR_W(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ return ExecAtomicSequence(emulator, inst, false);
+}
+
+static bool ExecAMOSWAP_W(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicSwap<uint32_t>(emulator, inst, 4, SextW);
+}
+
+static bool ExecAMOADD_W(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicADD<uint32_t>(emulator, inst, 4, SextW);
+}
+
+static bool ExecAMOXOR_W(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicBitOperate<uint32_t>(
+ emulator, inst, 4, SextW, [](uint32_t a, uint32_t b) { return a ^ b; });
+}
+
+static bool ExecAMOAND_W(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicBitOperate<uint32_t>(
+ emulator, inst, 4, SextW, [](uint32_t a, uint32_t b) { return a & b; });
+}
+
+static bool ExecAMOOR_W(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicBitOperate<uint32_t>(
+ emulator, inst, 4, SextW, [](uint32_t a, uint32_t b) { return a | b; });
+}
+
+static bool ExecAMOMIN_W(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicCmp<uint32_t>(
+ emulator, inst, 4, SextW, [](uint32_t a, uint32_t b) {
+ return uint32_t(std::min(int32_t(a), int32_t(b)));
+ });
+}
+
+static bool ExecAMOMAX_W(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicCmp<uint32_t>(
+ emulator, inst, 4, SextW, [](uint32_t a, uint32_t b) {
+ return uint32_t(std::max(int32_t(a), int32_t(b)));
+ });
+}
+
+static bool ExecAMOMINU_W(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicCmp<uint32_t>(
+ emulator, inst, 4, SextW,
+ [](uint32_t a, uint32_t b) { return std::min(a, b); });
+}
+
+static bool ExecAMOMAXU_W(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicCmp<uint32_t>(
+ emulator, inst, 4, SextW,
+ [](uint32_t a, uint32_t b) { return std::max(a, b); });
+}
+
+static bool ExecLR_D(EmulateInstructionRISCV &emulator, uint32_t inst, bool) {
+ return ExecAtomicSequence(emulator, inst, false);
+}
+
+static bool ExecAMOSWAP_D(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicSwap<uint64_t>(emulator, inst, 8, ZextD);
+}
+
+static bool ExecAMOADD_D(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicADD<uint64_t>(emulator, inst, 8, ZextD);
+}
+
+static bool ExecAMOXOR_D(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicBitOperate<uint64_t>(
+ emulator, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return a ^ b; });
+}
+
+static bool ExecAMOAND_D(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicBitOperate<uint64_t>(
+ emulator, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return a & b; });
+}
+
+static bool ExecAMOOR_D(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicBitOperate<uint64_t>(
+ emulator, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return a | b; });
+}
+
+static bool ExecAMOMIN_D(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicCmp<uint64_t>(
+ emulator, inst, 8, ZextD, [](uint64_t a, uint64_t b) {
+ return uint64_t(std::min(int64_t(a), int64_t(b)));
+ });
+}
+
+static bool ExecAMOMAX_D(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicCmp<uint64_t>(
+ emulator, inst, 8, ZextD, [](uint64_t a, uint64_t b) {
+ return uint64_t(std::max(int64_t(a), int64_t(b)));
+ });
+}
+
+static bool ExecAMOMINU_D(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicCmp<uint64_t>(
+ emulator, inst, 8, ZextD,
+ [](uint64_t a, uint64_t b) { return std::min(a, b); });
+}
+
+static bool ExecAMOMAXU_D(EmulateInstructionRISCV &emulator, uint32_t inst,
+ bool) {
+ return AtomicCmp<uint64_t>(
+ emulator, inst, 8, ZextD,
+ [](uint64_t a, uint64_t b) { return std::max(a, b); });
+}
+
static const InstrPattern PATTERNS[] = {
+ // RV32I & RV64I (The base integer ISA) //
{"LUI", 0x7F, 0x37, ExecLUI},
{"AUIPC", 0x7F, 0x17, ExecAUIPC},
{"JAL", 0x7F, 0x6F, ExecJAL},
@@ -712,19 +1150,55 @@ static const InstrPattern PATTERNS[] = {
{"SLLW", 0xFE00707F, 0x103B, ExecSLLW},
{"SRLW", 0xFE00707F, 0x503B, ExecSRLW},
{"SRAW", 0xFE00707F, 0x4000503B, ExecSRAW},
+
+ // RV32M & RV64M (The integer multiplication and division extension) //
+ {"MUL", 0xFE00707F, 0x2000033, ExecMUL},
+ {"MULH", 0xFE00707F, 0x2001033, ExecMULH},
+ {"MULHSU", 0xFE00707F, 0x2002033, ExecMULHSU},
+ {"MULHU", 0xFE00707F, 0x2003033, ExecMULHU},
+ {"DIV", 0xFE00707F, 0x2004033, ExecDIV},
+ {"DIVU", 0xFE00707F, 0x2005033, ExecDIVU},
+ {"REM", 0xFE00707F, 0x2006033, ExecREM},
+ {"REMU", 0xFE00707F, 0x2007033, ExecREMU},
+ {"MULW", 0xFE00707F, 0x200003B, ExecMULW},
+ {"DIVW", 0xFE00707F, 0x200403B, ExecDIVW},
+ {"DIVUW", 0xFE00707F, 0x200503B, ExecDIVUW},
+ {"REMW", 0xFE00707F, 0x200603B, ExecREMW},
+ {"REMUW", 0xFE00707F, 0x200703B, ExecREMUW},
+
+ // RV32A & RV64A (The standard atomic instruction extension) //
+ {"LR_W", 0xF9F0707F, 0x1000202F, ExecLR_W},
+ {"AMOSWAP_W", 0xF800707F, 0x800202F, ExecAMOSWAP_W},
+ {"AMOADD_W", 0xF800707F, 0x202F, ExecAMOADD_W},
+ {"AMOXOR_W", 0xF800707F, 0x2000202F, ExecAMOXOR_W},
+ {"AMOAND_W", 0xF800707F, 0x6000202F, ExecAMOAND_W},
+ {"AMOOR_W", 0xF800707F, 0x4000202F, ExecAMOOR_W},
+ {"AMOMIN_W", 0xF800707F, 0x8000202F, ExecAMOMIN_W},
+ {"AMOMAX_W", 0xF800707F, 0xA000202F, ExecAMOMAX_W},
+ {"AMOMINU_W", 0xF800707F, 0xC000202F, ExecAMOMINU_W},
+ {"AMOMAXU_W", 0xF800707F, 0xE000202F, ExecAMOMAXU_W},
+ {"LR_D", 0xF9F0707F, 0x1000302F, ExecLR_D},
+ {"AMOSWAP_D", 0xF800707F, 0x800302F, ExecAMOSWAP_D},
+ {"AMOADD_D", 0xF800707F, 0x302F, ExecAMOADD_D},
+ {"AMOXOR_D", 0xF800707F, 0x2000302F, ExecAMOXOR_D},
+ {"AMOAND_D", 0xF800707F, 0x6000302F, ExecAMOAND_D},
+ {"AMOOR_D", 0xF800707F, 0x4000302F, ExecAMOOR_D},
+ {"AMOMIN_D", 0xF800707F, 0x8000302F, ExecAMOMIN_D},
+ {"AMOMAX_D", 0xF800707F, 0xA000302F, ExecAMOMAX_D},
+ {"AMOMINU_D", 0xF800707F, 0xC000302F, ExecAMOMINU_D},
+ {"AMOMAXU_D", 0xF800707F, 0xE000302F, ExecAMOMAXU_D},
};
const InstrPattern *EmulateInstructionRISCV::Decode(uint32_t inst) {
for (const InstrPattern &pat : PATTERNS) {
- if ((inst & pat.type_mask) == pat.eigen) {
+ if ((inst & pat.type_mask) == pat.eigen)
return &pat;
- }
}
return nullptr;
}
/// This function only determines the next instruction address for software
-/// sigle stepping by emulating instructions
+/// single stepping by emulating instructions
bool EmulateInstructionRISCV::DecodeAndExecute(uint32_t inst,
bool ignore_cond) {
Log *log = GetLog(LLDBLog::Unwind);
@@ -735,11 +1209,9 @@ bool EmulateInstructionRISCV::DecodeAndExecute(uint32_t inst,
return pattern->exec(*this, inst, ignore_cond);
}
- LLDB_LOGF(log,
- "EmulateInstructionRISCV::%s: inst(0x%x) does not branch: "
- "no need to calculate the next pc address which is trivial.",
+ LLDB_LOGF(log, "EmulateInstructionRISCV::%s: inst(0x%x) was unsupported",
__FUNCTION__, inst);
- return true;
+ return false;
}
bool EmulateInstructionRISCV::EvaluateInstruction(uint32_t options) {
@@ -751,7 +1223,7 @@ bool EmulateInstructionRISCV::EvaluateInstruction(uint32_t options) {
lldb::addr_t old_pc = LLDB_INVALID_ADDRESS;
if (increase_pc) {
- old_pc = ReadPC(&success);
+ old_pc = ReadPC(success);
if (!success)
return false;
}
@@ -766,7 +1238,7 @@ bool EmulateInstructionRISCV::EvaluateInstruction(uint32_t options) {
return false;
if (increase_pc) {
- lldb::addr_t new_pc = ReadPC(&success);
+ lldb::addr_t new_pc = ReadPC(success);
if (!success)
return false;
@@ -780,7 +1252,7 @@ bool EmulateInstructionRISCV::EvaluateInstruction(uint32_t options) {
bool EmulateInstructionRISCV::ReadInstruction() {
bool success = false;
- m_addr = ReadPC(&success);
+ m_addr = ReadPC(success);
if (!success) {
m_addr = LLDB_INVALID_ADDRESS;
return false;
@@ -801,9 +1273,9 @@ bool EmulateInstructionRISCV::ReadInstruction() {
return true;
}
-lldb::addr_t EmulateInstructionRISCV::ReadPC(bool *success) {
+lldb::addr_t EmulateInstructionRISCV::ReadPC(bool &success) {
return ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
- LLDB_INVALID_ADDRESS, success);
+ LLDB_INVALID_ADDRESS, &success);
}
bool EmulateInstructionRISCV::WritePC(lldb::addr_t pc) {
diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h
index 07dfcf692fca8..1c7cf6cb08d66 100644
--- a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h
+++ b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h
@@ -12,6 +12,7 @@
#include "lldb/Core/EmulateInstruction.h"
#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
#include "lldb/Utility/Status.h"
namespace lldb_private {
@@ -78,11 +79,31 @@ class EmulateInstructionRISCV : public EmulateInstruction {
bool GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num,
RegisterInfo ®_info) override;
- lldb::addr_t ReadPC(bool *success);
+ lldb::addr_t ReadPC(bool &success);
bool WritePC(lldb::addr_t pc);
const InstrPattern *Decode(uint32_t inst);
bool DecodeAndExecute(uint32_t inst, bool ignore_cond);
+
+ template <typename T>
+ static std::enable_if_t<std::is_integral_v<T>, T>
+ ReadMem(EmulateInstructionRISCV &emulator, uint64_t addr, bool *success) {
+
+ EmulateInstructionRISCV::Context ctx;
+ ctx.type = EmulateInstruction::eContextRegisterLoad;
+ ctx.SetNoArgs();
+ return T(emulator.ReadMemoryUnsigned(ctx, addr, sizeof(T), T(), success));
+ }
+
+ template <typename T>
+ static bool WriteMem(EmulateInstructionRISCV &emulator, uint64_t addr,
+ RegisterValue value) {
+ EmulateInstructionRISCV::Context ctx;
+ ctx.type = EmulateInstruction::eContextRegisterStore;
+ ctx.SetNoArgs();
+ return emulator.WriteMemoryUnsigned(ctx, addr, value.GetAsUInt64(),
+ sizeof(T));
+ }
};
} // namespace lldb_private
diff --git a/lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp b/lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp
index 2ae8b4eb7149c..2df2d08711a8a 100644
--- a/lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp
+++ b/lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp
@@ -24,11 +24,14 @@ using namespace lldb_private;
struct RISCVEmulatorTester : public EmulateInstructionRISCV, testing::Test {
RegisterInfoPOSIX_riscv64::GPR gpr;
+ uint8_t memory[1024] = {0};
RISCVEmulatorTester()
: EmulateInstructionRISCV(ArchSpec("riscv64-unknown-linux-gnu")) {
EmulateInstruction::SetReadRegCallback(ReadRegisterCallback);
EmulateInstruction::SetWriteRegCallback(WriteRegisterCallback);
+ EmulateInstruction::SetReadMemCallback(ReadMemoryCallback);
+ EmulateInstruction::SetWriteMemCallback(WriteMemoryCallback);
}
static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
@@ -53,6 +56,25 @@ struct RISCVEmulatorTester : public EmulateInstructionRISCV, testing::Test {
tester->gpr.gpr[reg] = reg_value.GetAsUInt64();
return true;
}
+
+ static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton,
+ const Context &context, lldb::addr_t addr,
+ void *dst, size_t length) {
+ RISCVEmulatorTester *tester = (RISCVEmulatorTester *)instruction;
+ assert(addr + length < sizeof(tester->memory));
+ memcpy(dst, tester->memory + addr, length);
+ return length;
+ };
+
+ static size_t WriteMemoryCallback(EmulateInstruction *instruction,
+ void *baton, const Context &context,
+ lldb::addr_t addr, const void *dst,
+ size_t length) {
+ RISCVEmulatorTester *tester = (RISCVEmulatorTester *)instruction;
+ assert(addr + length < sizeof(tester->memory));
+ memcpy(tester->memory + addr, dst, length);
+ return length;
+ };
};
TEST_F(RISCVEmulatorTester, testJAL) {
@@ -64,7 +86,7 @@ TEST_F(RISCVEmulatorTester, testJAL) {
auto x1 = gpr.gpr[1];
bool success = false;
- auto pc = ReadPC(&success);
+ auto pc = ReadPC(success);
ASSERT_TRUE(success);
ASSERT_EQ(x1, old_pc + 4);
@@ -91,7 +113,7 @@ TEST_F(RISCVEmulatorTester, testJALR) {
auto x1 = gpr.gpr[1];
bool success = false;
- auto pc = ReadPC(&success);
+ auto pc = ReadPC(success);
ASSERT_TRUE(success);
ASSERT_EQ(x1, old_pc + 4);
@@ -144,7 +166,7 @@ void testBranch(RISCVEmulatorTester *tester, EncoderB encoder, bool branched,
uint32_t inst = encoder(1, 2, -256);
ASSERT_TRUE(tester->DecodeAndExecute(inst, false));
bool success = false;
- auto pc = tester->ReadPC(&success);
+ auto pc = tester->ReadPC(success);
ASSERT_TRUE(success);
ASSERT_EQ(pc, old_pc + (branched ? (-256) : 0));
}
@@ -161,6 +183,13 @@ void CheckRD(RISCVEmulatorTester *tester, uint64_t rd, uint64_t value) {
ASSERT_EQ(tester->gpr.gpr[rd], value);
}
+template <typename T>
+void CheckMem(RISCVEmulatorTester *tester, uint64_t addr, uint64_t value) {
+ bool success = false;
+ ASSERT_EQ(tester->ReadMem<T>(*tester, addr, &success), value);
+ ASSERT_TRUE(success);
+}
+
using RS1 = uint64_t;
using RS2 = uint64_t;
using PC = uint64_t;
@@ -171,21 +200,62 @@ void TestInst(RISCVEmulatorTester *tester, uint64_t inst, bool has_rs2,
lldb::addr_t old_pc = 0x114514;
tester->WritePC(old_pc);
- auto rd = DecodeRD(inst);
- auto rs1 = DecodeRS1(inst);
- auto rs2 = 0;
+ uint32_t rd = DecodeRD(inst);
+ uint32_t rs1 = DecodeRS1(inst);
+ uint32_t rs2 = 0;
+
+ uint64_t rs1_val = 0x19;
+ uint64_t rs2_val = 0x81;
+
if (rs1)
- tester->gpr.gpr[rs1] = 0x1919;
+ tester->gpr.gpr[rs1] = rs1_val;
if (has_rs2) {
rs2 = DecodeRS2(inst);
- if (rs2)
- tester->gpr.gpr[rs2] = 0x8181;
+ if (rs2) {
+ if (rs1 == rs2)
+ rs2_val = rs1_val;
+ tester->gpr.gpr[rs2] = rs2_val;
+ }
}
ASSERT_TRUE(tester->DecodeAndExecute(inst, false));
- CheckRD(tester, rd,
- rd_val(tester->gpr.gpr[rs1], rs2 ? tester->gpr.gpr[rs2] : 0, old_pc));
+ CheckRD(tester, rd, rd_val(rs1_val, rs2 ? rs2_val : 0, old_pc));
+}
+
+template <typename T>
+void TestAtomic(RISCVEmulatorTester *tester, uint64_t inst, T rs1_val,
+ T rs2_val, T rd_expected, T mem_expected) {
+ // Atomic inst must have rs1 and rs2
+
+ uint32_t rd = DecodeRD(inst);
+ uint32_t rs1 = DecodeRS1(inst);
+ uint32_t rs2 = DecodeRS2(inst);
+
+ // addr was stored in rs1
+ uint64_t atomic_addr = 0x100;
+
+ tester->gpr.gpr[rs1] = atomic_addr;
+ tester->gpr.gpr[rs2] = rs2_val;
+
+ // Write and check rs1_val in atomic_addr
+ ASSERT_TRUE(
+ tester->WriteMem<T>(*tester, atomic_addr, RegisterValue(rs1_val)));
+ CheckMem<T>(tester, atomic_addr, rs1_val);
+
+ ASSERT_TRUE(tester->DecodeAndExecute(inst, false));
+ CheckRD(tester, rd, rd_expected);
+ CheckMem<T>(tester, atomic_addr, mem_expected);
+}
+
+TEST_F(RISCVEmulatorTester, TestAtomicSequence) {
+ this->WritePC(0x0);
+ *(uint64_t *)this->memory = 0x100427af; // lr.w a5,(s0)
+ *(uint64_t *)(this->memory + 4) = 0x00079663; // bnez a5,12
+ *(uint64_t *)(this->memory + 8) = 0x1ce426af; // sc.w.aq a3,a4,(s0)
+ *(uint64_t *)(this->memory + 12) = 0xfe069ae3; // bnez a3,-12
+ ASSERT_TRUE(this->DecodeAndExecute(*(uint32_t *)this->memory, false));
+ ASSERT_EQ(this->gpr.gpr[0], uint64_t(16));
}
// GEN_BRANCH_TEST(opcode, imm1, imm2, imm3):
@@ -208,18 +278,43 @@ struct TestData {
TEST_F(RISCVEmulatorTester, TestDecodeAndExcute) {
std::vector<TestData> tests = {
+ // RV32I & RV64I Tests
{0x00010113, "ADDI", false, [](RS1 rs1, RS2, PC) { return rs1 + 0; }},
{0x00023517, "AUIPC", false, [](RS1, RS2, PC pc) { return pc + 143360; }},
{0x0006079b, "ADDIW", false, [](RS1 rs1, RS2, PC) { return rs1 + 0; }},
{0x00110837, "LUI", false, [](RS1, RS2, PC pc) { return 1114112; }},
{0x00147513, "ANDI", false, [](RS1 rs1, RS2, PC) { return rs1 & 1; }},
- {0x00153513, "SLTIU", false, [](RS1 rs1, RS2, PC) { return rs1 != 0; }},
- {0x00256513, "ORI", false, [](RS1 rs1, RS2, PC) { return rs1 | 1; }},
+ {0x00153513, "SLTIU", false, [](RS1 rs1, RS2, PC) { return 0; }},
+ {0x00256513, "ORI", false, [](RS1 rs1, RS2, PC) { return rs1 | 2; }},
{0x00451a13, "SLLI", false, [](RS1 rs1, RS2, PC) { return rs1 << 4; }},
{0x00455693, "SRLI", false, [](RS1 rs1, RS2, PC) { return rs1 >> 4; }},
{0x00a035b3, "SLTU", true, [](RS1 rs1, RS2 rs2, PC) { return rs2 != 0; }},
{0x00b50633, "ADD", true, [](RS1 rs1, RS2 rs2, PC) { return rs1 + rs2; }},
{0x40d507b3, "SUB", true, [](RS1 rs1, RS2 rs2, PC) { return rs1 - rs2; }},
+
+ // RV32M & RV64M Tests
+ {0x02f787b3, "MUL", true, [](RS1 rs1, RS2 rs2, PC) { return rs1 * rs2; }},
+ {0x2F797B3, "MULH", true, [](RS1 rs1, RS2 rs2, PC) { return 0; }},
+ {0x2F7A7B3, "MULHSU", true,
+ [](RS1 rs1, RS2 rs2, PC) { return 0; }},
+ {0x2F7B7B3, "MULHU", true,
+ [](RS1 rs1, RS2 rs2, PC) { return 0; }},
+ {0x02f747b3, "DIV", true, [](RS1 rs1, RS2 rs2, PC) { return rs1 / rs2; }},
+ {0x02f757b3, "DIVU", true,
+ [](RS1 rs1, RS2 rs2, PC) { return rs1 / rs2; }},
+ {0x02f767b3, "REM", true, [](RS1 rs1, RS2 rs2, PC) { return rs1 % rs2; }},
+ {0x02f777b3, "REMU", true,
+ [](RS1 rs1, RS2 rs2, PC) { return rs1 % rs2; }},
+ {0x02f787bb, "MULW", true,
+ [](RS1 rs1, RS2 rs2, PC) { return rs1 * rs2; }},
+ {0x02f747bb, "DIVW", true,
+ [](RS1 rs1, RS2 rs2, PC) { return rs1 / rs2; }},
+ {0x02f757bb, "DIVUW", true,
+ [](RS1 rs1, RS2 rs2, PC) { return rs1 / rs2; }},
+ {0x02f767bb, "REMW", true,
+ [](RS1 rs1, RS2 rs2, PC) { return rs1 % rs2; }},
+ {0x02f777bb, "REMUW", true,
+ [](RS1 rs1, RS2 rs2, PC) { return rs1 % rs2; }},
};
for (auto i : tests) {
const InstrPattern *pattern = this->Decode(i.inst);
@@ -229,3 +324,48 @@ TEST_F(RISCVEmulatorTester, TestDecodeAndExcute) {
TestInst(this, i.inst, i.has_rs2, i.rd_val);
}
}
+
+TEST_F(RISCVEmulatorTester, TestAMOSWAP) {
+ TestAtomic<uint32_t>(this, 0x8F7282F, 0x1, 0x2, 0x1, 0x2);
+ TestAtomic<uint64_t>(this, 0x8F7382F, 0x1, 0x2, 0x1, 0x2);
+}
+
+TEST_F(RISCVEmulatorTester, TestAMOADD) {
+ TestAtomic<uint32_t>(this, 0xF7282F, 0x1, 0x2, 0x1, 0x3);
+ TestAtomic<uint64_t>(this, 0xF7382F, 0x1, 0x2, 0x1, 0x3);
+}
+
+TEST_F(RISCVEmulatorTester, TestAMOXOR) {
+ TestAtomic<uint32_t>(this, 0x20F7282F, 0x1, 0x2, 0x1, 0x3);
+ TestAtomic<uint32_t>(this, 0x20F7382F, 0x1, 0x2, 0x1, 0x3);
+}
+
+TEST_F(RISCVEmulatorTester, TestAMOAND) {
+ TestAtomic<uint32_t>(this, 0x60F7282F, 0x1, 0x2, 0x1, 0x0);
+ TestAtomic<uint64_t>(this, 0x60F7382F, 0x1, 0x2, 0x1, 0x0);
+}
+
+TEST_F(RISCVEmulatorTester, TestAMOOR) {
+ TestAtomic<uint32_t>(this, 0x40F7282F, 0x1, 0x2, 0x1, 0x3);
+ TestAtomic<uint32_t>(this, 0x40F7382F, 0x1, 0x2, 0x1, 0x3);
+}
+
+TEST_F(RISCVEmulatorTester, TestAMOMIN) {
+ TestAtomic<uint32_t>(this, 0x80F7282F, 0x1, 0x2, 0x1, 0x1);
+ TestAtomic<uint64_t>(this, 0x80F7382F, 0x1, 0x2, 0x1, 0x1);
+}
+
+TEST_F(RISCVEmulatorTester, TestAMOMAX) {
+ TestAtomic<uint32_t>(this, 0xA0F7282F, 0x1, 0x2, 0x1, 0x2);
+ TestAtomic<uint64_t>(this, 0xA0F7382F, 0x1, 0x2, 0x1, 0x2);
+}
+
+TEST_F(RISCVEmulatorTester, TestAMOMINU) {
+ TestAtomic<uint32_t>(this, 0xC0F7282F, 0x1, 0x2, 0x1, 0x1);
+ TestAtomic<uint64_t>(this, 0xC0F7382F, 0x1, 0x2, 0x1, 0x1);
+}
+
+TEST_F(RISCVEmulatorTester, TestAMOMAXU) {
+ TestAtomic<uint32_t>(this, 0xE0F7282F, 0x1, 0x2, 0x1, 0x2);
+ TestAtomic<uint64_t>(this, 0xE0F7382F, 0x1, 0x2, 0x1, 0x2);
+}
More information about the lldb-commits
mailing list