1633ea072SLang Hames //===--------------- LLJITWithCustomObjectLinkingLayer.cpp ----------------===// 2633ea072SLang Hames // 3633ea072SLang Hames // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4633ea072SLang Hames // See https://llvm.org/LICENSE.txt for license information. 5633ea072SLang Hames // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6633ea072SLang Hames // 7633ea072SLang Hames //===----------------------------------------------------------------------===// 8633ea072SLang Hames // 9633ea072SLang Hames // This file shows how to switch LLJIT to use a custom object linking layer (we 10633ea072SLang Hames // use ObjectLinkingLayer, which is backed by JITLink, as an example). 11633ea072SLang Hames // 12633ea072SLang Hames //===----------------------------------------------------------------------===// 13633ea072SLang Hames 14633ea072SLang Hames #include "llvm/ADT/StringMap.h" 15633ea072SLang Hames #include "llvm/ExecutionEngine/JITLink/JITLink.h" 16633ea072SLang Hames #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" 174e30b20bSLang Hames #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" 18633ea072SLang Hames #include "llvm/ExecutionEngine/Orc/LLJIT.h" 19633ea072SLang Hames #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" 20633ea072SLang Hames #include "llvm/Support/InitLLVM.h" 21633ea072SLang Hames #include "llvm/Support/TargetSelect.h" 22633ea072SLang Hames #include "llvm/Support/raw_ostream.h" 23633ea072SLang Hames 24633ea072SLang Hames #include "../ExampleModules.h" 25633ea072SLang Hames 26633ea072SLang Hames using namespace llvm; 27633ea072SLang Hames using namespace llvm::orc; 28633ea072SLang Hames 29633ea072SLang Hames ExitOnError ExitOnErr; 30633ea072SLang Hames 31633ea072SLang Hames const llvm::StringRef TestMod = 32633ea072SLang Hames R"( 33633ea072SLang Hames define i32 @callee() { 34633ea072SLang Hames entry: 35633ea072SLang Hames ret i32 7 36633ea072SLang Hames } 37633ea072SLang Hames 38633ea072SLang Hames define i32 @entry() { 39633ea072SLang Hames entry: 40633ea072SLang Hames %0 = call i32 @callee() 41633ea072SLang Hames ret i32 %0 42633ea072SLang Hames } 43633ea072SLang Hames )"; 44633ea072SLang Hames 45633ea072SLang Hames class MyPlugin : public ObjectLinkingLayer::Plugin { 46633ea072SLang Hames public: 47633ea072SLang Hames // The modifyPassConfig callback gives us a chance to inspect the 48633ea072SLang Hames // MaterializationResponsibility and target triple for the object being 49633ea072SLang Hames // linked, then add any JITLink passes that we would like to run on the 50633ea072SLang Hames // link graph. A pass is just a function object that is callable as 51633ea072SLang Hames // Error(jitlink::LinkGraph&). In this case we will add two passes 52633ea072SLang Hames // defined as lambdas that call the printLinkerGraph method on our 53633ea072SLang Hames // plugin: One to run before the linker applies fixups and another to 54633ea072SLang Hames // run afterwards. 554e30b20bSLang Hames void modifyPassConfig(MaterializationResponsibility &MR, 564e30b20bSLang Hames jitlink::LinkGraph &LG, 57633ea072SLang Hames jitlink::PassConfiguration &Config) override { 584e30b20bSLang Hames 594e30b20bSLang Hames outs() << "MyPlugin -- Modifying pass config for " << LG.getName() << " (" 604e30b20bSLang Hames << LG.getTargetTriple().str() << "):\n"; 614e30b20bSLang Hames 624e30b20bSLang Hames // Print sections, symbol names and addresses, and any edges for the 6370c92126SLang Hames // associated blocks at the 'PostPrune' phase of JITLink (after 6470c92126SLang Hames // dead-stripping, but before addresses are allocated in the target 6570c92126SLang Hames // address space. See llvm/docs/JITLink.rst). 664e30b20bSLang Hames // 6770c92126SLang Hames // Experiment with adding the 'printGraph' pass at other points in the 6870c92126SLang Hames // pipeline. E.g. PrePrunePasses, PostAllocationPasses, and 6970c92126SLang Hames // PostFixupPasses. 7070c92126SLang Hames Config.PostPrunePasses.push_back(printGraph); 71633ea072SLang Hames } 72633ea072SLang Hames 73633ea072SLang Hames void notifyLoaded(MaterializationResponsibility &MR) override { 744e30b20bSLang Hames outs() << "Loading object defining " << MR.getSymbols() << "\n"; 75633ea072SLang Hames } 76633ea072SLang Hames 77633ea072SLang Hames Error notifyEmitted(MaterializationResponsibility &MR) override { 784e30b20bSLang Hames outs() << "Emitted object defining " << MR.getSymbols() << "\n"; 79633ea072SLang Hames return Error::success(); 80633ea072SLang Hames } 81633ea072SLang Hames 820aec49c8SLang Hames Error notifyFailed(MaterializationResponsibility &MR) override { 830aec49c8SLang Hames return Error::success(); 840aec49c8SLang Hames } 850aec49c8SLang Hames 860aec49c8SLang Hames Error notifyRemovingResources(ResourceKey K) override { 870aec49c8SLang Hames return Error::success(); 880aec49c8SLang Hames } 890aec49c8SLang Hames 900aec49c8SLang Hames void notifyTransferringResources(ResourceKey DstKey, 910aec49c8SLang Hames ResourceKey SrcKey) override {} 920aec49c8SLang Hames 93633ea072SLang Hames private: 944e30b20bSLang Hames static void printBlockContent(jitlink::Block &B) { 95633ea072SLang Hames constexpr JITTargetAddress LineWidth = 16; 96633ea072SLang Hames 974e30b20bSLang Hames if (B.isZeroFill()) { 984e30b20bSLang Hames outs() << " " << formatv("{0:x16}", B.getAddress()) << ": " 994e30b20bSLang Hames << B.getSize() << " bytes of zero-fill.\n"; 1004e30b20bSLang Hames return; 1014e30b20bSLang Hames } 102633ea072SLang Hames 103118e953bSLang Hames ExecutorAddr InitAddr(B.getAddress().getValue() & ~(LineWidth - 1)); 104118e953bSLang Hames ExecutorAddr StartAddr = B.getAddress(); 105118e953bSLang Hames ExecutorAddr EndAddr = B.getAddress() + B.getSize(); 1064e30b20bSLang Hames auto *Data = reinterpret_cast<const uint8_t *>(B.getContent().data()); 107633ea072SLang Hames 108118e953bSLang Hames for (ExecutorAddr CurAddr = InitAddr; CurAddr != EndAddr; ++CurAddr) { 109633ea072SLang Hames if (CurAddr % LineWidth == 0) 110118e953bSLang Hames outs() << " " << formatv("{0:x16}", CurAddr.getValue()) 111118e953bSLang Hames << ": "; 112633ea072SLang Hames if (CurAddr < StartAddr) 1134e30b20bSLang Hames outs() << " "; 114633ea072SLang Hames else 1154e30b20bSLang Hames outs() << formatv("{0:x-2}", Data[CurAddr - StartAddr]) << " "; 116633ea072SLang Hames if (CurAddr % LineWidth == LineWidth - 1) 1174e30b20bSLang Hames outs() << "\n"; 118633ea072SLang Hames } 119633ea072SLang Hames if (EndAddr % LineWidth != 0) 1204e30b20bSLang Hames outs() << "\n"; 1214e30b20bSLang Hames } 1224e30b20bSLang Hames 1234e30b20bSLang Hames static Error printGraph(jitlink::LinkGraph &G) { 1244e30b20bSLang Hames 1254e30b20bSLang Hames DenseSet<jitlink::Block *> BlocksAlreadyVisited; 1264e30b20bSLang Hames 1274e30b20bSLang Hames outs() << "Graph \"" << G.getName() << "\"\n"; 1284e30b20bSLang Hames // Loop over all sections... 1294e30b20bSLang Hames for (auto &S : G.sections()) { 1304e30b20bSLang Hames outs() << " Section " << S.getName() << ":\n"; 1314e30b20bSLang Hames 1324e30b20bSLang Hames // Loop over all symbols in the current section... 1334e30b20bSLang Hames for (auto *Sym : S.symbols()) { 1344e30b20bSLang Hames 1354e30b20bSLang Hames // Print the symbol's address. 1364e30b20bSLang Hames outs() << " " << formatv("{0:x16}", Sym->getAddress()) << ": "; 1374e30b20bSLang Hames 1384e30b20bSLang Hames // Print the symbol's name, or "<anonymous symbol>" if it doesn't have 1394e30b20bSLang Hames // one. 1404e30b20bSLang Hames if (Sym->hasName()) 1414e30b20bSLang Hames outs() << Sym->getName() << "\n"; 1424e30b20bSLang Hames else 1434e30b20bSLang Hames outs() << "<anonymous symbol>\n"; 1444e30b20bSLang Hames 1454e30b20bSLang Hames // Get the content block for this symbol. 1464e30b20bSLang Hames auto &B = Sym->getBlock(); 1474e30b20bSLang Hames 1484e30b20bSLang Hames if (BlocksAlreadyVisited.count(&B)) { 1494e30b20bSLang Hames outs() << " Block " << formatv("{0:x16}", B.getAddress()) 1504e30b20bSLang Hames << " already printed.\n"; 1514e30b20bSLang Hames continue; 1524e30b20bSLang Hames } else 1534e30b20bSLang Hames outs() << " Block " << formatv("{0:x16}", B.getAddress()) 1544e30b20bSLang Hames << ":\n"; 1554e30b20bSLang Hames 1564e30b20bSLang Hames outs() << " Content:\n"; 1574e30b20bSLang Hames printBlockContent(B); 1584e30b20bSLang Hames BlocksAlreadyVisited.insert(&B); 1594e30b20bSLang Hames 1604e30b20bSLang Hames if (!llvm::empty(B.edges())) { 1614e30b20bSLang Hames outs() << " Edges:\n"; 1624e30b20bSLang Hames for (auto &E : B.edges()) { 1634e30b20bSLang Hames outs() << " " 1644e30b20bSLang Hames << formatv("{0:x16}", B.getAddress() + E.getOffset()) 1654e30b20bSLang Hames << ": kind = " << formatv("{0:d}", E.getKind()) 1664e30b20bSLang Hames << ", addend = " << formatv("{0:x}", E.getAddend()) 1674e30b20bSLang Hames << ", target = "; 1684e30b20bSLang Hames jitlink::Symbol &TargetSym = E.getTarget(); 1694e30b20bSLang Hames if (TargetSym.hasName()) 1704e30b20bSLang Hames outs() << TargetSym.getName() << "\n"; 1714e30b20bSLang Hames else 1724e30b20bSLang Hames outs() << "<anonymous target>\n"; 173633ea072SLang Hames } 174633ea072SLang Hames } 1754e30b20bSLang Hames outs() << "\n"; 1764e30b20bSLang Hames } 1774e30b20bSLang Hames } 1784e30b20bSLang Hames return Error::success(); 179633ea072SLang Hames } 180633ea072SLang Hames }; 181633ea072SLang Hames 1824e30b20bSLang Hames static cl::opt<std::string> 1834e30b20bSLang Hames EntryPointName("entry", cl::desc("Symbol to call as main entry point"), 1844e30b20bSLang Hames cl::init("entry")); 1854e30b20bSLang Hames 1864e30b20bSLang Hames static cl::list<std::string> InputObjects(cl::Positional, cl::ZeroOrMore, 1874e30b20bSLang Hames cl::desc("input objects")); 1884e30b20bSLang Hames 189633ea072SLang Hames int main(int argc, char *argv[]) { 190633ea072SLang Hames // Initialize LLVM. 191633ea072SLang Hames InitLLVM X(argc, argv); 192633ea072SLang Hames 193633ea072SLang Hames InitializeNativeTarget(); 194633ea072SLang Hames InitializeNativeTargetAsmPrinter(); 195633ea072SLang Hames 196633ea072SLang Hames cl::ParseCommandLineOptions(argc, argv, "LLJITWithObjectLinkingLayerPlugin"); 197633ea072SLang Hames ExitOnErr.setBanner(std::string(argv[0]) + ": "); 198633ea072SLang Hames 199633ea072SLang Hames // Detect the host and set code model to small. 200633ea072SLang Hames auto JTMB = ExitOnErr(JITTargetMachineBuilder::detectHost()); 201633ea072SLang Hames JTMB.setCodeModel(CodeModel::Small); 202633ea072SLang Hames 203633ea072SLang Hames // Create an LLJIT instance with an ObjectLinkingLayer as the base layer. 204633ea072SLang Hames // We attach our plugin in to the newly created ObjectLinkingLayer before 205633ea072SLang Hames // returning it. 206633ea072SLang Hames auto J = ExitOnErr( 207633ea072SLang Hames LLJITBuilder() 208633ea072SLang Hames .setJITTargetMachineBuilder(std::move(JTMB)) 209633ea072SLang Hames .setObjectLinkingLayerCreator( 210633ea072SLang Hames [&](ExecutionSession &ES, const Triple &TT) { 211633ea072SLang Hames // Create ObjectLinkingLayer. 212633ea072SLang Hames auto ObjLinkingLayer = std::make_unique<ObjectLinkingLayer>( 213962a2479SLang Hames ES, ExitOnErr(jitlink::InProcessMemoryManager::Create())); 214633ea072SLang Hames // Add an instance of our plugin. 215633ea072SLang Hames ObjLinkingLayer->addPlugin(std::make_unique<MyPlugin>()); 2166edc3fe5SStefan Gränitz return ObjLinkingLayer; 217633ea072SLang Hames }) 218633ea072SLang Hames .create()); 219633ea072SLang Hames 2204e30b20bSLang Hames if (!InputObjects.empty()) { 221633ea072SLang Hames 2224e30b20bSLang Hames // If we have input objects then reflect process symbols so the input 2234e30b20bSLang Hames // objects can do interesting things, like call printf. 2244e30b20bSLang Hames J->getMainJITDylib().addGenerator( 2254e30b20bSLang Hames ExitOnErr(DynamicLibrarySearchGenerator::GetForCurrentProcess( 2264e30b20bSLang Hames J->getDataLayout().getGlobalPrefix()))); 2274e30b20bSLang Hames 2284e30b20bSLang Hames // Load the input objects. 2294e30b20bSLang Hames for (auto InputObject : InputObjects) { 2304e30b20bSLang Hames auto ObjBuffer = 2314e30b20bSLang Hames ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputObject))); 2324e30b20bSLang Hames ExitOnErr(J->addObjectFile(std::move(ObjBuffer))); 2334e30b20bSLang Hames } 2344e30b20bSLang Hames } else { 2354e30b20bSLang Hames auto M = ExitOnErr(parseExampleModule(TestMod, "test-module")); 2364e30b20bSLang Hames M.withModuleDo([](Module &MP) { 2374e30b20bSLang Hames outs() << "No input objects specified. Using demo module:\n" 2384e30b20bSLang Hames << MP << "\n"; 2394e30b20bSLang Hames }); 240633ea072SLang Hames ExitOnErr(J->addIRModule(std::move(M))); 2414e30b20bSLang Hames } 242633ea072SLang Hames 243633ea072SLang Hames // Look up the JIT'd function, cast it to a function pointer, then call it. 244*16dcbb53SLang Hames auto EntryAddr = ExitOnErr(J->lookup(EntryPointName)); 245*16dcbb53SLang Hames auto *Entry = EntryAddr.toPtr<int()>(); 246633ea072SLang Hames 247633ea072SLang Hames int Result = Entry(); 248633ea072SLang Hames outs() << "---Result---\n" 2494e30b20bSLang Hames << EntryPointName << "() = " << Result << "\n"; 250633ea072SLang Hames 251633ea072SLang Hames return 0; 252633ea072SLang Hames } 253