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(x: 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(JITDylib &JD, ResourceKey K) override { |
87 | return Error::success(); |
88 | } |
89 | |
90 | void notifyTransferringResources(JITDylib &JD, 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(Fmt: "{0:x16}" , Vals: 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(Fmt: "{0:x16}" , Vals: CurAddr.getValue()) |
111 | << ": " ; |
112 | if (CurAddr < StartAddr) |
113 | outs() << " " ; |
114 | else |
115 | outs() << formatv(Fmt: "{0:x-2}" , Vals: 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(Fmt: "{0:x16}" , Vals: 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(V: &B)) { |
149 | outs() << " Block " << formatv(Fmt: "{0:x16}" , Vals: B.getAddress()) |
150 | << " already printed.\n" ; |
151 | continue; |
152 | } else |
153 | outs() << " Block " << formatv(Fmt: "{0:x16}" , Vals: B.getAddress()) |
154 | << ":\n" ; |
155 | |
156 | outs() << " Content:\n" ; |
157 | printBlockContent(B); |
158 | BlocksAlreadyVisited.insert(V: &B); |
159 | |
160 | if (!B.edges().empty()) { |
161 | outs() << " Edges:\n" ; |
162 | for (auto &E : B.edges()) { |
163 | outs() << " " |
164 | << formatv(Fmt: "{0:x16}" , Vals: B.getAddress() + E.getOffset()) |
165 | << ": kind = " << formatv(Fmt: "{0:d}" , Vals: E.getKind()) |
166 | << ", addend = " << formatv(Fmt: "{0:x}" , Vals: 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(Val: "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, Overview: "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 | args&: ES, args: ExitOnErr(jitlink::InProcessMemoryManager::Create())); |
214 | // Add an instance of our plugin. |
215 | ObjLinkingLayer->addPlugin(P: 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(EO: MemoryBuffer::getFile(Filename: InputObject))); |
225 | ExitOnErr(J->addObjectFile(Obj: std::move(ObjBuffer))); |
226 | } |
227 | } else { |
228 | auto M = ExitOnErr(parseExampleModule(Source: TestMod, Name: "test-module" )); |
229 | M.withModuleDo(F: [](Module &MP) { |
230 | outs() << "No input objects specified. Using demo module:\n" |
231 | << MP << "\n" ; |
232 | }); |
233 | ExitOnErr(J->addIRModule(TSM: 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(UnmangledName: 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 | |