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