Index: unittests/ExecutionEngine/Orc/CMakeLists.txt =================================================================== --- unittests/ExecutionEngine/Orc/CMakeLists.txt +++ unittests/ExecutionEngine/Orc/CMakeLists.txt @@ -2,6 +2,7 @@ set(LLVM_LINK_COMPONENTS Core ExecutionEngine + IRReader Object OrcJIT RuntimeDyld @@ -16,6 +17,7 @@ GlobalMappingLayerTest.cpp LazyEmittingLayerTest.cpp LegacyAPIInteropTest.cpp + LLJITTest.cpp ObjectTransformLayerTest.cpp OrcCAPITest.cpp OrcTestCommon.cpp Index: unittests/ExecutionEngine/Orc/LLJITTest.cpp =================================================================== --- /dev/null +++ unittests/ExecutionEngine/Orc/LLJITTest.cpp @@ -0,0 +1,180 @@ +//===- LLJITTest.cpp - Unit tests for LLIJIT ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "OrcTestCommon.h" + +#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/SourceMgr.h" + +#include "gtest/gtest.h" + +#include +#include + +using namespace llvm; +using namespace llvm::orc; + +#define MULTILINE(...) #__VA_ARGS__ + +const char *FooIR = MULTILINE( + declare i32 @bar() + declare i32 @far() + + define i32 @foo() { + %1 = call i32 @bar() + %2 = call i32 @far() + %3 = add nsw i32 %1, %2 + ret i32 %3 + } +); + +const char *BarIR = MULTILINE( + define i32 @bar() { + ret i32 1 + } +); + +const char *FarIR = MULTILINE( + define i32 @far() { + ret i32 1 + } +); + +using MaterializeFunction = + std::function; + +using DiscardFunction = + std::function; + +using DestructorFunction = std::function; + +class SimpleMaterializationUnit : public MaterializationUnit { +public: + SimpleMaterializationUnit( + SymbolFlagsMap SymbolFlags, MaterializeFunction Materialize, + DiscardFunction Discard = DiscardFunction(), + DestructorFunction Destructor = DestructorFunction()) + : MaterializationUnit(std::move(SymbolFlags)), + Materialize(std::move(Materialize)), Discard(std::move(Discard)), + Destructor(std::move(Destructor)) {} + + ~SimpleMaterializationUnit() override { + if (Destructor) + Destructor(); + } + + void materialize(MaterializationResponsibility R) override { + Materialize(std::move(R)); + } + + void discard(const JITDylib &JD, SymbolStringPtr Name) override { + if (Discard) + Discard(JD, std::move(Name)); + else + llvm_unreachable("Discard not supported"); + } + +private: + MaterializeFunction Materialize; + DiscardFunction Discard; + DestructorFunction Destructor; +}; + +TEST(LLJIT, SymbolLookupRace) { + InitializeNativeTarget(); + InitializeNativeTargetAsmParser(); + InitializeNativeTargetAsmPrinter(); + + auto TMB = cantFail(JITTargetMachineBuilder::detectHost()); + auto TM = cantFail(TMB.createTargetMachine()); + auto DL = TM->createDataLayout(); + + // Use single JIT for all functions. + // -> Maintains one single ThinLTO index. + std::unique_ptr JIT = cantFail(LLJIT::Create( + llvm::make_unique(), std::move(TM), DL)); + + // Run all in one session. + ExecutionSession &CommonES = JIT->getExecutionSession(); + + // Emit foo and bar into one virtual dylib. + // -> Test inlining within one translation unit. + JITDylib &MainDylib = JIT->getMainJITDylib(); + JITDylib &FarDylib = CommonES.createJITDylib("Far"); + + // Register function names. + auto FooName = CommonES.getSymbolStringPool().intern("foo"); + auto BarName = CommonES.getSymbolStringPool().intern("bar"); + auto FarName = CommonES.getSymbolStringPool().intern("far"); + + //SymbolFlagsMap Symbols({{FooName, JITSymbolFlags::Exported}, + // {BarName, JITSymbolFlags::Exported}}); + + // One MaterializationResponsibility for each of the sumbols, so we can + // define dependencies below. + Optional FooMR; + Optional BarMR; + Optional FarMR; + + // Actually, I want one MaterializationUnit for Foo and Bar. + auto FooMU = llvm::make_unique( + SymbolFlagsMap({{FooName, JITSymbolFlags::Exported}}), + [&](MaterializationResponsibility R) { FooMR.emplace(std::move(R)); }); + + auto BarMU = llvm::make_unique( + SymbolFlagsMap({{BarName, JITSymbolFlags::Exported}}), + [&](MaterializationResponsibility R) { BarMR.emplace(std::move(R)); }); + + auto FarMU = llvm::make_unique( + SymbolFlagsMap({{FarName, JITSymbolFlags::Exported}}), + [&](MaterializationResponsibility R) { FarMR.emplace(std::move(R)); }); + + cantFail(MainDylib.define(FooMU)); + cantFail(MainDylib.define(BarMU)); + cantFail(FarDylib.define(FarMU)); + + // Foo depdens on both, Bar and Far. + CommonES.lookup({&MainDylib}, {FooName}, nullptr, nullptr, NoDependenciesToRegister); + FooMR->addDependenciesForAll({{&MainDylib, SymbolNameSet({BarName})}}); + FooMR->addDependenciesForAll({{&FarDylib, SymbolNameSet({FarName})}}); + + //std::mutex MUAccess; + CommonES.setDispatchMaterialization( + [&](JITDylib &JD, std::unique_ptr MU) { + //std::lock_guard Lock(MUAccess); + MU->doMaterialize(JD); + }); + + SMDiagnostic E; + LLVMContext FooCtx, BarCtx, FarCtx; + cantFail(JIT->addIRModule(parseIR(MemoryBufferRef(FooIR, "Foo"), E, FooCtx))); + cantFail(JIT->addIRModule(parseIR(MemoryBufferRef(BarIR, "Bar"), E, BarCtx))); + cantFail(JIT->addIRModule(parseIR(MemoryBufferRef(FarIR, "Far"), E, FarCtx))); + + using Sig_t = int(); + + std::thread RunFoo([&JIT]() { + JITEvaluatedSymbol FooSym = cantFail(JIT->lookup("foo")); + EXPECT_EQ(2, ((Sig_t *)FooSym.getAddress())()); + }); + + /*std::thread RunBar([&JIT]() { + JITEvaluatedSymbol BarSym = cantFail(JIT->lookup("bar")); + EXPECT_EQ(1, ((Sig_t *)BarSym.getAddress())()); + }); + + RunBar.join(); + */ + + RunFoo.join(); +}