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