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
18int 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
34const char FooMod[] = " define i32 @foo_body() { \n"
35 " entry: \n"
36 " ret i32 1 \n"
37 " } \n";
38
39const char BarMod[] = " define i32 @bar_body() { \n"
40 " entry: \n"
41 " ret i32 2 \n"
42 " } \n";
43
44const 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
67LLVMErrorRef 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
99int 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 FooTSM;
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
218cleanup : {
219 LLVMOrcDisposeIndirectStubsManager(ISM);
220 LLVMOrcDisposeLazyCallThroughManager(LCTM);
221}
222
223jit_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
237llvm_shutdown:
238 // Shut down LLVM.
239 LLVMShutdown();
240
241 return MainResult;
242}
243

source code of llvm/examples/OrcV2Examples/OrcV2CBindingsLazy/OrcV2CBindingsLazy.c