llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-driver

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
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to