242 lines · cpp
1//===--------------- LLJITWithCustomObjectLinkingLayer.cpp ----------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file shows how to switch LLJIT to use a custom object linking layer (we10// use ObjectLinkingLayer, which is backed by JITLink, as an example).11//12//===----------------------------------------------------------------------===//13 14#include "llvm/ADT/StringMap.h"15#include "llvm/ExecutionEngine/JITLink/JITLink.h"16#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"17#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"18#include "llvm/ExecutionEngine/Orc/LLJIT.h"19#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"20#include "llvm/Support/InitLLVM.h"21#include "llvm/Support/TargetSelect.h"22#include "llvm/Support/raw_ostream.h"23 24#include "../ExampleModules.h"25 26using namespace llvm;27using namespace llvm::orc;28 29ExitOnError ExitOnErr;30 31const llvm::StringRef TestMod =32 R"(33 define i32 @callee() {34 entry:35 ret i32 736 }37 38 define i32 @entry() {39 entry:40 %0 = call i32 @callee()41 ret i32 %042 }43)";44 45class MyPlugin : public ObjectLinkingLayer::Plugin {46public:47 // The modifyPassConfig callback gives us a chance to inspect the48 // MaterializationResponsibility and target triple for the object being49 // linked, then add any JITLink passes that we would like to run on the50 // link graph. A pass is just a function object that is callable as51 // Error(jitlink::LinkGraph&). In this case we will add two passes52 // defined as lambdas that call the printLinkerGraph method on our53 // plugin: One to run before the linker applies fixups and another to54 // run afterwards.55 void modifyPassConfig(MaterializationResponsibility &MR,56 jitlink::LinkGraph &LG,57 jitlink::PassConfiguration &Config) override {58 59 outs() << "MyPlugin -- Modifying pass config for " << LG.getName() << " ("60 << LG.getTargetTriple().str() << "):\n";61 62 // Print sections, symbol names and addresses, and any edges for the63 // associated blocks at the 'PostPrune' phase of JITLink (after64 // dead-stripping, but before addresses are allocated in the target65 // address space. See llvm/docs/JITLink.rst).66 //67 // Experiment with adding the 'printGraph' pass at other points in the68 // pipeline. E.g. PrePrunePasses, PostAllocationPasses, and69 // PostFixupPasses.70 Config.PostPrunePasses.push_back(printGraph);71 }72 73 Error notifyEmitted(MaterializationResponsibility &MR) override {74 outs() << "Emitted object defining " << MR.getSymbols() << "\n";75 return Error::success();76 }77 78 Error notifyFailed(MaterializationResponsibility &MR) override {79 return Error::success();80 }81 82 Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override {83 return Error::success();84 }85 86 void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey,87 ResourceKey SrcKey) override {}88 89private:90 static void printBlockContent(jitlink::Block &B) {91 constexpr JITTargetAddress LineWidth = 16;92 93 if (B.isZeroFill()) {94 outs() << " " << formatv("{0:x16}", B.getAddress()) << ": "95 << B.getSize() << " bytes of zero-fill.\n";96 return;97 }98 99 ExecutorAddr InitAddr(B.getAddress().getValue() & ~(LineWidth - 1));100 ExecutorAddr StartAddr = B.getAddress();101 ExecutorAddr EndAddr = B.getAddress() + B.getSize();102 auto *Data = reinterpret_cast<const uint8_t *>(B.getContent().data());103 104 for (ExecutorAddr CurAddr = InitAddr; CurAddr != EndAddr; ++CurAddr) {105 if (CurAddr % LineWidth == 0)106 outs() << " " << formatv("{0:x16}", CurAddr.getValue())107 << ": ";108 if (CurAddr < StartAddr)109 outs() << " ";110 else111 outs() << formatv("{0:x-2}", Data[CurAddr - StartAddr]) << " ";112 if (CurAddr % LineWidth == LineWidth - 1)113 outs() << "\n";114 }115 if (EndAddr % LineWidth != 0)116 outs() << "\n";117 }118 119 static Error printGraph(jitlink::LinkGraph &G) {120 121 DenseSet<jitlink::Block *> BlocksAlreadyVisited;122 123 outs() << "Graph \"" << G.getName() << "\"\n";124 // Loop over all sections...125 for (auto &S : G.sections()) {126 outs() << " Section " << S.getName() << ":\n";127 128 // Loop over all symbols in the current section...129 for (auto *Sym : S.symbols()) {130 131 // Print the symbol's address.132 outs() << " " << formatv("{0:x16}", Sym->getAddress()) << ": ";133 134 // Print the symbol's name, or "<anonymous symbol>" if it doesn't have135 // one.136 if (Sym->hasName())137 outs() << Sym->getName() << "\n";138 else139 outs() << "<anonymous symbol>\n";140 141 // Get the content block for this symbol.142 auto &B = Sym->getBlock();143 144 if (BlocksAlreadyVisited.count(&B)) {145 outs() << " Block " << formatv("{0:x16}", B.getAddress())146 << " already printed.\n";147 continue;148 } else149 outs() << " Block " << formatv("{0:x16}", B.getAddress())150 << ":\n";151 152 outs() << " Content:\n";153 printBlockContent(B);154 BlocksAlreadyVisited.insert(&B);155 156 if (!B.edges().empty()) {157 outs() << " Edges:\n";158 for (auto &E : B.edges()) {159 outs() << " "160 << formatv("{0:x16}", B.getAddress() + E.getOffset())161 << ": kind = " << formatv("{0:d}", E.getKind())162 << ", addend = " << formatv("{0:x}", E.getAddend())163 << ", target = ";164 jitlink::Symbol &TargetSym = E.getTarget();165 if (TargetSym.hasName())166 outs() << TargetSym.getName() << "\n";167 else168 outs() << "<anonymous target>\n";169 }170 }171 outs() << "\n";172 }173 }174 return Error::success();175 }176};177 178static cl::opt<std::string>179 EntryPointName("entry", cl::desc("Symbol to call as main entry point"),180 cl::init("entry"));181 182static cl::list<std::string> InputObjects(cl::Positional,183 cl::desc("input objects"));184 185int main(int argc, char *argv[]) {186 // Initialize LLVM.187 InitLLVM X(argc, argv);188 189 InitializeNativeTarget();190 InitializeNativeTargetAsmPrinter();191 192 cl::ParseCommandLineOptions(argc, argv, "LLJITWithObjectLinkingLayerPlugin");193 ExitOnErr.setBanner(std::string(argv[0]) + ": ");194 195 // Detect the host and set code model to small.196 auto JTMB = ExitOnErr(JITTargetMachineBuilder::detectHost());197 JTMB.setCodeModel(CodeModel::Small);198 199 // Create an LLJIT instance with an ObjectLinkingLayer as the base layer.200 // We attach our plugin in to the newly created ObjectLinkingLayer before201 // returning it.202 auto J = ExitOnErr(203 LLJITBuilder()204 .setJITTargetMachineBuilder(std::move(JTMB))205 .setObjectLinkingLayerCreator(206 [&](ExecutionSession &ES) {207 // Create ObjectLinkingLayer.208 auto ObjLinkingLayer = std::make_unique<ObjectLinkingLayer>(209 ES, ExitOnErr(jitlink::InProcessMemoryManager::Create()));210 // Add an instance of our plugin.211 ObjLinkingLayer->addPlugin(std::make_unique<MyPlugin>());212 return ObjLinkingLayer;213 })214 .create());215 216 if (!InputObjects.empty()) {217 // Load the input objects.218 for (auto InputObject : InputObjects) {219 auto ObjBuffer =220 ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputObject)));221 ExitOnErr(J->addObjectFile(std::move(ObjBuffer)));222 }223 } else {224 auto M = ExitOnErr(parseExampleModule(TestMod, "test-module"));225 M.withModuleDo([](Module &MP) {226 outs() << "No input objects specified. Using demo module:\n"227 << MP << "\n";228 });229 ExitOnErr(J->addIRModule(std::move(M)));230 }231 232 // Look up the JIT'd function, cast it to a function pointer, then call it.233 auto EntryAddr = ExitOnErr(J->lookup(EntryPointName));234 auto *Entry = EntryAddr.toPtr<int()>();235 236 int Result = Entry();237 outs() << "---Result---\n"238 << EntryPointName << "() = " << Result << "\n";239 240 return 0;241}242