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.
modifyPassConfig(MaterializationResponsibility & MR,jitlink::LinkGraph & LG,jitlink::PassConfiguration & Config)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 
notifyLoaded(MaterializationResponsibility & MR)73   void notifyLoaded(MaterializationResponsibility &MR) override {
74     outs() << "Loading object defining " << MR.getSymbols() << "\n";
75   }
76 
notifyEmitted(MaterializationResponsibility & MR)77   Error notifyEmitted(MaterializationResponsibility &MR) override {
78     outs() << "Emitted object defining " << MR.getSymbols() << "\n";
79     return Error::success();
80   }
81 
notifyFailed(MaterializationResponsibility & MR)82   Error notifyFailed(MaterializationResponsibility &MR) override {
83     return Error::success();
84   }
85 
notifyRemovingResources(JITDylib & JD,ResourceKey K)86   Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override {
87     return Error::success();
88   }
89 
notifyTransferringResources(JITDylib & JD,ResourceKey DstKey,ResourceKey SrcKey)90   void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey,
91                                    ResourceKey SrcKey) override {}
92 
93 private:
printBlockContent(jitlink::Block & B)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 
printGraph(jitlink::LinkGraph & G)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 (!B.edges().empty()) {
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 
main(int argc,char * argv[])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     // Load the input objects.
222     for (auto InputObject : InputObjects) {
223       auto ObjBuffer =
224           ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputObject)));
225       ExitOnErr(J->addObjectFile(std::move(ObjBuffer)));
226     }
227   } else {
228     auto M = ExitOnErr(parseExampleModule(TestMod, "test-module"));
229     M.withModuleDo([](Module &MP) {
230       outs() << "No input objects specified. Using demo module:\n"
231              << MP << "\n";
232     });
233     ExitOnErr(J->addIRModule(std::move(M)));
234   }
235 
236   // Look up the JIT'd function, cast it to a function pointer, then call it.
237   auto EntryAddr = ExitOnErr(J->lookup(EntryPointName));
238   auto *Entry = EntryAddr.toPtr<int()>();
239 
240   int Result = Entry();
241   outs() << "---Result---\n"
242          << EntryPointName << "() = " << Result << "\n";
243 
244   return 0;
245 }
246