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; + 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 @@ -107,7 +107,7 @@ const Tool &getCreator() const { return Creator; } /// Set to pass arguments via a response file when launching the command - void setResponseFile(const char *FileName); + virtual void setResponseFile(const char *FileName); /// Set an input file list, necessary if we need to use a response file but /// the tool being called only supports input files lists. @@ -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,27 @@ /// Set whether to print the input filenames when executing. void setPrintInputFilenames(bool P) { PrintInputFilenames = P; } + +protected: + /// Lay out the arguments just before the call, or create a response file + int PrepareExecution(SmallVectorImpl &Argv, std::string *ErrMsg, + bool *ExecutionFailed) 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 setResponseFile(const char *FileName) 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,49 @@ Environment.push_back(nullptr); } -int Command::Execute(ArrayRef> Redirects, - std::string *ErrMsg, bool *ExecutionFailed) const { +int Command::PrepareExecution(SmallVectorImpl &Argv, + std::string *ErrMsg, + bool *ExecutionFailed) const { if (PrintInputFilenames) { for (const char *Arg : InputFilenames) llvm::outs() << llvm::sys::path::filename(Arg) << "\n"; llvm::outs().flush(); } - 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; + } + } + return 0; +} + +int Command::Execute(ArrayRef> Redirects, + std::string *ErrMsg, bool *ExecutionFailed) const { + SmallVector Argv; + int R = PrepareExecution(Argv, ErrMsg, ExecutionFailed); + if (R) + return R; Optional> Env; std::vector ArgvVectorStorage; @@ -332,44 +368,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); - } - - // 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); - - // 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; - } - 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 cmd-line in ExecuteCC1Tool():\n"; + Command::Print(OS, Terminator, Quote, CrashInfo); +} + +int CC1Command::Execute(ArrayRef> /*Redirects*/, + std::string *ErrMsg, bool *ExecutionFailed) const { + SmallVector Argv; + int R = PrepareExecution(Argv, ErrMsg, ExecutionFailed); + if (R) + return R; + + // This flag simply indicates that the program couldn't start, which isn't + // applicable here. + if (ExecutionFailed) + *ExecutionFailed = false; + + llvm::CrashRecoveryContext CRC; + CRC.EnableExceptionHandler = true; + + const void *PrettyState = llvm::SavePrettyStackState(); + const Driver &D = getCreator().getToolChain().getDriver(); + + // Enter ExecuteCC1Tool() instead of starting up a new process + if (!CRC.RunSafely([&]() { R = D.CC1Main(Argv); })) { + llvm::RestorePrettyStackState(PrettyState); + return CRC.RetCode; + } + return R; +} + +void CC1Command::setResponseFile(const char *FileName) { + // We don't need response files when calling into ExecuteCC1Tool() +} + +void CC1Command::setEnvironment(llvm::ArrayRef NewEnvironment) { + // We don't support set a new environment when calling into ExecuteCC1Tool() + assert(0 && "The CC1Command doesn't support changing the environment vars!"); +} + FallbackCommand::FallbackCommand(const Action &Source_, const Tool &Creator_, const char *Executable_, const llvm::opt::ArgStringList &Arguments_, Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -5656,6 +5656,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/tools/driver/driver.cpp =================================================================== --- clang/tools/driver/driver.cpp +++ clang/tools/driver/driver.cpp @@ -30,6 +30,7 @@ #include "llvm/Option/OptTable.h" #include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" @@ -303,7 +304,13 @@ 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] + 4; void *GetExecutablePathVP = (void *)(intptr_t) GetExecutablePath; if (Tool == "") return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP); @@ -379,7 +386,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; @@ -455,6 +462,11 @@ SetBackdoorDriverOutputsFromEnvVars(TheDriver); + // Here we provide a shortcut for calling the -cc1 cmd-line within the same + // process, instead of starting a new process. This saves a huge amount of + // time of Windows, as process creation can be expensive on that platform. + TheDriver.CC1Main = &ExecuteCC1Tool; + std::unique_ptr C(TheDriver.BuildCompilation(argv)); int Res = 1; if (C && !C->containsError()) { @@ -503,7 +515,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;