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 @@ -16,6 +16,22 @@ 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"; } @@ -32,6 +48,8 @@ case eInstructionTypePrologueEpilogue: case eInstructionTypeAll: return false; + default: + llvm_unreachable("Unhandled instruction type"); } llvm_unreachable("Fully covered switch above!"); } @@ -64,6 +82,8 @@ lldb::addr_t ReadPC(bool *success); bool WritePC(lldb::addr_t pc); + + const InstrPattern *Decode(uint32_t inst); bool DecodeAndExecute(uint32_t inst, bool ignore_cond); }; 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 @@ -32,12 +32,6 @@ namespace lldb_private { -// Masks for detecting instructions types. According to riscv-spec Chap 26. -constexpr uint32_t I_MASK = 0b111000001111111; -constexpr uint32_t J_MASK = 0b000000001111111; -// no funct3 in the b-mask because the logic executing B is quite similar. -constexpr uint32_t B_MASK = 0b000000001111111; - // 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. @@ -48,13 +42,26 @@ constexpr uint32_t BLTU = 0b110; constexpr uint32_t BGEU = 0b111; -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; } +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); } +// used in executor +template +constexpr std::enable_if_t SextW(T value) { + return uint64_t(int64_t(int32_t(value))); +} + +// used in executor +template constexpr uint64_t ZextD(T value) { + return uint64_t(value); +} + constexpr uint32_t DecodeJImm(uint32_t inst) { return (uint64_t(int64_t(int32_t(inst & 0x80000000)) >> 11)) // imm[20] | (inst & 0xff000) // imm[19:12] @@ -73,6 +80,15 @@ | ((inst >> 7) & 0x1e); // imm[4:1] } +constexpr uint32_t DecodeSImm(uint32_t inst) { + return (uint64_t(int64_t(int32_t(inst & 0xFE00000)) >> 20)) // imm[11:5] + | ((inst & 0xF80) >> 7); // imm[4:0] +} + +constexpr uint32_t DecodeUImm(uint32_t inst) { + return SextW(inst & 0xFFFFF000); // imm[31:12] +} + static uint32_t GPREncodingToLLDB(uint32_t reg_encode) { if (reg_encode == 0) return gpr_x0_riscv; @@ -81,43 +97,43 @@ return LLDB_INVALID_REGNUM; } -static bool ReadRegister(EmulateInstructionRISCV *emulator, uint32_t reg_encode, +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); + return emulator.ReadRegister(eRegisterKindLLDB, lldb_reg, value); } -static bool WriteRegister(EmulateInstructionRISCV *emulator, +static bool WriteRegister(EmulateInstructionRISCV &emulator, uint32_t reg_encode, const RegisterValue &value) { uint32_t lldb_reg = GPREncodingToLLDB(reg_encode); EmulateInstruction::Context ctx; ctx.type = EmulateInstruction::eContextRegisterStore; ctx.SetNoArgs(); - return emulator->WriteRegister(ctx, eRegisterKindLLDB, lldb_reg, value); + return emulator.WriteRegister(ctx, eRegisterKindLLDB, lldb_reg, value); } -static bool ExecJAL(EmulateInstructionRISCV *emulator, uint32_t inst, bool) { +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) && + int64_t pc = emulator.ReadPC(&success); + return success && emulator.WritePC(pc + offset) && WriteRegister(emulator, DecodeRD(inst), RegisterValue(uint64_t(pc + 4))); } -static bool ExecJALR(EmulateInstructionRISCV *emulator, uint32_t inst, bool) { +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 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) && + return emulator.WritePC((rs1 + offset) & ~1) && WriteRegister(emulator, DecodeRD(inst), RegisterValue(uint64_t(pc + 4))); } @@ -141,17 +157,17 @@ } } -static bool ExecB(EmulateInstructionRISCV *emulator, uint32_t inst, +static bool ExecB(EmulateInstructionRISCV &emulator, uint32_t inst, bool ignore_cond) { bool success = false; - uint64_t pc = emulator->ReadPC(&success); + uint64_t pc = emulator.ReadPC(&success); if (!success) return false; uint64_t offset = SignExt(DecodeBImm(inst)); uint64_t target = pc + offset; if (ignore_cond) - return emulator->WritePC(target); + return emulator.WritePC(target); RegisterValue value1; RegisterValue value2; @@ -161,43 +177,563 @@ uint32_t funct3 = DecodeFunct3(inst); if (CompareB(value1.GetAsUInt64(), value2.GetAsUInt64(), funct3)) - return emulator->WritePC(target); + return emulator.WritePC(target); return true; } -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); -}; +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, T> +ReadMem(EmulateInstructionRISCV &emulator, uint64_t addr, bool *success) { + + EmulateInstructionRISCV::Context ctx; + ctx.type = EmulateInstruction::eContextRegisterLoad; + ctx.SetNoArgs(); + return T(emulator.ReadMemoryUnsigned(ctx, addr, sizeof(T), T(), success)); +} + +template +static bool WriteMem(EmulateInstructionRISCV &emulator, uint64_t addr, + RegisterValue value) { + EmulateInstructionRISCV::Context ctx; + ctx.type = EmulateInstruction::eContextRegisterStore; + ctx.SetNoArgs(); + return emulator.WriteMemoryUnsigned(ctx, addr, value.GetAsUInt64(), + sizeof(T)); +} + +static uint64_t LoadStoreAddr(EmulateInstructionRISCV &emulator, + uint32_t inst) { + auto rs1 = DecodeRS1(inst); + int32_t imm = SignExt(DecodeSImm(inst)); + RegisterValue value; + if (!ReadRegister(emulator, rs1, value)) + return LLDB_INVALID_ADDRESS; + uint64_t addr = value.GetAsUInt64() + 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; + bool success = false; + E value = E(ReadMem(emulator, addr, &success)); + if (!success) + return false; + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value))); +} + +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); +} + +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) { + auto rs1 = DecodeRS1(inst); + int32_t imm = SignExt(DecodeIImm(inst)); + + RegisterValue value; + if (!ReadRegister(emulator, rs1, value)) + return false; + + uint64_t result = int64_t(value.GetAsUInt64()) + int64_t(imm); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSLTI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + int32_t imm = SignExt(DecodeIImm(inst)); + + RegisterValue value; + if (!ReadRegister(emulator, rs1, value)) + return false; + + uint64_t result = int64_t(value.GetAsUInt64()) < int64_t(imm); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSLTIU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + int32_t imm = SignExt(DecodeIImm(inst)); + + RegisterValue value; + if (!ReadRegister(emulator, rs1, value)) + return false; + + uint64_t result = value.GetAsUInt64() < uint64_t(imm); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecXORI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + int32_t imm = SignExt(DecodeIImm(inst)); + + RegisterValue value; + if (!ReadRegister(emulator, rs1, value)) + return false; + + uint64_t result = value.GetAsUInt64() ^ uint64_t(imm); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecORI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + int32_t imm = SignExt(DecodeIImm(inst)); + + RegisterValue value; + if (!ReadRegister(emulator, rs1, value)) + return false; + + uint64_t result = value.GetAsUInt64() | uint64_t(imm); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecANDI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + int32_t imm = SignExt(DecodeIImm(inst)); + + RegisterValue value; + if (!ReadRegister(emulator, rs1, value)) + return false; + + uint64_t result = value.GetAsUInt64() & uint64_t(imm); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSLLI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto shamt = DecodeSHAMT7(inst); + + RegisterValue value; + if (!ReadRegister(emulator, rs1, value)) + return false; + + uint64_t result = value.GetAsUInt64() << shamt; + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSRLI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto shamt = DecodeSHAMT7(inst); + + RegisterValue value; + if (!ReadRegister(emulator, rs1, value)) + return false; + + uint64_t result = value.GetAsUInt64() >> shamt; + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSRAI(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto shamt = DecodeSHAMT7(inst); + + RegisterValue value; + if (!ReadRegister(emulator, rs1, value)) + return false; + + uint64_t result = int64_t(value.GetAsUInt64()) >> shamt; + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecADD(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; -static InstrPattern PATTERNS[] = { - {"JAL", J_MASK, 0b1101111, ExecJAL}, - {"JALR", I_MASK, 0b000000001100111, ExecJALR}, - {"B", B_MASK, 0b1100011, ExecB}, - // TODO: {LR/SC}.{W/D} and ECALL + uint64_t result = value1.GetAsUInt64() + value2.GetAsUInt64(); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSUB(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = value1.GetAsUInt64() - value2.GetAsUInt64(); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSLL(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = value1.GetAsUInt64() << (value2.GetAsUInt64() & 0b111111); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSLT(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = int64_t(value1.GetAsUInt64()) < int64_t(value2.GetAsUInt64()); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSLTU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = value1.GetAsUInt64() < value2.GetAsUInt64(); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecXOR(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = value1.GetAsUInt64() ^ value2.GetAsUInt64(); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSRL(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = value1.GetAsUInt64() >> (value2.GetAsUInt64() & 0b111111); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSRA(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = + int64_t(value1.GetAsUInt64()) >> (value2.GetAsUInt64() & 0b111111); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecOR(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = value1.GetAsUInt64() | value2.GetAsUInt64(); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecAND(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = value1.GetAsUInt64() & value2.GetAsUInt64(); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecADDIW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + int32_t imm = SignExt(DecodeIImm(inst)); + + RegisterValue value1; + if (!ReadRegister(emulator, rs1, value1)) + return false; + + uint64_t result = SextW(int32_t(value1.GetAsUInt64()) + imm); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSLLIW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto shamt = DecodeSHAMT5(inst); + + RegisterValue value1; + if (!ReadRegister(emulator, rs1, value1)) + return false; + + uint64_t result = SextW(uint32_t(value1.GetAsUInt64()) << shamt); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSRLIW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto shamt = DecodeSHAMT5(inst); + + RegisterValue value1; + if (!ReadRegister(emulator, rs1, value1)) + return false; + + uint64_t result = SextW(uint32_t(value1.GetAsUInt64()) >> shamt); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSRAIW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto shamt = DecodeSHAMT5(inst); + + RegisterValue value1; + if (!ReadRegister(emulator, rs1, value1)) + return false; + + uint64_t result = SextW(int32_t(value1.GetAsUInt64()) >> shamt); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecADDW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = SextW(value1.GetAsUInt32() + value2.GetAsUInt32()); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSUBW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = SextW(value1.GetAsUInt32() - value2.GetAsUInt32()); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSLLW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = SextW(uint32_t(value1.GetAsUInt64()) + << (value2.GetAsUInt64() & 0b111111)); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSRLW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = SextW(uint32_t(value1.GetAsUInt64()) >> + (value2.GetAsUInt64() & 0b111111)); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecSRAW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t result = + SextW(int32_t(value1.GetAsUInt64()) >> (value2.GetAsUInt64() & 0b111111)); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static const InstrPattern PATTERNS[] = { + {"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}, }; -/// This function only determines the next instruction address for software -/// sigle stepping by emulating branching instructions including: -/// - from Base Instruction Set : JAL, JALR, B, ECALL -/// - from Atomic Instruction Set: LR -> BNE -> SC -> BNE -/// We will get rid of this tedious code when the riscv debug spec is ratified. -bool EmulateInstructionRISCV::DecodeAndExecute(uint32_t inst, - bool ignore_cond) { - Log *log = GetLog(LLDBLog::Process | LLDBLog::Breakpoints); +const InstrPattern *EmulateInstructionRISCV::Decode(uint32_t inst) { 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); - return pat.exec(this, inst, ignore_cond); + return &pat; } } + return nullptr; +} + +/// This function only determines the next instruction address for software +/// sigle 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); + } LLDB_LOGF(log, "EmulateInstructionRISCV::%s: inst(0x%x) does not branch: " @@ -213,7 +749,7 @@ bool ignore_cond = options & eEmulateInstructionOptionIgnoreConditions; bool success = false; - lldb::addr_t old_pc = 0; + lldb::addr_t old_pc = LLDB_INVALID_ADDRESS; if (increase_pc) { old_pc = ReadPC(&success); if (!success) @@ -253,15 +789,14 @@ 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); + 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) { + if (try_rvc != 0 && mask != 3) m_opcode.SetOpcode16(try_rvc, GetByteOrder()); - } else { + else m_opcode.SetOpcode32(inst, GetByteOrder()); - } return true; } 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 @@ -157,6 +157,37 @@ testBranch(this, name, false, rs1, rs2_continued); \ } +void CheckRD(RISCVEmulatorTester *tester, uint64_t rd, uint64_t value) { + ASSERT_EQ(tester->gpr.gpr[rd], value); +} + +using RS1 = uint64_t; +using RS2 = uint64_t; +using PC = uint64_t; +using RDComputer = std::function; + +void TestInst(RISCVEmulatorTester *tester, uint64_t inst, bool has_rs2, + RDComputer rd_val) { + + lldb::addr_t old_pc = 0x114514; + tester->WritePC(old_pc); + auto rd = DecodeRD(inst); + auto rs1 = DecodeRS1(inst); + auto rs2 = 0; + if (rs1) + tester->gpr.gpr[rs1] = 0x1919; + + if (has_rs2) { + rs2 = DecodeRS2(inst); + if (rs2) + tester->gpr.gpr[rs2] = 0x8181; + } + + ASSERT_TRUE(tester->DecodeAndExecute(inst, false)); + CheckRD(tester, rd, + rd_val(tester->gpr.gpr[rs1], rs2 ? tester->gpr.gpr[rs2] : 0, old_pc)); +} + // GEN_BRANCH_TEST(opcode, imm1, imm2, imm3): // It should branch for instruction `opcode imm1, imm2` // It should do nothing for instruction `opcode imm1, imm3` @@ -167,30 +198,34 @@ GEN_BRANCH_TEST(BLTU, -2, -1, 1) GEN_BRANCH_TEST(BGEU, -2, 1, -1) -void testNothing(RISCVEmulatorTester *tester, uint32_t inst) { - lldb::addr_t old_pc = 0x114514; - tester->WritePC(old_pc); - tester->SetInstruction(Opcode(inst, tester->GetByteOrder()), - LLDB_INVALID_ADDRESS, nullptr); - ASSERT_TRUE(tester->EvaluateInstruction(0)); - bool success = false; - auto pc = tester->ReadPC(&success); - ASSERT_TRUE(success); - ASSERT_EQ(pc, old_pc); - ASSERT_TRUE( - tester->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC)); - pc = tester->ReadPC(&success); - ASSERT_TRUE(success); - ASSERT_EQ(pc, old_pc + 4); -} - -#define GEN_NOTHING_TEST(name, inst) \ - TEST_F(RISCVEmulatorTester, testDoNothing_##name) { testNothing(this, inst); } +struct TestData { + uint32_t inst; + std::string name; + bool has_rs2; + RDComputer rd_val; +}; -// GEN_NOTHING_TEST(name, inst): -// It should do nothing (except increasing pc) for instruction `inst` -GEN_NOTHING_TEST(mv, 0x01813083) // mv a0, a5 -GEN_NOTHING_TEST(li, 0x00078513) // li a5, 0 -GEN_NOTHING_TEST(sd, 0x02010413) // sd s0, sp(16) -GEN_NOTHING_TEST(lw, 0x0007879b) // lw a5, s0(-20) -GEN_NOTHING_TEST(addi, 0x00113423) // addi sp, sp, -16 +TEST_F(RISCVEmulatorTester, TestDecodeAndExcute) { + + std::vector tests = { + {0x00010113, "ADDI", false, [](RS1 rs1, RS2, PC) { return rs1 + 0; }}, + {0x00023517, "AUIPC", false, [](RS1, RS2, PC pc) { return pc + 143360; }}, + {0x0006079b, "ADDIW", false, [](RS1 rs1, RS2, PC) { return rs1 + 0; }}, + {0x00110837, "LUI", false, [](RS1, RS2, PC pc) { return 1114112; }}, + {0x00147513, "ANDI", false, [](RS1 rs1, RS2, PC) { return rs1 & 1; }}, + {0x00153513, "SLTIU", false, [](RS1 rs1, RS2, PC) { return rs1 != 0; }}, + {0x00256513, "ORI", false, [](RS1 rs1, RS2, PC) { return rs1 | 1; }}, + {0x00451a13, "SLLI", false, [](RS1 rs1, RS2, PC) { return rs1 << 4; }}, + {0x00455693, "SRLI", false, [](RS1 rs1, RS2, PC) { return rs1 >> 4; }}, + {0x00a035b3, "SLTU", true, [](RS1 rs1, RS2 rs2, PC) { return rs2 != 0; }}, + {0x00b50633, "ADD", true, [](RS1 rs1, RS2 rs2, PC) { return rs1 + rs2; }}, + {0x40d507b3, "SUB", true, [](RS1 rs1, RS2 rs2, PC) { return rs1 - rs2; }}, + }; + for (auto i : tests) { + const InstrPattern *pattern = this->Decode(i.inst); + ASSERT_TRUE(pattern != nullptr); + std::string name = pattern->name; + ASSERT_EQ(name, i.name); + TestInst(this, i.inst, i.has_rs2, i.rd_val); + } +}