From a01ce5e73a2b1edd703fdbf012de7c9964174f29 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Sun, 18 Jul 2021 16:22:34 +0200 Subject: [PATCH] Reland [Orc] Add verylazy example for C-bindings This patch relands https://reviews.llvm.org/D104799, but fixes the memory handling causing leak sanitizer failures. This reverts commit a56fe117e04f7d4b953a4226af412dad59425fb5. --- examples/OrcV2Examples/CMakeLists.txt | 1 + .../OrcV2CBindingsVeryLazy/CMakeLists.txt | 15 + .../OrcV2CBindingsVeryLazy.c | 301 ++++++++++++++++++ include/llvm-c/LLJIT.h | 8 + include/llvm-c/Orc.h | 230 ++++++++++++- lib/ExecutionEngine/Orc/OrcV2CBindings.cpp | 198 +++++++++++- test/CMakeLists.txt | 1 + test/lit.cfg.py | 3 +- unittests/ExecutionEngine/Orc/OrcCAPITest.cpp | 162 ++++++++++ 9 files changed, 906 insertions(+), 13 deletions(-) create mode 100644 examples/OrcV2Examples/OrcV2CBindingsVeryLazy/CMakeLists.txt create mode 100644 examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c diff --git a/examples/OrcV2Examples/CMakeLists.txt b/examples/OrcV2Examples/CMakeLists.txt index 59311f8fbf1..d6fa7423df1 100644 --- a/examples/OrcV2Examples/CMakeLists.txt +++ b/examples/OrcV2Examples/CMakeLists.txt @@ -15,6 +15,7 @@ add_subdirectory(OrcV2CBindingsIRTransforms) add_subdirectory(OrcV2CBindingsReflectProcessSymbols) add_subdirectory(OrcV2CBindingsRemovableCode) add_subdirectory(OrcV2CBindingsLazy) +add_subdirectory(OrcV2CBindingsVeryLazy) if(CMAKE_HOST_UNIX) add_subdirectory(LLJITWithRemoteDebugging) diff --git a/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/CMakeLists.txt b/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/CMakeLists.txt new file mode 100644 index 00000000000..0bc9610b1ad --- /dev/null +++ b/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/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c b/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c new file mode 100644 index 00000000000..93d36a40b12 --- /dev/null +++ b/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c @@ -0,0 +1,301 @@ +//===-------- 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/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 applyDataLayout(void *Ctx, LLVMModuleRef M) { + LLVMSetDataLayout(M, LLVMOrcLLJITGetDataLayoutStr((LLVMOrcLLJITRef)Ctx)); + return LLVMErrorSuccess; +} + +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, 1); + + // Parse the LLVM module. + LLVMModuleRef M; + char *ErrMsg; + if (LLVMParseIRInContext(Ctx, MB, &M, &ErrMsg)) { + LLVMErrorRef Err = LLVMCreateStringError(ErrMsg); + LLVMDisposeMessage(ErrMsg); + return Err; + } + + // 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 Destroy(void *Ctx) {} + +void Materialize(void *Ctx, LLVMOrcMaterializationResponsibilityRef MR) { + int MainResult = 0; + + size_t NumSymbols; + LLVMOrcSymbolStringPoolEntryRef *Symbols = + LLVMOrcMaterializationResponsibilityGetRequestedSymbols(MR, &NumSymbols); + + assert(NumSymbols == 1); + + LLVMOrcLLJITRef J = (LLVMOrcLLJITRef)Ctx; + LLVMOrcSymbolStringPoolEntryRef Sym = Symbols[0]; + + LLVMOrcThreadSafeModuleRef TSM = 0; + LLVMErrorRef Err; + + LLVMOrcSymbolStringPoolEntryRef FooBody = + LLVMOrcLLJITMangleAndIntern(J, "foo_body"); + LLVMOrcSymbolStringPoolEntryRef BarBody = + LLVMOrcLLJITMangleAndIntern(J, "bar_body"); + + if (Sym == FooBody) { + if ((Err = parseExampleModule(FooMod, strlen(FooMod), "foo-mod", &TSM))) { + MainResult = handleError(Err); + goto cleanup; + } + } else if (Sym == BarBody) { + if ((Err = parseExampleModule(BarMod, strlen(BarMod), "bar-mod", &TSM))) { + MainResult = handleError(Err); + goto cleanup; + } + } else { + MainResult = 1; + goto cleanup; + } + assert(TSM); + + if ((Err = LLVMOrcThreadSafeModuleWithModuleDo(TSM, &applyDataLayout, Ctx))) { + MainResult = handleError(Err); + goto cleanup; + } + +cleanup: + LLVMOrcReleaseSymbolStringPoolEntry(BarBody); + LLVMOrcReleaseSymbolStringPoolEntry(FooBody); + LLVMOrcDisposeSymbols(Symbols); + if (MainResult == 1) { + LLVMOrcMaterializationResponsibilityFailMaterialization(MR); + LLVMOrcDisposeMaterializationResponsibility(MR); + } else { + LLVMOrcIRTransformLayerRef IRLayer = LLVMOrcLLJITGetIRTransformLayer(J); + LLVMOrcIRTransformLayerEmit(IRLayer, MR, TSM); + } + 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, strlen(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 FooSym = { + LLVMOrcLLJITMangleAndIntern(J, "foo_body"), Flags}; + LLVMOrcCSymbolFlagsMapPair BarSym = { + LLVMOrcLLJITMangleAndIntern(J, "bar_body"), Flags}; + + // add custom MaterializationUnit + { + LLVMOrcMaterializationUnitRef FooMU = + LLVMOrcCreateCustomMaterializationUnit("FooMU", J, &FooSym, 1, NULL, + &Materialize, NULL, &Destroy); + + LLVMOrcMaterializationUnitRef BarMU = + LLVMOrcCreateCustomMaterializationUnit("BarMU", J, &BarSym, 1, NULL, + &Materialize, NULL, &Destroy); + + LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J); + LLVMOrcJITDylibDefine(MainJD, FooMU); + LLVMOrcJITDylibDefine(MainJD, BarMU); + } + + // 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; + } + } + + 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 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; +} diff --git a/include/llvm-c/LLJIT.h b/include/llvm-c/LLJIT.h index bd98cfbab83..f689ca0f1cf 100644 --- a/include/llvm-c/LLJIT.h +++ b/include/llvm-c/LLJIT.h @@ -227,6 +227,14 @@ LLVMOrcLLJITGetObjTransformLayer(LLVMOrcLLJITRef J); */ LLVMOrcIRTransformLayerRef LLVMOrcLLJITGetIRTransformLayer(LLVMOrcLLJITRef J); +/** + * Get the LLJIT instance's default data layout string. + * + * This string is owned by the LLJIT instance and does not need to be freed + * by the caller. + */ +const char *LLVMOrcLLJITGetDataLayoutStr(LLVMOrcLLJITRef J); + LLVM_C_EXTERN_C_END #endif /* LLVM_C_LLJIT_H */ diff --git a/include/llvm-c/Orc.h b/include/llvm-c/Orc.h index 62688d2675d..1790afbcecc 100644 --- a/include/llvm-c/Orc.h +++ b/include/llvm-c/Orc.h @@ -145,6 +145,34 @@ typedef struct { */ typedef LLVMOrcCSymbolAliasMapPair *LLVMOrcCSymbolAliasMapPairs; +/** + * A reference to an orc::JITDylib instance. + */ +typedef struct LLVMOrcOpaqueJITDylib *LLVMOrcJITDylibRef; + +/** + * Represents a list of LLVMOrcSymbolStringPoolEntryRef and the associated + * length. + */ +typedef struct { + LLVMOrcSymbolStringPoolEntryRef *Symbols; + size_t Length; +} LLVMOrcCSymbolsList; + +/** + * Represents a pair of a JITDylib and LLVMOrcCSymbolsList. + */ +typedef struct { + LLVMOrcJITDylibRef JD; + LLVMOrcCSymbolsList Names; +} LLVMOrcCDependenceMapPair; + +/** + * Represents a list of (JITDylibRef, (LLVMOrcSymbolStringPoolEntryRef*, + * size_t)) pairs that can be used to construct a SymbolDependenceMap. + */ +typedef LLVMOrcCDependenceMapPair *LLVMOrcCDependenceMapPairs; + /** * Lookup kind. This can be used by definition generators when deciding whether * to produce a definition for a requested symbol. @@ -210,11 +238,6 @@ typedef struct LLVMOrcOpaqueMaterializationUnit *LLVMOrcMaterializationUnitRef; typedef struct LLVMOrcOpaqueMaterializationResponsibility *LLVMOrcMaterializationResponsibilityRef; -/** - * A reference to an orc::JITDylib instance. - */ -typedef struct LLVMOrcOpaqueJITDylib *LLVMOrcJITDylibRef; - /** * A MaterializationUnit materialize callback. * @@ -597,6 +620,199 @@ LLVMOrcMaterializationUnitRef LLVMOrcLazyReexports( size_t NumPairs); // TODO: ImplSymbolMad SrcJDLoc +/** + * Disposes of the passed MaterializationResponsibility object. + * + * This should only be done after the symbols covered by the object have either + * been resolved and emitted (via + * LLVMOrcMaterializationResponsibilityNotifyResolved and + * LLVMOrcMaterializationResponsibilityNotifyEmitted) or failed (via + * LLVMOrcMaterializationResponsibilityFailMaterialization). + */ +void LLVMOrcDisposeMaterializationResponsibility( + LLVMOrcMaterializationResponsibilityRef MR); + +/** + * Returns the target JITDylib that these symbols are being materialized into. + */ +LLVMOrcJITDylibRef LLVMOrcMaterializationResponsibilityGetTargetDylib( + LLVMOrcMaterializationResponsibilityRef MR); + +/** + * Returns the ExecutionSession for this MaterializationResponsibility. + */ +LLVMOrcExecutionSessionRef +LLVMOrcMaterializationResponsibilityGetExecutionSession( + LLVMOrcMaterializationResponsibilityRef MR); + +/** + * Returns the symbol flags map for this responsibility instance. + * + * The length of the array is returned in NumPairs and the caller is responsible + * for the returned memory and needs to call LLVMOrcDisposeCSymbolFlagsMap. + * + * To use the returned symbols beyond the livetime of the + * MaterializationResponsibility requires the caller to retain the symbols + * explicitly. + */ +LLVMOrcCSymbolFlagsMapPairs LLVMOrcMaterializationResponsibilityGetSymbols( + LLVMOrcMaterializationResponsibilityRef MR, size_t *NumPairs); + +/** + * Disposes of the passed LLVMOrcCSymbolFlagsMap. + * + * Does not release the entries themselves. + */ +void LLVMOrcDisposeCSymbolFlagsMap(LLVMOrcCSymbolFlagsMapPairs Pairs); + +/** + * Returns the initialization pseudo-symbol, if any. This symbol will also + * be present in the SymbolFlagsMap for this MaterializationResponsibility + * object. + * + * The returned symbol is not retained over any mutating operation of the + * MaterializationResponsbility or beyond the lifetime thereof. + */ +LLVMOrcSymbolStringPoolEntryRef +LLVMOrcMaterializationResponsibilityGetInitializerSymbol( + LLVMOrcMaterializationResponsibilityRef MR); + +/** + * Returns the names of any symbols covered by this + * MaterializationResponsibility object that have queries pending. This + * information can be used to return responsibility for unrequested symbols + * back to the JITDylib via the delegate method. + */ +LLVMOrcSymbolStringPoolEntryRef * +LLVMOrcMaterializationResponsibilityGetRequestedSymbols( + LLVMOrcMaterializationResponsibilityRef MR, size_t *NumSymbols); + +/** + * Disposes of the passed LLVMOrcSymbolStringPoolEntryRef* . + * + * Does not release the symbols themselves. + */ +void LLVMOrcDisposeSymbols(LLVMOrcSymbolStringPoolEntryRef *Symbols); + +/* + * Notifies the target JITDylib that the given symbols have been resolved. + * This will update the given symbols' addresses in the JITDylib, and notify + * any pending queries on the given symbols of their resolution. The given + * symbols must be ones covered by this MaterializationResponsibility + * instance. Individual calls to this method may resolve a subset of the + * symbols, but all symbols must have been resolved prior to calling emit. + * + * This method will return an error if any symbols being resolved have been + * moved to the error state due to the failure of a dependency. If this + * method returns an error then clients should log it and call + * LLVMOrcMaterializationResponsibilityFailMaterialization. If no dependencies + * have been registered for the symbols covered by this + * MaterializationResponsibiility then this method is guaranteed to return + * LLVMErrorSuccess. + */ +LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyResolved( + LLVMOrcMaterializationResponsibilityRef MR, LLVMOrcCSymbolMapPairs Symbols, + size_t NumPairs); + +/** + * Notifies the target JITDylib (and any pending queries on that JITDylib) + * that all symbols covered by this MaterializationResponsibility instance + * have been emitted. + * + * This method will return an error if any symbols being resolved have been + * moved to the error state due to the failure of a dependency. If this + * method returns an error then clients should log it and call + * LLVMOrcMaterializationResponsibilityFailMaterialization. + * If no dependencies have been registered for the symbols covered by this + * MaterializationResponsibiility then this method is guaranteed to return + * LLVMErrorSuccess. + */ +LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyEmitted( + LLVMOrcMaterializationResponsibilityRef MR); + +/** + * Attempt to claim responsibility for new definitions. This method can be + * used to claim responsibility for symbols that are added to a + * materialization unit during the compilation process (e.g. literal pool + * symbols). Symbol linkage rules are the same as for symbols that are + * defined up front: duplicate strong definitions will result in errors. + * Duplicate weak definitions will be discarded (in which case they will + * not be added to this responsibility instance). + * + * This method can be used by materialization units that want to add + * additional symbols at materialization time (e.g. stubs, compile + * callbacks, metadata) + */ +LLVMErrorRef LLVMOrcMaterializationResponsibilityDefineMaterializing( + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcCSymbolFlagsMapPairs Pairs, size_t NumPairs); + +/** + * Notify all not-yet-emitted covered by this MaterializationResponsibility + * instance that an error has occurred. + * This will remove all symbols covered by this MaterializationResponsibilty + * from the target JITDylib, and send an error to any queries waiting on + * these symbols. + */ +void LLVMOrcMaterializationResponsibilityFailMaterialization( + LLVMOrcMaterializationResponsibilityRef MR); + +/** + * Transfers responsibility to the given MaterializationUnit for all + * symbols defined by that MaterializationUnit. This allows + * materializers to break up work based on run-time information (e.g. + * by introspecting which symbols have actually been looked up and + * materializing only those). + */ +LLVMErrorRef LLVMOrcMaterializationResponsibilityReplace( + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcMaterializationUnitRef MU); + +/** + * Delegates responsibility for the given symbols to the returned + * materialization responsibility. Useful for breaking up work between + * threads, or different kinds of materialization processes. + * + * The caller retains responsibility of the the passed + * MaterializationResponsibility. + */ +LLVMErrorRef LLVMOrcMaterializationResponsibilityDelegate( + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcSymbolStringPoolEntryRef *Symbols, size_t NumSymbols, + LLVMOrcMaterializationResponsibilityRef *Result); + +/** + * Adds dependencies to a symbol that the MaterializationResponsibility is + * responsible for. + * + * This function takes ownership of Dependencies struct. The Names + * array have been retained for this function. This allows the following + * pattern... + * + * LLVMOrcSymbolStringPoolEntryRef Names[] = {...}; + * LLVMOrcCDependenceMapPair Dependence = {JD, {Names, sizeof(Names)}} + * LLVMOrcMaterializationResponsibilityAddDependencies(JD, Name, &Dependence, + * 1); + * + * ... without requiring cleanup of the elements of the Names array afterwards. + * + * The client is still responsible for deleting the Dependencies.Names array + * itself. + */ +void LLVMOrcMaterializationResponsibilityAddDependencies( + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcSymbolStringPoolEntryRef Name, + LLVMOrcCDependenceMapPairs Dependencies, size_t NumPairs); + +/** + * Adds dependencies to all symbols that the MaterializationResponsibility is + * responsible for. See LLVMOrcMaterializationResponsibilityAddDependencies for + * notes about memory responsibility. + */ +void LLVMOrcMaterializationResponsibilityAddDependenciesForAll( + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcCDependenceMapPairs Dependencies, size_t NumPairs); + /** * Create a "bare" JITDylib. * @@ -845,6 +1061,10 @@ void LLVMOrcObjectLayerEmit(LLVMOrcObjectLayerRef ObjLayer, */ 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/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp b/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp index 3068a41b932..e6fdd065b99 100644 --- a/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp +++ b/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp @@ -240,6 +240,48 @@ static JITSymbolFlags toJITSymbolFlags(LLVMJITSymbolFlags F) { return JSF; } +static LLVMJITSymbolFlags fromJITSymbolFlags(JITSymbolFlags JSF) { + LLVMJITSymbolFlags F = {0, 0}; + if (JSF & JITSymbolFlags::Exported) + F.GenericFlags |= LLVMJITSymbolGenericFlagsExported; + if (JSF & JITSymbolFlags::Weak) + F.GenericFlags |= LLVMJITSymbolGenericFlagsWeak; + if (JSF & JITSymbolFlags::Callable) + F.GenericFlags |= LLVMJITSymbolGenericFlagsCallable; + if (JSF & JITSymbolFlags::MaterializationSideEffectsOnly) + F.GenericFlags |= LLVMJITSymbolGenericFlagsMaterializationSideEffectsOnly; + + F.TargetFlags = JSF.getTargetFlags(); + + return F; +} + +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; +} + +static SymbolDependenceMap +toSymbolDependenceMap(LLVMOrcCDependenceMapPairs Pairs, size_t NumPairs) { + SymbolDependenceMap SDM; + for (size_t I = 0; I != NumPairs; ++I) { + JITDylib *JD = unwrap(Pairs[I].JD); + SymbolNameSet Names; + + for (size_t J = 0; J != Pairs[I].Names.Length; ++J) { + auto Sym = Pairs[I].Names.Symbols[J]; + Names.insert(OrcV2CAPIHelper::moveToSymbolStringPtr(unwrap(Sym))); + } + SDM[JD] = Names; + } + return SDM; +} + } // end anonymous namespace void LLVMOrcExecutionSessionSetErrorReporter( @@ -334,13 +376,7 @@ LLVMOrcMaterializationUnitRef LLVMOrcCreateCustomMaterializationUnit( 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()); } @@ -364,6 +400,150 @@ LLVMOrcMaterializationUnitRef LLVMOrcLazyReexports( .release()); } +void LLVMOrcDisposeMaterializationResponsibility( + LLVMOrcMaterializationResponsibilityRef MR) { + std::unique_ptr TmpMR(unwrap(MR)); +} + +LLVMOrcJITDylibRef LLVMOrcMaterializationResponsibilityGetTargetDylib( + LLVMOrcMaterializationResponsibilityRef MR) { + return wrap(&unwrap(MR)->getTargetJITDylib()); +} + +LLVMOrcExecutionSessionRef +LLVMOrcMaterializationResponsibilityGetExecutionSession( + LLVMOrcMaterializationResponsibilityRef MR) { + return wrap(&unwrap(MR)->getExecutionSession()); +} + +LLVMOrcCSymbolFlagsMapPairs LLVMOrcMaterializationResponsibilityGetSymbols( + LLVMOrcMaterializationResponsibilityRef MR, size_t *NumPairs) { + + auto Symbols = unwrap(MR)->getSymbols(); + LLVMOrcCSymbolFlagsMapPairs Result = static_cast( + safe_malloc(Symbols.size() * sizeof(LLVMOrcCSymbolFlagsMapPair))); + size_t I = 0; + for (auto const &pair : Symbols) { + auto Name = wrap(OrcV2CAPIHelper::getRawPoolEntryPtr(pair.first)); + auto Flags = pair.second; + Result[I] = {Name, fromJITSymbolFlags(Flags)}; + I++; + } + *NumPairs = Symbols.size(); + return Result; +} + +void LLVMOrcDisposeCSymbolFlagsMap(LLVMOrcCSymbolFlagsMapPairs Pairs) { + free(Pairs); +} + +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()); +} + +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))); +} + +LLVMErrorRef LLVMOrcMaterializationResponsibilityReplace( + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcMaterializationUnitRef MU) { + std::unique_ptr TmpMU(unwrap(MU)); + return wrap(unwrap(MR)->replace(std::move(TmpMU))); +} + +LLVMErrorRef LLVMOrcMaterializationResponsibilityDelegate( + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcSymbolStringPoolEntryRef *Symbols, size_t NumSymbols, + LLVMOrcMaterializationResponsibilityRef *Result) { + SymbolNameSet Syms; + for (size_t I = 0; I != NumSymbols; I++) { + Syms.insert(OrcV2CAPIHelper::moveToSymbolStringPtr(unwrap(Symbols[I]))); + } + auto OtherMR = unwrap(MR)->delegate(Syms); + + if (!OtherMR) { + return wrap(OtherMR.takeError()); + } + *Result = wrap(OtherMR->release()); + return LLVMErrorSuccess; +} + +void LLVMOrcMaterializationResponsibilityAddDependencies( + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcSymbolStringPoolEntryRef Name, + LLVMOrcCDependenceMapPairs Dependencies, size_t NumPairs) { + + SymbolDependenceMap SDM = toSymbolDependenceMap(Dependencies, NumPairs); + auto Sym = OrcV2CAPIHelper::moveToSymbolStringPtr(unwrap(Name)); + unwrap(MR)->addDependencies(Sym, SDM); +} + +void LLVMOrcMaterializationResponsibilityAddDependenciesForAll( + LLVMOrcMaterializationResponsibilityRef MR, + LLVMOrcCDependenceMapPairs Dependencies, size_t NumPairs) { + + SymbolDependenceMap SDM = toSymbolDependenceMap(Dependencies, NumPairs); + unwrap(MR)->addDependenciesForAll(SDM); +} + +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) { @@ -749,6 +929,10 @@ LLVMOrcIRTransformLayerRef LLVMOrcLLJITGetIRTransformLayer(LLVMOrcLLJITRef J) { return wrap(&unwrap(J)->getIRTransformLayer()); } +const char *LLVMOrcLLJITGetDataLayoutStr(LLVMOrcLLJITRef J) { + return unwrap(J)->getDataLayout().getStringRepresentation().c_str(); +} + LLVMOrcIndirectStubsManagerRef LLVMOrcCreateLocalIndirectStubsManager(const char *TargetTriple) { auto builder = createLocalIndirectStubsManagerBuilder(Triple(TargetTriple)); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 89a2d8b9e11..fd60c508009 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -167,6 +167,7 @@ if(LLVM_BUILD_EXAMPLES) OrcV2CBindingsRemovableCode OrcV2CBindingsReflectProcessSymbols OrcV2CBindingsLazy + OrcV2CBindingsVeryLazy ) if(CMAKE_HOST_UNIX) list(APPEND LLVM_TEST_DEPENDS diff --git a/test/lit.cfg.py b/test/lit.cfg.py index 07ec33abd4c..524832b1f89 100644 --- a/test/lit.cfg.py +++ b/test/lit.cfg.py @@ -187,7 +187,8 @@ tools.extend([ 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) diff --git a/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp b/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp index ceadebf811c..72faccd795b 100644 --- a/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp +++ b/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp @@ -354,3 +354,165 @@ TEST_F(OrcCAPITestBase, ExecutionTest) { int32_t Result = SumFn(1, 1); ASSERT_EQ(2, Result); } + +void Destroy(void *Ctx) {} + +void TargetFn() {} + +void Materialize(void *Ctx, LLVMOrcMaterializationResponsibilityRef MR) { + LLVMOrcJITDylibRef JD = + LLVMOrcMaterializationResponsibilityGetTargetDylib(MR); + ASSERT_TRUE(!!JD); + + LLVMOrcExecutionSessionRef ES = + LLVMOrcMaterializationResponsibilityGetExecutionSession(MR); + ASSERT_TRUE(!!ES); + + LLVMOrcSymbolStringPoolEntryRef InitSym = + LLVMOrcMaterializationResponsibilityGetInitializerSymbol(MR); + ASSERT_TRUE(!InitSym); + + size_t NumSymbols; + LLVMOrcCSymbolFlagsMapPairs Symbols = + LLVMOrcMaterializationResponsibilityGetSymbols(MR, &NumSymbols); + + ASSERT_TRUE(!!Symbols); + ASSERT_EQ(NumSymbols, (size_t)1); + + LLVMOrcSymbolStringPoolEntryRef *RequestedSymbols = + LLVMOrcMaterializationResponsibilityGetRequestedSymbols(MR, &NumSymbols); + + ASSERT_TRUE(!!RequestedSymbols); + ASSERT_EQ(NumSymbols, (size_t)1); + + LLVMOrcCSymbolFlagsMapPair TargetSym = Symbols[0]; + + ASSERT_EQ(RequestedSymbols[0], TargetSym.Name); + LLVMOrcRetainSymbolStringPoolEntry(TargetSym.Name); + + LLVMOrcDisposeCSymbolFlagsMap(Symbols); + LLVMOrcDisposeSymbols(RequestedSymbols); + + LLVMOrcJITTargetAddress Addr = (LLVMOrcJITTargetAddress)(&TargetFn); + + LLVMJITSymbolFlags Flags = { + LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable, 0}; + ASSERT_EQ(TargetSym.Flags.GenericFlags, Flags.GenericFlags); + ASSERT_EQ(TargetSym.Flags.TargetFlags, Flags.TargetFlags); + + LLVMJITEvaluatedSymbol Sym = {Addr, Flags}; + + LLVMOrcLLJITRef J = (LLVMOrcLLJITRef)Ctx; + + LLVMOrcSymbolStringPoolEntryRef OtherSymbol = + LLVMOrcLLJITMangleAndIntern(J, "other"); + LLVMOrcSymbolStringPoolEntryRef DependencySymbol = + LLVMOrcLLJITMangleAndIntern(J, "dependency"); + + LLVMOrcRetainSymbolStringPoolEntry(OtherSymbol); + LLVMOrcRetainSymbolStringPoolEntry(DependencySymbol); + LLVMOrcCSymbolFlagsMapPair NewSymbols[] = { + {OtherSymbol, Flags}, + {DependencySymbol, Flags}, + }; + LLVMOrcMaterializationResponsibilityDefineMaterializing(MR, NewSymbols, 2); + + LLVMOrcRetainSymbolStringPoolEntry(OtherSymbol); + LLVMOrcMaterializationResponsibilityRef OtherMR = NULL; + { + LLVMErrorRef Err = LLVMOrcMaterializationResponsibilityDelegate( + MR, &OtherSymbol, 1, &OtherMR); + if (Err) { + char *ErrMsg = LLVMGetErrorMessage(Err); + fprintf(stderr, "Error: %s\n", ErrMsg); + LLVMDisposeErrorMessage(ErrMsg); + LLVMOrcMaterializationResponsibilityFailMaterialization(MR); + LLVMOrcDisposeMaterializationResponsibility(MR); + return; + } + } + assert(OtherMR); + + LLVMJITCSymbolMapPair OtherPair = {OtherSymbol, Sym}; + LLVMOrcMaterializationUnitRef OtherMU = LLVMOrcAbsoluteSymbols(&OtherPair, 1); + // OtherSymbol is no longer owned by us + { + LLVMErrorRef Err = + LLVMOrcMaterializationResponsibilityReplace(OtherMR, OtherMU); + if (Err) { + char *ErrMsg = LLVMGetErrorMessage(Err); + fprintf(stderr, "Error: %s\n", ErrMsg); + LLVMDisposeErrorMessage(ErrMsg); + + LLVMOrcMaterializationResponsibilityFailMaterialization(OtherMR); + LLVMOrcMaterializationResponsibilityFailMaterialization(MR); + + LLVMOrcDisposeMaterializationResponsibility(OtherMR); + LLVMOrcDisposeMaterializationResponsibility(MR); + LLVMOrcDisposeMaterializationUnit(OtherMU); + return; + } + } + LLVMOrcDisposeMaterializationResponsibility(OtherMR); + + // FIXME: Implement async lookup + // A real test of the dependence tracking in the success case would require + // async lookups. You could: + // 1. Materialize foo, making foo depend on other. + // 2. In the caller, verify that the lookup callback for foo has not run (due + // to the dependence) + // 3. Materialize other by looking it up. + // 4. In the caller, verify that the lookup callback for foo has now run. + + LLVMOrcRetainSymbolStringPoolEntry(TargetSym.Name); + LLVMOrcRetainSymbolStringPoolEntry(DependencySymbol); + LLVMOrcCDependenceMapPair Dependency = {JD, {&DependencySymbol, 1}}; + LLVMOrcMaterializationResponsibilityAddDependencies(MR, TargetSym.Name, + &Dependency, 1); + + LLVMOrcRetainSymbolStringPoolEntry(DependencySymbol); + LLVMOrcMaterializationResponsibilityAddDependenciesForAll(MR, &Dependency, 1); + + // See FIXME above + LLVMJITCSymbolMapPair Pair = {DependencySymbol, Sym}; + LLVMOrcMaterializationResponsibilityNotifyResolved(MR, &Pair, 1); + // DependencySymbol no longer owned by us + + Pair = {TargetSym.Name, Sym}; + LLVMOrcMaterializationResponsibilityNotifyResolved(MR, &Pair, 1); + + LLVMOrcMaterializationResponsibilityNotifyEmitted(MR); + LLVMOrcDisposeMaterializationResponsibility(MR); + return; +} + +TEST_F(OrcCAPITestBase, MaterializationResponsibility) { + LLVMJITSymbolFlags Flags = { + LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable, 0}; + LLVMOrcCSymbolFlagsMapPair Sym = {LLVMOrcLLJITMangleAndIntern(Jit, "foo"), + Flags}; + + LLVMOrcMaterializationUnitRef MU = LLVMOrcCreateCustomMaterializationUnit( + "MU", (void *)Jit, &Sym, 1, NULL, &Materialize, NULL, &Destroy); + LLVMOrcJITDylibRef JD = LLVMOrcLLJITGetMainJITDylib(Jit); + LLVMOrcJITDylibDefine(JD, MU); + + LLVMOrcJITTargetAddress Addr; + if (LLVMErrorRef Err = LLVMOrcLLJITLookup(Jit, &Addr, "foo")) { + FAIL() << "foo was not materialized " << toString(Err); + } + ASSERT_TRUE(!!Addr); + ASSERT_EQ(Addr, (LLVMOrcJITTargetAddress)&TargetFn); + + if (LLVMErrorRef Err = LLVMOrcLLJITLookup(Jit, &Addr, "other")) { + FAIL() << "other was not materialized " << toString(Err); + } + ASSERT_TRUE(!!Addr); + ASSERT_EQ(Addr, (LLVMOrcJITTargetAddress)&TargetFn); + + if (LLVMErrorRef Err = LLVMOrcLLJITLookup(Jit, &Addr, "dependency")) { + FAIL() << "dependency was not materialized " << toString(Err); + } + ASSERT_TRUE(!!Addr); + ASSERT_EQ(Addr, (LLVMOrcJITTargetAddress)&TargetFn); +}