[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.
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
namespace llvm {
|
||||
namespace orc {
|
||||
class LLJIT;
|
||||
class LLJITBuilder;
|
||||
class ThreadSafeContext;
|
||||
} // namespace orc
|
||||
} // namespace llvm
|
||||
@@ -127,6 +128,13 @@ protected:
|
||||
// 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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 @@ public:
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -18,14 +18,21 @@
|
||||
#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"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <system_error>
|
||||
|
||||
#if defined(_AIX)
|
||||
#define CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT
|
||||
#endif
|
||||
|
||||
using namespace clang;
|
||||
namespace {
|
||||
|
||||
@@ -41,6 +48,10 @@ struct LLVMInitRAII {
|
||||
LLVMInitRAII() {
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
LLVMInitializeARMTarget();
|
||||
LLVMInitializeARMTargetInfo();
|
||||
LLVMInitializeARMTargetMC();
|
||||
LLVMInitializeARMAsmPrinter();
|
||||
}
|
||||
~LLVMInitRAII() { llvm::llvm_shutdown(); }
|
||||
} LLVMInit;
|
||||
@@ -51,12 +62,30 @@ public:
|
||||
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;
|
||||
};
|
||||
|
||||
#ifdef _AIX
|
||||
#ifdef CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT
|
||||
TEST(InterpreterExtensionsTest, DISABLED_ExecutorCreateReset) {
|
||||
#else
|
||||
TEST(InterpreterExtensionsTest, ExecutorCreateReset) {
|
||||
@@ -69,6 +98,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());
|
||||
@@ -126,4 +157,90 @@ 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(); }
|
||||
};
|
||||
|
||||
#ifdef CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT
|
||||
TEST(InterpreterExtensionsTest, DISABLED_DefaultCrossJIT) {
|
||||
#else
|
||||
TEST(InterpreterExtensionsTest, DefaultCrossJIT) {
|
||||
#endif
|
||||
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));
|
||||
cantFail(Interp.CreateExecutor());
|
||||
}
|
||||
|
||||
#ifdef CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT
|
||||
TEST(InterpreterExtensionsTest, DISABLED_CustomCrossJIT) {
|
||||
#else
|
||||
TEST(InterpreterExtensionsTest, CustomCrossJIT) {
|
||||
#endif
|
||||
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([&]() {
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user