Author: Emmmer Date: 2022-10-05T19:45:28+08:00 New Revision: d0dcbb9b026af47378ad0603a45663fad9d8e9c2
URL: https://github.com/llvm/llvm-project/commit/d0dcbb9b026af47378ad0603a45663fad9d8e9c2 DIFF: https://github.com/llvm/llvm-project/commit/d0dcbb9b026af47378ad0603a45663fad9d8e9c2.diff LOG: [LLDB][RISCV][NFC] Rewrite instruction in algebraic datatype The old approach (dedicated ExecXXX for each instruction) is not flexible and results in duplicated code when RVC kicks in. According to the spec, every compressed instruction can be decoded to a non-compressed one. So we can lower compressed instructions to instructions we already had, which requires a decoupling between the decoder and executor. This patch: - use llvm::Optional and its combinators AMAP. - use template constraints on common instruction. - make instructions strongly-typed (no uint32_t everywhere bc it is error-prone and burdens the developer when lowering the RVC) with the help of algebraic datatype (std::variant). Note: (NFC) because this is more of a refactoring in preparation for RVC. Reviewed By: DavidSpickett Differential Revision: https://reviews.llvm.org/D135015 Added: lldb/source/Plugins/Instruction/RISCV/RISCVInstructions.h Modified: lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp index f84c1159f254d..8d1a4c999e8f8 100644 --- 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 @@ LLDB_PLUGIN_DEFINE_ADV(EmulateInstructionRISCV, InstructionRISCV) namespace lldb_private { +/// Returns all values wrapped in Optional, or None if any of the values is +/// None. +template <typename... Ts> +static llvm::Optional<std::tuple<Ts...>> zipOpt(llvm::Optional<Ts> &&...ts) { + if ((ts.has_value() && ...)) + return llvm::Optional<std::tuple<Ts...>>( + std::make_tuple(std::move(*ts)...)); + else + return llvm::None; +} + // The funct3 is the type of compare in B<CMP> 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 BGE = 0b101; 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 @@ static uint32_t GPREncodingToLLDB(uint32_t reg_encode) { 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<uint64_t> Rs::Read(EmulateInstructionRISCV &emulator) { + uint32_t lldbReg = GPREncodingToLLDB(rs); + RegisterValue value; + return emulator.ReadRegister(eRegisterKindLLDB, lldbReg, value) + ? llvm::Optional<uint64_t>(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<int32_t> Rs::ReadI32(EmulateInstructionRISCV &emulator) { + return Read(emulator).transform( + [](uint64_t value) { return int32_t(uint32_t(value)); }); +} + +llvm::Optional<int64_t> Rs::ReadI64(EmulateInstructionRISCV &emulator) { + return Read(emulator).transform( + [](uint64_t value) { return int64_t(value); }); +} + +llvm::Optional<uint32_t> 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 @@ static bool CompareB(uint64_t rs1, uint64_t rs2, uint32_t funct3) { } } -struct Wrapped { - RegisterValue value; - - template <typename T> - [[nodiscard]] std::enable_if_t<std::is_unsigned_v<T>, T> trunc() const { - return T(value.GetAsUInt64()); - } - - template <typename T> T sext() const = delete; -}; - -template <> int32_t Wrapped::sext<int32_t>() const { - return int32_t(trunc<uint32_t>()); -} - -template <> int64_t Wrapped::sext<int64_t>() const { - return int64_t(trunc<uint64_t>()); -} - -template <bool hasRS2> struct RSRegs { -private: - Wrapped rs1_value; - -public: - bool valid; - - Wrapped &rs1() { return rs1_value; } -}; - -template <> struct RSRegs<true> : RSRegs<false> { -private: - Wrapped rs2_value; - -public: - Wrapped &rs2() { return rs2_value; } -}; - -RSRegs<true> readRS1RS2(EmulateInstructionRISCV &emulator, uint32_t inst) { - RSRegs<true> value{}; - value.valid = ReadRegister(emulator, DecodeRS1(inst), value.rs1().value) && - ReadRegister(emulator, DecodeRS2(inst), value.rs2().value); - return value; -} - -RSRegs<false> readRS1(EmulateInstructionRISCV &emulator, uint32_t inst) { - RSRegs<false> value{}; - value.valid = ReadRegister(emulator, DecodeRS1(inst), value.rs1().value); - return value; -} - -static bool ExecB(EmulateInstructionRISCV &emulator, uint32_t inst, - bool ignore_cond) { - bool success = false; - uint64_t pc = emulator.ReadPC(success); - 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<uint64_t>(), rs.rs2().trunc<uint64_t>(), 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 <typename T> -static std::enable_if_t<std::is_integral_v<T>, llvm::Optional<T>> -ReadMem(EmulateInstructionRISCV &emulator, uint64_t addr) { - EmulateInstructionRISCV::Context ctx; - ctx.type = EmulateInstruction::eContextRegisterLoad; - ctx.SetNoArgs(); - 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<T, LB> || std::is_same_v<T, LH> || std::is_same_v<T, LW> || + std::is_same_v<T, LD> || std::is_same_v<T, LBU> || std::is_same_v<T, LHU> || + std::is_same_v<T, LWU>; template <typename T> -static bool WriteMem(EmulateInstructionRISCV &emulator, uint64_t addr, - RegisterValue value) { - EmulateInstructionRISCV::Context ctx; - ctx.type = EmulateInstruction::eContextRegisterStore; - ctx.SetNoArgs(); - return emulator.WriteMemoryUnsigned(ctx, addr, value.GetAsUInt64(), - sizeof(T)); -} - -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>() + uint64_t(imm); - return addr; -} - -// Read T from memory, then load its sign-extended value E to register. -template <typename T, typename E> -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<T>(emulator, addr).value()); - if (!value) - return false; - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value))); -} +constexpr bool is_store = std::is_same_v<T, SB> || std::is_same_v<T, SH> || + std::is_same_v<T, SW> || std::is_same_v<T, SD>; template <typename T> -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<T>(emulator, addr, value); -} - -// RV32I & RV64I (The base integer ISA) // -static bool ExecLB(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Load<uint8_t, int8_t>(emulator, inst, SextW); -} - -static bool ExecLH(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Load<uint16_t, int16_t>(emulator, inst, SextW); -} - -static bool ExecLW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Load<uint32_t, int32_t>(emulator, inst, SextW); -} - -static bool ExecLD(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Load<uint64_t, uint64_t>(emulator, inst, ZextD); -} - -static bool ExecLBU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Load<uint8_t, uint8_t>(emulator, inst, ZextD); -} - -static bool ExecLHU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Load<uint16_t, uint16_t>(emulator, inst, ZextD); -} - -static bool ExecLWU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Load<uint32_t, uint32_t>(emulator, inst, ZextD); -} - -static bool ExecSB(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Store<uint8_t>(emulator, inst); -} - -static bool ExecSH(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Store<uint16_t>(emulator, inst); -} - -static bool ExecSW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Store<uint32_t>(emulator, inst); -} - -static bool ExecSD(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - return Store<uint64_t>(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>() + 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>() < 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>() < 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>() ^ 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<T, AMOADD_W> || std::is_same_v<T, AMOADD_D>; - uint64_t result = rs.rs1().trunc<uint64_t>() | uint64_t(imm); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecANDI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - int32_t imm = SignExt(DecodeIImm(inst)); - - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = rs.rs1().trunc<uint64_t>() & uint64_t(imm); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSLLI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = rs.rs1().trunc<uint64_t>() << DecodeSHAMT7(inst); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSRLI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = rs.rs1().trunc<uint64_t>() >> DecodeSHAMT7(inst); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSRAI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = rs.rs1().sext<int64_t>() >> DecodeSHAMT7(inst); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecADD(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = rs.rs1().trunc<uint64_t>() + rs.rs2().trunc<uint64_t>(); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSUB(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = rs.rs1().trunc<uint64_t>() - rs.rs2().trunc<uint64_t>(); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSLL(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = rs.rs1().trunc<uint64_t>() - << (rs.rs2().trunc<uint64_t>() & 0b111111); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSLT(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = rs.rs1().sext<int64_t>() < rs.rs2().sext<int64_t>(); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSLTU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = rs.rs1().trunc<uint64_t>() < rs.rs2().trunc<uint64_t>(); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecXOR(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = rs.rs1().trunc<uint64_t>() ^ rs.rs2().trunc<uint64_t>(); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSRL(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = - rs.rs1().trunc<uint64_t>() >> (rs.rs2().trunc<uint64_t>() & 0b111111); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSRA(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = - rs.rs1().sext<int64_t>() >> (rs.rs2().trunc<uint64_t>() & 0b111111); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecOR(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = rs.rs1().trunc<uint64_t>() | rs.rs2().trunc<uint64_t>(); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecAND(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = rs.rs1().trunc<uint64_t>() & rs.rs2().trunc<uint64_t>(); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecADDIW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - int32_t imm = SignExt(DecodeIImm(inst)); - - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = SextW(rs.rs1().sext<int32_t>() + imm); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSLLIW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = SextW(rs.rs1().trunc<uint32_t>() << DecodeSHAMT5(inst)); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSRLIW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = SextW(rs.rs1().trunc<uint32_t>() >> DecodeSHAMT5(inst)); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSRAIW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = SextW(rs.rs1().sext<int32_t>() >> DecodeSHAMT5(inst)); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecADDW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = - SextW(uint32_t(rs.rs1().trunc<uint64_t>() + rs.rs2().trunc<uint64_t>())); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSUBW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = - SextW(uint32_t(rs.rs1().trunc<uint64_t>() - rs.rs2().trunc<uint64_t>())); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSLLW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = SextW(rs.rs1().trunc<uint32_t>() - << (rs.rs2().trunc<uint32_t>() & 0b11111)); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSRLW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = SextW(rs.rs1().trunc<uint32_t>() >> - (rs.rs2().trunc<uint32_t>() & 0b11111)); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecSRAW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - uint64_t result = SextW(rs.rs1().sext<int32_t>() >> - (rs.rs2().trunc<uint64_t>() & 0b111111)); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -// 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<uint64_t>() * rs.rs2().trunc<uint64_t>(); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecMULH(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - llvm::APInt mul = llvm::APInt(128, rs.rs1().trunc<uint64_t>(), true) * - llvm::APInt(128, rs.rs2().trunc<uint64_t>(), true); - - // signed * signed - auto result = mul.ashr(64).trunc(64).getZExtValue(); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecMULHSU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; - - // signed * unsigned - llvm::APInt mul = - llvm::APInt(128, rs.rs1().trunc<uint64_t>(), true).zext(128) * - llvm::APInt(128, rs.rs2().trunc<uint64_t>(), false); - - auto result = mul.lshr(64).trunc(64).getZExtValue(); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecMULHU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) - return false; +template <typename T> +constexpr bool is_amo_bit_op = + std::is_same_v<T, AMOXOR_W> || std::is_same_v<T, AMOXOR_D> || + std::is_same_v<T, AMOAND_W> || std::is_same_v<T, AMOAND_D> || + std::is_same_v<T, AMOOR_W> || std::is_same_v<T, AMOOR_D>; - // unsigned * unsigned - llvm::APInt mul = llvm::APInt(128, rs.rs1().trunc<uint64_t>(), false) * - llvm::APInt(128, rs.rs2().trunc<uint64_t>(), false); - auto result = mul.lshr(64).trunc(64).getZExtValue(); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} +template <typename T> +constexpr bool is_amo_swap = + std::is_same_v<T, AMOSWAP_W> || std::is_same_v<T, AMOSWAP_D>; -static bool ExecDIV(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) +template <typename T> +constexpr bool is_amo_cmp = + std::is_same_v<T, AMOMIN_W> || std::is_same_v<T, AMOMIN_D> || + std::is_same_v<T, AMOMAX_W> || std::is_same_v<T, AMOMAX_D> || + std::is_same_v<T, AMOMINU_W> || std::is_same_v<T, AMOMINU_D> || + std::is_same_v<T, AMOMAXU_W> || std::is_same_v<T, AMOMAXU_D>; + +template <typename I> +static std::enable_if_t<is_load<I> || is_store<I>, llvm::Optional<uint64_t>> +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 <typename I, typename T, typename m_emu> +static std::enable_if_t<is_load<I>, bool> +Load(EmulateInstructionRISCV &emulator, I inst, uint64_t (*extend)(m_emu)) { + auto addr = LoadStoreAddr(emulator, inst); + if (!addr) + return false; + return emulator.ReadMem<T>(*addr) + .transform([&](T t) { return inst.rd.Write(emulator, extend(m_emu(t))); }) + .value_or(false); +} + +template <typename I, typename T> +static std::enable_if_t<is_store<I>, 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<T>(*addr, rs2); }) + .value_or(false); +} + +template <typename I> +static std::enable_if_t<is_amo_add<I> || is_amo_bit_op<I> || is_amo_swap<I> || + is_amo_cmp<I>, + llvm::Optional<uint64_t>> +AtomicAddr(EmulateInstructionRISCV &emulator, I inst, unsigned int align) { + return inst.rs1.Read(emulator) + .transform([&](uint64_t rs1) { + return rs1 % align == 0 ? llvm::Optional<uint64_t>(rs1) : llvm::None; + }) + .value_or(llvm::None); +} + +template <typename I, typename T> +static std::enable_if_t<is_amo_swap<I>, 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<T>(*addr), inst.rs2.Read(emulator)) + .transform([&](auto &&tup) { + auto [tmp, rs2] = tup; + return emulator.WriteMem<T>(*addr, T(rs2)) && + inst.rd.Write(emulator, extend(tmp)); + }) + .value_or(false); +} + +template <typename I, typename T> +static std::enable_if_t<is_amo_add<I>, 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<T>(*addr), inst.rs2.Read(emulator)) + .transform([&](auto &&tup) { + auto [tmp, rs2] = tup; + return emulator.WriteMem<T>(*addr, T(tmp + rs2)) && + inst.rd.Write(emulator, extend(tmp)); + }) + .value_or(false); +} + +template <typename I, typename T> +static std::enable_if_t<is_amo_bit_op<I>, 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<T>(*addr), inst.rs2.Read(emulator)) + .transform([&](auto &&tup) { + auto [value, rs2] = tup; + return emulator.WriteMem<T>(*addr, operate(value, T(rs2))) && + inst.rd.Write(emulator, extend(value)); + }) + .value_or(false); +} + +template <typename I, typename T> +static std::enable_if_t<is_amo_cmp<I>, 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<T>(*addr), inst.rs2.Read(emulator)) + .transform([&](auto &&tup) { + auto [value, rs2] = tup; + return emulator.WriteMem<T>(*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: ........ <next instruction> + 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>(); - int64_t divisor = rs.rs2().sext<int64_t>(); - - if (divisor == 0) - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(UINT64_MAX)); - - if (dividend == INT64_MIN && divisor == -1) - return WriteRegister(emulator, DecodeRD(inst), - RegisterValue(uint64_t(dividend))); - - return WriteRegister(emulator, DecodeRD(inst), - RegisterValue(uint64_t(dividend / divisor))); -} - -static bool ExecDIVU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) + // The first instruction should be LR.W or LR.D + auto inst = emulator.ReadInstructionAt(current_pc); + if (!inst || (!std::holds_alternative<LR_W>(inst->decoded) && + !std::holds_alternative<LR_D>(inst->decoded))) return false; - uint64_t dividend = rs.rs1().trunc<uint64_t>(); - uint64_t divisor = rs.rs2().trunc<uint64_t>(); - - if (divisor == 0) - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(UINT64_MAX)); - - return WriteRegister(emulator, DecodeRD(inst), - RegisterValue(dividend / divisor)); -} - -static bool ExecREM(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) + // The second instruction should be BNE to exit address + inst = emulator.ReadInstructionAt(current_pc += 4); + if (!inst || !std::holds_alternative<B>(inst->decoded)) return false; - - int64_t dividend = rs.rs1().sext<int64_t>(); - int64_t divisor = rs.rs2().sext<int64_t>(); - - if (divisor == 0) - return WriteRegister(emulator, DecodeRD(inst), - RegisterValue(uint64_t(dividend))); - - if (dividend == INT64_MIN && divisor == -1) - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(uint64_t(0))); - - return WriteRegister(emulator, DecodeRD(inst), - RegisterValue(uint64_t(dividend % divisor))); -} - -static bool ExecREMU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) + auto bne_exit = std::get<B>(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>(); - uint64_t divisor = rs.rs2().trunc<uint64_t>(); - - if (divisor == 0) - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(dividend)); - - return WriteRegister(emulator, DecodeRD(inst), - RegisterValue(dividend % divisor)); -} - -static bool ExecMULW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) + // The third instruction should be SC.W or SC.D + inst = emulator.ReadInstructionAt(current_pc += 4); + if (!inst || (!std::holds_alternative<SC_W>(inst->decoded) && + !std::holds_alternative<SC_D>(inst->decoded))) return false; - uint64_t result = SextW(rs.rs1().sext<int32_t>() * rs.rs2().sext<int32_t>()); - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); -} - -static bool ExecDIVW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) + // The fourth instruction should be BNE to entry address + inst = emulator.ReadInstructionAt(current_pc += 4); + if (!inst || !std::holds_alternative<B>(inst->decoded)) return false; - - int32_t dividend = rs.rs1().sext<int32_t>(); - int32_t divisor = rs.rs2().sext<int32_t>(); - - if (divisor == 0) - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(UINT64_MAX)); - - if (dividend == INT32_MIN && divisor == -1) - return WriteRegister(emulator, DecodeRD(inst), - RegisterValue(SextW(dividend))); - - return WriteRegister(emulator, DecodeRD(inst), - RegisterValue(SextW(dividend / divisor))); -} - -static bool ExecDIVUW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) + auto bne_start = std::get<B>(inst->decoded); + if (bne_start.funct3 != BNE) return false; - - uint32_t dividend = rs.rs1().trunc<uint32_t>(); - uint32_t divisor = rs.rs2().trunc<uint32_t>(); - - if (divisor == 0) - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(UINT64_MAX)); - - return WriteRegister(emulator, DecodeRD(inst), - RegisterValue(SextW(dividend / divisor))); -} - -static bool ExecREMW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { - auto rs = readRS1RS2(emulator, inst); - if (!rs.valid) + if (entry_pc != current_pc + SextW(bne_start.imm)) return false; - int32_t dividend = rs.rs1().sext<int32_t>(); - int32_t divisor = rs.rs2().sext<int32_t>(); - - if (divisor == 0) - return WriteRegister(emulator, DecodeRD(inst), - RegisterValue(SextW(dividend))); - - if (dividend == INT32_MIN && divisor == -1) - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(uint64_t(0))); - - return WriteRegister(emulator, DecodeRD(inst), - RegisterValue(SextW(dividend % divisor))); + 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>(); - uint32_t divisor = rs.rs2().trunc<uint32_t>(); - - if (divisor == 0) - return WriteRegister(emulator, DecodeRD(inst), - RegisterValue(SextW(dividend))); - - return WriteRegister(emulator, DecodeRD(inst), - RegisterValue(SextW(dividend % divisor))); +template <typename T> 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 <typename T> static RISCVInst DecodeJType(uint32_t inst) { + return T{Rd{DecodeRD(inst)}, DecodeJImm(inst)}; } -template <typename T> -static bool AtomicSwap(EmulateInstructionRISCV &emulator, uint32_t inst, - int align, uint64_t (*extend)(T)) { - RegisterValue rs2; - if (!ReadRegister(emulator, DecodeRS2(inst), rs2)) - return false; - - uint64_t addr = AtomicAddr(emulator, DecodeRS1(inst), align); - if (addr == LLDB_INVALID_ADDRESS) - return false; - - T tmp = ReadMem<T>(emulator, addr).value(); - if (!tmp) - return false; - - bool success = - WriteMem<T>(emulator, addr, RegisterValue(T(rs2.GetAsUInt64()))); - if (!success) - return false; - - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(tmp))); +template <typename T> static RISCVInst DecodeIType(uint32_t inst) { + return T{Rd{DecodeRD(inst)}, Rs{DecodeRS1(inst)}, DecodeIImm(inst)}; } -template <typename T> -static bool AtomicADD(EmulateInstructionRISCV &emulator, uint32_t inst, - int align, uint64_t (*extend)(T)) { - RegisterValue rs2; - if (!ReadRegister(emulator, DecodeRS2(inst), rs2)) - return false; - - uint64_t addr = AtomicAddr(emulator, DecodeRS1(inst), align); - if (addr == LLDB_INVALID_ADDRESS) - return false; - - T value = ReadMem<T>(emulator, addr).value(); - if (!value) - return false; - - bool success = - WriteMem<T>(emulator, addr, RegisterValue(value + T(rs2.GetAsUInt64()))); - if (!success) - return false; - - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value))); +template <typename T> static RISCVInst DecodeBType(uint32_t inst) { + return T{Rs{DecodeRS1(inst)}, Rs{DecodeRS2(inst)}, DecodeBImm(inst), + (inst & 0x7000) >> 12}; } -template <typename T> -static bool AtomicBitOperate(EmulateInstructionRISCV &emulator, uint32_t inst, - int align, uint64_t (*extend)(T), - T (*operate)(T, T)) { - RegisterValue rs2; - if (!ReadRegister(emulator, DecodeRS2(inst), rs2)) - return false; - - uint64_t addr = AtomicAddr(emulator, DecodeRS1(inst), align); - if (addr == LLDB_INVALID_ADDRESS) - return false; - - T value = ReadMem<T>(emulator, addr).value(); - if (!value) - return false; - - bool success = WriteMem<T>( - emulator, addr, RegisterValue(operate(value, T(rs2.GetAsUInt64())))); - if (!success) - return false; - - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value))); +template <typename T> static RISCVInst DecodeSType(uint32_t inst) { + return T{Rs{DecodeRS1(inst)}, Rs{DecodeRS2(inst)}, DecodeSImm(inst)}; } -template <typename T> -static bool AtomicCmp(EmulateInstructionRISCV &emulator, uint32_t inst, - int align, uint64_t (*extend)(T), T (*cmp)(T, T)) { - RegisterValue rs2; - if (!ReadRegister(emulator, DecodeRS2(inst), rs2)) - return false; - - uint64_t addr = AtomicAddr(emulator, DecodeRS1(inst), align); - if (addr == LLDB_INVALID_ADDRESS) - return false; - - T value = ReadMem<T>(emulator, addr).value(); - if (!value) - return false; - - bool success = WriteMem<T>(emulator, addr, - RegisterValue(cmp(value, T(rs2.GetAsUInt64())))); - if (!success) - return false; - - return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value))); +template <typename T> 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 <typename T> 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 <typename T> 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: ........ <next instruction> - bool success = false; - llvm::Optional<lldb::addr_t> pc = emulator.ReadPC(success); - if (!success || !pc) - return false; - lldb::addr_t current_pc = pc.value(); - lldb::addr_t start_pc = current_pc; - llvm::Optional<uint32_t> value = inst; - - // inst must be LR - if (!value || !IsLR(value.value())) - return false; - value = ReadMem<uint32_t>(emulator, current_pc += 4).value(); +static const InstrPattern PATTERNS[] = { + // RV32I & RV64I (The base integer ISA) // + {"LUI", 0x7F, 0x37, DecodeUType<LUI>}, + {"AUIPC", 0x7F, 0x17, DecodeUType<AUIPC>}, + {"JAL", 0x7F, 0x6F, DecodeJType<JAL>}, + {"JALR", 0x707F, 0x67, DecodeIType<JALR>}, + {"B", 0x7F, 0x63, DecodeBType<B>}, + {"LB", 0x707F, 0x3, DecodeIType<LB>}, + {"LH", 0x707F, 0x1003, DecodeIType<LH>}, + {"LW", 0x707F, 0x2003, DecodeIType<LW>}, + {"LBU", 0x707F, 0x4003, DecodeIType<LBU>}, + {"LHU", 0x707F, 0x5003, DecodeIType<LHU>}, + {"SB", 0x707F, 0x23, DecodeSType<SB>}, + {"SH", 0x707F, 0x1023, DecodeSType<SH>}, + {"SW", 0x707F, 0x2023, DecodeSType<SW>}, + {"ADDI", 0x707F, 0x13, DecodeIType<ADDI>}, + {"SLTI", 0x707F, 0x2013, DecodeIType<SLTI>}, + {"SLTIU", 0x707F, 0x3013, DecodeIType<SLTIU>}, + {"XORI", 0x707F, 0x4013, DecodeIType<XORI>}, + {"ORI", 0x707F, 0x6013, DecodeIType<ORI>}, + {"ANDI", 0x707F, 0x7013, DecodeIType<ANDI>}, + {"SLLI", 0xF800707F, 0x1013, DecodeRShamtType<SLLI>}, + {"SRLI", 0xF800707F, 0x5013, DecodeRShamtType<SRLI>}, + {"SRAI", 0xF800707F, 0x40005013, DecodeRShamtType<SRAI>}, + {"ADD", 0xFE00707F, 0x33, DecodeRType<ADD>}, + {"SUB", 0xFE00707F, 0x40000033, DecodeRType<SUB>}, + {"SLL", 0xFE00707F, 0x1033, DecodeRType<SLL>}, + {"SLT", 0xFE00707F, 0x2033, DecodeRType<SLT>}, + {"SLTU", 0xFE00707F, 0x3033, DecodeRType<SLTU>}, + {"XOR", 0xFE00707F, 0x4033, DecodeRType<XOR>}, + {"SRL", 0xFE00707F, 0x5033, DecodeRType<SRL>}, + {"SRA", 0xFE00707F, 0x40005033, DecodeRType<SRA>}, + {"OR", 0xFE00707F, 0x6033, DecodeRType<OR>}, + {"AND", 0xFE00707F, 0x7033, DecodeRType<AND>}, + {"LWU", 0x707F, 0x6003, DecodeIType<LWU>}, + {"LD", 0x707F, 0x3003, DecodeIType<LD>}, + {"SD", 0x707F, 0x3023, DecodeSType<SD>}, + {"ADDIW", 0x707F, 0x1B, DecodeIType<ADDIW>}, + {"SLLIW", 0xFE00707F, 0x101B, DecodeRShamtType<SLLIW>}, + {"SRLIW", 0xFE00707F, 0x501B, DecodeRShamtType<SRLIW>}, + {"SRAIW", 0xFE00707F, 0x4000501B, DecodeRShamtType<SRAIW>}, + {"ADDW", 0xFE00707F, 0x3B, DecodeRType<ADDW>}, + {"SUBW", 0xFE00707F, 0x4000003B, DecodeRType<SUBW>}, + {"SLLW", 0xFE00707F, 0x103B, DecodeRType<SLLW>}, + {"SRLW", 0xFE00707F, 0x503B, DecodeRType<SRLW>}, + {"SRAW", 0xFE00707F, 0x4000503B, DecodeRType<SRAW>}, - // inst must be BNE to exit - if (!value || !IsBNE(value.value())) - return false; - auto exit_pc = current_pc + SextW(DecodeBImm(value.value())); - value = ReadMem<uint32_t>(emulator, current_pc += 4).value(); + // RV32M & RV64M (The integer multiplication and division extension) // + {"MUL", 0xFE00707F, 0x2000033, DecodeRType<MUL>}, + {"MULH", 0xFE00707F, 0x2001033, DecodeRType<MULH>}, + {"MULHSU", 0xFE00707F, 0x2002033, DecodeRType<MULHSU>}, + {"MULHU", 0xFE00707F, 0x2003033, DecodeRType<MULHU>}, + {"DIV", 0xFE00707F, 0x2004033, DecodeRType<DIV>}, + {"DIVU", 0xFE00707F, 0x2005033, DecodeRType<DIVU>}, + {"REM", 0xFE00707F, 0x2006033, DecodeRType<REM>}, + {"REMU", 0xFE00707F, 0x2007033, DecodeRType<REMU>}, + {"MULW", 0xFE00707F, 0x200003B, DecodeRType<MULW>}, + {"DIVW", 0xFE00707F, 0x200403B, DecodeRType<DIVW>}, + {"DIVUW", 0xFE00707F, 0x200503B, DecodeRType<DIVUW>}, + {"REMW", 0xFE00707F, 0x200603B, DecodeRType<REMW>}, + {"REMUW", 0xFE00707F, 0x200703B, DecodeRType<REMUW>}, - // inst must be SC - if (!value || !IsSC(value.value())) - return false; - value = ReadMem<uint32_t>(emulator, current_pc += 4).value(); + // RV32A & RV64A (The standard atomic instruction extension) // + {"LR_W", 0xF9F0707F, 0x1000202F, DecodeRRS1Type<LR_W>}, + {"LR_D", 0xF9F0707F, 0x1000302F, DecodeRRS1Type<LR_D>}, + {"SC_W", 0xF800707F, 0x1800202F, DecodeRType<SC_W>}, + {"SC_D", 0xF800707F, 0x1800302F, DecodeRType<SC_D>}, + {"AMOSWAP_W", 0xF800707F, 0x800202F, DecodeRType<AMOSWAP_W>}, + {"AMOADD_W", 0xF800707F, 0x202F, DecodeRType<AMOADD_W>}, + {"AMOXOR_W", 0xF800707F, 0x2000202F, DecodeRType<AMOXOR_W>}, + {"AMOAND_W", 0xF800707F, 0x6000202F, DecodeRType<AMOAND_W>}, + {"AMOOR_W", 0xF800707F, 0x4000202F, DecodeRType<AMOOR_W>}, + {"AMOMIN_W", 0xF800707F, 0x8000202F, DecodeRType<AMOMIN_W>}, + {"AMOMAX_W", 0xF800707F, 0xA000202F, DecodeRType<AMOMAX_W>}, + {"AMOMINU_W", 0xF800707F, 0xC000202F, DecodeRType<AMOMINU_W>}, + {"AMOMAXU_W", 0xF800707F, 0xE000202F, DecodeRType<AMOMAXU_W>}, + {"AMOSWAP_D", 0xF800707F, 0x800302F, DecodeRType<AMOSWAP_D>}, + {"AMOADD_D", 0xF800707F, 0x302F, DecodeRType<AMOADD_D>}, + {"AMOXOR_D", 0xF800707F, 0x2000302F, DecodeRType<AMOXOR_D>}, + {"AMOAND_D", 0xF800707F, 0x6000302F, DecodeRType<AMOAND_D>}, + {"AMOOR_D", 0xF800707F, 0x4000302F, DecodeRType<AMOOR_D>}, + {"AMOMIN_D", 0xF800707F, 0x8000302F, DecodeRType<AMOMIN_D>}, + {"AMOMAX_D", 0xF800707F, 0xA000302F, DecodeRType<AMOMAX_D>}, + {"AMOMINU_D", 0xF800707F, 0xC000302F, DecodeRType<AMOMINU_D>}, + {"AMOMAXU_D", 0xF800707F, 0xE000302F, DecodeRType<AMOMAXU_D>}, +}; - // 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<DecodeResult> 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<uint32_t>(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<uint32_t>(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<uint32_t>( - 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<LB, uint8_t, int8_t>(m_emu, inst, SextW); + } + bool operator()(LH inst) { + return Load<LH, uint16_t, int16_t>(m_emu, inst, SextW); + } + bool operator()(LW inst) { + return Load<LW, uint32_t, int32_t>(m_emu, inst, SextW); + } + bool operator()(LBU inst) { + return Load<LBU, uint8_t, uint8_t>(m_emu, inst, ZextD); + } + bool operator()(LHU inst) { + return Load<LHU, uint16_t, uint16_t>(m_emu, inst, ZextD); + } + bool operator()(SB inst) { return Store<SB, uint8_t>(m_emu, inst); } + bool operator()(SH inst) { return Store<SH, uint16_t>(m_emu, inst); } + bool operator()(SW inst) { return Store<SW, uint32_t>(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<LWU, uint32_t, uint32_t>(m_emu, inst, ZextD); + } + bool operator()(LD inst) { + return Load<LD, uint64_t, uint64_t>(m_emu, inst, ZextD); + } + bool operator()(SD inst) { return Store<SD, uint64_t>(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<uint32_t>( - 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<uint32_t>( - 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<uint32_t>( - 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<uint32_t>( - 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<uint32_t>( - 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<uint32_t>( - 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<uint64_t>(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<uint64_t>(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<uint64_t>( - 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<uint64_t>( - 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<uint64_t>( - 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<uint64_t>( - 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<uint64_t>( - 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<uint64_t>( - 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<uint64_t>( - 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<CMP>", 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<AMOSWAP_W, uint32_t>(m_emu, inst, 4, SextW); + } + bool operator()(AMOADD_W inst) { + return AtomicADD<AMOADD_W, uint32_t>(m_emu, inst, 4, SextW); + } + bool operator()(AMOXOR_W inst) { + return AtomicBitOperate<AMOXOR_W, uint32_t>( + m_emu, inst, 4, SextW, [](uint32_t a, uint32_t b) { return a ^ b; }); + } + bool operator()(AMOAND_W inst) { + return AtomicBitOperate<AMOAND_W, uint32_t>( + m_emu, inst, 4, SextW, [](uint32_t a, uint32_t b) { return a & b; }); + } + bool operator()(AMOOR_W inst) { + return AtomicBitOperate<AMOOR_W, uint32_t>( + m_emu, inst, 4, SextW, [](uint32_t a, uint32_t b) { return a | b; }); + } + bool operator()(AMOMIN_W inst) { + return AtomicCmp<AMOMIN_W, uint32_t>( + 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<AMOMAX_W, uint32_t>( + 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<AMOMINU_W, uint32_t>( + m_emu, inst, 4, SextW, + [](uint32_t a, uint32_t b) { return std::min(a, b); }); + } + bool operator()(AMOMAXU_W inst) { + return AtomicCmp<AMOMAXU_W, uint32_t>( + m_emu, inst, 4, SextW, + [](uint32_t a, uint32_t b) { return std::max(a, b); }); + } + bool operator()(AMOSWAP_D inst) { + return AtomicSwap<AMOSWAP_D, uint64_t>(m_emu, inst, 8, ZextD); + } + bool operator()(AMOADD_D inst) { + return AtomicADD<AMOADD_D, uint64_t>(m_emu, inst, 8, ZextD); } + bool operator()(AMOXOR_D inst) { + return AtomicBitOperate<AMOXOR_D, uint64_t>( + m_emu, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return a ^ b; }); + } + bool operator()(AMOAND_D inst) { + return AtomicBitOperate<AMOAND_D, uint64_t>( + m_emu, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return a & b; }); + } + bool operator()(AMOOR_D inst) { + return AtomicBitOperate<AMOOR_D, uint64_t>( + m_emu, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return a | b; }); + } + bool operator()(AMOMIN_D inst) { + return AtomicCmp<AMOMIN_D, uint64_t>( + 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<AMOMAX_D, uint64_t>( + 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<AMOMINU_D, uint64_t>( + m_emu, inst, 8, ZextD, + [](uint64_t a, uint64_t b) { return std::min(a, b); }); + } + bool operator()(AMOMAXU_D inst) { + return AtomicCmp<AMOMAXU_D, uint64_t>( + 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<DecodeResult> +EmulateInstructionRISCV::ReadInstructionAt(lldb::addr_t addr) { + return ReadMem<uint32_t>(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<lldb::addr_t> EmulateInstructionRISCV::ReadPC() { + bool success = false; + auto addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, + LLDB_INVALID_ADDRESS, &success); + return success ? llvm::Optional<lldb::addr_t>(addr) : llvm::None; } bool EmulateInstructionRISCV::WritePC(lldb::addr_t pc) { diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h index 810d4b40bd056..19b4755c2450f 100644 --- 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 @@ class EmulateInstructionRISCV : public EmulateInstruction { llvm::Optional<RegisterInfo> GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num) override; - lldb::addr_t ReadPC(bool &success); + llvm::Optional<lldb::addr_t> ReadPC(); bool WritePC(lldb::addr_t pc); - const InstrPattern *Decode(uint32_t inst); - bool DecodeAndExecute(uint32_t inst, bool ignore_cond); + llvm::Optional<DecodeResult> ReadInstructionAt(lldb::addr_t addr); + llvm::Optional<DecodeResult> Decode(uint32_t inst); + bool Execute(DecodeResult inst, bool ignore_cond); template <typename T> - static std::enable_if_t<std::is_integral_v<T>, T> - ReadMem(EmulateInstructionRISCV &emulator, uint64_t addr, bool *success) { - + std::enable_if_t<std::is_integral_v<T>, llvm::Optional<T>> + 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 <typename T> - static bool WriteMem(EmulateInstructionRISCV &emulator, uint64_t addr, - RegisterValue value) { + template <typename T> 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/RISCVInstructions.h b/lldb/source/Plugins/Instruction/RISCV/RISCVInstructions.h new file mode 100644 index 0000000000000..380dcefeecfd2 --- /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 <cstdint> +#include <variant> + +#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<uint64_t> Read(EmulateInstructionRISCV &emulator); + llvm::Optional<int32_t> ReadI32(EmulateInstructionRISCV &emulator); + llvm::Optional<int64_t> ReadI64(EmulateInstructionRISCV &emulator); + llvm::Optional<uint32_t> 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<LUI, AUIPC, JAL, JALR, B, LB, LH, LW, LBU, LHU, SB, SH, SW, + ADDI, SLTI, SLTIU, XORI, ORI, ANDI, ADD, SUB, SLL, SLT, SLTU, + XOR, SRL, SRA, OR, AND, LWU, LD, SD, SLLI, SRLI, SRAI, ADDIW, + SLLIW, SRLIW, SRAIW, ADDW, SUBW, SLLW, SRLW, SRAW, MUL, MULH, + MULHSU, MULHU, DIV, DIVU, REM, REMU, MULW, DIVW, DIVUW, REMW, + REMUW, LR_W, SC_W, AMOSWAP_W, AMOADD_W, AMOXOR_W, AMOAND_W, + AMOOR_W, AMOMIN_W, AMOMAX_W, AMOMINU_W, AMOMAXU_W, LR_D, SC_D, + AMOSWAP_D, AMOADD_D, AMOXOR_D, AMOAND_D, AMOOR_D, AMOMIN_D, + AMOMAX_D, AMOMINU_D, AMOMAXU_D>; + +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 index 2d0f40e31fcb4..5645bae8016bb 100644 --- a/lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp +++ b/lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp @@ -75,6 +75,12 @@ struct RISCVEmulatorTester : public EmulateInstructionRISCV, testing::Test { 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 @@ TEST_F(RISCVEmulatorTester, testJAL) { 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 @@ constexpr uint32_t EncodeIType(uint32_t opcode, uint32_t funct3, uint32_t rd, 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 @@ TEST_F(RISCVEmulatorTester, testJALR) { 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 @@ void testBranch(RISCVEmulatorTester *tester, EncoderB encoder, bool branched, // b<cmp> 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 @@ void CheckRD(RISCVEmulatorTester *tester, uint64_t rd, uint64_t value) { template <typename T> void CheckMem(RISCVEmulatorTester *tester, uint64_t addr, uint64_t value) { - bool success = false; - ASSERT_EQ(tester->ReadMem<T>(*tester, addr, &success), value); - ASSERT_TRUE(success); + auto mem = tester->ReadMem<T>(addr); + ASSERT_TRUE(mem.has_value()); + ASSERT_EQ(*mem, value); } using RS1 = uint64_t; @@ -195,13 +194,13 @@ using RS2 = uint64_t; using PC = uint64_t; using RDComputer = std::function<uint64_t(RS1, RS2, PC)>; -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 @@ void TestInst(RISCVEmulatorTester *tester, uint64_t inst, bool has_rs2, 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 @@ void TestInst(RISCVEmulatorTester *tester, uint64_t inst, bool has_rs2, } } - 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 @@ void TestAtomic(RISCVEmulatorTester *tester, uint64_t inst, T rs1_val, tester->gpr.gpr[rs2] = rs2_val; // Write and check rs1_val in atomic_addr - ASSERT_TRUE( - tester->WriteMem<T>(*tester, atomic_addr, RegisterValue(rs1_val))); + ASSERT_TRUE(tester->WriteMem<T>(atomic_addr, rs1_val)); CheckMem<T>(tester, atomic_addr, rs1_val); ASSERT_TRUE(tester->DecodeAndExecute(inst, false)); @@ -295,10 +293,8 @@ TEST_F(RISCVEmulatorTester, TestDecodeAndExcute) { // 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 @@ TEST_F(RISCVEmulatorTester, TestDecodeAndExcute) { [](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); } } _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits