[PATCH] D130455: [ORC][COFF] Introduce COFFVCRuntimeBootstrapper.
Sunho Kim via Phabricator via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 24 19:23:17 PDT 2022
sunho created this revision.
Herald added subscribers: mstorsjo, hiraditya.
Herald added a project: All.
sunho requested review of this revision.
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/COFFVCRuntimeSupport.h b/llvm/include/llvm/ExecutionEngine/Orc/COFFVCRuntimeSupport.h
new file mode 100644
index 000000000000..4084012a64d9
- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/COFFVCRuntimeSupport.h
@@ -0,0 +1,83 @@
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_COFFCRUNTIMESUPPORT_H
+#define LLVM_EXECUTIONENGINE_ORC_COFFCRUNTIMESUPPORT_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ExecutionEngine/Orc/Core.h"
+#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
+#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
+#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
+
+#include <future>
+#include <memory>
+#include <thread>
+#include <vector>
+
+namespace llvm {
+namespace orc {
+
+/// Bootstraps the vc runtime within jitdylibs.
+class COFFVCRuntimeBootstrapper {
+public:
+ /// A function that will be called with the name of dll file that must be
+ /// loaded.
+ using LoadDynamicLibrary =
+ unique_function<Error(JITDylib &JD, StringRef DLLFileName)>;
+
+ /// Try to create a COFFVCRuntimeBootstrapper instance. An optional
+ /// RuntimePath can be given to specify the location of directory that
+ /// contains all vc runtime library files such as ucrt.lib and msvcrt.lib. If
+ /// not path was given, it will try to search the MSVC toolchain and Windows
+ /// SDK installation and use the found library files automatically.
+ ///
+ /// Note that depending on the build setting, a different library
+ /// file must be used. In general, if vc runtime was statically linked to the
+ /// object file that is to be jit-linked, LoadStaticVCRuntime and
+ /// InitializeStaticVCRuntime must be used with libcmt.lib, libucrt.lib,
+ /// libvcruntimelib. If vc runtime was dynamically linked LoadDynamicVCRuntime
+ /// must be used along with msvcrt.lib, ucrt.lib, vcruntime.lib.
+ ///
+ /// More information is on:
+ /// https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features
+ static Expected<std::unique_ptr<COFFVCRuntimeBootstrapper>>
+ Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
+ LoadDynamicLibrary LoadDynLibrary, const char *RuntimePath = nullptr);
+
+ /// Adds symbol definitions of static version of msvc runtime libraries.
+ Error loadStaticVCRuntime(JITDylib &JD, bool DebugVersion = false);
+
+ /// Runs the initializer of static version of msvc runtime libraries.
+ /// This must be called before calling any functions requiring c runtime (e.g.
+ /// printf) within the jit session. Note that proper initialization of vc
+ /// runtime requires ability of running static initializers. Cosider setting
+ /// up COFFPlatform.
+ Error initializeStaticVCRuntime(JITDylib &JD);
+
+ /// Adds symbol definitions of dynamic versino of msvc runtie libraries.
+ Error loadDynamicVCRuntime(JITDylib &JD, bool DebugVersion = false);
+
+private:
+ COFFVCRuntimeBootstrapper(ExecutionSession &ES,
+ ObjectLinkingLayer &ObjLinkingLayer,
+ LoadDynamicLibrary LoadDynLibrary,
+ const char *RuntimePath);
+
+ ExecutionSession &ES;
+ ObjectLinkingLayer &ObjLinkingLayer;
+ std::string RuntimePath;
+ LoadDynamicLibrary LoadDynLibrary;
+
+ struct MSVCToolchainPath {
+ SmallString<256> VCToolchainLib;
+ SmallString<256> UCRTSdkLib;
+ };
+
+ static Expected<MSVCToolchainPath> getMSVCToolchainPath();
+ Error lLoadVCRuntime(JITDylib &JD, ArrayRef<StringRef> VCLibs,
+ ArrayRef<StringRef> UCRTLibs);
+};
+
+} // namespace orc
+} // namespace llvm
+
+#endif
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
index 105dac8e8d04..98a4a05180c5 100644
- a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
@@ -1,471 +1,490 @@
//===- ExecutorProcessControl.h - Executor process control APIs -*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Utilities for interacting with the executor processes.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H
#define LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"
#include "llvm/ExecutionEngine/Orc/TaskDispatch.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/MSVCErrorWorkarounds.h"
#include <future>
#include <mutex>
#include <vector>
namespace llvm {
namespace orc {
class ExecutionSession;
class SymbolLookupSet;
/// ExecutorProcessControl supports interaction with a JIT target process.
class ExecutorProcessControl {
friend class ExecutionSession;
public:
/// A handler or incoming WrapperFunctionResults -- either return values from
/// callWrapper* calls, or incoming JIT-dispatch requests.
///
/// IncomingWFRHandlers are constructible from
/// unique_function<void(shared::WrapperFunctionResult)>s using the
/// runInPlace function or a RunWithDispatch object.
class IncomingWFRHandler {
friend class ExecutorProcessControl;
public:
IncomingWFRHandler() = default;
explicit operator bool() const { return !!H; }
void operator()(shared::WrapperFunctionResult WFR) { H(std::move(WFR)); }
private:
template <typename FnT> IncomingWFRHandler(FnT &&Fn)
: H(std::forward<FnT>(Fn)) {}
unique_function<void(shared::WrapperFunctionResult)> H;
};
/// Constructs an IncomingWFRHandler from a function object that is callable
/// as void(shared::WrapperFunctionResult). The function object will be called
/// directly. This should be used with care as it may block listener threads
/// in remote EPCs. It is only suitable for simple tasks (e.g. setting a
/// future), or for performing some quick analysis before dispatching "real"
/// work as a Task.
class RunInPlace {
public:
template <typename FnT>
IncomingWFRHandler operator()(FnT &&Fn) {
return IncomingWFRHandler(std::forward<FnT>(Fn));
}
};
/// Constructs an IncomingWFRHandler from a function object by creating a new
/// function object that dispatches the original using a TaskDispatcher,
/// wrapping the original as a GenericNamedTask.
///
/// This is the default approach for running WFR handlers.
class RunAsTask {
public:
RunAsTask(TaskDispatcher &D) : D(D) {}
template <typename FnT>
IncomingWFRHandler operator()(FnT &&Fn) {
return IncomingWFRHandler(
[&D = this->D, Fn = std::move(Fn)]
(shared::WrapperFunctionResult WFR) mutable {
D.dispatch(
makeGenericNamedTask(
[Fn = std::move(Fn), WFR = std::move(WFR)]() mutable {
Fn(std::move(WFR));
}, "WFR handler task"));
});
}
private:
TaskDispatcher &D;
};
/// APIs for manipulating memory in the target process.
class MemoryAccess {
public:
/// Callback function for asynchronous writes.
using WriteResultFn = unique_function<void(Error)>;
virtual ~MemoryAccess();
virtual void writeUInt8sAsync(ArrayRef<tpctypes::UInt8Write> Ws,
WriteResultFn OnWriteComplete) = 0;
virtual void writeUInt16sAsync(ArrayRef<tpctypes::UInt16Write> Ws,
WriteResultFn OnWriteComplete) = 0;
virtual void writeUInt32sAsync(ArrayRef<tpctypes::UInt32Write> Ws,
WriteResultFn OnWriteComplete) = 0;
virtual void writeUInt64sAsync(ArrayRef<tpctypes::UInt64Write> Ws,
WriteResultFn OnWriteComplete) = 0;
virtual void writeBuffersAsync(ArrayRef<tpctypes::BufferWrite> Ws,
WriteResultFn OnWriteComplete) = 0;
Error writeUInt8s(ArrayRef<tpctypes::UInt8Write> Ws) {
std::promise<MSVCPError> ResultP;
auto ResultF = ResultP.get_future();
writeUInt8sAsync(Ws,
[&](Error Err) { ResultP.set_value(std::move(Err)); });
return ResultF.get();
}
Error writeUInt16s(ArrayRef<tpctypes::UInt16Write> Ws) {
std::promise<MSVCPError> ResultP;
auto ResultF = ResultP.get_future();
writeUInt16sAsync(Ws,
[&](Error Err) { ResultP.set_value(std::move(Err)); });
return ResultF.get();
}
Error writeUInt32s(ArrayRef<tpctypes::UInt32Write> Ws) {
std::promise<MSVCPError> ResultP;
auto ResultF = ResultP.get_future();
writeUInt32sAsync(Ws,
[&](Error Err) { ResultP.set_value(std::move(Err)); });
return ResultF.get();
}
Error writeUInt64s(ArrayRef<tpctypes::UInt64Write> Ws) {
std::promise<MSVCPError> ResultP;
auto ResultF = ResultP.get_future();
writeUInt64sAsync(Ws,
[&](Error Err) { ResultP.set_value(std::move(Err)); });
return ResultF.get();
}
Error writeBuffers(ArrayRef<tpctypes::BufferWrite> Ws) {
std::promise<MSVCPError> ResultP;
auto ResultF = ResultP.get_future();
writeBuffersAsync(Ws,
[&](Error Err) { ResultP.set_value(std::move(Err)); });
return ResultF.get();
}
};
/// A pair of a dylib and a set of symbols to be looked up.
struct LookupRequest {
LookupRequest(tpctypes::DylibHandle Handle, const SymbolLookupSet &Symbols)
: Handle(Handle), Symbols(Symbols) {}
tpctypes::DylibHandle Handle;
const SymbolLookupSet &Symbols;
};
/// Contains the address of the dispatch function and context that the ORC
/// runtime can use to call functions in the JIT.
struct JITDispatchInfo {
ExecutorAddr JITDispatchFunction;
ExecutorAddr JITDispatchContext;
};
ExecutorProcessControl(std::shared_ptr<SymbolStringPool> SSP,
std::unique_ptr<TaskDispatcher> D)
: SSP(std::move(SSP)), D(std::move(D)) {}
virtual ~ExecutorProcessControl();
/// Return the ExecutionSession associated with this instance.
/// Not callable until the ExecutionSession has been associated.
ExecutionSession &getExecutionSession() {
assert(ES && "No ExecutionSession associated yet");
return *ES;
}
/// Intern a symbol name in the SymbolStringPool.
SymbolStringPtr intern(StringRef SymName) { return SSP->intern(SymName); }
/// Return a shared pointer to the SymbolStringPool for this instance.
std::shared_ptr<SymbolStringPool> getSymbolStringPool() const { return SSP; }
TaskDispatcher &getDispatcher() { return *D; }
/// Return the Triple for the target process.
const Triple &getTargetTriple() const { return TargetTriple; }
/// Get the page size for the target process.
unsigned getPageSize() const { return PageSize; }
/// Get the JIT dispatch function and context address for the executor.
const JITDispatchInfo &getJITDispatchInfo() const { return JDI; }
/// Return a MemoryAccess object for the target process.
MemoryAccess &getMemoryAccess() const {
assert(MemAccess && "No MemAccess object set.");
return *MemAccess;
}
/// Return a JITLinkMemoryManager for the target process.
jitlink::JITLinkMemoryManager &getMemMgr() const {
assert(MemMgr && "No MemMgr object set");
return *MemMgr;
}
/// Returns the bootstrap symbol map.
const StringMap<ExecutorAddr> &getBootstrapSymbolsMap() const {
return BootstrapSymbols;
}
/// For each (ExecutorAddr&, StringRef) pair, looks up the string in the
/// bootstrap symbols map and writes its address to the ExecutorAddr if
/// found. If any symbol is not found then the function returns an error.
Error getBootstrapSymbols(
ArrayRef<std::pair<ExecutorAddr &, StringRef>> Pairs) const {
for (auto &KV : Pairs) {
auto I = BootstrapSymbols.find(KV.second);
if (I == BootstrapSymbols.end())
return make_error<StringError>("Symbol \"" + KV.second +
"\" not found "
"in bootstrap symbols map",
inconvertibleErrorCode());
KV.first = I->second;
}
return Error::success();
}
/// Load the dynamic library at the given path and return a handle to it.
/// If LibraryPath is null this function will return the global handle for
/// the target process.
virtual Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) = 0;
/// Search for symbols in the target process.
///
/// The result of the lookup is a 2-dimentional array of target addresses
/// that correspond to the lookup order. If a required symbol is not
/// found then this method will return an error. If a weakly referenced
/// symbol is not found then it be assigned a '0' value.
virtual Expected<std::vector<tpctypes::LookupResult>>
lookupSymbols(ArrayRef<LookupRequest> Request) = 0;
/// Run function with a main-like signature.
virtual Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) = 0;
+ /// Run function with a int (*)(void) signature.
+ virtual Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) = 0;
+
+ /// Run function with a int (*)(int) signature.
+ virtual Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr,
+ int Arg) = 0;
+
/// Run a wrapper function in the executor. The given WFRHandler will be
/// called on the result when it is returned.
///
/// The wrapper function should be callable as:
///
/// \code{.cpp}
/// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size);
/// \endcode{.cpp}
virtual void callWrapperAsync(ExecutorAddr WrapperFnAddr,
IncomingWFRHandler OnComplete,
ArrayRef<char> ArgBuffer) = 0;
/// Run a wrapper function in the executor using the given Runner to dispatch
/// OnComplete when the result is ready.
template <typename RunPolicyT, typename FnT>
void callWrapperAsync(RunPolicyT &&Runner, ExecutorAddr WrapperFnAddr,
FnT &&OnComplete, ArrayRef<char> ArgBuffer) {
callWrapperAsync(
WrapperFnAddr, Runner(std::forward<FnT>(OnComplete)), ArgBuffer);
}
/// Run a wrapper function in the executor. OnComplete will be dispatched
/// as a GenericNamedTask using this instance's TaskDispatch object.
template <typename FnT>
void callWrapperAsync(ExecutorAddr WrapperFnAddr, FnT &&OnComplete,
ArrayRef<char> ArgBuffer) {
callWrapperAsync(RunAsTask(*D), WrapperFnAddr,
std::forward<FnT>(OnComplete), ArgBuffer);
}
/// Run a wrapper function in the executor. The wrapper function should be
/// callable as:
///
/// \code{.cpp}
/// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size);
/// \endcode{.cpp}
shared::WrapperFunctionResult callWrapper(ExecutorAddr WrapperFnAddr,
ArrayRef<char> ArgBuffer) {
std::promise<shared::WrapperFunctionResult> RP;
auto RF = RP.get_future();
callWrapperAsync(
RunInPlace(), WrapperFnAddr,
[&](shared::WrapperFunctionResult R) {
RP.set_value(std::move(R));
}, ArgBuffer);
return RF.get();
}
/// Run a wrapper function using SPS to serialize the arguments and
/// deserialize the results.
template <typename SPSSignature, typename RunPolicyT, typename SendResultT,
typename... ArgTs>
void callSPSWrapperAsync(RunPolicyT &&Runner, ExecutorAddr WrapperFnAddr,
SendResultT &&SendResult, const ArgTs &...Args) {
shared::WrapperFunction<SPSSignature>::callAsync(
[this, WrapperFnAddr, Runner = std::move(Runner)]
(auto &&SendResult, const char *ArgData, size_t ArgSize) mutable {
this->callWrapperAsync(std::move(Runner), WrapperFnAddr,
std::move(SendResult),
ArrayRef<char>(ArgData, ArgSize));
},
std::forward<SendResultT>(SendResult), Args...);
}
/// Run a wrapper function using SPS to serialize the arguments and
/// deserialize the results.
template <typename SPSSignature, typename SendResultT, typename... ArgTs>
void callSPSWrapperAsync(ExecutorAddr WrapperFnAddr, SendResultT &&SendResult,
const ArgTs &...Args) {
callSPSWrapperAsync<SPSSignature>(RunAsTask(*D), WrapperFnAddr,
std::forward<SendResultT>(SendResult),
Args...);
}
/// Run a wrapper function using SPS to serialize the arguments and
/// deserialize the results.
///
/// If SPSSignature is a non-void function signature then the second argument
/// (the first in the Args list) should be a reference to a return value.
template <typename SPSSignature, typename... WrapperCallArgTs>
Error callSPSWrapper(ExecutorAddr WrapperFnAddr,
WrapperCallArgTs &&...WrapperCallArgs) {
return shared::WrapperFunction<SPSSignature>::call(
[this, WrapperFnAddr](const char *ArgData, size_t ArgSize) {
return callWrapper(WrapperFnAddr, ArrayRef<char>(ArgData, ArgSize));
},
std::forward<WrapperCallArgTs>(WrapperCallArgs)...);
}
/// Disconnect from the target process.
///
/// This should be called after the JIT session is shut down.
virtual Error disconnect() = 0;
protected:
std::shared_ptr<SymbolStringPool> SSP;
std::unique_ptr<TaskDispatcher> D;
ExecutionSession *ES = nullptr;
Triple TargetTriple;
unsigned PageSize = 0;
JITDispatchInfo JDI;
MemoryAccess *MemAccess = nullptr;
jitlink::JITLinkMemoryManager *MemMgr = nullptr;
StringMap<ExecutorAddr> BootstrapSymbols;
};
/// A ExecutorProcessControl instance that asserts if any of its methods are
/// used. Suitable for use is unit tests, and by ORC clients who haven't moved
/// to ExecutorProcessControl-based APIs yet.
class UnsupportedExecutorProcessControl : public ExecutorProcessControl {
public:
UnsupportedExecutorProcessControl(
std::shared_ptr<SymbolStringPool> SSP = nullptr,
std::unique_ptr<TaskDispatcher> D = nullptr,
const std::string &TT = "", unsigned PageSize = 0)
: ExecutorProcessControl(SSP ? std::move(SSP)
: std::make_shared<SymbolStringPool>(),
D ? std::move(D)
: std::make_unique<InPlaceTaskDispatcher>()) {
this->TargetTriple = Triple(TT);
this->PageSize = PageSize;
}
Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) override {
llvm_unreachable("Unsupported");
}
Expected<std::vector<tpctypes::LookupResult>>
lookupSymbols(ArrayRef<LookupRequest> Request) override {
llvm_unreachable("Unsupported");
}
Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) override {
llvm_unreachable("Unsupported");
}
+ Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) override {
+ llvm_unreachable("Unsupported");
+ }
+
+ Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) override {
+ llvm_unreachable("Unsupported");
+ }
+
void callWrapperAsync(ExecutorAddr WrapperFnAddr,
IncomingWFRHandler OnComplete,
ArrayRef<char> ArgBuffer) override {
llvm_unreachable("Unsupported");
}
Error disconnect() override { return Error::success(); }
};
/// A ExecutorProcessControl implementation targeting the current process.
class SelfExecutorProcessControl
: public ExecutorProcessControl,
private ExecutorProcessControl::MemoryAccess {
public:
SelfExecutorProcessControl(
std::shared_ptr<SymbolStringPool> SSP, std::unique_ptr<TaskDispatcher> D,
Triple TargetTriple, unsigned PageSize,
std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr);
/// Create a SelfExecutorProcessControl with the given symbol string pool and
/// memory manager.
/// If no symbol string pool is given then one will be created.
/// If no memory manager is given a jitlink::InProcessMemoryManager will
/// be created and used by default.
static Expected<std::unique_ptr<SelfExecutorProcessControl>>
Create(std::shared_ptr<SymbolStringPool> SSP = nullptr,
std::unique_ptr<TaskDispatcher> D = nullptr,
std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr = nullptr);
Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) override;
Expected<std::vector<tpctypes::LookupResult>>
lookupSymbols(ArrayRef<LookupRequest> Request) override;
Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) override;
+ Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) override;
+
+ Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) override;
+
void callWrapperAsync(ExecutorAddr WrapperFnAddr,
IncomingWFRHandler OnComplete,
ArrayRef<char> ArgBuffer) override;
Error disconnect() override;
private:
void writeUInt8sAsync(ArrayRef<tpctypes::UInt8Write> Ws,
WriteResultFn OnWriteComplete) override;
void writeUInt16sAsync(ArrayRef<tpctypes::UInt16Write> Ws,
WriteResultFn OnWriteComplete) override;
void writeUInt32sAsync(ArrayRef<tpctypes::UInt32Write> Ws,
WriteResultFn OnWriteComplete) override;
void writeUInt64sAsync(ArrayRef<tpctypes::UInt64Write> Ws,
WriteResultFn OnWriteComplete) override;
void writeBuffersAsync(ArrayRef<tpctypes::BufferWrite> Ws,
WriteResultFn OnWriteComplete) override;
static shared::CWrapperFunctionResult
jitDispatchViaWrapperFunctionManager(void *Ctx, const void *FnTag,
const char *Data, size_t Size);
std::unique_ptr<jitlink::JITLinkMemoryManager> OwnedMemMgr;
char GlobalManglingPrefix = 0;
std::vector<std::unique_ptr<sys::DynamicLibrary>> DynamicLibraries;
};
} // end namespace orc
} // end namespace llvm
#endif // LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H
diff --git a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
index 1c9b9e8a1b31..06d0401fff11 100644
- a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
@@ -1,74 +1,76 @@
add_llvm_component_library(LLVMOrcJIT
+ COFFVCRuntimeSupport.cpp
CompileOnDemandLayer.cpp
CompileUtils.cpp
Core.cpp
DebugObjectManagerPlugin.cpp
DebuggerSupportPlugin.cpp
DebugUtils.cpp
EPCDynamicLibrarySearchGenerator.cpp
EPCDebugObjectRegistrar.cpp
EPCEHFrameRegistrar.cpp
EPCGenericDylibManager.cpp
EPCGenericJITLinkMemoryManager.cpp
EPCGenericRTDyldMemoryManager.cpp
EPCIndirectionUtils.cpp
ExecutionUtils.cpp
ObjectFileInterface.cpp
IndirectionUtils.cpp
IRCompileLayer.cpp
IRTransformLayer.cpp
JITTargetMachineBuilder.cpp
LazyReexports.cpp
Layer.cpp
LookupAndRecordAddrs.cpp
LLJIT.cpp
MachOPlatform.cpp
MemoryMapper.cpp
ELFNixPlatform.cpp
Mangling.cpp
ObjectLinkingLayer.cpp
ObjectTransformLayer.cpp
OrcABISupport.cpp
OrcV2CBindings.cpp
RTDyldObjectLinkingLayer.cpp
SimpleRemoteEPC.cpp
Speculation.cpp
SpeculateAnalyses.cpp
ExecutorProcessControl.cpp
TaskDispatch.cpp
ThreadSafeModule.cpp
ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/ExecutionEngine/Orc
DEPENDS
intrinsics_gen
LINK_LIBS
${LLVM_PTHREAD_LIB}
LINK_COMPONENTS
Core
ExecutionEngine
JITLink
Object
OrcShared
OrcTargetProcess
+ WindowsDriver
MC
MCDisassembler
Passes
RuntimeDyld
Support
Target
TransformUtils
)
add_subdirectory(Shared)
add_subdirectory(TargetProcess)
target_link_libraries(LLVMOrcJIT
PRIVATE
LLVMAnalysis
LLVMBitReader
LLVMBitWriter
LLVMPasses
)
diff --git a/llvm/lib/ExecutionEngine/Orc/COFFVCRuntimeSupport.cpp b/llvm/lib/ExecutionEngine/Orc/COFFVCRuntimeSupport.cpp
new file mode 100644
index 000000000000..a8b65fa3b4a8
- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/COFFVCRuntimeSupport.cpp
@@ -0,0 +1,178 @@
+#include "llvm/ExecutionEngine/Orc/COFFVCRuntimeSupport.h"
+
+#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
+#include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include "llvm/WindowsDriver/MSVCPaths.h"
+
+#define DEBUG_TYPE "orc"
+
+using namespace llvm;
+using namespace llvm::orc;
+using namespace llvm::orc::shared;
+
+Expected<std::unique_ptr<COFFVCRuntimeBootstrapper>>
+COFFVCRuntimeBootstrapper::Create(ExecutionSession &ES,
+ ObjectLinkingLayer &ObjLinkingLayer,
+ LoadDynamicLibrary LoadDynLibrary,
+ const char *RuntimePath) {
+ return std::unique_ptr<COFFVCRuntimeBootstrapper>(
+ new COFFVCRuntimeBootstrapper(ES, ObjLinkingLayer,
+ std::move(LoadDynLibrary), RuntimePath));
+}
+
+COFFVCRuntimeBootstrapper::COFFVCRuntimeBootstrapper(
+ ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer,
+ LoadDynamicLibrary LoadDynLibrary, const char *RuntimePath)
+ : ES(ES), ObjLinkingLayer(ObjLinkingLayer),
+ LoadDynLibrary(std::move(LoadDynLibrary)) {
+ if (RuntimePath)
+ this->RuntimePath = RuntimePath;
+}
+
+Error COFFVCRuntimeBootstrapper::loadStaticVCRuntime(JITDylib &JD,
+ bool DebugVersion) {
+ StringRef VCLibs[] = {"libvcruntime.lib", "libcmt.lib", "libcpmt.lib"};
+ StringRef UCRTLibs[] = {"libucrt.lib"};
+ if (auto Err =
+ lLoadVCRuntime(JD, makeArrayRef(VCLibs), makeArrayRef(UCRTLibs)))
+ return Err;
+
+ return Error::success();
+}
+
+Error COFFVCRuntimeBootstrapper::loadDynamicVCRuntime(JITDylib &JD,
+ bool DebugVersion) {
+ StringRef VCLibs[] = {"vcruntime.lib", "msvcrt.lib", "msvcprt.lib"};
+ StringRef UCRTLibs[] = {"ucrt.lib"};
+ return lLoadVCRuntime(JD, makeArrayRef(VCLibs), makeArrayRef(UCRTLibs));
+}
+
+Error COFFVCRuntimeBootstrapper::lLoadVCRuntime(JITDylib &JD,
+ ArrayRef<StringRef> VCLibs,
+ ArrayRef<StringRef> UCRTLibs) {
+ MSVCToolchainPath Path;
+ if (!RuntimePath.empty()) {
+ Path.UCRTSdkLib = RuntimePath;
+ Path.VCToolchainLib = RuntimePath;
+ } else {
+ auto ToolchainPath = getMSVCToolchainPath();
+ if (!ToolchainPath)
+ return ToolchainPath.takeError();
+ Path = *ToolchainPath;
+ }
+ LLVM_DEBUG({
+ dbgs() << "Using VC toolchain pathes\n";
+ dbgs() << " VC toolchain path: " << Path.VCToolchainLib << "\n";
+ dbgs() << " UCRT path: " << Path.UCRTSdkLib << "\n";
+ });
+
+ std::vector<std::string> ImportedLibraries;
+ auto LoadLibrary = [&](SmallString<256> LibPath, StringRef LibName) -> Error {
+ sys::path::append(LibPath, LibName);
+
+ auto G = StaticLibraryDefinitionGenerator::Load(ObjLinkingLayer,
+ LibPath.c_str());
+ if (!G)
+ return G.takeError();
+
+ for (auto &Lib : (*G)->getImportedDynamicLibraries())
+ ImportedLibraries.push_back(Lib);
+
+ JD.addGenerator(std::move(*G));
+
+ return Error::success();
+ };
+ for (auto &Lib : UCRTLibs)
+ if (auto Err = LoadLibrary(Path.UCRTSdkLib, Lib))
+ return Err;
+
+ for (auto &Lib : VCLibs)
+ if (auto Err = LoadLibrary(Path.VCToolchainLib, Lib))
+ return Err;
+ ImportedLibraries.push_back("ntdll.dll");
+ ImportedLibraries.push_back("Kernel32.dll");
+
+ for (auto &Lib : ImportedLibraries)
+ if (auto Err = LoadDynLibrary(JD, Lib))
+ return Err;
+
+ return Error::success();
+}
+
+Error COFFVCRuntimeBootstrapper::initializeStaticVCRuntime(JITDylib &JD) {
+ ExecutorAddr jit_scrt_initialize, jit_scrt_dllmain_before_initialize_c,
+ jit_scrt_initialize_type_info,
+ jit_scrt_initialize_default_local_stdio_options;
+ if (auto Err = lookupAndRecordAddrs(
+ ES, LookupKind::Static, makeJITDylibSearchOrder(&JD),
+ {{ES.intern("__scrt_initialize_crt"), &jit_scrt_initialize},
+ {ES.intern("__scrt_dllmain_before_initialize_c"),
+ &jit_scrt_dllmain_before_initialize_c},
+ {ES.intern("?__scrt_initialize_type_info@@YAXXZ"),
+ &jit_scrt_initialize_type_info},
+ {ES.intern("__scrt_initialize_default_local_stdio_options"),
+ &jit_scrt_initialize_default_local_stdio_options}}))
+ return Err;
+
+ auto RunVoidInitFunc = [&](ExecutorAddr Addr) -> Error {
+ if (auto Res = ES.getExecutorProcessControl().runAsVoidFunction(Addr))
+ return Error::success();
+ else
+ return Res.takeError();
+ };
+
+ auto R =
+ ES.getExecutorProcessControl().runAsIntFunction(jit_scrt_initialize, 0);
+ if (!R)
+ return R.takeError();
+
+ if (auto Err = RunVoidInitFunc(jit_scrt_dllmain_before_initialize_c))
+ return Err;
+
+ if (auto Err = RunVoidInitFunc(jit_scrt_initialize_type_info))
+ return Err;
+
+ if (auto Err =
+ RunVoidInitFunc(jit_scrt_initialize_default_local_stdio_options))
+ return Err;
+
+ SymbolAliasMap Alias;
+ Alias[ES.intern("__run_after_c_init")] = {
+ ES.intern("__scrt_dllmain_after_initialize_c"), JITSymbolFlags::Exported};
+ if (auto Err = JD.define(symbolAliases(Alias)))
+ return Err;
+
+ return Error::success();
+}
+
+Expected<COFFVCRuntimeBootstrapper::MSVCToolchainPath>
+COFFVCRuntimeBootstrapper::getMSVCToolchainPath() {
+ std::string VCToolChainPath;
+ ToolsetLayout VSLayout;
+ IntrusiveRefCntPtr<vfs::FileSystem> VFS = vfs::getRealFileSystem();
+ if (!findVCToolChainViaCommandLine(*VFS, None, None, None, VCToolChainPath,
+ VSLayout) &&
+ !findVCToolChainViaEnvironment(*VFS, VCToolChainPath, VSLayout) &&
+ !findVCToolChainViaSetupConfig(*VFS, VCToolChainPath, VSLayout) &&
+ !findVCToolChainViaRegistry(VCToolChainPath, VSLayout))
+ return make_error<StringError>("Couldn't find msvc toolchain.",
+ inconvertibleErrorCode());
+
+ std::string UniversalCRTSdkPath;
+ std::string UCRTVersion;
+ if (!getUniversalCRTSdkDir(*VFS, None, None, None, UniversalCRTSdkPath,
+ UCRTVersion))
+ return make_error<StringError>("Couldn't find universal sdk.",
+ inconvertibleErrorCode());
+
+ MSVCToolchainPath ToolchainPath;
+ SmallString<256> VCToolchainLib(VCToolChainPath);
+ sys::path::append(VCToolchainLib, "lib", "x64");
+ ToolchainPath.VCToolchainLib = VCToolchainLib;
+
+ SmallString<256> UCRTSdkLib(UniversalCRTSdkPath);
+ sys::path::append(UCRTSdkLib, "Lib", UCRTVersion, "ucrt", "x64");
+ ToolchainPath.UCRTSdkLib = UCRTSdkLib;
+ return ToolchainPath;
+}
diff --git a/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp b/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp
index 412b9f95ea62..ea78c15b02e6 100644
- a/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp
@@ -1,197 +1,209 @@
//===---- ExecutorProcessControl.cpp -- Executor process control APIs -----===//
//
// 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/ExecutionEngine/Orc/ExecutorProcessControl.h"
#include "llvm/ExecutionEngine/Orc/Core.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Process.h"
#define DEBUG_TYPE "orc"
namespace llvm {
namespace orc {
ExecutorProcessControl::MemoryAccess::~MemoryAccess() = default;
ExecutorProcessControl::~ExecutorProcessControl() = default;
SelfExecutorProcessControl::SelfExecutorProcessControl(
std::shared_ptr<SymbolStringPool> SSP, std::unique_ptr<TaskDispatcher> D,
Triple TargetTriple, unsigned PageSize,
std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr)
: ExecutorProcessControl(std::move(SSP), std::move(D)) {
OwnedMemMgr = std::move(MemMgr);
if (!OwnedMemMgr)
OwnedMemMgr = std::make_unique<jitlink::InProcessMemoryManager>(
sys::Process::getPageSizeEstimate());
this->TargetTriple = std::move(TargetTriple);
this->PageSize = PageSize;
this->MemMgr = OwnedMemMgr.get();
this->MemAccess = this;
this->JDI = {ExecutorAddr::fromPtr(jitDispatchViaWrapperFunctionManager),
ExecutorAddr::fromPtr(this)};
if (this->TargetTriple.isOSBinFormatMachO())
GlobalManglingPrefix = '_';
}
Expected<std::unique_ptr<SelfExecutorProcessControl>>
SelfExecutorProcessControl::Create(
std::shared_ptr<SymbolStringPool> SSP,
std::unique_ptr<TaskDispatcher> D,
std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr) {
if (!SSP)
SSP = std::make_shared<SymbolStringPool>();
if (!D) {
#if LLVM_ENABLE_THREADS
D = std::make_unique<DynamicThreadPoolTaskDispatcher>();
#else
D = std::make_unique<InPlaceTaskDispatcher>();
#endif
}
auto PageSize = sys::Process::getPageSize();
if (!PageSize)
return PageSize.takeError();
Triple TT(sys::getProcessTriple());
return std::make_unique<SelfExecutorProcessControl>(
std::move(SSP), std::move(D), std::move(TT), *PageSize,
std::move(MemMgr));
}
Expected<tpctypes::DylibHandle>
SelfExecutorProcessControl::loadDylib(const char *DylibPath) {
std::string ErrMsg;
auto Dylib = std::make_unique<sys::DynamicLibrary>(
sys::DynamicLibrary::getPermanentLibrary(DylibPath, &ErrMsg));
if (!Dylib->isValid())
return make_error<StringError>(std::move(ErrMsg), inconvertibleErrorCode());
DynamicLibraries.push_back(std::move(Dylib));
return pointerToJITTargetAddress(DynamicLibraries.back().get());
}
Expected<std::vector<tpctypes::LookupResult>>
SelfExecutorProcessControl::lookupSymbols(ArrayRef<LookupRequest> Request) {
std::vector<tpctypes::LookupResult> R;
for (auto &Elem : Request) {
auto *Dylib = jitTargetAddressToPointer<sys::DynamicLibrary *>(Elem.Handle);
assert(llvm::any_of(DynamicLibraries,
[=](const std::unique_ptr<sys::DynamicLibrary> &DL) {
return DL.get() == Dylib;
}) &&
"Invalid handle");
R.push_back(std::vector<JITTargetAddress>());
for (auto &KV : Elem.Symbols) {
auto &Sym = KV.first;
std::string Tmp((*Sym).data() + !!GlobalManglingPrefix,
(*Sym).size() - !!GlobalManglingPrefix);
void *Addr = Dylib->getAddressOfSymbol(Tmp.c_str());
if (!Addr && KV.second == SymbolLookupFlags::RequiredSymbol) {
// FIXME: Collect all failing symbols before erroring out.
SymbolNameVector MissingSymbols;
MissingSymbols.push_back(Sym);
return make_error<SymbolsNotFound>(SSP, std::move(MissingSymbols));
}
R.back().push_back(pointerToJITTargetAddress(Addr));
}
}
return R;
}
Expected<int32_t>
SelfExecutorProcessControl::runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) {
using MainTy = int (*)(int, char *[]);
return orc::runAsMain(MainFnAddr.toPtr<MainTy>(), Args);
}
+Expected<int32_t>
+SelfExecutorProcessControl::runAsVoidFunction(ExecutorAddr VoidFnAddr) {
+ using VoidTy = int (*)();
+ return VoidFnAddr.toPtr<VoidTy>()();
+}
+
+Expected<int32_t>
+SelfExecutorProcessControl::runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) {
+ using IntTy = int (*)(int);
+ return IntFnAddr.toPtr<IntTy>()(Arg);
+}
+
void SelfExecutorProcessControl::callWrapperAsync(ExecutorAddr WrapperFnAddr,
IncomingWFRHandler SendResult,
ArrayRef<char> ArgBuffer) {
using WrapperFnTy =
shared::CWrapperFunctionResult (*)(const char *Data, size_t Size);
auto *WrapperFn = WrapperFnAddr.toPtr<WrapperFnTy>();
SendResult(WrapperFn(ArgBuffer.data(), ArgBuffer.size()));
}
Error SelfExecutorProcessControl::disconnect() {
D->shutdown();
return Error::success();
}
void SelfExecutorProcessControl::writeUInt8sAsync(
ArrayRef<tpctypes::UInt8Write> Ws, WriteResultFn OnWriteComplete) {
for (auto &W : Ws)
*W.Addr.toPtr<uint8_t *>() = W.Value;
OnWriteComplete(Error::success());
}
void SelfExecutorProcessControl::writeUInt16sAsync(
ArrayRef<tpctypes::UInt16Write> Ws, WriteResultFn OnWriteComplete) {
for (auto &W : Ws)
*W.Addr.toPtr<uint16_t *>() = W.Value;
OnWriteComplete(Error::success());
}
void SelfExecutorProcessControl::writeUInt32sAsync(
ArrayRef<tpctypes::UInt32Write> Ws, WriteResultFn OnWriteComplete) {
for (auto &W : Ws)
*W.Addr.toPtr<uint32_t *>() = W.Value;
OnWriteComplete(Error::success());
}
void SelfExecutorProcessControl::writeUInt64sAsync(
ArrayRef<tpctypes::UInt64Write> Ws, WriteResultFn OnWriteComplete) {
for (auto &W : Ws)
*W.Addr.toPtr<uint64_t *>() = W.Value;
OnWriteComplete(Error::success());
}
void SelfExecutorProcessControl::writeBuffersAsync(
ArrayRef<tpctypes::BufferWrite> Ws, WriteResultFn OnWriteComplete) {
for (auto &W : Ws)
memcpy(W.Addr.toPtr<char *>(), W.Buffer.data(), W.Buffer.size());
OnWriteComplete(Error::success());
}
shared::CWrapperFunctionResult
SelfExecutorProcessControl::jitDispatchViaWrapperFunctionManager(
void *Ctx, const void *FnTag, const char *Data, size_t Size) {
LLVM_DEBUG({
dbgs() << "jit-dispatch call with tag " << FnTag << " and " << Size
<< " byte payload.\n";
});
std::promise<shared::WrapperFunctionResult> ResultP;
auto ResultF = ResultP.get_future();
static_cast<SelfExecutorProcessControl *>(Ctx)
->getExecutionSession()
.runJITDispatchHandler(
[ResultP = std::move(ResultP)](
shared::WrapperFunctionResult Result) mutable {
ResultP.set_value(std::move(Result));
},
pointerToJITTargetAddress(FnTag), {Data, Size});
return ResultF.get().release();
}
} // end namespace orc
} // end namespace llvm
https://reviews.llvm.org/D130455
Files:
llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp
Index: llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp
===================================================================
--- llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp
+++ llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp
@@ -121,6 +121,18 @@
return orc::runAsMain(MainFnAddr.toPtr<MainTy>(), Args);
}
+Expected<int32_t>
+SelfExecutorProcessControl::runAsVoidFunction(ExecutorAddr VoidFnAddr) {
+ using VoidTy = int (*)();
+ return VoidFnAddr.toPtr<VoidTy>()();
+}
+
+Expected<int32_t>
+SelfExecutorProcessControl::runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) {
+ using IntTy = int (*)(int);
+ return IntFnAddr.toPtr<IntTy>()(Arg);
+}
+
void SelfExecutorProcessControl::callWrapperAsync(ExecutorAddr WrapperFnAddr,
IncomingWFRHandler SendResult,
ArrayRef<char> ArgBuffer) {
Index: llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
===================================================================
--- llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
+++ llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
@@ -259,6 +259,13 @@
virtual Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) = 0;
+ /// Run function with a int (*)(void) signature.
+ virtual Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) = 0;
+
+ /// Run function with a int (*)(int) signature.
+ virtual Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr,
+ int Arg) = 0;
+
/// Run a wrapper function in the executor. The given WFRHandler will be
/// called on the result when it is returned.
///
@@ -397,6 +404,14 @@
llvm_unreachable("Unsupported");
}
+ Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) override {
+ llvm_unreachable("Unsupported");
+ }
+
+ Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) override {
+ llvm_unreachable("Unsupported");
+ }
+
void callWrapperAsync(ExecutorAddr WrapperFnAddr,
IncomingWFRHandler OnComplete,
ArrayRef<char> ArgBuffer) override {
@@ -434,6 +449,10 @@
Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) override;
+ Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) override;
+
+ Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) override;
+
void callWrapperAsync(ExecutorAddr WrapperFnAddr,
IncomingWFRHandler OnComplete,
ArrayRef<char> ArgBuffer) override;
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D130455.447179.patch
Type: text/x-patch
Size: 2759 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20220725/51c0f2cc/attachment.bin>
More information about the llvm-commits
mailing list