[Lldb-commits] [lldb] [llvm] [lldb][RISCV] function calls support in lldb expressions (PR #99336)

via lldb-commits lldb-commits at lists.llvm.org
Wed Jul 17 08:08:16 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: None (dlav-sc)

<details>
<summary>Changes</summary>

To make function calls inside lldb expressions ABI support, JIT engine support are required. This patch augments corresponding functionality to RISCV ABI and implements RISCV relocation resolver in JIT, which allows to make function calls with integer and void function arguments and return value. Also it adds RISCV specific DirectToIndirectFunctionCallsReplacement IR pass, that allows to make assembly jumps at any 64bit address without RISCV large code model, which has not been implemented yet. This pass is needed, because jitted code contains more that +-2GB jumps, which are not available in RISCV without large code model now.

Fixed tests:
```
TestCallThatRestarts.ExprCommandThatRestartsTestCase
TestMainThreadExit.ThreadExitTestCase
TestSBValuePersist.SBValuePersistTestCase
TestThisClassTypeMixing.TestCase
Test11588.Issue11581TestCase
TestExprsChar.ExprCharTestCase
TestExpressions.TestExpressions
TestCallWithTimeout.ExprCommandWithTimeoutsTestCase
TestInlineNamespace.TestInlineNamespace
TestInlineNamespaceAlias.TestInlineNamespace
TestSetValues.SetValuesTestCase
TestValueAPIAddressOfVoidStar.ValueAPIVoidStarTestCase
TestXValuePrinting.ExprXValuePrintingTestCase
lldbsuite.test.lldbtest.TestRedefinitionsInInlines
lldbsuite.test.lldbtest.TestVirtualOverload
TestSetValues.SetValuesTestCase
TestExprs.BasicExprCommandsTestCase
TestExprHelpExamples.Radar9673644TestCase
TestExprEntryBP.ExprEntryBPTestCase
TestExprInsideLambdas.ExprInsideLambdaTestCase
TestConstStaticIntegralMemberInt128.TestCase
TestDWIMPrint.TestCase
TestCPPStaticMembers.TestCase
TestNestedPersistentTypes.NestedPersistentTypesTestCase
TestSaveJITObjects.SaveJITObjectsTestCase
TestNamespaceLookup.NamespaceLookupTestCase
TestCCallingConventions.TestCase
TestCppTypedef.TestCppTypedef
TestUnwindExpression.UnwindFromExpressionTest
TestCxxChar8_t.CxxChar8_tTestCase
TestCallCPPFunction.CallCPPFunctionTestCase
TestExprDoesntBlock.ExprDoesntDeadlockTestCase
TestEnumTypes.EnumTypesTestCase
TestAnonymous.AnonymousTestCase
```

---

Patch is 35.55 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/99336.diff


15 Files Affected:

- (modified) lldb/include/lldb/Core/Architecture.h (+12) 
- (modified) lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp (+82-7) 
- (modified) lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.h (+3) 
- (modified) lldb/source/Plugins/Architecture/CMakeLists.txt (+1) 
- (added) lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.cpp (+55) 
- (added) lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.h (+34) 
- (added) lldb/source/Plugins/Architecture/RISCV/CMakeLists.txt (+12) 
- (added) lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.cpp (+194) 
- (added) lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.h (+58) 
- (modified) lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp (+23) 
- (added) lldb/test/API/riscv/expressions/Makefile (+3) 
- (added) lldb/test/API/riscv/expressions/TestExpressions.py (+88) 
- (added) lldb/test/API/riscv/expressions/main.cpp (+51) 
- (modified) llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp (+152-3) 
- (modified) llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h (+7) 


``````````diff
diff --git a/lldb/include/lldb/Core/Architecture.h b/lldb/include/lldb/Core/Architecture.h
index b6fc1a20e1e69..ca6a9207d8012 100644
--- a/lldb/include/lldb/Core/Architecture.h
+++ b/lldb/include/lldb/Core/Architecture.h
@@ -12,6 +12,7 @@
 #include "lldb/Core/PluginInterface.h"
 #include "lldb/Target/DynamicRegisterInfo.h"
 #include "lldb/Target/MemoryTagManager.h"
+#include "llvm/IR/LegacyPassManager.h"
 
 namespace lldb_private {
 
@@ -129,6 +130,17 @@ class Architecture : public PluginInterface {
                                        RegisterContext &reg_context) const {
     return false;
   }
+
+  // Takes a Pass Manager and adds passes for this Architecture that should be
+  // run before IRForTarget
+  virtual std::unique_ptr<llvm::legacy::PassManager>
+  GetArchitectureCustomPasses(const ExecutionContext &exe_ctx,
+                              const llvm::StringRef expr) const {
+    return nullptr;
+  }
+
+  static constexpr llvm::StringLiteral s_target_incompatibility_marker =
+      "target_incompatibility_detected";
 };
 
 } // namespace lldb_private
