Skip to content

Commit 0cf4788

Browse files
[clang-repl] Factor out CreateJITBuilder() and allow specialization in derived classes (#84461)
The LLJITBuilder interface provides a very convenient way to configure the ORCv2 JIT engine. IncrementalExecutor already used it internally to construct the JIT, but didn't provide external access. This patch lifts control of the creation process to the Interpreter and allows injection of a custom instance through the extended interface. The Interpreter's default behavior remains unchanged and the IncrementalExecutor remains an implementation detail.
1 parent aa962d6 commit 0cf4788

File tree

5 files changed

+175
-22
lines changed

5 files changed

+175
-22
lines changed

clang/include/clang/Interpreter/Interpreter.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
namespace llvm {
3131
namespace orc {
3232
class LLJIT;
33+
class LLJITBuilder;
3334
class ThreadSafeContext;
3435
} // namespace orc
3536
} // namespace llvm
@@ -127,6 +128,13 @@ class Interpreter {
127128
// custom runtime.
128129
virtual std::unique_ptr<RuntimeInterfaceBuilder> FindRuntimeInterface();
129130

131+
// Lazily construct thev ORCv2 JITBuilder. This called when the internal
132+
// IncrementalExecutor is created. The default implementation populates an
133+
// in-process JIT with debugging support. Override this to configure the JIT
134+
// engine used for execution.
135+
virtual llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
136+
CreateJITBuilder(CompilerInstance &CI);
137+
130138
public:
131139
virtual ~Interpreter();
132140

clang/lib/Interpreter/IncrementalExecutor.cpp

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
2121
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
2222
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
23+
#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
2324
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
2425
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
2526
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
@@ -36,26 +37,28 @@ LLVM_ATTRIBUTE_USED void linkComponents() {
3637

3738
namespace clang {
3839

40+
llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
41+
IncrementalExecutor::createDefaultJITBuilder(
42+
llvm::orc::JITTargetMachineBuilder JTMB) {
43+
auto JITBuilder = std::make_unique<llvm::orc::LLJITBuilder>();
44+
JITBuilder->setJITTargetMachineBuilder(std::move(JTMB));
45+
JITBuilder->setPrePlatformSetup([](llvm::orc::LLJIT &J) {
46+
// Try to enable debugging of JIT'd code (only works with JITLink for
47+
// ELF and MachO).
48+
consumeError(llvm::orc::enableDebuggerSupport(J));
49+
return llvm::Error::success();
50+
});
51+
return std::move(JITBuilder);
52+
}
53+
3954
IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC,
40-
llvm::Error &Err,
41-
const clang::TargetInfo &TI)
55+
llvm::orc::LLJITBuilder &JITBuilder,
56+
llvm::Error &Err)
4257
: TSCtx(TSC) {
4358
using namespace llvm::orc;
4459
llvm::ErrorAsOutParameter EAO(&Err);
4560

46-
auto JTMB = JITTargetMachineBuilder(TI.getTriple());
47-
JTMB.addFeatures(TI.getTargetOpts().Features);
48-
LLJITBuilder Builder;
49-
Builder.setJITTargetMachineBuilder(JTMB);
50-
Builder.setPrePlatformSetup(
51-
[](LLJIT &J) {
52-
// Try to enable debugging of JIT'd code (only works with JITLink for
53-
// ELF and MachO).
54-
consumeError(enableDebuggerSupport(J));
55-
return llvm::Error::success();
56-
});
57-
58-
if (auto JitOrErr = Builder.create())
61+
if (auto JitOrErr = JITBuilder.create())
5962
Jit = std::move(*JitOrErr);
6063
else {
6164
Err = JitOrErr.takeError();

clang/lib/Interpreter/IncrementalExecutor.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
namespace llvm {
2424
class Error;
2525
namespace orc {
26+
class JITTargetMachineBuilder;
2627
class LLJIT;
28+
class LLJITBuilder;
2729
class ThreadSafeContext;
2830
} // namespace orc
2931
} // namespace llvm
@@ -44,8 +46,8 @@ class IncrementalExecutor {
4446
public:
4547
enum SymbolNameKind { IRName, LinkerName };
4648

47-
IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC, llvm::Error &Err,
48-
const clang::TargetInfo &TI);
49+
IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC,
50+
llvm::orc::LLJITBuilder &JITBuilder, llvm::Error &Err);
4951
~IncrementalExecutor();
5052

5153
llvm::Error addModule(PartialTranslationUnit &PTU);
@@ -56,6 +58,9 @@ class IncrementalExecutor {
5658
getSymbolAddress(llvm::StringRef Name, SymbolNameKind NameKind) const;
5759

5860
llvm::orc::LLJIT &GetExecutionEngine() { return *Jit; }
61+
62+
static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
63+
createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB);
5964
};
6065

6166
} // end namespace clang

clang/lib/Interpreter/Interpreter.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,15 +372,35 @@ Interpreter::Parse(llvm::StringRef Code) {
372372
return IncrParser->Parse(Code);
373373
}
374374

375+
static llvm::Expected<llvm::orc::JITTargetMachineBuilder>
376+
createJITTargetMachineBuilder(const std::string &TT) {
377+
if (TT == llvm::sys::getProcessTriple())
378+
// This fails immediately if the target backend is not registered
379+
return llvm::orc::JITTargetMachineBuilder::detectHost();
380+
381+
// If the target backend is not registered, LLJITBuilder::create() will fail
382+
return llvm::orc::JITTargetMachineBuilder(llvm::Triple(TT));
383+
}
384+
385+
llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
386+
Interpreter::CreateJITBuilder(CompilerInstance &CI) {
387+
auto JTMB = createJITTargetMachineBuilder(CI.getTargetOpts().Triple);
388+
if (!JTMB)
389+
return JTMB.takeError();
390+
return IncrementalExecutor::createDefaultJITBuilder(std::move(*JTMB));
391+
}
392+
375393
llvm::Error Interpreter::CreateExecutor() {
376-
const clang::TargetInfo &TI =
377-
getCompilerInstance()->getASTContext().getTargetInfo();
378394
if (IncrExecutor)
379395
return llvm::make_error<llvm::StringError>("Operation failed. "
380396
"Execution engine exists",
381397
std::error_code());
398+
llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>> JB =
399+
CreateJITBuilder(*getCompilerInstance());
400+
if (!JB)
401+
return JB.takeError();
382402
llvm::Error Err = llvm::Error::success();
383-
auto Executor = std::make_unique<IncrementalExecutor>(*TSCtx, Err, TI);
403+
auto Executor = std::make_unique<IncrementalExecutor>(*TSCtx, **JB, Err);
384404
if (!Err)
385405
IncrExecutor = std::move(Executor);
386406

clang/unittests/Interpreter/InterpreterExtensionsTest.cpp

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,21 @@
1818
#include "clang/Sema/Sema.h"
1919

2020
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
21+
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
2122
#include "llvm/Support/Error.h"
2223
#include "llvm/Support/TargetSelect.h"
24+
#include "llvm/Support/Threading.h"
2325
#include "llvm/Testing/Support/Error.h"
2426

2527
#include "gmock/gmock.h"
2628
#include "gtest/gtest.h"
29+
2730
#include <system_error>
2831

32+
#if defined(_AIX)
33+
#define CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT
34+
#endif
35+
2936
using namespace clang;
3037
namespace {
3138

@@ -41,6 +48,10 @@ struct LLVMInitRAII {
4148
LLVMInitRAII() {
4249
llvm::InitializeNativeTarget();
4350
llvm::InitializeNativeTargetAsmPrinter();
51+
LLVMInitializeARMTarget();
52+
LLVMInitializeARMTargetInfo();
53+
LLVMInitializeARMTargetMC();
54+
LLVMInitializeARMAsmPrinter();
4455
}
4556
~LLVMInitRAII() { llvm::llvm_shutdown(); }
4657
} LLVMInit;
@@ -51,12 +62,30 @@ class TestCreateResetExecutor : public Interpreter {
5162
llvm::Error &Err)
5263
: Interpreter(std::move(CI), Err) {}
5364

54-
llvm::Error testCreateExecutor() { return Interpreter::CreateExecutor(); }
65+
llvm::Error testCreateJITBuilderError() {
66+
JB = nullptr;
67+
return Interpreter::CreateExecutor();
68+
}
69+
70+
llvm::Error testCreateExecutor() {
71+
JB = std::make_unique<llvm::orc::LLJITBuilder>();
72+
return Interpreter::CreateExecutor();
73+
}
5574

5675
void resetExecutor() { Interpreter::ResetExecutor(); }
76+
77+
private:
78+
llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
79+
CreateJITBuilder(CompilerInstance &CI) override {
80+
if (JB)
81+
return std::move(JB);
82+
return llvm::make_error<llvm::StringError>("TestError", std::error_code());
83+
}
84+
85+
std::unique_ptr<llvm::orc::LLJITBuilder> JB;
5786
};
5887

59-
#ifdef _AIX
88+
#ifdef CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT
6089
TEST(InterpreterExtensionsTest, DISABLED_ExecutorCreateReset) {
6190
#else
6291
TEST(InterpreterExtensionsTest, ExecutorCreateReset) {
@@ -69,6 +98,8 @@ TEST(InterpreterExtensionsTest, ExecutorCreateReset) {
6998
llvm::Error ErrOut = llvm::Error::success();
7099
TestCreateResetExecutor Interp(cantFail(CB.CreateCpp()), ErrOut);
71100
cantFail(std::move(ErrOut));
101+
EXPECT_THAT_ERROR(Interp.testCreateJITBuilderError(),
102+
llvm::FailedWithMessage("TestError"));
72103
cantFail(Interp.testCreateExecutor());
73104
Interp.resetExecutor();
74105
cantFail(Interp.testCreateExecutor());
@@ -126,4 +157,90 @@ TEST(InterpreterExtensionsTest, FindRuntimeInterface) {
126157
EXPECT_EQ(1U, Interp.RuntimeIBPtr->TransformerQueries);
127158
}
128159

160+
class CustomJBInterpreter : public Interpreter {
161+
using CustomJITBuilderCreatorFunction =
162+
std::function<llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>()>;
163+
CustomJITBuilderCreatorFunction JBCreator = nullptr;
164+
165+
public:
166+
CustomJBInterpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &ErrOut)
167+
: Interpreter(std::move(CI), ErrOut) {}
168+
169+
~CustomJBInterpreter() override {
170+
// Skip cleanUp() because it would trigger LLJIT default dtors
171+
Interpreter::ResetExecutor();
172+
}
173+
174+
void setCustomJITBuilderCreator(CustomJITBuilderCreatorFunction Fn) {
175+
JBCreator = std::move(Fn);
176+
}
177+
178+
llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
179+
CreateJITBuilder(CompilerInstance &CI) override {
180+
if (JBCreator)
181+
return JBCreator();
182+
return Interpreter::CreateJITBuilder(CI);
183+
}
184+
185+
llvm::Error CreateExecutor() { return Interpreter::CreateExecutor(); }
186+
};
187+
188+
#ifdef CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT
189+
TEST(InterpreterExtensionsTest, DISABLED_DefaultCrossJIT) {
190+
#else
191+
TEST(InterpreterExtensionsTest, DefaultCrossJIT) {
192+
#endif
193+
IncrementalCompilerBuilder CB;
194+
CB.SetTargetTriple("armv6-none-eabi");
195+
auto CI = cantFail(CB.CreateCpp());
196+
llvm::Error ErrOut = llvm::Error::success();
197+
CustomJBInterpreter Interp(std::move(CI), ErrOut);
198+
cantFail(std::move(ErrOut));
199+
cantFail(Interp.CreateExecutor());
200+
}
201+
202+
#ifdef CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT
203+
TEST(InterpreterExtensionsTest, DISABLED_CustomCrossJIT) {
204+
#else
205+
TEST(InterpreterExtensionsTest, CustomCrossJIT) {
206+
#endif
207+
std::string TargetTriple = "armv6-none-eabi";
208+
209+
IncrementalCompilerBuilder CB;
210+
CB.SetTargetTriple(TargetTriple);
211+
auto CI = cantFail(CB.CreateCpp());
212+
llvm::Error ErrOut = llvm::Error::success();
213+
CustomJBInterpreter Interp(std::move(CI), ErrOut);
214+
cantFail(std::move(ErrOut));
215+
216+
using namespace llvm::orc;
217+
LLJIT *JIT = nullptr;
218+
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Objs;
219+
Interp.setCustomJITBuilderCreator([&]() {
220+
auto JTMB = JITTargetMachineBuilder(llvm::Triple(TargetTriple));
221+
JTMB.setCPU("cortex-m0plus");
222+
auto JB = std::make_unique<LLJITBuilder>();
223+
JB->setJITTargetMachineBuilder(JTMB);
224+
JB->setPlatformSetUp(setUpInactivePlatform);
225+
JB->setNotifyCreatedCallback([&](LLJIT &J) {
226+
ObjectLayer &ObjLayer = J.getObjLinkingLayer();
227+
auto *JITLinkObjLayer = llvm::dyn_cast<ObjectLinkingLayer>(&ObjLayer);
228+
JITLinkObjLayer->setReturnObjectBuffer(
229+
[&Objs](std::unique_ptr<llvm::MemoryBuffer> MB) {
230+
Objs.push_back(std::move(MB));
231+
});
232+
JIT = &J;
233+
return llvm::Error::success();
234+
});
235+
return JB;
236+
});
237+
238+
EXPECT_EQ(0U, Objs.size());
239+
cantFail(Interp.CreateExecutor());
240+
cantFail(Interp.ParseAndExecute("int a = 1;"));
241+
ExecutorAddr Addr = cantFail(JIT->lookup("a"));
242+
EXPECT_NE(0U, Addr.getValue());
243+
EXPECT_EQ(1U, Objs.size());
244+
}
245+
129246
} // end anonymous namespace

0 commit comments

Comments
 (0)