Index: clang/CMakeLists.txt =================================================================== --- clang/CMakeLists.txt +++ clang/CMakeLists.txt @@ -237,6 +237,9 @@ set(ENABLE_EXPERIMENTAL_NEW_PASS_MANAGER FALSE CACHE BOOL "Enable the experimental new pass manager by default.") +set(CLANG_SPAWN_CC1 OFF CACHE BOOL + "Whether clang should use a new process for the CC1 invocation") + # TODO: verify the values against LangStandards.def? set(CLANG_DEFAULT_STD_C "" CACHE STRING "Default standard to use for C/ObjC code (IDENT from LangStandards.def, empty for platform default)") Index: clang/include/clang/Config/config.h.cmake =================================================================== --- clang/include/clang/Config/config.h.cmake +++ clang/include/clang/Config/config.h.cmake @@ -80,4 +80,7 @@ #cmakedefine01 CLANG_ENABLE_OBJC_REWRITER #cmakedefine01 CLANG_ENABLE_STATIC_ANALYZER +/* Spawn a new process clang.exe for the CC1 tool invocation, when necessary */ +#cmakedefine01 CLANG_SPAWN_CC1 + #endif Index: clang/include/clang/Driver/Driver.h =================================================================== --- clang/include/clang/Driver/Driver.h +++ clang/include/clang/Driver/Driver.h @@ -204,6 +204,13 @@ /// Whether the driver is generating diagnostics for debugging purposes. unsigned CCGenDiagnostics : 1; + /// Pointer to the ExecuteCC1Tool function, if available. + /// When the clangDriver lib is used through clang.exe, this provides a + /// shortcut for executing the -cc1 command-line directly, in the same + /// process. + typedef int (*CC1ToolFunc)(ArrayRef argv); + CC1ToolFunc CC1Main = nullptr; + private: /// Raw target triple. std::string TargetTriple; Index: clang/include/clang/Driver/Job.h =================================================================== --- clang/include/clang/Driver/Job.h +++ clang/include/clang/Driver/Job.h @@ -119,7 +119,7 @@ /// \param NewEnvironment An array of environment variables. /// \remark If the environment remains unset, then the environment /// from the parent process will be used. - void setEnvironment(llvm::ArrayRef NewEnvironment); + virtual void setEnvironment(llvm::ArrayRef NewEnvironment); const char *getExecutable() const { return Executable; } @@ -130,6 +130,24 @@ /// Set whether to print the input filenames when executing. void setPrintInputFilenames(bool P) { PrintInputFilenames = P; } + +protected: + /// Optionally print the filenames to be compiled + void PrintFileNames() const; +}; + +/// Use the CC1 tool callback when available, to avoid creating a new process +class CC1Command : public Command { +public: + using Command::Command; + + void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, + CrashReportInfo *CrashInfo = nullptr) const override; + + int Execute(ArrayRef> Redirects, std::string *ErrMsg, + bool *ExecutionFailed) const override; + + void setEnvironment(llvm::ArrayRef NewEnvironment) override; }; /// Like Command, but with a fallback which is executed in case Index: clang/lib/Driver/Job.cpp =================================================================== --- clang/lib/Driver/Job.cpp +++ clang/lib/Driver/Job.cpp @@ -19,8 +19,10 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" #include @@ -313,15 +315,46 @@ Environment.push_back(nullptr); } -int Command::Execute(ArrayRef> Redirects, - std::string *ErrMsg, bool *ExecutionFailed) const { +void Command::PrintFileNames() const { if (PrintInputFilenames) { for (const char *Arg : InputFilenames) llvm::outs() << llvm::sys::path::filename(Arg) << "\n"; llvm::outs().flush(); } +} - SmallVector Argv; +int Command::Execute(ArrayRef> Redirects, + std::string *ErrMsg, bool *ExecutionFailed) const { + PrintFileNames(); + + SmallVector Argv; + if (ResponseFile == nullptr) { + Argv.push_back(Executable); + Argv.append(Arguments.begin(), Arguments.end()); + Argv.push_back(nullptr); + } else { + // If the command is too large, we need to put arguments in a response file. + std::string RespContents; + llvm::raw_string_ostream SS(RespContents); + + // Write file contents and build the Argv vector + writeResponseFile(SS); + buildArgvForResponseFile(Argv); + Argv.push_back(nullptr); + SS.flush(); + + // Save the response file in the appropriate encoding + if (std::error_code EC = writeFileWithEncoding( + ResponseFile, RespContents, Creator.getResponseFileEncoding())) { + if (ErrMsg) + *ErrMsg = EC.message(); + if (ExecutionFailed) + *ExecutionFailed = true; + // Return -1 by convention (see llvm/include/llvm/Support/Program.h) to + // indicate the requested executable cannot be started. + return -1; + } + } Optional> Env; std::vector ArgvVectorStorage; @@ -332,42 +365,53 @@ Env = makeArrayRef(ArgvVectorStorage); } - if (ResponseFile == nullptr) { - Argv.push_back(Executable); - Argv.append(Arguments.begin(), Arguments.end()); - Argv.push_back(nullptr); + auto Args = llvm::toStringRefArray(Argv.data()); + return llvm::sys::ExecuteAndWait(Executable, Args, Env, Redirects, + /*secondsToWait*/ 0, + /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); +} - auto Args = llvm::toStringRefArray(Argv.data()); - return llvm::sys::ExecuteAndWait( - Executable, Args, Env, Redirects, /*secondsToWait*/ 0, - /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); - } +void CC1Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, + CrashReportInfo *CrashInfo) const { + OS << " Running the following in ExecuteCC1Tool():\n"; + Command::Print(OS, Terminator, Quote, CrashInfo); +} - // We need to put arguments in a response file (command is too large) - // Open stream to store the response file contents - std::string RespContents; - llvm::raw_string_ostream SS(RespContents); +int CC1Command::Execute(ArrayRef> /*Redirects*/, + std::string *ErrMsg, bool *ExecutionFailed) const { + PrintFileNames(); - // Write file contents and build the Argv vector - writeResponseFile(SS); - buildArgvForResponseFile(Argv); + SmallVector Argv; + Argv.push_back(getExecutable()); + Argv.append(getArguments().begin(), getArguments().end()); Argv.push_back(nullptr); - SS.flush(); - - // Save the response file in the appropriate encoding - if (std::error_code EC = writeFileWithEncoding( - ResponseFile, RespContents, Creator.getResponseFileEncoding())) { - if (ErrMsg) - *ErrMsg = EC.message(); - if (ExecutionFailed) - *ExecutionFailed = true; - return -1; + + // This flag simply indicates that the program couldn't start, which isn't + // applicable here. + if (ExecutionFailed) + *ExecutionFailed = false; + + llvm::CrashRecoveryContext::Enable(); + + llvm::CrashRecoveryContext CRC; + CRC.DumpStackAndCleanupOnFailure = true; + + const void *PrettyState = llvm::SavePrettyStackState(); + const Driver &D = getCreator().getToolChain().getDriver(); + + int R = 0; + // Enter ExecuteCC1Tool() instead of starting up a new process + if (!CRC.RunSafely([&]() { R = D.CC1Main(Argv); })) { + llvm::RestorePrettyStackState(PrettyState); + return CRC.RetCode; } + return R; +} - auto Args = llvm::toStringRefArray(Argv.data()); - return llvm::sys::ExecuteAndWait(Executable, Args, Env, Redirects, - /*secondsToWait*/ 0, - /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); +void CC1Command::setEnvironment(llvm::ArrayRef NewEnvironment) { + // We don't support set a new environment when calling into ExecuteCC1Tool() + llvm_unreachable( + "The CC1Command doesn't support changing the environment vars!"); } FallbackCommand::FallbackCommand(const Action &Source_, const Tool &Creator_, Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -5863,6 +5863,10 @@ // fails, so that the main compilation's fallback to cl.exe runs. C.addCommand(std::make_unique(JA, *this, Exec, CmdArgs, Inputs)); + } else if (D.CC1Main && !D.CCGenDiagnostics) { + // Invoke the CC1 directly in this process + C.addCommand( + std::make_unique(JA, *this, Exec, CmdArgs, Inputs)); } else { C.addCommand(std::make_unique(JA, *this, Exec, CmdArgs, Inputs)); } Index: clang/test/CMakeLists.txt =================================================================== --- clang/test/CMakeLists.txt +++ clang/test/CMakeLists.txt @@ -22,6 +22,7 @@ CLANG_BUILD_EXAMPLES CLANG_ENABLE_ARCMT CLANG_ENABLE_STATIC_ANALYZER + CLANG_SPAWN_CC1 ENABLE_BACKTRACES ENABLE_EXPERIMENTAL_NEW_PASS_MANAGER HAVE_LIBZ Index: clang/test/Driver/crash-diagnostics-dir.c =================================================================== --- clang/test/Driver/crash-diagnostics-dir.c +++ clang/test/Driver/crash-diagnostics-dir.c @@ -3,3 +3,6 @@ #pragma clang __debug parser_crash // CHECK: Preprocessed source(s) and associated run script(s) are located at: // CHECK: diagnostic msg: {{.*}}{{/|\\}}crash-diagnostics-dir.c.tmp{{(/|\\).*}}.c + +// RUN: env CLANG_SPAWN_CC1=0 not %clang -fcrash-diagnostics-dir=%t -c %s -o - 2>&1 | FileCheck %s +// RUN: env CLANG_SPAWN_CC1=1 not %clang -fcrash-diagnostics-dir=%t -c %s -o - 2>&1 | FileCheck %s Index: clang/test/Driver/cuda-simple.cu =================================================================== --- clang/test/Driver/cuda-simple.cu +++ clang/test/Driver/cuda-simple.cu @@ -5,11 +5,11 @@ // Verify that we pass -x cuda-cpp-output to compiler after // preprocessing a CUDA file // RUN: %clang -Werror -### -save-temps -c %s 2>&1 | FileCheck %s -// CHECK: "-cc1" -// CHECK: "-E" -// CHECK: "-x" "cuda" -// CHECK-NEXT: "-cc1" -// CHECK: "-x" "cuda-cpp-output" +// CHECK: "{{.*}}clang{{.*}}" "-cc1 +// CHECK-SAME: "-E" +// CHECK-SAME: "-x" "cuda" +// CHECK: "{{.*}}clang{{.*}}" "-cc1 +// CHECK-SAME: "-x" "cuda-cpp-output" // // Verify that compiler accepts CUDA syntax with "-x cuda-cpp-output". // RUN: %clang -Werror -fsyntax-only -x cuda-cpp-output -c %s Index: clang/test/Driver/offloading-interoperability.c =================================================================== --- clang/test/Driver/offloading-interoperability.c +++ clang/test/Driver/offloading-interoperability.c @@ -12,6 +12,6 @@ // NO-OPENMP-FLAGS-FOR-CUDA-DEVICE-NOT: -fopenmp // NO-OPENMP-FLAGS-FOR-CUDA-DEVICE-NEXT: ptxas" "-m64" // NO-OPENMP-FLAGS-FOR-CUDA-DEVICE-NEXT: fatbinary"{{( "--cuda")?}} "-64" -// NO-OPENMP-FLAGS-FOR-CUDA-DEVICE-NEXT: clang{{.*}}" "-cc1" "-triple" "powerpc64le-unknown-linux-gnu" -// NO-OPENMP-FLAGS-FOR-CUDA-DEVICE: -fopenmp +// NO-OPENMP-FLAGS-FOR-CUDA-DEVICE: clang{{.*}}" "-cc1" "-triple" "powerpc64le-unknown-linux-gnu" +// NO-OPENMP-FLAGS-FOR-CUDA-DEVICE-SAME: -fopenmp // NO-OPENMP-FLAGS-FOR-CUDA-DEVICE-NEXT: {{ld(.exe)?"}} {{.*}}"-m" "elf64lppc" Index: clang/test/Driver/option-aliases.c =================================================================== --- clang/test/Driver/option-aliases.c +++ clang/test/Driver/option-aliases.c @@ -3,12 +3,14 @@ // RUN: --param=FOO --output=FOO %s 2>&1 | \ // RUN: FileCheck %s -// CHECK: "-cc1" -// CHECK: "-E" -// CHECK: "-U" "FOO" -// CHECK: "-U" "BAR" -// CHECK: "-o" "option-aliases.i" +// CHECK: "{{.*}}clang{{.*}}" "-cc1 +// CHECK-SAME: "-E" +// CHECK-SAME: "-U" "FOO" +// CHECK-SAME: "-U" "BAR" +// CHECK-SAME: "-o" "option-aliases.i" -// CHECK-NEXT: "-cc1" -// CHECK: "-S" -// CHECK: "-o" "FOO" +// CHECK: "{{.*}}clang{{.*}}" "-cc1 + +// CHECK: "{{.*}}clang{{.*}}" "-cc1 +// CHECK-SAME: "-S" +// CHECK-SAME: "-o" "FOO" Index: clang/test/Driver/unknown-arg.c =================================================================== --- clang/test/Driver/unknown-arg.c +++ clang/test/Driver/unknown-arg.c @@ -54,7 +54,7 @@ // SILENT-NOT: warning: // CC1AS-DID-YOU-MEAN: error: unknown argument '-hell'; did you mean '-help'? // CC1AS-DID-YOU-MEAN: error: unknown argument '--version'; did you mean '-version'? -// UNKNOWN-INTEGRATED: error: unknown integrated tool 'asphalt'. Valid tools include '-cc1' and '-cc1as'. +// UNKNOWN-INTEGRATED: error: unknown integrated tool '-cc1asphalt'. Valid tools include '-cc1' and '-cc1as'. // RUN: %clang -S %s -o %t.s -Wunknown-to-clang-option 2>&1 | FileCheck --check-prefix=IGNORED %s Index: clang/tools/driver/driver.cpp =================================================================== --- clang/tools/driver/driver.cpp +++ clang/tools/driver/driver.cpp @@ -14,6 +14,7 @@ #include "clang/Driver/Driver.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/Stack.h" +#include "clang/Config/config.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" @@ -239,6 +240,8 @@ *NumberSignPtr = '='; } +static int ExecuteCC1Tool(ArrayRef argv); + static void SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) { // Handle CC_PRINT_OPTIONS and CC_PRINT_OPTIONS_FILE. TheDriver.CCPrintOptions = !!::getenv("CC_PRINT_OPTIONS"); @@ -254,6 +257,21 @@ TheDriver.CCLogDiagnostics = !!::getenv("CC_LOG_DIAGNOSTICS"); if (TheDriver.CCLogDiagnostics) TheDriver.CCLogDiagnosticsFilename = ::getenv("CC_LOG_DIAGNOSTICS_FILE"); + + // Whether the cc1 tool should be called inside the current process, or if we + // should spawn a new clang process (old behavior). + // Not having an additional process saves some execution time of Windows, + // and makes debugging easier. + bool UseNewCC1Process = CLANG_SPAWN_CC1; + + StringRef SpawnCC1Str = ::getenv("CLANG_SPAWN_CC1"); + if (!SpawnCC1Str.empty()) { + UseNewCC1Process = SpawnCC1Str.compare("1") == 0 || + SpawnCC1Str.compare_lower("on") == 0 || + SpawnCC1Str.compare_lower("true") == 0; + } + if (!UseNewCC1Process) + TheDriver.CC1Main = &ExecuteCC1Tool; } static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient, @@ -303,13 +321,19 @@ TheDriver.setInstalledDir(InstalledPathParent); } -static int ExecuteCC1Tool(ArrayRef argv, StringRef Tool) { +static int ExecuteCC1Tool(ArrayRef argv) { + // If we call the cc1 tool from the clangDriver library (through + // Driver::CC1Main), we need to cleanup the options usage count. The options + // are currently global, and they might have been used previously by the + // driver. + llvm::cl::ResetAllOptionOccurrences(); + StringRef Tool = argv[1]; void *GetExecutablePathVP = (void *)(intptr_t) GetExecutablePath; - if (Tool == "") + if (Tool == "-cc1") return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP); - if (Tool == "as") + if (Tool == "-cc1as") return cc1as_main(argv.slice(2), argv[0], GetExecutablePathVP); - if (Tool == "gen-reproducer") + if (Tool == "-cc1gen-reproducer") return cc1gen_reproducer_main(argv.slice(2), argv[0], GetExecutablePathVP); // Reject unknown tools. @@ -379,7 +403,7 @@ auto newEnd = std::remove(argv.begin(), argv.end(), nullptr); argv.resize(newEnd - argv.begin()); } - return ExecuteCC1Tool(argv, argv[1] + 4); + return ExecuteCC1Tool(argv); } bool CanonicalPrefixes = true; @@ -503,7 +527,7 @@ #ifdef _WIN32 // Exit status should not be negative on Win32, unless abnormal termination. - // Once abnormal termiation was caught, negative status should not be + // Once abnormal termination was caught, negative status should not be // propagated. if (Res < 0) Res = 1;