[compiler-rt] [clang] [llvm] [clang-repl] [ORC] Add support for out-of-process execution on ELF (PR #79936)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 1 15:35:11 PST 2024
================
@@ -143,6 +169,201 @@ ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos,
return Comps;
}
+static llvm::Error sanitizeOopArguments(const char *ArgV0) {
+ llvm::Triple SystemTriple(llvm::sys::getProcessTriple());
+ if ((OutOfProcessExecutor.getNumOccurrences() ||
+ OutOfProcessExecutorConnect.getNumOccurrences()) &&
+ (!SystemTriple.isOSBinFormatELF()))
+ return llvm::make_error<llvm::StringError>(
+ "Out-process-executors are currently only supported on ELF",
+ llvm::inconvertibleErrorCode());
+
+ // Only one of -oop-executor and -oop-executor-connect can be used.
+ if (!!OutOfProcessExecutor.getNumOccurrences() &&
+ !!OutOfProcessExecutorConnect.getNumOccurrences())
+ return llvm::make_error<llvm::StringError>(
+ "Only one of -" + OutOfProcessExecutor.ArgStr + " and -" +
+ OutOfProcessExecutorConnect.ArgStr + " can be specified",
+ llvm::inconvertibleErrorCode());
+
+ // If -oop-executor was used but no value was specified then use a sensible
+ // default.
+ if (!!OutOfProcessExecutor.getNumOccurrences() &&
+ OutOfProcessExecutor.empty()) {
+ llvm::SmallString<256> OOPExecutorPath(llvm::sys::fs::getMainExecutable(
+ ArgV0, reinterpret_cast<void *>(&sanitizeOopArguments)));
+ llvm::sys::path::remove_filename(OOPExecutorPath);
+ llvm::sys::path::append(OOPExecutorPath, "llvm-jitlink-executor");
+ OutOfProcessExecutor = OOPExecutorPath.str().str();
+ }
+
+ // Out-of-process executors must run with the ORC runtime for destructor support.
+ if (OrcRuntimePath.empty() && (OutOfProcessExecutor.getNumOccurrences() || OutOfProcessExecutorConnect.getNumOccurrences())) {
+ llvm::SmallString<256> OrcPath(llvm::sys::fs::getMainExecutable(
+ ArgV0, reinterpret_cast<void *>(&sanitizeOopArguments)));
+ llvm::sys::path::remove_filename(OrcPath); // Remove clang-repl filename.
+ llvm::sys::path::remove_filename(OrcPath); // Remove ./bin directory.
+ llvm::sys::path::append(OrcPath, "lib/clang/18/lib/x86_64-unknown-linux-gnu/liborc_rt.a");
+ OrcRuntimePath = OrcPath.str().str();
+ }
+
+ return llvm::Error::success();
+}
+
+static llvm::Expected<std::unique_ptr<llvm::orc::ExecutorProcessControl>>
+launchExecutor() {
+ constexpr int ReadEnd = 0;
+ constexpr int WriteEnd = 1;
+
+ // Pipe FDs.
+ int ToExecutor[2];
+ int FromExecutor[2];
+
+ pid_t ChildPID;
+
+ // Create pipes to/from the executor..
+ if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
+ return llvm::make_error<llvm::StringError>(
+ "Unable to create pipe for executor", llvm::inconvertibleErrorCode());
+
+ ChildPID = fork();
+
+ if (ChildPID == 0) {
+ // In the child...
+
+ // Close the parent ends of the pipes
+ close(ToExecutor[WriteEnd]);
+ close(FromExecutor[ReadEnd]);
+
+ // Execute the child process.
+ std::unique_ptr<char[]> ExecutorPath, FDSpecifier;
+ {
+ ExecutorPath = std::make_unique<char[]>(OutOfProcessExecutor.size() + 1);
+ strcpy(ExecutorPath.get(), OutOfProcessExecutor.data());
+
+ std::string FDSpecifierStr("filedescs=");
+ FDSpecifierStr += llvm::utostr(ToExecutor[ReadEnd]);
+ FDSpecifierStr += ',';
+ FDSpecifierStr += llvm::utostr(FromExecutor[WriteEnd]);
+ FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
+ strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
+ }
+
+ char *const Args[] = {ExecutorPath.get(), FDSpecifier.get(), nullptr};
+ int RC = execvp(ExecutorPath.get(), Args);
+ if (RC != 0) {
+ llvm::errs() << "unable to launch out-of-process executor \""
+ << ExecutorPath.get() << "\"\n";
+ exit(1);
+ }
+ }
+ // else we're the parent...
+
+ // Close the child ends of the pipes
+ close(ToExecutor[ReadEnd]);
+ close(FromExecutor[WriteEnd]);
+
+ auto S = llvm::orc::SimpleRemoteEPC::Setup();
+
+ return llvm::orc::SimpleRemoteEPC::Create<
+ llvm::orc::FDSimpleRemoteEPCTransport>(
+ std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(),
+ std::move(S), FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
+}
+
+#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
+static llvm::Error createTCPSocketError(llvm::Twine Details) {
+ return llvm::make_error<llvm::StringError>(
+ formatv("Failed to connect TCP socket '{0}': {1}",
+ OutOfProcessExecutorConnect, Details),
+ llvm::inconvertibleErrorCode());
+}
+
+static llvm::Expected<int> connectTCPSocket(std::string Host,
+ std::string PortStr) {
+ addrinfo *AI;
+ addrinfo Hints{};
+ Hints.ai_family = AF_INET;
+ Hints.ai_socktype = SOCK_STREAM;
+ Hints.ai_flags = AI_NUMERICSERV;
+
+ if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
+ return createTCPSocketError("Address resolution failed (" +
+ llvm::StringRef(gai_strerror(EC)) + ")");
+
+ // Cycle through the returned addrinfo structures and connect to the first
+ // reachable endpoint.
+ int SockFD;
+ addrinfo *Server;
+ for (Server = AI; Server != nullptr; Server = Server->ai_next) {
+ // socket might fail, e.g. if the address family is not supported. Skip to
+ // the next addrinfo structure in such a case.
+ if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
+ continue;
+
+ // If connect returns null, we exit the loop with a working socket.
+ if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
+ break;
+
+ close(SockFD);
+ }
+ freeaddrinfo(AI);
+
+ // If we reached the end of the loop without connecting to a valid endpoint,
+ // dump the last error that was logged in socket() or connect().
+ if (Server == nullptr)
+ return createTCPSocketError(std::strerror(errno));
+
+ return SockFD;
+}
+#endif
+
+static llvm::Expected<std::unique_ptr<llvm::orc::ExecutorProcessControl>>
+connectToExecutor() {
+#ifndef LLVM_ON_UNIX
+ // FIXME: Add TCP support for Windows.
+ return llvm::make_error<StringError>(
+ "-" + OutOfProcessExecutorConnect.ArgStr +
+ " not supported on non-unix platforms",
+ inconvertibleErrorCode());
+#elif !LLVM_ENABLE_THREADS
+ // Out of process mode using SimpleRemoteEPC depends on threads.
+ return llvm::make_error<StringError>(
+ "-" + OutOfProcessExecutorConnect.ArgStr +
+ " requires threads, but LLVM was built with "
+ "LLVM_ENABLE_THREADS=Off",
+ inconvertibleErrorCode());
+#else
+
+ llvm::StringRef Host, PortStr;
+ std::tie(Host, PortStr) =
+ llvm::StringRef(OutOfProcessExecutorConnect).split(':');
+ if (Host.empty())
+ return createTCPSocketError("Host name for -" +
+ OutOfProcessExecutorConnect.ArgStr +
+ " can not be empty");
+ if (PortStr.empty())
+ return createTCPSocketError("Port number in -" +
+ OutOfProcessExecutorConnect.ArgStr +
+ " can not be empty");
+ int Port = 0;
+ if (PortStr.getAsInteger(10, Port))
+ return createTCPSocketError("Port number '" + PortStr +
+ "' is not a valid integer");
+
+ llvm::Expected<int> SockFD = connectTCPSocket(Host.str(), PortStr.str());
+ if (!SockFD)
+ return SockFD.takeError();
+
+ auto S = llvm::orc::SimpleRemoteEPC::Setup();
+
+ return llvm::orc::SimpleRemoteEPC::Create<
+ llvm::orc::FDSimpleRemoteEPCTransport>(
+ std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(),
+ std::move(S), *SockFD, *SockFD);
+#endif
+}
----------------
jameshu15869 wrote:
Could you elaborate more on what you mean by use case? I've tested running oop with a running instance of `llvm-jitlink-executor` on Linux, but the original `llvm-jitlink` tool that I got this section of code from didn't actually support TCP connections on Windows.
https://github.com/llvm/llvm-project/pull/79936
More information about the cfe-commits
mailing list