https://github.com/anutosh491 updated 
https://github.com/llvm/llvm-project/pull/222531

>From b4491966852910f6fcd22c6c0a1c956211e2e2fc Mon Sep 17 00:00:00 2001
From: anutosh491 <[email protected]>
Date: Tue, 8 Sep 2026 18:42:12 +0530
Subject: [PATCH 1/5] [Support] Add LLVMToolSession for in-process tool
 invocation

---
 llvm/include/llvm/Support/LLVMDriver.h        |  67 +++++++++-
 llvm/lib/Support/CMakeLists.txt               |   1 +
 llvm/lib/Support/LLVMToolSession.cpp          | 119 ++++++++++++++++++
 .../tools/llvm-driver/session-dispatch.test   |   7 ++
 llvm/tools/llvm-driver/llvm-driver.cpp        |  66 +++-------
 llvm/unittests/Support/CMakeLists.txt         |   1 +
 .../Support/LLVMToolSession/CMakeLists.txt    |  20 +++
 .../LLVMToolSession/LLVMToolSessionTest.cpp   |  65 ++++++++++
 8 files changed, 298 insertions(+), 48 deletions(-)
 create mode 100644 llvm/lib/Support/LLVMToolSession.cpp
 create mode 100644 llvm/test/tools/llvm-driver/session-dispatch.test
 create mode 100644 llvm/unittests/Support/LLVMToolSession/CMakeLists.txt
 create mode 100644 
llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp

diff --git a/llvm/include/llvm/Support/LLVMDriver.h 
b/llvm/include/llvm/Support/LLVMDriver.h
index 0b2e265d50b42..48122bfaceb7c 100644
--- a/llvm/include/llvm/Support/LLVMDriver.h
+++ b/llvm/include/llvm/Support/LLVMDriver.h
@@ -9,9 +9,35 @@
 #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 <memory>
+
 namespace llvm {
 
-struct ToolContext {
+class LLVMToolSession;
+class ToolContext;
+
+using ToolMainFn = 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 Main != nullptr; }
+};
+
+/// Describes how a tool was invoked and provides access to its host session.
+class ToolContext {
+  LLVMToolSession *Session = nullptr;
+
+  friend class LLVMToolSession;
+
+public:
   const char *Path;
   const char *PrependArg;
   // PrependArg will be added unconditionally by the llvm-driver, but
@@ -20,6 +46,45 @@ 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 int callTool(ArrayRef<const char *> Args) const;
+};
+
+/// 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.
+class LLVM_ABI LLVMToolSession {
+public:
+  LLVMToolSession(int &Argc, char **&Argv, ArrayRef<CallableTool> Tools,
+                  bool InstallPipeSignalExitHandler = true,
+                  bool NeedsPOSIXUtilitySignalHandling = false);
+  ~LLVMToolSession();
+
+  LLVMToolSession(const LLVMToolSession &) = delete;
+  LLVMToolSession &operator=(const LLVMToolSession &) = 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.
+  int callTool(ArrayRef<const char *> Args);
+
+private:
+  struct Impl;
+  std::unique_ptr<Impl> PImpl;
+
+  ErrorOr<CallableTool> findTool(StringRef Name) const;
+  ToolContext makeContext(StringRef InvokedName);
+
+  friend class ToolContext;
 };
 
 } // namespace llvm
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index e7dfcb0dcd891..c5187f856e541 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -229,6 +229,7 @@ add_llvm_component_library(LLVMSupport
   LineIterator.cpp
   Locale.cpp
   LockFileManager.cpp
+  LLVMToolSession.cpp
   ManagedStatic.cpp
   MathExtras.cpp
   MemAlloc.cpp
diff --git a/llvm/lib/Support/LLVMToolSession.cpp 
b/llvm/lib/Support/LLVMToolSession.cpp
new file mode 100644
index 0000000000000..94f596a9d8903
--- /dev/null
+++ b/llvm/lib/Support/LLVMToolSession.cpp
@@ -0,0 +1,119 @@
+//===-- 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 <string>
+#include <system_error>
+#include <utility>
+#include <vector>
+
+using namespace llvm;
+
+namespace {
+
+bool matchesToolName(StringRef RegisteredName, StringRef InvokedName) {
+  StringRef Stem = sys::path::stem(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(sys::path::filename(InvokedName));
+}
+
+bool isMulticallName(StringRef Name) { return matchesToolName("llvm", Name); }
+
+} // namespace
+
+struct LLVMToolSession::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);
+  }
+};
+
+LLVMToolSession::LLVMToolSession(int &Argc, char **&Argv,
+                                 ArrayRef<CallableTool> Tools,
+                                 bool InstallPipeSignalExitHandler,
+                                 bool NeedsPOSIXUtilitySignalHandling)
+    : PImpl(std::make_unique<Impl>(Argc, Argv, Tools,
+                                   InstallPipeSignalExitHandler,
+                                   NeedsPOSIXUtilitySignalHandling)) {}
+
+LLVMToolSession::~LLVMToolSession() = default;
+
+ErrorOr<CallableTool> LLVMToolSession::findTool(StringRef Name) const {
+  for (const auto &[RegisteredName, Main] : PImpl->Tools)
+    if (matchesToolName(RegisteredName, Name))
+      return CallableTool{RegisteredName, Main};
+  return make_error_code(std::errc::no_such_file_or_directory);
+}
+
+ToolContext LLVMToolSession::makeContext(StringRef InvokedName) {
+  bool NeedsPrependArg = !matchesToolName(InvokedName, PImpl->ExecutablePath);
+  ToolContext Context(PImpl->ExecutablePath.c_str(), InvokedName.data(),
+                      NeedsPrependArg);
+  Context.Session = this;
+  return Context;
+}
+
+int LLVMToolSession::callTool(ArrayRef<const char *> Args) {
+  if (Args.empty())
+    return -1;
+
+  StringRef InvokedName = Args.front();
+  ErrorOr<CallableTool> Tool = findTool(InvokedName);
+  if (!Tool) {
+    if (InvokedName != PImpl->ExecutablePath && !isMulticallName(InvokedName))
+      return -1;
+    Args = Args.drop_front();
+    if (Args.empty())
+      return -1;
+    InvokedName = Args.front();
+    Tool = findTool(InvokedName);
+  }
+
+  if (!Tool)
+    return -1;
+
+  ToolContext Context = makeContext(InvokedName);
+  SmallVector<char *, 16> MutableArgs;
+  MutableArgs.reserve(Args.size() + 1);
+  for (const char *Arg : Args)
+    MutableArgs.push_back(const_cast<char *>(Arg));
+  MutableArgs.push_back(nullptr);
+  return Tool->Main(Args.size(), MutableArgs.data(), Context);
+}
+
+ErrorOr<CallableTool> ToolContext::getCallableTool(StringRef Name) const {
+  if (!Session)
+    return make_error_code(std::errc::operation_not_permitted);
+  return Session->findTool(Name);
+}
+
+int ToolContext::callTool(ArrayRef<const char *> Args) const {
+  if (!Session)
+    return -1;
+  return Session->callTool(Args);
+}
diff --git a/llvm/test/tools/llvm-driver/session-dispatch.test 
b/llvm/test/tools/llvm-driver/session-dispatch.test
new file mode 100644
index 0000000000000..6ac93be6ce431
--- /dev/null
+++ b/llvm/test/tools/llvm-driver/session-dispatch.test
@@ -0,0 +1,7 @@
+# REQUIRES: llvm-driver
+
+## Exercise a real LLVM tool entry point through LLVMToolSession rather than
+## only testing the registry with synthetic callbacks.
+# RUN: %llvm cxxfilt _Z3foov | FileCheck %s
+
+# CHECK: foo()
diff --git a/llvm/tools/llvm-driver/llvm-driver.cpp 
b/llvm/tools/llvm-driver/llvm-driver.cpp
index 14ce162faee46..865d6d432d473 100644
--- a/llvm/tools/llvm-driver/llvm-driver.cpp
+++ b/llvm/tools/llvm-driver/llvm-driver.cpp
@@ -6,14 +6,11 @@
 //
 
