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,232 @@ +//===-------- 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 + +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; + { + LLVMOrcExecutionSessionRef ES = LLVMOrcLLJITGetExecutionSession(J); + LCTM = LLVMOrcCreateLocalLazyCallThroughManager(TargetTriple, ES, 0); + } + + 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 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 @@ -118,6 +118,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. @@ -368,6 +390,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. * @@ -531,6 +565,14 @@ LLVMOrcMaterializationUnitRef LLVMOrcAbsoluteSymbols(LLVMOrcCSymbolMapPairs Syms, size_t NumPairs); +LLVMOrcMaterializationUnitRef +LLVMOrcLazyReexports(LLVMOrcLazyCallThroughManagerRef LCTM, + LLVMOrcIndirectStubsManagerRef ISM, + LLVMOrcJITDylibRef SourceRef, + LLVMOrcCSymbolAliasMapPairs CallableAliases, + size_t NumPairs); + // TODO: ImplSymbolMad SrcJDLoc + /** * Create a "bare" JITDylib. * @@ -794,6 +836,29 @@ 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); + +LLVMOrcLazyCallThroughManagerRef LLVMOrcCreateLocalLazyCallThroughManager( + const char *TargetTriple, LLVMOrcExecutionSessionRef ES, + LLVMOrcJITTargetAddress ErrorHandlerAddr); + +/** + * Dispose of an LazyCallThroughManager. + */ +void LLVMOrcDisposeLazyCallThroughManager(LLVMOrcLazyCallThroughManagerRef LCM); + /** * 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,28 @@ 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 +750,26 @@ 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)); +} + +LLVMOrcLazyCallThroughManagerRef LLVMOrcCreateLocalLazyCallThroughManager( + const char *TargetTriple, LLVMOrcExecutionSessionRef ES, + LLVMOrcJITTargetAddress ErrorHandlerAddr) { + auto LCTM = createLocalLazyCallThroughManager( + Triple(TargetTriple), *unwrap(ES), ErrorHandlerAddr); + + return wrap(LCTM->release()); +} + +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 @@ -164,6 +164,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 @@ -184,7 +184,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)