//===- 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 #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, UnusedErr); } }; class CustomJBInterpreter : public Interpreter { using CustomJITBuilderCreatorFunction = std::function>()>; CustomJITBuilderCreatorFunction JBCreator = nullptr; public: CustomJBInterpreter(std::unique_ptr CI, llvm::Error &ErrOut, std::unique_ptr IEB) : Interpreter(std::move(CI), ErrOut, std::move(IEB)) {} ~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> Objs; auto JTMB = JITTargetMachineBuilder(llvm::Triple(TargetTriple)); JTMB.setCPU("cortex-m0plus"); auto JB = std::make_unique(); JB->setJITTargetMachineBuilder(JTMB); JB->setPlatformSetUp(setUpInactivePlatform); JB->setNotifyCreatedCallback([&](LLJIT &J) { ObjectLayer &ObjLayer = J.getObjLinkingLayer(); auto *JITLinkObjLayer = llvm::dyn_cast(&ObjLayer); JITLinkObjLayer->setReturnObjectBuffer( [&Objs](std::unique_ptr MB) { Objs.push_back(std::move(MB)); }); JIT = &J; return llvm::Error::success(); }); auto IEB = std::make_unique(); IEB->JITBuilder = std::move(JB); llvm::Error ErrOut = llvm::Error::success(); CustomJBInterpreter Interp(std::move(CI), ErrOut, std::move(IEB)); 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()); } TEST_F(InterpreterExtensionsTest, CustomIncrementalExecutor) { struct RecordingIncrementalExecutor : public clang::IncrementalExecutor { mutable unsigned RanCtors = false; unsigned Added = 0; unsigned Removed = 0; // Default ctor is fine; builder.create() will just return this IE. RecordingIncrementalExecutor() = default; llvm::Error addModule(clang::PartialTranslationUnit &PTU) override { Added++; return llvm::Error::success(); } llvm::Error removeModule(clang::PartialTranslationUnit &PTU) override { Removed++; return llvm::Error::success(); } llvm::Error runCtors() const override { RanCtors++; return llvm::Error::success(); } llvm::Error cleanUp() override { return llvm::Error::success(); } llvm::Expected getSymbolAddress(llvm::StringRef /*Name*/, SymbolNameKind /*NameKind*/) const override { // Return an error here; test doesn't need a real address. return llvm::make_error( "not implemented in test", llvm::inconvertibleErrorCode()); } llvm::Error LoadDynamicLibrary(const char * /*name*/) override { return llvm::Error::success(); } }; // Prepare a builder that hands out our recording executor. auto B = std::make_unique(); B->IE = std::make_unique(); IncrementalCompilerBuilder CB; auto CI = cantFail(CB.CreateCpp()); auto I = cantFail(Interpreter::create(std::move(CI), std::move(B))); ASSERT_TRUE(I); const auto &Rec = static_cast( cantFail(I->getExecutionEngine())); unsigned NumInitAdded = Rec.Added; unsigned NumInitRanCtors = Rec.RanCtors; unsigned NumInitRemoved = Rec.Removed; cantFail(I->ParseAndExecute("int a = 1;")); EXPECT_TRUE(Rec.Added == NumInitAdded + 1); EXPECT_TRUE(Rec.RanCtors == NumInitRanCtors + 1); EXPECT_TRUE(Rec.Removed == NumInitRemoved); } } // end anonymous namespace