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 @@ -14,6 +14,7 @@ add_subdirectory(OrcV2CBindingsIRTransforms) add_subdirectory(OrcV2CBindingsReflectProcessSymbols) add_subdirectory(OrcV2CBindingsRemovableCode) +add_subdirectory(OrcV2CBindingsLazy) if(CMAKE_HOST_UNIX) add_subdirectory(LLJITWithRemoteDebugging) diff --git a/llvm/examples/OrcV2Examples/OrcV2CBindingsLazy/CMakeLists.txt b/llvm/examples/OrcV2Examples/OrcV2CBindingsLazy/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/examples/OrcV2Examples/OrcV2CBindingsLazy/CMakeLists.txt @@ -0,0 +1,15 @@ +set(LLVM_LINK_COMPONENTS + Core + ExecutionEngine + IRReader + JITLink + MC + OrcJIT + Support + Target + nativecodegen + ) + +add_llvm_example(OrcV2CBindingsLazy + OrcV2CBindingsLazy.c + ) diff --git a/llvm/examples/OrcV2Examples/OrcV2CBindingsLazy/OrcV2CBindingsLazy.c b/llvm/examples/OrcV2Examples/OrcV2CBindingsLazy/OrcV2CBindingsLazy.c new file mode 100644 --- /dev/null +++ b/llvm/examples/OrcV2Examples/OrcV2CBindingsLazy/OrcV2CBindingsLazy.c @@ -0,0 +1,244 @@ +//===-------- 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 + +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, 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 demo modules to the JIT. + { + LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J); + LLVMErrorRef Err; + + LLVMOrcThreadSafeModuleRef FooTSM; + if ((Err = + parseExampleModule(FooMod, sizeof(FooMod), "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), "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), "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; +} 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 @@ -123,6 +123,28 @@ */ typedef LLVMJITCSymbolMapPair *LLVMOrcCSymbolMapPairs; +/** + * Represents a SymbolAliasMapEntry + */ +typedef struct { + LLVMOrcSymbolStringPoolEntryRef Name; + LLVMJITSymbolFlags Flags; +} LLVMOrcCSymbolAliasMapEntry; + +/** + * Represents a pair of a symbol name and SymbolAliasMapEntry. + */ +typedef struct { + LLVMOrcSymbolStringPoolEntryRef Name; + LLVMOrcCSymbolAliasMapEntry Entry; +} LLVMOrcCSymbolAliasMapPair; + +/** + * Represents a list of (SymbolStringPtr, (SymbolStringPtr, JITSymbolFlags)) + * pairs that can be used to construct a SymbolFlagsMap. + */ +typedef LLVMOrcCSymbolAliasMapPair *LLVMOrcCSymbolAliasMapPairs; + /** * Lookup kind. This can be used by definition generators when deciding whether * to produce a definition for a requested symbol. @@ -373,6 +395,18 @@ typedef LLVMErrorRef (*LLVMOrcObjectTransformLayerTransformFunction)( void *Ctx, LLVMMemoryBufferRef *ObjInOut); +/** + * A reference to an orc::IndirectStubsManager instance. + */ +typedef struct LLVMOrcOpaqueIndirectStubsManager + *LLVMOrcIndirectStubsManagerRef; + +/** + * A reference to an orc::LazyCallThroughManager instance. + */ +typedef struct LLVMOrcOpaqueLazyCallThroughManager + *LLVMOrcLazyCallThroughManagerRef; + /** * A reference to an orc::DumpObjects object. * @@ -536,6 +570,33 @@ LLVMOrcMaterializationUnitRef LLVMOrcAbsoluteSymbols(LLVMOrcCSymbolMapPairs Syms, size_t NumPairs); +/** + * Create a MaterializationUnit to define lazy re-expots. These are callable + * entry points that call through to the given symbols. + * + * This function takes ownership of the CallableAliases array. The Name + * fields of the array elements are taken to have been retained for this + * function. This allows the following pattern... + * + * size_t NumPairs; + * LLVMOrcCSymbolAliasMapPairs CallableAliases; + * -- Build CallableAliases array -- + * LLVMOrcMaterializationUnitRef MU = + * LLVMOrcLazyReexports(LCTM, ISM, JD, CallableAliases, NumPairs); + * + * ... without requiring cleanup of the elements of the CallableAliases array afterwards. + * + * The client is still responsible for deleting the CallableAliases array itself. + * + * If a client wishes to reuse elements of the CallableAliases array after this call they + * must explicitly retain each of the elements for themselves. + */ +LLVMOrcMaterializationUnitRef LLVMOrcLazyReexports( + LLVMOrcLazyCallThroughManagerRef LCTM, LLVMOrcIndirectStubsManagerRef ISM, + LLVMOrcJITDylibRef SourceRef, LLVMOrcCSymbolAliasMapPairs CallableAliases, + size_t NumPairs); +// TODO: ImplSymbolMad SrcJDLoc + /** * Create a "bare" JITDylib. * @@ -799,6 +860,31 @@ LLVMOrcObjectTransformLayerRef ObjTransformLayer, LLVMOrcObjectTransformLayerTransformFunction TransformFunction, void *Ctx); +/** + * Create a LocalIndirectStubsManager from the given target triple. + * + * The resulting IndirectStubsManager is owned by the client + * and must be disposed of by calling LLVMOrcDisposeDisposeIndirectStubsManager. + */ +LLVMOrcIndirectStubsManagerRef +LLVMOrcCreateLocalIndirectStubsManager(const char *TargetTriple); + +/** + * Dispose of an IndirectStubsManager. + */ +void LLVMOrcDisposeIndirectStubsManager(LLVMOrcIndirectStubsManagerRef ISM); + +LLVMErrorRef LLVMOrcCreateLocalLazyCallThroughManager( + const char *TargetTriple, LLVMOrcExecutionSessionRef ES, + LLVMOrcJITTargetAddress ErrorHandlerAddr, + LLVMOrcLazyCallThroughManagerRef *LCTM); + +/** + * Dispose of an LazyCallThroughManager. + */ +void LLVMOrcDisposeLazyCallThroughManager( + LLVMOrcLazyCallThroughManagerRef LCTM); + /** * Create a DumpObjects instance. * 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 @@ -98,9 +98,12 @@ DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ObjectTransformLayer, LLVMOrcObjectTransformLayerRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DumpObjects, LLVMOrcDumpObjectsRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(IndirectStubsManager, + LLVMOrcIndirectStubsManagerRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LazyCallThroughManager, + LLVMOrcLazyCallThroughManagerRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LLJITBuilder, LLVMOrcLLJITBuilderRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LLJIT, LLVMOrcLLJITRef) - DEFINE_SIMPLE_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef) namespace llvm { @@ -341,6 +344,26 @@ return wrap(absoluteSymbols(std::move(SM)).release()); } +LLVMOrcMaterializationUnitRef LLVMOrcLazyReexports( + LLVMOrcLazyCallThroughManagerRef LCTM, LLVMOrcIndirectStubsManagerRef ISM, + LLVMOrcJITDylibRef SourceJD, LLVMOrcCSymbolAliasMapPairs CallableAliases, + size_t NumPairs) { + + SymbolAliasMap SAM; + for (size_t I = 0; I != NumPairs; ++I) { + auto pair = CallableAliases[I]; + JITSymbolFlags Flags = toJITSymbolFlags(pair.Entry.Flags); + SymbolStringPtr Name = + OrcV2CAPIHelper::moveToSymbolStringPtr(unwrap(pair.Entry.Name)); + SAM[OrcV2CAPIHelper::moveToSymbolStringPtr(unwrap(pair.Name))] = + SymbolAliasMapEntry(Name, Flags); + } + + return wrap(lazyReexports(*unwrap(LCTM), *unwrap(ISM), *unwrap(SourceJD), + std::move(SAM)) + .release()); +} + LLVMOrcJITDylibRef LLVMOrcExecutionSessionCreateBareJITDylib(LLVMOrcExecutionSessionRef ES, const char *Name) { @@ -725,3 +748,31 @@ LLVMOrcIRTransformLayerRef LLVMOrcLLJITGetIRTransformLayer(LLVMOrcLLJITRef J) { return wrap(&unwrap(J)->getIRTransformLayer()); } + +LLVMOrcIndirectStubsManagerRef +LLVMOrcCreateLocalIndirectStubsManager(const char *TargetTriple) { + auto builder = createLocalIndirectStubsManagerBuilder(Triple(TargetTriple)); + return wrap(builder().release()); +} + +void LLVMOrcDisposeIndirectStubsManager(LLVMOrcIndirectStubsManagerRef ISM) { + std::unique_ptr TmpISM(unwrap(ISM)); +} + +LLVMErrorRef LLVMOrcCreateLocalLazyCallThroughManager( + const char *TargetTriple, LLVMOrcExecutionSessionRef ES, + LLVMOrcJITTargetAddress ErrorHandlerAddr, + LLVMOrcLazyCallThroughManagerRef *Result) { + auto LCTM = createLocalLazyCallThroughManager(Triple(TargetTriple), + *unwrap(ES), ErrorHandlerAddr); + + if (!LCTM) + return wrap(LCTM.takeError()); + *Result = wrap(LCTM->release()); + return LLVMErrorSuccess; +} + +void LLVMOrcDisposeLazyCallThroughManager( + LLVMOrcLazyCallThroughManagerRef LCM) { + std::unique_ptr TmpLCM(unwrap(LCM)); +} diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt --- a/llvm/test/CMakeLists.txt +++ b/llvm/test/CMakeLists.txt @@ -166,6 +166,7 @@ OrcV2CBindingsAddObjectFile OrcV2CBindingsRemovableCode OrcV2CBindingsReflectProcessSymbols + OrcV2CBindingsLazy ) if(CMAKE_HOST_UNIX) list(APPEND LLVM_TEST_DEPENDS diff --git a/llvm/test/Examples/OrcV2Examples/orcv2-cbindings-lazy.test b/llvm/test/Examples/OrcV2Examples/orcv2-cbindings-lazy.test new file mode 100644 --- /dev/null +++ b/llvm/test/Examples/OrcV2Examples/orcv2-cbindings-lazy.test @@ -0,0 +1,5 @@ +# RUN: OrcV2CBindingsLazy 2>&1 | FileCheck -check-prefix=THIS %s +# RUN: OrcV2CBindingsLazy 0 2>&1 | FileCheck -check-prefix=OTHER %s + +# THIS: entry(1) = 1 +# OTHER: entry(2) = 2 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 @@ -186,7 +186,8 @@ ToolSubst('OrcV2CBindingsBasicUsage', unresolved='ignore'), ToolSubst('OrcV2CBindingsAddObjectFile', unresolved='ignore'), ToolSubst('OrcV2CBindingsRemovableCode', unresolved='ignore'), - ToolSubst('OrcV2CBindingsReflectProcessSymbols', unresolved='ignore')]) + ToolSubst('OrcV2CBindingsReflectProcessSymbols', unresolved='ignore'), + ToolSubst('OrcV2CBindingsLazy', unresolved='ignore')]) llvm_config.add_tool_substitutions(tools, config.llvm_tools_dir)