diff --git a/llvm/examples/OrcV2Examples/CMakeLists.txt b/llvm/examples/OrcV2Examples/CMakeLists.txt index f6a11d6f0ef20..3c7301e06d797 100644 --- a/llvm/examples/OrcV2Examples/CMakeLists.txt +++ b/llvm/examples/OrcV2Examples/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(LLJITWithTargetProcessControl) add_subdirectory(LLJITWithThinLTOSummaries) add_subdirectory(OrcV2CBindingsAddObjectFile) add_subdirectory(OrcV2CBindingsBasicUsage) +add_subdirectory(OrcV2CBindingsDumpObjects) add_subdirectory(OrcV2CBindingsReflectProcessSymbols) add_subdirectory(OrcV2CBindingsRemovableCode) diff --git a/llvm/examples/OrcV2Examples/OrcV2CBindingsDumpObjects/CMakeLists.txt b/llvm/examples/OrcV2Examples/OrcV2CBindingsDumpObjects/CMakeLists.txt new file mode 100644 index 0000000000000..8e2c97d782062 --- /dev/null +++ b/llvm/examples/OrcV2Examples/OrcV2CBindingsDumpObjects/CMakeLists.txt @@ -0,0 +1,15 @@ +set(LLVM_LINK_COMPONENTS + Core + ExecutionEngine + IRReader + JITLink + MC + OrcJIT + Support + Target + nativecodegen + ) + +add_llvm_example(OrcV2CBindingsDumpObjects + OrcV2CBindingsDumpObjects.c + ) diff --git a/llvm/examples/OrcV2Examples/OrcV2CBindingsDumpObjects/OrcV2CBindingsDumpObjects.c b/llvm/examples/OrcV2Examples/OrcV2CBindingsDumpObjects/OrcV2CBindingsDumpObjects.c new file mode 100644 index 0000000000000..1a90138d20364 --- /dev/null +++ b/llvm/examples/OrcV2Examples/OrcV2CBindingsDumpObjects/OrcV2CBindingsDumpObjects.c @@ -0,0 +1,140 @@ +//===- OrcV2CBindingsDumpObjects.c - Dump JIT'd objects to disk via C API -===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// To run the demo build 'OrcV2CBindingsDumpObjects', then run the built +// program. It will execute as for OrcV2CBindingsBasicUsage, but will write +// a single JIT'd object out to the working directory. +// +// Try experimenting with the DumpDir and IdentifierOverride arguments to +// LLVMOrcCreateDumpObjects. +// +//===----------------------------------------------------------------------===// + +#include "llvm-c/Core.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; +} + +LLVMOrcThreadSafeModuleRef createDemoModule() { + LLVMOrcThreadSafeContextRef TSCtx = LLVMOrcCreateNewThreadSafeContext(); + LLVMContextRef Ctx = LLVMOrcThreadSafeContextGetContext(TSCtx); + LLVMModuleRef M = LLVMModuleCreateWithNameInContext("demo", Ctx); + LLVMTypeRef ParamTypes[] = {LLVMInt32Type(), LLVMInt32Type()}; + LLVMTypeRef SumFunctionType = + LLVMFunctionType(LLVMInt32Type(), ParamTypes, 2, 0); + LLVMValueRef SumFunction = LLVMAddFunction(M, "sum", SumFunctionType); + LLVMBasicBlockRef EntryBB = LLVMAppendBasicBlock(SumFunction, "entry"); + LLVMBuilderRef Builder = LLVMCreateBuilder(); + LLVMPositionBuilderAtEnd(Builder, EntryBB); + LLVMValueRef SumArg0 = LLVMGetParam(SumFunction, 0); + LLVMValueRef SumArg1 = LLVMGetParam(SumFunction, 1); + LLVMValueRef Result = LLVMBuildAdd(Builder, SumArg0, SumArg1, "result"); + LLVMBuildRet(Builder, Result); + LLVMOrcThreadSafeModuleRef TSM = LLVMOrcCreateNewThreadSafeModule(M, TSCtx); + LLVMOrcDisposeThreadSafeContext(TSCtx); + return TSM; +} + +LLVMErrorRef dumpObjectsTransform(LLVMMemoryBufferRef *ObjInOut, void *Ctx) { + LLVMOrcDumpObjectsRef DumpObjects = *(LLVMOrcDumpObjectsRef *)Ctx; + return LLVMOrcDumpObjects_CallOperator(DumpObjects, ObjInOut); +} + +int main(int argc, char *argv[]) { + + int MainResult = 0; + + LLVMParseCommandLineOptions(argc, (const char **)argv, ""); + LLVMInitializeCore(LLVMGetGlobalPassRegistry()); + + LLVMInitializeNativeTarget(); + LLVMInitializeNativeAsmPrinter(); + + // Create a DumpObjects instance to use when dumping objects to disk. + LLVMOrcDumpObjectsRef DumpObjects = LLVMOrcCreateDumpObjects("", ""); + + // Create the JIT instance. + LLVMOrcLLJITRef J; + { + LLVMErrorRef Err; + if ((Err = LLVMOrcCreateLLJIT(&J, 0))) { + MainResult = handleError(Err); + goto llvm_shutdown; + } + } + + // Set an object transform to call our DumpObjects instance for every + // JIT'd object. + LLVMOrcObjectTransformLayerSetTransform(LLVMOrcLLJITGetObjTransformLayer(J), + dumpObjectsTransform, &DumpObjects); + + // Create our demo module. + LLVMOrcThreadSafeModuleRef TSM = createDemoModule(); + + // Add our demo module to the JIT. + { + LLVMOrcJITDylibRef MainJD = LLVMOrcLLJITGetMainJITDylib(J); + LLVMErrorRef Err; + if ((Err = LLVMOrcLLJITAddLLVMIRModule(J, MainJD, TSM))) { + // If adding the ThreadSafeModule fails then we need to clean it up + // ourselves. If adding it succeeds the JIT will manage the memory. + LLVMOrcDisposeThreadSafeModule(TSM); + MainResult = handleError(Err); + goto jit_cleanup; + } + } + + // Look up the address of our demo entry point. + LLVMOrcJITTargetAddress SumAddr; + { + LLVMErrorRef Err; + if ((Err = LLVMOrcLLJITLookup(J, &SumAddr, "sum"))) { + MainResult = handleError(Err); + goto jit_cleanup; + } + } + + // If we made it here then everything succeeded. Execute our JIT'd code. + int32_t (*Sum)(int32_t, int32_t) = (int32_t(*)(int32_t, int32_t))SumAddr; + int32_t Result = Sum(1, 2); + + // Print the result. + printf("1 + 2 = %i\n", Result); + +jit_cleanup: + + // Destroy our JIT instance. + { + LLVMErrorRef Err; + if ((Err = LLVMOrcDisposeLLJIT(J))) { + int NewFailureResult = handleError(Err); + if (MainResult == 0) + MainResult = NewFailureResult; + } + } + +llvm_shutdown: + // Destroy our DumpObjects instance. + LLVMOrcDisposeDumpObjects(DumpObjects); + + // Shut down LLVM. + LLVMShutdown(); + + return MainResult; +} diff --git a/llvm/include/llvm-c/LLJIT.h b/llvm/include/llvm-c/LLJIT.h index b140a5d058811..37f4d4e94c498 100644 --- a/llvm/include/llvm-c/LLJIT.h +++ b/llvm/include/llvm-c/LLJIT.h @@ -216,6 +216,12 @@ LLVMErrorRef LLVMOrcLLJITLookup(LLVMOrcLLJITRef J, */ LLVMOrcObjectLayerRef LLVMOrcLLJITGetObjLinkingLayer(LLVMOrcLLJITRef J); +/** + * Returns a non-owning reference to the LLJIT instance's object linking layer. + */ +LLVMOrcObjectTransformLayerRef +LLVMOrcLLJITGetObjTransformLayer(LLVMOrcLLJITRef J); + LLVM_C_EXTERN_C_END #endif /* LLVM_C_LLJIT_H */ diff --git a/llvm/include/llvm-c/Orc.h b/llvm/include/llvm-c/Orc.h index 612c80bae6611..4b942b2d12d2d 100644 --- a/llvm/include/llvm-c/Orc.h +++ b/llvm/include/llvm-c/Orc.h @@ -315,6 +315,29 @@ typedef struct LLVMOrcOpaqueObjectLayer *LLVMOrcObjectLayerRef; */ typedef struct LLVMOrcOpaqueObjectLinkingLayer *LLVMOrcObjectLinkingLayerRef; +/** + * A reference to an orc::ObjectTransformLayer instance. + */ +typedef struct LLVMOrcOpaqueObjectTransformLayer + *LLVMOrcObjectTransformLayerRef; + +/** + * A function for applying transformations to an object file buffer. + * + * The transform is allowed to return an error, in which case the ObjInOut + * buffer should be disposed of and set to null. + */ +typedef LLVMErrorRef (*LLVMOrcObjectTransformLayerTransformFunction)( + LLVMMemoryBufferRef *ObjInOut, void *Ctx); + +/** + * A reference to an orc::DumpObjects object. + * + * Can be used to dump object files to disk with unique names. Useful as an + * ObjectTransformLayer transform. + */ +typedef struct LLVMOrcOpaqueDumpObjects *LLVMOrcDumpObjectsRef; + /** * Attach a custom error reporter function to the ExecutionSession. * @@ -710,6 +733,41 @@ void LLVMOrcObjectLayerEmit(LLVMOrcObjectLayerRef ObjLayer, */ void LLVMOrcDisposeObjectLayer(LLVMOrcObjectLayerRef ObjLayer); +/** + * Set the transform function on an LLVMOrcObjectTransformLayer. + */ +void LLVMOrcObjectTransformLayerSetTransform( + LLVMOrcObjectTransformLayerRef ObjTransformLayer, + LLVMOrcObjectTransformLayerTransformFunction TransformFunction, void *Ctx); + +/** + * Create a DumpObjects instance. + * + * DumpDir specifies the path to write dumped objects to. DumpDir may be empty + * in which case files will be dumped to the working directory. + * + * IdentifierOverride specifies a file name stem to use when dumping objects. + * If empty then each MemoryBuffer's identifier will be used (with a .o suffix + * added if not already present). If an identifier override is supplied it will + * be used instead, along with an incrementing counter (since all buffers will + * use the same identifier, the resulting files will be named .o, + * .2.o, .3.o, and so on). IdentifierOverride should not contain + * an extension, as a .o suffix will be added by DumpObjects. + */ +LLVMOrcDumpObjectsRef LLVMOrcCreateDumpObjects(const char *DumpDir, + const char *IdentifierOverride); + +/** + * Dispose of a DumpObjects instance. + */ +void LLVMOrcDisposeDumpObjects(LLVMOrcDumpObjectsRef DumpObjects); + +/** + * Dump the contents of the given MemoryBuffer. + */ +LLVMErrorRef LLVMOrcDumpObjects_CallOperator(LLVMOrcDumpObjectsRef DumpObjects, + LLVMMemoryBufferRef *ObjBuffer); + LLVM_C_EXTERN_C_END #endif /* LLVM_C_ORC_H */ diff --git a/llvm/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp b/llvm/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp index 3c6bd8fa4ac05..71e260f8caf09 100644 --- a/llvm/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp +++ b/llvm/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp @@ -13,6 +13,7 @@ #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" #include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h" #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" @@ -93,6 +94,9 @@ DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ThreadSafeModule, LLVMOrcThreadSafeModuleRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(JITTargetMachineBuilder, LLVMOrcJITTargetMachineBuilderRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ObjectLayer, LLVMOrcObjectLayerRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ObjectTransformLayer, + LLVMOrcObjectTransformLayerRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(DumpObjects, LLVMOrcDumpObjectsRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LLJITBuilder, LLVMOrcLLJITBuilderRef) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LLJIT, LLVMOrcLLJITRef) @@ -513,6 +517,44 @@ void LLVMOrcDisposeObjectLayer(LLVMOrcObjectLayerRef ObjLayer) { delete unwrap(ObjLayer); } +void LLVMOrcObjectTransformLayerSetTransform( + LLVMOrcObjectTransformLayerRef ObjTransformLayer, + LLVMOrcObjectTransformLayerTransformFunction TransformFunction, void *Ctx) { + unwrap(ObjTransformLayer) + ->setTransform([TransformFunction, Ctx](std::unique_ptr Obj) + -> Expected> { + LLVMMemoryBufferRef ObjBuffer = wrap(Obj.release()); + if (LLVMErrorRef Err = TransformFunction(&ObjBuffer, Ctx)) { + assert(!ObjBuffer && "ObjBuffer was not reset to null on error"); + return unwrap(Err); + } + return std::unique_ptr(unwrap(ObjBuffer)); + }); +} + +LLVMOrcDumpObjectsRef LLVMOrcCreateDumpObjects(const char *DumpDir, + const char *IdentifierOverride) { + assert(DumpDir && "DumpDir should not be null"); + assert(IdentifierOverride && "IdentifierOverride should not be null"); + return wrap(new DumpObjects(DumpDir, IdentifierOverride)); +} + +void LLVMOrcDisposeDumpObjects(LLVMOrcDumpObjectsRef DumpObjects) { + delete unwrap(DumpObjects); +} + +LLVMErrorRef LLVMOrcDumpObjects_CallOperator(LLVMOrcDumpObjectsRef DumpObjects, + LLVMMemoryBufferRef *ObjBuffer) { + std::unique_ptr OB(unwrap(*ObjBuffer)); + if (auto Result = (*unwrap(DumpObjects))(std::move(OB))) { + *ObjBuffer = wrap(Result->release()); + return LLVMErrorSuccess; + } else { + *ObjBuffer = nullptr; + return wrap(Result.takeError()); + } +} + LLVMOrcLLJITBuilderRef LLVMOrcCreateLLJITBuilder(void) { return wrap(new LLJITBuilder()); } @@ -632,6 +674,11 @@ LLVMOrcObjectLayerRef LLVMOrcLLJITGetObjLinkingLayer(LLVMOrcLLJITRef J) { return wrap(&unwrap(J)->getObjLinkingLayer()); } +LLVMOrcObjectTransformLayerRef +LLVMOrcLLJITGetObjTransformLayer(LLVMOrcLLJITRef J) { + return wrap(&unwrap(J)->getObjTransformLayer()); +} + LLVMOrcObjectLayerRef LLVMOrcCreateRTDyldObjectLinkingLayerWithSectionMemoryManager( LLVMOrcExecutionSessionRef ES) {