[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:
Stefan Gränitz
2024-03-25 09:44:25 +01:00
committed by GitHub
parent aa962d67ee
commit 0cf4788d9d
5 changed files with 175 additions and 22 deletions

View File

@@ -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();

View File

@@ -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();

View File

@@ -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

View File

@@ -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);

View File

@@ -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