[clang] [clang-repl] Factor out CreateJITBuilder() and allow specialization in derived classes (PR #84461)

Stefan Gränitz via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 12 06:18:33 PDT 2024


https://github.com/weliveindetail updated https://github.com/llvm/llvm-project/pull/84461

>From 88271c39b30b84041b4b7fb8b8f34c211d8190d5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Thu, 7 Mar 2024 23:04:22 +0100
Subject: [PATCH] [clang-repl] Add CreateJITBuilder() for specialization in
 derived classes

The LLJITBuilder interface provides a very convenient way to configure the JIT.
---
 clang/include/clang/Interpreter/Interpreter.h |   9 ++
 clang/lib/Interpreter/IncrementalExecutor.cpp |  33 ++---
 clang/lib/Interpreter/IncrementalExecutor.h   |   9 +-
 clang/lib/Interpreter/Interpreter.cpp         |  26 +++-
 .../Interpreter/InterpreterExtensionsTest.cpp | 119 +++++++++++++++++-
 5 files changed, 175 insertions(+), 21 deletions(-)

diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 1dcba1ef967980..33ce4bbf5bea10 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -29,7 +29,9 @@
 
 namespace llvm {
 namespace orc {
+class JITTargetMachineBuilder;
 class LLJIT;
+class LLJITBuilder;
 class ThreadSafeContext;
 } // namespace orc
 } // namespace llvm
@@ -127,6 +129,13 @@ class Interpreter {
   // custom runtime.
   virtual std::unique_ptr<RuntimeInterfaceBuilder> FindRuntimeInterface();
 
+  // Lazily construct thev ORCv2 JITBuilder. This called when the internal
+  // IncrementalExecutor is created. The default implementation populates an
+  // in-process JIT with debugging support. Override this to configure the JIT
+  // engine used for execution.
+  virtual llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
+  CreateJITBuilder(CompilerInstance &CI);
+
 public:
   virtual ~Interpreter();
 
diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp
index 40bcef94797d43..6f036107c14a9c 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.cpp
+++ b/clang/lib/Interpreter/IncrementalExecutor.cpp
@@ -20,6 +20,7 @@
 #include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
 #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
 #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
+#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
 #include "llvm/ExecutionEngine/Orc/LLJIT.h"
 #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
 #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
@@ -36,26 +37,28 @@ LLVM_ATTRIBUTE_USED void linkComponents() {
 
 namespace clang {
 
+llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
+IncrementalExecutor::createDefaultJITBuilder(
+    llvm::orc::JITTargetMachineBuilder JTMB) {
+  auto JITBuilder = std::make_unique<llvm::orc::LLJITBuilder>();
+  JITBuilder->setJITTargetMachineBuilder(std::move(JTMB));
+  JITBuilder->setPrePlatformSetup([](llvm::orc::LLJIT &J) {
+    // Try to enable debugging of JIT'd code (only works with JITLink for
+    // ELF and MachO).
+    consumeError(llvm::orc::enableDebuggerSupport(J));
+    return llvm::Error::success();
+  });
+  return std::move(JITBuilder);
+}
+
 IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC,
-                                         llvm::Error &Err,
-                                         const clang::TargetInfo &TI)
+                                         llvm::orc::LLJITBuilder &JITBuilder,
+                                         llvm::Error &Err)
     : TSCtx(TSC) {
   using namespace llvm::orc;
   llvm::ErrorAsOutParameter EAO(&Err);
 
-  auto JTMB = JITTargetMachineBuilder(TI.getTriple());
-  JTMB.addFeatures(TI.getTargetOpts().Features);
-  LLJITBuilder Builder;
-  Builder.setJITTargetMachineBuilder(JTMB);
-  Builder.setPrePlatformSetup(
-      [](LLJIT &J) {
-        // Try to enable debugging of JIT'd code (only works with JITLink for
-        // ELF and MachO).
-        consumeError(enableDebuggerSupport(J));
-        return llvm::Error::success();
-      });
-
-  if (auto JitOrErr = Builder.create())
+  if (auto JitOrErr = JITBuilder.create())
     Jit = std::move(*JitOrErr);
   else {
     Err = JitOrErr.takeError();
diff --git a/clang/lib/Interpreter/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h
index dd0a210a061415..b4347209e14fe3 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.h
+++ b/clang/lib/Interpreter/IncrementalExecutor.h
@@ -23,7 +23,9 @@
 namespace llvm {
 class Error;
 namespace orc {
+class JITTargetMachineBuilder;
 class LLJIT;
+class LLJITBuilder;
 class ThreadSafeContext;
 } // namespace orc
 } // namespace llvm
@@ -44,8 +46,8 @@ class IncrementalExecutor {
 public:
   enum SymbolNameKind { IRName, LinkerName };
 
-  IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC, llvm::Error &Err,
-                      const clang::TargetInfo &TI);
+  IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC,
+                      llvm::orc::LLJITBuilder &JITBuilder, llvm::Error &Err);
   ~IncrementalExecutor();
 
   llvm::Error addModule(PartialTranslationUnit &PTU);
@@ -56,6 +58,9 @@ class IncrementalExecutor {
   getSymbolAddress(llvm::StringRef Name, SymbolNameKind NameKind) const;
 
   llvm::orc::LLJIT &GetExecutionEngine() { return *Jit; }
+
+  static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
+  createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB);
 };
 
 } // end namespace clang
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 7fa52f2f15fc49..cf31456b6950ac 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -372,15 +372,35 @@ Interpreter::Parse(llvm::StringRef Code) {
   return IncrParser->Parse(Code);
 }
 
+static llvm::Expected<llvm::orc::JITTargetMachineBuilder>
+createJITTargetMachineBuilder(const std::string &TT) {
+  if (TT == llvm::sys::getProcessTriple())
+    // This fails immediately if the target backend is not registered
+    return llvm::orc::JITTargetMachineBuilder::detectHost();
+
+  // If the target backend is not registered, LLJITBuilder::create() will fail
+  return llvm::orc::JITTargetMachineBuilder(llvm::Triple(TT));
+}
+
+llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
+Interpreter::CreateJITBuilder(CompilerInstance &CI) {
+  auto JTMB = createJITTargetMachineBuilder(CI.getTargetOpts().Triple);
+  if (!JTMB)
+    return JTMB.takeError();
+  return IncrementalExecutor::createDefaultJITBuilder(std::move(*JTMB));
+}
+
 llvm::Error Interpreter::CreateExecutor() {
-  const clang::TargetInfo &TI =
-      getCompilerInstance()->getASTContext().getTargetInfo();
   if (IncrExecutor)
     return llvm::make_error<llvm::StringError>("Operation failed. "
                                                "Execution engine exists",
                                                std::error_code());
+  llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>> JB =
+      CreateJITBuilder(*getCompilerInstance());
+  if (!JB)
+    return JB.takeError();
   llvm::Error Err = llvm::Error::success();
-  auto Executor = std::make_unique<IncrementalExecutor>(*TSCtx, Err, TI);
+  auto Executor = std::make_unique<IncrementalExecutor>(*TSCtx, **JB, Err);
   if (!Err)
     IncrExecutor = std::move(Executor);
 
diff --git a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
index f1c3d65ab0a95d..a7778ddfcfd7db 100644
--- a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
+++ b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
@@ -17,7 +17,11 @@
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Sema.h"
 
+#include "llvm/ExecutionEngine/Orc/LLJIT.h"
+#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/Threading.h"
 #include "llvm/Testing/Support/Error.h"
 
 #include "gmock/gmock.h"
@@ -33,9 +37,27 @@ class TestCreateResetExecutor : public Interpreter {
                           llvm::Error &Err)
       : Interpreter(std::move(CI), Err) {}
 
-  llvm::Error testCreateExecutor() { return Interpreter::CreateExecutor(); }
+  llvm::Error testCreateJITBuilderError() {
+    JB = nullptr;
+    return Interpreter::CreateExecutor();
+  }
+
+  llvm::Error testCreateExecutor() {
+    JB = std::make_unique<llvm::orc::LLJITBuilder>();
+    return Interpreter::CreateExecutor();
+  }
 
   void resetExecutor() { Interpreter::ResetExecutor(); }
+
+private:
+  llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
+  CreateJITBuilder(CompilerInstance &CI) override {
+    if (JB)
+      return std::move(JB);
+    return llvm::make_error<llvm::StringError>("TestError", std::error_code());
+  }
+
+  std::unique_ptr<llvm::orc::LLJITBuilder> JB;
 };
 
 TEST(InterpreterExtensionsTest, ExecutorCreateReset) {
@@ -43,6 +65,8 @@ TEST(InterpreterExtensionsTest, ExecutorCreateReset) {
   llvm::Error ErrOut = llvm::Error::success();
   TestCreateResetExecutor Interp(cantFail(CB.CreateCpp()), ErrOut);
   cantFail(std::move(ErrOut));
+  EXPECT_THAT_ERROR(Interp.testCreateJITBuilderError(),
+                    llvm::FailedWithMessage("TestError"));
   cantFail(Interp.testCreateExecutor());
   Interp.resetExecutor();
   cantFail(Interp.testCreateExecutor());
@@ -100,4 +124,97 @@ TEST(InterpreterExtensionsTest, FindRuntimeInterface) {
   EXPECT_EQ(1U, Interp.RuntimeIBPtr->TransformerQueries);
 }
 
+class CustomJBInterpreter : public Interpreter {
+  using CustomJITBuilderCreatorFunction =
+      std::function<llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>()>;
+  CustomJITBuilderCreatorFunction JBCreator = nullptr;
+
+public:
+  CustomJBInterpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &ErrOut)
+      : Interpreter(std::move(CI), ErrOut) {}
+
+  ~CustomJBInterpreter() override {
+    // Skip cleanUp() because it would trigger LLJIT default dtors
+    Interpreter::ResetExecutor();
+  }
+
+  void setCustomJITBuilderCreator(CustomJITBuilderCreatorFunction Fn) {
+    JBCreator = std::move(Fn);
+  }
+
+  llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
+  CreateJITBuilder(CompilerInstance &CI) override {
+    if (JBCreator)
+      return JBCreator();
+    return Interpreter::CreateJITBuilder(CI);
+  }
+
+  llvm::Error CreateExecutor() { return Interpreter::CreateExecutor(); }
+};
+
+static void initArmTarget() {
+  static llvm::once_flag F;
+  llvm::call_once(F, [] {
+    LLVMInitializeARMTarget();
+    LLVMInitializeARMTargetInfo();
+    LLVMInitializeARMTargetMC();
+    LLVMInitializeARMAsmPrinter();
+  });
+}
+
+llvm::llvm_shutdown_obj Shutdown;
+
+TEST(InterpreterExtensionsTest, DefaultCrossJIT) {
+  IncrementalCompilerBuilder CB;
+  CB.SetTargetTriple("armv6-none-eabi");
+  auto CI = cantFail(CB.CreateCpp());
+  llvm::Error ErrOut = llvm::Error::success();
+  CustomJBInterpreter Interp(std::move(CI), ErrOut);
+  cantFail(std::move(ErrOut));
+
+  initArmTarget();
+  cantFail(Interp.CreateExecutor());
+}
+
+TEST(InterpreterExtensionsTest, CustomCrossJIT) {
+  std::string TargetTriple = "armv6-none-eabi";
+
+  IncrementalCompilerBuilder CB;
+  CB.SetTargetTriple(TargetTriple);
+  auto CI = cantFail(CB.CreateCpp());
+  llvm::Error ErrOut = llvm::Error::success();
+  CustomJBInterpreter Interp(std::move(CI), ErrOut);
+  cantFail(std::move(ErrOut));
+
+  using namespace llvm::orc;
+  LLJIT *JIT = nullptr;
+  std::vector<std::unique_ptr<llvm::MemoryBuffer>> Objs;
+  Interp.setCustomJITBuilderCreator([&]() {
+    initArmTarget();
+    auto JTMB = JITTargetMachineBuilder(llvm::Triple(TargetTriple));
+    JTMB.setCPU("cortex-m0plus");
+    auto JB = std::make_unique<LLJITBuilder>();
+    JB->setJITTargetMachineBuilder(JTMB);
+    JB->setPlatformSetUp(setUpInactivePlatform);
+    JB->setNotifyCreatedCallback([&](LLJIT &J) {
+      ObjectLayer &ObjLayer = J.getObjLinkingLayer();
+      auto *JITLinkObjLayer = llvm::dyn_cast<ObjectLinkingLayer>(&ObjLayer);
+      JITLinkObjLayer->setReturnObjectBuffer(
+          [&Objs](std::unique_ptr<llvm::MemoryBuffer> MB) {
+            Objs.push_back(std::move(MB));
+          });
+      JIT = &J;
+      return llvm::Error::success();
+    });
+    return JB;
+  });
+
+  EXPECT_EQ(0U, Objs.size());
+  cantFail(Interp.CreateExecutor());
+  cantFail(Interp.ParseAndExecute("int a = 1;"));
+  ExecutorAddr Addr = cantFail(JIT->lookup("a"));
+  EXPECT_NE(0U, Addr.getValue());
+  EXPECT_EQ(1U, Objs.size());
+}
+
 } // end anonymous namespace



More information about the cfe-commits mailing list