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. 64 Config.PostPrunePasses.push_back(printGraph); 65 66 // Print graph contents before and after fixups: 67 // 68 // Config.PostPrunePasses.push_back([this](jitlink::LinkGraph &G) -> Error { 69 // printLinkGraphContent(G, "Before fixup:"); 70 // return Error::success(); 71 // }); 72 // Config.PostFixupPasses.push_back([this](jitlink::LinkGraph &G) -> Error { 73 // printLinkGraphContent(G, "After fixup:"); 74 // return Error::success(); 75 // }); 76 } 77 78 void notifyLoaded(MaterializationResponsibility &MR) override { 79 outs() << "Loading object defining " << MR.getSymbols() << "\n"; 80 } 81 82 Error notifyEmitted(MaterializationResponsibility &MR) override { 83 outs() << "Emitted object defining " << MR.getSymbols() << "\n"; 84 return Error::success(); 85 } 86 87 Error notifyFailed(MaterializationResponsibility &MR) override { 88 return Error::success(); 89 } 90 91 Error notifyRemovingResources(ResourceKey K) override { 92 return Error::success(); 93 } 94 95 void notifyTransferringResources(ResourceKey DstKey, 96 ResourceKey SrcKey) override {} 97 98 private: 99 static void printBlockContent(jitlink::Block &B) { 100 constexpr JITTargetAddress LineWidth = 16; 101 102 if (B.isZeroFill()) { 103 outs() << " " << formatv("{0:x16}", B.getAddress()) << ": " 104 << B.getSize() << " bytes of zero-fill.\n"; 105 return; 106 } 107 108 JITTargetAddress InitAddr = B.getAddress() & ~(LineWidth - 1); 109 JITTargetAddress StartAddr = B.getAddress(); 110 JITTargetAddress EndAddr = B.getAddress() + B.getSize(); 111 auto *Data = reinterpret_cast<const uint8_t *>(B.getContent().data()); 112 113 for (JITTargetAddress CurAddr = InitAddr; CurAddr != EndAddr; ++CurAddr) { 114 if (CurAddr % LineWidth == 0) 115 outs() << " " << formatv("{0:x16}", CurAddr) << ": "; 116 if (CurAddr < StartAddr) 117 outs() << " "; 118 else 119 outs() << formatv("{0:x-2}", Data[CurAddr - StartAddr]) << " "; 120 if (CurAddr % LineWidth == LineWidth - 1) 121 outs() << "\n"; 122 } 123 if (EndAddr % LineWidth != 0) 124 outs() << "\n"; 125 } 126 127 static Error printGraph(jitlink::LinkGraph &G) { 128 129 DenseSet<jitlink::Block *> BlocksAlreadyVisited; 130 131 outs() << "Graph \"" << G.getName() << "\"\n"; 132 // Loop over all sections... 133 for (auto &S : G.sections()) { 134 outs() << " Section " << S.getName() << ":\n"; 135 136 // Loop over all symbols in the current section... 137 for (auto *Sym : S.symbols()) { 138 139 // Print the symbol's address. 140 outs() << " " << formatv("{0:x16}", Sym->getAddress()) << ": "; 141 142 // Print the symbol's name, or "<anonymous symbol>" if it doesn't have 143 // one. 144 if (Sym->hasName()) 145 outs() << Sym->getName() << "\n"; 146 else 147 outs() << "<anonymous symbol>\n"; 148 149 // Get the content block for this symbol. 150 auto &B = Sym->getBlock(); 151 152 if (BlocksAlreadyVisited.count(&B)) { 153 outs() << " Block " << formatv("{0:x16}", B.getAddress()) 154 << " already printed.\n"; 155 continue; 156 } else 157 outs() << " Block " << formatv("{0:x16}", B.getAddress()) 158 << ":\n"; 159 160 outs() << " Content:\n"; 161 printBlockContent(B); 162 BlocksAlreadyVisited.insert(&B); 163 164 if (!llvm::empty(B.edges())) { 165 outs() << " Edges:\n"; 166 for (auto &E : B.edges()) { 167 outs() << " " 168 << formatv("{0:x16}", B.getAddress() + E.getOffset()) 169 << ": kind = " << formatv("{0:d}", E.getKind()) 170 << ", addend = " << formatv("{0:x}", E.getAddend()) 171 << ", target = "; 172 jitlink::Symbol &TargetSym = E.getTarget(); 173 if (TargetSym.hasName()) 174 outs() << TargetSym.getName() << "\n"; 175 else 176 outs() << "<anonymous target>\n"; 177 } 178 } 179 outs() << "\n"; 180 } 181 } 182 return Error::success(); 183 } 184 }; 185 186 static cl::opt<std::string> 187 EntryPointName("entry", cl::desc("Symbol to call as main entry point"), 188 cl::init("entry")); 189 190 static cl::list<std::string> InputObjects(cl::Positional, cl::ZeroOrMore, 191 cl::desc("input objects")); 192 193 int main(int argc, char *argv[]) { 194 // Initialize LLVM. 195 InitLLVM X(argc, argv); 196 197 InitializeNativeTarget(); 198 InitializeNativeTargetAsmPrinter(); 199 200 cl::ParseCommandLineOptions(argc, argv, "LLJITWithObjectLinkingLayerPlugin"); 201 ExitOnErr.setBanner(std::string(argv[0]) + ": "); 202 203 // Detect the host and set code model to small. 204 auto JTMB = ExitOnErr(JITTargetMachineBuilder::detectHost()); 205 JTMB.setCodeModel(CodeModel::Small); 206 207 // Create an LLJIT instance with an ObjectLinkingLayer as the base layer. 208 // We attach our plugin in to the newly created ObjectLinkingLayer before 209 // returning it. 210 auto J = ExitOnErr( 211 LLJITBuilder() 212 .setJITTargetMachineBuilder(std::move(JTMB)) 213 .setObjectLinkingLayerCreator( 214 [&](ExecutionSession &ES, const Triple &TT) { 215 // Create ObjectLinkingLayer. 216 auto ObjLinkingLayer = std::make_unique<ObjectLinkingLayer>( 217 ES, std::make_unique<jitlink::InProcessMemoryManager>()); 218 // Add an instance of our plugin. 219 ObjLinkingLayer->addPlugin(std::make_unique<MyPlugin>()); 220 return ObjLinkingLayer; 221 }) 222 .create()); 223 224 if (!InputObjects.empty()) { 225 226 // If we have input objects then reflect process symbols so the input 227 // objects can do interesting things, like call printf. 228 J->getMainJITDylib().addGenerator( 229 ExitOnErr(DynamicLibrarySearchGenerator::GetForCurrentProcess( 230 J->getDataLayout().getGlobalPrefix()))); 231 232 // Load the input objects. 233 for (auto InputObject : InputObjects) { 234 auto ObjBuffer = 235 ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputObject))); 236 ExitOnErr(J->addObjectFile(std::move(ObjBuffer))); 237 } 238 } else { 239 auto M = ExitOnErr(parseExampleModule(TestMod, "test-module")); 240 M.withModuleDo([](Module &MP) { 241 outs() << "No input objects specified. Using demo module:\n" 242 << MP << "\n"; 243 }); 244 ExitOnErr(J->addIRModule(std::move(M))); 245 } 246 247 // Look up the JIT'd function, cast it to a function pointer, then call it. 248 auto EntrySym = ExitOnErr(J->lookup(EntryPointName)); 249 auto *Entry = (int (*)())EntrySym.getAddress(); 250 251 int Result = Entry(); 252 outs() << "---Result---\n" 253 << EntryPointName << "() = " << Result << "\n"; 254 255 return 0; 256 } 257