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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file shows how to switch LLJIT to use a custom object linking layer (we 10 // 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 26 using namespace llvm; 27 using namespace llvm::orc; 28 29 ExitOnError ExitOnErr; 30 31 const llvm::StringRef TestMod = 32 R"( 33 define i32 @callee() { 34 entry: 35 ret i32 7 36 } 37 38 define i32 @entry() { 39 entry: 40 %0 = call i32 @callee() 41 ret i32 %0 42 } 43 )"; 44 45 class MyPlugin : public ObjectLinkingLayer::Plugin { 46 public: 47 // The modifyPassConfig callback gives us a chance to inspect the 48 // MaterializationResponsibility and target triple for the object being 49 // linked, then add any JITLink passes that we would like to run on the 50 // link graph. A pass is just a function object that is callable as 51 // Error(jitlink::LinkGraph&). In this case we will add two passes 52 // defined as lambdas that call the printLinkerGraph method on our 53 // plugin: One to run before the linker applies fixups and another to 54 // 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 the 63 // associated blocks at the 'PostPrune' phase of JITLink (after 64 // dead-stripping, but before addresses are allocated in the target 65 // address space. See llvm/docs/JITLink.rst). 66 // 67 // Experiment with adding the 'printGraph' pass at other points in the 68 // pipeline. E.g. PrePrunePasses, PostAllocationPasses, and 69 // PostFixupPasses. 70 Config.PostPrunePasses.push_back(printGraph); 71 } 72 73 void notifyLoaded(MaterializationResponsibility &MR) override { 74 outs() << "Loading object defining " << MR.getSymbols() << "\n"; 75 } 76 77 Error notifyEmitted(MaterializationResponsibility &MR) override { 78 outs() << "Emitted object defining " << MR.getSymbols() << "\n"; 79 return Error::success(); 80 } 81 82 Error notifyFailed(MaterializationResponsibility &MR) override { 83 return Error::success(); 84 } 85 86 Error notifyRemovingResources(ResourceKey K) override { 87 return Error::success(); 88 } 89 90 void notifyTransferringResources(ResourceKey DstKey, 91 ResourceKey SrcKey) override {} 92 93 private: 94 static void printBlockContent(jitlink::Block &B) { 95 constexpr JITTargetAddress LineWidth = 16; 96 97 if (B.isZeroFill()) { 98 outs() << " " << formatv("{0:x16}", B.getAddress()) << ": " 99 << B.getSize() << " bytes of zero-fill.\n"; 100 return; 101 } 102 103 ExecutorAddr InitAddr(B.getAddress().getValue() & ~(LineWidth - 1)); 104 ExecutorAddr StartAddr = B.getAddress(); 105 ExecutorAddr EndAddr = B.getAddress() + B.getSize(); 106 auto *Data = reinterpret_cast<const uint8_t *>(B.getContent().data()); 107 108 for (ExecutorAddr CurAddr = InitAddr; CurAddr != EndAddr; ++CurAddr) { 109 if (CurAddr % LineWidth == 0) 110 outs() << " " << formatv("{0:x16}", CurAddr.getValue()) 111 << ": "; 112 if (CurAddr < StartAddr) 113 outs() << " "; 114 else 115 outs() << formatv("{0:x-2}", Data[CurAddr - StartAddr]) << " "; 116 if (CurAddr % LineWidth == LineWidth - 1) 117 outs() << "\n"; 118 } 119 if (EndAddr % LineWidth != 0) 120 outs() << "\n"; 121 } 122 123 static Error printGraph(jitlink::LinkGraph &G) { 124 125 DenseSet<jitlink::Block *> BlocksAlreadyVisited; 126 127 outs() << "Graph \"" << G.getName() << "\"\n"; 128 // Loop over all sections... 129 for (auto &S : G.sections()) { 130 outs() << " Section " << S.getName() << ":\n"; 131 132 // Loop over all symbols in the current section... 133 for (auto *Sym : S.symbols()) { 134 135 // Print the symbol's address. 136 outs() << " " << formatv("{0:x16}", Sym->getAddress()) << ": "; 137 138 // Print the symbol's name, or "<anonymous symbol>" if it doesn't have 139 // one. 140 if (Sym->hasName()) 141 outs() << Sym->getName() << "\n"; 142 else 143 outs() << "<anonymous symbol>\n"; 144 145 // Get the content block for this symbol. 146 auto &B = Sym->getBlock(); 147 148 if (BlocksAlreadyVisited.count(&B)) { 149 outs() << " Block " << formatv("{0:x16}", B.getAddress()) 150 << " already printed.\n"; 151 continue; 152 } else 153 outs() << " Block " << formatv("{0:x16}", B.getAddress()) 154 << ":\n"; 155 156 outs() << " Content:\n"; 157 printBlockContent(B); 158 BlocksAlreadyVisited.insert(&B); 159 160 if (!llvm::empty(B.edges())) { 161 outs() << " Edges:\n"; 162 for (auto &E : B.edges()) { 163 outs() << " " 164 << formatv("{0:x16}", B.getAddress() + E.getOffset()) 165 << ": kind = " << formatv("{0:d}", E.getKind()) 166 << ", addend = " << formatv("{0:x}", E.getAddend()) 167 << ", target = "; 168 jitlink::Symbol &TargetSym = E.getTarget(); 169 if (TargetSym.hasName()) 170 outs() << TargetSym.getName() << "\n"; 171 else 172 outs() << "<anonymous target>\n"; 173 } 174 } 175 outs() << "\n"; 176 } 177 } 178 return Error::success(); 179 } 180 }; 181 182 static cl::opt<std::string> 183 EntryPointName("entry", cl::desc("Symbol to call as main entry point"), 184 cl::init("entry")); 185 186 static cl::list<std::string> InputObjects(cl::Positional, 187 cl::desc("input objects")); 188 189 int main(int argc, char *argv[]) { 190 // Initialize LLVM. 191 InitLLVM X(argc, argv); 192 193 InitializeNativeTarget(); 194 InitializeNativeTargetAsmPrinter(); 195 196 cl::ParseCommandLineOptions(argc, argv, "LLJITWithObjectLinkingLayerPlugin"); 197 ExitOnErr.setBanner(std::string(argv[0]) + ": "); 198 199 // Detect the host and set code model to small. 200 auto JTMB = ExitOnErr(JITTargetMachineBuilder::detectHost()); 201 JTMB.setCodeModel(CodeModel::Small); 202 203 // Create an LLJIT instance with an ObjectLinkingLayer as the base layer. 204 // We attach our plugin in to the newly created ObjectLinkingLayer before 205 // returning it. 206 auto J = ExitOnErr( 207 LLJITBuilder() 208 .setJITTargetMachineBuilder(std::move(JTMB)) 209 .setObjectLinkingLayerCreator( 210 [&](ExecutionSession &ES, const Triple &TT) { 211 // Create ObjectLinkingLayer. 212 auto ObjLinkingLayer = std::make_unique<ObjectLinkingLayer>( 213 ES, ExitOnErr(jitlink::InProcessMemoryManager::Create())); 214 // Add an instance of our plugin. 215 ObjLinkingLayer->addPlugin(std::make_unique<MyPlugin>()); 216 return ObjLinkingLayer; 217 }) 218 .create()); 219 220 if (!InputObjects.empty()) { 221 222 // If we have input objects then reflect process symbols so the input 223 // objects can do interesting things, like call printf. 224 J->getMainJITDylib().addGenerator( 225 ExitOnErr(DynamicLibrarySearchGenerator::GetForCurrentProcess( 226 J->getDataLayout().getGlobalPrefix()))); 227 228 // Load the input objects. 229 for (auto InputObject : InputObjects) { 230 auto ObjBuffer = 231 ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputObject))); 232 ExitOnErr(J->addObjectFile(std::move(ObjBuffer))); 233 } 234 } else { 235 auto M = ExitOnErr(parseExampleModule(TestMod, "test-module")); 236 M.withModuleDo([](Module &MP) { 237 outs() << "No input objects specified. Using demo module:\n" 238 << MP << "\n"; 239 }); 240 ExitOnErr(J->addIRModule(std::move(M))); 241 } 242 243 // Look up the JIT'd function, cast it to a function pointer, then call it. 244 auto EntryAddr = ExitOnErr(J->lookup(EntryPointName)); 245 auto *Entry = EntryAddr.toPtr<int()>(); 246 247 int Result = Entry(); 248 outs() << "---Result---\n" 249 << EntryPointName << "() = " << Result << "\n"; 250 251 return 0; 252 } 253