diff --git a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp
index 6395f5bb5bd9b..f3edee1dd6dc1 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 "lldb/Core/PluginManager.h"
@@ -19,6 +21,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()
@@ -163,11 +166,83 @@ 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 %s: 0x%" PRIx64, reg_info->name,
+           static_cast<uint64_t>(value));
+  if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, value)) {
+    LLDB_LOG(log, "Writing %s: 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) {
+  assert(log);
+  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() > s_regs_for_args_count) {
+    LLDB_LOG(log, "Function has %lu arguments, but only %lu are allowed!",
+             args.size(), s_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%lu (0x%" PRIx64 ") into %s", idx, arg,
+             reg_info->name);
+
+    if (!reg_ctx_sp->WriteRegisterFromUnsigned(reg_info, arg)) {
+      LLDB_LOG(log, "Failed to write arg%lu (0x%" PRIx64 ") into %s", 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::%s: success", __func__);
+  return true;
 }
 
 bool ABISysV_riscv::PrepareTrivialCall(
@@ -221,14 +296,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 > s_regs_for_args_count ? s_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 <= s_regs_for_args_count
+                      ? 0
+                      : args_size - s_regs_for_args_count;
   auto offset = on_stack * word_size;
 
   uint8_t reg_value[8];
@@ -259,7 +334,7 @@ bool ABISysV_riscv::PrepareTrivialCall(
       ++reg_index;
     }
 
-    if (reg_index < regs_for_args_count || size == 0)
+    if (reg_index < s_regs_for_args_count || size == 0)
       continue;
 
     // Remaining arguments are passed on the stack.
diff --git a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.h b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.h
index d8cf008dbb0bf..04ec018c8a718 100644
--- a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.h
+++ b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.h
@@ -124,6 +124,9 @@ class ABISysV_riscv : public lldb_private::RegInfoBasedABI {
   using lldb_private::RegInfoBasedABI::RegInfoBasedABI; // Call CreateInstance
                                                         // instead.
   bool m_is_rv64; // true if target is riscv64; false if target is riscv32
+  static constexpr size_t s_regs_for_args_count =
+      8U; // number of argument registers (the base integer calling convention
+          // provides 8 argument registers, a0-a7)
 };
 
 #endif // liblldb_ABISysV_riscv_h_
diff --git a/lldb/source/Plugins/Architecture/CMakeLists.txt b/lldb/source/Plugins/Architecture/CMakeLists.txt
index 9ed8edf70af3f..78cdaa0bdf2d4 100644
--- a/lldb/source/Plugins/Architecture/CMakeLists.txt
+++ b/lldb/source/Plugins/Architecture/CMakeLists.txt
@@ -2,3 +2,4 @@ add_subdirectory(Arm)
 add_subdirectory(Mips)
 add_subdirectory(PPC64)
 add_subdirectory(AArch64)
+add_subdirectory(RISCV)
diff --git a/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.cpp b/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.cpp
new file mode 100644
index 0000000000000..e4608b41bd787
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.cpp
@@ -0,0 +1,55 @@
+//===-- ArchitectureRISCV.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 "Plugins/Architecture/RISCV/ArchitectureRISCV.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ArchSpec.h"
+
+#include "llvm/IR/LegacyPassManager.h"
+
+#include "DirectToIndirectFCR.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+LLDB_PLUGIN_DEFINE(ArchitectureRISCV)
+
+void ArchitectureRISCV::Initialize() {
+  PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                "RISCV-specific algorithms",
+                                &ArchitectureRISCV::Create);
+}
+
+void ArchitectureRISCV::Terminate() {
+  PluginManager::UnregisterPlugin(&ArchitectureRISCV::Create);
+}
+
+std::unique_ptr<Architecture> ArchitectureRISCV::Create(const ArchSpec &arch) {
+  if (!arch.GetTriple().isRISCV())
+    return nullptr;
+  return std::unique_ptr<Architecture>(new ArchitectureRISCV());
+}
+
+void ArchitectureRISCV::OverrideStopInfo(Thread &thread) const {}
+
+std::unique_ptr<llvm::legacy::PassManager>
+ArchitectureRISCV::GetArchitectureCustomPasses(
+    const ExecutionContext &exe_ctx, const llvm::StringRef expr) const {
+  // LLDB generates additional support functions like
+  // '_$__lldb_valid_pointer_check', that do not require custom passes
+  if (expr != "$__lldb_expr")
+    return nullptr;
+
+  std::unique_ptr<llvm::legacy::PassManager> custom_passes =
+      std::make_unique<llvm::legacy::PassManager>();
+  auto *P = createDirectToIndirectFCR(exe_ctx);
+  custom_passes->add(P);
+  return custom_passes;
+}
diff --git a/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.h b/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.h
new file mode 100644
index 0000000000000..6ef6c62de5f27
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.h
@@ -0,0 +1,34 @@
+//===-- ArchitectureRISCV.h -------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+#include "lldb/Core/Architecture.h"
+
+namespace lldb_private {
+
+class ArchitectureRISCV : public Architecture {
+public:
+  static llvm::StringRef GetPluginNameStatic() { return "riscv"; }
+  static void Initialize();
+  static void Terminate();
+
+  llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+  void OverrideStopInfo(Thread &thread) const override;
+
+  std::unique_ptr<llvm::legacy::PassManager>
+  GetArchitectureCustomPasses(const ExecutionContext &exe_ctx,
+                              const llvm::StringRef expr) const override;
+
+private:
+  static std::unique_ptr<Architecture> Create(const ArchSpec &arch);
+  ArchitectureRISCV() = default;
+};
+
+} // namespace lldb_private
diff --git a/lldb/source/Plugins/Architecture/RISCV/CMakeLists.txt b/lldb/source/Plugins/Architecture/RISCV/CMakeLists.txt
new file mode 100644
index 0000000000000..f2545eb35b000
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/RISCV/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_lldb_library(lldbPluginArchitectureRISCV PLUGIN
+  ArchitectureRISCV.cpp
+  DirectToIndirectFCR.cpp
+
+  LINK_LIBS
+    lldbPluginProcessUtility
+    lldbCore
+    lldbTarget
+    lldbUtility
+  LINK_COMPONENTS
+    Support
+  )
diff --git a/lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.cpp b/lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.cpp
new file mode 100644
index 0000000000000..71d3001fc06e8
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.cpp
@@ -0,0 +1,194 @@
+//===--- DirectToIndirectFCR.cpp - RISC-V specific pass -------------------===//
+//
+// 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 "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Value.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+
+#include "Plugins/Architecture/RISCV/DirectToIndirectFCR.h"
+
+#include "lldb/Core/Architecture.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Symtab.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+#include <optional>
+
+using namespace llvm;
+using namespace lldb_private;
+
+namespace {
+std::string GetValueTypeStr(const llvm::Type *value_ty) {
+  assert(value_ty);
+  std::string str_type;
+  llvm::raw_string_ostream rso(str_type);
+  value_ty->print(rso);
+  return rso.str();
+}
+
+template <typename... Args> void LogMessage(const char *msg, Args &&...args) {
+  Log *log = GetLog(LLDBLog::Expressions);
+  LLDB_LOG(log, msg, std::forward<Args>(args)...);
+}
+} // namespace
+
+bool DirectToIndirectFCR::canBeReplaced(const llvm::CallInst *ci) {
+  assert(ci);
+  auto *return_value_ty = ci->getType();
+  if (!(return_value_ty->isIntegerTy() || return_value_ty->isVoidTy())) {
+    LogMessage("DirectToIndirectFCR: function {0} has unsupported "
+               "return type ({1})\n",
+               ci->getCalledFunction()->getName(),
+               GetValueTypeStr(return_value_ty));
+    return false;
+  }
+
+  const auto *arg = llvm::find_if_not(ci->args(), [](const auto &arg) {
+    const auto *type = arg->getType();
+    return type->isIntegerTy();
+  });
+
+  if (arg != ci->arg_end()) {
+    LogMessage("DirectToIndirectFCR: argument {0} of {1} function "
+               "has unsupported type ({2})\n",
+               (*arg)->getName(), ci->getCalledFunction()->getName(),
+               GetValueTypeStr((*arg)->getType()));
+    return false;
+  }
+  return true;
+}
+
+std::vector<llvm::Value *>
+DirectToIndirectFCR::getFunctionArgsAsValues(const llvm::CallInst *ci) {
+  assert(ci);
+  std::vector<llvm::Value *> args{};
+  llvm::transform(ci->args(), std::back_inserter(args),
+                  [](const auto &arg) { return arg.get(); });
+  return args;
+}
+
+std::optional<lldb::addr_t>
+DirectToIndirectFCR::getFunctionAddress(const llvm::CallInst *ci) const {
+  auto *target = m_exe_ctx.GetTargetPtr();
+  const auto &lldb_module_sp = target->GetExecutableModule();
+  const auto &symtab = lldb_module_sp->GetSymtab();
+  const llvm::StringRef name = ci->getCalledFunction()->getName();
+
+  // eSymbolTypeCode: we try to find function
+  // eDebugNo: not a debug symbol
+  // eVisibilityExtern: function from extern module
+  const auto *symbol = symtab->FindFirstSymbolWithNameAndType(
+      ConstString(name), lldb::SymbolType::eSymbolTypeCode,
+      Symtab::Debug::eDebugNo, Symtab::Visibility::eVisibilityExtern);
+  if (!symbol) {
+    LogMessage("DirectToIndirectFCR: can't find {0} in symtab\n", name);
+    return std::nullopt;
+  }
+
+  lldb::addr_t addr = symbol->GetLoadAddress(target);
+  LogMessage("DirectToIndirectFCR: found address ({0}) of symbol {1}\n", addr,
+             name);
+  return addr;
+}
+
+llvm::CallInst *DirectToIndirectFCR::getInstReplace(llvm::CallInst *ci) const {
+  assert(ci);
+
+  std::optional<lldb::addr_t> addr_or_null = getFunctionAddress(ci);
+  if (!addr_or_null.has_value())
+    return nullptr;
+
+  lldb::addr_t addr = addr_or_null.value();
+
+  llvm::IRBuilder<> builder(ci);
+
+  std::vector<llvm::Value *> args = getFunctionArgsAsValues(ci);
+  llvm::Constant *func_addr = builder.getInt64(addr);
+  llvm::PointerType *ptr_func_ty = builder.getPtrTy();
+  auto *cast = builder.CreateIntToPtr(func_addr, ptr_func_ty);
+  auto *new_inst =
+      builder.CreateCall(ci->getFunctionType(), cast, ArrayRef(args));
+  return new_inst;
+}
+
+DirectToIndirectFCR::DirectToIndirectFCR(const ExecutionContext &exe_ctx)
+    : FunctionPass(ID), m_exe_ctx{exe_ctx} {}
+
+DirectToIndirectFCR::~DirectToIndirectFCR() = default;
+
+bool DirectToIndirectFCR::runOnFunction(llvm::Function &func) {
+  bool has_irreplaceable =
+      llvm::any_of(instructions(func), [this](llvm::Instruction &inst) {
+        llvm::CallInst *ci = dyn_cast<llvm::CallInst>(&inst);
+        if (!ci || ci->getCalledFunction()->isIntrinsic() ||
+            (DirectToIndirectFCR::canBeReplaced(ci) &&
+             getFunctionAddress(ci).has_value()))
+          return false;
+        return true;
+      });
+
+  if (has_irreplaceable) {
+    func.getParent()->getOrInsertNamedMetadata(
+        Architecture::s_target_incompatibility_marker);
+    return false;
+  }
+
+  std::vector<std::reference_wrapper<llvm::Instruction>>
+      replaceable_function_calls{};
+  llvm::copy_if(instructions(func),
+                std::back_inserter(replaceable_function_calls),
+                [](llvm::Instruction &inst) {
+                  llvm::CallInst *ci = dyn_cast<llvm::CallInst>(&inst);
+                  if (ci && !ci->getCalledFunction()->isIntrinsic())
+                    return true;
+                  return false;
+                });
+
+  if (replaceable_function_calls.empty())
+    return false;
+
+  std::vector<std::pair<llvm::CallInst *, llvm::CallInst *>> replaces;
+  llvm::transform(replaceable_function_calls, std::back_inserter(replaces),
+                  [this](std::reference_wrapper<llvm::Instruction> inst)
+                      -> std::pair<llvm::CallInst *, llvm::CallInst *> {
+                    llvm::CallInst *ci = cast<llvm::CallInst>(&(inst.get()));
+                    llvm::CallInst *new_inst = getInstReplace(ci);
+                    return {ci, new_inst};
+                  });
+
+  for (auto &&[from, to] : replaces) {
+    from->replaceAllUsesWith(to);
+    from->eraseFromParent();
+  }
+
+  return true;
+}
+
+llvm::StringRef DirectToIndirectFCR::getPassName() const {
+  return "Transform function calls to calls by address";
+}
+
+char DirectToIndirectFCR::ID = 0;
+
+llvm::FunctionPass *
+lldb_private::createDirectToIndirectFCR(const ExecutionContext &exe_ctx) {
+  return new DirectToIndirectFCR(exe_ctx);
+}
diff --git a/lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.h b/lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.h
new file mode 100644
index 0000000000000..5ac5820bd95a0
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.h
@@ -0,0 +1,58 @@
+//===--- DirectToIndirectFCR.h - RISC-V specific pass ---------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+#include "lldb/lldb-types.h"
+
+#include "llvm/IR/Instructions.h"
+#include "llvm/Pass.h"
+
+namespace lldb_private {
+
+class ExecutionContext;
+
+// During the lldb expression execution lldb wraps a user expression, jittes
+// fabricated code and then puts it into the stack memory. Thus, if user tried
+// to make a function call there will be a jump from a stack address to a code
+// sections's address. RISC-V Architecture doesn't have a large code model yet
+// and can make only a +-2GiB jumps, but in 64-bit architecture a distance
+// between stack addresses and code sections's addresses is longer. Therefore,
+// relocations resolver obtains an invalid address. To avoid such problem, this
+// pass should be used. It replaces function calls with appropriate function's
+// addresses explicitly. By doing so it removes relocations related to function
+// calls. This pass should be cosidered as temprorary solution until a large
+// code model will be approved.
+class DirectToIndirectFCR : public llvm::FunctionPass {
+
+  static bool canBeReplaced(const llvm::CallInst *ci);
+
+  static std::vector<llvm::Value *>
+  getFunctionArgsAsValues(const llvm::CallInst *ci);
+
+  std::optional<lldb::addr_t>
+  getFunctionAddress(const llvm::CallInst *ci) const;
+
+  llvm::CallInst *getInstReplace(llvm::CallInst *ci) const;
+
+public:
+  static char ID;
+
+  DirectToIndirectFCR(const ExecutionContext &exe_ctx);
+  ~DirectT...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/99336


More information about the lldb-commits mailing list