Index: lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h =================================================================== --- lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h +++ lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.h @@ -12,6 +12,7 @@ #include "lldb/Core/EmulateInstruction.h" #include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" #include "lldb/Utility/Status.h" namespace lldb_private { @@ -83,6 +84,26 @@ const InstrPattern *Decode(uint32_t inst); bool DecodeAndExecute(uint32_t inst, bool ignore_cond); + + template + static std::enable_if_t, T> + ReadMem(EmulateInstructionRISCV &emulator, uint64_t addr, bool *success) { + + EmulateInstructionRISCV::Context ctx; + ctx.type = EmulateInstruction::eContextRegisterLoad; + ctx.SetNoArgs(); + return T(emulator.ReadMemoryUnsigned(ctx, addr, sizeof(T), T(), success)); + } + + template + static bool WriteMem(EmulateInstructionRISCV &emulator, uint64_t addr, + RegisterValue value) { + EmulateInstructionRISCV::Context ctx; + ctx.type = EmulateInstruction::eContextRegisterStore; + ctx.SetNoArgs(); + return emulator.WriteMemoryUnsigned(ctx, addr, value.GetAsUInt64(), + sizeof(T)); + } }; } // namespace lldb_private Index: lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp =================================================================== --- lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp +++ lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp @@ -19,7 +19,6 @@ #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/LLDBLog.h" -#include "lldb/Utility/RegisterValue.h" #include "lldb/Utility/Stream.h" #include "llvm/ADT/STLExtras.h" @@ -253,6 +252,7 @@ return WriteMem(emulator, addr, value); } +// RV32I & RV64I static bool ExecLB(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { return Load(emulator, inst, SextW); } @@ -329,6 +329,13 @@ if (!ReadRegister(emulator, rs1, value)) return false; + if (imm == 1) { + if (value.GetAsUInt64() == 0) + return WriteRegister(emulator, DecodeRD(inst), + RegisterValue(uint64_t(1))); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(uint64_t(0))); + } + uint64_t result = value.GetAsUInt64() < uint64_t(imm); return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); } @@ -457,7 +464,8 @@ !ReadRegister(emulator, rs2, value2)) return false; - uint64_t result = int64_t(value1.GetAsUInt64()) < int64_t(value2.GetAsUInt64()); + uint64_t result = + int64_t(value1.GetAsUInt64()) < int64_t(value2.GetAsUInt64()); return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); } @@ -604,7 +612,8 @@ !ReadRegister(emulator, rs2, value2)) return false; - uint64_t result = SextW(value1.GetAsUInt32() + value2.GetAsUInt32()); + uint64_t result = + SextW(uint32_t(value1.GetAsUInt64() + value2.GetAsUInt64())); return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); } @@ -618,7 +627,8 @@ !ReadRegister(emulator, rs2, value2)) return false; - uint64_t result = SextW(value1.GetAsUInt32() - value2.GetAsUInt32()); + uint64_t result = + SextW(uint32_t(value1.GetAsUInt64() - value2.GetAsUInt64())); return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); } @@ -667,7 +677,510 @@ return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); } +// RV32M & RV64M +static bool ExecMUL(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 ExecMULH(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + // TODO: Implement this. + return false; +} + +static bool ExecMULHSU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + // TODO: Implement this. + return false; +} + +static bool ExecMULHU(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + // TODO: Implement this. + return false; +} + +static bool ExecDIV(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; + + int64_t dividend = value1.GetAsUInt64(); + int64_t divisor = value2.GetAsUInt64(); + + if (divisor == 0) + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(UINT64_MAX)); + + if (dividend == INT64_MIN && divisor == -1) + return WriteRegister(emulator, DecodeRD(inst), + RegisterValue(value1.GetAsUInt64())); + + return WriteRegister(emulator, DecodeRD(inst), + RegisterValue(uint64_t(dividend / divisor))); +} + +static bool ExecDIVU(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 dividend = value1.GetAsUInt64(); + uint64_t divisor = value2.GetAsUInt64(); + + 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 rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + int64_t dividend = value1.GetAsUInt64(); + int64_t divisor = value2.GetAsUInt64(); + + if (divisor == 0) + return WriteRegister(emulator, DecodeRD(inst), + RegisterValue(value1.GetAsUInt64())); + + 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 rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint64_t dividend = value1.GetAsUInt64(); + uint64_t divisor = value2.GetAsUInt64(); + + if (divisor == 0) + return WriteRegister(emulator, DecodeRD(inst), + RegisterValue(value1.GetAsUInt64())); + + return WriteRegister(emulator, DecodeRD(inst), + RegisterValue(dividend % divisor)); +} + +static bool ExecMULW(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(uint32_t(value1.GetAsUInt64() * value2.GetAsUInt64())); + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(result)); +} + +static bool ExecDIVW(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; + + int32_t dividend = value1.GetAsUInt64(); + int32_t divisor = value2.GetAsUInt64(); + + 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 rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint32_t dividend = value1.GetAsUInt64(); + uint32_t divisor = value2.GetAsUInt64(); + + 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 rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + int32_t dividend = value1.GetAsUInt64(); + int32_t divisor = value2.GetAsUInt64(); + + if (divisor == 0) + return WriteRegister(emulator, DecodeRD(inst), + RegisterValue(SextW(dividend))); + + if (dividend == INT32_MIN && divisor == -1) + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(uint64_t(0))); + + return WriteRegister(emulator, DecodeRD(inst), + RegisterValue(SextW(dividend % divisor))); +} + +static bool ExecREMUW(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + auto rs1 = DecodeRS1(inst); + auto rs2 = DecodeRS2(inst); + + RegisterValue value1; + RegisterValue value2; + if (!ReadRegister(emulator, rs1, value1) || + !ReadRegister(emulator, rs2, value2)) + return false; + + uint32_t dividend = value1.GetAsUInt64(); + uint32_t divisor = value2.GetAsUInt64(); + + if (divisor == 0) + return WriteRegister(emulator, DecodeRD(inst), + RegisterValue(SextW(dividend))); + + return WriteRegister(emulator, DecodeRD(inst), + RegisterValue(SextW(dividend % divisor))); +} + +// RV32A & RV64A +static uint64_t AtomicAddr(EmulateInstructionRISCV &emulator, uint32_t reg, + int align) { + RegisterValue value; + if (!ReadRegister(emulator, reg, value)) + return 0; + + uint64_t addr = value.GetAsUInt64(); + if (addr % align != 0) + return 0; + + return addr; +} + +template +static bool AtomicSwap(EmulateInstructionRISCV &emulator, uint32_t inst, + int align, uint64_t (*extend)(T)) { + RegisterValue rs2; + if (!ReadRegister(emulator, DecodeRS2(inst), rs2)) + return false; + + uint64_t addr = AtomicAddr(emulator, DecodeRS1(inst), align); + if (addr == 0) + return false; + + bool success = false; + T tmp = ReadMem(emulator, addr, &success); + if (!success) + return false; + + success = WriteMem(emulator, addr, RegisterValue(T(rs2.GetAsUInt64()))); + if (!success) + return false; + + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(tmp))); +} + +template +static bool AtomicADD(EmulateInstructionRISCV &emulator, uint32_t inst, + int align, uint64_t (*extend)(T)) { + RegisterValue rs2; + if (!ReadRegister(emulator, DecodeRS2(inst), rs2)) + return false; + + uint64_t addr = AtomicAddr(emulator, DecodeRS1(inst), align); + if (addr == 0) + return false; + + bool success = false; + T value = ReadMem(emulator, addr, &success); + if (!success) + return false; + + success = + WriteMem(emulator, addr, RegisterValue(value + T(rs2.GetAsUInt64()))); + if (!success) + return false; + + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value))); +} + +template +static bool AtomicBitOperate(EmulateInstructionRISCV &emulator, uint32_t inst, + int align, uint64_t (*extend)(T), + T (*operate)(T, T)) { + RegisterValue rs2; + if (!ReadRegister(emulator, DecodeRS2(inst), rs2)) + return false; + + uint64_t addr = AtomicAddr(emulator, DecodeRS1(inst), align); + if (addr == 0) + return false; + + bool success = false; + T value = ReadMem(emulator, addr, &success); + if (!success) + return false; + + success = WriteMem(emulator, addr, + RegisterValue(operate(value, T(rs2.GetAsUInt64())))); + if (!success) + return false; + + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value))); +} + +template +static bool AtomicCmp(EmulateInstructionRISCV &emulator, uint32_t inst, + int align, uint64_t (*extend)(T), T (*cmp)(T, T)) { + RegisterValue rs2; + if (!ReadRegister(emulator, DecodeRS2(inst), rs2)) + return false; + + uint64_t addr = AtomicAddr(emulator, DecodeRS1(inst), align); + if (addr == 0) + return false; + + bool success = false; + T value = ReadMem(emulator, addr, &success); + if (!success) + return false; + + success = WriteMem(emulator, addr, + RegisterValue(cmp(value, T(rs2.GetAsUInt64())))); + if (!success) + return false; + + return WriteRegister(emulator, DecodeRD(inst), RegisterValue(extend(value))); +} + +static bool IsLR(uint32_t inst) { + return (inst & 0xF9F0707F) == 0x1000202F || (inst & 0xF9F0707F) == 0x1000302F; +} + +static bool IsSC(uint32_t inst) { + return (inst & 0xF800707F) == 0x1800202F || (inst & 0xF800707F) == 0x1800302F; +} + +static bool IsBNE(uint32_t inst) { return (inst & 0x707F) == 0x1063; } + +static bool ExecAtomicSequence(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + bool success = false; + auto pc = emulator.ReadPC(&success); + auto start_pc = pc; + + // inst must be LR + if (!IsLR(inst) || !success) + return false; + inst = ReadMem(emulator, pc += 4, &success); + + // inst must be BNE to exit + if (!IsBNE(inst) || !success) + return false; + auto exit_pc = pc + SextW(DecodeBImm(inst)); + inst = ReadMem(emulator, pc += 4, &success); + + // inst must be SC + if (!IsSC(inst) || !success) + return false; + inst = ReadMem(emulator, pc += 4, &success); + + // inst must be BNE to restart + if (!IsBNE(inst) || !success) + return false; + if (pc + SextW(DecodeBImm(inst)) != start_pc) + return false; + pc += 4; + + if (exit_pc != pc) + return false; + + return emulator.WritePC(pc); +} + +static bool ExecLR_W(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + return ExecAtomicSequence(emulator, inst, false); +} + +static bool ExecAMOSWAP_W(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicSwap(emulator, inst, 4, SextW); +} + +static bool ExecAMOADD_W(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicADD(emulator, inst, 4, SextW); +} + +static bool ExecAMOXOR_W(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicBitOperate( + emulator, inst, 4, SextW, [](uint32_t a, uint32_t b) { return a ^ b; }); +} + +static bool ExecAMOAND_W(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicBitOperate( + emulator, inst, 4, SextW, [](uint32_t a, uint32_t b) { return a & b; }); +} + +static bool ExecAMOOR_W(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicBitOperate( + emulator, inst, 4, SextW, [](uint32_t a, uint32_t b) { return a | b; }); +} + +static bool ExecAMOMIN_W(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicCmp( + emulator, inst, 4, SextW, [](uint32_t a, uint32_t b) { + return uint32_t(std::min(int32_t(a), int32_t(b))); + }); +} + +static bool ExecAMOMAX_W(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicCmp( + emulator, inst, 4, SextW, [](uint32_t a, uint32_t b) { + return uint32_t(std::max(int32_t(a), int32_t(b))); + }); +} + +static bool ExecAMOMINU_W(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicCmp( + emulator, inst, 4, SextW, + [](uint32_t a, uint32_t b) { return std::min(a, b); }); +} + +static bool ExecAMOMAXU_W(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicCmp( + emulator, inst, 4, SextW, + [](uint32_t a, uint32_t b) { return std::max(a, b); }); +} + +static bool ExecLR_D(EmulateInstructionRISCV &emulator, uint32_t inst, bool) { + return ExecAtomicSequence(emulator, inst, false); +} + +static bool ExecAMOSWAP_D(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicSwap(emulator, inst, 8, ZextD); +} + +static bool ExecAMOADD_D(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicADD(emulator, inst, 8, ZextD); +} + +static bool ExecAMOXOR_D(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicBitOperate( + emulator, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return a ^ b; }); +} + +static bool ExecAMOAND_D(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicBitOperate( + emulator, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return a & b; }); +} + +static bool ExecAMOOR_D(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicBitOperate( + emulator, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return a | b; }); +} + +static bool ExecAMOMIN_D(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicCmp( + emulator, inst, 8, ZextD, [](uint64_t a, uint64_t b) { + return uint64_t(std::min(int64_t(a), int64_t(b))); + }); +} + +static bool ExecAMOMAX_D(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicCmp( + emulator, inst, 8, ZextD, [](uint64_t a, uint64_t b) { + return uint64_t(std::max(int64_t(a), int64_t(b))); + }); +} + +static bool ExecAMOMINU_D(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicCmp( + emulator, inst, 8, ZextD, + [](uint64_t a, uint64_t b) { return std::min(a, b); }); +} + +static bool ExecAMOMAXU_D(EmulateInstructionRISCV &emulator, uint32_t inst, + bool) { + return AtomicCmp( + emulator, inst, 8, ZextD, + [](uint64_t a, uint64_t b) { return std::max(a, b); }); +} + static const InstrPattern PATTERNS[] = { + // RV32I & RV64I {"LUI", 0x7F, 0x37, ExecLUI}, {"AUIPC", 0x7F, 0x17, ExecAUIPC}, {"JAL", 0x7F, 0x6F, ExecJAL}, @@ -712,13 +1225,49 @@ {"SLLW", 0xFE00707F, 0x103B, ExecSLLW}, {"SRLW", 0xFE00707F, 0x503B, ExecSRLW}, {"SRAW", 0xFE00707F, 0x4000503B, ExecSRAW}, + + // RV32M & RV64M + {"MUL", 0xFE00707F, 0x2000033, ExecMUL}, + {"MULH", 0xFE00707F, 0x2001033, ExecMULH}, + {"MULHSU", 0xFE00707F, 0x2002033, ExecMULHSU}, + {"MULHU", 0xFE00707F, 0x2003033, ExecMULHU}, + {"DIV", 0xFE00707F, 0x2004033, ExecDIV}, + {"DIVU", 0xFE00707F, 0x2005033, ExecDIVU}, + {"REM", 0xFE00707F, 0x2006033, ExecREM}, + {"REMU", 0xFE00707F, 0x2007033, ExecREMU}, + {"MULW", 0xFE00707F, 0x200003B, ExecMULW}, + {"DIVW", 0xFE00707F, 0x200403B, ExecDIVW}, + {"DIVUW", 0xFE00707F, 0x200503B, ExecDIVUW}, + {"REMW", 0xFE00707F, 0x200603B, ExecREMW}, + {"REMUW", 0xFE00707F, 0x200703B, ExecREMUW}, + + // RV32A & RV64A + {"LR_W", 0xF9F0707F, 0x1000202F, ExecLR_W}, + {"AMOSWAP_W", 0xF800707F, 0x800202F, ExecAMOSWAP_W}, + {"AMOADD_W", 0xF800707F, 0x202F, ExecAMOADD_W}, + {"AMOXOR_W", 0xF800707F, 0x2000202F, ExecAMOXOR_W}, + {"AMOAND_W", 0xF800707F, 0x6000202F, ExecAMOAND_W}, + {"AMOOR_W", 0xF800707F, 0x4000202F, ExecAMOOR_W}, + {"AMOMIN_W", 0xF800707F, 0x8000202F, ExecAMOMIN_W}, + {"AMOMAX_W", 0xF800707F, 0xA000202F, ExecAMOMAX_W}, + {"AMOMINU_W", 0xF800707F, 0xC000202F, ExecAMOMINU_W}, + {"AMOMAXU_W", 0xF800707F, 0xE000202F, ExecAMOMAXU_W}, + {"LR_D", 0xF9F0707F, 0x1000302F, ExecLR_D}, + {"AMOSWAP_D", 0xF800707F, 0x800302F, ExecAMOSWAP_D}, + {"AMOADD_D", 0xF800707F, 0x302F, ExecAMOADD_D}, + {"AMOXOR_D", 0xF800707F, 0x2000302F, ExecAMOXOR_D}, + {"AMOAND_D", 0xF800707F, 0x6000302F, ExecAMOAND_D}, + {"AMOOR_D", 0xF800707F, 0x4000302F, ExecAMOOR_D}, + {"AMOMIN_D", 0xF800707F, 0x8000302F, ExecAMOMIN_D}, + {"AMOMAX_D", 0xF800707F, 0xA000302F, ExecAMOMAX_D}, + {"AMOMINU_D", 0xF800707F, 0xC000302F, ExecAMOMINU_D}, + {"AMOMAXU_D", 0xF800707F, 0xE000302F, ExecAMOMAXU_D}, }; const InstrPattern *EmulateInstructionRISCV::Decode(uint32_t inst) { for (const InstrPattern &pat : PATTERNS) { - if ((inst & pat.type_mask) == pat.eigen) { + if ((inst & pat.type_mask) == pat.eigen) return &pat; - } } return nullptr; } Index: lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp =================================================================== --- lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp +++ lldb/unittests/Instruction/RISCV/TestRISCVEmulator.cpp @@ -24,11 +24,14 @@ struct RISCVEmulatorTester : public EmulateInstructionRISCV, testing::Test { RegisterInfoPOSIX_riscv64::GPR gpr; + uint8_t memory[1024] = {0}; RISCVEmulatorTester() : EmulateInstructionRISCV(ArchSpec("riscv64-unknown-linux-gnu")) { EmulateInstruction::SetReadRegCallback(ReadRegisterCallback); EmulateInstruction::SetWriteRegCallback(WriteRegisterCallback); + EmulateInstruction::SetReadMemCallback(ReadMemoryCallback); + EmulateInstruction::SetWriteMemCallback(WriteMemoryCallback); } static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton, @@ -53,6 +56,29 @@ tester->gpr.gpr[reg] = reg_value.GetAsUInt64(); return true; } + + static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton, + const Context &context, lldb::addr_t addr, + void *dst, size_t length) { + RISCVEmulatorTester *tester = (RISCVEmulatorTester *)instruction; + if (addr + length >= sizeof(tester->memory)) + return 0; + + memcpy(dst, tester->memory + addr, length); + return length; + }; + + static size_t WriteMemoryCallback(EmulateInstruction *instruction, + void *baton, const Context &context, + lldb::addr_t addr, const void *dst, + size_t length) { + RISCVEmulatorTester *tester = (RISCVEmulatorTester *)instruction; + if (addr + length >= sizeof(tester->memory)) + return 0; + + memcpy(tester->memory + addr, dst, length); + return length; + }; }; TEST_F(RISCVEmulatorTester, testJAL) { @@ -161,6 +187,13 @@ ASSERT_EQ(tester->gpr.gpr[rd], value); } +template +void CheckMem(RISCVEmulatorTester *tester, uint64_t addr, uint64_t value) { + bool success = false; + ASSERT_EQ(tester->ReadMem(*tester, addr, &success), value); + ASSERT_TRUE(success); +} + using RS1 = uint64_t; using RS2 = uint64_t; using PC = uint64_t; @@ -171,21 +204,62 @@ lldb::addr_t old_pc = 0x114514; tester->WritePC(old_pc); - auto rd = DecodeRD(inst); - auto rs1 = DecodeRS1(inst); - auto rs2 = 0; + uint32_t rd = DecodeRD(inst); + uint32_t rs1 = DecodeRS1(inst); + uint32_t rs2 = 0; + + uint64_t rs1_val = 0x19; + uint64_t rs2_val = 0x81; + if (rs1) - tester->gpr.gpr[rs1] = 0x1919; + tester->gpr.gpr[rs1] = rs1_val; if (has_rs2) { rs2 = DecodeRS2(inst); - if (rs2) - tester->gpr.gpr[rs2] = 0x8181; + if (rs2) { + if (rs1 == rs2) + rs2_val = rs1_val; + tester->gpr.gpr[rs2] = rs2_val; + } } ASSERT_TRUE(tester->DecodeAndExecute(inst, false)); - CheckRD(tester, rd, - rd_val(tester->gpr.gpr[rs1], rs2 ? tester->gpr.gpr[rs2] : 0, old_pc)); + CheckRD(tester, rd, rd_val(rs1_val, rs2 ? rs2_val : 0, old_pc)); +} + +template +void TestAtomic(RISCVEmulatorTester *tester, uint64_t inst, T rs1_val, + T rs2_val, T rd_expected, T mem_expected) { + // Atomic inst must have rs1 and rs2 + + uint32_t rd = DecodeRD(inst); + uint32_t rs1 = DecodeRS1(inst); + uint32_t rs2 = DecodeRS2(inst); + + // addr was stored in rs1 + uint64_t atomic_addr = 0x100; + + tester->gpr.gpr[rs1] = atomic_addr; + tester->gpr.gpr[rs2] = rs2_val; + + // Write and check rs1_val in atomic_addr + ASSERT_TRUE( + tester->WriteMem(*tester, atomic_addr, RegisterValue(rs1_val))); + CheckMem(tester, atomic_addr, rs1_val); + + ASSERT_TRUE(tester->DecodeAndExecute(inst, false)); + CheckRD(tester, rd, rd_expected); + CheckMem(tester, atomic_addr, mem_expected); +} + +TEST_F(RISCVEmulatorTester, TestAtomicSequence) { + this->WritePC(0x0); + *(uint64_t *)this->memory = 0x100427af; // lr.w a5,(s0) + *(uint64_t *)(this->memory + 4) = 0x00079663; // bnez a5,12 + *(uint64_t *)(this->memory + 8) = 0x1ce426af; // sc.w.aq a3,a4,(s0) + *(uint64_t *)(this->memory + 12) = 0xfe069ae3; // bnez a3,-12 + ASSERT_TRUE(this->DecodeAndExecute(*(uint32_t *)this->memory, false)); + ASSERT_EQ(this->gpr.gpr[0], uint64_t(16)); } // GEN_BRANCH_TEST(opcode, imm1, imm2, imm3): @@ -208,18 +282,38 @@ TEST_F(RISCVEmulatorTester, TestDecodeAndExcute) { std::vector tests = { + // RV32I & RV64I Tests {0x00010113, "ADDI", false, [](RS1 rs1, RS2, PC) { return rs1 + 0; }}, {0x00023517, "AUIPC", false, [](RS1, RS2, PC pc) { return pc + 143360; }}, {0x0006079b, "ADDIW", false, [](RS1 rs1, RS2, PC) { return rs1 + 0; }}, {0x00110837, "LUI", false, [](RS1, RS2, PC pc) { return 1114112; }}, {0x00147513, "ANDI", false, [](RS1 rs1, RS2, PC) { return rs1 & 1; }}, - {0x00153513, "SLTIU", false, [](RS1 rs1, RS2, PC) { return rs1 != 0; }}, - {0x00256513, "ORI", false, [](RS1 rs1, RS2, PC) { return rs1 | 1; }}, + {0x00153513, "SLTIU", false, [](RS1 rs1, RS2, PC) { return 0; }}, + {0x00256513, "ORI", false, [](RS1 rs1, RS2, PC) { return rs1 | 2; }}, {0x00451a13, "SLLI", false, [](RS1 rs1, RS2, PC) { return rs1 << 4; }}, {0x00455693, "SRLI", false, [](RS1 rs1, RS2, PC) { return rs1 >> 4; }}, {0x00a035b3, "SLTU", true, [](RS1 rs1, RS2 rs2, PC) { return rs2 != 0; }}, {0x00b50633, "ADD", true, [](RS1 rs1, RS2 rs2, PC) { return rs1 + rs2; }}, {0x40d507b3, "SUB", true, [](RS1 rs1, RS2 rs2, PC) { return rs1 - rs2; }}, + + // RV32M & RV64M Tests + {0x02f787b3, "MUL", true, [](RS1 rs1, RS2 rs2, PC) { return rs1 * rs2; }}, + {0x02f747b3, "DIV", true, [](RS1 rs1, RS2 rs2, PC) { return rs1 / rs2; }}, + {0x02f757b3, "DIVU", true, + [](RS1 rs1, RS2 rs2, PC) { return rs1 / rs2; }}, + {0x02f767b3, "REM", true, [](RS1 rs1, RS2 rs2, PC) { return rs1 % rs2; }}, + {0x02f777b3, "REMU", true, + [](RS1 rs1, RS2 rs2, PC) { return rs1 % rs2; }}, + {0x02f787bb, "MULW", true, + [](RS1 rs1, RS2 rs2, PC) { return rs1 * rs2; }}, + {0x02f747bb, "DIVW", true, + [](RS1 rs1, RS2 rs2, PC) { return rs1 / rs2; }}, + {0x02f757bb, "DIVUW", true, + [](RS1 rs1, RS2 rs2, PC) { return rs1 / rs2; }}, + {0x02f767bb, "REMW", true, + [](RS1 rs1, RS2 rs2, PC) { return rs1 % rs2; }}, + {0x02f777bb, "REMUW", true, + [](RS1 rs1, RS2 rs2, PC) { return rs1 % rs2; }}, }; for (auto i : tests) { const InstrPattern *pattern = this->Decode(i.inst); @@ -229,3 +323,48 @@ TestInst(this, i.inst, i.has_rs2, i.rd_val); } } + +TEST_F(RISCVEmulatorTester, TestAMOSWAP) { + TestAtomic(this, 0x8F7282F, 0x1, 0x2, 0x1, 0x2); + TestAtomic(this, 0x8F7382F, 0x1, 0x2, 0x1, 0x2); +} + +TEST_F(RISCVEmulatorTester, TestAMOADD) { + TestAtomic(this, 0xF7282F, 0x1, 0x2, 0x1, 0x3); + TestAtomic(this, 0xF7382F, 0x1, 0x2, 0x1, 0x3); +} + +TEST_F(RISCVEmulatorTester, TestAMOXOR) { + TestAtomic(this, 0x20F7282F, 0x1, 0x2, 0x1, 0x3); + TestAtomic(this, 0x20F7382F, 0x1, 0x2, 0x1, 0x3); +} + +TEST_F(RISCVEmulatorTester, TestAMOAND) { + TestAtomic(this, 0x60F7282F, 0x1, 0x2, 0x1, 0x0); + TestAtomic(this, 0x60F7382F, 0x1, 0x2, 0x1, 0x0); +} + +TEST_F(RISCVEmulatorTester, TestAMOOR) { + TestAtomic(this, 0x40F7282F, 0x1, 0x2, 0x1, 0x3); + TestAtomic(this, 0x40F7382F, 0x1, 0x2, 0x1, 0x3); +} + +TEST_F(RISCVEmulatorTester, TestAMOMIN) { + TestAtomic(this, 0x80F7282F, 0x1, 0x2, 0x1, 0x1); + TestAtomic(this, 0x80F7382F, 0x1, 0x2, 0x1, 0x1); +} + +TEST_F(RISCVEmulatorTester, TestAMOMAX) { + TestAtomic(this, 0xA0F7282F, 0x1, 0x2, 0x1, 0x2); + TestAtomic(this, 0xA0F7382F, 0x1, 0x2, 0x1, 0x2); +} + +TEST_F(RISCVEmulatorTester, TestAMOMINU) { + TestAtomic(this, 0xC0F7282F, 0x1, 0x2, 0x1, 0x1); + TestAtomic(this, 0xC0F7382F, 0x1, 0x2, 0x1, 0x1); +} + +TEST_F(RISCVEmulatorTester, TestAMOMAXU) { + TestAtomic(this, 0xE0F7282F, 0x1, 0x2, 0x1, 0x2); + TestAtomic(this, 0xE0F7382F, 0x1, 0x2, 0x1, 0x2); +}