1 | //===-------- BasicOrcV2CBindings.c - Basic OrcV2 C Bindings Demo ---------===// |
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 | #include "llvm-c/Core.h" |
10 | #include "llvm-c/Error.h" |
11 | #include "llvm-c/IRReader.h" |
12 | #include "llvm-c/LLJIT.h" |
13 | #include "llvm-c/Support.h" |
14 | #include "llvm-c/Target.h" |
15 | |
16 | #include <assert.h> |
17 | #include <stdio.h> |
18 | #include <string.h> |
19 | |
20 | int handleError(LLVMErrorRef Err) { |
21 | char *ErrMsg = LLVMGetErrorMessage(Err); |
22 | fprintf(stderr, format: "Error: %s\n" , ErrMsg); |
23 | LLVMDisposeErrorMessage(ErrMsg); |
24 | return 1; |
25 | } |
26 | |
27 | // Example IR modules. |
28 | // |
29 | // Note that in the conditionally compiled modules, FooMod and BarMod, functions |
30 | // have been given an _body suffix. This is to ensure that their names do not |
31 | // clash with their lazy-reexports. |
32 | // For clients who do not wish to rename function bodies (e.g. because they want |
33 | // to re-use cached objects between static and JIT compiles) techniques exist to |
34 | // avoid renaming. See the lazy-reexports section of the ORCv2 design doc. |
35 | |
36 | const char FooMod[] = " define i32 @foo_body() { \n" |
37 | " entry: \n" |
38 | " ret i32 1 \n" |
39 | " } \n" ; |
40 | |
41 | const char BarMod[] = " define i32 @bar_body() { \n" |
42 | " entry: \n" |
43 | " ret i32 2 \n" |
44 | " } \n" ; |
45 | |
46 | const char MainMod[] = |
47 | " define i32 @entry(i32 %argc) { \n" |
48 | " entry: \n" |
49 | " %and = and i32 %argc, 1 \n" |
50 | " %tobool = icmp eq i32 %and, 0 \n" |
51 | " br i1 %tobool, label %if.end, label %if.then \n" |
52 | " \n" |
53 | " if.then: \n" |
54 | " %call = tail call i32 @foo() \n" |
55 | " br label %return \n" |
56 | " \n" |
57 | " if.end: \n" |
58 | " %call1 = tail call i32 @bar() \n" |
59 | " br label %return \n" |
60 | " \n" |
61 | " return: \n" |
62 | " %retval.0 = phi i32 [ %call, %if.then ], [ %call1, %if.end ] \n" |
63 | " ret i32 %retval.0 \n" |
64 | " } \n" |
65 | " \n" |
66 | " declare i32 @foo() \n" |
67 | " declare i32 @bar() \n" ; |
68 | |
69 | LLVMErrorRef applyDataLayout(void *Ctx, LLVMModuleRef M) { |
70 | LLVMSetDataLayout(M, DataLayoutStr: LLVMOrcLLJITGetDataLayoutStr(J: (LLVMOrcLLJITRef)Ctx)); |
71 | return LLVMErrorSuccess; |
72 | } |
73 | |
74 | LLVMErrorRef parseExampleModule(const char *Source, size_t Len, |
75 | const char *Name, |
76 | LLVMOrcThreadSafeModuleRef *TSM) { |
77 | // Create a new ThreadSafeContext and underlying LLVMContext. |
78 | LLVMOrcThreadSafeContextRef TSCtx = LLVMOrcCreateNewThreadSafeContext(); |
79 | |
80 | // Get a reference to the underlying LLVMContext. |
81 | LLVMContextRef Ctx = LLVMOrcThreadSafeContextGetContext(TSCtx); |
82 | |
83 | // Wrap Source in a MemoryBuffer |
84 | LLVMMemoryBufferRef MB = |
85 | LLVMCreateMemoryBufferWithMemoryRange(InputData: Source, InputDataLength: Len, BufferName: Name, RequiresNullTerminator: 1); |
86 | |
87 | // Parse the LLVM module. |
88 | LLVMModuleRef M; |
89 | char *ErrMsg; |
90 | if (LLVMParseIRInContext(ContextRef: Ctx, MemBuf: MB, OutM: &M, OutMessage: &ErrMsg)) { |
91 | LLVMErrorRef Err = LLVMCreateStringError(ErrMsg); |
92 | LLVMDisposeMessage(Message: ErrMsg); |
93 | return Err; |
94 | } |
95 | |
96 | // Our module is now complete. Wrap it and our ThreadSafeContext in a |
97 | // ThreadSafeModule. |
98 | *TSM = LLVMOrcCreateNewThreadSafeModule(M, TSCtx); |
99 | |
100 | // Dispose of our local ThreadSafeContext value. The underlying LLVMContext |
101 | // will be kept alive by our ThreadSafeModule, TSM. |
102 | LLVMOrcDisposeThreadSafeContext(TSCtx); |
103 | |
104 | return LLVMErrorSuccess; |
105 | } |
106 | |
107 | void Destroy(void *Ctx) {} |
108 | |
109 | void Materialize(void *Ctx, LLVMOrcMaterializationResponsibilityRef MR) { |
110 | int MainResult = 0; |
111 | |
112 | size_t NumSymbols; |
113 | LLVMOrcSymbolStringPoolEntryRef *Symbols = |
114 | LLVMOrcMaterializationResponsibilityGetRequestedSymbols(MR, NumSymbols: &NumSymbols); |
115 | |
116 | assert(NumSymbols == 1); |
117 | |
118 | LLVMOrcLLJITRef J = (LLVMOrcLLJITRef)Ctx; |
119 | LLVMOrcSymbolStringPoolEntryRef Sym = Symbols[0]; |
120 | |
121 | LLVMOrcThreadSafeModuleRef TSM = 0; |
122 | LLVMErrorRef Err; |
123 | |
124 | LLVMOrcSymbolStringPoolEntryRef FooBody = |
125 | LLVMOrcLLJITMangleAndIntern(J, UnmangledName: "foo_body" ); |
126 | LLVMOrcSymbolStringPoolEntryRef BarBody = |
127 | LLVMOrcLLJITMangleAndIntern(J, UnmangledName: "bar_body" ); |
128 | |
129 | if (Sym == FooBody) { |
130 | if ((Err = parseExampleModule(Source: FooMod, Len: strlen(s: FooMod), Name: "foo-mod" , TSM: &TSM))) { |
131 | MainResult = handleError(Err); |
132 | goto cleanup; |
133 | } |
134 | } else if (Sym == BarBody) { |
135 | if ((Err = parseExampleModule(Source: BarMod, Len: strlen(s: BarMod), Name: "bar-mod" , TSM: &TSM))) { |
136 | MainResult = handleError(Err); |
137 | goto cleanup; |
138 | } |
139 | } else { |
140 | MainResult = 1; |
141 | goto cleanup; |
142 | } |
143 | assert(TSM); |
144 | |
145 | if ((Err = LLVMOrcThreadSafeModuleWithModuleDo(TSM, F: &applyDataLayout, Ctx))) { |
146 | MainResult = handleError(Err); |
147 | goto cleanup; |
148 | } |
149 | |
150 | cleanup: |
151 | LLVMOrcReleaseSymbolStringPoolEntry(S: BarBody); |
152 | LLVMOrcReleaseSymbolStringPoolEntry(S: FooBody); |
153 | LLVMOrcDisposeSymbols(Symbols); |
154 | if (MainResult == 1) { |
155 | LLVMOrcMaterializationResponsibilityFailMaterialization(MR); |
156 | LLVMOrcDisposeMaterializationResponsibility(MR); |
157 | } else { |
158 | LLVMOrcIRTransformLayerRef IRLayer = LLVMOrcLLJITGetIRTransformLayer(J); |
159 | LLVMOrcIRTransformLayerEmit(IRTransformLayer: IRLayer, MR, TSM); |
160 | } |
161 | } |
162 | |
163 | int main(int argc, const char *argv[]) { |
164 | |
165 | int MainResult = 0; |
166 | |
167 | // Parse command line arguments and initialize LLVM Core. |
168 | LLVMParseCommandLineOptions(argc, argv, Overview: "" ); |
169 | |
170 | // Initialize native target codegen and asm printer. |
171 | LLVMInitializeNativeTarget(); |
172 | LLVMInitializeNativeAsmPrinter(); |
173 | |
174 | // Set up a JIT instance. |
175 | LLVMOrcLLJITRef J; |
176 | const char *TargetTriple; |
177 | { |
178 | LLVMErrorRef Err; |
179 | if ((Err = LLVMOrcCreateLLJIT(Result: &J, Builder: 0))) { |
180 | MainResult = handleError(Err); |
181 | goto llvm_shutdown; |
182 | } |
183 | TargetTriple = LLVMOrcLLJITGetTripleString(J); |
184 | } |
185 | |
186 | // Add our main module to the JIT. |
187 | { |
188 | LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J); |
189 | LLVMErrorRef Err; |
190 | |
191 | LLVMOrcThreadSafeModuleRef MainTSM; |
192 | if ((Err = parseExampleModule(Source: MainMod, Len: strlen(s: MainMod), Name: "main-mod" , |
193 | TSM: &MainTSM))) { |
194 | MainResult = handleError(Err); |
195 | goto jit_cleanup; |
196 | } |
197 | |
198 | if ((Err = LLVMOrcLLJITAddLLVMIRModule(J, JD: MainJD, TSM: MainTSM))) { |
199 | LLVMOrcDisposeThreadSafeModule(TSM: MainTSM); |
200 | MainResult = handleError(Err); |
201 | goto jit_cleanup; |
202 | } |
203 | } |
204 | |
205 | LLVMJITSymbolFlags Flags = { |
206 | LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable, 0}; |
207 | LLVMOrcCSymbolFlagsMapPair FooSym = { |
208 | LLVMOrcLLJITMangleAndIntern(J, UnmangledName: "foo_body" ), Flags}; |
209 | LLVMOrcCSymbolFlagsMapPair BarSym = { |
210 | LLVMOrcLLJITMangleAndIntern(J, UnmangledName: "bar_body" ), Flags}; |
211 | |
212 | // add custom MaterializationUnit |
213 | { |
214 | LLVMOrcMaterializationUnitRef FooMU = |
215 | LLVMOrcCreateCustomMaterializationUnit(Name: "FooMU" , Ctx: J, Syms: &FooSym, NumSyms: 1, NULL, |
216 | Materialize: &Materialize, NULL, Destroy: &Destroy); |
217 | |
218 | LLVMOrcMaterializationUnitRef BarMU = |
219 | LLVMOrcCreateCustomMaterializationUnit(Name: "BarMU" , Ctx: J, Syms: &BarSym, NumSyms: 1, NULL, |
220 | Materialize: &Materialize, NULL, Destroy: &Destroy); |
221 | |
222 | LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J); |
223 | LLVMOrcJITDylibDefine(JD: MainJD, MU: FooMU); |
224 | LLVMOrcJITDylibDefine(JD: MainJD, MU: BarMU); |
225 | } |
226 | |
227 | // add lazy reexports |
228 | LLVMOrcIndirectStubsManagerRef ISM = |
229 | LLVMOrcCreateLocalIndirectStubsManager(TargetTriple); |
230 | |
231 | LLVMOrcLazyCallThroughManagerRef LCTM; |
232 | { |
233 | LLVMErrorRef Err; |
234 | LLVMOrcExecutionSessionRef ES = LLVMOrcLLJITGetExecutionSession(J); |
235 | if ((Err = LLVMOrcCreateLocalLazyCallThroughManager(TargetTriple, ES, ErrorHandlerAddr: 0, |
236 | LCTM: &LCTM))) { |
237 | LLVMOrcDisposeIndirectStubsManager(ISM); |
238 | MainResult = handleError(Err); |
239 | goto jit_cleanup; |
240 | } |
241 | } |
242 | |
243 | LLVMOrcCSymbolAliasMapPair ReExports[2] = { |
244 | {LLVMOrcLLJITMangleAndIntern(J, UnmangledName: "foo" ), |
245 | {LLVMOrcLLJITMangleAndIntern(J, UnmangledName: "foo_body" ), Flags}}, |
246 | {LLVMOrcLLJITMangleAndIntern(J, UnmangledName: "bar" ), |
247 | {LLVMOrcLLJITMangleAndIntern(J, UnmangledName: "bar_body" ), Flags}}, |
248 | }; |
249 | |
250 | { |
251 | LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J); |
252 | LLVMOrcMaterializationUnitRef MU = |
253 | LLVMOrcLazyReexports(LCTM, ISM, SourceRef: MainJD, CallableAliases: ReExports, NumPairs: 2); |
254 | LLVMOrcJITDylibDefine(JD: MainJD, MU); |
255 | } |
256 | |
257 | // Look up the address of our demo entry point. |
258 | LLVMOrcJITTargetAddress EntryAddr; |
259 | { |
260 | LLVMErrorRef Err; |
261 | if ((Err = LLVMOrcLLJITLookup(J, Result: &EntryAddr, Name: "entry" ))) { |
262 | MainResult = handleError(Err); |
263 | goto cleanup; |
264 | } |
265 | } |
266 | |
267 | // If we made it here then everything succeeded. Execute our JIT'd code. |
268 | int32_t (*Entry)(int32_t) = (int32_t(*)(int32_t))EntryAddr; |
269 | int32_t Result = Entry(argc); |
270 | |
271 | printf(format: "--- Result ---\n" ); |
272 | printf(format: "entry(%i) = %i\n" , argc, Result); |
273 | |
274 | cleanup : { |
275 | LLVMOrcDisposeIndirectStubsManager(ISM); |
276 | LLVMOrcDisposeLazyCallThroughManager(LCTM); |
277 | } |
278 | |
279 | jit_cleanup: |
280 | // Destroy our JIT instance. This will clean up any memory that the JIT has |
281 | // taken ownership of. This operation is non-trivial (e.g. it may need to |
282 | // JIT static destructors) and may also fail. In that case we want to render |
283 | // the error to stderr, but not overwrite any existing return value. |
284 | { |
285 | LLVMErrorRef Err; |
286 | if ((Err = LLVMOrcDisposeLLJIT(J))) { |
287 | int NewFailureResult = handleError(Err); |
288 | if (MainResult == 0) |
289 | MainResult = NewFailureResult; |
290 | } |
291 | } |
292 | |
293 | llvm_shutdown: |
294 | // Shut down LLVM. |
295 | LLVMShutdown(); |
296 | |
297 | return MainResult; |
298 | } |
299 | |