diff --git a/llvm/examples/OrcV2Examples/CMakeLists.txt b/llvm/examples/OrcV2Examples/CMakeLists.txt --- a/llvm/examples/OrcV2Examples/CMakeLists.txt +++ b/llvm/examples/OrcV2Examples/CMakeLists.txt @@ -15,6 +15,7 @@ add_subdirectory(OrcV2CBindingsReflectProcessSymbols) add_subdirectory(OrcV2CBindingsRemovableCode) add_subdirectory(OrcV2CBindingsLazy) +add_subdirectory(OrcV2CBindingsVeryLazy) if(CMAKE_HOST_UNIX) add_subdirectory(LLJITWithRemoteDebugging) diff --git a/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/CMakeLists.txt b/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/CMakeLists.txt @@ -0,0 +1,15 @@ +set(LLVM_LINK_COMPONENTS + Core + ExecutionEngine + IRReader + JITLink + MC + OrcJIT + Support + Target + nativecodegen + ) + +add_llvm_example(OrcV2CBindingsVeryLazy + OrcV2CBindingsVeryLazy.c + ) diff --git a/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c b/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c new file mode 100644 --- /dev/null +++ b/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c @@ -0,0 +1,274 @@ +//===-------- 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/IRReader.h" +#include "llvm-c/Error.h" +#include "llvm-c/Initialization.h" +#include "llvm-c/LLJIT.h" +#include "llvm-c/Support.h" +#include "llvm-c/Target.h" + +#include +#include +#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; +} + +void Materialize(void *Ctx, LLVMOrcMaterializationResponsibilityRef MR) { + int MainResult = 0; + + // Question@lhames: Better way to get LLVMOrcLLJITRef J; + LLVMOrcLLJITRef J = (LLVMOrcLLJITRef)Ctx; + + LLVMOrcJITDylibRef JD = + LLVMOrcMaterializationResponsibilityGetTargetDylib(MR); + + size_t NumSymbols; + LLVMOrcSymbolStringPoolEntryRef *Symbols = + LLVMOrcMaterializationResponsibilityGetRequestedSymbols(MR, &NumSymbols); + + for (size_t I = 0; I != NumSymbols; ++I) { + LLVMOrcSymbolStringPoolEntryRef Sym = Symbols[I]; + const char *CSym = LLVMOrcSymbolStringPoolEntryStr(Sym); + + LLVMOrcThreadSafeModuleRef TSM; + LLVMErrorRef Err; + if (strcmp(CSym, "foo_body")) { + if ((Err = parseExampleModule(FooMod, sizeof(FooMod), "foo-mod", &TSM))) { + MainResult = handleError(Err); + goto cleanup; + } + } else if (strcmp(CSym, "bar_body")) { + if ((Err = parseExampleModule(BarMod, sizeof(BarMod), "bar-mod", &TSM))) { + MainResult = handleError(Err); + goto cleanup; + } + } else { + // LLVMOrcMaterializationResponsibilityDefineNonExistent(MR, &Sym, 1); + MainResult = 1; + goto cleanup; + // continue; + } + assert(TSM); + + LLVMOrcMaterializationResponsibilityRef Delegate = + LLVMOrcMaterializationResponsibilityDelegate(MR, &Sym, 1); + LLVMOrcIRTransformLayerRef IRLayer = LLVMOrcLLJITGetIRTransformLayer(J); + LLVMOrcIRTransformLayerEmit(IRLayer, Delegate, TSM); + } + +cleanup: + LLVMOrcDisposeSymbols(Symbols); + if (MainResult == 1) { + LLVMOrcMaterializationResponsibilityFailMaterialization(MR); + } + return; +} + +int main(int argc, char *argv[]) { + + int MainResult = 0; + + // Parse command line arguments and initialize LLVM Core. + LLVMParseCommandLineOptions(argc, (const char **)argv, ""); + LLVMInitializeCore(LLVMGetGlobalPassRegistry()); + + // 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 main module to the JIT. + { + LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J); + LLVMErrorRef Err; + + LLVMOrcThreadSafeModuleRef MainTSM; + if ((Err = parseExampleModule(MainMod, sizeof(MainMod), "main-mod", &MainTSM))) { + MainResult = handleError(Err); + goto jit_cleanup; + } + + if ((Err = LLVMOrcLLJITAddLLVMIRModule(J, MainJD, MainTSM))) { + LLVMOrcDisposeThreadSafeModule(MainTSM); + MainResult = handleError(Err); + goto jit_cleanup; + } + } + + LLVMJITSymbolFlags Flags = {LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable, 0}; + LLVMOrcCSymbolFlagsMapPair Syms[2] = { + { LLVMOrcLLJITMangleAndIntern(J, "foo_body"), Flags}, + { LLVMOrcLLJITMangleAndIntern(J, "bar_body"), Flags} + }; + + // add custom MaterializationUnit + { + LLVMOrcMaterializationUnitRef CustomMU = + LLVMOrcCreateCustomMaterializationUnit("CustomMU", J, Syms, 2, NULL, + *Materialize, NULL, NULL); + + LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J); + LLVMOrcJITDylibDefine(MainJD, CustomMU); + } + + // add lazy reexports + LLVMOrcIndirectStubsManagerRef ISM = + LLVMOrcCreateLocalIndirectStubsManager(TargetTriple); + + LLVMOrcLazyCallThroughManagerRef LCTM; + { + LLVMOrcExecutionSessionRef ES = LLVMOrcLLJITGetExecutionSession(J); + LCTM = LLVMOrcCreateLocalLazyCallThroughManager(TargetTriple, ES, 0); + } + + LLVMOrcCSymbolAliasMapPair ReExports[2] = { + { LLVMOrcLLJITMangleAndIntern(J, "foo"), + { LLVMOrcLLJITMangleAndIntern(J, "foo_body"), Flags} + }, + { LLVMOrcLLJITMangleAndIntern(J, "bar"), + { LLVMOrcLLJITMangleAndIntern(J, "bar_body"), Flags} + }, + }; + + { + 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 jit_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); + +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. + { + // TODO: Dispose ISM&LCM + LLVMErrorRef Err; + if ((Err = LLVMOrcDisposeLLJIT(J))) { + int NewFailureResult = handleError(Err); + if (MainResult == 0) + MainResult = NewFailureResult; + } + } + +llvm_shutdown: + // Shut down LLVM. + LLVMShutdown(); + + return MainResult; +} diff --git a/llvm/include/llvm-c/Orc.h b/llvm/include/llvm-c/Orc.h --- a/llvm/include/llvm-c/Orc.h +++ b/llvm/include/llvm-c/Orc.h @@ -573,6 +573,58 @@ size_t NumPairs); // TODO: ImplSymbolMad SrcJDLoc +LLVMOrcJITDylibRef LLVMOrcMaterializationResponsibilityGetTargetDylib( + LLVMOrcMaterializationResponsibilityRef MR); + +// TODO(vchuravy) Test +LLVMOrcExecutionSessionRef +LLVMOrcMaterializationResponsibilityGetExecutionSession( + LLVMOrcMaterializationResponsibilityRef MR); + +// TODO(vchuravy) Test +LLVMOrcSymbolStringPoolEntryRef +LLVMOrcMaterializationResponsibilityGetInitializerSymbol( + LLVMOrcMaterializationResponsibilityRef MR); + +LLVMOrcSymbolStringPoolEntryRef *LLVMOrcMaterializationResponsibilityGetRequestedSymbols( + LLVMOrcMaterializationResponsibilityRef MR, size_t *NumSymbols); + +void LLVMOrcDisposeSymbols(LLVMOrcSymbolStringPoolEntryRef *Symbols); + +LLVMErrorRef +LLVMOrcMaterializationResponsibilityNotifyResolved( + LLVMOrcMaterializationResponsibilityRef MR, LLVMOrcCSymbolMapPairs Symbols, + size_t NumPairs); + +LLVMErrorRef +LLVMOrcMaterializationResponsibilityNotifyEmitted( + LLVMOrcMaterializationResponsibilityRef MR); + +// TODO(vchuravy) Test +LLVMErrorRef +LLVMOrcMaterializationResponsibilityDefineMaterializing( + LLVMOrcMaterializationResponsibilityRef MR, LLVMOrcCSymbolMapPairs Syms, + size_t NumPairs); + +void LLVMOrcMaterializationResponsibilityDefineNonExistent( + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcSymbolStringPoolEntryRef *Symbols, size_t NumSymbols); + +void LLVMOrcMaterializationResponsibilityFailMaterialization( + LLVMOrcMaterializationResponsibilityRef MR); + +// TODO +// MaterializationResponsibility::replace + +LLVMOrcMaterializationResponsibilityRef +LLVMOrcMaterializationResponsibilityDelegate( + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcSymbolStringPoolEntryRef *Symbols, size_t NumSymbols); + +// MaterializationResponsibility::delegate +// MaterializationResponsibility::addDependencies +// MaterializationResponsibility::addDependenciesForAll + /** * Create a "bare" JITDylib. * @@ -821,6 +873,10 @@ */ void LLVMOrcDisposeObjectLayer(LLVMOrcObjectLayerRef ObjLayer); +void LLVMOrcIRTransformLayerEmit(LLVMOrcIRTransformLayerRef IRTransformLayer, + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcThreadSafeModuleRef TSM); + /** * Set the transform function of the provided transform layer, passing through a * pointer to user provided context. diff --git a/llvm/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp b/llvm/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp --- a/llvm/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp +++ b/llvm/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp @@ -240,6 +240,16 @@ return JSF; } + static SymbolMap toSymbolMap(LLVMOrcCSymbolMapPairs Syms, size_t NumPairs) { + SymbolMap SM; + for (size_t I = 0; I != NumPairs; ++I) { + JITSymbolFlags Flags = toJITSymbolFlags(Syms[I].Sym.Flags); + SM[OrcV2CAPIHelper::moveToSymbolStringPtr(unwrap(Syms[I].Name))] = + JITEvaluatedSymbol(Syms[I].Sym.Address, Flags); + } + return SM; + } + } // end anonymous namespace void LLVMOrcExecutionSessionSetErrorReporter( @@ -334,13 +344,7 @@ LLVMOrcMaterializationUnitRef LLVMOrcAbsoluteSymbols(LLVMOrcCSymbolMapPairs Syms, size_t NumPairs) { - SymbolMap SM; - for (size_t I = 0; I != NumPairs; ++I) { - JITSymbolFlags Flags = toJITSymbolFlags(Syms[I].Sym.Flags); - SM[OrcV2CAPIHelper::moveToSymbolStringPtr(unwrap(Syms[I].Name))] = - JITEvaluatedSymbol(Syms[I].Sym.Address, Flags); - } - + SymbolMap SM = toSymbolMap(Syms, NumPairs); return wrap(absoluteSymbols(std::move(SM)).release()); } @@ -366,6 +370,108 @@ *unwrap(LCTM), *unwrap(ISM), *unwrap(SourceJD), std::move(SAM)).release()); } +LLVMOrcJITDylibRef LLVMOrcMaterializationResponsibilityGetTargetDylib( + LLVMOrcMaterializationResponsibilityRef MR) { + return wrap(&unwrap(MR)->getTargetJITDylib()); +} + +LLVMOrcExecutionSessionRef +LLVMOrcMaterializationResponsibilityGetExecutionSession( + LLVMOrcMaterializationResponsibilityRef MR) { + return wrap(&unwrap(MR)->getExecutionSession()); +} + +LLVMOrcSymbolStringPoolEntryRef +LLVMOrcMaterializationResponsibilityGetInitializerSymbol( + LLVMOrcMaterializationResponsibilityRef MR) { + auto Sym = unwrap(MR)->getInitializerSymbol(); + return wrap(OrcV2CAPIHelper::getRawPoolEntryPtr(Sym)); +} + +LLVMOrcSymbolStringPoolEntryRef *LLVMOrcMaterializationResponsibilityGetRequestedSymbols( + LLVMOrcMaterializationResponsibilityRef MR, size_t *NumSymbols) { + + auto Symbols = unwrap(MR)->getRequestedSymbols(); + LLVMOrcSymbolStringPoolEntryRef *Result = + static_cast( + safe_malloc(Symbols.size() * sizeof(LLVMOrcSymbolStringPoolEntryRef))); + size_t I = 0; + for (auto &Name : Symbols) { + Result[I] = wrap(OrcV2CAPIHelper::getRawPoolEntryPtr(Name)); + I++; + } + *NumSymbols = Symbols.size(); + return Result; +} + +void LLVMOrcDisposeSymbols(LLVMOrcSymbolStringPoolEntryRef *Symbols) { + free(Symbols); +} + +LLVMErrorRef +LLVMOrcMaterializationResponsibilityNotifyResolved( + LLVMOrcMaterializationResponsibilityRef MR, LLVMOrcCSymbolMapPairs Symbols, + size_t NumPairs) { + SymbolMap SM = toSymbolMap(Symbols, NumPairs); + return wrap(unwrap(MR)->notifyResolved(std::move(SM))); +} + +LLVMErrorRef +LLVMOrcMaterializationResponsibilityNotifyEmitted( + LLVMOrcMaterializationResponsibilityRef MR) { + return wrap(unwrap(MR)->notifyEmitted()); +} + +// TODO(vchuravy) Test +LLVMErrorRef +LLVMOrcMaterializationResponsibilityDefineMaterializing( + LLVMOrcMaterializationResponsibilityRef MR, LLVMOrcCSymbolFlagsMapPairs Syms, + size_t NumSyms) { + SymbolFlagsMap SFM; + for (size_t I = 0; I != NumSyms; ++I) + SFM[OrcV2CAPIHelper::moveToSymbolStringPtr(unwrap(Syms[I].Name))] = + toJITSymbolFlags(Syms[I].Flags); + + return wrap(unwrap(MR)->defineMaterializing(std::move(SFM))); +} + +LLVMOrcMaterializationResponsibilityRef +LLVMOrcMaterializationResponsibilityDelegate( + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcSymbolStringPoolEntryRef *Symbols, size_t NumSymbols) { + SymbolNameSet Syms; + for (size_t I = 0; I != NumSymbols; I++) { + Syms.insert(OrcV2CAPIHelper::moveToSymbolStringPtr(unwrap(Symbols[I]))); + } + return wrap(unwrap(MR)->delegate(Syms)->release()); +} + +// TODO(@lhames) MaterializationResponsibility::defineNonExistent is nonExistent +// void LLVMOrcMaterializationResponsibilityDefineNonExistent( +// LLVMOrcMaterializationResponsibilityRef MR, +// LLVMOrcSymbolStringPoolEntryRef *Symbols, size_t NumSymbols) { +// SmallVector Syms; +// for (size_t I = 0; I != NumSymbols; I++) { +// Syms.push_back(OrcV2CAPIHelper::moveToSymbolStringPtr(unwrap(Symbols[I]))); +// } +// unwrap(MR)->defineNonExistent(Syms); +// } + +void LLVMOrcMaterializationResponsibilityFailMaterialization( + LLVMOrcMaterializationResponsibilityRef MR) { + unwrap(MR)->failMaterialization(); +} + +void LLVMOrcIRTransformLayerEmit( + LLVMOrcIRTransformLayerRef IRLayer, + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcThreadSafeModuleRef TSM) { + std::unique_ptr TmpTSM(unwrap(TSM)); + unwrap(IRLayer)->emit( + std::unique_ptr(unwrap(MR)), + std::move(*TmpTSM)); +} + LLVMOrcJITDylibRef LLVMOrcExecutionSessionCreateBareJITDylib(LLVMOrcExecutionSessionRef ES, const char *Name) { diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt --- a/llvm/test/CMakeLists.txt +++ b/llvm/test/CMakeLists.txt @@ -165,6 +165,7 @@ OrcV2CBindingsRemovableCode OrcV2CBindingsReflectProcessSymbols OrcV2CBindingsLazy + OrcV2CBindingsVeryLazy ) if(CMAKE_HOST_UNIX) list(APPEND LLVM_TEST_DEPENDS diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -185,7 +185,8 @@ ToolSubst('OrcV2CBindingsAddObjectFile', unresolved='ignore'), ToolSubst('OrcV2CBindingsRemovableCode', unresolved='ignore'), ToolSubst('OrcV2CBindingsReflectProcessSymbols', unresolved='ignore'), - ToolSubst('OrcV2CBindingsLazy', unresolved='ignore')]) + ToolSubst('OrcV2CBindingsLazy', unresolved='ignore'), + ToolSubst('OrcV2CBindingsVeryLazy', unresolved='ignore')]) llvm_config.add_tool_substitutions(tools, config.llvm_tools_dir)