//===-------- BasicOrcV2CBindings.c - Basic OrcV2 C Bindings Demo ---------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm-c/Core.h" #include "llvm-c/Error.h" #include "llvm-c/IRReader.h" #include "llvm-c/LLJIT.h" #include "llvm-c/Support.h" #include "llvm-c/Target.h" #include int handleError(LLVMErrorRef Err) { char *ErrMsg = LLVMGetErrorMessage(Err); fprintf(stderr, "Error: %s\n", ErrMsg); LLVMDisposeErrorMessage(ErrMsg); return 1; } // Example IR modules. // // Note that in the conditionally compiled modules, FooMod and BarMod, functions // have been given an _body suffix. This is to ensure that their names do not // clash with their lazy-reexports. // For clients who do not wish to rename function bodies (e.g. because they want // to re-use cached objects between static and JIT compiles) techniques exist to // avoid renaming. See the lazy-reexports section of the ORCv2 design doc. const char FooMod[] = " define i32 @foo_body() { \n" " entry: \n" " ret i32 1 \n" " } \n"; const char BarMod[] = " define i32 @bar_body() { \n" " entry: \n" " ret i32 2 \n" " } \n"; const char MainMod[] = " define i32 @entry(i32 %argc) { \n" " entry: \n" " %and = and i32 %argc, 1 \n" " %tobool = icmp eq i32 %and, 0 \n" " br i1 %tobool, label %if.end, label %if.then \n" " \n" " if.then: \n" " %call = tail call i32 @foo() \n" " br label %return \n" " \n" " if.end: \n" " %call1 = tail call i32 @bar() \n" " br label %return \n" " \n" " return: \n" " %retval.0 = phi i32 [ %call, %if.then ], [ %call1, %if.end ] \n" " ret i32 %retval.0 \n" " } \n" " \n" " declare i32 @foo() \n" " declare i32 @bar() \n"; LLVMErrorRef parseExampleModule(const char *Source, size_t Len, const char *Name, LLVMOrcThreadSafeModuleRef *TSM) { // Create a new ThreadSafeContext and underlying LLVMContext. LLVMOrcThreadSafeContextRef TSCtx = LLVMOrcCreateNewThreadSafeContext(); // Get a reference to the underlying LLVMContext. LLVMContextRef Ctx = LLVMOrcThreadSafeContextGetContext(TSCtx); // Wrap Source in a MemoryBuffer LLVMMemoryBufferRef MB = LLVMCreateMemoryBufferWithMemoryRange(Source, Len, Name, 0); // Parse the LLVM module. LLVMModuleRef M; char *ErrMsg; if (LLVMParseIRInContext(Ctx, MB, &M, &ErrMsg)) { return LLVMCreateStringError(ErrMsg); // TODO: LLVMDisposeMessage(ErrMsg); } // Our module is now complete. Wrap it and our ThreadSafeContext in a // ThreadSafeModule. *TSM = LLVMOrcCreateNewThreadSafeModule(M, TSCtx); // Dispose of our local ThreadSafeContext value. The underlying LLVMContext // will be kept alive by our ThreadSafeModule, TSM. LLVMOrcDisposeThreadSafeContext(TSCtx); return LLVMErrorSuccess; } int main(int argc, const char *argv[]) { int MainResult = 0; // Parse command line arguments and initialize LLVM Core. LLVMParseCommandLineOptions(argc, argv, ""); // Initialize native target codegen and asm printer. LLVMInitializeNativeTarget(); LLVMInitializeNativeAsmPrinter(); // Set up a JIT instance. LLVMOrcLLJITRef J; const char *TargetTriple; { LLVMErrorRef Err; if ((Err = LLVMOrcCreateLLJIT(&J, 0))) { MainResult = handleError(Err); goto llvm_shutdown; } TargetTriple = LLVMOrcLLJITGetTripleString(J); } // Add our demo modules to the JIT. { LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J); LLVMErrorRef Err; LLVMOrcThreadSafeModuleRef FooTSM; if ((Err = parseExampleModule(FooMod, sizeof(FooMod) - 1, "foo-mod", &FooTSM))) { MainResult = handleError(Err); goto jit_cleanup; } if ((Err = LLVMOrcLLJITAddLLVMIRModule(J, MainJD, FooTSM))) { // If adding the ThreadSafeModule fails then we need to clean it up // ourselves. If adding it succeeds the JIT will manage the memory. LLVMOrcDisposeThreadSafeModule(FooTSM); MainResult = handleError(Err); goto jit_cleanup; } LLVMOrcThreadSafeModuleRef BarTSM; if ((Err = parseExampleModule(BarMod, sizeof(BarMod) - 1, "bar-mod", &BarTSM))) { MainResult = handleError(Err); goto jit_cleanup; } if ((Err = LLVMOrcLLJITAddLLVMIRModule(J, MainJD, BarTSM))) { LLVMOrcDisposeThreadSafeModule(BarTSM); MainResult = handleError(Err); goto jit_cleanup; } LLVMOrcThreadSafeModuleRef MainTSM; if ((Err = parseExampleModule(MainMod, sizeof(MainMod) - 1, "main-mod", &MainTSM))) { MainResult = handleError(Err); goto jit_cleanup; } if ((Err = LLVMOrcLLJITAddLLVMIRModule(J, MainJD, MainTSM))) { LLVMOrcDisposeThreadSafeModule(MainTSM); MainResult = handleError(Err); goto jit_cleanup; } } // add lazy reexports LLVMOrcIndirectStubsManagerRef ISM = LLVMOrcCreateLocalIndirectStubsManager(TargetTriple); LLVMOrcLazyCallThroughManagerRef LCTM; { LLVMErrorRef Err; LLVMOrcExecutionSessionRef ES = LLVMOrcLLJITGetExecutionSession(J); if ((Err = LLVMOrcCreateLocalLazyCallThroughManager(TargetTriple, ES, 0, &LCTM))) { LLVMOrcDisposeIndirectStubsManager(ISM); MainResult = handleError(Err); goto jit_cleanup; } } LLVMJITSymbolFlags flag = { LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable, 0}; LLVMOrcCSymbolAliasMapPair ReExports[2] = { {LLVMOrcLLJITMangleAndIntern(J, "foo"), {LLVMOrcLLJITMangleAndIntern(J, "foo_body"), flag}}, {LLVMOrcLLJITMangleAndIntern(J, "bar"), {LLVMOrcLLJITMangleAndIntern(J, "bar_body"), flag}}, }; { LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J); LLVMOrcMaterializationUnitRef MU = LLVMOrcLazyReexports(LCTM, ISM, MainJD, ReExports, 2); LLVMOrcJITDylibDefine(MainJD, MU); } // Look up the address of our demo entry point. LLVMOrcJITTargetAddress EntryAddr; { LLVMErrorRef Err; if ((Err = LLVMOrcLLJITLookup(J, &EntryAddr, "entry"))) { MainResult = handleError(Err); goto cleanup; } } // If we made it here then everything succeeded. Execute our JIT'd code. int32_t (*Entry)(int32_t) = (int32_t(*)(int32_t))EntryAddr; int32_t Result = Entry(argc); printf("--- Result ---\n"); printf("entry(%i) = %i\n", argc, Result); cleanup : { LLVMOrcDisposeIndirectStubsManager(ISM); LLVMOrcDisposeLazyCallThroughManager(LCTM); } jit_cleanup: // Destroy our JIT instance. This will clean up any memory that the JIT has // taken ownership of. This operation is non-trivial (e.g. it may need to // JIT static destructors) and may also fail. In that case we want to render // the error to stderr, but not overwrite any existing return value. { LLVMErrorRef Err; if ((Err = LLVMOrcDisposeLLJIT(J))) { int NewFailureResult = handleError(Err); if (MainResult == 0) MainResult = NewFailureResult; } } llvm_shutdown: // Shut down LLVM. LLVMShutdown(); return MainResult; }