Author: dlav-sc Date: 2024-10-02T12:08:22+03:00 New Revision: 87121403e251828ed0fee8a9c2a2993ce8f57861
URL: https://github.com/llvm/llvm-project/commit/87121403e251828ed0fee8a9c2a2993ce8f57861 DIFF: https://github.com/llvm/llvm-project/commit/87121403e251828ed0fee8a9c2a2993ce8f57861.diff LOG: [lldb][RISCV] function calls support in lldb expressions (#99336) [lldb][RISCV] add jitted function calls to ABI Function calls support in LLDB expressions for RISCV: 1 of 4 Augments corresponding functionality to RISCV ABI, which allows to jit lldb expressions and thus make function calls inside them. Only function calls with integer and void function arguments and return value are supported. [lldb][RISCV] add JIT relocations resolver Function calls support in LLDB expressions for RISCV: 2 of 4 Adds required RISCV relocations resolving functionality in lldb ExecutionEngine. [lldb][RISCV] RISC-V large code model in lldb expressions Function calls support in LLDB expressions for RISCV: 3 of 4 This patch sets large code model in MCJIT settings for RISC-V 64-bit targets that allows to make assembly jumps at any 64bit address. This is needed, because resulted jitted code may contain more that +-2GB jumps, that are not available in RISC-V with medium code model. [lldb][RISCV] doubles support in lldb expressions Function calls support in LLDB expressions for RISCV: 4 of 4 This patch adds desired feature flags in MCJIT compiler to enable hard-float instructions if target supports them and allows to use floats and doubles in lldb expressions. Added: Modified: lldb/source/Expression/IRExecutionUnit.cpp lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h Removed: ################################################################################ diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp index 15ca2ddbbae046..baf70cbccc8c5c 100644 --- a/lldb/source/Expression/IRExecutionUnit.cpp +++ b/lldb/source/Expression/IRExecutionUnit.cpp @@ -277,6 +277,12 @@ void IRExecutionUnit::GetRunnableInfo(Status &error, lldb::addr_t &func_addr, .setMCJITMemoryManager(std::make_unique<MemoryManager>(*this)) .setOptLevel(llvm::CodeGenOptLevel::Less); + // Resulted jitted code can be placed too far from the code in the binary + // and thus can contain more than +-2GB jumps, that are not available + // in RISC-V without large code model. + if (triple.isRISCV64()) + builder.setCodeModel(llvm::CodeModel::Large); + llvm::StringRef mArch; llvm::StringRef mCPU; llvm::SmallVector<std::string, 0> mAttrs; diff --git a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp index de304183f67175..38d452b6ab560c 100644 --- a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp +++ b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp @@ -10,7 +10,9 @@ #include <array> #include <limits> +#include <sstream> +#include "llvm/ADT/STLExtras.h" #include "llvm/IR/DerivedTypes.h" #include "Utility/RISCV_DWARF_Registers.h" @@ -20,6 +22,7 @@ #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Thread.h" +#include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/RegisterValue.h" #define DEFINE_REG_NAME(reg_num) ConstString(#reg_num).GetCString() @@ -120,6 +123,10 @@ static const std::array<RegisterInfo, 33> g_register_infos = { } // namespace dwarf } // namespace +// Number of argument registers (the base integer calling convention +// provides 8 argument registers, a0-a7) +static constexpr size_t g_regs_for_args_count = 8U; + const RegisterInfo *ABISysV_riscv::GetRegisterInfoArray(uint32_t &count) { count = dwarf::g_register_infos.size(); return dwarf::g_register_infos.data(); @@ -164,11 +171,81 @@ TotalArgsSizeInWords(bool is_rv64, return total_size; } +static bool UpdateRegister(RegisterContext *reg_ctx, + const lldb::RegisterKind reg_kind, + const uint32_t reg_num, const addr_t value) { + Log *log = GetLog(LLDBLog::Expressions); + + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(reg_kind, reg_num); + + LLDB_LOG(log, "Writing {0}: 0x{1:x}", reg_info->name, + static_cast<uint64_t>(value)); + if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, value)) { + LLDB_LOG(log, "Writing {0}: failed", reg_info->name); + return false; + } + return true; +} + +static void LogInitInfo(Log &log, const Thread &thread, addr_t sp, + addr_t func_addr, addr_t return_addr, + const llvm::ArrayRef<addr_t> args) { + std::stringstream ss; + ss << "ABISysV_riscv::PrepareTrivialCall" + << " (tid = 0x" << std::hex << thread.GetID() << ", sp = 0x" << sp + << ", func_addr = 0x" << func_addr << ", return_addr = 0x" << return_addr; + + for (auto [idx, arg] : enumerate(args)) + ss << ", arg" << std::dec << idx << " = 0x" << std::hex << arg; + ss << ")"; + log.PutString(ss.str()); +} + bool ABISysV_riscv::PrepareTrivialCall(Thread &thread, addr_t sp, addr_t func_addr, addr_t return_addr, llvm::ArrayRef<addr_t> args) const { - // TODO: Implement - return false; + Log *log = GetLog(LLDBLog::Expressions); + if (log) + LogInitInfo(*log, thread, sp, func_addr, return_addr, args); + + const auto reg_ctx_sp = thread.GetRegisterContext(); + if (!reg_ctx_sp) { + LLDB_LOG(log, "Failed to get RegisterContext"); + return false; + } + + if (args.size() > g_regs_for_args_count) { + LLDB_LOG(log, "Function has {0} arguments, but only {1} are allowed!", + args.size(), g_regs_for_args_count); + return false; + } + + // Write arguments to registers + for (auto [idx, arg] : enumerate(args)) { + const RegisterInfo *reg_info = reg_ctx_sp->GetRegisterInfo( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + idx); + LLDB_LOG(log, "About to write arg{0} (0x{1:x}) into {2}", idx, arg, + reg_info->name); + + if (!reg_ctx_sp->WriteRegisterFromUnsigned(reg_info, arg)) { + LLDB_LOG(log, "Failed to write arg{0} (0x{1:x}) into {2}", idx, arg, + reg_info->name); + return false; + } + } + + if (!UpdateRegister(reg_ctx_sp.get(), eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_PC, func_addr)) + return false; + if (!UpdateRegister(reg_ctx_sp.get(), eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_SP, sp)) + return false; + if (!UpdateRegister(reg_ctx_sp.get(), eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_RA, return_addr)) + return false; + + LLDB_LOG(log, "ABISysV_riscv::{0}() success", __FUNCTION__); + return true; } bool ABISysV_riscv::PrepareTrivialCall( @@ -222,14 +299,14 @@ bool ABISysV_riscv::PrepareTrivialCall( assert(prototype.getFunctionNumParams() == args.size()); const size_t num_args = args.size(); - const size_t regs_for_args_count = 8U; const size_t num_args_in_regs = - num_args > regs_for_args_count ? regs_for_args_count : num_args; + num_args > g_regs_for_args_count ? g_regs_for_args_count : num_args; // Number of arguments passed on stack. size_t args_size = TotalArgsSizeInWords(m_is_rv64, args); - auto on_stack = - args_size <= regs_for_args_count ? 0 : args_size - regs_for_args_count; + auto on_stack = args_size <= g_regs_for_args_count + ? 0 + : args_size - g_regs_for_args_count; auto offset = on_stack * word_size; uint8_t reg_value[8]; @@ -260,7 +337,7 @@ bool ABISysV_riscv::PrepareTrivialCall( ++reg_index; } - if (reg_index < regs_for_args_count || size == 0) + if (reg_index < g_regs_for_args_count || size == 0) continue; // Remaining arguments are passed on the stack. diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index 9b056ea73a77fb..39f0e6269cd0f0 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -44,6 +44,7 @@ #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/TargetParser/Triple.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" @@ -442,24 +443,50 @@ static void SetupDefaultClangDiagnostics(CompilerInstance &compiler) { /// \return /// A string representing target ABI for the current architecture. static std::string GetClangTargetABI(const ArchSpec &target_arch) { - std::string abi; - if (target_arch.IsMIPS()) { switch (target_arch.GetFlags() & ArchSpec::eMIPSABI_mask) { case ArchSpec::eMIPSABI_N64: - abi = "n64"; - break; + return "n64"; case ArchSpec::eMIPSABI_N32: - abi = "n32"; - break; + return "n32"; case ArchSpec::eMIPSABI_O32: - abi = "o32"; - break; + return "o32"; default: - break; + return {}; } } - return abi; + + if (target_arch.GetTriple().isRISCV64()) { + switch (target_arch.GetFlags() & ArchSpec::eRISCV_float_abi_mask) { + case ArchSpec::eRISCV_float_abi_soft: + return "lp64"; + case ArchSpec::eRISCV_float_abi_single: + return "lp64f"; + case ArchSpec::eRISCV_float_abi_double: + return "lp64d"; + case ArchSpec::eRISCV_float_abi_quad: + return "lp64q"; + default: + return {}; + } + } + + if (target_arch.GetTriple().isRISCV32()) { + switch (target_arch.GetFlags() & ArchSpec::eRISCV_float_abi_mask) { + case ArchSpec::eRISCV_float_abi_soft: + return "ilp32"; + case ArchSpec::eRISCV_float_abi_single: + return "ilp32f"; + case ArchSpec::eRISCV_float_abi_double: + return "ilp32d"; + case ArchSpec::eRISCV_float_abi_soft | ArchSpec::eRISCV_rve: + return "ilp32e"; + default: + return {}; + } + } + + return {}; } static void SetupTargetOpts(CompilerInstance &compiler, @@ -506,6 +533,18 @@ static void SetupTargetOpts(CompilerInstance &compiler, // Set the target ABI if (std::string abi = GetClangTargetABI(target_arch); !abi.empty()) compiler.getTargetOpts().ABI = std::move(abi); + + if ((target_machine == llvm::Triple::riscv64 && + compiler.getTargetOpts().ABI == "lp64f") || + (target_machine == llvm::Triple::riscv32 && + compiler.getTargetOpts().ABI == "ilp32f")) + compiler.getTargetOpts().FeaturesAsWritten.emplace_back("+f"); + + if ((target_machine == llvm::Triple::riscv64 && + compiler.getTargetOpts().ABI == "lp64d") || + (target_machine == llvm::Triple::riscv32 && + compiler.getTargetOpts().ABI == "ilp32d")) + compiler.getTargetOpts().FeaturesAsWritten.emplace_back("+d"); } static void SetupLangOpts(CompilerInstance &compiler, @@ -757,7 +796,7 @@ ClangExpressionParser::ClangExpressionParser( m_compiler->getCodeGenOpts().EmitDeclMetadata = true; m_compiler->getCodeGenOpts().InstrumentFunctions = false; m_compiler->getCodeGenOpts().setFramePointer( - CodeGenOptions::FramePointerKind::All); + CodeGenOptions::FramePointerKind::All); if (generate_debug_info) m_compiler->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo); else @@ -771,7 +810,7 @@ ClangExpressionParser::ClangExpressionParser( // FIXME: We shouldn't need to do this, the target should be immutable once // created. This complexity should be lifted elsewhere. m_compiler->getTarget().adjust(m_compiler->getDiagnostics(), - m_compiler->getLangOpts()); + m_compiler->getLangOpts()); // 5. Set up the diagnostic buffer for reporting errors auto diag_mgr = new ClangDiagnosticManagerAdapter( @@ -1191,8 +1230,7 @@ ClangExpressionParser::ParseInternal(DiagnosticManager &diagnostic_manager, if (auto fileEntry = m_compiler->getFileManager().getOptionalFileRef( result_path)) { source_mgr.setMainFileID(source_mgr.createFileID( - *fileEntry, - SourceLocation(), SrcMgr::C_User)); + *fileEntry, SourceLocation(), SrcMgr::C_User)); created_main_file = true; } } diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp index b4e088d5339466..92f37c22ae5132 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp @@ -1011,6 +1011,135 @@ void RuntimeDyldELF::resolveBPFRelocation(const SectionEntry &Section, } } +static void applyUTypeImmRISCV(uint8_t *InstrAddr, uint32_t Imm) { + uint32_t UpperImm = (Imm + 0x800) & 0xfffff000; + auto Instr = support::ulittle32_t::ref(InstrAddr); + Instr = (Instr & 0xfff) | UpperImm; +} + +static void applyITypeImmRISCV(uint8_t *InstrAddr, uint32_t Imm) { + uint32_t LowerImm = Imm & 0xfff; + auto Instr = support::ulittle32_t::ref(InstrAddr); + Instr = (Instr & 0xfffff) | (LowerImm << 20); +} + +void RuntimeDyldELF::resolveRISCVRelocation(const SectionEntry &Section, + uint64_t Offset, uint64_t Value, + uint32_t Type, int64_t Addend, + SID SectionID) { + switch (Type) { + default: { + std::string Err = "Unimplemented reloc type: " + std::to_string(Type); + llvm::report_fatal_error(Err.c_str()); + } + // 32-bit PC-relative function call, macros call, tail (PIC) + // Write first 20 bits of 32 bit value to the auipc instruction + // Last 12 bits to the jalr instruction + case ELF::R_RISCV_CALL: + case ELF::R_RISCV_CALL_PLT: { + uint64_t P = Section.getLoadAddressWithOffset(Offset); + uint64_t PCOffset = Value + Addend - P; + applyUTypeImmRISCV(Section.getAddressWithOffset(Offset), PCOffset); + applyITypeImmRISCV(Section.getAddressWithOffset(Offset + 4), PCOffset); + break; + } + // High 20 bits of 32-bit absolute address, %hi(symbol) + case ELF::R_RISCV_HI20: { + uint64_t PCOffset = Value + Addend; + applyUTypeImmRISCV(Section.getAddressWithOffset(Offset), PCOffset); + break; + } + // Low 12 bits of 32-bit absolute address, %lo(symbol) + case ELF::R_RISCV_LO12_I: { + uint64_t PCOffset = Value + Addend; + applyITypeImmRISCV(Section.getAddressWithOffset(Offset), PCOffset); + break; + } + // High 20 bits of 32-bit PC-relative reference, %pcrel_hi(symbol) + case ELF::R_RISCV_GOT_HI20: + case ELF::R_RISCV_PCREL_HI20: { + uint64_t P = Section.getLoadAddressWithOffset(Offset); + uint64_t PCOffset = Value + Addend - P; + applyUTypeImmRISCV(Section.getAddressWithOffset(Offset), PCOffset); + break; + } + + // label: + // auipc a0, %pcrel_hi(symbol) // R_RISCV_PCREL_HI20 + // addi a0, a0, %pcrel_lo(label) // R_RISCV_PCREL_LO12_I + // + // The low 12 bits of relative address between pc and symbol. + // The symbol is related to the high part instruction which is marked by + // label. + case ELF::R_RISCV_PCREL_LO12_I: { + for (auto &&PendingReloc : PendingRelocs) { + const RelocationValueRef &MatchingValue = PendingReloc.first; + RelocationEntry &Reloc = PendingReloc.second; + uint64_t HIRelocPC = + getSectionLoadAddress(Reloc.SectionID) + Reloc.Offset; + if (Value + Addend == HIRelocPC) { + uint64_t Symbol = getSectionLoadAddress(MatchingValue.SectionID) + + MatchingValue.Addend; + auto PCOffset = Symbol - HIRelocPC; + applyITypeImmRISCV(Section.getAddressWithOffset(Offset), PCOffset); + return; + } + } + + llvm::report_fatal_error( + "R_RISCV_PCREL_LO12_I without matching R_RISCV_PCREL_HI20"); + } + case ELF::R_RISCV_32_PCREL: { + uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + int64_t RealOffset = Value + Addend - FinalAddress; + int32_t TruncOffset = Lo_32(RealOffset); + support::ulittle32_t::ref(Section.getAddressWithOffset(Offset)) = + TruncOffset; + break; + } + case ELF::R_RISCV_32: { + auto Ref = support::ulittle32_t::ref(Section.getAddressWithOffset(Offset)); + Ref = Value + Addend; + break; + } + case ELF::R_RISCV_64: { + auto Ref = support::ulittle64_t::ref(Section.getAddressWithOffset(Offset)); + Ref = Value + Addend; + break; + } + case ELF::R_RISCV_ADD16: { + auto Ref = support::ulittle16_t::ref(Section.getAddressWithOffset(Offset)); + Ref = Ref + Value + Addend; + break; + } + case ELF::R_RISCV_ADD32: { + auto Ref = support::ulittle32_t::ref(Section.getAddressWithOffset(Offset)); + Ref = Ref + Value + Addend; + break; + } + case ELF::R_RISCV_ADD64: { + auto Ref = support::ulittle64_t::ref(Section.getAddressWithOffset(Offset)); + Ref = Ref + Value + Addend; + break; + } + case ELF::R_RISCV_SUB16: { + auto Ref = support::ulittle16_t::ref(Section.getAddressWithOffset(Offset)); + Ref = Ref - Value - Addend; + break; + } + case ELF::R_RISCV_SUB32: { + auto Ref = support::ulittle32_t::ref(Section.getAddressWithOffset(Offset)); + Ref = Ref - Value - Addend; + break; + } + case ELF::R_RISCV_SUB64: { + auto Ref = support::ulittle64_t::ref(Section.getAddressWithOffset(Offset)); + Ref = Ref - Value - Addend; + break; + } + } +} + // The target location for the relocation is described by RE.SectionID and // RE.Offset. RE.SectionID can be used to find the SectionEntry. Each // SectionEntry has three members describing its location. @@ -1076,12 +1205,17 @@ void RuntimeDyldELF::resolveRelocation(const SectionEntry &Section, case Triple::bpfeb: resolveBPFRelocation(Section, Offset, Value, Type, Addend); break; + case Triple::riscv32: // Fall through. + case Triple::riscv64: + resolveRISCVRelocation(Section, Offset, Value, Type, Addend, SectionID); + break; default: llvm_unreachable("Unsupported CPU type!"); } } -void *RuntimeDyldELF::computePlaceholderAddress(unsigned SectionID, uint64_t Offset) const { +void *RuntimeDyldELF::computePlaceholderAddress(unsigned SectionID, + uint64_t Offset) const { return (void *)(Sections[SectionID].getObjAddress() + Offset); } @@ -1870,7 +2004,8 @@ RuntimeDyldELF::processRelocationRef( Value.Addend += support::ulittle32_t::ref(computePlaceholderAddress(SectionID, Offset)); processSimpleRelocation(SectionID, Offset, RelType, Value); } else if (RelType == ELF::R_X86_64_PC64) { - Value.Addend += support::ulittle64_t::ref(computePlaceholderAddress(SectionID, Offset)); + Value.Addend += support::ulittle64_t::ref( + computePlaceholderAddress(SectionID, Offset)); processSimpleRelocation(SectionID, Offset, RelType, Value); } else if (RelType == ELF::R_X86_64_GOTTPOFF) { processX86_64GOTTPOFFRelocation(SectionID, Offset, Value, Addend); @@ -1884,9 +2019,23 @@ RuntimeDyldELF::processRelocationRef( } else { processSimpleRelocation(SectionID, Offset, RelType, Value); } + } else if (Arch == Triple::riscv32 || Arch == Triple::riscv64) { + // *_LO12 relocation receive information about a symbol from the + // corresponding *_HI20 relocation, so we have to collect this information + // before resolving + if (RelType == ELF::R_RISCV_GOT_HI20 || + RelType == ELF::R_RISCV_PCREL_HI20 || + RelType == ELF::R_RISCV_TPREL_HI20 || + RelType == ELF::R_RISCV_TLS_GD_HI20 || + RelType == ELF::R_RISCV_TLS_GOT_HI20) { + RelocationEntry RE(SectionID, Offset, RelType, Addend); + PendingRelocs.push_back({Value, RE}); + } + processSimpleRelocation(SectionID, Offset, RelType, Value); } else { if (Arch == Triple::x86) { - Value.Addend += support::ulittle32_t::ref(computePlaceholderAddress(SectionID, Offset)); + Value.Addend += support::ulittle32_t::ref( + computePlaceholderAddress(SectionID, Offset)); } processSimpleRelocation(SectionID, Offset, RelType, Value); } diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h index 1b90013dfe2df9..97517884654bc5 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h @@ -58,6 +58,10 @@ class RuntimeDyldELF : public RuntimeDyldImpl { void resolveBPFRelocation(const SectionEntry &Section, uint64_t Offset, uint64_t Value, uint32_t Type, int64_t Addend); + void resolveRISCVRelocation(const SectionEntry &Section, uint64_t Offset, + uint64_t Value, uint32_t Type, int64_t Addend, + SID SectionID); + unsigned getMaxStubSize() const override { if (Arch == Triple::aarch64 || Arch == Triple::aarch64_be) return 20; // movz; movk; movk; movk; br @@ -146,6 +150,9 @@ class RuntimeDyldELF : public RuntimeDyldImpl { // *HI16 relocations will be added for resolving when we find matching // *LO16 part. (Mips specific) + // + // *HI20 relocations will be added for resolving when we find matching + // *LO12 part. (RISC-V specific) SmallVector<std::pair<RelocationValueRef, RelocationEntry>, 8> PendingRelocs; // When a module is loaded we save the SectionID of the EH frame section _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits