diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h --- a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h +++ b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h @@ -9,6 +9,8 @@ #ifndef LLDB_SOURCE_PLUGINS_INSTRUCTION_RISCV_EMULATEINSTRUCTIONRISCV_H #define LLDB_SOURCE_PLUGINS_INSTRUCTION_RISCV_EMULATEINSTRUCTIONRISCV_H +#include "RISCVInstructions.h" + #include "lldb/Core/EmulateInstruction.h" #include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Log.h" @@ -17,22 +19,6 @@ namespace lldb_private { -constexpr uint32_t DecodeRD(uint32_t inst) { return (inst & 0xF80) >> 7; } -constexpr uint32_t DecodeRS1(uint32_t inst) { return (inst & 0xF8000) >> 15; } -constexpr uint32_t DecodeRS2(uint32_t inst) { return (inst & 0x1F00000) >> 20; } - -class EmulateInstructionRISCV; - -struct InstrPattern { - const char *name; - /// Bit mask to check the type of a instruction (B-Type, I-Type, J-Type, etc.) - uint32_t type_mask; - /// Characteristic value after bitwise-and with type_mask. - uint32_t eigen; - bool (*exec)(EmulateInstructionRISCV &emulator, uint32_t inst, - bool ignore_cond); -}; - class EmulateInstructionRISCV : public EmulateInstruction { public: static llvm::StringRef GetPluginNameStatic() { return "riscv"; } @@ -79,31 +65,36 @@ llvm::Optional GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num) override; - lldb::addr_t ReadPC(bool &success); + llvm::Optional ReadPC(); bool WritePC(lldb::addr_t pc); - const InstrPattern *Decode(uint32_t inst); - bool DecodeAndExecute(uint32_t inst, bool ignore_cond); + llvm::Optional ReadInstructionAt(lldb::addr_t addr); + llvm::Optional Decode(uint32_t inst); + bool Execute(DecodeResult inst, bool ignore_cond); template - static std::enable_if_t, T> - ReadMem(EmulateInstructionRISCV &emulator, uint64_t addr, bool *success) { - + std::enable_if_t, llvm::Optional> + ReadMem(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 = ReadMemoryUnsigned(ctx, addr, sizeof(T), T(), &success); + if (!success) + return {}; // aka return false + return result; } - template - static bool WriteMem(EmulateInstructionRISCV &emulator, uint64_t addr, - RegisterValue value) { + template bool WriteMem(uint64_t addr, uint64_t value) { EmulateInstructionRISCV::Context ctx; ctx.type = EmulateInstruction::eContextRegisterStore; ctx.SetNoArgs(); - return emulator.WriteMemoryUnsigned(ctx, addr, value.GetAsUInt64(), - sizeof(T)); + return WriteMemoryUnsigned(ctx, addr, value, sizeof(T)); } + +private: + /// Last decoded instruction from m_opcode + DecodeResult m_decoded; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp --- a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp +++ b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp @@ -11,6 +11,7 @@ #include "EmulateInstructionRISCV.h" #include "Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h" #include "Plugins/Process/Utility/lldb-riscv-register-enums.h" +#include "RISCVInstructions.h" #include "lldb/Core/Address.h" #include "lldb/Core/PluginManager.h" @@ -31,6 +32,17 @@ namespace lldb_private { +/// Returns all values wrapped in Optional, or None if any of the values is +/// None. +template +static llvm::Optional> zipOpt(llvm::Optional &&...ts) { + if ((ts.has_value() && ...)) + return llvm::Optional>( + std::make_tuple(std::move(*ts)...)); + else + return llvm::None; +} + // The funct3 is the type of compare in B instructions. // funct3 means "3-bits function selector", which RISC-V ISA uses as minor // opcode. It reuses the major opcode encoding space. @@ -41,12 +53,6 @@ constexpr uint32_t BLTU = 0b110; constexpr uint32_t BGEU = 0b111; -constexpr uint32_t DecodeSHAMT5(uint32_t inst) { return DecodeRS2(inst); } -constexpr uint32_t DecodeSHAMT7(uint32_t inst) { - return (inst & 0x7F00000) >> 20; -} -constexpr uint32_t DecodeFunct3(uint32_t inst) { return (inst & 0x7000) >> 12; } - // used in decoder constexpr int32_t SignExt(uint32_t imm) { return int32_t(imm); } @@ -96,45 +102,38 @@ return LLDB_INVALID_REGNUM; } -static bool ReadRegister(EmulateInstructionRISCV &emulator, uint32_t reg_encode, - RegisterValue &value) { - uint32_t lldb_reg = GPREncodingToLLDB(reg_encode); - return emulator.ReadRegister(eRegisterKindLLDB, lldb_reg, value); -} - -static bool WriteRegister(EmulateInstructionRISCV &emulator, - uint32_t reg_encode, const RegisterValue &value) { - uint32_t lldb_reg = GPREncodingToLLDB(reg_encode); +bool Rd::Write(EmulateInstructionRISCV &emulator, uint64_t value) { + uint32_t lldb_reg = GPREncodingToLLDB(rd); EmulateInstruction::Context ctx; ctx.type = EmulateInstruction::eContextRegisterStore; ctx.SetNoArgs(); - return emulator.WriteRegister(ctx, eRegisterKindLLDB, lldb_reg, value); + RegisterValue registerValue; + registerValue.SetUInt64(value); + return emulator.WriteRegister(ctx, eRegisterKindLLDB, lldb_reg, + registerValue); } -static bool ExecJAL(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - bool success = false; - int64_t offset = SignExt(DecodeJImm(inst)); - int64_t pc = emulator.ReadPC(success); - return success && emulator.WritePC(pc + offset) && - WriteRegister(emulator, DecodeRD(inst), - RegisterValue(uint64_t(pc + 4))); +llvm::Optional Rs::Read(EmulateInstructionRISCV &emulator) { + uint32_t lldbReg = GPREncodingToLLDB(rs); + RegisterValue value; + return emulator.ReadRegister(eRegisterKindLLDB, lldbReg, value) + ? llvm::Optional(value.GetAsUInt64()) + : llvm::None; } -static bool ExecJALR(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - int64_t offset = SignExt(DecodeIImm(inst)); - RegisterValue value; - if (!ReadRegister(emulator, DecodeRS1(inst), value)) - return false; - bool success = false; - 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 - // address, to simplify hardware and to allow auxiliary information to be - // stored in function pointers." - return emulator.WritePC((rs1 + offset) & ~1) && - WriteRegister(emulator, DecodeRD(inst), - RegisterValue(uint64_t(pc + 4))); +llvm::Optional Rs::ReadI32(EmulateInstructionRISCV &emulator) { + return Read(emulator).transform( + [](uint64_t value) { return int32_t(uint32_t(value)); }); +} + +llvm::Optional Rs::ReadI64(EmulateInstructionRISCV &emulator) { + return Read(emulator).transform( + [](uint64_t value) { return int64_t(value); }); +} + +llvm::Optional Rs::ReadU32(EmulateInstructionRISCV &emulator) { + return Read(emulator).transform( + [](uint64_t value) { return uint32_t(value); }); } static bool CompareB(uint64_t rs1, uint64_t rs2, uint32_t funct3) { @@ -156,1126 +155,926 @@ } } -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); - if (!success) - return false; - - uint64_t offset = SignExt(DecodeBImm(inst)); - uint64_t target = pc + offset; - if (ignore_cond) - return emulator.WritePC(target); - - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint32_t funct3 = DecodeFunct3(inst); - if (CompareB(rs.rs1().trunc(), rs.rs2().trunc(), funct3)) - return emulator.WritePC(target); - - return true; -} - -static bool ExecLUI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - uint32_t imm = DecodeUImm(inst); - RegisterValue value; - value.SetUInt64(SignExt(imm)); - return WriteRegister(emulator, DecodeRD(inst), value); -} - -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)); - return success && WriteRegister(emulator, DecodeRD(inst), value); -} - template -static std::enable_if_t, llvm::Optional> -ReadMem(EmulateInstructionRISCV &emulator, uint64_t addr) { - EmulateInstructionRISCV::Context ctx; - ctx.type = EmulateInstruction::eContextRegisterLoad; - ctx.SetNoArgs(); - bool success = false; - T result = emulator.ReadMemoryUnsigned(ctx, addr, sizeof(T), T(), &success); - if (!success) - return {}; // aka return false - return result; -} +constexpr bool is_load = + std::is_same_v || std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || std::is_same_v || + std::is_same_v; 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)); -} - -static uint64_t LoadStoreAddr(EmulateInstructionRISCV &emulator, - uint32_t inst) { - int32_t imm = SignExt(DecodeSImm(inst)); - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return LLDB_INVALID_ADDRESS; - uint64_t addr = rs.rs1().trunc() + uint64_t(imm); - return addr; -} - -// Read T from memory, then load its sign-extended value E to register. -template -static bool Load(EmulateInstructionRISCV &emulator, uint32_t inst, - uint64_t (*extend)(E)) { - uint64_t addr = LoadStoreAddr(emulator, inst); - if (addr == LLDB_INVALID_ADDRESS) - return false; - E value = E(ReadMem(emulator, addr).value()); - if (!value) - return false; - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value))); -} +constexpr bool is_store = std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v; template -static bool Store(EmulateInstructionRISCV &emulator, uint32_t inst) { - uint64_t addr = LoadStoreAddr(emulator, inst); - if (addr == LLDB_INVALID_ADDRESS) - return false; - RegisterValue value; - if (!ReadRegister(emulator, DecodeRS2(inst), value)) - return false; - 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); -} - -static bool ExecLH(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Load(emulator, inst, SextW); -} - -static bool ExecLW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Load(emulator, inst, SextW); -} - -static bool ExecLD(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Load(emulator, inst, ZextD); -} - -static bool ExecLBU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Load(emulator, inst, ZextD); -} - -static bool ExecLHU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Load(emulator, inst, ZextD); -} - -static bool ExecLWU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Load(emulator, inst, ZextD); -} - -static bool ExecSB(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Store(emulator, inst); -} - -static bool ExecSH(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Store(emulator, inst); -} - -static bool ExecSW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Store(emulator, inst); -} - -static bool ExecSD(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Store(emulator, inst); -} - -static bool ExecADDI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - int32_t imm = SignExt(DecodeIImm(inst)); - - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - 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) { - int32_t imm = SignExt(DecodeIImm(inst)); - - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - 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) { - int32_t imm = SignExt(DecodeIImm(inst)); - - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - 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) { - int32_t imm = SignExt(DecodeIImm(inst)); - - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - 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) { - int32_t imm = SignExt(DecodeIImm(inst)); - - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; +constexpr bool is_amo_add = + std::is_same_v || std::is_same_v; - 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) { - int32_t imm = SignExt(DecodeIImm(inst)); - - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = - rs.rs1().sext() >> (rs.rs2().trunc() & 0b111111); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecOR(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - 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) { - int32_t imm = SignExt(DecodeIImm(inst)); - - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - 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 rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - 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 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)); -} - -// 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 = 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; +template +constexpr bool is_amo_bit_op = + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v; - // 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)); -} +template +constexpr bool is_amo_swap = + std::is_same_v || std::is_same_v; -static bool ExecDIV(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) +template +constexpr bool is_amo_cmp = + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v; + +template +static std::enable_if_t || is_store, llvm::Optional> +LoadStoreAddr(EmulateInstructionRISCV &emulator, I inst) { + return inst.rs1.Read(emulator).transform( + [&](uint64_t rs1) { return rs1 + uint64_t(SignExt(inst.imm)); }); +} + +// Read T from memory, then load its sign-extended value m_emu to register. +template +static std::enable_if_t, bool> +Load(EmulateInstructionRISCV &emulator, I inst, uint64_t (*extend)(m_emu)) { + auto addr = LoadStoreAddr(emulator, inst); + if (!addr) + return false; + return emulator.ReadMem(*addr) + .transform([&](T t) { return inst.rd.Write(emulator, extend(m_emu(t))); }) + .value_or(false); +} + +template +static std::enable_if_t, bool> +Store(EmulateInstructionRISCV &emulator, I inst) { + auto addr = LoadStoreAddr(emulator, inst); + if (!addr) + return false; + return inst.rs2.Read(emulator) + .transform([&](uint64_t rs2) { return emulator.WriteMem(*addr, rs2); }) + .value_or(false); +} + +template +static std::enable_if_t || is_amo_bit_op || is_amo_swap || + is_amo_cmp, + llvm::Optional> +AtomicAddr(EmulateInstructionRISCV &emulator, I inst, unsigned int align) { + return inst.rs1.Read(emulator) + .transform([&](uint64_t rs1) { + return rs1 % align == 0 ? llvm::Optional(rs1) : llvm::None; + }) + .value_or(llvm::None); +} + +template +static std::enable_if_t, bool> +AtomicSwap(EmulateInstructionRISCV &emulator, I inst, int align, + uint64_t (*extend)(T)) { + auto addr = AtomicAddr(emulator, inst, align); + if (!addr) + return false; + return zipOpt(emulator.ReadMem(*addr), inst.rs2.Read(emulator)) + .transform([&](auto &&tup) { + auto [tmp, rs2] = tup; + return emulator.WriteMem(*addr, T(rs2)) && + inst.rd.Write(emulator, extend(tmp)); + }) + .value_or(false); +} + +template +static std::enable_if_t, bool> +AtomicADD(EmulateInstructionRISCV &emulator, I inst, int align, + uint64_t (*extend)(T)) { + auto addr = AtomicAddr(emulator, inst, align); + if (!addr) + return false; + return zipOpt(emulator.ReadMem(*addr), inst.rs2.Read(emulator)) + .transform([&](auto &&tup) { + auto [tmp, rs2] = tup; + return emulator.WriteMem(*addr, T(tmp + rs2)) && + inst.rd.Write(emulator, extend(tmp)); + }) + .value_or(false); +} + +template +static std::enable_if_t, bool> +AtomicBitOperate(EmulateInstructionRISCV &emulator, I inst, int align, + uint64_t (*extend)(T), T (*operate)(T, T)) { + auto addr = AtomicAddr(emulator, inst, align); + if (!addr) + return false; + return zipOpt(emulator.ReadMem(*addr), inst.rs2.Read(emulator)) + .transform([&](auto &&tup) { + auto [value, rs2] = tup; + return emulator.WriteMem(*addr, operate(value, T(rs2))) && + inst.rd.Write(emulator, extend(value)); + }) + .value_or(false); +} + +template +static std::enable_if_t, bool> +AtomicCmp(EmulateInstructionRISCV &emulator, I inst, int align, + uint64_t (*extend)(T), T (*cmp)(T, T)) { + auto addr = AtomicAddr(emulator, inst, align); + if (!addr) + return false; + return zipOpt(emulator.ReadMem(*addr), inst.rs2.Read(emulator)) + .transform([&](auto &&tup) { + auto [value, rs2] = tup; + return emulator.WriteMem(*addr, cmp(value, T(rs2))) && + inst.rd.Write(emulator, extend(value)); + }) + .value_or(false); +} + +bool AtomicSequence(EmulateInstructionRISCV &emulator) { + // 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: ........ + const auto pc = emulator.ReadPC(); + if (!pc) return false; + auto current_pc = pc.value(); + const auto entry_pc = current_pc; - 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) + // The first instruction should be LR.W or LR.D + auto inst = emulator.ReadInstructionAt(current_pc); + if (!inst || (!std::holds_alternative(inst->decoded) && + !std::holds_alternative(inst->decoded))) 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) + // The second instruction should be BNE to exit address + inst = emulator.ReadInstructionAt(current_pc += 4); + if (!inst || !std::holds_alternative(inst->decoded)) 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) + auto bne_exit = std::get(inst->decoded); + if (bne_exit.funct3 != BNE) return false; + // save the exit address to check later + const auto exit_pc = current_pc + SextW(bne_exit.imm); - 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) + // The third instruction should be SC.W or SC.D + inst = emulator.ReadInstructionAt(current_pc += 4); + if (!inst || (!std::holds_alternative(inst->decoded) && + !std::holds_alternative(inst->decoded))) 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) + // The fourth instruction should be BNE to entry address + inst = emulator.ReadInstructionAt(current_pc += 4); + if (!inst || !std::holds_alternative(inst->decoded)) 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) + auto bne_start = std::get(inst->decoded); + if (bne_start.funct3 != BNE) 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) + if (entry_pc != current_pc + SextW(bne_start.imm)) 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))); + current_pc += 4; + // check the exit address and jump to it + return exit_pc == current_pc && emulator.WritePC(current_pc); } -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))); +template static RISCVInst DecodeUType(uint32_t inst) { + return T{Rd{DecodeRD(inst)}, DecodeUImm(inst)}; } -// 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 RISCVInst DecodeJType(uint32_t inst) { + return T{Rd{DecodeRD(inst)}, DecodeJImm(inst)}; } -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 RISCVInst DecodeIType(uint32_t inst) { + return T{Rd{DecodeRD(inst)}, Rs{DecodeRS1(inst)}, DecodeIImm(inst)}; } -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 RISCVInst DecodeBType(uint32_t inst) { + return T{Rs{DecodeRS1(inst)}, Rs{DecodeRS2(inst)}, DecodeBImm(inst), + (inst & 0x7000) >> 12}; } -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 RISCVInst DecodeSType(uint32_t inst) { + return T{Rs{DecodeRS1(inst)}, Rs{DecodeRS2(inst)}, DecodeSImm(inst)}; } -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))); +template static RISCVInst DecodeRType(uint32_t inst) { + return T{Rd{DecodeRD(inst)}, Rs{DecodeRS1(inst)}, Rs{DecodeRS2(inst)}}; } -// 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; +template static RISCVInst DecodeRShamtType(uint32_t inst) { + return T{Rd{DecodeRD(inst)}, Rs{DecodeRS1(inst)}, DecodeRS2(inst)}; } -// 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; +template static RISCVInst DecodeRRS1Type(uint32_t inst) { + return T{Rd{DecodeRD(inst)}, Rs{DecodeRS1(inst)}}; } -// 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(); +static const InstrPattern PATTERNS[] = { + // RV32I & RV64I (The base integer ISA) // + {"LUI", 0x7F, 0x37, DecodeUType}, + {"AUIPC", 0x7F, 0x17, DecodeUType}, + {"JAL", 0x7F, 0x6F, DecodeJType}, + {"JALR", 0x707F, 0x67, DecodeIType}, + {"B", 0x7F, 0x63, DecodeBType}, + {"LB", 0x707F, 0x3, DecodeIType}, + {"LH", 0x707F, 0x1003, DecodeIType}, + {"LW", 0x707F, 0x2003, DecodeIType}, + {"LBU", 0x707F, 0x4003, DecodeIType}, + {"LHU", 0x707F, 0x5003, DecodeIType}, + {"SB", 0x707F, 0x23, DecodeSType}, + {"SH", 0x707F, 0x1023, DecodeSType}, + {"SW", 0x707F, 0x2023, DecodeSType}, + {"ADDI", 0x707F, 0x13, DecodeIType}, + {"SLTI", 0x707F, 0x2013, DecodeIType}, + {"SLTIU", 0x707F, 0x3013, DecodeIType}, + {"XORI", 0x707F, 0x4013, DecodeIType}, + {"ORI", 0x707F, 0x6013, DecodeIType}, + {"ANDI", 0x707F, 0x7013, DecodeIType}, + {"SLLI", 0xF800707F, 0x1013, DecodeRShamtType}, + {"SRLI", 0xF800707F, 0x5013, DecodeRShamtType}, + {"SRAI", 0xF800707F, 0x40005013, DecodeRShamtType}, + {"ADD", 0xFE00707F, 0x33, DecodeRType}, + {"SUB", 0xFE00707F, 0x40000033, DecodeRType}, + {"SLL", 0xFE00707F, 0x1033, DecodeRType}, + {"SLT", 0xFE00707F, 0x2033, DecodeRType}, + {"SLTU", 0xFE00707F, 0x3033, DecodeRType}, + {"XOR", 0xFE00707F, 0x4033, DecodeRType}, + {"SRL", 0xFE00707F, 0x5033, DecodeRType}, + {"SRA", 0xFE00707F, 0x40005033, DecodeRType}, + {"OR", 0xFE00707F, 0x6033, DecodeRType}, + {"AND", 0xFE00707F, 0x7033, DecodeRType}, + {"LWU", 0x707F, 0x6003, DecodeIType}, + {"LD", 0x707F, 0x3003, DecodeIType}, + {"SD", 0x707F, 0x3023, DecodeSType}, + {"ADDIW", 0x707F, 0x1B, DecodeIType}, + {"SLLIW", 0xFE00707F, 0x101B, DecodeRShamtType}, + {"SRLIW", 0xFE00707F, 0x501B, DecodeRShamtType}, + {"SRAIW", 0xFE00707F, 0x4000501B, DecodeRShamtType}, + {"ADDW", 0xFE00707F, 0x3B, DecodeRType}, + {"SUBW", 0xFE00707F, 0x4000003B, DecodeRType}, + {"SLLW", 0xFE00707F, 0x103B, DecodeRType}, + {"SRLW", 0xFE00707F, 0x503B, DecodeRType}, + {"SRAW", 0xFE00707F, 0x4000503B, DecodeRType}, - // 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(); + // RV32M & RV64M (The integer multiplication and division extension) // + {"MUL", 0xFE00707F, 0x2000033, DecodeRType}, + {"MULH", 0xFE00707F, 0x2001033, DecodeRType}, + {"MULHSU", 0xFE00707F, 0x2002033, DecodeRType}, + {"MULHU", 0xFE00707F, 0x2003033, DecodeRType}, + {"DIV", 0xFE00707F, 0x2004033, DecodeRType
}, + {"DIVU", 0xFE00707F, 0x2005033, DecodeRType}, + {"REM", 0xFE00707F, 0x2006033, DecodeRType}, + {"REMU", 0xFE00707F, 0x2007033, DecodeRType}, + {"MULW", 0xFE00707F, 0x200003B, DecodeRType}, + {"DIVW", 0xFE00707F, 0x200403B, DecodeRType}, + {"DIVUW", 0xFE00707F, 0x200503B, DecodeRType}, + {"REMW", 0xFE00707F, 0x200603B, DecodeRType}, + {"REMUW", 0xFE00707F, 0x200703B, DecodeRType}, - // inst must be SC - if (!value || !IsSC(value.value())) - return false; - value = ReadMem(emulator, current_pc += 4).value(); + // RV32A & RV64A (The standard atomic instruction extension) // + {"LR_W", 0xF9F0707F, 0x1000202F, DecodeRRS1Type}, + {"LR_D", 0xF9F0707F, 0x1000302F, DecodeRRS1Type}, + {"SC_W", 0xF800707F, 0x1800202F, DecodeRType}, + {"SC_D", 0xF800707F, 0x1800302F, DecodeRType}, + {"AMOSWAP_W", 0xF800707F, 0x800202F, DecodeRType}, + {"AMOADD_W", 0xF800707F, 0x202F, DecodeRType}, + {"AMOXOR_W", 0xF800707F, 0x2000202F, DecodeRType}, + {"AMOAND_W", 0xF800707F, 0x6000202F, DecodeRType}, + {"AMOOR_W", 0xF800707F, 0x4000202F, DecodeRType}, + {"AMOMIN_W", 0xF800707F, 0x8000202F, DecodeRType}, + {"AMOMAX_W", 0xF800707F, 0xA000202F, DecodeRType}, + {"AMOMINU_W", 0xF800707F, 0xC000202F, DecodeRType}, + {"AMOMAXU_W", 0xF800707F, 0xE000202F, DecodeRType}, + {"AMOSWAP_D", 0xF800707F, 0x800302F, DecodeRType}, + {"AMOADD_D", 0xF800707F, 0x302F, DecodeRType}, + {"AMOXOR_D", 0xF800707F, 0x2000302F, DecodeRType}, + {"AMOAND_D", 0xF800707F, 0x6000302F, DecodeRType}, + {"AMOOR_D", 0xF800707F, 0x4000302F, DecodeRType}, + {"AMOMIN_D", 0xF800707F, 0x8000302F, DecodeRType}, + {"AMOMAX_D", 0xF800707F, 0xA000302F, DecodeRType}, + {"AMOMINU_D", 0xF800707F, 0xC000302F, DecodeRType}, + {"AMOMAXU_D", 0xF800707F, 0xE000302F, DecodeRType}, +}; - // 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; +llvm::Optional EmulateInstructionRISCV::Decode(uint32_t inst) { + Log *log = GetLog(LLDBLog::Unwind); - if (exit_pc != current_pc) - return false; + uint16_t try_rvc = uint16_t(inst & 0x0000ffff); + // check whether the compressed encode could be valid + uint16_t mask = try_rvc & 0b11; + bool is_rvc = try_rvc != 0 && mask != 3; - return emulator.WritePC(current_pc); + for (const InstrPattern &pat : PATTERNS) { + if ((inst & pat.type_mask) == pat.eigen) { + LLDB_LOGF(log, "EmulateInstructionRISCV::%s: inst(%x) was decoded to %s", + __FUNCTION__, inst, pat.name); + auto decoded = pat.decode(inst); + return DecodeResult{decoded, inst, is_rvc, pat}; + } + } + LLDB_LOGF(log, "EmulateInstructionRISCV::%s: inst(0x%x) was unsupported", + __FUNCTION__, inst); + return llvm::None; } -static bool ExecLR_W(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return ExecAtomicSequence(emulator, inst, false); -} +class Executor { + EmulateInstructionRISCV &m_emu; + bool m_ignore_cond; + bool m_is_rvc; -static bool ExecAMOSWAP_W(EmulateInstructionRISCV &emulator, uint32_t inst, - bool) { - return AtomicSwap(emulator, inst, 4, SextW); -} +public: + // also used in EvaluateInstruction() + static uint64_t size(bool is_rvc) { return is_rvc ? 2 : 4; } -static bool ExecAMOADD_W(EmulateInstructionRISCV &emulator, uint32_t inst, - bool) { - return AtomicADD(emulator, inst, 4, SextW); -} +private: + uint64_t delta() { return size(m_is_rvc); } -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; }); -} +public: + Executor(EmulateInstructionRISCV &emulator, bool ignoreCond, bool is_rvc) + : m_emu(emulator), m_ignore_cond(ignoreCond), m_is_rvc(is_rvc) {} + + bool operator()(LUI inst) { return inst.rd.Write(m_emu, SignExt(inst.imm)); } + bool operator()(AUIPC inst) { + return m_emu.ReadPC() + .transform([&](uint64_t pc) { + return inst.rd.Write(m_emu, SignExt(inst.imm) + pc); + }) + .value_or(false); + } + bool operator()(JAL inst) { + return m_emu.ReadPC() + .transform([&](uint64_t pc) { + return inst.rd.Write(m_emu, pc + delta()) && + m_emu.WritePC(SignExt(inst.imm) + pc); + }) + .value_or(false); + } + bool operator()(JALR inst) { + return zipOpt(m_emu.ReadPC(), inst.rs1.Read(m_emu)) + .transform([&](auto &&tup) { + auto [pc, rs1] = tup; + return inst.rd.Write(m_emu, pc + delta()) && + m_emu.WritePC((SignExt(inst.imm) + rs1) & ~1); + }) + .value_or(false); + } + bool operator()(B inst) { + return zipOpt(m_emu.ReadPC(), inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [pc, rs1, rs2] = tup; + if (m_ignore_cond || CompareB(rs1, rs2, inst.funct3)) + return m_emu.WritePC(SignExt(inst.imm) + pc); + return true; + }) + .value_or(false); + } + bool operator()(LB inst) { + return Load(m_emu, inst, SextW); + } + bool operator()(LH inst) { + return Load(m_emu, inst, SextW); + } + bool operator()(LW inst) { + return Load(m_emu, inst, SextW); + } + bool operator()(LBU inst) { + return Load(m_emu, inst, ZextD); + } + bool operator()(LHU inst) { + return Load(m_emu, inst, ZextD); + } + bool operator()(SB inst) { return Store(m_emu, inst); } + bool operator()(SH inst) { return Store(m_emu, inst); } + bool operator()(SW inst) { return Store(m_emu, inst); } + bool operator()(ADDI inst) { + return inst.rs1.ReadI64(m_emu) + .transform([&](int64_t rs1) { + return inst.rd.Write(m_emu, rs1 + int64_t(SignExt(inst.imm))); + }) + .value_or(false); + } + bool operator()(SLTI inst) { + return inst.rs1.ReadI64(m_emu) + .transform([&](int64_t rs1) { + return inst.rd.Write(m_emu, rs1 < int64_t(SignExt(inst.imm))); + }) + .value_or(false); + } + bool operator()(SLTIU inst) { + return inst.rs1.Read(m_emu) + .transform([&](uint64_t rs1) { + return inst.rd.Write(m_emu, rs1 < uint64_t(SignExt(inst.imm))); + }) + .value_or(false); + } + bool operator()(XORI inst) { + return inst.rs1.Read(m_emu) + .transform([&](uint64_t rs1) { + return inst.rd.Write(m_emu, rs1 ^ uint64_t(SignExt(inst.imm))); + }) + .value_or(false); + } + bool operator()(ORI inst) { + return inst.rs1.Read(m_emu) + .transform([&](uint64_t rs1) { + return inst.rd.Write(m_emu, rs1 | uint64_t(SignExt(inst.imm))); + }) + .value_or(false); + } + bool operator()(ANDI inst) { + return inst.rs1.Read(m_emu) + .transform([&](uint64_t rs1) { + return inst.rd.Write(m_emu, rs1 & uint64_t(SignExt(inst.imm))); + }) + .value_or(false); + } + bool operator()(ADD inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, rs1 + rs2); + }) + .value_or(false); + } + bool operator()(SUB inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, rs1 - rs2); + }) + .value_or(false); + } + bool operator()(SLL inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, rs1 << (rs2 & 0b111111)); + }) + .value_or(false); + } + bool operator()(SLT inst) { + return zipOpt(inst.rs1.ReadI64(m_emu), inst.rs2.ReadI64(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, rs1 < rs2); + }) + .value_or(false); + } + bool operator()(SLTU inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, rs1 < rs2); + }) + .value_or(false); + } + bool operator()(XOR inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, rs1 ^ rs2); + }) + .value_or(false); + } + bool operator()(SRL inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, rs1 >> (rs2 & 0b111111)); + }) + .value_or(false); + } + bool operator()(SRA inst) { + return zipOpt(inst.rs1.ReadI64(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, rs1 >> (rs2 & 0b111111)); + }) + .value_or(false); + } + bool operator()(OR inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, rs1 | rs2); + }) + .value_or(false); + } + bool operator()(AND inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, rs1 & rs2); + }) + .value_or(false); + } + bool operator()(LWU inst) { + return Load(m_emu, inst, ZextD); + } + bool operator()(LD inst) { + return Load(m_emu, inst, ZextD); + } + bool operator()(SD inst) { return Store(m_emu, inst); } + bool operator()(SLLI inst) { + return inst.rs1.Read(m_emu) + .transform([&](uint64_t rs1) { + return inst.rd.Write(m_emu, rs1 << inst.shamt); + }) + .value_or(false); + } + bool operator()(SRLI inst) { + return inst.rs1.Read(m_emu) + .transform([&](uint64_t rs1) { + return inst.rd.Write(m_emu, rs1 >> inst.shamt); + }) + .value_or(false); + } + bool operator()(SRAI inst) { + return inst.rs1.ReadI64(m_emu) + .transform([&](int64_t rs1) { + return inst.rd.Write(m_emu, rs1 >> inst.shamt); + }) + .value_or(false); + } + bool operator()(ADDIW inst) { + return inst.rs1.ReadI32(m_emu) + .transform([&](int32_t rs1) { + return inst.rd.Write(m_emu, SextW(rs1 + SignExt(inst.imm))); + }) + .value_or(false); + } + bool operator()(SLLIW inst) { + return inst.rs1.ReadU32(m_emu) + .transform([&](uint32_t rs1) { + return inst.rd.Write(m_emu, SextW(rs1 << inst.shamt)); + }) + .value_or(false); + } + bool operator()(SRLIW inst) { + return inst.rs1.ReadU32(m_emu) + .transform([&](uint32_t rs1) { + return inst.rd.Write(m_emu, SextW(rs1 >> inst.shamt)); + }) + .value_or(false); + } + bool operator()(SRAIW inst) { + return inst.rs1.ReadI32(m_emu) + .transform([&](int32_t rs1) { + return inst.rd.Write(m_emu, SextW(rs1 >> inst.shamt)); + }) + .value_or(false); + } + bool operator()(ADDW inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, SextW(uint32_t(rs1 + rs2))); + }) + .value_or(false); + } + bool operator()(SUBW inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, SextW(uint32_t(rs1 - rs2))); + }) + .value_or(false); + } + bool operator()(SLLW inst) { + return zipOpt(inst.rs1.ReadU32(m_emu), inst.rs2.ReadU32(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, SextW(rs1 << (rs2 & 0b11111))); + }) + .value_or(false); + } + bool operator()(SRLW inst) { + return zipOpt(inst.rs1.ReadU32(m_emu), inst.rs2.ReadU32(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, SextW(rs1 >> (rs2 & 0b11111))); + }) + .value_or(false); + } + bool operator()(SRAW inst) { + return zipOpt(inst.rs1.ReadI32(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, SextW(rs1 >> (rs2 & 0b11111))); + }) + .value_or(false); + } + // RV32M & RV64M (Integer Multiplication and Division Extension) // + bool operator()(MUL inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, rs1 * rs2); + }) + .value_or(false); + } + bool operator()(MULH inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + // signed * signed + auto mul = llvm::APInt(128, rs1, true) * llvm::APInt(128, rs2, true); + return inst.rd.Write(m_emu, mul.ashr(64).trunc(64).getZExtValue()); + }) + .value_or(false); + } + bool operator()(MULHSU inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + // signed * unsigned + auto mul = llvm::APInt(128, rs1, true).zext(128) * + llvm::APInt(128, rs2, false); + return inst.rd.Write(m_emu, mul.lshr(64).trunc(64).getZExtValue()); + }) + .value_or(false); + } + bool operator()(MULHU inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + // unsigned * unsigned + auto mul = + llvm::APInt(128, rs1, false) * llvm::APInt(128, rs2, false); + return inst.rd.Write(m_emu, mul.lshr(64).trunc(64).getZExtValue()); + }) + .value_or(false); + } + bool operator()(DIV inst) { + return zipOpt(inst.rs1.ReadI64(m_emu), inst.rs2.ReadI64(m_emu)) + .transform([&](auto &&tup) { + auto [dividend, divisor] = tup; -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; }); -} + if (divisor == 0) + return inst.rd.Write(m_emu, UINT64_MAX); -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; }); -} + if (dividend == INT64_MIN && divisor == -1) + return inst.rd.Write(m_emu, dividend); -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))); - }); -} + return inst.rd.Write(m_emu, dividend / divisor); + }) + .value_or(false); + } + bool operator()(DIVU inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [dividend, divisor] = tup; -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))); - }); -} + if (divisor == 0) + return inst.rd.Write(m_emu, UINT64_MAX); -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); }); -} + return inst.rd.Write(m_emu, dividend / divisor); + }) + .value_or(false); + } + bool operator()(REM inst) { + return zipOpt(inst.rs1.ReadI64(m_emu), inst.rs2.ReadI64(m_emu)) + .transform([&](auto &&tup) { + auto [dividend, divisor] = tup; -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); }); -} + if (divisor == 0) + return inst.rd.Write(m_emu, dividend); -static bool ExecLR_D(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return ExecAtomicSequence(emulator, inst, false); -} + if (dividend == INT64_MIN && divisor == -1) + return inst.rd.Write(m_emu, 0); -static bool ExecAMOSWAP_D(EmulateInstructionRISCV &emulator, uint32_t inst, - bool) { - return AtomicSwap(emulator, inst, 8, ZextD); -} + return inst.rd.Write(m_emu, dividend % divisor); + }) + .value_or(false); + } + bool operator()(REMU inst) { + return zipOpt(inst.rs1.Read(m_emu), inst.rs2.Read(m_emu)) + .transform([&](auto &&tup) { + auto [dividend, divisor] = tup; -static bool ExecAMOADD_D(EmulateInstructionRISCV &emulator, uint32_t inst, - bool) { - return AtomicADD(emulator, inst, 8, ZextD); -} + if (divisor == 0) + return inst.rd.Write(m_emu, dividend); -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; }); -} + return inst.rd.Write(m_emu, dividend % divisor); + }) + .value_or(false); + } + bool operator()(MULW inst) { + return zipOpt(inst.rs1.ReadI32(m_emu), inst.rs2.ReadI32(m_emu)) + .transform([&](auto &&tup) { + auto [rs1, rs2] = tup; + return inst.rd.Write(m_emu, SextW(rs1 * rs2)); + }) + .value_or(false); + } + bool operator()(DIVW inst) { + return zipOpt(inst.rs1.ReadI32(m_emu), inst.rs2.ReadI32(m_emu)) + .transform([&](auto &&tup) { + auto [dividend, divisor] = tup; -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; }); -} + if (divisor == 0) + return inst.rd.Write(m_emu, UINT64_MAX); -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; }); -} + if (dividend == INT32_MIN && divisor == -1) + return inst.rd.Write(m_emu, SextW(dividend)); -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))); - }); -} + return inst.rd.Write(m_emu, SextW(dividend / divisor)); + }) + .value_or(false); + } + bool operator()(DIVUW inst) { + return zipOpt(inst.rs1.ReadU32(m_emu), inst.rs2.ReadU32(m_emu)) + .transform([&](auto &&tup) { + auto [dividend, divisor] = tup; -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))); - }); -} + if (divisor == 0) + return inst.rd.Write(m_emu, UINT64_MAX); -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); }); -} + return inst.rd.Write(m_emu, SextW(dividend / divisor)); + }) + .value_or(false); + } + bool operator()(REMW inst) { + return zipOpt(inst.rs1.ReadI32(m_emu), inst.rs2.ReadI32(m_emu)) + .transform([&](auto &&tup) { + auto [dividend, divisor] = tup; -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); }); -} + if (divisor == 0) + return inst.rd.Write(m_emu, SextW(dividend)); -static const InstrPattern PATTERNS[] = { - // RV32I & RV64I (The base integer ISA) // - {"LUI", 0x7F, 0x37, ExecLUI}, - {"AUIPC", 0x7F, 0x17, ExecAUIPC}, - {"JAL", 0x7F, 0x6F, ExecJAL}, - {"JALR", 0x707F, 0x67, ExecJALR}, - {"B", 0x7F, 0x63, ExecB}, - {"LB", 0x707F, 0x3, ExecLB}, - {"LH", 0x707F, 0x1003, ExecLH}, - {"LW", 0x707F, 0x2003, ExecLW}, - {"LBU", 0x707F, 0x4003, ExecLBU}, - {"LHU", 0x707F, 0x5003, ExecLHU}, - {"SB", 0x707F, 0x23, ExecSB}, - {"SH", 0x707F, 0x1023, ExecSH}, - {"SW", 0x707F, 0x2023, ExecSW}, - {"ADDI", 0x707F, 0x13, ExecADDI}, - {"SLTI", 0x707F, 0x2013, ExecSLTI}, - {"SLTIU", 0x707F, 0x3013, ExecSLTIU}, - {"XORI", 0x707F, 0x4013, ExecXORI}, - {"ORI", 0x707F, 0x6013, ExecORI}, - {"ANDI", 0x707F, 0x7013, ExecANDI}, - {"SLLI", 0xF800707F, 0x1013, ExecSLLI}, - {"SRLI", 0xF800707F, 0x5013, ExecSRLI}, - {"SRAI", 0xF800707F, 0x40005013, ExecSRAI}, - {"ADD", 0xFE00707F, 0x33, ExecADD}, - {"SUB", 0xFE00707F, 0x40000033, ExecSUB}, - {"SLL", 0xFE00707F, 0x1033, ExecSLL}, - {"SLT", 0xFE00707F, 0x2033, ExecSLT}, - {"SLTU", 0xFE00707F, 0x3033, ExecSLTU}, - {"XOR", 0xFE00707F, 0x4033, ExecXOR}, - {"SRL", 0xFE00707F, 0x5033, ExecSRL}, - {"SRA", 0xFE00707F, 0x40005033, ExecSRA}, - {"OR", 0xFE00707F, 0x6033, ExecOR}, - {"AND", 0xFE00707F, 0x7033, ExecAND}, - {"LWU", 0x707F, 0x6003, ExecLWU}, - {"LD", 0x707F, 0x3003, ExecLD}, - {"SD", 0x707F, 0x3023, ExecSD}, - {"ADDIW", 0x707F, 0x1B, ExecADDIW}, - {"SLLIW", 0xFE00707F, 0x101B, ExecSLLIW}, - {"SRLIW", 0xFE00707F, 0x501B, ExecSRLIW}, - {"SRAIW", 0xFE00707F, 0x4000501B, ExecSRAIW}, - {"ADDW", 0xFE00707F, 0x3B, ExecADDW}, - {"SUBW", 0xFE00707F, 0x4000003B, ExecSUBW}, - {"SLLW", 0xFE00707F, 0x103B, ExecSLLW}, - {"SRLW", 0xFE00707F, 0x503B, ExecSRLW}, - {"SRAW", 0xFE00707F, 0x4000503B, ExecSRAW}, + if (dividend == INT32_MIN && divisor == -1) + return inst.rd.Write(m_emu, 0); - // 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}, + return inst.rd.Write(m_emu, SextW(dividend % divisor)); + }) + .value_or(false); + } + bool operator()(REMUW inst) { + return zipOpt(inst.rs1.ReadU32(m_emu), inst.rs2.ReadU32(m_emu)) + .transform([&](auto &&tup) { + auto [dividend, divisor] = tup; - // 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}, -}; + if (divisor == 0) + return inst.rd.Write(m_emu, SextW(dividend)); -const InstrPattern *EmulateInstructionRISCV::Decode(uint32_t inst) { - for (const InstrPattern &pat : PATTERNS) { - if ((inst & pat.type_mask) == pat.eigen) - return &pat; + return inst.rd.Write(m_emu, SextW(dividend % divisor)); + }) + .value_or(false); } - return nullptr; -} - -/// This function only determines the next instruction address for software -/// single stepping by emulating instructions -bool EmulateInstructionRISCV::DecodeAndExecute(uint32_t inst, - bool ignore_cond) { - Log *log = GetLog(LLDBLog::Unwind); - const InstrPattern *pattern = this->Decode(inst); - if (pattern) { - LLDB_LOGF(log, "EmulateInstructionRISCV::%s: inst(%x) was decoded to %s", - __FUNCTION__, inst, pattern->name); - return pattern->exec(*this, inst, ignore_cond); + // RV32A & RV64A (The standard atomic instruction extension) // + bool operator()(LR_W) { return AtomicSequence(m_emu); } + bool operator()(LR_D) { return AtomicSequence(m_emu); } + bool operator()(SC_W) { + llvm_unreachable("should be handled in AtomicSequence"); + } + bool operator()(SC_D) { + llvm_unreachable("should be handled in AtomicSequence"); + } + bool operator()(AMOSWAP_W inst) { + return AtomicSwap(m_emu, inst, 4, SextW); + } + bool operator()(AMOADD_W inst) { + return AtomicADD(m_emu, inst, 4, SextW); + } + bool operator()(AMOXOR_W inst) { + return AtomicBitOperate( + m_emu, inst, 4, SextW, [](uint32_t a, uint32_t b) { return a ^ b; }); + } + bool operator()(AMOAND_W inst) { + return AtomicBitOperate( + m_emu, inst, 4, SextW, [](uint32_t a, uint32_t b) { return a & b; }); + } + bool operator()(AMOOR_W inst) { + return AtomicBitOperate( + m_emu, inst, 4, SextW, [](uint32_t a, uint32_t b) { return a | b; }); + } + bool operator()(AMOMIN_W inst) { + return AtomicCmp( + m_emu, inst, 4, SextW, [](uint32_t a, uint32_t b) { + return uint32_t(std::min(int32_t(a), int32_t(b))); + }); + } + bool operator()(AMOMAX_W inst) { + return AtomicCmp( + m_emu, inst, 4, SextW, [](uint32_t a, uint32_t b) { + return uint32_t(std::max(int32_t(a), int32_t(b))); + }); + } + bool operator()(AMOMINU_W inst) { + return AtomicCmp( + m_emu, inst, 4, SextW, + [](uint32_t a, uint32_t b) { return std::min(a, b); }); + } + bool operator()(AMOMAXU_W inst) { + return AtomicCmp( + m_emu, inst, 4, SextW, + [](uint32_t a, uint32_t b) { return std::max(a, b); }); + } + bool operator()(AMOSWAP_D inst) { + return AtomicSwap(m_emu, inst, 8, ZextD); + } + bool operator()(AMOADD_D inst) { + return AtomicADD(m_emu, inst, 8, ZextD); } + bool operator()(AMOXOR_D inst) { + return AtomicBitOperate( + m_emu, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return a ^ b; }); + } + bool operator()(AMOAND_D inst) { + return AtomicBitOperate( + m_emu, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return a & b; }); + } + bool operator()(AMOOR_D inst) { + return AtomicBitOperate( + m_emu, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return a | b; }); + } + bool operator()(AMOMIN_D inst) { + return AtomicCmp( + m_emu, inst, 8, ZextD, [](uint64_t a, uint64_t b) { + return uint64_t(std::min(int64_t(a), int64_t(b))); + }); + } + bool operator()(AMOMAX_D inst) { + return AtomicCmp( + m_emu, inst, 8, ZextD, [](uint64_t a, uint64_t b) { + return uint64_t(std::max(int64_t(a), int64_t(b))); + }); + } + bool operator()(AMOMINU_D inst) { + return AtomicCmp( + m_emu, inst, 8, ZextD, + [](uint64_t a, uint64_t b) { return std::min(a, b); }); + } + bool operator()(AMOMAXU_D inst) { + return AtomicCmp( + m_emu, inst, 8, ZextD, + [](uint64_t a, uint64_t b) { return std::max(a, b); }); + } +}; - LLDB_LOGF(log, "EmulateInstructionRISCV::%s: inst(0x%x) was unsupported", - __FUNCTION__, inst); - return false; +bool EmulateInstructionRISCV::Execute(DecodeResult inst, bool ignore_cond) { + return std::visit(Executor(*this, ignore_cond, inst.is_rvc), inst.decoded); } bool EmulateInstructionRISCV::EvaluateInstruction(uint32_t options) { - uint32_t inst_size = m_opcode.GetByteSize(); - uint32_t inst = m_opcode.GetOpcode32(); bool increase_pc = options & eEmulateInstructionOptionAutoAdvancePC; bool ignore_cond = options & eEmulateInstructionOptionIgnoreConditions; - bool success = false; - lldb::addr_t old_pc = LLDB_INVALID_ADDRESS; - if (increase_pc) { - old_pc = ReadPC(success); - if (!success) - return false; - } + if (!increase_pc) + return Execute(m_decoded, ignore_cond); - if (inst_size == 2) { - // TODO: execute RVC + auto old_pc = ReadPC(); + if (!old_pc) return false; - } - success = DecodeAndExecute(inst, ignore_cond); + bool success = Execute(m_decoded, ignore_cond); if (!success) return false; - if (increase_pc) { - lldb::addr_t new_pc = ReadPC(success); - if (!success) - return false; + auto new_pc = ReadPC(); + if (!new_pc) + return false; + + // If the pc is not updated during execution, we do it here. + return new_pc != old_pc || + WritePC(*old_pc + Executor::size(m_decoded.is_rvc)); +} - if (new_pc == old_pc) { - if (!WritePC(old_pc + inst_size)) - return false; - } - } - return true; +llvm::Optional +EmulateInstructionRISCV::ReadInstructionAt(lldb::addr_t addr) { + return ReadMem(addr) + .transform([&](uint32_t inst) { return Decode(inst); }) + .value_or(llvm::None); } bool EmulateInstructionRISCV::ReadInstruction() { - bool success = false; - m_addr = ReadPC(success); - if (!success) { - m_addr = LLDB_INVALID_ADDRESS; + auto addr = ReadPC(); + m_addr = addr.value_or(LLDB_INVALID_ADDRESS); + if (!addr) return false; - } - - Context ctx; - ctx.type = eContextReadOpcode; - ctx.SetNoArgs(); - uint32_t inst = uint32_t(ReadMemoryUnsigned(ctx, m_addr, 4, 0, &success)); - uint16_t try_rvc = uint16_t(inst & 0x0000ffff); - // check whether the compressed encode could be valid - uint16_t mask = try_rvc & 0b11; - if (try_rvc != 0 && mask != 3) - m_opcode.SetOpcode16(try_rvc, GetByteOrder()); + auto inst = ReadInstructionAt(*addr); + if (!inst) + return false; + m_decoded = *inst; + if (inst->is_rvc) + m_opcode.SetOpcode16(inst->inst, GetByteOrder()); else - m_opcode.SetOpcode32(inst, GetByteOrder()); - + m_opcode.SetOpcode32(inst->inst, GetByteOrder()); return true; } -lldb::addr_t EmulateInstructionRISCV::ReadPC(bool &success) { - return ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, - LLDB_INVALID_ADDRESS, &success); +llvm::Optional EmulateInstructionRISCV::ReadPC() { + bool success = false; + auto addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, + LLDB_INVALID_ADDRESS, &success); + return success ? llvm::Optional(addr) : llvm::None; } bool EmulateInstructionRISCV::WritePC(lldb::addr_t pc) { diff --git a/lldb/source/Plugins/Instruction/RISCV/RISCVInstructions.h b/lldb/source/Plugins/Instruction/RISCV/RISCVInstructions.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Instruction/RISCV/RISCVInstructions.h @@ -0,0 +1,220 @@ +//===-- RISCVInstructions.h -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_INSTRUCTION_RISCV_RISCVINSTRUCTION_H +#define LLDB_SOURCE_PLUGINS_INSTRUCTION_RISCV_RISCVINSTRUCTION_H + +#include +#include + +#include "EmulateInstructionRISCV.h" +#include "llvm/ADT/Optional.h" + +namespace lldb_private { + +class EmulateInstructionRISCV; + +struct Rd { + uint32_t rd; + bool Write(EmulateInstructionRISCV &emulator, uint64_t value); +}; + +struct Rs { + uint32_t rs; + llvm::Optional Read(EmulateInstructionRISCV &emulator); + llvm::Optional ReadI32(EmulateInstructionRISCV &emulator); + llvm::Optional ReadI64(EmulateInstructionRISCV &emulator); + llvm::Optional ReadU32(EmulateInstructionRISCV &emulator); +}; + +#define I_TYPE_INST(NAME) \ + struct NAME { \ + Rd rd; \ + Rs rs1; \ + uint32_t imm; \ + } +#define S_TYPE_INST(NAME) \ + struct NAME { \ + Rs rs1; \ + Rs rs2; \ + uint32_t imm; \ + } +#define U_TYPE_INST(NAME) \ + struct NAME { \ + Rd rd; \ + uint32_t imm; \ + } +/// The memory layout are the same in our code. +#define J_TYPE_INST(NAME) U_TYPE_INST(NAME) +#define R_TYPE_INST(NAME) \ + struct NAME { \ + Rd rd; \ + Rs rs1; \ + Rs rs2; \ + } +#define R_SHAMT_TYPE_INST(NAME) \ + struct NAME { \ + Rd rd; \ + Rs rs1; \ + uint32_t shamt; \ + } +#define R_RS1_TYPE_INST(NAME) \ + struct NAME { \ + Rd rd; \ + Rs rs1; \ + } + +// RV32I instructions (The base integer ISA) +struct B { + Rs rs1; + Rs rs2; + uint32_t imm; + uint32_t funct3; +}; +U_TYPE_INST(LUI); +U_TYPE_INST(AUIPC); +J_TYPE_INST(JAL); +I_TYPE_INST(JALR); +I_TYPE_INST(LB); +I_TYPE_INST(LH); +I_TYPE_INST(LW); +I_TYPE_INST(LBU); +I_TYPE_INST(LHU); +S_TYPE_INST(SB); +S_TYPE_INST(SH); +S_TYPE_INST(SW); +I_TYPE_INST(ADDI); +I_TYPE_INST(SLTI); +I_TYPE_INST(SLTIU); +I_TYPE_INST(XORI); +I_TYPE_INST(ORI); +I_TYPE_INST(ANDI); +R_TYPE_INST(ADD); +R_TYPE_INST(SUB); +R_TYPE_INST(SLL); +R_TYPE_INST(SLT); +R_TYPE_INST(SLTU); +R_TYPE_INST(XOR); +R_TYPE_INST(SRL); +R_TYPE_INST(SRA); +R_TYPE_INST(OR); +R_TYPE_INST(AND); + +// RV64I inst (The base integer ISA) +I_TYPE_INST(LWU); +I_TYPE_INST(LD); +S_TYPE_INST(SD); +R_SHAMT_TYPE_INST(SLLI); +R_SHAMT_TYPE_INST(SRLI); +R_SHAMT_TYPE_INST(SRAI); +I_TYPE_INST(ADDIW); +R_SHAMT_TYPE_INST(SLLIW); +R_SHAMT_TYPE_INST(SRLIW); +R_SHAMT_TYPE_INST(SRAIW); +R_TYPE_INST(ADDW); +R_TYPE_INST(SUBW); +R_TYPE_INST(SLLW); +R_TYPE_INST(SRLW); +R_TYPE_INST(SRAW); + +// RV32M inst (The standard integer multiplication and division extension) +R_TYPE_INST(MUL); +R_TYPE_INST(MULH); +R_TYPE_INST(MULHSU); +R_TYPE_INST(MULHU); +R_TYPE_INST(DIV); +R_TYPE_INST(DIVU); +R_TYPE_INST(REM); +R_TYPE_INST(REMU); + +// RV64M inst (The standard integer multiplication and division extension) +R_TYPE_INST(MULW); +R_TYPE_INST(DIVW); +R_TYPE_INST(DIVUW); +R_TYPE_INST(REMW); +R_TYPE_INST(REMUW); + +// RV32A inst (The standard atomic instruction extension) +R_RS1_TYPE_INST(LR_W); +R_TYPE_INST(SC_W); +R_TYPE_INST(AMOSWAP_W); +R_TYPE_INST(AMOADD_W); +R_TYPE_INST(AMOXOR_W); +R_TYPE_INST(AMOAND_W); +R_TYPE_INST(AMOOR_W); +R_TYPE_INST(AMOMIN_W); +R_TYPE_INST(AMOMAX_W); +R_TYPE_INST(AMOMINU_W); +R_TYPE_INST(AMOMAXU_W); + +// RV64A inst (The standard atomic instruction extension) +R_RS1_TYPE_INST(LR_D); +R_TYPE_INST(SC_D); +R_TYPE_INST(AMOSWAP_D); +R_TYPE_INST(AMOADD_D); +R_TYPE_INST(AMOXOR_D); +R_TYPE_INST(AMOAND_D); +R_TYPE_INST(AMOOR_D); +R_TYPE_INST(AMOMIN_D); +R_TYPE_INST(AMOMAX_D); +R_TYPE_INST(AMOMINU_D); +R_TYPE_INST(AMOMAXU_D); + +using RISCVInst = + std::variant; + +struct InstrPattern { + const char *name; + /// Bit mask to check the type of a instruction (B-Type, I-Type, J-Type, etc.) + uint32_t type_mask; + /// Characteristic value after bitwise-and with type_mask. + uint32_t eigen; + RISCVInst (*decode)(uint32_t inst); +}; + +struct DecodeResult { + RISCVInst decoded; + uint32_t inst; + bool is_rvc; + InstrPattern pattern; +}; + +constexpr uint32_t DecodeRD(uint32_t inst) { return (inst & 0xF80) >> 7; } +constexpr uint32_t DecodeRS1(uint32_t inst) { return (inst & 0xF8000) >> 15; } +constexpr uint32_t DecodeRS2(uint32_t inst) { return (inst & 0x1F00000) >> 20; } + +// decode register for RVC +constexpr uint16_t DecodeCR_RD(uint16_t inst) { return DecodeRD(inst); } +constexpr uint16_t DecodeCI_RD(uint16_t inst) { return DecodeRD(inst); } +constexpr uint16_t DecodeCIW_RD(uint16_t inst) { return (inst & 0x1C) >> 2; } +constexpr uint16_t DecodeCL_RD(uint16_t inst) { return DecodeCIW_RD(inst); } +constexpr uint16_t DecodeCA_RD(uint16_t inst) { return (inst & 0x380) >> 7; } +constexpr uint16_t DecodeCB_RD(uint16_t inst) { return DecodeCA_RD(inst); } + +constexpr uint16_t DecodeCR_RS1(uint16_t inst) { return DecodeRD(inst); } +constexpr uint16_t DecodeCI_RS1(uint16_t inst) { return DecodeRD(inst); } +constexpr uint16_t DecodeCL_RS1(uint16_t inst) { return DecodeCA_RD(inst); } +constexpr uint16_t DecodeCS_RS1(uint16_t inst) { return DecodeCA_RD(inst); } +constexpr uint16_t DecodeCA_RS1(uint16_t inst) { return DecodeCA_RD(inst); } +constexpr uint16_t DecodeCB_RS1(uint16_t inst) { return DecodeCA_RD(inst); } + +constexpr uint16_t DecodeCR_RS2(uint16_t inst) { return (inst & 0x7C) >> 2; } +constexpr uint16_t DecodeCSS_RS2(uint16_t inst) { return DecodeCR_RS2(inst); } +constexpr uint16_t DecodeCS_RS2(uint16_t inst) { return DecodeCIW_RD(inst); } +constexpr uint16_t DecodeCA_RS2(uint16_t inst) { return DecodeCIW_RD(inst); } + +} // namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_INSTRUCTION_RISCV_RISCVINSTRUCTION_H diff --git a/lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp b/lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp --- a/lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp +++ b/lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp @@ -75,6 +75,12 @@ memcpy(tester->memory + addr, dst, length); return length; }; + + bool DecodeAndExecute(uint32_t inst, bool ignore_cond) { + return Decode(inst) + .transform([&](DecodeResult res) { return Execute(res, ignore_cond); }) + .value_or(false); + } }; TEST_F(RISCVEmulatorTester, testJAL) { @@ -84,13 +90,10 @@ uint32_t inst = 0b11111110100111111111000011101111; ASSERT_TRUE(DecodeAndExecute(inst, false)); auto x1 = gpr.gpr[1]; - - bool success = false; - auto pc = ReadPC(success); - - ASSERT_TRUE(success); + auto pc = ReadPC(); + ASSERT_TRUE(pc.has_value()); ASSERT_EQ(x1, old_pc + 4); - ASSERT_EQ(pc, old_pc + (-6 * 4)); + ASSERT_EQ(*pc, old_pc + (-6 * 4)); } constexpr uint32_t EncodeIType(uint32_t opcode, uint32_t funct3, uint32_t rd, @@ -98,7 +101,7 @@ return imm << 20 | rs1 << 15 | funct3 << 12 | rd << 7 | opcode; } -constexpr uint32_t JALR(uint32_t rd, uint32_t rs1, int32_t offset) { +constexpr uint32_t EncodeJALR(uint32_t rd, uint32_t rs1, int32_t offset) { return EncodeIType(0b1100111, 0, rd, rs1, uint32_t(offset)); } @@ -108,17 +111,14 @@ WritePC(old_pc); gpr.gpr[2] = old_x2; // jalr x1, x2(-255) - uint32_t inst = JALR(1, 2, -255); + uint32_t inst = EncodeJALR(1, 2, -255); ASSERT_TRUE(DecodeAndExecute(inst, false)); auto x1 = gpr.gpr[1]; - - bool success = false; - auto pc = ReadPC(success); - - ASSERT_TRUE(success); + auto pc = ReadPC(); + ASSERT_TRUE(pc.has_value()); ASSERT_EQ(x1, old_pc + 4); // JALR always zeros the bottom bit of the target address. - ASSERT_EQ(pc, (old_x2 + (-255)) & (~1)); + ASSERT_EQ(*pc, (old_x2 + (-255)) & (~1)); } constexpr uint32_t EncodeBType(uint32_t opcode, uint32_t funct3, uint32_t rs1, @@ -165,10 +165,9 @@ // b x1, x2, (-256) uint32_t inst = encoder(1, 2, -256); ASSERT_TRUE(tester->DecodeAndExecute(inst, false)); - bool success = false; - auto pc = tester->ReadPC(success); - ASSERT_TRUE(success); - ASSERT_EQ(pc, old_pc + (branched ? (-256) : 0)); + auto pc = tester->ReadPC(); + ASSERT_TRUE(pc.has_value()); + ASSERT_EQ(*pc, old_pc + (branched ? (-256) : 0)); } #define GEN_BRANCH_TEST(name, rs1, rs2_branched, rs2_continued) \ @@ -185,9 +184,9 @@ 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); + auto mem = tester->ReadMem(addr); + ASSERT_TRUE(mem.has_value()); + ASSERT_EQ(*mem, value); } using RS1 = uint64_t; @@ -195,13 +194,13 @@ using PC = uint64_t; using RDComputer = std::function; -void TestInst(RISCVEmulatorTester *tester, uint64_t inst, bool has_rs2, +void TestInst(RISCVEmulatorTester *tester, DecodeResult inst, bool has_rs2, RDComputer rd_val) { lldb::addr_t old_pc = 0x114514; tester->WritePC(old_pc); - uint32_t rd = DecodeRD(inst); - uint32_t rs1 = DecodeRS1(inst); + uint32_t rd = DecodeRD(inst.inst); + uint32_t rs1 = DecodeRS1(inst.inst); uint32_t rs2 = 0; uint64_t rs1_val = 0x19; @@ -211,7 +210,7 @@ tester->gpr.gpr[rs1] = rs1_val; if (has_rs2) { - rs2 = DecodeRS2(inst); + rs2 = DecodeRS2(inst.inst); if (rs2) { if (rs1 == rs2) rs2_val = rs1_val; @@ -219,7 +218,7 @@ } } - ASSERT_TRUE(tester->DecodeAndExecute(inst, false)); + ASSERT_TRUE(tester->Execute(inst, false)); CheckRD(tester, rd, rd_val(rs1_val, rs2 ? rs2_val : 0, old_pc)); } @@ -239,8 +238,7 @@ tester->gpr.gpr[rs2] = rs2_val; // Write and check rs1_val in atomic_addr - ASSERT_TRUE( - tester->WriteMem(*tester, atomic_addr, RegisterValue(rs1_val))); + ASSERT_TRUE(tester->WriteMem(atomic_addr, rs1_val)); CheckMem(tester, atomic_addr, rs1_val); ASSERT_TRUE(tester->DecodeAndExecute(inst, false)); @@ -295,10 +293,8 @@ // 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; }}, + {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; }}, @@ -317,11 +313,11 @@ [](RS1 rs1, RS2 rs2, PC) { return rs1 % rs2; }}, }; for (auto i : tests) { - const InstrPattern *pattern = this->Decode(i.inst); - ASSERT_TRUE(pattern != nullptr); - std::string name = pattern->name; + auto decode = this->Decode(i.inst); + ASSERT_TRUE(decode.has_value()); + std::string name = decode->pattern.name; ASSERT_EQ(name, i.name); - TestInst(this, i.inst, i.has_rs2, i.rd_val); + TestInst(this, *decode, i.has_rs2, i.rd_val); } }