Index: lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h =================================================================== --- lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h +++ 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 @@ 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 + static std::enable_if_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 + 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 Index: lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp =================================================================== --- lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp +++ 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 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 @@ 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 @@ } } +struct Wrapped { + RegisterValue value; + + template + [[nodiscard]] std::enable_if_t, T> trunc() const { + return T(value.GetAsUInt64()); + } + + template T sext() const = delete; +}; + +template <> int32_t Wrapped::sext() const { + return int32_t(trunc()); +} + +template <> int64_t Wrapped::sext() const { + return int64_t(trunc()); +} + +template struct RSRegs { +private: + Wrapped rs1_value; + +public: + bool valid; + + Wrapped &rs1() { return rs1_value; } +}; + +template <> struct RSRegs : RSRegs { +private: + Wrapped rs2_value; + +public: + Wrapped &rs2() { return rs2_value; } +}; + +RSRegs readRS1RS2(EmulateInstructionRISCV &emulator, uint32_t inst) { + RSRegs value{}; + value.valid = ReadRegister(emulator, DecodeRS1(inst), value.rs1().value) && + ReadRegister(emulator, DecodeRS2(inst), value.rs2().value); + return value; +} + +RSRegs readRS1(EmulateInstructionRISCV &emulator, uint32_t inst) { + RSRegs 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 @@ 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(), rs.rs2().trunc(), funct3)) return emulator.WritePC(target); return true; @@ -193,18 +240,21 @@ 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 -static std::enable_if_t, T> -ReadMem(EmulateInstructionRISCV &emulator, uint64_t addr, bool *success) { - +static std::enable_if_t, llvm::Optional> +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 @@ -219,12 +269,11 @@ 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(imm); return addr; } @@ -235,9 +284,8 @@ uint64_t addr = LoadStoreAddr(emulator, inst); if (addr == LLDB_INVALID_ADDRESS) return false; - bool success = false; - E value = E(ReadMem(emulator, addr, &success)); - if (!success) + E value = E(ReadMem(emulator, addr).value()); + if (!value) return false; return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value))); } @@ -253,6 +301,7 @@ return WriteMem(emulator, addr, value); } +// RV32I & RV64I (The base integer ISA) // static bool ExecLB(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { return Load(emulator, inst, SextW); } @@ -298,376 +347,765 @@ } 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(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(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(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(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(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(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() << 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() >> 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() >> 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() + rs.rs2().trunc(); 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() - rs.rs2().trunc(); 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() + << (rs.rs2().trunc() & 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() < rs.rs2().sext(); 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() < rs.rs2().trunc(); 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() ^ rs.rs2().trunc(); 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() >> (rs.rs2().trunc() & 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() >> (rs.rs2().trunc() & 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() | rs.rs2().trunc(); 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() & rs.rs2().trunc(); 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() + 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() << 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() >> 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() >> 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() + rs.rs2().trunc())); 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() - rs.rs2().trunc())); 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() + << (rs.rs2().trunc() & 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() >> + (rs.rs2().trunc() & 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() >> + (rs.rs2().trunc() & 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() * rs.rs2().trunc(); + 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(), true) * + llvm::APInt(128, rs.rs2().trunc(), 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(), true).zext(128) * + llvm::APInt(128, rs.rs2().trunc(), 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(), false) * + llvm::APInt(128, rs.rs2().trunc(), 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 divisor = rs.rs2().sext(); + + 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 divisor = rs.rs2().trunc(); + + 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 divisor = rs.rs2().sext(); + + 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 divisor = rs.rs2().trunc(); + + 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() * rs.rs2().sext()); 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 divisor = rs.rs2().sext(); + + 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 divisor = rs.rs2().trunc(); + + 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 divisor = rs.rs2().sext(); + + 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 divisor = rs.rs2().trunc(); + + 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 +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(emulator, addr).value(); + if (!tmp) + return false; + + bool success = + WriteMem(emulator, addr, RegisterValue(T(rs2.GetAsUInt64()))); + if (!success) + return false; + + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(tmp))); +} + +template +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(emulator, addr).value(); + if (!value) + return false; + + bool success = + WriteMem(emulator, addr, RegisterValue(value + T(rs2.GetAsUInt64()))); + if (!success) + return false; + + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value))); +} + +template +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(emulator, addr).value(); + if (!value) + return false; + + bool success = WriteMem( + emulator, addr, RegisterValue(operate(value, T(rs2.GetAsUInt64())))); + if (!success) + return false; + + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value))); +} + +template +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(emulator, addr).value(); + if (!value) + return false; + + bool success = WriteMem(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: ........ + bool success = false; + llvm::Optional 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 value = inst; + + // inst must be LR + if (!value || !IsLR(value.value())) + return false; + value = ReadMem(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(emulator, current_pc += 4).value(); + + // inst must be SC + if (!value || !IsSC(value.value())) + return false; + value = ReadMem(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(emulator, inst, 4, SextW); +} + +static bool ExecAMOADD_W(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicADD(emulator, inst, 4, SextW); +} + +static bool ExecAMOXOR_W(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicBitOperate( + 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( + 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( + 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( + 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( + 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( + 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( + 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(emulator, inst, 8, ZextD); +} + +static bool ExecAMOADD_D(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicADD(emulator, inst, 8, ZextD); +} + +static bool ExecAMOXOR_D(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicBitOperate( + 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( + 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( + 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( + 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( + 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( + 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( + 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 @@ {"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 @@ 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 @@ 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 @@ 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::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 @@ 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) { Index: lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp =================================================================== --- lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp +++ lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp @@ -24,11 +24,14 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ ASSERT_EQ(tester->gpr.gpr[rd], value); } +template +void CheckMem(RISCVEmulatorTester *tester, uint64_t addr, uint64_t value) { + bool success = false; + ASSERT_EQ(tester->ReadMem(*tester, addr, &success), value); + ASSERT_TRUE(success); +} + using RS1 = uint64_t; using RS2 = uint64_t; using PC = uint64_t; @@ -171,21 +200,62 @@ 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 +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(*tester, atomic_addr, RegisterValue(rs1_val))); + CheckMem(tester, atomic_addr, rs1_val); + + ASSERT_TRUE(tester->DecodeAndExecute(inst, false)); + CheckRD(tester, rd, rd_expected); + CheckMem(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 @@ TEST_F(RISCVEmulatorTester, TestDecodeAndExcute) { std::vector 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 @@ TestInst(this, i.inst, i.has_rs2, i.rd_val); } } + +TEST_F(RISCVEmulatorTester, TestAMOSWAP) { + TestAtomic(this, 0x8F7282F, 0x1, 0x2, 0x1, 0x2); + TestAtomic(this, 0x8F7382F, 0x1, 0x2, 0x1, 0x2); +} + +TEST_F(RISCVEmulatorTester, TestAMOADD) { + TestAtomic(this, 0xF7282F, 0x1, 0x2, 0x1, 0x3); + TestAtomic(this, 0xF7382F, 0x1, 0x2, 0x1, 0x3); +} + +TEST_F(RISCVEmulatorTester, TestAMOXOR) { + TestAtomic(this, 0x20F7282F, 0x1, 0x2, 0x1, 0x3); + TestAtomic(this, 0x20F7382F, 0x1, 0x2, 0x1, 0x3); +} + +TEST_F(RISCVEmulatorTester, TestAMOAND) { + TestAtomic(this, 0x60F7282F, 0x1, 0x2, 0x1, 0x0); + TestAtomic(this, 0x60F7382F, 0x1, 0x2, 0x1, 0x0); +} + +TEST_F(RISCVEmulatorTester, TestAMOOR) { + TestAtomic(this, 0x40F7282F, 0x1, 0x2, 0x1, 0x3); + TestAtomic(this, 0x40F7382F, 0x1, 0x2, 0x1, 0x3); +} + +TEST_F(RISCVEmulatorTester, TestAMOMIN) { + TestAtomic(this, 0x80F7282F, 0x1, 0x2, 0x1, 0x1); + TestAtomic(this, 0x80F7382F, 0x1, 0x2, 0x1, 0x1); +} + +TEST_F(RISCVEmulatorTester, TestAMOMAX) { + TestAtomic(this, 0xA0F7282F, 0x1, 0x2, 0x1, 0x2); + TestAtomic(this, 0xA0F7382F, 0x1, 0x2, 0x1, 0x2); +} + +TEST_F(RISCVEmulatorTester, TestAMOMINU) { + TestAtomic(this, 0xC0F7282F, 0x1, 0x2, 0x1, 0x1); + TestAtomic(this, 0xC0F7382F, 0x1, 0x2, 0x1, 0x1); +} + +TEST_F(RISCVEmulatorTester, TestAMOMAXU) { + TestAtomic(this, 0xE0F7282F, 0x1, 0x2, 0x1, 0x2); + TestAtomic(this, 0xE0F7382F, 0x1, 0x2, 0x1, 0x2); +}