diff --git a/lldb/source/Plugins/Instruction/CMakeLists.txt b/lldb/source/Plugins/Instruction/CMakeLists.txt --- a/lldb/source/Plugins/Instruction/CMakeLists.txt +++ b/lldb/source/Plugins/Instruction/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(ARM) add_subdirectory(ARM64) +add_subdirectory(LoongArch) add_subdirectory(MIPS) add_subdirectory(MIPS64) add_subdirectory(PPC64) diff --git a/lldb/source/Plugins/Instruction/LoongArch/CMakeLists.txt b/lldb/source/Plugins/Instruction/LoongArch/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Instruction/LoongArch/CMakeLists.txt @@ -0,0 +1,11 @@ +add_lldb_library(lldbPluginInstructionLoongArch PLUGIN + EmulateInstructionLoongArch.cpp + + LINK_LIBS + lldbCore + lldbInterpreter + lldbPluginProcessUtility + lldbSymbol + LINK_COMPONENTS + Support + ) diff --git a/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h @@ -0,0 +1,76 @@ +//===---EmulateInstructionLoongArch.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_LOONGARCH_EMULATEINSTRUCTIONLOONGARCH_H +#define LLDB_SOURCE_PLUGINS_INSTRUCTION_LOONGARCH_EMULATEINSTRUCTIONLOONGARCH_H + +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Interpreter/OptionValue.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Status.h" + +namespace lldb_private { + +class EmulateInstructionLoongArch : public EmulateInstruction { +public: + static llvm::StringRef GetPluginNameStatic() { return "LoongArch"; } + + static llvm::StringRef GetPluginDescriptionStatic() { + return "Emulate instructions for the LoongArch architecture."; + } + + static bool SupportsThisInstructionType(InstructionType inst_type) { + return inst_type == eInstructionTypePCModifying; + } + + static bool SupportsThisArch(const ArchSpec &arch); + + static lldb_private::EmulateInstruction * + CreateInstance(const lldb_private::ArchSpec &arch, InstructionType inst_type); + + static void Initialize(); + + static void Terminate(); + +public: + EmulateInstructionLoongArch(const ArchSpec &arch) + : EmulateInstruction(arch) {} + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + bool SupportsEmulatingInstructionsOfType(InstructionType inst_type) override { + return SupportsThisInstructionType(inst_type); + } + + bool SetTargetTriple(const ArchSpec &arch) override; + bool ReadInstruction() override; + bool EvaluateInstruction(uint32_t options) override; + bool TestEmulation(Stream *out_stream, ArchSpec &arch, + OptionValueDictionary *test_data) override; + + llvm::Optional GetRegisterInfo(lldb::RegisterKind reg_kind, + uint32_t reg_num) override; + lldb::addr_t ReadPC(bool *success); + bool WritePC(lldb::addr_t pc); + +private: + struct Opcode { + uint32_t mask; + uint32_t value; + bool (EmulateInstructionLoongArch::*callback)(uint32_t opcode); + const char *name; + }; + + Opcode *GetOpcodeForInstruction(uint32_t inst); + + bool EmulateNonJMP(uint32_t inst); +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_INSTRUCTION_LOONGARCH_EMULATEINSTRUCTIONLOONGARCH_H diff --git a/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp @@ -0,0 +1,181 @@ +//===---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 + +#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 of jump instruction. + static EmulateInstructionLoongArch::Opcode g_opcodes[] = { + {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::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); +} + +llvm::Optional +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::EmulateNonJMP(uint32_t inst) { return false; } + +} // namespace lldb_private diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -883,7 +883,7 @@ bool NativeProcessLinux::SupportHardwareSingleStepping() const { if (m_arch.IsMIPS() || m_arch.GetMachine() == llvm::Triple::arm || - m_arch.GetTriple().isRISCV()) + m_arch.GetTriple().isRISCV() || m_arch.GetTriple().isLoongArch()) return false; return true; } diff --git a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp --- a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp +++ b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp @@ -168,7 +168,7 @@ size_hint = 4; } } else if (arch.IsMIPS() || arch.GetTriple().isPPC64() || - arch.GetTriple().isRISCV()) + arch.GetTriple().isRISCV() || arch.GetTriple().isLoongArch()) size_hint = 4; error = process.SetBreakpoint(next_pc, size_hint, /*hardware=*/false); diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt --- a/lldb/tools/lldb-server/CMakeLists.txt +++ b/lldb/tools/lldb-server/CMakeLists.txt @@ -51,6 +51,7 @@ lldbVersion ${LLDB_PLUGINS} lldbPluginInstructionARM + lldbPluginInstructionLoongArch lldbPluginInstructionMIPS lldbPluginInstructionMIPS64 lldbPluginInstructionRISCV diff --git a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp --- a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp +++ b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp @@ -29,6 +29,11 @@ #include "Plugins/Instruction/ARM/EmulateInstructionARM.h" #endif +#if defined(__loongarch__) +#define LLDB_TARGET_LoongArch +#include "Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h" +#endif + #if defined(__mips64__) || defined(mips64) || defined(__mips64) || \ defined(__MIPS64__) || defined(_M_MIPS64) #define LLDB_TARGET_MIPS64 @@ -57,6 +62,9 @@ #if defined(LLDB_TARGET_ARM) || defined(LLDB_TARGET_ARM64) EmulateInstructionARM::Initialize(); #endif +#if defined(LLDB_TARGET_LoongArch) + EmulateInstructionLoongArch::Initialize(); +#endif #if defined(LLDB_TARGET_MIPS) || defined(LLDB_TARGET_MIPS64) EmulateInstructionMIPS::Initialize(); #endif @@ -76,6 +84,9 @@ #if defined(LLDB_TARGET_ARM) || defined(LLDB_TARGET_ARM64) EmulateInstructionARM::Terminate(); #endif +#if defined(LLDB_TARGET_LoongArch) + EmulateInstructionLoongArch::Terminate(); +#endif #if defined(LLDB_TARGET_MIPS) || defined(LLDB_TARGET_MIPS64) EmulateInstructionMIPS::Terminate(); #endif