[ORC][examples] Port LLJITWithRemoteDebugging to SimpleRemoteEPC

Though this is a full port of the example, it is not yet fully functional due to a threading issue in the SimpleRemoteEPC implementation. The issue was discussed in D110530, but it needs a more thorough solution. For now we are dropping the dependency to the old `OrcRPC` here (it's been the last use-case in-tree). The test for the example is under review in ... and will be re-enabled once the threading issue is solved.
This commit is contained in:
Stefan Gränitz 2021-09-27 14:36:50 +02:00
parent 1380eae590
commit ac2daacb31
3 changed files with 124 additions and 346 deletions

View File

@ -79,6 +79,8 @@
#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
@ -116,9 +118,9 @@ static cl::opt<std::string>
OOPExecutor("executor", cl::desc("Set the out-of-process executor"),
cl::value_desc("filename"));
// Network address of a running executor process that we can connected through a
// TCP socket. It may run locally or on a remote machine.
static cl::opt<std::string> OOPExecutorConnect(
// Network address of a running executor process that we can connect via TCP. It
// may run locally or on a remote machine.
static cl::opt<std::string> OOPExecutorConnectTCP(
"connect",
cl::desc("Connect to an out-of-process executor through a TCP socket"),
cl::value_desc("<hostname>:<port>"));
@ -133,41 +135,6 @@ static cl::opt<bool>
ExitOnError ExitOnErr;
static std::unique_ptr<JITLinkExecutor> connectExecutor(const char *Argv0) {
// Connect to a running out-of-process executor through a TCP socket.
if (!OOPExecutorConnect.empty()) {
std::unique_ptr<TCPSocketJITLinkExecutor> Exec =
ExitOnErr(JITLinkExecutor::ConnectTCPSocket(OOPExecutorConnect,
std::ref(ExitOnErr)));
outs() << "Connected to executor at " << OOPExecutorConnect << "\n";
if (WaitForDebugger) {
outs() << "Attach a debugger and press any key to continue.\n";
fflush(stdin);
getchar();
}
return std::move(Exec);
}
// Launch a out-of-process executor locally in a child process.
std::unique_ptr<ChildProcessJITLinkExecutor> Exec = ExitOnErr(
OOPExecutor.empty() ? JITLinkExecutor::FindLocal(Argv0)
: JITLinkExecutor::CreateLocal(OOPExecutor));
outs() << "Found out-of-process executor: " << Exec->getPath() << "\n";
ExitOnErr(Exec->launch(std::ref(ExitOnErr)));
if (WaitForDebugger) {
outs() << "Launched executor in subprocess: " << Exec->getPID() << "\n"
<< "Attach a debugger and press any key to continue.\n";
fflush(stdin);
getchar();
}
return std::move(Exec);
}
int main(int argc, char *argv[]) {
InitLLVM X(argc, argv);
@ -177,8 +144,27 @@ int main(int argc, char *argv[]) {
ExitOnErr.setBanner(std::string(argv[0]) + ": ");
cl::ParseCommandLineOptions(argc, argv, "LLJITWithRemoteDebugging");
// Launch/connect the out-of-process executor.
std::unique_ptr<JITLinkExecutor> Executor = connectExecutor(argv[0]);
std::unique_ptr<SimpleRemoteEPC> EPC;
if (OOPExecutorConnectTCP.getNumOccurrences() > 0) {
// Connect to a running out-of-process executor through a TCP socket.
EPC = ExitOnErr(connectTCPSocket(OOPExecutorConnectTCP));
outs() << "Connected to executor at " << OOPExecutorConnectTCP << "\n";
} else {
// Launch an out-of-process executor locally in a child process.
std::string Path =
OOPExecutor.empty() ? findLocalExecutor(argv[0]) : OOPExecutor;
outs() << "Found out-of-process executor: " << Path << "\n";
uint64_t PID;
std::tie(EPC, PID) = ExitOnErr(launchLocalExecutor(Path));
outs() << "Launched executor in subprocess: " << PID << "\n";
}
if (WaitForDebugger) {
outs() << "Attach a debugger and press any key to continue.\n";
fflush(stdin);
getchar();
}
// Load the given IR files.
std::vector<ThreadSafeModule> TSMs;
@ -211,44 +197,51 @@ int main(int argc, char *argv[]) {
JTMB.setRelocationModel(Reloc::PIC_);
// Create LLJIT and destroy it before disconnecting the target process.
outs() << "Initializing LLJIT for remote executor\n";
auto J = ExitOnErr(LLJITBuilder()
.setExecutorProcessControl(std::move(EPC))
.setJITTargetMachineBuilder(std::move(JTMB))
.setObjectLinkingLayerCreator([&](auto &ES, const auto &TT) {
return std::make_unique<ObjectLinkingLayer>(ES);
})
.create());
// Add plugin for debug support.
ExitOnErr(addDebugSupport(J->getObjLinkingLayer()));
// Load required shared libraries on the remote target and add a generator
// for each of it, so the compiler can lookup their symbols.
for (const std::string &Path : Dylibs)
J->getMainJITDylib().addGenerator(
ExitOnErr(loadDylib(J->getExecutionSession(), Path)));
// Add the loaded IR module to the JIT. This will set up symbol tables and
// prepare for materialization.
for (ThreadSafeModule &TSM : TSMs)
ExitOnErr(J->addIRModule(std::move(TSM)));
// The example uses a non-lazy JIT for simplicity. Thus, looking up the main
// function will materialize all reachable code. It also triggers debug
// registration in the remote target process.
JITEvaluatedSymbol MainFn = ExitOnErr(J->lookup("main"));
outs() << "Running: main(";
int Pos = 0;
std::vector<std::string> ActualArgv{"LLJITWithRemoteDebugging"};
for (const std::string &Arg : InputArgv) {
outs() << (Pos++ == 0 ? "" : ", ") << "\"" << Arg << "\"";
ActualArgv.push_back(Arg);
}
outs() << ")\n";
// Execute the code in the remote target process and dump the result. With
// the debugger attached to the target, it should be possible to inspect the
// JITed code as if it was compiled statically.
{
std::unique_ptr<ExecutionSession> ES = Executor->startSession();
outs() << "Initializing LLJIT for remote executor\n";
auto J = ExitOnErr(LLJITBuilder()
.setExecutionSession(std::move(ES))
.setJITTargetMachineBuilder(std::move(JTMB))
.setObjectLinkingLayerCreator(std::ref(*Executor))
.create());
// Add plugin for debug support.
ExitOnErr(Executor->addDebugSupport(J->getObjLinkingLayer()));
// Load required shared libraries on the remote target and add a generator
// for each of it, so the compiler can lookup their symbols.
for (const std::string &Path : Dylibs)
J->getMainJITDylib().addGenerator(ExitOnErr(Executor->loadDylib(Path)));
// Add the loaded IR module to the JIT. This will set up symbol tables and
// prepare for materialization.
for (ThreadSafeModule &TSM : TSMs)
ExitOnErr(J->addIRModule(std::move(TSM)));
// The example uses a non-lazy JIT for simplicity. Thus, looking up the main
// function will materialize all reachable code. It also triggers debug
// registration in the remote target process.
JITEvaluatedSymbol MainFn = ExitOnErr(J->lookup("main"));
outs() << "Running: main(";
int Pos = 0;
for (const std::string &Arg : InputArgv)
outs() << (Pos++ == 0 ? "" : ", ") << "\"" << Arg << "\"";
outs() << ")\n";
// Execute the code in the remote target process and dump the result. With
// the debugger attached to the target, it should be possible to inspect the
// JITed code as if it was compiled statically.
int Result = ExitOnErr(Executor->runAsMain(MainFn, InputArgv));
JITTargetAddress MainFnAddr = MainFn.getAddress();
ExecutorProcessControl &EPC =
J->getExecutionSession().getExecutorProcessControl();
int Result = ExitOnErr(EPC.runAsMain(ExecutorAddr(MainFnAddr), ActualArgv));
outs() << "Exit code: " << Result << "\n";
}

View File

@ -11,12 +11,10 @@
#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
#include "llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h"
#include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h"
#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/ToolOutputFile.h"
#ifdef LLVM_ON_UNIX
#include <netdb.h>
@ -28,176 +26,55 @@
using namespace llvm;
using namespace llvm::orc;
namespace llvm {
namespace orc {
class RemoteExecutorProcessControl
: public OrcRPCExecutorProcessControlBase<
shared::MultiThreadedRPCEndpoint<JITLinkExecutor::RPCChannel>> {
public:
using RPCChannel = JITLinkExecutor::RPCChannel;
using RPCEndpoint = shared::MultiThreadedRPCEndpoint<RPCChannel>;
private:
using ThisT = RemoteExecutorProcessControl;
using BaseT = OrcRPCExecutorProcessControlBase<RPCEndpoint>;
using MemoryAccess = OrcRPCEPCMemoryAccess<ThisT>;
using MemoryManager = OrcRPCEPCJITLinkMemoryManager<ThisT>;
public:
using BaseT::initializeORCRPCEPCBase;
RemoteExecutorProcessControl(std::unique_ptr<RPCChannel> Channel,
std::unique_ptr<RPCEndpoint> Endpoint,
BaseT::ErrorReporter ReportError);
void initializeMemoryManagement();
Error disconnect() override;
private:
std::unique_ptr<RPCChannel> Channel;
std::unique_ptr<RPCEndpoint> Endpoint;
std::unique_ptr<MemoryAccess> OwnedMemAccess;
std::unique_ptr<MemoryManager> OwnedMemMgr;
std::atomic<bool> Finished{false};
std::thread ListenerThread;
};
RemoteExecutorProcessControl::RemoteExecutorProcessControl(
std::unique_ptr<RPCChannel> Channel, std::unique_ptr<RPCEndpoint> Endpoint,
BaseT::ErrorReporter ReportError)
: BaseT(std::make_shared<SymbolStringPool>(), *Endpoint,
std::move(ReportError)),
Channel(std::move(Channel)), Endpoint(std::move(Endpoint)) {
ListenerThread = std::thread([&]() {
while (!Finished) {
if (auto Err = this->Endpoint->handleOne()) {
reportError(std::move(Err));
return;
}
}
});
}
void RemoteExecutorProcessControl::initializeMemoryManagement() {
OwnedMemAccess = std::make_unique<MemoryAccess>(*this);
OwnedMemMgr = std::make_unique<MemoryManager>(*this);
// Base class needs non-owning access.
MemAccess = OwnedMemAccess.get();
MemMgr = OwnedMemMgr.get();
}
Error RemoteExecutorProcessControl::disconnect() {
std::promise<MSVCPError> P;
auto F = P.get_future();
auto Err = closeConnection([&](Error Err) -> Error {
P.set_value(std::move(Err));
Finished = true;
return Error::success();
});
ListenerThread.join();
return joinErrors(std::move(Err), F.get());
}
} // namespace orc
} // namespace llvm
JITLinkExecutor::JITLinkExecutor() = default;
JITLinkExecutor::~JITLinkExecutor() = default;
Expected<std::unique_ptr<ObjectLayer>>
JITLinkExecutor::operator()(ExecutionSession &ES, const Triple &TT) {
assert(EPC && "RemoteExecutorProcessControl must be initialized");
return std::make_unique<ObjectLinkingLayer>(ES, EPC->getMemMgr());
}
std::unique_ptr<ExecutionSession> JITLinkExecutor::startSession() {
assert(OwnedEPC && "RemoteExecutorProcessControl must be initialized");
return std::make_unique<ExecutionSession>(std::move(OwnedEPC));
}
Error JITLinkExecutor::addDebugSupport(ObjectLayer &ObjLayer) {
auto Registrar = createJITLoaderGDBRegistrar(EPC->getExecutionSession());
Error addDebugSupport(ObjectLayer &ObjLayer) {
ExecutionSession &ES = ObjLayer.getExecutionSession();
auto Registrar = createJITLoaderGDBRegistrar(ES);
if (!Registrar)
return Registrar.takeError();
cast<ObjectLinkingLayer>(&ObjLayer)->addPlugin(
std::make_unique<DebugObjectManagerPlugin>(ObjLayer.getExecutionSession(),
std::move(*Registrar)));
auto *ObjLinkingLayer = cast<ObjectLinkingLayer>(&ObjLayer);
if (!ObjLinkingLayer)
return createStringError(inconvertibleErrorCode(),
"No debug support for given object layer type");
ObjLinkingLayer->addPlugin(
std::make_unique<DebugObjectManagerPlugin>(ES, std::move(*Registrar)));
return Error::success();
}
Expected<std::unique_ptr<DefinitionGenerator>>
JITLinkExecutor::loadDylib(StringRef RemotePath) {
if (auto Handle = EPC->loadDylib(RemotePath.data()))
return std::make_unique<EPCDynamicLibrarySearchGenerator>(
EPC->getExecutionSession(), *Handle);
loadDylib(ExecutionSession &ES, StringRef RemotePath) {
if (auto Handle = ES.getExecutorProcessControl().loadDylib(RemotePath.data()))
return std::make_unique<EPCDynamicLibrarySearchGenerator>(ES, *Handle);
else
return Handle.takeError();
}
Expected<int> JITLinkExecutor::runAsMain(JITEvaluatedSymbol MainSym,
ArrayRef<std::string> Args) {
return EPC->runAsMain(ExecutorAddr(MainSym.getAddress()), Args);
}
Error JITLinkExecutor::disconnect() { return EPC->disconnect(); }
static std::string defaultPath(const char *HostArgv0, StringRef ExecutorName) {
// This just needs to be some symbol in the binary; C++ doesn't
static void findLocalExecutorHelper() {}
std::string findLocalExecutor(const char *HostArgv0) {
// This just needs to be some static symbol in the binary; C++ doesn't
// allow taking the address of ::main however.
void *P = (void *)(intptr_t)defaultPath;
SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, P));
uintptr_t UIntPtr = reinterpret_cast<uintptr_t>(&findLocalExecutorHelper);
void *VoidPtr = reinterpret_cast<void *>(UIntPtr);
SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, VoidPtr));
sys::path::remove_filename(FullName);
sys::path::append(FullName, ExecutorName);
sys::path::append(FullName, "llvm-jitlink-executor");
return FullName.str().str();
}
Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
JITLinkExecutor::FindLocal(const char *HostArgv) {
std::string BestGuess = defaultPath(HostArgv, "llvm-jitlink-executor");
auto Executor = CreateLocal(BestGuess);
if (!Executor) {
consumeError(Executor.takeError());
return make_error<StringError>(
formatv("Unable to find usable executor: {0}", BestGuess),
inconvertibleErrorCode());
}
return Executor;
}
Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
JITLinkExecutor::CreateLocal(std::string ExecutablePath) {
if (!sys::fs::can_execute(ExecutablePath))
return make_error<StringError>(
formatv("Specified executor invalid: {0}", ExecutablePath),
inconvertibleErrorCode());
return std::unique_ptr<ChildProcessJITLinkExecutor>(
new ChildProcessJITLinkExecutor(std::move(ExecutablePath)));
}
TCPSocketJITLinkExecutor::TCPSocketJITLinkExecutor(
std::unique_ptr<RemoteExecutorProcessControl> EPC) {
this->OwnedEPC = std::move(EPC);
this->EPC = this->OwnedEPC.get();
}
#ifndef LLVM_ON_UNIX
// FIXME: Add support for Windows.
Error ChildProcessJITLinkExecutor::launch(ExecutionSession &ES) {
Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
launchLocalExecutor(StringRef ExecutablePath) {
return make_error<StringError>(
"Remote JITing not yet supported on non-unix platforms",
inconvertibleErrorCode());
}
// FIXME: Add support for Windows.
Expected<std::unique_ptr<TCPSocketJITLinkExecutor>>
JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,
ExecutionSession &ES) {
Expected<std::unique_ptr<SimpleRemoteEPC>>
connectTCPSocket(StringRef NetworkAddress) {
return make_error<StringError>(
"Remote JITing not yet supported on non-unix platforms",
inconvertibleErrorCode());
@ -205,11 +82,16 @@ JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,
#else
Error ChildProcessJITLinkExecutor::launch(
unique_function<void(Error)> ErrorReporter) {
Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
launchLocalExecutor(StringRef ExecutablePath) {
constexpr int ReadEnd = 0;
constexpr int WriteEnd = 1;
if (!sys::fs::can_execute(ExecutablePath))
return make_error<StringError>(
formatv("Specified executor invalid: {0}", ExecutablePath),
inconvertibleErrorCode());
// Pipe FDs.
int ToExecutor[2];
int FromExecutor[2];
@ -219,7 +101,7 @@ Error ChildProcessJITLinkExecutor::launch(
return make_error<StringError>("Unable to create pipe for executor",
inconvertibleErrorCode());
ProcessID = fork();
pid_t ProcessID = fork();
if (ProcessID == 0) {
// In the child...
@ -256,22 +138,12 @@ Error ChildProcessJITLinkExecutor::launch(
close(ToExecutor[ReadEnd]);
close(FromExecutor[WriteEnd]);
auto Channel =
std::make_unique<RPCChannel>(FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
auto Endpoint = std::make_unique<RemoteExecutorProcessControl::RPCEndpoint>(
*Channel, true);
auto EPC = SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
if (!EPC)
return EPC.takeError();
OwnedEPC = std::make_unique<RemoteExecutorProcessControl>(
std::move(Channel), std::move(Endpoint), std::move(ErrorReporter));
if (auto Err = OwnedEPC->initializeORCRPCEPCBase())
return joinErrors(std::move(Err), OwnedEPC->disconnect());
OwnedEPC->initializeMemoryManagement();
EPC = OwnedEPC.get();
shared::registerStringError<RPCChannel>();
return Error::success();
return std::make_pair(std::move(*EPC), static_cast<uint64_t>(ProcessID));
}
static Expected<int> connectTCPSocketImpl(std::string Host,
@ -313,9 +185,8 @@ static Expected<int> connectTCPSocketImpl(std::string Host,
return SockFD;
}
Expected<std::unique_ptr<TCPSocketJITLinkExecutor>>
JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,
unique_function<void(Error)> ErrorReporter) {
Expected<std::unique_ptr<SimpleRemoteEPC>>
connectTCPSocket(StringRef NetworkAddress) {
auto CreateErr = [NetworkAddress](StringRef Details) {
return make_error<StringError>(
formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
@ -337,21 +208,7 @@ JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,
if (!SockFD)
return CreateErr(toString(SockFD.takeError()));
auto Channel = std::make_unique<RPCChannel>(*SockFD, *SockFD);
auto Endpoint = std::make_unique<RemoteExecutorProcessControl::RPCEndpoint>(
*Channel, true);
auto EPC = std::make_unique<RemoteExecutorProcessControl>(
std::move(Channel), std::move(Endpoint), std::move(ErrorReporter));
if (auto Err = EPC->initializeORCRPCEPCBase())
return joinErrors(std::move(Err), EPC->disconnect());
EPC->initializeMemoryManagement();
shared::registerStringError<RPCChannel>();
return std::unique_ptr<TCPSocketJITLinkExecutor>(
new TCPSocketJITLinkExecutor(std::move(EPC)));
return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(*SockFD);
}
#endif

View File

@ -14,103 +14,31 @@
#ifndef LLVM_EXAMPLES_ORCV2EXAMPLES_LLJITWITHREMOTEDEBUGGING_REMOTEJITUTILS_H
#define LLVM_EXAMPLES_ORCV2EXAMPLES_LLJITWITHREMOTEDEBUGGING_REMOTEJITUTILS_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/Orc/Core.h"
#include "llvm/ExecutionEngine/Orc/Layer.h"
#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h"
#include "llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h"
#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
#include "llvm/Support/Error.h"
#include <cstdint>
#include <memory>
#include <string>
#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
#else
#include <io.h>
#endif
/// Find the default exectuable on disk and create a JITLinkExecutor for it.
std::string findLocalExecutor(const char *HostArgv0);
namespace llvm {
namespace orc {
llvm::Expected<std::pair<std::unique_ptr<llvm::orc::SimpleRemoteEPC>, uint64_t>>
launchLocalExecutor(llvm::StringRef ExecutablePath);
class ChildProcessJITLinkExecutor;
class RemoteExecutorProcessControl;
class TCPSocketJITLinkExecutor;
/// Create a JITLinkExecutor that connects to the given network address
/// through a TCP socket. A valid NetworkAddress provides hostname and port,
/// e.g. localhost:20000.
llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>
connectTCPSocket(llvm::StringRef NetworkAddress);
class JITLinkExecutor {
public:
using RPCChannel = shared::FDRawByteChannel;
llvm::Error addDebugSupport(llvm::orc::ObjectLayer &ObjLayer);
/// Create a JITLinkExecutor for the given exectuable on disk.
static Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
CreateLocal(std::string ExecutablePath);
/// Find the default exectuable on disk and create a JITLinkExecutor for it.
static Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
FindLocal(const char *JITArgv0);
/// Create a JITLinkExecutor that connects to the given network address
/// through a TCP socket. A valid NetworkAddress provides hostname and port,
/// e.g. localhost:20000.
static Expected<std::unique_ptr<TCPSocketJITLinkExecutor>>
ConnectTCPSocket(StringRef NetworkAddress,
unique_function<void(Error)> ErrorReporter);
// Implement ObjectLinkingLayerCreator
Expected<std::unique_ptr<ObjectLayer>> operator()(ExecutionSession &,
const Triple &);
std::unique_ptr<ExecutionSession> startSession();
Error disconnect();
Error addDebugSupport(ObjectLayer &ObjLayer);
Expected<std::unique_ptr<DefinitionGenerator>>
loadDylib(StringRef RemotePath);
Expected<int> runAsMain(JITEvaluatedSymbol MainSym,
ArrayRef<std::string> Args);
virtual ~JITLinkExecutor();
protected:
std::unique_ptr<RemoteExecutorProcessControl> OwnedEPC;
RemoteExecutorProcessControl *EPC{nullptr};
JITLinkExecutor();
};
/// JITLinkExecutor that runs in a child process on the local machine.
class ChildProcessJITLinkExecutor : public JITLinkExecutor {
public:
Error launch(unique_function<void(Error)> ErrorReporter);
pid_t getPID() const { return ProcessID; }
StringRef getPath() const { return ExecutablePath; }
private:
std::string ExecutablePath;
pid_t ProcessID;
ChildProcessJITLinkExecutor(std::string ExecutablePath)
: ExecutablePath(std::move(ExecutablePath)) {}
static std::string defaultPath(const char *HostArgv0, StringRef ExecutorName);
friend class JITLinkExecutor;
};
/// JITLinkExecutor connected through a TCP socket.
class TCPSocketJITLinkExecutor : public JITLinkExecutor {
private:
TCPSocketJITLinkExecutor(std::unique_ptr<RemoteExecutorProcessControl> EPC);
friend class JITLinkExecutor;
};
} // namespace orc
} // namespace llvm
llvm::Expected<std::unique_ptr<llvm::orc::DefinitionGenerator>>
loadDylib(llvm::orc::ExecutionSession &ES, llvm::StringRef RemotePath);
#endif