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