Skip to content

Commit 1cd8493

Browse files
committed
[ORC] Expand the OrcV2 C API bindings.
Adds basic support for LLJITBuilder and DynamicLibrarySearchGenerator. This allows C API clients to configure LLJIT to expose process symbols to JIT'd code. An example of this is added in llvm/examples/OrcV2CBindingsReflectProcessSymbols.
1 parent 372cc57 commit 1cd8493

File tree

8 files changed

+593
-18
lines changed

8 files changed

+593
-18
lines changed

Diff for: llvm/examples/OrcV2Examples/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
add_subdirectory(BasicOrcV2CBindings)
21
add_subdirectory(LLJITDumpObjects)
32
add_subdirectory(LLJITWithCustomObjectLinkingLayer)
43
add_subdirectory(LLJITWithGDBRegistrationListener)
54
add_subdirectory(LLJITWithLazyReexports)
65
add_subdirectory(LLJITWithObjectCache)
76
add_subdirectory(LLJITWithObjectLinkingLayerPlugin)
7+
add_subdirectory(OrcV2CBindingsBasicUsage)
8+
add_subdirectory(OrcV2CBindingsReflectProcessSymbols)

Diff for: llvm/examples/OrcV2Examples/BasicOrcV2CBindings/CMakeLists.txt renamed to llvm/examples/OrcV2Examples/OrcV2CBindingsBasicUsage/CMakeLists.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ set(LLVM_LINK_COMPONENTS
1010
nativecodegen
1111
)
1212

