[clang] [clang-repl] Simplify the logic around out of process execution. NFC (PR #175322)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 10 07:50:48 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Vassil Vassilev (vgvassilev)
<details>
<summary>Changes</summary>
cc: @<!-- -->aaronj0, @<!-- -->Vipul-Cariappa
---
Patch is 31.01 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/175322.diff
7 Files Affected:
- (modified) clang/include/clang/Interpreter/Interpreter.h (+55-36)
- (modified) clang/lib/Interpreter/IncrementalExecutor.cpp (+1-2)
- (modified) clang/lib/Interpreter/IncrementalExecutor.h (+1-5)
- (modified) clang/lib/Interpreter/Interpreter.cpp (+108-107)
- (modified) clang/tools/clang-repl/ClangRepl.cpp (+18-13)
- (modified) clang/unittests/Interpreter/InterpreterExtensionsTest.cpp (-39)
- (modified) clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp (+57-41)
``````````diff
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 078d70b3b1749..4a69b37338349 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -38,7 +38,7 @@ class ThreadSafeContext;
namespace clang {
namespace driver {
-class ToolChain;
+class Compilation;
} // namespace driver
class CompilerInstance;
@@ -50,6 +50,8 @@ class IncrementalCUDADeviceParser;
/// Create a pre-configured \c CompilerInstance for incremental processing.
class IncrementalCompilerBuilder {
+ using DriverCompilationFn = llvm::Error(const driver::Compilation &);
+
public:
IncrementalCompilerBuilder() {}
@@ -68,11 +70,16 @@ class IncrementalCompilerBuilder {
// CUDA specific
void SetCudaSDK(llvm::StringRef path) { CudaSDKPath = path; };
+ // Hand over the compilation.
+ void SetDriverCompilationCallback(std::function<DriverCompilationFn> C) {
+ CompilationCB = C;
+ }
+
llvm::Expected<std::unique_ptr<CompilerInstance>> CreateCudaHost();
llvm::Expected<std::unique_ptr<CompilerInstance>> CreateCudaDevice();
private:
- static llvm::Expected<std::unique_ptr<CompilerInstance>>
+ llvm::Expected<std::unique_ptr<CompilerInstance>>
create(std::string TT, std::vector<const char *> &ClangArgv);
llvm::Expected<std::unique_ptr<CompilerInstance>> createCuda(bool device);
@@ -82,6 +89,46 @@ class IncrementalCompilerBuilder {
llvm::StringRef OffloadArch;
llvm::StringRef CudaSDKPath;
+
+ std::optional<std::function<DriverCompilationFn>> CompilationCB;
+};
+
+// FIXME: Consider deriving from the LLJITBuilder into a common interpreter
+// creation configuraion class.
+class IncrementalExecutorBuilder {
+public:
+ /// 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 = false;
+ /// Representing the slab allocation size for memory management in kb.
+ unsigned SlabAllocateSize = 0;
+ /// Path to the ORC runtime library.
+ std::string OrcRuntimePath = "";
+ /// PID of the out-of-process JIT executor.
+ uint32_t ExecutorPID = 0;
+ /// Custom lambda to be executed inside child process/executor
+ std::function<void()> CustomizeFork = nullptr;
+ /// An optional code model to provide to the JITTargetMachineBuilder
+ std::optional<llvm::CodeModel::Model> CM = std::nullopt;
+ /// An optional external IncrementalExecutor
+ // std::unique_ptr<IncrementalExecutor> IE;
+ std::function<llvm::Error(const driver::Compilation &)>
+ UpdateOrcRuntimePathCB = [this](const driver::Compilation &C) {
+ return UpdateOrcRuntimePath(C);
+ };
+
+ ~IncrementalExecutorBuilder();
+
+ llvm::Expected<std::unique_ptr<IncrementalExecutor>>
+ create(llvm::orc::ThreadSafeContext &TSC,
+ llvm::orc::LLJITBuilder &JITBuilder);
+
+private:
+ llvm::Error UpdateOrcRuntimePath(const driver::Compilation &C);
};
class IncrementalAction;
@@ -120,42 +167,16 @@ class Interpreter {
/// An optional compiler instance for CUDA offloading
std::unique_ptr<CompilerInstance> DeviceCI;
-public:
- struct JITConfig {
- /// 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 = false;
- /// Representing the slab allocation size for memory management in kb.
- unsigned SlabAllocateSize = 0;
- /// Path to the ORC runtime library.
- std::string OrcRuntimePath = "";
- /// PID of the out-of-process JIT executor.
- uint32_t ExecutorPID = 0;
- /// Custom lambda to be executed inside child process/executor
- std::function<void()> CustomizeFork = nullptr;
- /// An optional code model to provide to the JITTargetMachineBuilder
- std::optional<llvm::CodeModel::Model> CM = std::nullopt;
-
- JITConfig()
- : IsOutOfProcess(false), OOPExecutor(""), OOPExecutorConnect(""),
- UseSharedMemory(false), SlabAllocateSize(0), OrcRuntimePath(""),
- ExecutorPID(0), CustomizeFork(nullptr), CM(std::nullopt) {}
- };
-
protected:
// Derived classes can use an extended interface of the Interpreter.
Interpreter(std::unique_ptr<CompilerInstance> Instance, llvm::Error &Err,
std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr,
std::unique_ptr<clang::ASTConsumer> Consumer = nullptr,
- JITConfig Config = JITConfig());
+ std::unique_ptr<IncrementalExecutorBuilder> IEB = nullptr);
// Create the internal IncrementalExecutor, or re-create it after calling
// ResetExecutor().
- llvm::Error CreateExecutor(JITConfig Config = JITConfig());
+ llvm::Error CreateExecutor();
// Delete the internal IncrementalExecutor. This causes a hard shutdown of the
// JIT engine. In particular, it doesn't run cleanup or destructors.
@@ -164,18 +185,14 @@ class Interpreter {
public:
virtual ~Interpreter();
static llvm::Expected<std::unique_ptr<Interpreter>>
- create(std::unique_ptr<CompilerInstance> CI, JITConfig Config = {});
+ create(std::unique_ptr<CompilerInstance> CI,
+ std::unique_ptr<IncrementalExecutorBuilder> IEB = nullptr);
static llvm::Expected<std::unique_ptr<Interpreter>>
createWithCUDA(std::unique_ptr<CompilerInstance> CI,
std::unique_ptr<CompilerInstance> DCI);
static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
createLLJITBuilder(std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC,
llvm::StringRef OrcRuntimePath);
- static llvm::Expected<
- std::pair<std::unique_ptr<llvm::orc::LLJITBuilder>, uint32_t>>
- outOfProcessJITBuilder(JITConfig Config);
- static llvm::Expected<std::string>
- getOrcRuntimePath(const driver::ToolChain &TC);
const ASTContext &getASTContext() const;
ASTContext &getASTContext();
@@ -221,6 +238,8 @@ class Interpreter {
std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder;
+ std::unique_ptr<IncrementalExecutorBuilder> IncrExecutorBuilder;
+
/// @}
/// @name Value and pretty printing support
/// @{
diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp
index 11ab2cfaac17a..f0069c4924f7a 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.cpp
+++ b/clang/lib/Interpreter/IncrementalExecutor.cpp
@@ -69,9 +69,8 @@ IncrementalExecutor::createDefaultJITBuilder(
IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC,
llvm::orc::LLJITBuilder &JITBuilder,
- Interpreter::JITConfig Config,
llvm::Error &Err)
- : TSCtx(TSC), OutOfProcessChildPid(Config.ExecutorPID) {
+ : TSCtx(TSC) {
using namespace llvm::orc;
llvm::ErrorAsOutParameter EAO(&Err);
diff --git a/clang/lib/Interpreter/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h
index bb1ec33452515..c11c99b9bff8d 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.h
+++ b/clang/lib/Interpreter/IncrementalExecutor.h
@@ -46,7 +46,6 @@ class IncrementalExecutor {
using CtorDtorIterator = llvm::orc::CtorDtorIterator;
std::unique_ptr<llvm::orc::LLJIT> Jit;
llvm::orc::ThreadSafeContext &TSCtx;
- uint32_t OutOfProcessChildPid = -1;
llvm::DenseMap<const PartialTranslationUnit *, llvm::orc::ResourceTrackerSP>
ResourceTrackers;
@@ -58,8 +57,7 @@ class IncrementalExecutor {
enum SymbolNameKind { IRName, LinkerName };
IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC,
- llvm::orc::LLJITBuilder &JITBuilder,
- Interpreter::JITConfig Config, llvm::Error &Err);
+ llvm::orc::LLJITBuilder &JITBuilder, llvm::Error &Err);
virtual ~IncrementalExecutor();
virtual llvm::Error addModule(PartialTranslationUnit &PTU);
@@ -71,8 +69,6 @@ class IncrementalExecutor {
llvm::orc::LLJIT &GetExecutionEngine() { return *Jit; }
- uint32_t getOutOfProcessChildPid() const { return OutOfProcessChildPid; }
-
static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB);
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 1a6f6ea0813b7..01e534416152b 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -182,6 +182,10 @@ IncrementalCompilerBuilder::create(std::string TT,
llvm::ArrayRef<const char *> RF = llvm::ArrayRef(ClangArgv);
std::unique_ptr<driver::Compilation> Compilation(Driver.BuildCompilation(RF));
+ if (CompilationCB)
+ if (auto Err = (*CompilationCB)(*Compilation.get()))
+ return std::move(Err);
+
if (Compilation->getArgs().hasArg(options::OPT_v))
Compilation->getJobs().Print(llvm::errs(), "\n", /*Quote=*/false);
@@ -247,12 +251,83 @@ IncrementalCompilerBuilder::CreateCudaHost() {
return IncrementalCompilerBuilder::createCuda(false);
}
+IncrementalExecutorBuilder::~IncrementalExecutorBuilder() = default;
+
+llvm::Expected<std::unique_ptr<IncrementalExecutor>>
+IncrementalExecutorBuilder::create(llvm::orc::ThreadSafeContext &TSC,
+ llvm::orc::LLJITBuilder &JITBuilder) {
+ // if (IE)
+ // return std::move(IE);
+
+ llvm::Error Err = llvm::Error::success();
+ std::unique_ptr<IncrementalExecutor> Executor;
+#ifdef __EMSCRIPTEN__
+ Executor = std::make_unique<WasmIncrementalExecutor>(TSC);
+#else
+ Executor = std::make_unique<IncrementalExecutor>(TSC, JITBuilder, Err);
+#endif
+
+ if (Err)
+ return std::move(Err);
+
+ return std::move(Executor);
+}
+
+llvm::Error
+IncrementalExecutorBuilder::UpdateOrcRuntimePath(const driver::Compilation &C) {
+ if (!IsOutOfProcess)
+ return llvm::Error::success();
+
+ const std::array<const char *, 3> OrcRTLibNames = {
+ "liborc_rt.a", "liborc_rt_osx.a", "liborc_rt-x86_64.a"};
+
+ auto findInDir = [&](llvm::StringRef Base) -> std::optional<std::string> {
+ for (const char *LibName : OrcRTLibNames) {
+ llvm::SmallString<256> CandidatePath(Base);
+ llvm::sys::path::append(CandidatePath, LibName);
+ if (llvm::sys::fs::exists(CandidatePath))
+ return std::string(CandidatePath.str());
+ }
+ return std::nullopt;
+ };
+
+ const driver::ToolChain &TC = C.getDefaultToolChain();
+ std::string SearchedPaths;
+ if (std::optional<std::string> CompilerRTPath = TC.getCompilerRTPath()) {
+ if (auto Found = findInDir(*CompilerRTPath)) {
+ OrcRuntimePath = *Found;
+ return llvm::Error::success();
+ }
+ SearchedPaths += *CompilerRTPath;
+ } else {
+ return llvm::make_error<llvm::StringError>("CompilerRT path not found",
+ std::error_code());
+ }
+
+ if (std::optional<std::string> ResourceDir = TC.getRuntimePath()) {
+ if (auto Found = findInDir(*ResourceDir)) {
+ OrcRuntimePath = *Found;
+ return llvm::Error::success();
+ }
+ if (!SearchedPaths.empty())
+ SearchedPaths += "; ";
+ SearchedPaths += *ResourceDir;
+ } else {
+ return llvm::make_error<llvm::StringError>("ResourceDir path not found",
+ std::error_code());
+ }
+
+ return llvm::make_error<llvm::StringError>(
+ llvm::Twine("OrcRuntime library not found in: ") + SearchedPaths,
+ std::error_code());
+}
+
Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance,
llvm::Error &ErrOut,
std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder,
std::unique_ptr<clang::ASTConsumer> Consumer,
- JITConfig Config)
- : JITBuilder(std::move(JITBuilder)) {
+ std::unique_ptr<IncrementalExecutorBuilder> IEB)
+ : JITBuilder(std::move(JITBuilder)), IncrExecutorBuilder(std::move(IEB)) {
CI = std::move(Instance);
llvm::ErrorAsOutParameter EAO(&ErrOut);
auto LLVMCtx = std::make_unique<llvm::LLVMContext>();
@@ -286,7 +361,7 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance,
ASTContext &C = CI->getASTContext();
IncrParser->RegisterPTU(C.getTranslationUnitDecl(), std::move(M));
}
- if (llvm::Error Err = CreateExecutor(Config)) {
+ if (llvm::Error Err = CreateExecutor()) {
ErrOut = joinErrors(std::move(ErrOut), std::move(Err));
return;
}
@@ -348,25 +423,28 @@ const char *const Runtimes = R"(
EXTERN_C void __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, ...);
)";
-llvm::Expected<std::pair<std::unique_ptr<llvm::orc::LLJITBuilder>, uint32_t>>
-Interpreter::outOfProcessJITBuilder(JITConfig Config) {
+static llvm::Expected<
+ std::pair<std::unique_ptr<llvm::orc::LLJITBuilder>, uint32_t>>
+outOfProcessJITBuilder(const IncrementalExecutorBuilder &IncrExecutorBuilder) {
std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC;
uint32_t childPid = -1;
- if (!Config.OOPExecutor.empty()) {
+ if (!IncrExecutorBuilder.OOPExecutor.empty()) {
// Launch an out-of-process executor locally in a child process.
auto ResultOrErr = IncrementalExecutor::launchExecutor(
- Config.OOPExecutor, Config.UseSharedMemory, Config.SlabAllocateSize,
- Config.CustomizeFork);
+ IncrExecutorBuilder.OOPExecutor, IncrExecutorBuilder.UseSharedMemory,
+ IncrExecutorBuilder.SlabAllocateSize,
+ IncrExecutorBuilder.CustomizeFork);
if (!ResultOrErr)
return ResultOrErr.takeError();
childPid = ResultOrErr->second;
auto EPCOrErr = std::move(ResultOrErr->first);
EPC = std::move(EPCOrErr);
- } else if (Config.OOPExecutorConnect != "") {
+ } else if (IncrExecutorBuilder.OOPExecutorConnect != "") {
#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
auto EPCOrErr = IncrementalExecutor::connectTCPSocket(
- Config.OOPExecutorConnect, Config.UseSharedMemory,
- Config.SlabAllocateSize);
+ IncrExecutorBuilder.OOPExecutorConnect,
+ IncrExecutorBuilder.UseSharedMemory,
+ IncrExecutorBuilder.SlabAllocateSize);
if (!EPCOrErr)
return EPCOrErr.takeError();
EPC = std::move(*EPCOrErr);
@@ -380,7 +458,7 @@ Interpreter::outOfProcessJITBuilder(JITConfig Config) {
std::unique_ptr<llvm::orc::LLJITBuilder> JB;
if (EPC) {
auto JBOrErr = clang::Interpreter::createLLJITBuilder(
- std::move(EPC), Config.OrcRuntimePath);
+ std::move(EPC), IncrExecutorBuilder.OrcRuntimePath);
if (!JBOrErr)
return JBOrErr.takeError();
JB = std::move(*JBOrErr);
@@ -389,84 +467,14 @@ Interpreter::outOfProcessJITBuilder(JITConfig Config) {
return std::make_pair(std::move(JB), childPid);
}
-llvm::Expected<std::string>
-Interpreter::getOrcRuntimePath(const driver::ToolChain &TC) {
- const std::array<const char *, 3> OrcRTLibNames = {
- "liborc_rt.a", "liborc_rt_osx.a", "liborc_rt-x86_64.a"};
-
- auto findInDir = [&](llvm::StringRef Base) -> std::optional<std::string> {
- for (const char *LibName : OrcRTLibNames) {
- llvm::SmallString<256> CandidatePath(Base);
- llvm::sys::path::append(CandidatePath, LibName);
- if (llvm::sys::fs::exists(CandidatePath))
- return std::string(CandidatePath.str());
- }
- return std::nullopt;
- };
-
- std::string SearchedPaths;
-
- if (std::optional<std::string> CompilerRTPath = TC.getCompilerRTPath()) {
- if (auto Found = findInDir(*CompilerRTPath))
- return *Found;
- SearchedPaths += *CompilerRTPath;
- } else {
- return llvm::make_error<llvm::StringError>("CompilerRT path not found",
- std::error_code());
- }
-
- if (std::optional<std::string> ResourceDir = TC.getRuntimePath()) {
- if (auto Found = findInDir(*ResourceDir))
- return *Found;
- if (!SearchedPaths.empty())
- SearchedPaths += "; ";
- SearchedPaths += *ResourceDir;
- } else {
- return llvm::make_error<llvm::StringError>("ResourceDir path not found",
- std::error_code());
- }
-
- return llvm::make_error<llvm::StringError>(
- llvm::Twine("OrcRuntime library not found in: ") + SearchedPaths,
- std::error_code());
-}
-
-llvm::Expected<std::unique_ptr<Interpreter>>
-Interpreter::create(std::unique_ptr<CompilerInstance> CI, JITConfig Config) {
-
- if (Config.IsOutOfProcess) {
- const TargetInfo &TI = CI->getTarget();
- const llvm::Triple &Triple = TI.getTriple();
-
- DiagnosticsEngine &Diags = CI->getDiagnostics();
- std::string BinaryName = llvm::sys::fs::getMainExecutable(nullptr, nullptr);
- driver::Driver Driver(BinaryName, Triple.str(), Diags);
- // Need fake args to get the driver to create a compilation.
- 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());
- }
- if (Config.OrcRuntimePath == "") {
- const clang::driver::ToolChain &TC = C->getDefaultToolChain();
-
- auto OrcRuntimePathOrErr = getOrcRuntimePath(TC);
- if (!OrcRuntimePathOrErr) {
- return OrcRuntimePathOrErr.takeError();
- }
-
- Config.OrcRuntimePath = *OrcRuntimePathOrErr;
- }
- }
-
+llvm::Expected<std::unique_ptr<Interpreter>> Interpreter::create(
+ std::unique_ptr<CompilerInstance> CI,
+ std::unique_ptr<IncrementalExecutorBuilder> IEB /*=nullptr*/) {
llvm::Error Err = llvm::Error::success();
std::unique_ptr<llvm::orc::LLJITBuilder> JB;
auto Interp = std::unique_ptr<Interpreter>(new Interpreter(
- std::move(CI), Err, std::move(JB), /*Consumer=*/nullptr, Config));
+ std::move(CI), Err, std::move(JB), /*Consumer=*/nullptr, std::move(IEB)));
if (auto E = std::move(Err))
return std::move(E);
@@ -560,7 +568,7 @@ size_t Interpreter::getEffectivePTUSize() const {
uint32_t Interpreter::getOutOfProcessExecutorPID() const {
if (IncrExecutor)
- return IncrExecutor->getOutOfProcessChildPid();
+ return IncrExecutorBuilder->ExecutorPID;
return -1;
}
@@ -627,7 +635,7 @@ Interpreter::createLLJITBuilder(
return std::move(*JB);
}
-llvm::Error Interpreter::CreateExecutor(JITConfig Config) {
+llvm::Error Interpreter::CreateExecutor() {
if (IncrExecutor)
return llvm::make_error<llvm::StringError>("Operation failed. "
"Execution engine exists",
@@ -641,13 +649,16 @@ llvm::Error Interpreter::CreateExecutor(JITConfig Config) {
llvm::Triple TargetTriple(TT);
bool IsWindowsTarget = TargetTriple.isOSWindows();
- if (!IsWindowsTarget && Config.IsOutOfProcess) {
+ if (!IncrExecutorBuilder)
+ IncrExecutorBuilder = std::make_unique<IncrementalExecutorBuilder>();
+
+ if (!IsWindowsTarget && IncrExecutorBuilder->IsOutOfProcess) {
if (!JITBuilder) {
- auto ResOrErr = outOfProcessJITBuilder(Config);
+ auto ResOrErr = outOfProcessJITBuilder(*IncrExecutorBuilder);
if (!ResOrErr)
return ResOrErr.takeError();
JITBuilder = std::move(ResOrErr->first);
- Config.ExecutorPID = ResOrErr->second;
+ IncrExecutorBuilder->ExecutorPID = ResOrErr->second;
}
if (!JITBuilder)
return llvm::make_error<llvm::StringError>(
@@ -659,29 +670,19 @@ llvm::Error Interpreter::CreateExecutor(JITConfig Config) {
auto JTMB = createJITTargetMachineBuilder(TT);
if (!JTMB)
return JTMB.takeError();
- if (Config.CM)
- JTMB->setCodeModel(Config.CM);
+ if (IncrExecutorBuilder->CM)
+ JTMB->setCodeModel(IncrExecutorBuilder->CM);
auto JB = IncrementalExecutor::createDefaultJITBuilder(std::move(*JTMB));
if (!JB)
return JB.takeError();
JITBuilder = std::move(*JB);
}
- llvm::Error Err = llvm::Error::success();
-
- // Fix: Declare Execu...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/175322
More information about the cfe-commits
mailing list