| //===- LLJITWithExecutorProcessControl.cpp - LLJIT example with EPC utils -===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // In this example we will use the lazy re-exports utility to lazily compile | |
| // IR modules. We will do this in seven steps: | |
| // | |
| // 1. Create an LLJIT instance. | |
| // 2. Install a transform so that we can see what is being compiled. | |
| // 3. Create an indirect stubs manager and lazy call-through manager. | |
| // 4. Add two modules that will be conditionally compiled, plus a main module. | |
| // 5. Add lazy-rexports of the symbols in the conditionally compiled modules. | |
| // 6. Dump the ExecutionSession state to see the symbol table prior to | |
| // executing any code. | |
| // 7. Verify that only modules containing executed code are compiled. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ADT/StringMap.h" | |
| #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" | |
| #include "llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h" | |
| #include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" | |
| #include "llvm/ExecutionEngine/Orc/LLJIT.h" | |
| #include "llvm/ExecutionEngine/Orc/MemoryAccess.h" | |
| #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" | |
| #include "llvm/ExecutionEngine/Orc/OrcABISupport.h" | |
| #include "llvm/ExecutionEngine/Orc/SelfExecutorProcessControl.h" | |
| #include "llvm/Support/InitLLVM.h" | |
| #include "llvm/Support/TargetSelect.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "../ExampleModules.h" | |
| #include <future> | |
| using namespace llvm; | |
| using namespace llvm::orc; | |
| ExitOnError ExitOnErr; | |
| // Example IR modules. | |
| // | |
| // Note that in the conditionally compiled modules, FooMod and BarMod, functions | |
| // have been given an _body suffix. This is to ensure that their names do not | |
| // clash with their lazy-reexports. | |
| // For clients who do not wish to rename function bodies (e.g. because they want | |
| // to re-use cached objects between static and JIT compiles) techniques exist to | |
| // avoid renaming. See the lazy-reexports section of the ORCv2 design doc. | |
| const llvm::StringRef FooMod = | |
| R"( | |
| declare i32 @return1() | |
| define i32 @foo_body() { | |
| entry: | |
| %0 = call i32 @return1() | |
| ret i32 %0 | |
| } | |
| )"; | |
| const llvm::StringRef BarMod = | |
| R"( | |
| declare i32 @return2() | |
| define i32 @bar_body() { | |
| entry: | |
| %0 = call i32 @return2() | |
| ret i32 %0 | |
| } | |
| )"; | |
| const llvm::StringRef MainMod = | |
| R"( | |
| define i32 @entry(i32 %argc) { | |
| entry: | |
| %and = and i32 %argc, 1 | |
| %tobool = icmp eq i32 %and, 0 | |
| br i1 %tobool, label %if.end, label %if.then | |
| if.then: ; preds = %entry | |
| %call = tail call i32 @foo() #2 | |
| br label %return | |
| if.end: ; preds = %entry | |
| %call1 = tail call i32 @bar() #2 | |
| br label %return | |
| return: ; preds = %if.end, %if.then | |
| %retval.0 = phi i32 [ %call, %if.then ], [ %call1, %if.end ] | |
| ret i32 %retval.0 | |
| } | |
| declare i32 @foo() | |
| declare i32 @bar() | |
| )"; | |
| extern "C" int32_t return1() { return 1; } | |
| extern "C" int32_t return2() { return 2; } | |
| static void *reenter(void *Ctx, void *TrampolineAddr) { | |
| std::promise<void *> LandingAddressP; | |
| auto LandingAddressF = LandingAddressP.get_future(); | |
| auto *EPCIU = static_cast<EPCIndirectionUtils *>(Ctx); | |
| EPCIU->getLazyCallThroughManager().resolveTrampolineLandingAddress( | |
| ExecutorAddr::fromPtr(TrampolineAddr), [&](ExecutorAddr LandingAddress) { | |
| LandingAddressP.set_value(LandingAddress.toPtr<void *>()); | |
| }); | |
| return LandingAddressF.get(); | |
| } | |
| static void reportErrorAndExit() { | |
| errs() << "Unable to lazily compile function. Exiting.\n"; | |
| exit(1); | |
| } | |
| static cl::list<std::string> InputArgv(cl::Positional, | |
| cl::desc("<program arguments>...")); | |
| int main(int argc, char *argv[]) { | |
| // Initialize LLVM. | |
| InitLLVM X(argc, argv); | |
| InitializeNativeTarget(); | |
| InitializeNativeTargetAsmPrinter(); | |
| cl::ParseCommandLineOptions(argc, argv, "LLJITWithLazyReexports"); | |
| ExitOnErr.setBanner(std::string(argv[0]) + ": "); | |
| // (1) Create LLJIT instance. | |
| auto EPC = ExitOnErr(SelfExecutorProcessControl::Create()); | |
| auto J = ExitOnErr( | |
| LLJITBuilder().setExecutorProcessControl(std::move(EPC)).create()); | |
| // (2) Install transform to print modules as they are compiled: | |
| J->getIRTransformLayer().setTransform( | |
| [](ThreadSafeModule TSM, | |
| const MaterializationResponsibility &R) -> Expected<ThreadSafeModule> { | |
| TSM.withModuleDo([](Module &M) { dbgs() << "---Compiling---\n" << M; }); | |
| return std::move(TSM); // Not a redundant move: fix build on gcc-7.5 | |
| }); | |
| // (3) Create stubs and call-through managers: | |
| auto MemAccess = ExitOnErr(J->getExecutionSession() | |
| .getExecutorProcessControl() | |
| .createDefaultMemoryAccess()); | |
| auto EPCIU = ExitOnErr(EPCIndirectionUtils::Create( | |
| J->getExecutionSession().getExecutorProcessControl(), | |
| J->getMemoryManager(), *MemAccess)); | |
| ExitOnErr(EPCIU->writeResolverBlock(ExecutorAddr::fromPtr(&reenter), | |
| ExecutorAddr::fromPtr(EPCIU.get()))); | |
| EPCIU->createLazyCallThroughManager( | |
| J->getExecutionSession(), ExecutorAddr::fromPtr(&reportErrorAndExit)); | |
| auto ISM = EPCIU->createIndirectStubsManager(); | |
| // (4) Add modules. | |
| ExitOnErr(J->addIRModule(ExitOnErr(parseExampleModule(FooMod, "foo-mod")))); | |
| ExitOnErr(J->addIRModule(ExitOnErr(parseExampleModule(BarMod, "bar-mod")))); | |
| ExitOnErr(J->addIRModule(ExitOnErr(parseExampleModule(MainMod, "main-mod")))); | |
| // (5) Add lazy reexports. | |
| MangleAndInterner Mangle(J->getExecutionSession(), J->getDataLayout()); | |
| SymbolAliasMap ReExports( | |
| {{Mangle("foo"), | |
| {Mangle("foo_body"), | |
| JITSymbolFlags::Exported | JITSymbolFlags::Callable}}, | |
| {Mangle("bar"), | |
| {Mangle("bar_body"), | |
| JITSymbolFlags::Exported | JITSymbolFlags::Callable}}}); | |
| ExitOnErr(J->getMainJITDylib().define( | |
| lazyReexports(EPCIU->getLazyCallThroughManager(), *ISM, | |
| J->getMainJITDylib(), std::move(ReExports)))); | |
| // (6) Dump the ExecutionSession state. | |
| dbgs() << "---Session state---\n"; | |
| J->getExecutionSession().dump(dbgs()); | |
| dbgs() << "\n"; | |
| // (7) Execute the JIT'd main function and pass the example's command line | |
| // arguments unmodified. This should cause either ExampleMod1 or ExampleMod2 | |
| // to be compiled, and either "1" or "2" returned depending on the number of | |
| // arguments passed. | |
| // Look up the JIT'd function, cast it to a function pointer, then call it. | |
| auto EntryAddr = ExitOnErr(J->lookup("entry")); | |
| auto *Entry = EntryAddr.toPtr<int(int)>(); | |
| int Result = Entry(argc); | |
| outs() << "---Result---\n" | |
| << "entry(" << argc << ") = " << Result << "\n"; | |
| // Destroy the EPCIndirectionUtils utility. | |
| ExitOnErr(EPCIU->cleanup()); | |
| return 0; | |
| } |