//===----------------------------------------------------------------------===//
 
-#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/LLVMDriver.h"
 #include "llvm/Support/Path.h"
-#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
 
 using namespace llvm;
 
@@ -36,51 +33,26 @@ static void printHelpMessage() {
                << "OPTIONS:\n\n  --help - Display this message\n";
 }
 
-static int findTool(int Argc, char **Argv, const char *Argv0) {
-  if (!Argc) {
-    printHelpMessage();
-    return 1;
-  }
+int main(int Argc, char **Argv) {
+  const CallableTool Tools[] = {
+#define LLVM_DRIVER_TOOL(tool, entry) {tool, entry##_main},
+#include "LLVMDriverTools.def"
+  };
 
-  StringRef ToolName = Argv[0];
+  LLVMToolSession Session(Argc, Argv, Tools);
 
-  if (ToolName == "--help") {
+  StringRef Stem = sys::path::stem(Argv[0]);
+  if (Stem.equals_insensitive("llvm") &&
+      (Argc == 1 || (Argc == 2 && StringRef(Argv[1]) == "--help"))) {
     printHelpMessage();
-    return 0;
+    return Argc == 1 ? 1 : 0;
   }
 
-  StringRef Stem = sys::path::stem(ToolName);
-  auto Is = [=](StringRef Tool) {
-    auto IsImpl = [=](StringRef Stem) {
-      auto I = Stem.rfind_insensitive(Tool);
-      return I != StringRef::npos && (I + Tool.size() == Stem.size() ||
-                                      !llvm::isAlnum(Stem[I + Tool.size()]));
-    };
-    for (StringRef S : {Stem, sys::path::filename(ToolName)})
-      if (IsImpl(S))
-        return true;
-    return false;
-  };
-
-  auto MakeDriverArgs = [=]() -> llvm::ToolContext {
-    if (ToolName != Argv0)
-      return {Argv0, ToolName.data(), true};
-    return {Argv0, sys::path::filename(Argv0).data(), false};
-  };
-
-#define LLVM_DRIVER_TOOL(tool, entry)                                          
\
-  if (Is(tool))                                                                
\
-    return entry##_main(Argc, Argv, MakeDriverArgs());
-#include "LLVMDriverTools.def"
-
-  if (Is("llvm") || Argv0 == Argv[0])
-    return findTool(Argc - 1, Argv + 1, Argv0);
-
-  printHelpMessage();
-  return 1;
-}
-
-int main(int Argc, char **Argv) {
-  llvm::InitLLVM X(Argc, Argv);
-  return findTool(Argc, Argv, Argv[0]);
+  SmallVector<const char *, 16> Args(Argv, Argv + Argc);
+  int Result = Session.callTool(Args);
+  if (Result == -1) {
+    printHelpMessage();
+    return 1;
+  }
+  return Result;
 }
diff --git a/llvm/unittests/Support/CMakeLists.txt 
b/llvm/unittests/Support/CMakeLists.txt
index cc88a6c5670ca..f9732ff4e0e4b 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -162,3 +162,4 @@ if(NOT LLVM_INTEGRATED_CRT_ALLOC)
 endif()
 
 add_subdirectory(CommandLineInit)
+add_subdirectory(LLVMToolSession)
diff --git a/llvm/unittests/Support/LLVMToolSession/CMakeLists.txt 
b/llvm/unittests/Support/LLVMToolSession/CMakeLists.txt
new file mode 100644
index 0000000000000..2d343295fb191
--- /dev/null
+++ b/llvm/unittests/Support/LLVMToolSession/CMakeLists.txt
@@ -0,0 +1,20 @@
+set(test_name LLVMToolSessionTests)
+set(test_suite UnitTests)
+
+# This test supplies its own main() so a single LLVMToolSession can own
+# InitLLVM for the complete test process.
+if (NOT LLVM_BUILD_TESTS)
+  set(EXCLUDE_FROM_ALL ON)
+endif()
+
+list(APPEND LLVM_LINK_COMPONENTS Support)
+
+add_llvm_executable(${test_name}
+  IGNORE_EXTERNALIZE_DEBUGINFO NO_INSTALL_RPATH
+  LLVMToolSessionTest.cpp)
+
+target_link_libraries(${test_name} PRIVATE llvm_gtest)
+add_dependencies(${test_suite} ${test_name})
+
+set(outdir ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR})
+set_output_directory(${test_name} BINARY_DIR ${outdir} LIBRARY_DIR ${outdir})
diff --git a/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp 
b/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
new file mode 100644
index 0000000000000..a8a12f3a7e4d1
--- /dev/null
+++ b/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
@@ -0,0 +1,65 @@
+//===- LLVMToolSessionTest.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 "gtest/gtest.h"
+
+#include <memory>
+
+using namespace llvm;
+
+namespace {
+
+std::unique_ptr<LLVMToolSession> Session;
+unsigned CompilerCalls;
+unsigned LinkerCalls;
+
+int linkerMain(int Argc, char **Argv, const ToolContext &Context) {
+  ++LinkerCalls;
+  EXPECT_EQ(Argc, 3);
+  EXPECT_STREQ(Argv[0], "wasm-ld");
+  EXPECT_TRUE(Context.getCallableTool("clang"));
+  return 0;
+}
+
+int compilerMain(int Argc, char **Argv, const ToolContext &Context) {
+  ++CompilerCalls;
+  EXPECT_EQ(Argc, 2);
+  EXPECT_STREQ(Argv[0], "clang");
+  const char *LinkArgs[] = {"wasm-ld", Argv[1], "out.wasm"};
+  return Context.callTool(LinkArgs);
+}
+
+TEST(LLVMToolSessionTest, SupportsSequentialNestedToolCalls) {
+  const char *First[] = {"clang", "first.cpp"};
+  const char *Second[] = {"clang", "second.cpp"};
+
+  EXPECT_EQ(Session->callTool(First), 0);
+  EXPECT_EQ(Session->callTool(Second), 0);
+  EXPECT_EQ(CompilerCalls, 2u);
+  EXPECT_EQ(LinkerCalls, 2u);
+}
+
+TEST(LLVMToolSessionTest, ReportsUnknownTools) {
+  const char *Args[] = {"not-an-llvm-tool"};
+  EXPECT_EQ(Session->callTool(Args), -1);
+}
+
+} // namespace
+
+int main(int Argc, char **Argv) {
+  const CallableTool Tools[] = {
+      {"clang", compilerMain},
+      {"wasm-ld", linkerMain},
+  };
+  Session = std::make_unique<LLVMToolSession>(Argc, Argv, Tools);
+  testing::InitGoogleTest(&Argc, Argv);
+  int Result = RUN_ALL_TESTS();
+  Session.reset();
+  return Result;
+}

>From 70d8573986c1c4596f4dca3f62afa7497670e94f Mon Sep 17 00:00:00 2001
From: anutosh491 <[email protected]>
Date: Fri, 11 Sep 2026 10:54:05 +0530
Subject: [PATCH 2/5] [Support] Address LLVMToolSession review feedback

---
 llvm/include/llvm/Support/LLVMDriver.h        | 14 ++--
 llvm/lib/Support/CMakeLists.txt               |  2 +-
 llvm/lib/Support/LLVMToolSession.cpp          | 41 ++++++----
 .../tools/llvm-driver/session-dispatch.test   |  6 +-
 llvm/tools/llvm-driver/llvm-driver.cpp        |  6 +-
 .../LLVMToolSession/LLVMToolSessionTest.cpp   | 76 +++++++++++++++++--
 6 files changed, 113 insertions(+), 32 deletions(-)

diff --git a/llvm/include/llvm/Support/LLVMDriver.h 
b/llvm/include/llvm/Support/LLVMDriver.h
index 48122bfaceb7c..e1107b0ba57f0 100644
--- a/llvm/include/llvm/Support/LLVMDriver.h
+++ b/llvm/include/llvm/Support/LLVMDriver.h
@@ -14,6 +14,7 @@
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorOr.h"
 
+#include <functional>
 #include <memory>
 
 namespace llvm {
@@ -21,14 +22,14 @@ namespace llvm {
 class LLVMToolSession;
 class ToolContext;
 
-using ToolMainFn = int (*)(int, char **, const 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 Main != nullptr; }
+  explicit operator bool() const { return static_cast<bool>(Main); }
 };
 
 /// Describes how a tool was invoked and provides access to its host session.
@@ -54,7 +55,7 @@ class ToolContext {
   LLVM_ABI ErrorOr<CallableTool> getCallableTool(StringRef Name) const;
 
   /// Invokes another tool registered with the same host session.
-  LLVM_ABI int callTool(ArrayRef<const char *> Args) const;
+  LLVM_ABI ErrorOr<int> callTool(ArrayRef<const char *> Args) const;
 };
 
 /// Owns LLVM process initialization and an in-process tool registry.
@@ -62,6 +63,9 @@ class ToolContext {
 /// 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 LLVMToolSession {
 public:
   LLVMToolSession(int &Argc, char **&Argv, ArrayRef<CallableTool> Tools,
@@ -75,14 +79,14 @@ class LLVM_ABI LLVMToolSession {
   /// 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.
-  int callTool(ArrayRef<const char *> Args);
+  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);
+  ToolContext makeContext(StringRef InvokedName, const char *PrependArg);
 
   friend class ToolContext;
 };
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index c5187f856e541..2b2e6516e54f2 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -227,9 +227,9 @@ add_llvm_component_library(LLVMSupport
   KnownFPClass.cpp
   LEB128.cpp
   LineIterator.cpp
+  LLVMToolSession.cpp
   Locale.cpp
   LockFileManager.cpp
-  LLVMToolSession.cpp
   ManagedStatic.cpp
   MathExtras.cpp
   MemAlloc.cpp
diff --git a/llvm/lib/Support/LLVMToolSession.cpp 
b/llvm/lib/Support/LLVMToolSession.cpp
index 94f596a9d8903..ff63f31ce965c 100644
--- a/llvm/lib/Support/LLVMToolSession.cpp
+++ b/llvm/lib/Support/LLVMToolSession.cpp
@@ -13,6 +13,7 @@
 #include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/Path.h"
 
+#include <cassert>
 #include <string>
 #include <system_error>
 #include <utility>
@@ -56,48 +57,60 @@ struct LLVMToolSession::Impl {
 LLVMToolSession::LLVMToolSession(int &Argc, char **&Argv,
                                  ArrayRef<CallableTool> Tools,
                                  bool InstallPipeSignalExitHandler,
-                                 bool NeedsPOSIXUtilitySignalHandling)
-    : PImpl(std::make_unique<Impl>(Argc, Argv, Tools,
-                                   InstallPipeSignalExitHandler,
-                                   NeedsPOSIXUtilitySignalHandling)) {}
+                                 bool NeedsPOSIXUtilitySignalHandling) {
+  assert(Argc > 0 && Argv && Argv[0] &&
+         "LLVMToolSession requires a valid argv[0]");
+  PImpl = std::make_unique<Impl>(Argc, Argv, Tools,
+                                 InstallPipeSignalExitHandler,
+                                 NeedsPOSIXUtilitySignalHandling);
+}
 
 LLVMToolSession::~LLVMToolSession() = default;
 
 ErrorOr<CallableTool> LLVMToolSession::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 auto &[RegisteredName, Main] : PImpl->Tools)
     if (matchesToolName(RegisteredName, Name))
       return CallableTool{RegisteredName, Main};
   return make_error_code(std::errc::no_such_file_or_directory);
 }
 
-ToolContext LLVMToolSession::makeContext(StringRef InvokedName) {
+ToolContext LLVMToolSession::makeContext(StringRef InvokedName,
+                                         const char *PrependArg) {
   bool NeedsPrependArg = !matchesToolName(InvokedName, PImpl->ExecutablePath);
-  ToolContext Context(PImpl->ExecutablePath.c_str(), InvokedName.data(),
+  ToolContext Context(PImpl->ExecutablePath.c_str(), PrependArg,
                       NeedsPrependArg);
   Context.Session = this;
   return Context;
 }
 
-int LLVMToolSession::callTool(ArrayRef<const char *> Args) {
+ErrorOr<int> LLVMToolSession::callTool(ArrayRef<const char *> Args) {
   if (Args.empty())
-    return -1;
+    return make_error_code(std::errc::invalid_argument);
 
   StringRef InvokedName = Args.front();
   ErrorOr<CallableTool> Tool = findTool(InvokedName);
   if (!Tool) {
     if (InvokedName != PImpl->ExecutablePath && !isMulticallName(InvokedName))
-      return -1;
+      return make_error_code(std::errc::no_such_file_or_directory);
     Args = Args.drop_front();
     if (Args.empty())
-      return -1;
+      return make_error_code(std::errc::invalid_argument);
     InvokedName = Args.front();
     Tool = findTool(InvokedName);
   }
 
   if (!Tool)
-    return -1;
+    return Tool.getError();
 
-  ToolContext Context = makeContext(InvokedName);
+  std::string PrependArg = sys::path::stem(InvokedName).str();
+  ToolContext Context = makeContext(InvokedName, PrependArg.c_str());
   SmallVector<char *, 16> MutableArgs;
   MutableArgs.reserve(Args.size() + 1);
   for (const char *Arg : Args)
@@ -112,8 +125,8 @@ ErrorOr<CallableTool> 
ToolContext::getCallableTool(StringRef Name) const {
   return Session->findTool(Name);
 }
 
-int ToolContext::callTool(ArrayRef<const char *> Args) const {
+ErrorOr<int> ToolContext::callTool(ArrayRef<const char *> Args) const {
   if (!Session)
-    return -1;
+    return make_error_code(std::errc::operation_not_permitted);
   return Session->callTool(Args);
 }
diff --git a/llvm/test/tools/llvm-driver/session-dispatch.test 
b/llvm/test/tools/llvm-driver/session-dispatch.test
index 6ac93be6ce431..609d40cb505e9 100644
--- a/llvm/test/tools/llvm-driver/session-dispatch.test
+++ b/llvm/test/tools/llvm-driver/session-dispatch.test
@@ -2,6 +2,8 @@
 
 ## Exercise a real LLVM tool entry point through LLVMToolSession rather than
 ## only testing the registry with synthetic callbacks.
-# RUN: %llvm cxxfilt _Z3foov | FileCheck %s
+# RUN: %llvm cxxfilt _Z3foov | FileCheck %s --check-prefix=CXXFILT
+# RUN: %llvm --help | FileCheck %s --check-prefix=HELP
 
-# CHECK: foo()
+# CXXFILT: foo()
+# HELP: OVERVIEW: llvm toolchain driver
diff --git a/llvm/tools/llvm-driver/llvm-driver.cpp 
b/llvm/tools/llvm-driver/llvm-driver.cpp
index 865d6d432d473..b9fd476beb8c2 100644
--- a/llvm/tools/llvm-driver/llvm-driver.cpp
+++ b/llvm/tools/llvm-driver/llvm-driver.cpp
@@ -49,10 +49,10 @@ int main(int Argc, char **Argv) {
   }
 
   SmallVector<const char *, 16> Args(Argv, Argv + Argc);
-  int Result = Session.callTool(Args);
-  if (Result == -1) {
+  ErrorOr<int> Result = Session.callTool(Args);
+  if (!Result) {
     printHelpMessage();
     return 1;
   }
-  return Result;
+  return *Result;
 }
diff --git a/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp 
b/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
index a8a12f3a7e4d1..7d604396716d0 100644
--- a/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
+++ b/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
@@ -7,6 +7,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "llvm/Support/LLVMDriver.h"
+#include "llvm/Support/CommandLine.h"
 #include "gtest/gtest.h"
 
 #include <memory>
@@ -18,6 +19,7 @@ namespace {
 std::unique_ptr<LLVMToolSession> Session;
 unsigned CompilerCalls;
 unsigned LinkerCalls;
+unsigned WrapperCalls;
 
 int linkerMain(int Argc, char **Argv, const ToolContext &Context) {
   ++LinkerCalls;
@@ -32,29 +34,89 @@ int compilerMain(int Argc, char **Argv, const ToolContext 
&Context) {
   EXPECT_EQ(Argc, 2);
   EXPECT_STREQ(Argv[0], "clang");
   const char *LinkArgs[] = {"wasm-ld", Argv[1], "out.wasm"};
-  return Context.callTool(LinkArgs);
+  ErrorOr<int> Result = Context.callTool(LinkArgs);
+  EXPECT_TRUE(Result);
+  return Result ? *Result : 1;
+}
+
+int clangWrapperMain(int Argc, char **Argv, const ToolContext &Context) {
+  ++WrapperCalls;
+  EXPECT_EQ(Argc, 1);
+  EXPECT_STREQ(Argv[0], "/tmp/clang-wrapper.exe");
+  EXPECT_STREQ(Context.PrependArg, "clang-wrapper");
+  return 0;
+}
+
+int resetOptionsMain(int, char **, const ToolContext &) {
+  cl::ResetAllOptionOccurrences();
+  return 0;
 }
 
 TEST(LLVMToolSessionTest, SupportsSequentialNestedToolCalls) {
+  unsigned CompilerCallsBefore = CompilerCalls;
+  unsigned LinkerCallsBefore = LinkerCalls;
   const char *First[] = {"clang", "first.cpp"};
   const char *Second[] = {"clang", "second.cpp"};
 
-  EXPECT_EQ(Session->callTool(First), 0);
-  EXPECT_EQ(Session->callTool(Second), 0);
-  EXPECT_EQ(CompilerCalls, 2u);
-  EXPECT_EQ(LinkerCalls, 2u);
+  ErrorOr<int> FirstResult = Session->callTool(First);
+  ASSERT_TRUE(FirstResult);
+  EXPECT_EQ(*FirstResult, 0);
+  ErrorOr<int> SecondResult = Session->callTool(Second);
+  ASSERT_TRUE(SecondResult);
+  EXPECT_EQ(*SecondResult, 0);
+  EXPECT_EQ(CompilerCalls, CompilerCallsBefore + 2);
+  EXPECT_EQ(LinkerCalls, LinkerCallsBefore + 2);
 }
 
 TEST(LLVMToolSessionTest, ReportsUnknownTools) {
-  const char *Args[] = {"not-an-llvm-tool"};
-  EXPECT_EQ(Session->callTool(Args), -1);
+  const char *Args[] = {"not-a-tool"};
+  ErrorOr<int> Result = Session->callTool(Args);
+  EXPECT_FALSE(Result);
+  EXPECT_EQ(Result.getError(),
+            make_error_code(std::errc::no_such_file_or_directory));
+}
+
+TEST(LLVMToolSessionTest, SupportsStatefulCallableTools) {
+  const char *Args[] = {"stateful-tool"};
+  ErrorOr<int> Result = Session->callTool(Args);
+  ASSERT_TRUE(Result);
+  EXPECT_EQ(*Result, 42);
+}
+
+TEST(LLVMToolSessionTest, PrefersExactToolNameBeforeFuzzyMatch) {
+  const char *Args[] = {"/tmp/clang-wrapper.exe"};
+  ErrorOr<int> Result = Session->callTool(Args);
+  ASSERT_TRUE(Result);
+  EXPECT_EQ(*Result, 0);
+  EXPECT_EQ(WrapperCalls, 1u);
+}
+
+TEST(LLVMToolSessionTest, SurvivesCommandLineOptionReset) {
+  const char *ResetArgs[] = {"reset-options"};
+  ErrorOr<int> ResetResult = Session->callTool(ResetArgs);
+  ASSERT_TRUE(ResetResult);
+  EXPECT_EQ(*ResetResult, 0);
+
+  unsigned CompilerCallsBefore = CompilerCalls;
+  const char *CompilerArgs[] = {"clang", "after-reset.cpp"};
+  ErrorOr<int> CompilerResult = Session->callTool(CompilerArgs);
+  ASSERT_TRUE(CompilerResult);
+  EXPECT_EQ(*CompilerResult, 0);
+  EXPECT_EQ(CompilerCalls, CompilerCallsBefore + 1);
 }
 
 } // namespace
 
 int main(int Argc, char **Argv) {
+  int StatefulResult = 42;
   const CallableTool Tools[] = {
       {"clang", compilerMain},
+      {"clang-wrapper", clangWrapperMain},
+      {"reset-options", resetOptionsMain},
+      {"stateful-tool",
+       [&StatefulResult](int, char **, const ToolContext &) {
+         return StatefulResult;
+       }},
       {"wasm-ld", linkerMain},
   };
   Session = std::make_unique<LLVMToolSession>(Argc, Argv, Tools);

>From c40d89063f1f141cf5e1f7be1e7f9e5bfe1306e3 Mon Sep 17 00:00:00 2001
From: anutosh491 <[email protected]>
Date: Fri, 11 Sep 2026 11:01:24 +0530
Subject: [PATCH 3/5] [Support] Apply clang-format

---
 llvm/lib/Support/LLVMToolSession.cpp                        | 6 +++---
 .../Support/LLVMToolSession/LLVMToolSessionTest.cpp         | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Support/LLVMToolSession.cpp 
b/llvm/lib/Support/LLVMToolSession.cpp
index ff63f31ce965c..c92ef835fab7a 100644
--- a/llvm/lib/Support/LLVMToolSession.cpp
+++ b/llvm/lib/Support/LLVMToolSession.cpp
@@ -60,9 +60,9 @@ LLVMToolSession::LLVMToolSession(int &Argc, char **&Argv,
                                  bool NeedsPOSIXUtilitySignalHandling) {
   assert(Argc > 0 && Argv && Argv[0] &&
          "LLVMToolSession requires a valid argv[0]");
-  PImpl = std::make_unique<Impl>(Argc, Argv, Tools,
-                                 InstallPipeSignalExitHandler,
-                                 NeedsPOSIXUtilitySignalHandling);
+  PImpl =
+      std::make_unique<Impl>(Argc, Argv, Tools, InstallPipeSignalExitHandler,
+                             NeedsPOSIXUtilitySignalHandling);
 }
 
 LLVMToolSession::~LLVMToolSession() = default;
diff --git a/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp 
b/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
index 7d604396716d0..a358279c2820d 100644
--- a/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
+++ b/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
@@ -6,8 +6,8 @@
 //
 
//===----------------------------------------------------------------------===//
 
-#include "llvm/Support/LLVMDriver.h"
 #include "llvm/Support/CommandLine.h"
+#include "llvm/Support/LLVMDriver.h"
 #include "gtest/gtest.h"
 
 #include <memory>

>From 79e0c343fd4ce0d4c31a816819955e08daab315e Mon Sep 17 00:00:00 2001
From: anutosh491 <[email protected]>
Date: Wed, 16 Sep 2026 15:37:32 +0530
Subject: [PATCH 4/5] [Support] Rename LLVMToolSession to ToolSession

---
 llvm/include/llvm/Support/LLVMDriver.h        | 20 +++++++--------
 llvm/lib/Support/LLVMToolSession.cpp          | 25 +++++++++----------
 .../tools/llvm-driver/session-dispatch.test   |  2 +-
 llvm/tools/llvm-driver/llvm-driver.cpp        |  2 +-
 .../Support/LLVMToolSession/CMakeLists.txt    |  2 +-
 .../LLVMToolSession/LLVMToolSessionTest.cpp   |  4 +--
 6 files changed, 27 insertions(+), 28 deletions(-)

diff --git a/llvm/include/llvm/Support/LLVMDriver.h 
b/llvm/include/llvm/Support/LLVMDriver.h
index e1107b0ba57f0..83025c9c8e34a 100644
--- a/llvm/include/llvm/Support/LLVMDriver.h
+++ b/llvm/include/llvm/Support/LLVMDriver.h
@@ -19,7 +19,7 @@
 
 namespace llvm {
 
-class LLVMToolSession;
+class ToolSession;
 class ToolContext;
 
 using ToolMainFn = std::function<int(int, char **, const ToolContext &)>;
@@ -34,9 +34,9 @@ struct CallableTool {
 
 /// Describes how a tool was invoked and provides access to its host session.
 class ToolContext {
-  LLVMToolSession *Session = nullptr;
+  ToolSession *Session = nullptr;
 
-  friend class LLVMToolSession;
+  friend class ToolSession;
 
 public:
   const char *Path;
@@ -66,15 +66,15 @@ class ToolContext {
 ///
 /// LLVM tools may use process-global state. Tool invocations must be 
externally
 /// serialized; concurrent calls are not supported.
-class LLVM_ABI LLVMToolSession {
+class LLVM_ABI ToolSession {
 public:
-  LLVMToolSession(int &Argc, char **&Argv, ArrayRef<CallableTool> Tools,
-                  bool InstallPipeSignalExitHandler = true,
-                  bool NeedsPOSIXUtilitySignalHandling = false);
-  ~LLVMToolSession();
+  ToolSession(int &Argc, char **&Argv, ArrayRef<CallableTool> Tools,
+              bool InstallPipeSignalExitHandler = true,
+              bool NeedsPOSIXUtilitySignalHandling = false);
+  ~ToolSession();
 
-  LLVMToolSession(const LLVMToolSession &) = delete;
-  LLVMToolSession &operator=(const LLVMToolSession &) = delete;
+  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
diff --git a/llvm/lib/Support/LLVMToolSession.cpp 
b/llvm/lib/Support/LLVMToolSession.cpp
index c92ef835fab7a..93704610af38c 100644
--- a/llvm/lib/Support/LLVMToolSession.cpp
+++ b/llvm/lib/Support/LLVMToolSession.cpp
@@ -25,20 +25,21 @@ 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(sys::path::filename(InvokedName));
+  return Matches(Stem) || Matches(Filename);
 }
 
 bool isMulticallName(StringRef Name) { return matchesToolName("llvm", Name); }
 
 } // namespace
 
-struct LLVMToolSession::Impl {
+struct ToolSession::Impl {
   InitLLVM Initialization;
   std::string ExecutablePath;
   std::vector<std::pair<std::string, ToolMainFn>> Tools;
@@ -54,20 +55,18 @@ struct LLVMToolSession::Impl {
   }
 };
 
-LLVMToolSession::LLVMToolSession(int &Argc, char **&Argv,
-                                 ArrayRef<CallableTool> Tools,
-                                 bool InstallPipeSignalExitHandler,
-                                 bool NeedsPOSIXUtilitySignalHandling) {
-  assert(Argc > 0 && Argv && Argv[0] &&
-         "LLVMToolSession requires a valid argv[0]");
+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);
 }
 
-LLVMToolSession::~LLVMToolSession() = default;
+ToolSession::~ToolSession() = default;
 
-ErrorOr<CallableTool> LLVMToolSession::findTool(StringRef Name) const {
+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)
@@ -81,8 +80,8 @@ ErrorOr<CallableTool> LLVMToolSession::findTool(StringRef 
Name) const {
   return make_error_code(std::errc::no_such_file_or_directory);
 }
 
-ToolContext LLVMToolSession::makeContext(StringRef InvokedName,
-                                         const char *PrependArg) {
+ToolContext ToolSession::makeContext(StringRef InvokedName,
+                                     const char *PrependArg) {
   bool NeedsPrependArg = !matchesToolName(InvokedName, PImpl->ExecutablePath);
   ToolContext Context(PImpl->ExecutablePath.c_str(), PrependArg,
                       NeedsPrependArg);
@@ -90,7 +89,7 @@ ToolContext LLVMToolSession::makeContext(StringRef 
InvokedName,
   return Context;
 }
 
-ErrorOr<int> LLVMToolSession::callTool(ArrayRef<const char *> Args) {
+ErrorOr<int> ToolSession::callTool(ArrayRef<const char *> Args) {
   if (Args.empty())
     return make_error_code(std::errc::invalid_argument);
 
diff --git a/llvm/test/tools/llvm-driver/session-dispatch.test 
b/llvm/test/tools/llvm-driver/session-dispatch.test
index 609d40cb505e9..a8e627f5a1305 100644
--- a/llvm/test/tools/llvm-driver/session-dispatch.test
+++ b/llvm/test/tools/llvm-driver/session-dispatch.test
@@ -1,6 +1,6 @@
 # REQUIRES: llvm-driver
 
-## Exercise a real LLVM tool entry point through LLVMToolSession rather than
+## Exercise a real LLVM tool entry point through ToolSession rather than
 ## only testing the registry with synthetic callbacks.
 # RUN: %llvm cxxfilt _Z3foov | FileCheck %s --check-prefix=CXXFILT
 # RUN: %llvm --help | FileCheck %s --check-prefix=HELP
diff --git a/llvm/tools/llvm-driver/llvm-driver.cpp 
b/llvm/tools/llvm-driver/llvm-driver.cpp
index b9fd476beb8c2..bfc0f5355ca07 100644
--- a/llvm/tools/llvm-driver/llvm-driver.cpp
+++ b/llvm/tools/llvm-driver/llvm-driver.cpp
@@ -39,7 +39,7 @@ int main(int Argc, char **Argv) {
 #include "LLVMDriverTools.def"
   };
 
-  LLVMToolSession Session(Argc, Argv, Tools);
+  ToolSession Session(Argc, Argv, Tools);
 
   StringRef Stem = sys::path::stem(Argv[0]);
   if (Stem.equals_insensitive("llvm") &&
diff --git a/llvm/unittests/Support/LLVMToolSession/CMakeLists.txt 
b/llvm/unittests/Support/LLVMToolSession/CMakeLists.txt
index 2d343295fb191..2c61f3c535615 100644
--- a/llvm/unittests/Support/LLVMToolSession/CMakeLists.txt
+++ b/llvm/unittests/Support/LLVMToolSession/CMakeLists.txt
@@ -1,7 +1,7 @@
 set(test_name LLVMToolSessionTests)
 set(test_suite UnitTests)
 
-# This test supplies its own main() so a single LLVMToolSession can own
+# This test supplies its own main() so a single ToolSession can own
 # InitLLVM for the complete test process.
 if (NOT LLVM_BUILD_TESTS)
   set(EXCLUDE_FROM_ALL ON)
diff --git a/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp 
b/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
index a358279c2820d..6d3116fbcf55f 100644
--- a/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
+++ b/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
@@ -16,7 +16,7 @@ using namespace llvm;
 
 namespace {
 
-std::unique_ptr<LLVMToolSession> Session;
+std::unique_ptr<ToolSession> Session;
 unsigned CompilerCalls;
 unsigned LinkerCalls;
 unsigned WrapperCalls;
@@ -119,7 +119,7 @@ int main(int Argc, char **Argv) {
        }},
       {"wasm-ld", linkerMain},
   };
-  Session = std::make_unique<LLVMToolSession>(Argc, Argv, Tools);
+  Session = std::make_unique<ToolSession>(Argc, Argv, Tools);
   testing::InitGoogleTest(&Argc, Argv);
   int Result = RUN_ALL_TESTS();
   Session.reset();

>From d20ad0bb66d666b787db9d626e826b861c40a862 Mon Sep 17 00:00:00 2001
From: anutosh491 <[email protected]>
Date: Thu, 10 Sep 2026 12:22:00 +0530
Subject: [PATCH 5/5] [clang] Support multiple in-process cc1 jobs in a
 ToolSession

---
 clang/include/clang/Driver/Job.h              |  6 ++++
 clang/lib/Driver/Job.cpp                      |  8 +++++
 clang/test/Driver/cc1-spawnprocess.c          |  2 ++
 clang/test/Driver/clang-translation.c         |  2 --
 clang/test/Driver/in-process-multiple-cc1.c   | 36 +++++++++++++++++++
 clang/tools/driver/driver.cpp                 | 13 +++++++
 llvm/include/llvm/Support/LLVMDriver.h        |  3 ++
 .../LLVMToolSession/LLVMToolSessionTest.cpp   |  6 ++++
 8 files changed, 74 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/Driver/in-process-multiple-cc1.c

diff --git a/clang/include/clang/Driver/Job.h b/clang/include/clang/Driver/Job.h
index 03779be5b5a6a..796acafc1fae9 100644
--- a/clang/include/clang/Driver/Job.h
+++ b/clang/include/clang/Driver/Job.h
@@ -177,6 +177,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 +241,9 @@ class Command {
 
   void replaceExecutable(const char *Exe) { Executable = Exe; }
 
+  /// 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..6eddb3f53c3e7 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"
@@ -387,6 +388,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 +403,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-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..bcf3bc4c48eec 100644
--- a/clang/tools/driver/driver.cpp
+++ b/clang/tools/driver/driver.cpp
@@ -387,6 +387,19 @@ 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;
+    }
+  }
+
   Driver::ReproLevel ReproLevel = Driver::ReproLevel::OnCrash;
   if (Arg *A = C->getArgs().getLastArg(options::OPT_gen_reproducer_eq)) {
     auto Level =
diff --git a/llvm/include/llvm/Support/LLVMDriver.h 
b/llvm/include/llvm/Support/LLVMDriver.h
index 83025c9c8e34a..3660f03b3977b 100644
--- a/llvm/include/llvm/Support/LLVMDriver.h
+++ b/llvm/include/llvm/Support/LLVMDriver.h
@@ -56,6 +56,9 @@ class ToolContext {
 
   /// 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.
diff --git a/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp 
b/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
index 6d3116fbcf55f..0dd96faafabd5 100644
--- a/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
+++ b/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp
@@ -25,6 +25,7 @@ int linkerMain(int Argc, char **Argv, const ToolContext 
&Context) {
   ++LinkerCalls;
   EXPECT_EQ(Argc, 3);
   EXPECT_STREQ(Argv[0], "wasm-ld");
+  EXPECT_TRUE(Context.hasSession());
   EXPECT_TRUE(Context.getCallableTool("clang"));
   return 0;
 }
@@ -52,6 +53,11 @@ int resetOptionsMain(int, char **, const ToolContext &) {
   return 0;
 }
 
+TEST(LLVMToolSessionTest, DistinguishesStandaloneContext) {
+  ToolContext Context("clang", nullptr, false);
+  EXPECT_FALSE(Context.hasSession());
+}
+
 TEST(LLVMToolSessionTest, SupportsSequentialNestedToolCalls) {
   unsigned CompilerCallsBefore = CompilerCalls;
   unsigned LinkerCallsBefore = LinkerCalls;

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to