-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[Clang-Repl] Add Lambda Support, PID Retrieval, and Dynamic liborc_rt Path in Clang-Repl #155140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
79849c3
825f005
52b0902
3b4fcad
e174e98
b744a9f
0c6c995
2cf71ed
7c29008
41f8e54
4f1d203
a6a4369
63de886
c24e84e
b114bb8
850b953
956f393
14e5afd
65afbff
7dc6590
095a63b
2b6dc6c
7ad10a6
6a58d5f
f1b9135
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
//===-- OutOfProcessJITConfig.h - Struct for Out-Of-Process JIT--*- 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This file defines a struct that holds configuration options for | ||
// out-of-process JIT execution. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLVM_CLANG_INTERPRETER_OUTOFPROCESSJITCONFIG_H | ||
#define LLVM_CLANG_INTERPRETER_OUTOFPROCESSJITCONFIG_H | ||
|
||
#include <functional> | ||
#include <string> | ||
#include <utility> | ||
|
||
namespace clang { | ||
|
||
/// \brief Configuration options for out-of-process JIT execution. | ||
struct OutOfProcessJITConfig { | ||
/// Indicates whether out-of-process JIT execution is enabled. | ||
bool IsOutOfProcess = false; | ||
|
||
/// Path to the out-of-process JIT executor. | ||
std::string OOPExecutor; | ||
|
||
std::string OOPExecutorConnect; | ||
|
||
/// Indicates whether to use shared memory for communication. | ||
bool UseSharedMemory; | ||
|
||
/// String representing the slab allocation size for memory management. | ||
std::string SlabAllocateSizeString; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be an |
||
|
||
/// Path to the ORC runtime library. | ||
std::string OrcRuntimePath; | ||
}; | ||
|
||
} // namespace clang | ||
|
||
#endif // LLVM_CLANG_INTERPRETER_OUTOFPROCESSJITCONFIG_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,8 @@ | |
#include "clang/Frontend/TextDiagnosticBuffer.h" | ||
#include "clang/FrontendTool/Utils.h" | ||
#include "clang/Interpreter/Interpreter.h" | ||
#include "clang/Interpreter/OutOfProcessJITConfig.h" | ||
#include "clang/Interpreter/RemoteJITUtils.h" | ||
#include "clang/Interpreter/Value.h" | ||
#include "clang/Lex/PreprocessorOptions.h" | ||
#include "clang/Sema/Lookup.h" | ||
|
@@ -55,6 +57,8 @@ | |
#include "llvm/TargetParser/Host.h" | ||
#include "llvm/Transforms/Utils/Cloning.h" // for CloneModule | ||
|
||
#include <iostream> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We probably do not need this include. |
||
|
||
#define DEBUG_TYPE "clang-repl" | ||
|
||
using namespace clang; | ||
|
@@ -460,10 +464,99 @@ const char *const Runtimes = R"( | |
EXTERN_C void __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, ...); | ||
)"; | ||
|
||
llvm::ExitOnError ExitOnErr; | ||
|
||
std::unique_ptr<llvm::orc::LLJITBuilder> | ||
Interpreter::outOfProcessJITBuilder(OutOfProcessJITConfig OutOfProcessConfig) { | ||
std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC; | ||
if (OutOfProcessConfig.OOPExecutor != "") { | ||
// Launch an out-of-process executor locally in a child process. | ||
EPC = ExitOnErr(launchExecutor(OutOfProcessConfig.OOPExecutor, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remember this is the library, it must not abort the process. Instead the interface should return |
||
OutOfProcessConfig.UseSharedMemory, | ||
OutOfProcessConfig.SlabAllocateSizeString)); | ||
} else if (OutOfProcessConfig.OOPExecutorConnect != "") { | ||
EPC = | ||
ExitOnErr(connectTCPSocket(OutOfProcessConfig.OOPExecutorConnect, | ||
OutOfProcessConfig.UseSharedMemory, | ||
OutOfProcessConfig.SlabAllocateSizeString)); | ||
} | ||
|
||
std::unique_ptr<llvm::orc::LLJITBuilder> JB; | ||
if (EPC) { | ||
JB = ExitOnErr(clang::Interpreter::createLLJITBuilder( | ||
std::move(EPC), OutOfProcessConfig.OrcRuntimePath)); | ||
} | ||
|
||
return JB; | ||
} | ||
|
||
llvm::Expected<std::string> | ||
Interpreter::getOrcRuntimePath(const driver::ToolChain &TC) { | ||
std::optional<std::string> CompilerRTPath = TC.getCompilerRTPath(); | ||
if (!CompilerRTPath) { | ||
return llvm::make_error<llvm::StringError>("CompilerRT path not found", | ||
std::error_code()); | ||
} | ||
llvm::SmallString<256> BasePath(llvm::sys::fs::getMainExecutable( | ||
"clang-repl", reinterpret_cast<void *>(&getOrcRuntimePath))); | ||
Comment on lines
+500
to
+501
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That does not work when we are in a library. Why do we need the BasePath at all? I'd think we can ask the |
||
// Remove paths until you find /build at last | ||
while (!(llvm::sys::path::filename(BasePath) == "build") && | ||
!BasePath.empty()) { | ||
llvm::sys::path::remove_filename(BasePath); | ||
} | ||
|
||
llvm::sys::path::append(BasePath, *CompilerRTPath); | ||
|
||
if (llvm::sys::fs::exists(BasePath.str().str() + "/liborc_rt.a")) { | ||
llvm::sys::path::append(BasePath, "liborc_rt.a"); | ||
} else if (llvm::sys::fs::exists(BasePath.str().str() + "/liborc_rt_osx.a")) { | ||
llvm::sys::path::append(BasePath, "liborc_rt_osx.a"); | ||
} else if (llvm::sys::fs::exists(BasePath.str().str() + | ||
"/liborc_rt-x86_64.a")) { | ||
llvm::sys::path::append(BasePath, "liborc_rt-x86_64.a"); | ||
} else { | ||
return llvm::make_error<llvm::StringError>("OrcRuntime library not found", | ||
std::error_code()); | ||
} | ||
|
||
return BasePath.str().str(); | ||
} | ||
|
||
llvm::Expected<std::unique_ptr<Interpreter>> | ||
Interpreter::create(std::unique_ptr<CompilerInstance> CI, | ||
std::unique_ptr<llvm::orc::LLJITBuilder> JB) { | ||
std::optional<OutOfProcessJITConfig> OutOfProcessConfig) { | ||
llvm::Error Err = llvm::Error::success(); | ||
|
||
std::unique_ptr<llvm::orc::LLJITBuilder> JB; | ||
|
||
if (OutOfProcessConfig != std::nullopt && | ||
OutOfProcessConfig->IsOutOfProcess) { | ||
const TargetInfo &TI = CI->getTarget(); | ||
const llvm::Triple &Triple = TI.getTriple(); | ||
|
||
DiagnosticsEngine &Diags = CI->getDiagnostics(); | ||
driver::Driver Driver("clang", Triple.str(), Diags); | ||
|
||
std::vector<const char *> Args = {"clang", "--version"}; | ||
std::unique_ptr<clang::driver::Compilation> C( | ||
Driver.BuildCompilation(Args)); | ||
if (!C) { | ||
return llvm::make_error<llvm::StringError>( | ||
"Failed to create driver compilation for out-of-process JIT", | ||
std::error_code()); | ||
} | ||
|
||
const clang::driver::ToolChain &TC = C->getDefaultToolChain(); | ||
|
||
auto OrcRuntimePathOrErr = getOrcRuntimePath(TC); | ||
if (!OrcRuntimePathOrErr) { | ||
return OrcRuntimePathOrErr.takeError(); | ||
} | ||
|
||
OutOfProcessConfig->OrcRuntimePath = *OrcRuntimePathOrErr; | ||
JB = outOfProcessJITBuilder(*OutOfProcessConfig); | ||
} | ||
|
||
auto Interp = std::unique_ptr<Interpreter>( | ||
new Interpreter(std::move(CI), Err, JB ? std::move(JB) : nullptr)); | ||
if (Err) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,10 @@ | |
using namespace llvm; | ||
using namespace llvm::orc; | ||
|
||
#if LLVM_ON_UNIX | ||
static std::vector<pid_t> LaunchedExecutorPID; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That should be a member of the Interpreter class I think. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This vector is updated in launchExecutor. And launchExecutor is called before creation of interpreter. I can define it as static but I don't think it is related enough to be in interpreter class. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need a home for this. The process is not the right home. Either the Interpreter class or the IncrementalExecutor should be good options. |
||
#endif | ||
|
||
Expected<uint64_t> getSlabAllocSize(StringRef SizeString) { | ||
SizeString = SizeString.trim(); | ||
|
||
|
@@ -89,9 +93,14 @@ createSharedMemoryManager(SimpleRemoteEPC &SREPC, | |
SlabSize, SREPC, SAs); | ||
} | ||
|
||
// Launches an out-of-process executor for remote JIT. The calling program can | ||
// provide a CustomizeFork callback, which allows it to run custom code in the | ||
// child process before exec. This enables sending custom setup or code to be | ||
// executed in the child (out-of-process) executor. | ||
Expected<std::unique_ptr<SimpleRemoteEPC>> | ||
launchExecutor(StringRef ExecutablePath, bool UseSharedMemory, | ||
llvm::StringRef SlabAllocateSizeString) { | ||
llvm::StringRef SlabAllocateSizeString, | ||
std::function<void()> CustomizeFork) { | ||
#ifndef LLVM_ON_UNIX | ||
// FIXME: Add support for Windows. | ||
return make_error<StringError>("-" + ExecutablePath + | ||
|
@@ -134,6 +143,9 @@ launchExecutor(StringRef ExecutablePath, bool UseSharedMemory, | |
close(ToExecutor[WriteEnd]); | ||
close(FromExecutor[ReadEnd]); | ||
|
||
if (CustomizeFork) | ||
CustomizeFork(); | ||
|
||
// Execute the child process. | ||
std::unique_ptr<char[]> ExecutorPath, FDSpecifier; | ||
{ | ||
|
@@ -158,6 +170,8 @@ launchExecutor(StringRef ExecutablePath, bool UseSharedMemory, | |
} | ||
// else we're the parent... | ||
|
||
LaunchedExecutorPID.push_back(ChildPID); | ||
|
||
// Close the child ends of the pipes | ||
close(ToExecutor[ReadEnd]); | ||
close(FromExecutor[WriteEnd]); | ||
|
@@ -265,3 +279,18 @@ connectTCPSocket(StringRef NetworkAddress, bool UseSharedMemory, | |
std::move(S), *SockFD, *SockFD); | ||
#endif | ||
} | ||
|
||
#if LLVM_ON_UNIX | ||
|
||
pid_t getLastLaunchedExecutorPID() { | ||
if (!LaunchedExecutorPID.size()) | ||
return -1; | ||
return LaunchedExecutorPID.back(); | ||
} | ||
|
||
pid_t getNthLaunchedExecutorPID(int n) { | ||
if (n - 1 < 0 || n - 1 >= static_cast<int>(LaunchedExecutorPID.size())) | ||
return -1; | ||
return LaunchedExecutorPID.at(n - 1); | ||
} | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That struct can go as a nested Interpreter class.