https://github.com/anutosh491 updated https://github.com/llvm/llvm-project/pull/222531
>From a8ddbf7f8cbfe10cdcc62a915d4f640beab48218 Mon Sep 17 00:00:00 2001 From: anutosh491 <[email protected]> Date: Thu, 10 Sep 2026 12:22:00 +0530 Subject: [PATCH] [clang] Support multiple in-process cc1 jobs in a ToolSession --- clang/include/clang/Driver/Compilation.h | 9 ++- clang/include/clang/Driver/Driver.h | 6 +- clang/include/clang/Driver/Job.h | 6 ++ clang/lib/Driver/Compilation.cpp | 7 +- clang/lib/Driver/Driver.cpp | 12 ++-- clang/lib/Driver/Job.cpp | 13 ++-- clang/lib/Driver/ToolChains/Clang.cpp | 7 +- clang/test/Driver/cc1-spawnprocess.c | 11 +-- clang/test/Driver/clang-translation.c | 4 +- .../Driver/in-process-multiple-cc1-crash.c | 20 ++++++ clang/test/Driver/in-process-multiple-cc1.c | 68 +++++++++++++++++++ clang/tools/driver/cc1_main.cpp | 40 ++++++----- clang/tools/driver/driver.cpp | 4 +- llvm/include/llvm/Support/Driver.h | 3 + .../LLVMToolSession/LLVMToolSessionTest.cpp | 6 ++ 15 files changed, 175 insertions(+), 41 deletions(-) create mode 100644 clang/test/Driver/in-process-multiple-cc1-crash.c create mode 100644 clang/test/Driver/in-process-multiple-cc1.c diff --git a/clang/include/clang/Driver/Compilation.h b/clang/include/clang/Driver/Compilation.h index 825806b6cfe332..79960bb6e80a12 100644 --- a/clang/include/clang/Driver/Compilation.h +++ b/clang/include/clang/Driver/Compilation.h @@ -121,6 +121,10 @@ class Compilation { /// Whether we're compiling for diagnostic purposes. bool ForDiagnostics = false; + /// Whether the in-process cc1 callback supports repeated invocation. + /// Reusable callbacks must free per-invocation state before returning. + bool CC1MainIsReusable = false; + /// Whether an error during the parsing of the input args. bool ContainsError; @@ -134,7 +138,8 @@ class Compilation { public: Compilation(const Driver &D, const ToolChain &DefaultToolChain, llvm::opt::InputArgList *Args, - llvm::opt::DerivedArgList *TranslatedArgs, bool ContainsError); + llvm::opt::DerivedArgList *TranslatedArgs, bool ContainsError, + bool CC1MainIsReusable); ~Compilation(); const Driver &getDriver() const { return TheDriver; } @@ -337,6 +342,8 @@ class Compilation { /// Return true if we're compiling for diagnostics. bool isForDiagnostics() const { return ForDiagnostics; } + bool isCC1MainReusable() const { return CC1MainIsReusable; } + /// Return whether an error during the parsing of the input args. bool containsError() const { return ContainsError; } diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h index 6ba0acacdeaf26..7d75e5e03a79d1 100644 --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -464,11 +464,15 @@ class Driver { /// BuildCompilation - Construct a compilation object for a command /// line argument vector. /// + /// \param CC1MainIsReusable Whether the in-process cc1 callback supports + /// repeated invocation and therefore requires normal per-job cleanup. + /// /// \return A compilation, or 0 if none was built for the given /// argument vector. A null return value does not necessarily /// indicate an error condition, the diagnostics should be queried /// to determine if an error occurred. - Compilation *BuildCompilation(ArrayRef<const char *> Args); + Compilation *BuildCompilation(ArrayRef<const char *> Args, + bool CC1MainIsReusable = false); /// ParseArgStrings - Parse the given list of strings into an /// ArgList. diff --git a/clang/include/clang/Driver/Job.h b/clang/include/clang/Driver/Job.h index 03779be5b5a6a0..5ac3d2bae84bb7 100644 --- a/clang/include/clang/Driver/Job.h +++ b/clang/include/clang/Driver/Job.h @@ -259,7 +259,13 @@ class Command { /// Use the CC1 tool callback when available, to avoid creating a new process class CC1Command : public Command { + static bool CanRunAgain; + public: + /// Whether this process can run cc1 again after crash recovery. + /// Embedding hosts must discard it if this returns false. + static bool canRunAgain(); + CC1Command(const Action &Source, const Tool &Creator, ResponseFileSupport ResponseSupport, const char *Executable, const llvm::opt::ArgStringList &Arguments, diff --git a/clang/lib/Driver/Compilation.cpp b/clang/lib/Driver/Compilation.cpp index 9b46b38811cb0b..e5340f471be156 100644 --- a/clang/lib/Driver/Compilation.cpp +++ b/clang/lib/Driver/Compilation.cpp @@ -39,9 +39,10 @@ using namespace llvm::opt; Compilation::Compilation(const Driver &D, const ToolChain &_DefaultToolChain, InputArgList *_Args, DerivedArgList *_TranslatedArgs, - bool ContainsError) + bool ContainsError, bool CC1MainIsReusable) : TheDriver(D), DefaultToolChain(_DefaultToolChain), Args(_Args), - TranslatedArgs(_TranslatedArgs), ContainsError(ContainsError) { + TranslatedArgs(_TranslatedArgs), CC1MainIsReusable(CC1MainIsReusable), + ContainsError(ContainsError) { // The offloading host toolchain is the default toolchain. OrderedOffloadingToolchains.insert( std::make_pair(Action::OFK_Host, &DefaultToolChain)); @@ -406,6 +407,8 @@ void Compilation::ExecuteJobs(const JobList &Jobs, if (TheDriver.IsCLMode()) return; } + if (Job.InProcess && !CC1Command::canRunAgain()) + return; } } diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index e43bd6b1049b81..959fec331b6f44 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -1528,7 +1528,8 @@ bool Driver::loadDefaultConfigFiles(llvm::cl::ExpansionContext &ExpCtx) { return false; } -Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { +Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList, + bool CC1MainIsReusable) { llvm::PrettyStackTraceString CrashInfo("Compilation construction"); // FIXME: Handle environment options which affect driver behavior, somewhere @@ -1861,7 +1862,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) { // The compilation takes ownership of Args. Compilation *C = new Compilation(*this, TC, UArgs.release(), TranslatedArgs, - ContainsError); + ContainsError, CC1MainIsReusable); if (!HandleImmediateArgs(*C)) return C; @@ -4699,9 +4700,10 @@ void Driver::BuildJobs(Compilation &C) const { /*TargetDeviceOffloadKind*/ Action::OFK_None); } - // If we have more than one job, then disable integrated-cc1 for now. Do this - // also when we need to report process execution statistics. - if (C.getJobs().size() > 1 || CCPrintProcessStats) + // Unless the callback explicitly supports repeated invocation, use a + // separate process when there is more than one job. Process execution + // statistics always require a separate process. + if ((C.getJobs().size() > 1 && !C.isCC1MainReusable()) || CCPrintProcessStats) for (auto &J : C.getJobs()) J.InProcess = false; diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index da7a1f2e07e900..7ceaae0860b2fe 100644 --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -387,6 +387,10 @@ int Command::Execute(ArrayRef<std::optional<StringRef>> Redirects, ErrMsg, ExecutionFailed, &ProcStat); } +bool CC1Command::CanRunAgain = true; + +bool CC1Command::canRunAgain() { return CanRunAgain; } + CC1Command::CC1Command(const Action &Source, const Tool &Creator, ResponseFileSupport ResponseSupport, const char *Executable, @@ -400,17 +404,15 @@ CC1Command::CC1Command(const Action &Source, const Tool &Creator, void CC1Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo) const { - if (InProcess) + if (InProcess && canRunAgain()) OS << " (in-process)\n"; Command::Print(OS, Terminator, Quote, CrashInfo); } int CC1Command::Execute(ArrayRef<std::optional<StringRef>> Redirects, std::string *ErrMsg, bool *ExecutionFailed) const { - // FIXME: Currently, if there're more than one job, we disable - // -fintegrate-cc1. If we're no longer a integrated-cc1 job, fallback to - // out-of-process execution. See discussion in https://reviews.llvm.org/D74447 - if (!InProcess) + // Use a separate process for crash diagnostics after an in-process crash. + if (!InProcess || !canRunAgain()) return Command::Execute(Redirects, ErrMsg, ExecutionFailed); PrintFileNames(); @@ -440,6 +442,7 @@ int CC1Command::Execute(ArrayRef<std::optional<StringRef>> Redirects, int R = 0; // Enter ExecuteCC1Tool() instead of starting up a new process if (!CRC.RunSafely([&]() { R = D.CC1Main(Argv); })) { + CanRunAgain = false; llvm::RestorePrettyStackState(PrettyState); return CRC.RetCode; } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 5a1c8531812bd3..75b606f7301f43 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5828,9 +5828,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-fembed-bitcode=marker"); // We normally speed up the clang process a bit by skipping destructors at - // exit, but when we're generating diagnostics we can rely on some of the - // cleanup. - if (!C.isForDiagnostics()) + // exit. Diagnostic compilations and reusable in-process callbacks must run + // the normal cleanup instead. + if (!C.isForDiagnostics() && + (!C.isCC1MainReusable() || D.CCPrintProcessStats)) CmdArgs.push_back("-disable-free"); CmdArgs.push_back("-clear-ast-before-backend"); diff --git a/clang/test/Driver/cc1-spawnprocess.c b/clang/test/Driver/cc1-spawnprocess.c index a6ff7d148604e3..dea0a778ec9cca 100644 --- a/clang/test/Driver/cc1-spawnprocess.c +++ b/clang/test/Driver/cc1-spawnprocess.c @@ -31,15 +31,18 @@ // Only one TU, one job, thus integrated-cc1 is enabled. // RUN: %clang -fintegrated-cc1 -fintegrated-as -c %s -### 2>&1 | FileCheck %s --check-prefix=YES -// Only one TU, but we're linking, two jobs, thus integrated-cc1 is disabled. -// RUN: %clang -fintegrated-cc1 %s -### 2>&1 | FileCheck %s --check-prefix=NO +// Only one TU, but we're linking, two jobs. A reusable cc1 callback remains +// in-process; an ordinary callback is disabled. +// RUN: %clang -fintegrated-cc1 %s -### 2>&1 | FileCheck %s \ +// RUN: --check-prefix=%if llvm-driver %{YES%} %else %{NO%} // RUN: echo 'int main() { return f() + g(); }' > %t1.cpp // RUN: echo 'int f() { return 1; }' > %t2.cpp // RUN: echo 'int g() { return 2; }' > %t3.cpp -// Three jobs, thus integrated-cc1 is disabled. -// RUN: %clang -fintegrated-cc1 -c %t1.cpp %t2.cpp %t3.cpp -### 2>&1 | FileCheck %s --check-prefix=NO +// Three jobs likewise use a reusable callback when one is available. +// RUN: %clang -fintegrated-cc1 -c %t1.cpp %t2.cpp %t3.cpp -### 2>&1 \ +// RUN: | FileCheck %s --check-prefix=%if llvm-driver %{YES%} %else %{NO%} // -fintegrated-cc1 works with cc1as. // macOS triples have an extra -x assembler-with-cpp job so (in-process) is not triggered. diff --git a/clang/test/Driver/clang-translation.c b/clang/test/Driver/clang-translation.c index 5ec052a7aaa112..3f98b0bc8c5f62 100644 --- a/clang/test/Driver/clang-translation.c +++ b/clang/test/Driver/clang-translation.c @@ -1,4 +1,4 @@ -// RUN: %clang -target i386-unknown-unknown -### -S -O0 -Os %s -o %t.s -fverbose-asm -fvisibility=hidden 2>&1 | FileCheck -check-prefix=I386 %s +// RUN: %clang -fno-integrated-cc1 -target i386-unknown-unknown -### -S -O0 -Os %s -o %t.s -fverbose-asm -fvisibility=hidden 2>&1 | FileCheck -check-prefix=I386 %s // I386: "-triple" "i386-unknown-unknown" // I386: "-Os" // I386: "-S" @@ -10,7 +10,7 @@ // I386: "-o" // 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 +// RUN: %clang -fno-integrated-cc1 -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" diff --git a/clang/test/Driver/in-process-multiple-cc1-crash.c b/clang/test/Driver/in-process-multiple-cc1-crash.c new file mode 100644 index 00000000000000..3fc4d39a7d06dc --- /dev/null +++ b/clang/test/Driver/in-process-multiple-cc1-crash.c @@ -0,0 +1,20 @@ +// REQUIRES: llvm-driver, webassembly-registered-target, crash-recovery + +// A recovered compiler crash must not execute the next independent cc1 job. +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: cd %t && not %clang -fintegrated-cc1 -fno-crash-diagnostics \ +// RUN: --target=wasm32-unknown-unknown -c before.c crash.c after.c 2>&1 \ +// RUN: | FileCheck %s --implicit-check-not='Error handler already registered' +// RUN: test -f %t/before.o +// RUN: not test -f %t/after.o +// CHECK: fatal error: error in backend: #pragma clang __debug llvm_fatal_error + +//--- before.c +int before(void) { return 1; } + +//--- crash.c +#pragma clang __debug llvm_fatal_error + +//--- after.c +int after(void) { return 2; } 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 00000000000000..a01f91e1680fb2 --- /dev/null +++ b/clang/test/Driver/in-process-multiple-cc1.c @@ -0,0 +1,68 @@ +// 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: rm -rf %t +// RUN: split-file %s %t +// RUN: cd %t && %clang -fintegrated-cc1 --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 -fintegrated-cc1 --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 -fintegrated-cc1 --target=wasm32-unknown-unknown \ +// RUN: -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 + +// A failed cc1 job does not prevent the following job from running in the +// same process, regardless of which source comes first. +// RUN: cd %t && not %clang -fintegrated-cc1 --target=wasm32-unknown-unknown \ +// RUN: -c good-before-bad.c bad-after-good.c +// RUN: test -f %t/good-before-bad.o +// RUN: cd %t && not %clang -fintegrated-cc1 --target=wasm32-unknown-unknown \ +// RUN: -c bad-before-good.c good-after-bad.c +// RUN: test -f %t/good-after-bad.o + +// Failure to open one time-trace file must still clean up the profiler before +// the next in-process job initializes it again. +// RUN: mkdir %t/first.json +// RUN: cd %t && %clang -fintegrated-cc1 --target=wasm32-unknown-unknown \ +// RUN: -ftime-trace -c first.c second.c 2>&1 \ +// RUN: | FileCheck %s --check-prefix=TRACE +// RUN: test -f %t/second.json +// TRACE: error: unable to open output file 'first.json' + +//--- first.c +int first(void) { return 1; } + +//--- second.c +int second(void) { return 2; } + +//--- good-before-bad.c +int good_before_bad(void) { return 3; } + +//--- bad-after-good.c +int bad_after_good(void) { return does_not_parse( } + +//--- bad-before-good.c +int bad_before_good(void) { return does_not_parse( } + +//--- good-after-bad.c +int good_after_bad(void) { return 4; } diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp index 5ed0c372717e1d..ebba605bf33ed0 100644 --- a/clang/tools/driver/cc1_main.cpp +++ b/clang/tools/driver/cc1_main.cpp @@ -38,6 +38,7 @@ #include "llvm/Option/OptTable.h" #include "llvm/Support/BuryPointer.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/IOSandbox.h" #include "llvm/Support/ManagedStatic.h" @@ -250,16 +251,6 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) { auto Clang = std::make_unique<CompilerInstance>(std::move(Invocation), std::move(PCHOps)); - if (!Clang->getFrontendOpts().TimeTracePath.empty()) { - llvm::TimeTraceCompression Compress = - Clang->getFrontendOpts().TimeTraceCompress; - if (Compress == llvm::TimeTraceCompression::Infer) - Compress = llvm::inferTimeTraceCompressionFromPath( - Clang->getFrontendOpts().TimeTracePath); - llvm::timeTraceProfilerInitialize( - Clang->getFrontendOpts().TimeTraceGranularity, Argv0, - Clang->getFrontendOpts().TimeTraceVerbose, Compress); - } // --print-supported-cpus takes priority over the actual compilation. if (Clang->getFrontendOpts().PrintSupportedCPUs) return PrintSupportedCPUs(Clang->getTargetOpts().Triple); @@ -288,15 +279,29 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) { // Create the actual diagnostics engine. Clang->createDiagnostics(); - // Set an error handler, so that any LLVM backend diagnostics go through our - // error handler. - llvm::install_fatal_error_handler(LLVMErrorHandler, - static_cast<void*>(&Clang->getDiagnostics())); - DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics()); if (!Success) return 1; + // Initialize process-global state only after the early-return paths above. + if (!Clang->getFrontendOpts().TimeTracePath.empty()) { + llvm::TimeTraceCompression Compress = + Clang->getFrontendOpts().TimeTraceCompress; + if (Compress == llvm::TimeTraceCompression::Infer) + Compress = llvm::inferTimeTraceCompressionFromPath( + Clang->getFrontendOpts().TimeTracePath); + llvm::timeTraceProfilerInitialize( + Clang->getFrontendOpts().TimeTraceGranularity, Argv0, + Clang->getFrontendOpts().TimeTraceVerbose, Compress); + } + + // Set an error handler, so that any LLVM backend diagnostics go through our + // error handler. + auto ErrorHandler = std::make_unique<llvm::ScopedFatalErrorHandler>( + LLVMErrorHandler, &Clang->getDiagnostics()); + llvm::CrashRecoveryContextCleanupRegistrar<llvm::ScopedFatalErrorHandler> + ErrorHandlerCleanup(ErrorHandler.get()); + // Execute the frontend actions. Success = ExecuteCompilerInvocation(Clang.get()); @@ -329,15 +334,16 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) { /*useTemporary=*/false)) { llvm::timeTraceProfilerWrite(*profilerOutput); profilerOutput.reset(); - llvm::timeTraceProfilerCleanup(); Clang->clearOutputFiles(false); } + llvm::timeTraceProfilerCleanup(); } // Our error handler depends on the Diagnostics object, which we're // potentially about to delete. Uninstall the handler now so that any // later errors use the default handling behavior instead. - llvm::remove_fatal_error_handler(); + ErrorHandlerCleanup.unregister(); + ErrorHandler.reset(); // When running with -disable-free, don't do any destruction or shutdown. if (Clang->getFrontendOpts().DisableFree) { diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp index af96812e9ebd97..ab41b11c2046ca 100644 --- a/clang/tools/driver/driver.cpp +++ b/clang/tools/driver/driver.cpp @@ -385,7 +385,9 @@ int clang_main(int Argc, char **Argv, const llvm::ToolContext &ToolContext) { /*NeedsPOSIXUtilitySignalHandling=*/true); } - std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Args)); + bool CC1MainIsReusable = ToolContext.hasSession() && !UseNewCC1Process; + std::unique_ptr<Compilation> C( + TheDriver.BuildCompilation(Args, CC1MainIsReusable)); Driver::ReproLevel ReproLevel = Driver::ReproLevel::OnCrash; if (Arg *A = C->getArgs().getLastArg(options::OPT_gen_reproducer_eq)) { diff --git a/llvm/include/llvm/Support/Driver.h b/llvm/include/llvm/Support/Driver.h index eef1d21cc8e0a9..e65cc416ba048e 100644 --- a/llvm/include/llvm/Support/Driver.h +++ b/llvm/include/llvm/Support/Driver.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 e0252324ed4037..c6eb625f347076 100644 --- a/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp +++ b/llvm/unittests/Support/LLVMToolSession/LLVMToolSessionTest.cpp @@ -28,6 +28,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; } @@ -72,6 +73,11 @@ int fuzzyMain(int Argc, char **Argv, const ToolContext &Context) { 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
