diff --git a/llvm/include/llvm/ExecutionEngine/Orc/COFFPlatform.h b/llvm/include/llvm/ExecutionEngine/Orc/COFFPlatform.h --- a/llvm/include/llvm/ExecutionEngine/Orc/COFFPlatform.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/COFFPlatform.h @@ -39,6 +39,14 @@ /// Try to create a COFFPlatform instance, adding the ORC runtime to the /// given JITDylib. + static Expected> + Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, + JITDylib &PlatformJD, + std::unique_ptr OrcRuntimeArchiveBuffer, + LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime = false, + const char *VCRuntimePath = nullptr, + std::optional RuntimeAliases = std::nullopt); + static Expected> Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, JITDylib &PlatformJD, const char *OrcRuntimePath, @@ -136,10 +144,14 @@ static bool supportedTarget(const Triple &TT); - COFFPlatform(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, - JITDylib &PlatformJD, const char *OrcRuntimePath, - LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime, - const char *VCRuntimePath, Error &Err); + COFFPlatform( + ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, + JITDylib &PlatformJD, + std::unique_ptr OrcRuntimeGenerator, + std::unique_ptr OrcRuntimeArchiveBuffer, + std::unique_ptr OrcRuntimeArchive, + LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime, + const char *VCRuntimePath, Error &Err); // Associate COFFPlatform JIT-side runtime support functions with handlers. Error associateRuntimeSupportFunctions(JITDylib &PlatformJD); diff --git a/llvm/include/llvm/ExecutionEngine/Orc/LLJIT.h b/llvm/include/llvm/ExecutionEngine/Orc/LLJIT.h --- a/llvm/include/llvm/ExecutionEngine/Orc/LLJIT.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/LLJIT.h @@ -258,7 +258,7 @@ std::function>( JITTargetMachineBuilder JTMB)>; - using PlatformSetupFunction = std::function; + using PlatformSetupFunction = unique_function; std::unique_ptr EPC; std::unique_ptr ES; @@ -447,8 +447,40 @@ public LLLazyJITBuilderSetters {}; -/// Configure the LLJIT instance to use orc runtime support. -Error setUpOrcPlatform(LLJIT& J); +/// Configure the LLJIT instance to use orc runtime support. This overload +/// assumes that the client has manually configured a Platform object. +Error setUpOrcPlatformManually(LLJIT &J); + +/// Configure the LLJIT instance to use the ORC runtime and the detected +/// native target for the executor. +class SetUpExecutorNativePlatform { +public: + /// The default PlatformPrepare implementation -- Uses the Main JITDylib and + /// adds a generator for all process symbols. + static Expected defaultPlatformPrepare(LLJIT &J); + + /// Set up using path to Orc runtime. PlatformPrepare will be run before + /// attempting to construct the platform instance. It should be used (if + /// needed) to provide the offers an opportunity + /// to load process symbols. + SetUpExecutorNativePlatform(std::string OrcRuntimePath, + unique_function(LLJIT &J)> + PlatformPrepare = defaultPlatformPrepare) + : Runtime(std::move(OrcRuntimePath)), + PlatformPrepare(std::move(PlatformPrepare)) {} + + /// Set up using the given memory buffer. + SetUpExecutorNativePlatform(std::unique_ptr MB, + unique_function(LLJIT &J)> + PlatformPrepare = defaultPlatformPrepare) + : Runtime(std::move(MB)), PlatformPrepare(std::move(PlatformPrepare)) {} + + Error operator()(LLJIT &J); + +private: + std::variant> Runtime; + unique_function(LLJIT &J)> PlatformPrepare; +}; /// Configure the LLJIT instance to scrape modules for llvm.global_ctors and /// llvm.global_dtors variables and (if present) build initialization and diff --git a/llvm/lib/ExecutionEngine/Orc/COFFPlatform.cpp b/llvm/lib/ExecutionEngine/Orc/COFFPlatform.cpp --- a/llvm/lib/ExecutionEngine/Orc/COFFPlatform.cpp +++ b/llvm/lib/ExecutionEngine/Orc/COFFPlatform.cpp @@ -160,12 +160,11 @@ namespace llvm { namespace orc { -Expected> -COFFPlatform::Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, - JITDylib &PlatformJD, const char *OrcRuntimePath, - LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime, - const char *VCRuntimePath, - std::optional RuntimeAliases) { +Expected> COFFPlatform::Create( + ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, + JITDylib &PlatformJD, std::unique_ptr OrcRuntimeArchiveBuffer, + LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime, + const char *VCRuntimePath, std::optional RuntimeAliases) { auto &EPC = ES.getExecutorProcessControl(); // If the target is not supported then bail out immediately. @@ -174,6 +173,22 @@ EPC.getTargetTriple().str(), inconvertibleErrorCode()); + auto GeneratorArchive = + object::Archive::create(OrcRuntimeArchiveBuffer->getMemBufferRef()); + if (!GeneratorArchive) + return GeneratorArchive.takeError(); + + auto OrcRuntimeArchiveGenerator = StaticLibraryDefinitionGenerator::Create( + ObjLinkingLayer, nullptr, std::move(*GeneratorArchive)); + if (!OrcRuntimeArchiveGenerator) + return OrcRuntimeArchiveGenerator.takeError(); + + // We need a second instance of the archive (for now) for the Platform. We + // can `cantFail` this call, since if it were going to fail it would have + // failed above. + auto RuntimeArchive = cantFail( + object::Archive::create(OrcRuntimeArchiveBuffer->getMemBufferRef())); + // Create default aliases if the caller didn't supply any. if (!RuntimeAliases) RuntimeAliases = standardPlatformAliases(ES); @@ -199,13 +214,30 @@ // Create the instance. Error Err = Error::success(); auto P = std::unique_ptr(new COFFPlatform( - ES, ObjLinkingLayer, PlatformJD, OrcRuntimePath, + ES, ObjLinkingLayer, PlatformJD, std::move(*OrcRuntimeArchiveGenerator), + std::move(OrcRuntimeArchiveBuffer), std::move(RuntimeArchive), std::move(LoadDynLibrary), StaticVCRuntime, VCRuntimePath, Err)); if (Err) return std::move(Err); return std::move(P); } +Expected> +COFFPlatform::Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, + JITDylib &PlatformJD, const char *OrcRuntimePath, + LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime, + const char *VCRuntimePath, + std::optional RuntimeAliases) { + + auto ArchiveBuffer = MemoryBuffer::getFile(OrcRuntimePath); + if (!ArchiveBuffer) + return createFileError(OrcRuntimePath, ArchiveBuffer.getError()); + + return Create(ES, ObjLinkingLayer, PlatformJD, std::move(*ArchiveBuffer), + std::move(LoadDynLibrary), StaticVCRuntime, VCRuntimePath, + std::move(RuntimeAliases)); +} + Expected COFFPlatform::getPerJDObjectFile() { auto PerJDObj = OrcRuntimeArchive->findSym("__orc_rt_coff_per_jd_marker"); if (!PerJDObj) @@ -349,37 +381,22 @@ } } -COFFPlatform::COFFPlatform(ExecutionSession &ES, - ObjectLinkingLayer &ObjLinkingLayer, - JITDylib &PlatformJD, const char *OrcRuntimePath, - LoadDynamicLibrary LoadDynLibrary, - bool StaticVCRuntime, const char *VCRuntimePath, - Error &Err) +COFFPlatform::COFFPlatform( + ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, + JITDylib &PlatformJD, + std::unique_ptr OrcRuntimeGenerator, + std::unique_ptr OrcRuntimeArchiveBuffer, + std::unique_ptr OrcRuntimeArchive, + LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime, + const char *VCRuntimePath, Error &Err) : ES(ES), ObjLinkingLayer(ObjLinkingLayer), LoadDynLibrary(std::move(LoadDynLibrary)), + OrcRuntimeArchiveBuffer(std::move(OrcRuntimeArchiveBuffer)), + OrcRuntimeArchive(std::move(OrcRuntimeArchive)), StaticVCRuntime(StaticVCRuntime), COFFHeaderStartSymbol(ES.intern("__ImageBase")) { ErrorAsOutParameter _(&Err); - // Create a generator for the ORC runtime archive. - auto OrcRuntimeArchiveGenerator = - StaticLibraryDefinitionGenerator::Load(ObjLinkingLayer, OrcRuntimePath); - if (!OrcRuntimeArchiveGenerator) { - Err = OrcRuntimeArchiveGenerator.takeError(); - return; - } - - auto ArchiveBuffer = MemoryBuffer::getFile(OrcRuntimePath); - if (!ArchiveBuffer) { - Err = createFileError(OrcRuntimePath, ArchiveBuffer.getError()); - return; - } - OrcRuntimeArchiveBuffer = std::move(*ArchiveBuffer); - OrcRuntimeArchive = - std::make_unique(*OrcRuntimeArchiveBuffer, Err); - if (Err) - return; - Bootstrapping.store(true); ObjLinkingLayer.addPlugin(std::make_unique(*this)); @@ -392,7 +409,7 @@ } VCRuntimeBootstrap = std::move(*VCRT); - for (auto &Lib : (*OrcRuntimeArchiveGenerator)->getImportedDynamicLibraries()) + for (auto &Lib : OrcRuntimeGenerator->getImportedDynamicLibraries()) DylibsToPreload.insert(Lib); auto ImportedLibs = @@ -406,7 +423,7 @@ for (auto &Lib : *ImportedLibs) DylibsToPreload.insert(Lib); - PlatformJD.addGenerator(std::move(*OrcRuntimeArchiveGenerator)); + PlatformJD.addGenerator(std::move(OrcRuntimeGenerator)); // PlatformJD hasn't been set up by the platform yet (since we're creating // the platform now), so set it up. diff --git a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp --- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp +++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp @@ -9,6 +9,8 @@ #include "llvm/ExecutionEngine/Orc/LLJIT.h" #include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h" #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" +#include "llvm/ExecutionEngine/Orc/COFFPlatform.h" +#include "llvm/ExecutionEngine/Orc/ELFNixPlatform.h" #include "llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h" #include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" #include "llvm/ExecutionEngine/Orc/MachOPlatform.h" @@ -945,11 +947,114 @@ return Error::success(); } -Error setUpOrcPlatform(LLJIT& J) { - LLVM_DEBUG( - { dbgs() << "Setting up orc platform support for LLJIT\n"; }); - J.setPlatformSupport(std::make_unique(J)); +Error setUpOrcPlatformManually(LLJIT &J) { + LLVM_DEBUG({ dbgs() << "Setting up orc platform support for LLJIT\n"; }); + J.setPlatformSupport(std::make_unique(J)); + return Error::success(); +} + +class LoadAndLinkDynLibrary { +public: + LoadAndLinkDynLibrary(LLJIT &J) : J(J) {} + Error operator()(JITDylib &JD, StringRef DLLName) { + if (!DLLName.endswith_insensitive(".dll")) + return make_error("DLLName not ending with .dll", + inconvertibleErrorCode()); + // TODO: Actually load library. + (void)J; return Error::success(); + } + +private: + LLJIT &J; +}; + +Expected +SetUpExecutorNativePlatform::defaultPlatformPrepare(LLJIT &J) { + orc::MangleAndInterner Mangle(J.getExecutionSession(), J.getDataLayout()); + + auto G = orc::DynamicLibrarySearchGenerator::GetForCurrentProcess( + J.getDataLayout().getGlobalPrefix(), + [MainName = Mangle("main")](const orc::SymbolStringPtr &Name) { + return Name != MainName; + }); + if (!G) + return G.takeError(); + + J.getMainJITDylib().addGenerator(std::move(*G)); + + return &J.getMainJITDylib(); +} + +Error SetUpExecutorNativePlatform::operator()(LLJIT &J) { + const Triple &TT = J.getTargetTriple(); + ObjectLinkingLayer *ObjLinkingLayer = + dyn_cast(&J.getObjLinkingLayer()); + + if (!ObjLinkingLayer) + return make_error( + "SetUpTargetPlatform requires ObjectLinkingLayer", + inconvertibleErrorCode()); + + std::unique_ptr RuntimeArchiveBuffer; + if (Runtime.index() == 0) { + auto A = errorOrToExpected(MemoryBuffer::getFile(std::get<0>(Runtime))); + if (!A) + return A.takeError(); + RuntimeArchiveBuffer = std::move(*A); + } else + RuntimeArchiveBuffer = std::move(std::get<1>(Runtime)); + + auto &ES = J.getExecutionSession(); + auto PlatformJD = PlatformPrepare(J); + if (!PlatformJD) + return PlatformJD.takeError(); + + // FIXME: ORCPlatformSupport isn't going to work for COFF. + J.setPlatformSupport(std::make_unique(J)); + + switch (TT.getObjectFormat()) { + case Triple::COFF: + if (auto P = COFFPlatform::Create(ES, *ObjLinkingLayer, **PlatformJD, + std::move(RuntimeArchiveBuffer), + LoadAndLinkDynLibrary(J))) + J.getExecutionSession().setPlatform(std::move(*P)); + else + return P.takeError(); + break; + case Triple::ELF: { + auto G = StaticLibraryDefinitionGenerator::Create( + *ObjLinkingLayer, std::move(RuntimeArchiveBuffer), TT); + if (!G) + return G.takeError(); + + if (auto P = ELFNixPlatform::Create(ES, *ObjLinkingLayer, **PlatformJD, + std::move(*G))) + J.getExecutionSession().setPlatform(std::move(*P)); + else + return P.takeError(); + break; + } + case Triple::MachO: { + auto G = StaticLibraryDefinitionGenerator::Create( + *ObjLinkingLayer, std::move(RuntimeArchiveBuffer), TT); + if (!G) + return G.takeError(); + + if (auto P = MachOPlatform::Create(ES, *ObjLinkingLayer, **PlatformJD, + std::move(*G))) + ES.setPlatform(std::move(*P)); + else + return P.takeError(); + break; + } + default: + return make_error("Unsupported object format in triple " + + TT.str(), + inconvertibleErrorCode()); + } + + return Error::success(); } void setUpGenericLLVMIRPlatform(LLJIT &J) { diff --git a/llvm/tools/lli/lli.cpp b/llvm/tools/lli/lli.cpp --- a/llvm/tools/lli/lli.cpp +++ b/llvm/tools/lli/lli.cpp @@ -27,7 +27,6 @@ #include "llvm/ExecutionEngine/ObjectCache.h" #include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h" #include "llvm/ExecutionEngine/Orc/DebugUtils.h" -#include "llvm/ExecutionEngine/Orc/ELFNixPlatform.h" #include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h" #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" #include "llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h" @@ -35,7 +34,6 @@ #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" #include "llvm/ExecutionEngine/Orc/LLJIT.h" -#include "llvm/ExecutionEngine/Orc/MachOPlatform.h" #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" #include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h" #include "llvm/ExecutionEngine/Orc/SymbolStringPool.h" @@ -236,20 +234,22 @@ cl::desc("Do not resolve lli process symbols in JIT'd code"), cl::init(false)); - enum class LLJITPlatform { Inactive, DetectHost, ORC, GenericIR }; - - cl::opt - Platform("lljit-platform", cl::desc("Platform to use with LLJIT"), - cl::init(LLJITPlatform::DetectHost), - cl::values(clEnumValN(LLJITPlatform::DetectHost, "DetectHost", - "Select based on JIT target triple"), - clEnumValN(LLJITPlatform::ORC, "ORC", - "Use ORCPlatform with the ORC runtime"), - clEnumValN(LLJITPlatform::GenericIR, "GenericIR", - "Use LLJITGenericIRPlatform"), - clEnumValN(LLJITPlatform::Inactive, "Inactive", - "Disable platform support explicitly")), - cl::Hidden); + enum class LLJITPlatform { Inactive, Auto, ExecutorNative, GenericIR }; + + cl::opt Platform( + "lljit-platform", cl::desc("Platform to use with LLJIT"), + cl::init(LLJITPlatform::Auto), + cl::values(clEnumValN(LLJITPlatform::Auto, "Auto", + "Like 'ExecutorNative' if ORC runtime " + "provided, otherwise like 'GenericIR'"), + clEnumValN(LLJITPlatform::ExecutorNative, "ExecutorNative", + "Use the native platform for the executor." + "Requires -orc-runtime"), + clEnumValN(LLJITPlatform::GenericIR, "GenericIR", + "Use LLJITGenericIRPlatform"), + clEnumValN(LLJITPlatform::Inactive, "Inactive", + "Disable platform support explicitly")), + cl::Hidden); enum class DumpKind { NoDump, @@ -904,17 +904,39 @@ // Set up LLJIT platform. LLJITPlatform P = Platform; - if (P == LLJITPlatform::DetectHost) { - if (JITLinker == JITLinkerKind::JITLink && !OrcRuntime.empty() && - (TT->isOSBinFormatMachO() || TT->isOSBinFormatELF())) - P = LLJITPlatform::ORC; - else - P = LLJITPlatform::GenericIR; - } + if (P == LLJITPlatform::Auto) + P = OrcRuntime.empty() ? LLJITPlatform::GenericIR + : LLJITPlatform::ExecutorNative; + switch (P) { - case LLJITPlatform::ORC: - Builder.setPlatformSetUp(orc::setUpOrcPlatform); + case LLJITPlatform::ExecutorNative: { + auto PlatformPrepare = [](orc::LLJIT &J) -> Expected { + // Note: We're honouring NoProcessSymbols here, but disabling process + // symbols while using executor-native is guaranteed to fail unless the + // client is using a custom ORC runtime that's fully self contained + // (unlikely). We + // should probably warn here. + if (!NoProcessSymbols) { + orc::MangleAndInterner Mangle(J.getExecutionSession(), + J.getDataLayout()); + auto G = orc::DynamicLibrarySearchGenerator::GetForCurrentProcess( + J.getDataLayout().getGlobalPrefix(), + [MainName = Mangle("main")](const orc::SymbolStringPtr &Name) { + return Name != MainName; + }); + if (!G) + return G.takeError(); + + J.getMainJITDylib().addGenerator(std::move(*G)); + }; + + return &J.getMainJITDylib(); + }; + + Builder.setPlatformSetUp(orc::SetUpExecutorNativePlatform( + OrcRuntime, std::move(PlatformPrepare))); break; + } case LLJITPlatform::GenericIR: // Nothing to do: LLJITBuilder will use this by default. break; @@ -933,7 +955,7 @@ Builder.setObjectLinkingLayerCreator([&EPC, &P](orc::ExecutionSession &ES, const Triple &TT) { auto L = std::make_unique(ES, EPC->getMemMgr()); - if (P != LLJITPlatform::ORC) { + if (P != LLJITPlatform::ExecutorNative) { L->addPlugin(std::make_unique( ES, ExitOnErr(orc::EPCEHFrameRegistrar::Create(ES)))); L->addPlugin(std::make_unique( @@ -972,7 +994,10 @@ // Unless they've been explicitly disabled, make process symbols available to // JIT'd code. - if (!NoProcessSymbols) + // Note: If P is ExecutorNative then we've already done this above. + // FIXME: We should probably automate process-symbol reflection LLJIT since + // clients want it more often than not. + if (P != LLJITPlatform::ExecutorNative && !NoProcessSymbols) J->getMainJITDylib().addGenerator( ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess( J->getDataLayout().getGlobalPrefix(), @@ -985,31 +1010,6 @@ std::make_unique(GenerateBuiltinFunctions, Mangle)); - if (P == LLJITPlatform::ORC) { - if (auto *OLL = llvm::dyn_cast(ObjLayer)) { - auto &ES = J->getExecutionSession(); - if (TT->isOSBinFormatMachO()) { - if (auto P = llvm::orc::MachOPlatform::Create( - ES, *OLL, J->getMainJITDylib(), OrcRuntime.c_str())) - ES.setPlatform(std::move(*P)); - else - ExitOnErr(P.takeError()); - } else if (TT->isOSBinFormatELF()) { - if (auto P = llvm::orc::ELFNixPlatform::Create( - ES, *OLL, J->getMainJITDylib(), OrcRuntime.c_str())) - ES.setPlatform(std::move(*P)); - else - ExitOnErr(P.takeError()); - } else { - errs() << "No ORC platform support\n"; - exit(1); - } - } else { - errs() << "ORC platform requires JITLink\n"; - exit(1); - } - } - // Regular modules are greedy: They materialize as a whole and trigger // materialization for all required symbols recursively. Lazy modules go // through partitioning and they replace outgoing calls with reexport stubs