llvm-project/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
Stefan Gränitz 0cf4788d9d
[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.
2024-03-25 09:44:25 +01:00

247 lines
7.8 KiB
C++

//===- unittests/Interpreter/InterpreterExtensionsTest.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
//
//===----------------------------------------------------------------------===//
//
// Unit tests for Clang's Interpreter library.
//
//===----------------------------------------------------------------------===//
#include "clang/Interpreter/Interpreter.h"
#include "clang/AST/Expr.h"
#include "clang/Frontend/CompilerInstance.h"
#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"
#include "gtest/gtest.h"
#include <system_error>
#if defined(_AIX)
#define CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT
#endif
using namespace clang;
namespace {
static bool HostSupportsJit() {
auto J = llvm::orc::LLJITBuilder().create();
if (J)
return true;
LLVMConsumeError(llvm::wrap(J.takeError()));
return false;
}
struct LLVMInitRAII {
LLVMInitRAII() {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
LLVMInitializeARMTarget();
LLVMInitializeARMTargetInfo();
LLVMInitializeARMTargetMC();
LLVMInitializeARMAsmPrinter();
}
~LLVMInitRAII() { llvm::llvm_shutdown(); }
} LLVMInit;
class TestCreateResetExecutor : public Interpreter {
public:
TestCreateResetExecutor(std::unique_ptr<CompilerInstance> CI,
llvm::Error &Err)
: Interpreter(std::move(CI), Err) {}
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 CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT
TEST(InterpreterExtensionsTest, DISABLED_ExecutorCreateReset) {
#else
TEST(InterpreterExtensionsTest, ExecutorCreateReset) {
#endif
// Make sure we can create the executer on the platform.
if (!HostSupportsJit())
GTEST_SKIP();
clang::IncrementalCompilerBuilder CB;
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());
EXPECT_THAT_ERROR(Interp.testCreateExecutor(),
llvm::FailedWithMessage("Operation failed. "
"Execution engine exists"));
}
class RecordRuntimeIBMetrics : public Interpreter {
struct NoopRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder {
NoopRuntimeInterfaceBuilder(Sema &S) : S(S) {}
TransformExprFunction *getPrintValueTransformer() override {
TransformerQueries += 1;
return &noop;
}
static ExprResult noop(RuntimeInterfaceBuilder *Builder, Expr *E,
ArrayRef<Expr *> FixedArgs) {
auto *B = static_cast<NoopRuntimeInterfaceBuilder *>(Builder);
B->TransformedExprs += 1;
return B->S.ActOnFinishFullExpr(E, /*DiscardedValue=*/false);
}
Sema &S;
size_t TransformedExprs = 0;
size_t TransformerQueries = 0;
};
public:
// Inherit with using wouldn't make it public
RecordRuntimeIBMetrics(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err)
: Interpreter(std::move(CI), Err) {}
std::unique_ptr<RuntimeInterfaceBuilder> FindRuntimeInterface() override {
assert(RuntimeIBPtr == nullptr && "We create the builder only once");
Sema &S = getCompilerInstance()->getSema();
auto RuntimeIB = std::make_unique<NoopRuntimeInterfaceBuilder>(S);
RuntimeIBPtr = RuntimeIB.get();
return RuntimeIB;
}
NoopRuntimeInterfaceBuilder *RuntimeIBPtr = nullptr;
};
TEST(InterpreterExtensionsTest, FindRuntimeInterface) {
clang::IncrementalCompilerBuilder CB;
llvm::Error ErrOut = llvm::Error::success();
RecordRuntimeIBMetrics Interp(cantFail(CB.CreateCpp()), ErrOut);
cantFail(std::move(ErrOut));
cantFail(Interp.Parse("int a = 1; a"));
cantFail(Interp.Parse("int b = 2; b"));
cantFail(Interp.Parse("int c = 3; c"));
EXPECT_EQ(3U, Interp.RuntimeIBPtr->TransformedExprs);
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