13-
add_llvm_example(BasicOrcV2CBindings
14-
BasicOrcV2CBindings.c
13+
add_llvm_example(OrcV2CBindingsBasicUsage
14+
OrcV2CBindingsBasicUsage.c
1515
)

Diff for: llvm/examples/OrcV2Examples/BasicOrcV2CBindings/BasicOrcV2CBindings.c renamed to llvm/examples/OrcV2Examples/OrcV2CBindingsBasicUsage/OrcV2CBindingsBasicUsage.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ int main(int argc, char *argv[]) {
8383
LLVMOrcLLJITRef J;
8484
{
8585
LLVMErrorRef Err;
86-
if ((Err = LLVMOrcCreateDefaultLLJIT(&J))) {
86+
if ((Err = LLVMOrcCreateLLJIT(&J, 0))) {
8787
MainResult = handleError(Err);
8888
goto llvm_shutdown;
8989
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
set(LLVM_LINK_COMPONENTS
2+
Core
3+
ExecutionEngine
4+
IRReader
5+
JITLink
6+
MC
7+
OrcJIT
8+
Support
9+
Target
10+
nativecodegen
11+
)
12+
13+
add_llvm_example(OrcV2CBindingsReflectProcessSymbols
14+
OrcV2CBindingsReflectProcessSymbols.c
15+
)
16+
17+
export_executable_symbols(OrcV2CBindingsReflectProcessSymbols)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
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/Initialization.h"
12+
#include "llvm-c/Orc.h"
13+
#include "llvm-c/Support.h"
14+
#include "llvm-c/Target.h"
15+
16+
#include <assert.h>
17+
#include <stdio.h>
18+
19+
int handleError(LLVMErrorRef Err) {
20+
char *ErrMsg = LLVMGetErrorMessage(Err);
21+
fprintf(stderr, "Error: %s\n", ErrMsg);
22+
LLVMDisposeErrorMessage(ErrMsg);
23+
return 1;
24+
}
25+
26+
int32_t add(int32_t X, int32_t Y) { return X + Y; }
27+
28+
int32_t mul(int32_t X, int32_t Y) { return X * Y; }
29+
30+
int whitelistedSymbols(LLVMOrcSymbolStringPoolEntryRef Sym, void *Ctx) {
31+
assert(Ctx && "Cannot call whitelistedSymbols with a null context");
32+
33+
LLVMOrcSymbolStringPoolEntryRef *Whitelist =
34+
(LLVMOrcSymbolStringPoolEntryRef *)Ctx;
35+
36+
// If Sym appears in the whitelist then return true.
37+
LLVMOrcSymbolStringPoolEntryRef *P = Whitelist;
38+
while (*P) {
39+
if (Sym == *P)
40+
return 1;
41+
++P;
42+
}
43+
44+
// otherwise return false.
45+
return 0;
46+
}
47+
48+
LLVMOrcThreadSafeModuleRef createDemoModule() {
49+
// Create a new ThreadSafeContext and underlying LLVMContext.
50+
LLVMOrcThreadSafeContextRef TSCtx = LLVMOrcCreateNewThreadSafeContext();
51+
52+
// Get a reference to the underlying LLVMContext.
53+
LLVMContextRef Ctx = LLVMOrcThreadSafeContextGetContext(TSCtx);
54+
55+
// Create a new LLVM module.
56+
LLVMModuleRef M = LLVMModuleCreateWithNameInContext("demo", Ctx);
57+
58+
// Add a "sum" function":
59+
// - Create the function type and function instance.
60+
LLVMTypeRef I32BinOpParamTypes[] = {LLVMInt32Type(), LLVMInt32Type()};
61+
LLVMTypeRef I32BinOpFunctionType =
62+
LLVMFunctionType(LLVMInt32Type(), I32BinOpParamTypes, 2, 0);
63+
LLVMValueRef AddI32Function = LLVMAddFunction(M, "add", I32BinOpFunctionType);
64+
LLVMValueRef MulI32Function = LLVMAddFunction(M, "mul", I32BinOpFunctionType);
65+
66+
LLVMTypeRef MulAddParamTypes[] = {LLVMInt32Type(), LLVMInt32Type(),
67+
LLVMInt32Type()};
68+
LLVMTypeRef MulAddFunctionType =
69+
LLVMFunctionType(LLVMInt32Type(), MulAddParamTypes, 3, 0);
70+
LLVMValueRef MulAddFunction =
71+
LLVMAddFunction(M, "mul_add", MulAddFunctionType);
72+
73+
// - Add a basic block to the function.
74+
LLVMBasicBlockRef EntryBB = LLVMAppendBasicBlock(MulAddFunction, "entry");
75+
76+
// - Add an IR builder and point it at the end of the basic block.
77+
LLVMBuilderRef Builder = LLVMCreateBuilder();
78+
LLVMPositionBuilderAtEnd(Builder, EntryBB);
79+
80+
// - Get the three function arguments and use them co construct calls to
81+
// 'mul' and 'add':
82+
//
83+
// i32 mul_add(i32 %0, i32 %1, i32 %2) {
84+
// %t = call i32 @mul(i32 %0, i32 %1)
85+
// %r = call i32 @add(i32 %t, i32 %2)
86+
// ret i32 %r
87+
// }
88+
LLVMValueRef SumArg0 = LLVMGetParam(MulAddFunction, 0);
89+
LLVMValueRef SumArg1 = LLVMGetParam(MulAddFunction, 1);
90+
LLVMValueRef SumArg2 = LLVMGetParam(MulAddFunction, 2);
91+
92+
LLVMValueRef MulArgs[] = {SumArg0, SumArg1};
93+
LLVMValueRef MulResult = LLVMBuildCall2(Builder, I32BinOpFunctionType,
94+
MulI32Function, MulArgs, 2, "t");
95+
96+
LLVMValueRef AddArgs[] = {MulResult, SumArg2};
97+
LLVMValueRef AddResult = LLVMBuildCall2(Builder, I32BinOpFunctionType,
98+
AddI32Function, AddArgs, 2, "r");
99+
100+
// - Build the return instruction.
101+
LLVMBuildRet(Builder, AddResult);
102+
103+
// Our demo module is now complete. Wrap it and our ThreadSafeContext in a
104+
// ThreadSafeModule.
105+
LLVMOrcThreadSafeModuleRef TSM = LLVMOrcCreateNewThreadSafeModule(M, TSCtx);
106+
107+
// Dispose of our local ThreadSafeContext value. The underlying LLVMContext
108+
// will be kept alive by our ThreadSafeModule, TSM.
109+
LLVMOrcDisposeThreadSafeContext(TSCtx);
110+
111+
// Return the result.
112+
return TSM;
113+
}
114+
115+
int main(int argc, char *argv[]) {
116+
117+
int MainResult = 0;
118+
119+
// Parse command line arguments and initialize LLVM Core.
120+
LLVMParseCommandLineOptions(argc, (const char **)argv, "");
121+
LLVMInitializeCore(LLVMGetGlobalPassRegistry());
122+
123+
// Initialize native target codegen and asm printer.
124+
LLVMInitializeNativeTarget();
125+
LLVMInitializeNativeAsmPrinter();
126+
127+
// Create the JIT instance.
128+
LLVMOrcLLJITRef J;
129+
{
130+
LLVMErrorRef Err;
131+
if ((Err = LLVMOrcCreateLLJIT(&J, 0))) {
132+
MainResult = handleError(Err);
133+
goto llvm_shutdown;
134+
}
135+
}
136+
137+
// Build a filter to allow JIT'd code to only access whitelisted symbols.
138+
// This filter is optional: If a null value is suppled for the Filter
139+
// argument to LLVMOrcCreateDynamicLibrarySearchGeneratorForProcess then
140+
// all process symbols will be reflected.
141+
LLVMOrcSymbolStringPoolEntryRef Whitelist[] = {
142+
LLVMOrcLLJITMangleAndIntern(J, "mul"),
143+
LLVMOrcLLJITMangleAndIntern(J, "add"), 0};
144+
145+
{
146+
LLVMOrcJITDylibDefinitionGeneratorRef ProcessSymbolsGenerator = 0;
147+
LLVMErrorRef Err;
148+
if ((Err = LLVMOrcCreateDynamicLibrarySearchGeneratorForProcess(
149+
&ProcessSymbolsGenerator, LLVMOrcLLJITGetGlobalPrefix(J),
150+
whitelistedSymbols, Whitelist))) {
151+
MainResult = handleError(Err);
152+
goto jit_cleanup;
153+
}
154+
155+
LLVMOrcJITDylibAddGenerator(LLVMOrcLLJITGetMainJITDylib(J),
156+
ProcessSymbolsGenerator);
157+
}
158+
159+
// Create our demo module.
160+
LLVMOrcThreadSafeModuleRef TSM = createDemoModule();
161+
162+
// Add our demo module to the JIT.
163+
{
164+
LLVMErrorRef Err;
165+
if ((Err = LLVMOrcLLJITAddLLVMIRModule(J, TSM))) {
166+
// If adding the ThreadSafeModule fails then we need to clean it up
167+
// ourselves. If adding it succeeds the JIT will manage the memory.
168+
LLVMOrcDisposeThreadSafeModule(TSM);
169+
MainResult = handleError(Err);
170+
goto jit_cleanup;
171+
}
172+
}
173+
174+
// Look up the address of our demo entry point.
175+
LLVMOrcJITTargetAddress MulAddAddr;
176+
{
177+
LLVMErrorRef Err;
178+
if ((Err = LLVMOrcLLJITLookup(J, &MulAddAddr, "mul_add"))) {
179+
MainResult = handleError(Err);
180+
goto jit_cleanup;
181+
}
182+
}
183+
184+
// If we made it here then everything succeeded. Execute our JIT'd code.
185+
int32_t (*MulAdd)(int32_t, int32_t, int32_t) =
186+
(int32_t(*)(int32_t, int32_t, int32_t))MulAddAddr;
187+
int32_t Result = MulAdd(3, 4, 5);
188+
189+
// Print the result.
190+
printf("3 * 4 + 5 = %i\n", Result);
191+
192+
jit_cleanup:
193+
// Release all symbol string pool entries that we have allocated. In this
194+
// example that's just our whitelist entries.
195+
{
196+
LLVMOrcSymbolStringPoolEntryRef *P = Whitelist;
197+
while (*P)
198+
LLVMOrcReleaseSymbolStringPoolEntry(*P++);
199+
}
200+
201+
// Destroy our JIT instance. This will clean up any memory that the JIT has
202+
// taken ownership of. This operation is non-trivial (e.g. it may need to
203+
// JIT static destructors) and may also fail. In that case we want to render
204+
// the error to stderr, but not overwrite any existing return value.
205+
{
206+
LLVMErrorRef Err;
207+
if ((Err = LLVMOrcDisposeLLJIT(J))) {
208+
int NewFailureResult = handleError(Err);
209+
if (MainResult == 0)
210+
MainResult = NewFailureResult;
211+
}
212+
}
213+
214+
llvm_shutdown:
215+
// Shut down LLVM.
216+
LLVMShutdown();
217+
218+
return MainResult;
219+
}

0 commit comments

Comments
 (0)