
This patch improves the design of the IncrementalParser and Interpreter classes. Now the incremental parser is only responsible for building the partial translation unit declaration and the AST, while the Interpreter fills in the lower level llvm::Module and other JIT-related infrastructure. Finally the Interpreter class now orchestrates the AST and the LLVM IR with the IncrementalParser and IncrementalExecutor classes. The design improvement allows us to rework some of the logic that extracts an interpreter value into the clang::Value object. The new implementation simplifies use-cases which are used for out-of-process execution by allowing interpreter to be inherited or customized with an clang::ASTConsumer. This change will enable completing the pretty printing work which is in llvm/llvm-project#84769
175 lines
5.5 KiB
C++
175 lines
5.5 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 "InterpreterTestFixture.h"
|
|
|
|
#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/MC/TargetRegistry.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) || defined(__MVS__)
|
|
#define CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT
|
|
#endif
|
|
|
|
using namespace clang;
|
|
namespace {
|
|
|
|
class InterpreterExtensionsTest : public InterpreterTestBase {
|
|
protected:
|
|
void SetUp() override {
|
|
#ifdef CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT
|
|
GTEST_SKIP();
|
|
#endif
|
|
}
|
|
|
|
static void SetUpTestSuite() {
|
|
llvm::InitializeAllTargets();
|
|
llvm::InitializeAllTargetInfos();
|
|
llvm::InitializeAllTargetMCs();
|
|
llvm::InitializeAllAsmPrinters();
|
|
}
|
|
|
|
public:
|
|
// Some tests require a arm-registered-target
|
|
static bool IsARMTargetRegistered() {
|
|
llvm::Triple TT;
|
|
TT.setArch(llvm::Triple::arm);
|
|
TT.setVendor(llvm::Triple::UnknownVendor);
|
|
TT.setOS(llvm::Triple::UnknownOS);
|
|
|
|
std::string UnusedErr;
|
|
return llvm::TargetRegistry::lookupTarget(TT.str(), UnusedErr);
|
|
}
|
|
};
|
|
|
|
struct OutOfProcInterpreter : public Interpreter {
|
|
OutOfProcInterpreter(
|
|
std::unique_ptr<CompilerInstance> CI, llvm::Error &ErrOut,
|
|
std::unique_ptr<clang::ASTConsumer> Consumer,
|
|
std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr)
|
|
: Interpreter(std::move(CI), ErrOut, std::move(JITBuilder),
|
|
std::move(Consumer)) {}
|
|
};
|
|
|
|
TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) {
|
|
if (!HostSupportsJIT())
|
|
GTEST_SKIP();
|
|
|
|
clang::IncrementalCompilerBuilder CB;
|
|
llvm::Error ErrOut = llvm::Error::success();
|
|
auto CI = cantFail(CB.CreateCpp());
|
|
// Do not attach the default consumer which is specialized for in-process.
|
|
class NoopConsumer : public ASTConsumer {};
|
|
std::unique_ptr<ASTConsumer> C = std::make_unique<NoopConsumer>();
|
|
OutOfProcInterpreter I(std::move(CI), ErrOut, std::move(C),
|
|
/*JITBuilder=*/nullptr);
|
|
cantFail(std::move(ErrOut));
|
|
cantFail(I.Parse("int a = 1; a"));
|
|
cantFail(I.Parse("int b = 2; b"));
|
|
cantFail(I.Parse("int c = 3; c"));
|
|
|
|
// Make sure no clang::Value logic is attached by the Interpreter.
|
|
Value V1;
|
|
llvm::cantFail(I.ParseAndExecute("int x = 42;"));
|
|
llvm::cantFail(I.ParseAndExecute("x", &V1));
|
|
EXPECT_FALSE(V1.isValid());
|
|
EXPECT_FALSE(V1.hasValue());
|
|
}
|
|
|
|
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,
|
|
std::unique_ptr<llvm::orc::LLJITBuilder> JB)
|
|
: Interpreter(std::move(CI), ErrOut, std::move(JB)) {}
|
|
|
|
~CustomJBInterpreter() override {
|
|
// Skip cleanUp() because it would trigger LLJIT default dtors
|
|
Interpreter::ResetExecutor();
|
|
}
|
|
|
|
llvm::Error CreateExecutor() { return Interpreter::CreateExecutor(); }
|
|
};
|
|
|
|
TEST_F(InterpreterExtensionsTest, DefaultCrossJIT) {
|
|
if (!IsARMTargetRegistered())
|
|
GTEST_SKIP();
|
|
|
|
IncrementalCompilerBuilder CB;
|
|
CB.SetTargetTriple("armv6-none-eabi");
|
|
auto CI = cantFail(CB.CreateCpp());
|
|
llvm::Error ErrOut = llvm::Error::success();
|
|
CustomJBInterpreter Interp(std::move(CI), ErrOut, nullptr);
|
|
cantFail(std::move(ErrOut));
|
|
}
|
|
|
|
TEST_F(InterpreterExtensionsTest, CustomCrossJIT) {
|
|
if (!IsARMTargetRegistered())
|
|
GTEST_SKIP();
|
|
|
|
std::string TargetTriple = "armv6-none-eabi";
|
|
|
|
IncrementalCompilerBuilder CB;
|
|
CB.SetTargetTriple(TargetTriple);
|
|
auto CI = cantFail(CB.CreateCpp());
|
|
|
|
using namespace llvm::orc;
|
|
LLJIT *JIT = nullptr;
|
|
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Objs;
|
|
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();
|
|
});
|
|
|
|
llvm::Error ErrOut = llvm::Error::success();
|
|
CustomJBInterpreter Interp(std::move(CI), ErrOut, std::move(JB));
|
|
cantFail(std::move(ErrOut));
|
|
|
|
EXPECT_EQ(0U, Objs.size());
|
|
cantFail(Interp.ParseAndExecute("int a = 1;"));
|
|
ASSERT_NE(JIT, nullptr); // But it is, because JBCreator was never called
|
|
ExecutorAddr Addr = cantFail(JIT->lookup("a"));
|
|
EXPECT_NE(0U, Addr.getValue());
|
|
EXPECT_EQ(1U, Objs.size());
|
|
}
|
|
|
|
} // end anonymous namespace
|