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 | |
18 | int handleError(LLVMErrorRef Err) { |
19 | char *ErrMsg = LLVMGetErrorMessage(Err); |
20 | fprintf(stderr, format: "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 | |
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(InputData: Source, InputDataLength: Len, BufferName: Name, RequiresNullTerminator: 0); |
79 | |
80 | // Parse the LLVM module. |
81 | LLVMModuleRef M; |
82 | char *ErrMsg; |
83 | if (LLVMParseIRInContext(ContextRef: Ctx, MemBuf: MB, OutM: &M, OutMessage: &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 | |
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, Overview: "" ); |
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(Result: &J, Builder: 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 ; |
128 | if ((Err = parseExampleModule(Source: FooMod, Len: sizeof(FooMod) - 1, Name: "foo-mod" , |
129 | TSM: &FooTSM))) { |
130 | MainResult = handleError(Err); |
131 | goto jit_cleanup; |
132 | } |
133 | |
134 | if ((Err = LLVMOrcLLJITAddLLVMIRModule(J, JD: MainJD, TSM: 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(TSM: FooTSM); |
138 | MainResult = handleError(Err); |
139 | goto jit_cleanup; |
140 | } |
141 | |
142 | LLVMOrcThreadSafeModuleRef BarTSM; |
143 | if ((Err = parseExampleModule(Source: BarMod, Len: sizeof(BarMod) - 1, Name: "bar-mod" , |
144 | TSM: &BarTSM))) { |
145 | MainResult = handleError(Err); |
146 | goto jit_cleanup; |
147 | } |
148 | |
149 | if ((Err = LLVMOrcLLJITAddLLVMIRModule(J, JD: MainJD, TSM: BarTSM))) { |
150 | LLVMOrcDisposeThreadSafeModule(TSM: BarTSM); |
151 | MainResult = handleError(Err); |
152 | goto jit_cleanup; |
153 | } |
154 | |
155 | LLVMOrcThreadSafeModuleRef MainTSM; |
156 | if ((Err = parseExampleModule(Source: MainMod, Len: sizeof(MainMod) - 1, Name: "main-mod" , |
157 | TSM: &MainTSM))) { |
158 | MainResult = handleError(Err); |
159 | goto jit_cleanup; |
160 | } |
161 | |
162 | if ((Err = LLVMOrcLLJITAddLLVMIRModule(J, JD: MainJD, TSM: MainTSM))) { |
163 | LLVMOrcDisposeThreadSafeModule(TSM: 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, ErrorHandlerAddr: 0, |
178 | LCTM: &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, UnmangledName: "foo" ), |
189 | {LLVMOrcLLJITMangleAndIntern(J, UnmangledName: "foo_body" ), flag}}, |
190 | {LLVMOrcLLJITMangleAndIntern(J, UnmangledName: "bar" ), |
191 | {LLVMOrcLLJITMangleAndIntern(J, UnmangledName: "bar_body" ), flag}}, |
192 | }; |
193 | |
194 | { |
195 | LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J); |
196 | LLVMOrcMaterializationUnitRef MU = |
197 | LLVMOrcLazyReexports(LCTM, ISM, SourceRef: MainJD, CallableAliases: ReExports, NumPairs: 2); |
198 | LLVMOrcJITDylibDefine(JD: 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, Result: &EntryAddr, Name: "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(format: "--- Result ---\n" ); |
216 | printf(format: "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 | |