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
