300 lines · c
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-exception6//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 <assert.h>17#include <stdio.h>18#include <string.h>19 20int handleError(LLVMErrorRef Err) {21 char *ErrMsg = LLVMGetErrorMessage(Err);22 fprintf(stderr, "Error: %s\n", ErrMsg);23 LLVMDisposeErrorMessage(ErrMsg);24 return 1;25}26 27// Example IR modules.28//29// Note that in the conditionally compiled modules, FooMod and BarMod, functions30// have been given an _body suffix. This is to ensure that their names do not31// clash with their lazy-reexports.32// For clients who do not wish to rename function bodies (e.g. because they want33// to re-use cached objects between static and JIT compiles) techniques exist to34// avoid renaming. See the lazy-reexports section of the ORCv2 design doc.35 36const char FooMod[] = " define i32 @foo_body() { \n"37 " entry: \n"38 " ret i32 1 \n"39 " } \n";40 41const char BarMod[] = " define i32 @bar_body() { \n"42 " entry: \n"43 " ret i32 2 \n"44 " } \n";45 46const char MainMod[] =47 " define i32 @entry(i32 %argc) { \n"48 " entry: \n"49 " %and = and i32 %argc, 1 \n"50 " %tobool = icmp eq i32 %and, 0 \n"51 " br i1 %tobool, label %if.end, label %if.then \n"52 " \n"53 " if.then: \n"54 " %call = tail call i32 @foo() \n"55 " br label %return \n"56 " \n"57 " if.end: \n"58 " %call1 = tail call i32 @bar() \n"59 " br label %return \n"60 " \n"61 " return: \n"62 " %retval.0 = phi i32 [ %call, %if.then ], [ %call1, %if.end ] \n"63 " ret i32 %retval.0 \n"64 " } \n"65 " \n"66 " declare i32 @foo() \n"67 " declare i32 @bar() \n";68 69LLVMErrorRef applyDataLayout(void *Ctx, LLVMModuleRef M) {70 LLVMSetDataLayout(M, LLVMOrcLLJITGetDataLayoutStr((LLVMOrcLLJITRef)Ctx));71 return LLVMErrorSuccess;72}73 74LLVMErrorRef parseExampleModule(const char *Source, size_t Len,75 const char *Name,76 LLVMOrcThreadSafeModuleRef *TSM) {77 // Create an LLVMContext.78 LLVMContextRef Ctx = LLVMContextCreate();79 80 // Wrap Source in a MemoryBuffer81 LLVMMemoryBufferRef MB =82 LLVMCreateMemoryBufferWithMemoryRange(Source, Len, Name, 1);83 84 // Parse the LLVM module.85 LLVMModuleRef M;86 char *ErrMsg;87 if (LLVMParseIRInContext(Ctx, MB, &M, &ErrMsg)) {88 LLVMErrorRef Err = LLVMCreateStringError(ErrMsg);89 LLVMDisposeMessage(ErrMsg);90 return Err;91 }92 93 // Create a new ThreadSafeContext to hold the context.94 LLVMOrcThreadSafeContextRef TSCtx =95 LLVMOrcCreateNewThreadSafeContextFromLLVMContext(Ctx);96 97 // Our module is now complete. Wrap it and our ThreadSafeContext in a98 // ThreadSafeModule.99 *TSM = LLVMOrcCreateNewThreadSafeModule(M, TSCtx);100 101 // Dispose of our local ThreadSafeContext value. The underlying LLVMContext102 // will be kept alive by our ThreadSafeModule, TSM.103 LLVMOrcDisposeThreadSafeContext(TSCtx);104 105 return LLVMErrorSuccess;106}107 108void Destroy(void *Ctx) {}109 110void Materialize(void *Ctx, LLVMOrcMaterializationResponsibilityRef MR) {111 int MainResult = 0;112 113 size_t NumSymbols;114 LLVMOrcSymbolStringPoolEntryRef *Symbols =115 LLVMOrcMaterializationResponsibilityGetRequestedSymbols(MR, &NumSymbols);116 117 assert(NumSymbols == 1);118 119 LLVMOrcLLJITRef J = (LLVMOrcLLJITRef)Ctx;120 LLVMOrcSymbolStringPoolEntryRef Sym = Symbols[0];121 122 LLVMOrcThreadSafeModuleRef TSM = 0;123 LLVMErrorRef Err;124 125 LLVMOrcSymbolStringPoolEntryRef FooBody =126 LLVMOrcLLJITMangleAndIntern(J, "foo_body");127 LLVMOrcSymbolStringPoolEntryRef BarBody =128 LLVMOrcLLJITMangleAndIntern(J, "bar_body");129 130 if (Sym == FooBody) {131 if ((Err = parseExampleModule(FooMod, strlen(FooMod), "foo-mod", &TSM))) {132 MainResult = handleError(Err);133 goto cleanup;134 }135 } else if (Sym == BarBody) {136 if ((Err = parseExampleModule(BarMod, strlen(BarMod), "bar-mod", &TSM))) {137 MainResult = handleError(Err);138 goto cleanup;139 }140 } else {141 MainResult = 1;142 goto cleanup;143 }144 assert(TSM);145 146 if ((Err = LLVMOrcThreadSafeModuleWithModuleDo(TSM, &applyDataLayout, Ctx))) {147 MainResult = handleError(Err);148 goto cleanup;149 }150 151cleanup:152 LLVMOrcReleaseSymbolStringPoolEntry(BarBody);153 LLVMOrcReleaseSymbolStringPoolEntry(FooBody);154 LLVMOrcDisposeSymbols(Symbols);155 if (MainResult == 1) {156 LLVMOrcMaterializationResponsibilityFailMaterialization(MR);157 LLVMOrcDisposeMaterializationResponsibility(MR);158 } else {159 LLVMOrcIRTransformLayerRef IRLayer = LLVMOrcLLJITGetIRTransformLayer(J);160 LLVMOrcIRTransformLayerEmit(IRLayer, MR, TSM);161 }162}163 164int main(int argc, const char *argv[]) {165 166 int MainResult = 0;167 168 // Parse command line arguments and initialize LLVM Core.169 LLVMParseCommandLineOptions(argc, argv, "");170 171 // Initialize native target codegen and asm printer.172 LLVMInitializeNativeTarget();173 LLVMInitializeNativeAsmPrinter();174 175 // Set up a JIT instance.176 LLVMOrcLLJITRef J;177 const char *TargetTriple;178 {179 LLVMErrorRef Err;180 if ((Err = LLVMOrcCreateLLJIT(&J, 0))) {181 MainResult = handleError(Err);182 goto llvm_shutdown;183 }184 TargetTriple = LLVMOrcLLJITGetTripleString(J);185 }186 187 // Add our main module to the JIT.188 {189 LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J);190 LLVMErrorRef Err;191 192 LLVMOrcThreadSafeModuleRef MainTSM;193 if ((Err = parseExampleModule(MainMod, strlen(MainMod), "main-mod",194 &MainTSM))) {195 MainResult = handleError(Err);196 goto jit_cleanup;197 }198 199 if ((Err = LLVMOrcLLJITAddLLVMIRModule(J, MainJD, MainTSM))) {200 LLVMOrcDisposeThreadSafeModule(MainTSM);201 MainResult = handleError(Err);202 goto jit_cleanup;203 }204 }205 206 LLVMJITSymbolFlags Flags = {207 LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable, 0};208 LLVMOrcCSymbolFlagsMapPair FooSym = {209 LLVMOrcLLJITMangleAndIntern(J, "foo_body"), Flags};210 LLVMOrcCSymbolFlagsMapPair BarSym = {211 LLVMOrcLLJITMangleAndIntern(J, "bar_body"), Flags};212 213 // add custom MaterializationUnit214 {215 LLVMOrcMaterializationUnitRef FooMU =216 LLVMOrcCreateCustomMaterializationUnit("FooMU", J, &FooSym, 1, NULL,217 &Materialize, NULL, &Destroy);218 219 LLVMOrcMaterializationUnitRef BarMU =220 LLVMOrcCreateCustomMaterializationUnit("BarMU", J, &BarSym, 1, NULL,221 &Materialize, NULL, &Destroy);222 223 LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J);224 LLVMOrcJITDylibDefine(MainJD, FooMU);225 LLVMOrcJITDylibDefine(MainJD, BarMU);226 }227 228 // add lazy reexports229 LLVMOrcIndirectStubsManagerRef ISM =230 LLVMOrcCreateLocalIndirectStubsManager(TargetTriple);231 232 LLVMOrcLazyCallThroughManagerRef LCTM;233 {234 LLVMErrorRef Err;235 LLVMOrcExecutionSessionRef ES = LLVMOrcLLJITGetExecutionSession(J);236 if ((Err = LLVMOrcCreateLocalLazyCallThroughManager(TargetTriple, ES, 0,237 &LCTM))) {238 LLVMOrcDisposeIndirectStubsManager(ISM);239 MainResult = handleError(Err);240 goto jit_cleanup;241 }242 }243 244 LLVMOrcCSymbolAliasMapPair ReExports[2] = {245 {LLVMOrcLLJITMangleAndIntern(J, "foo"),246 {LLVMOrcLLJITMangleAndIntern(J, "foo_body"), Flags}},247 {LLVMOrcLLJITMangleAndIntern(J, "bar"),248 {LLVMOrcLLJITMangleAndIntern(J, "bar_body"), Flags}},249 };250 251 {252 LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J);253 LLVMOrcMaterializationUnitRef MU =254 LLVMOrcLazyReexports(LCTM, ISM, MainJD, ReExports, 2);255 LLVMOrcJITDylibDefine(MainJD, MU);256 }257 258 // Look up the address of our demo entry point.259 LLVMOrcJITTargetAddress EntryAddr;260 {261 LLVMErrorRef Err;262 if ((Err = LLVMOrcLLJITLookup(J, &EntryAddr, "entry"))) {263 MainResult = handleError(Err);264 goto cleanup;265 }266 }267 268 // If we made it here then everything succeeded. Execute our JIT'd code.269 int32_t (*Entry)(int32_t) = (int32_t(*)(int32_t))EntryAddr;270 int32_t Result = Entry(argc);271 272 printf("--- Result ---\n");273 printf("entry(%i) = %i\n", argc, Result);274 275cleanup : {276 LLVMOrcDisposeIndirectStubsManager(ISM);277 LLVMOrcDisposeLazyCallThroughManager(LCTM);278}279 280jit_cleanup:281 // Destroy our JIT instance. This will clean up any memory that the JIT has282 // taken ownership of. This operation is non-trivial (e.g. it may need to283 // JIT static destructors) and may also fail. In that case we want to render284 // the error to stderr, but not overwrite any existing return value.285 {286 LLVMErrorRef Err;287 if ((Err = LLVMOrcDisposeLLJIT(J))) {288 int NewFailureResult = handleError(Err);289 if (MainResult == 0)290 MainResult = NewFailureResult;291 }292 }293 294llvm_shutdown:295 // Shut down LLVM.296 LLVMShutdown();297 298 return MainResult;299}300