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