[clang] [lld] [llvm] [WIP][clang][lld] Dispatch registered linkers through ToolSession (PR #224023)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 16 06:58:32 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-support
Author: Anutosh Bhat (anutosh491)
<details>
<summary>Changes</summary>
Please read: [RFC: Embeddable LLVM tool drivers for long-lived hosts](https://discourse.llvm.org/t/rfc-embeddable-llvm-tool-drivers-for-long-lived-hosts/91754)
Depends on #<!-- -->221996 and #<!-- -->222531.
This is the third small patch in the embeddable-tools series. After #<!-- -->222531 allows session-owned Clang to execute multiple `cc1` jobs, this patch allows Clang to dispatch a registered linker through the same `ToolSession`.
Clang continues to construct its normal jobs. A linker job runs in-process only when its executable is registered and belongs to the current session; unrelated external linkers retain the normal subprocess path.
This enables commands such as:
```bash
clang++ --target=wasm32-unknown-emscripten add.cpp main.cpp -o project.wasm
```
to execute as:
```text
ToolSession
└── clang
├── cc1(add.cpp)
├── cc1(main.cpp)
└── wasm-ld(add.o, main.o)
```
The mechanism is target-independent. The tests cover in-process WebAssembly and AArch64/ELF links, ensure an unrelated external `ld` is not intercepted, and exercise repeated LLD execution.
Session-owned LLD uses its existing reusable cleanup path before returning to Clang. Standalone LLD keeps its current fast path.
---
Patch is 31.63 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/224023.diff
17 Files Affected:
- (modified) clang/include/clang/Driver/Job.h (+20)
- (modified) clang/lib/Driver/Job.cpp (+27)
- (modified) clang/test/Driver/cc1-spawnprocess.c (+2)
- (modified) clang/test/Driver/clang-translation.c (-2)
- (added) clang/test/Driver/in-process-link.c (+48)
- (added) clang/test/Driver/in-process-multiple-cc1.c (+36)
- (modified) clang/tools/driver/driver.cpp (+35)
- (modified) lld/tools/lld/lld.cpp (+9-5)
- (modified) llvm/include/llvm/Support/LLVMDriver.h (+73-1)
- (modified) llvm/lib/Support/CMakeLists.txt (+1)
- (added) llvm/lib/Support/LLVMToolSession.cpp (+131)
- (modified) llvm/test/tools/llvm-driver/passthrough-lld.test (+3)
- (added) llvm/test/tools/llvm-driver/session-dispatch.test (+9)
- (modified) llvm/tools/llvm-driver/llvm-driver.cpp (+19-47)
- (modified) llvm/unittests/Support/CMakeLists.txt (+1)
- (added) llvm/unittests/Support/LLVMToolSession/CMakeLists.txt (+20)
- (added) llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp (+133)
``````````diff
diff --git a/clang/include/clang/Driver/Job.h b/clang/include/clang/Driver/Job.h
index 03779be5b5a6a..03db9c9cc236a 100644
--- a/clang/include/clang/Driver/Job.h
+++ b/clang/include/clang/Driver/Job.h
@@ -24,6 +24,10 @@
#include <utility>
#include <vector>
+namespace llvm {
+class ToolContext;
+}
+
namespace clang {
namespace driver {
@@ -151,6 +155,10 @@ class Command {
/// Information on executable run provided by OS.
mutable std::optional<llvm::sys::ProcessStatistics> ProcStat;
+ /// Non-owning host context used to invoke this command without spawning a
+ /// process.
+ const llvm::ToolContext *InProcessToolContext = nullptr;
+
/// The bound architecture for this command (e.g. "arm64", "gfx90a").
std::string BoundArchStr;
@@ -177,6 +185,9 @@ class Command {
/// Whether the command will be executed in this process or not.
bool InProcess = false;
+ /// Whether this command accepts -disable-free.
+ bool SupportsDisableFree = false;
+
Command(const Action &Source, const Tool &Creator,
ResponseFileSupport ResponseSupport, const char *Executable,
const llvm::opt::ArgStringList &Arguments, ArrayRef<InputInfo> Inputs,
@@ -238,6 +249,15 @@ class Command {
void replaceExecutable(const char *Exe) { Executable = Exe; }
+ /// Execute this command through a tool registered with the host session.
+ void setInProcessToolContext(const llvm::ToolContext &Context) {
+ InProcessToolContext = &Context;
+ InProcess = true;
+ }
+
+ /// Ensure that this command frees memory before returning to its caller.
+ void enableFree();
+
const char *getExecutable() const { return Executable; }
const llvm::opt::ArgStringList &getArguments() const { return Arguments; }
diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp
index da7a1f2e07e90..6cbe7e0252721 100644
--- a/clang/lib/Driver/Job.cpp
+++ b/clang/lib/Driver/Job.cpp
@@ -13,6 +13,7 @@
#include "clang/Driver/Tool.h"
#include "clang/Driver/ToolChain.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
@@ -22,6 +23,7 @@
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/IOSandbox.h"
+#include "llvm/Support/LLVMDriver.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Program.h"
@@ -205,6 +207,9 @@ rewriteIncludes(const llvm::ArrayRef<const char *> &Args, size_t Idx,
void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote,
CrashReportInfo *CrashInfo) const {
+ if (InProcessToolContext)
+ OS << " (in-process)\n";
+
// Always quote the exe.
OS << ' ';
llvm::sys::printArg(OS, Executable, /*Quote=*/true);
@@ -367,6 +372,21 @@ int Command::Execute(ArrayRef<std::optional<StringRef>> Redirects,
auto Args = llvm::toStringRefArray(Argv.data());
+ if (InProcessToolContext) {
+ llvm::ErrorOr<int> Result = InProcessToolContext->callTool(
+ ArrayRef<const char *>(Argv).drop_back());
+ if (!Result) {
+ if (ErrMsg)
+ *ErrMsg = Result.getError().message();
+ if (ExecutionFailed)
+ *ExecutionFailed = true;
+ return -1;
+ }
+ if (ExecutionFailed)
+ *ExecutionFailed = false;
+ return *Result;
+ }
+
// Use Job-specific redirect files if they are present.
if (!RedirectFiles.empty()) {
std::vector<std::optional<StringRef>> RedirectFilesOptional;
@@ -387,6 +407,12 @@ int Command::Execute(ArrayRef<std::optional<StringRef>> Redirects,
ErrMsg, ExecutionFailed, &ProcStat);
}
+void Command::enableFree() {
+ llvm::erase_if(Arguments, [](const char *Arg) {
+ return StringRef(Arg) == "-disable-free";
+ });
+}
+
CC1Command::CC1Command(const Action &Source, const Tool &Creator,
ResponseFileSupport ResponseSupport,
const char *Executable,
@@ -396,6 +422,7 @@ CC1Command::CC1Command(const Action &Source, const Tool &Creator,
: Command(Source, Creator, ResponseSupport, Executable, Arguments, Inputs,
Outputs, PrependArg) {
InProcess = true;
+ SupportsDisableFree = true;
}
void CC1Command::Print(raw_ostream &OS, const char *Terminator, bool Quote,
diff --git a/clang/test/Driver/cc1-spawnprocess.c b/clang/test/Driver/cc1-spawnprocess.c
index a6ff7d148604e..deb774e3a0cfa 100644
--- a/clang/test/Driver/cc1-spawnprocess.c
+++ b/clang/test/Driver/cc1-spawnprocess.c
@@ -1,3 +1,5 @@
+// UNSUPPORTED: llvm-driver
+
// If a toolchain uses an external assembler, the test would fail because using
// an external assember would increase job counts. Most toolchains in tree
// use integrated assembler, but we still support external assembler.
diff --git a/clang/test/Driver/clang-translation.c b/clang/test/Driver/clang-translation.c
index 5ec052a7aaa11..53ea9d98330dd 100644
--- a/clang/test/Driver/clang-translation.c
+++ b/clang/test/Driver/clang-translation.c
@@ -2,7 +2,6 @@
// I386: "-triple" "i386-unknown-unknown"
// I386: "-Os"
// I386: "-S"
-// I386: "-disable-free"
// I386: "-mrelocation-model" "static"
// I386: "-mframe-pointer=all"
// I386: "-funwind-tables=2"
@@ -11,7 +10,6 @@
// I386: clang-translation
// RUN: %clang -target i386-unknown-unknown -### -S %s -o %t.s -Xclang -no-disable-free 2>&1 | FileCheck -check-prefix=FREE %s
-// FREE: "-disable-free"
// FREE: "-no-disable-free"
// RUN: %clang -target i386-unknown-unknown -### -S %s -fasynchronous-unwind-tables -fno-unwind-tables 2>&1 | FileCheck --check-prefix=UNWIND-TABLES %s --implicit-check-not=warning:
diff --git a/clang/test/Driver/in-process-link.c b/clang/test/Driver/in-process-link.c
new file mode 100644
index 0000000000000..1b3bacf66532c
--- /dev/null
+++ b/clang/test/Driver/in-process-link.c
@@ -0,0 +1,48 @@
+// REQUIRES: llvm-driver, lld, aarch64-registered-target
+// REQUIRES: webassembly-registered-target
+
+// Clang and LLD are registered in one ToolSession. Verify that frontend and
+// linker jobs can execute in-process for both WebAssembly and native targets.
+// RUN: split-file %s %t
+// RUN: %clang --target=wasm32-unknown-unknown -nostdlib -fuse-ld=lld \
+// RUN: -Wl,--no-entry -### %t/add.c %t/sub.c 2>&1 \
+// RUN: | FileCheck %s --check-prefix=PRINT
+// PRINT-COUNT-3: (in-process)
+// PRINT: {{.*}}wasm-ld
+
+// The mechanism is target-independent: a registered ELF linker is also
+// dispatched in-process.
+// RUN: %clang --target=aarch64-unknown-linux-gnu -nostdlib -fuse-ld=lld \
+// RUN: -Wl,-e,add -### %t/add.c 2>&1 \
+// RUN: | FileCheck %s --check-prefix=ELF-PRINT
+// ELF-PRINT-COUNT-2: (in-process)
+// ELF-PRINT: {{.*}}ld.lld
+
+// An unrelated external linker with a registered name remains out-of-process.
+// RUN: %if !system-windows %{ mkdir -p %t.external && touch %t.external/ld && \
+// RUN: chmod +x %t.external/ld %}
+// RUN: %if !system-windows %{ %clang --target=aarch64-unknown-linux-gnu \
+// RUN: -nostdlib -B%t.external -### %t/add.c 2>&1 \
+// RUN: | FileCheck %s --check-prefix=EXTERNAL %}
+// EXTERNAL: (in-process)
+// EXTERNAL-NOT: (in-process)
+// EXTERNAL: {{.*}}external{{[/\\]}}ld
+
+// RUN: env LLD_IN_TEST=2 %clang --target=aarch64-unknown-linux-gnu \
+// RUN: -nostdlib -fuse-ld=lld -Wl,-e,add %t/add.c -o %t.elf
+// RUN: llvm-readobj --file-headers %t.elf \
+// RUN: | FileCheck %s --check-prefix=ELF
+// ELF: Format: elf64-littleaarch64
+
+// RUN: env LLD_IN_TEST=2 %clang --target=wasm32-unknown-unknown \
+// RUN: -nostdlib -fuse-ld=lld \
+// RUN: -Wl,--no-entry -Wl,--export=add -Wl,--export=sub \
+// RUN: %t/add.c %t/sub.c -o %t.wasm
+// RUN: llvm-readobj --file-headers %t.wasm | FileCheck %s --check-prefix=WASM
+// WASM: Format: WASM
+
+//--- add.c
+int add(int lhs, int rhs) { return lhs + rhs; }
+
+//--- sub.c
+int sub(int lhs, int rhs) { return lhs - rhs; }
diff --git a/clang/test/Driver/in-process-multiple-cc1.c b/clang/test/Driver/in-process-multiple-cc1.c
new file mode 100644
index 0000000000000..286405c96b8ce
--- /dev/null
+++ b/clang/test/Driver/in-process-multiple-cc1.c
@@ -0,0 +1,36 @@
+// REQUIRES: llvm-driver, webassembly-registered-target
+
+// A Clang invocation owned by ToolSession can execute multiple cc1 jobs
+// in-process. Each job must free its CompilerInstance before returning.
+// RUN: split-file %s %t
+// RUN: cd %t && %clang --target=wasm32-unknown-unknown -c -### \
+// RUN: first.c second.c 2>&1 \
+// RUN: | FileCheck %s --check-prefix=COMMANDS \
+// RUN: --implicit-check-not='"-disable-free"'
+// COMMANDS-COUNT-2: (in-process)
+
+// The same cleanup rule applies to a single cc1 job because the session stays
+// alive after the top-level Clang invocation returns.
+// RUN: cd %t && %clang --target=wasm32-unknown-unknown -c -### first.c 2>&1 \
+// RUN: | FileCheck %s --check-prefix=SINGLE \
+// RUN: --implicit-check-not='"-disable-free"'
+// SINGLE: (in-process)
+
+// An explicit request for a separate cc1 process remains authoritative.
+// RUN: cd %t && %clang --target=wasm32-unknown-unknown \
+// RUN: -fno-integrated-cc1 -c -### first.c second.c 2>&1 \
+// RUN: | FileCheck %s --check-prefix=SPAWN \
+// RUN: --implicit-check-not='(in-process)'
+// SPAWN-COUNT-2: "-disable-free"
+
+// Exercise the jobs and verify that both objects were emitted successfully.
+// RUN: cd %t && %clang --target=wasm32-unknown-unknown -c first.c second.c
+// RUN: llvm-readobj --file-headers %t/first.o %t/second.o \
+// RUN: | FileCheck %s --check-prefix=OBJECTS
+// OBJECTS-COUNT-2: Format: WASM
+
+//--- first.c
+int first(void) { return 1; }
+
+//--- second.c
+int second(void) { return 2; }
diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp
index d4d913a8977a4..633f2b1bde0bd 100644
--- a/clang/tools/driver/driver.cpp
+++ b/clang/tools/driver/driver.cpp
@@ -16,6 +16,7 @@
#include "clang/Basic/HeaderInclude.h"
#include "clang/Basic/Stack.h"
#include "clang/Config/config.h"
+#include "clang/Driver/Action.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/ToolChain.h"
@@ -63,6 +64,14 @@ using namespace clang;
using namespace clang::driver;
using namespace llvm::opt;
+static bool isSessionOwnedTool(const llvm::ToolContext &Context,
+ llvm::StringRef Executable) {
+ // A bare name means that Clang did not find an external executable. A path
+ // is session-owned only when it names the session's multicall binary.
+ return llvm::sys::path::parent_path(Executable).empty() ||
+ llvm::sys::fs::equivalent(Executable, Context.Path);
+}
+
std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) {
if (!CanonicalPrefixes) {
SmallString<128> ExecutablePath(Argv0);
@@ -387,6 +396,32 @@ int clang_main(int Argc, char **Argv, const llvm::ToolContext &ToolContext) {
std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Args));
+ // A long-lived host cannot rely on process exit to reclaim memory after a
+ // cc1 invocation. Run each cc1 job through Clang's existing callback and
+ // ensure that it destroys its CompilerInstance before returning.
+ if (ToolContext.hasSession() && !UseNewCC1Process &&
+ !TheDriver.CCPrintProcessStats) {
+ for (Command &Job : C->getJobs()) {
+ if (!Job.SupportsDisableFree)
+ continue;
+ Job.enableFree();
+ Job.InProcess = true;
+ }
+ }
+
+ // A host session may provide the linker as a callable tool. Other external
+ // commands retain the subprocess path.
+ if (ToolContext.hasSession()) {
+ for (Command &Job : C->getJobs()) {
+ if (!isa<LinkJobAction>(Job.getSource()))
+ continue;
+ if (!isSessionOwnedTool(ToolContext, Job.getExecutable()))
+ continue;
+ if (ToolContext.getCallableTool(Job.getExecutable()))
+ Job.setInProcessToolContext(ToolContext);
+ }
+ }
+
Driver::ReproLevel ReproLevel = Driver::ReproLevel::OnCrash;
if (Arg *A = C->getArgs().getLastArg(options::OPT_gen_reproducer_eq)) {
auto Level =
diff --git a/lld/tools/lld/lld.cpp b/lld/tools/lld/lld.cpp
index d6800fa1eea4b..711fab35d646f 100644
--- a/lld/tools/lld/lld.cpp
+++ b/lld/tools/lld/lld.cpp
@@ -72,7 +72,7 @@ LLD_HAS_DRIVER(mingw)
LLD_HAS_DRIVER(macho)
LLD_HAS_DRIVER(wasm)
-int lld_main(int argc, char **argv, const llvm::ToolContext &) {
+int lld_main(int argc, char **argv, const llvm::ToolContext &ToolContext) {
sys::Process::UseANSIEscapeCodes(true);
if (::getenv("FORCE_LLD_DIAGNOSTICS_CRASH")) {
@@ -83,9 +83,10 @@ int lld_main(int argc, char **argv, const llvm::ToolContext &) {
ArrayRef<const char *> args(argv, argv + argc);
- // Not running in lit tests, just take the shortest codepath with global
- // exception handling and no memory cleanup on exit.
- if (!inTestVerbosity()) {
+ // A standalone invocation can take the shortest path and let process exit
+ // reclaim its resources. A session-owned invocation must clean up before
+ // returning to its host.
+ if (!ToolContext.hasSession() && !inTestVerbosity()) {
int r =
lld::unsafeLldMain(args, llvm::outs(), llvm::errs(), LLD_ALL_DRIVERS,
/*exitEarly=*/true);
@@ -94,8 +95,11 @@ int lld_main(int argc, char **argv, const llvm::ToolContext &) {
std::optional<int> mainRet;
CrashRecoveryContext::Enable();
+ unsigned Iterations = inTestVerbosity();
+ if (!Iterations)
+ Iterations = 1;
- for (unsigned i = inTestVerbosity(); i > 0; --i) {
+ for (unsigned i = Iterations; i > 0; --i) {
// Disable stdout/stderr for all iterations but the last one.
inTestOutputDisabled = (i != 1);
diff --git a/llvm/include/llvm/Support/LLVMDriver.h b/llvm/include/llvm/Support/LLVMDriver.h
index 0b2e265d50b42..3660f03b3977b 100644
--- a/llvm/include/llvm/Support/LLVMDriver.h
+++ b/llvm/include/llvm/Support/LLVMDriver.h
@@ -9,9 +9,36 @@
#ifndef LLVM_SUPPORT_LLVMDRIVER_H
#define LLVM_SUPPORT_LLVMDRIVER_H
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorOr.h"
+
+#include <functional>
+#include <memory>
+
namespace llvm {
-struct ToolContext {
+class ToolSession;
+class ToolContext;
+
+using ToolMainFn = std::function<int(int, char **, const ToolContext &)>;
+
+/// An LLVM command-line tool that can be invoked without creating a process.
+struct CallableTool {
+ StringRef Name;
+ ToolMainFn Main;
+
+ explicit operator bool() const { return static_cast<bool>(Main); }
+};
+
+/// Describes how a tool was invoked and provides access to its host session.
+class ToolContext {
+ ToolSession *Session = nullptr;
+
+ friend class ToolSession;
+
+public:
const char *Path;
const char *PrependArg;
// PrependArg will be added unconditionally by the llvm-driver, but
@@ -20,6 +47,51 @@ struct ToolContext {
// point to the llvm-driver executable, where PrependArg will be needed to
// invoke the correct tool.
bool NeedsPrependArg;
+
+ ToolContext(const char *Path, const char *PrependArg, bool NeedsPrependArg)
+ : Path(Path), PrependArg(PrependArg), NeedsPrependArg(NeedsPrependArg) {}
+
+ /// Finds a tool registered with the session that owns this context.
+ LLVM_ABI ErrorOr<CallableTool> getCallableTool(StringRef Name) const;
+
+ /// Invokes another tool registered with the same host session.
+ LLVM_ABI ErrorOr<int> callTool(ArrayRef<const char *> Args) const;
+
+ /// Returns true when this invocation is owned by a tool session.
+ bool hasSession() const { return Session != nullptr; }
+};
+
+/// Owns LLVM process initialization and an in-process tool registry.
+///
+/// A long-lived host constructs one session and uses it for every embedded
+/// tool invocation. The individual tools borrow a ToolContext and therefore do
+/// not initialize or shut down LLVM themselves.
+///
+/// LLVM tools may use process-global state. Tool invocations must be externally
+/// serialized; concurrent calls are not supported.
+class LLVM_ABI ToolSession {
+public:
+ ToolSession(int &Argc, char **&Argv, ArrayRef<CallableTool> Tools,
+ bool InstallPipeSignalExitHandler = true,
+ bool NeedsPOSIXUtilitySignalHandling = false);
+ ~ToolSession();
+
+ ToolSession(const ToolSession &) = delete;
+ ToolSession &operator=(const ToolSession &) = delete;
+
+ /// Invokes the tool named by Args[0]. Args may instead contain a
+ /// process-style argv beginning with the session executable or an LLVM
+ /// multicall name.
+ ErrorOr<int> callTool(ArrayRef<const char *> Args);
+
+private:
+ struct Impl;
+ std::unique_ptr<Impl> PImpl;
+
+ ErrorOr<CallableTool> findTool(StringRef Name) const;
+ ToolContext makeContext(StringRef InvokedName, const char *PrependArg);
+
+ friend class ToolContext;
};
} // namespace llvm
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index e7dfcb0dcd891..2b2e6516e54f2 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -227,6 +227,7 @@ add_llvm_component_library(LLVMSupport
KnownFPClass.cpp
LEB128.cpp
LineIterator.cpp
+ LLVMToolSession.cpp
Locale.cpp
LockFileManager.cpp
ManagedStatic.cpp
diff --git a/llvm/lib/Support/LLVMToolSession.cpp b/llvm/lib/Support/LLVMToolSession.cpp
new file mode 100644
index 0000000000000..93704610af38c
--- /dev/null
+++ b/llvm/lib/Support/LLVMToolSession.cpp
@@ -0,0 +1,131 @@
+//===-- LLVMToolSession.cpp ----------------------------------------------===//
+//
+// 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/Support/LLVMDriver.h"
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Path.h"
+
+#include <cassert>
+#include <string>
+#include <system_error>
+#include <utility>
+#include <vector>
+
+using namespace llvm;
+
+namespace {
+
+bool matchesToolName(StringRef RegisteredName, StringRef InvokedName) {
+ StringRef Stem = sys::path::stem(InvokedName);
+ StringRef Filename = sys::path::filename(InvokedName);
+ auto Matches = [RegisteredName](StringRef Candidate) {
+ size_t Position = Candidate.rfind_insensitive(RegisteredName);
+ return Position != StringRef::npos &&
+ (Position + RegisteredName.size() == Candidate.size() ||
+ !llvm::isAlnum(Candidate[Position + RegisteredName.size()]));
+ };
+ return Matches(Stem) || Matches(Filename);
+}
+
+bool isMulticallName(StringRef Name) { return matchesToolName("llvm", Name); }
+
+} // namespace
+
+struct ToolSession::Impl {
+ InitLLVM Initialization;
+ std::string ExecutablePath;
+ std::vector<std::pair<std::string, ToolMainFn>> Tools;
+
+ Impl(int &Argc, char **&Argv, ArrayRef<CallableTool> RegisteredTools,
+ bool InstallPipeSignalExitHandler, bool NeedsPOSIXUtilitySignalHandling)
+ : Initialization(Argc, Argv, InstallPipeSignalExitHandler,
+ NeedsPOSIXUtilitySignalHandling),
+ ExecutablePath(Argv[0]) {
+ Tools.reserve(RegisteredTools.size());
+ for (const CallableTool &Tool : RegisteredTools)
+ Tools.emplace_back(Tool.Name.str(), Tool.Main);
+ }
+};
+
+ToolSession::ToolSession(int &Argc, char **&Argv, ArrayRef<CallableTool> Tools,
+ bool InstallPipeSignalExitHandler,
+ bool NeedsPOSIXUtilitySignalHandling) {
+ assert(Argc > 0 && Argv && Argv[0] && "ToolSession requires a valid argv[0]");
+ PImpl =
+ std::make_unique<Impl>(Argc, Argv, Tools, InstallPipeSignalExitHandler,
+ NeedsPOSIXUtilitySignalHandling);
+}
+
+ToolSession::~ToolSession() = default;
+
+ErrorOr<CallableTool> ToolSession::findTool(StringRef Name) const {
+ StringRef Stem = sys::path::stem(Name);
+ StringRef Filename = sys::path::filename(Name);
+ for (const auto &[RegisteredName, Main] : PImpl->Tools)
+ if (Stem.equals_insensitive(RegisteredName) ||
+ Filename.equals_insensitive(RegisteredName))
+ return CallableTool{RegisteredName, Main};
+
+ for (const a...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/224023
More information about the cfe-commits
mailing list