| //===---EmulateInstructionLoongArch.cpp------------------------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include <cstdlib> |
| #include <optional> |
| |
| #include "EmulateInstructionLoongArch.h" |
| #include "Plugins/Process/Utility/InstructionUtils.h" |
| #include "Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.h" |
| #include "Plugins/Process/Utility/lldb-loongarch-register-enums.h" |
| #include "lldb/Core/Address.h" |
| #include "lldb/Core/PluginManager.h" |
| #include "lldb/Interpreter/OptionValueArray.h" |
| #include "lldb/Interpreter/OptionValueDictionary.h" |
| #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" |
| #include "llvm/Support/MathExtras.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| LLDB_PLUGIN_DEFINE_ADV(EmulateInstructionLoongArch, InstructionLoongArch) |
| |
| namespace lldb_private { |
| |
| EmulateInstructionLoongArch::Opcode * |
| EmulateInstructionLoongArch::GetOpcodeForInstruction(uint32_t inst) { |
| // TODO: Add the mask for other instruction. |
| static EmulateInstructionLoongArch::Opcode g_opcodes[] = { |
| {0xfc000000, 0x40000000, &EmulateInstructionLoongArch::EmulateBEQZ, |
| "beqz rj, offs21"}, |
| {0xfc000000, 0x44000000, &EmulateInstructionLoongArch::EmulateBNEZ, |
| "bnez rj, offs21"}, |
| {0xfc000300, 0x48000000, &EmulateInstructionLoongArch::EmulateBCEQZ, |
| "bceqz cj, offs21"}, |
| {0xfc000300, 0x48000100, &EmulateInstructionLoongArch::EmulateBCNEZ, |
| "bcnez cj, offs21"}, |
| {0xfc000000, 0x4c000000, &EmulateInstructionLoongArch::EmulateJIRL, |
| "jirl rd, rj, offs16"}, |
| {0xfc000000, 0x50000000, &EmulateInstructionLoongArch::EmulateB, |
| " b offs26"}, |
| {0xfc000000, 0x54000000, &EmulateInstructionLoongArch::EmulateBL, |
| "bl offs26"}, |
| {0xfc000000, 0x58000000, &EmulateInstructionLoongArch::EmulateBEQ, |
| "beq rj, rd, offs16"}, |
| {0xfc000000, 0x5c000000, &EmulateInstructionLoongArch::EmulateBNE, |
| "bne rj, rd, offs16"}, |
| {0xfc000000, 0x60000000, &EmulateInstructionLoongArch::EmulateBLT, |
| "blt rj, rd, offs16"}, |
| {0xfc000000, 0x64000000, &EmulateInstructionLoongArch::EmulateBGE, |
| "bge rj, rd, offs16"}, |
| {0xfc000000, 0x68000000, &EmulateInstructionLoongArch::EmulateBLTU, |
| "bltu rj, rd, offs16"}, |
| {0xfc000000, 0x6c000000, &EmulateInstructionLoongArch::EmulateBGEU, |
| "bgeu rj, rd, offs16"}, |
| {0x00000000, 0x00000000, &EmulateInstructionLoongArch::EmulateNonJMP, |
| "NonJMP"}}; |
| static const size_t num_loongarch_opcodes = std::size(g_opcodes); |
| |
| for (size_t i = 0; i < num_loongarch_opcodes; ++i) |
| if ((g_opcodes[i].mask & inst) == g_opcodes[i].value) |
| return &g_opcodes[i]; |
| return nullptr; |
| } |
| |
| bool EmulateInstructionLoongArch::TestExecute(uint32_t inst) { |
| Opcode *opcode_data = GetOpcodeForInstruction(inst); |
| if (!opcode_data) |
| return false; |
| // Call the Emulate... function. |
| if (!(this->*opcode_data->callback)(inst)) |
| return false; |
| return true; |
| } |
| |
| bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) { |
| uint32_t inst_size = m_opcode.GetByteSize(); |
| uint32_t inst = m_opcode.GetOpcode32(); |
| bool increase_pc = options & eEmulateInstructionOptionAutoAdvancePC; |
| bool success = false; |
| |
| Opcode *opcode_data = GetOpcodeForInstruction(inst); |
| if (!opcode_data) |
| return false; |
| |
| lldb::addr_t old_pc = 0; |
| if (increase_pc) { |
| old_pc = ReadPC(&success); |
| if (!success) |
| return false; |
| } |
| |
| // Call the Emulate... function. |
| if (!(this->*opcode_data->callback)(inst)) |
| return false; |
| |
| if (increase_pc) { |
| lldb::addr_t new_pc = ReadPC(&success); |
| if (!success) |
| return false; |
| |
| if (new_pc == old_pc && !WritePC(old_pc + inst_size)) |
| return false; |
| } |
| return true; |
| } |
| |
| bool EmulateInstructionLoongArch::ReadInstruction() { |
| bool success = false; |
| m_addr = ReadPC(&success); |
| if (!success) { |
| m_addr = LLDB_INVALID_ADDRESS; |
| return false; |
| } |
| |
| Context ctx; |
| ctx.type = eContextReadOpcode; |
| ctx.SetNoArgs(); |
| uint32_t inst = (uint32_t)ReadMemoryUnsigned(ctx, m_addr, 4, 0, &success); |
| m_opcode.SetOpcode32(inst, GetByteOrder()); |
| |
| return true; |
| } |
| |
| lldb::addr_t EmulateInstructionLoongArch::ReadPC(bool *success) { |
| return ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, |
| LLDB_INVALID_ADDRESS, success); |
| } |
| |
| bool EmulateInstructionLoongArch::WritePC(lldb::addr_t pc) { |
| EmulateInstruction::Context ctx; |
| ctx.type = eContextAdvancePC; |
| ctx.SetNoArgs(); |
| return WriteRegisterUnsigned(ctx, eRegisterKindGeneric, |
| LLDB_REGNUM_GENERIC_PC, pc); |
| } |
| |
| std::optional<RegisterInfo> |
| EmulateInstructionLoongArch::GetRegisterInfo(lldb::RegisterKind reg_kind, |
| uint32_t reg_index) { |
| if (reg_kind == eRegisterKindGeneric) { |
| switch (reg_index) { |
| case LLDB_REGNUM_GENERIC_PC: |
| reg_kind = eRegisterKindLLDB; |
| reg_index = gpr_pc_loongarch; |
| break; |
| case LLDB_REGNUM_GENERIC_SP: |
| reg_kind = eRegisterKindLLDB; |
| reg_index = gpr_sp_loongarch; |
| break; |
| case LLDB_REGNUM_GENERIC_FP: |
| reg_kind = eRegisterKindLLDB; |
| reg_index = gpr_fp_loongarch; |
| break; |
| case LLDB_REGNUM_GENERIC_RA: |
| reg_kind = eRegisterKindLLDB; |
| reg_index = gpr_ra_loongarch; |
| break; |
| // We may handle LLDB_REGNUM_GENERIC_ARGx when more instructions are |
| // supported. |
| default: |
| llvm_unreachable("unsupported register"); |
| } |
| } |
| |
| const RegisterInfo *array = |
| RegisterInfoPOSIX_loongarch64::GetRegisterInfoPtr(m_arch); |
| const uint32_t length = |
| RegisterInfoPOSIX_loongarch64::GetRegisterInfoCount(m_arch); |
| |
| if (reg_index >= length || reg_kind != eRegisterKindLLDB) |
| return {}; |
| return array[reg_index]; |
| } |
| |
| bool EmulateInstructionLoongArch::SetTargetTriple(const ArchSpec &arch) { |
| return SupportsThisArch(arch); |
| } |
| |
| bool EmulateInstructionLoongArch::TestEmulation( |
| Stream &out_stream, ArchSpec &arch, OptionValueDictionary *test_data) { |
| return false; |
| } |
| |
| void EmulateInstructionLoongArch::Initialize() { |
| PluginManager::RegisterPlugin(GetPluginNameStatic(), |
| GetPluginDescriptionStatic(), CreateInstance); |
| } |
| |
| void EmulateInstructionLoongArch::Terminate() { |
| PluginManager::UnregisterPlugin(CreateInstance); |
| } |
| |
| lldb_private::EmulateInstruction * |
| EmulateInstructionLoongArch::CreateInstance(const ArchSpec &arch, |
| InstructionType inst_type) { |
| if (EmulateInstructionLoongArch::SupportsThisInstructionType(inst_type) && |
| SupportsThisArch(arch)) |
| return new EmulateInstructionLoongArch(arch); |
| return nullptr; |
| } |
| |
| bool EmulateInstructionLoongArch::SupportsThisArch(const ArchSpec &arch) { |
| return arch.GetTriple().isLoongArch(); |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateBEQZ(uint32_t inst) { |
| return IsLoongArch64() ? EmulateBEQZ64(inst) : false; |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateBNEZ(uint32_t inst) { |
| return IsLoongArch64() ? EmulateBNEZ64(inst) : false; |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateBCEQZ(uint32_t inst) { |
| return IsLoongArch64() ? EmulateBCEQZ64(inst) : false; |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateBCNEZ(uint32_t inst) { |
| return IsLoongArch64() ? EmulateBCNEZ64(inst) : false; |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateJIRL(uint32_t inst) { |
| return IsLoongArch64() ? EmulateJIRL64(inst) : false; |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateB(uint32_t inst) { |
| return IsLoongArch64() ? EmulateB64(inst) : false; |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateBL(uint32_t inst) { |
| return IsLoongArch64() ? EmulateBL64(inst) : false; |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateBEQ(uint32_t inst) { |
| return IsLoongArch64() ? EmulateBEQ64(inst) : false; |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateBNE(uint32_t inst) { |
| return IsLoongArch64() ? EmulateBNE64(inst) : false; |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateBLT(uint32_t inst) { |
| return IsLoongArch64() ? EmulateBLT64(inst) : false; |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateBGE(uint32_t inst) { |
| return IsLoongArch64() ? EmulateBGE64(inst) : false; |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateBLTU(uint32_t inst) { |
| return IsLoongArch64() ? EmulateBLTU64(inst) : false; |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateBGEU(uint32_t inst) { |
| return IsLoongArch64() ? EmulateBGEU64(inst) : false; |
| } |
| |
| bool EmulateInstructionLoongArch::EmulateNonJMP(uint32_t inst) { return false; } |
| |
| // beqz rj, offs21 |
| // if GR[rj] == 0: |
| // PC = PC + SignExtend({offs21, 2'b0}, GRLEN) |
| bool EmulateInstructionLoongArch::EmulateBEQZ64(uint32_t inst) { |
| bool success = false; |
| uint32_t rj = Bits32(inst, 9, 5); |
| uint64_t pc = ReadPC(&success); |
| if (!success) |
| return false; |
| uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16); |
| uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); |
| if (!success) |
| return false; |
| if (rj_val == 0) { |
| uint64_t next_pc = pc + llvm::SignExtend64<23>(offs21 << 2); |
| return WritePC(next_pc); |
| } else |
| return WritePC(pc + 4); |
| } |
| |
| // bnez rj, offs21 |
| // if GR[rj] != 0: |
| // PC = PC + SignExtend({offs21, 2'b0}, GRLEN) |
| bool EmulateInstructionLoongArch::EmulateBNEZ64(uint32_t inst) { |
| bool success = false; |
| uint32_t rj = Bits32(inst, 9, 5); |
| uint64_t pc = ReadPC(&success); |
| if (!success) |
| return false; |
| uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16); |
| uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); |
| if (!success) |
| return false; |
| if (rj_val != 0) { |
| uint64_t next_pc = pc + llvm::SignExtend64<23>(offs21 << 2); |
| return WritePC(next_pc); |
| } else |
| return WritePC(pc + 4); |
| } |
| |
| // bceqz cj, offs21 |
| // if CFR[cj] == 0: |
| // PC = PC + SignExtend({offs21, 2'b0}, GRLEN) |
| bool EmulateInstructionLoongArch::EmulateBCEQZ64(uint32_t inst) { |
| bool success = false; |
| uint32_t cj = Bits32(inst, 7, 5) + fpr_fcc0_loongarch; |
| uint64_t pc = ReadPC(&success); |
| if (!success) |
| return false; |
| uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16); |
| uint8_t cj_val = |
| (uint8_t)ReadRegisterUnsigned(eRegisterKindLLDB, cj, 0, &success); |
| if (!success) |
| return false; |
| if (cj_val == 0) { |
| uint64_t next_pc = pc + llvm::SignExtend64<23>(offs21 << 2); |
| return WritePC(next_pc); |
| } else |
| return WritePC(pc + 4); |
| return false; |
| } |
| |
| // bcnez cj, offs21 |
| // if CFR[cj] != 0: |
| // PC = PC + SignExtend({offs21, 2'b0}, GRLEN) |
| bool EmulateInstructionLoongArch::EmulateBCNEZ64(uint32_t inst) { |
| bool success = false; |
| uint32_t cj = Bits32(inst, 7, 5) + fpr_fcc0_loongarch; |
| uint64_t pc = ReadPC(&success); |
| if (!success) |
| return false; |
| uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16); |
| uint8_t cj_val = |
| (uint8_t)ReadRegisterUnsigned(eRegisterKindLLDB, cj, 0, &success); |
| if (!success) |
| return false; |
| if (cj_val != 0) { |
| uint64_t next_pc = pc + llvm::SignExtend64<23>(offs21 << 2); |
| return WritePC(next_pc); |
| } else |
| return WritePC(pc + 4); |
| return false; |
| } |
| |
| // jirl rd, rj, offs16 |
| // GR[rd] = PC + 4 |
| // PC = GR[rj] + SignExtend({offs16, 2'b0}, GRLEN) |
| bool EmulateInstructionLoongArch::EmulateJIRL64(uint32_t inst) { |
| uint32_t rj = Bits32(inst, 9, 5); |
| uint32_t rd = Bits32(inst, 4, 0); |
| bool success = false; |
| uint64_t pc = ReadPC(&success); |
| if (!success) |
| return false; |
| EmulateInstruction::Context ctx; |
| if (!WriteRegisterUnsigned(ctx, eRegisterKindLLDB, rd, pc + 4)) |
| return false; |
| uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); |
| if (!success) |
| return false; |
| uint64_t next_pc = rj_val + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); |
| return WritePC(next_pc); |
| } |
| |
| // b offs26 |
| // PC = PC + SignExtend({offs26, 2' b0}, GRLEN) |
| bool EmulateInstructionLoongArch::EmulateB64(uint32_t inst) { |
| bool success = false; |
| uint64_t pc = ReadPC(&success); |
| if (!success) |
| return false; |
| uint32_t offs26 = Bits32(inst, 25, 10) + (Bits32(inst, 9, 0) << 16); |
| uint64_t next_pc = pc + llvm::SignExtend64<28>(offs26 << 2); |
| return WritePC(next_pc); |
| } |
| |
| // bl offs26 |
| // GR[1] = PC + 4 |
| // PC = PC + SignExtend({offs26, 2'b0}, GRLEN) |
| bool EmulateInstructionLoongArch::EmulateBL64(uint32_t inst) { |
| bool success = false; |
| uint64_t pc = ReadPC(&success); |
| if (!success) |
| return false; |
| EmulateInstruction::Context ctx; |
| if (!WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_loongarch, pc + 4)) |
| return false; |
| uint32_t offs26 = Bits32(inst, 25, 10) + (Bits32(inst, 9, 0) << 16); |
| uint64_t next_pc = pc + llvm::SignExtend64<28>(offs26 << 2); |
| return WritePC(next_pc); |
| } |
| |
| // beq rj, rd, offs16 |
| // if GR[rj] == GR[rd]: |
| // PC = PC + SignExtend({offs16, 2'b0}, GRLEN) |
| bool EmulateInstructionLoongArch::EmulateBEQ64(uint32_t inst) { |
| bool success = false; |
| uint32_t rj = Bits32(inst, 9, 5); |
| uint32_t rd = Bits32(inst, 4, 0); |
| uint64_t pc = ReadPC(&success); |
| if (!success) |
| return false; |
| uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); |
| if (!success) |
| return false; |
| uint64_t rd_val = ReadRegisterUnsigned(eRegisterKindLLDB, rd, 0, &success); |
| if (!success) |
| return false; |
| if (rj_val == rd_val) { |
| uint64_t next_pc = pc + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); |
| return WritePC(next_pc); |
| } else |
| return WritePC(pc + 4); |
| } |
| |
| // bne rj, rd, offs16 |
| // if GR[rj] != GR[rd]: |
| // PC = PC + SignExtend({offs16, 2'b0}, GRLEN) |
| bool EmulateInstructionLoongArch::EmulateBNE64(uint32_t inst) { |
| bool success = false; |
| uint32_t rj = Bits32(inst, 9, 5); |
| uint32_t rd = Bits32(inst, 4, 0); |
| uint64_t pc = ReadPC(&success); |
| if (!success) |
| return false; |
| uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); |
| if (!success) |
| return false; |
| uint64_t rd_val = ReadRegisterUnsigned(eRegisterKindLLDB, rd, 0, &success); |
| if (!success) |
| return false; |
| if (rj_val != rd_val) { |
| uint64_t next_pc = pc + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); |
| return WritePC(next_pc); |
| } else |
| return WritePC(pc + 4); |
| } |
| |
| // blt rj, rd, offs16 |
| // if signed(GR[rj]) < signed(GR[rd]): |
| // PC = PC + SignExtend({offs16, 2'b0}, GRLEN) |
| bool EmulateInstructionLoongArch::EmulateBLT64(uint32_t inst) { |
| bool success = false; |
| uint32_t rj = Bits32(inst, 9, 5); |
| uint32_t rd = Bits32(inst, 4, 0); |
| uint64_t pc = ReadPC(&success); |
| if (!success) |
| return false; |
| int64_t rj_val = |
| (int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); |
| if (!success) |
| return false; |
| int64_t rd_val = |
| (int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rd, 0, &success); |
| if (!success) |
| return false; |
| if (rj_val < rd_val) { |
| uint64_t next_pc = pc + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); |
| return WritePC(next_pc); |
| } else |
| return WritePC(pc + 4); |
| } |
| |
| // bge rj, rd, offs16 |
| // if signed(GR[rj]) >= signed(GR[rd]): |
| // PC = PC + SignExtend({offs16, 2'b0}, GRLEN) |
| bool EmulateInstructionLoongArch::EmulateBGE64(uint32_t inst) { |
| bool success = false; |
| uint32_t rj = Bits32(inst, 9, 5); |
| uint32_t rd = Bits32(inst, 4, 0); |
| uint64_t pc = ReadPC(&success); |
| if (!success) |
| return false; |
| int64_t rj_val = |
| (int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); |
| if (!success) |
| return false; |
| int64_t rd_val = |
| (int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rd, 0, &success); |
| if (!success) |
| return false; |
| if (rj_val >= rd_val) { |
| uint64_t next_pc = pc + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); |
| return WritePC(next_pc); |
| } else |
| return WritePC(pc + 4); |
| } |
| |
| // bltu rj, rd, offs16 |
| // if unsigned(GR[rj]) < unsigned(GR[rd]): |
| // PC = PC + SignExtend({offs16, 2'b0}, GRLEN) |
| bool EmulateInstructionLoongArch::EmulateBLTU64(uint32_t inst) { |
| bool success = false; |
| uint32_t rj = Bits32(inst, 9, 5); |
| uint32_t rd = Bits32(inst, 4, 0); |
| uint64_t pc = ReadPC(&success); |
| if (!success) |
| return false; |
| uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); |
| if (!success) |
| return false; |
| uint64_t rd_val = ReadRegisterUnsigned(eRegisterKindLLDB, rd, 0, &success); |
| if (!success) |
| return false; |
| if (rj_val < rd_val) { |
| uint64_t next_pc = pc + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); |
| return WritePC(next_pc); |
| } else |
| return WritePC(pc + 4); |
| } |
| |
| // bgeu rj, rd, offs16 |
| // if unsigned(GR[rj]) >= unsigned(GR[rd]): |
| // PC = PC + SignExtend({offs16, 2'b0}, GRLEN) |
| bool EmulateInstructionLoongArch::EmulateBGEU64(uint32_t inst) { |
| bool success = false; |
| uint32_t rj = Bits32(inst, 9, 5); |
| uint32_t rd = Bits32(inst, 4, 0); |
| uint64_t pc = ReadPC(&success); |
| if (!success) |
| return false; |
| uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); |
| if (!success) |
| return false; |
| uint64_t rd_val = ReadRegisterUnsigned(eRegisterKindLLDB, rd, 0, &success); |
| if (!success) |
| return false; |
| if (rj_val >= rd_val) { |
| uint64_t next_pc = pc + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); |
| return WritePC(next_pc); |
| } else |
| return WritePC(pc + 4); |
| } |
| |
| } // namespace lldb_private |