Index: clang/include/clang/Driver/Job.h =================================================================== --- clang/include/clang/Driver/Job.h +++ clang/include/clang/Driver/Job.h @@ -55,9 +55,6 @@ /// The list of program arguments which are inputs. llvm::opt::ArgStringList InputFilenames; - /// Whether to print the input filenames when executing. - bool PrintInputFilenames = false; - /// Response file name, if this command is set to use one, or nullptr /// otherwise const char *ResponseFile = nullptr; @@ -86,6 +83,12 @@ void writeResponseFile(raw_ostream &OS) const; public: + /// Whether to print the input filenames when executing. + bool PrintInputFilenames; + + /// Whether the command will be executed in this process or not. + bool InProcess; + Command(const Action &Source, const Tool &Creator, const char *Executable, const llvm::opt::ArgStringList &Arguments, ArrayRef Inputs); @@ -128,8 +131,8 @@ /// Print a command argument, and optionally quote it. static void printArg(llvm::raw_ostream &OS, StringRef Arg, bool Quote); - /// Set whether to print the input filenames when executing. - void setPrintInputFilenames(bool P) { PrintInputFilenames = P; } + /// Ensure the memory allocated by the job is freed after execution. + void enableFree(); protected: /// Optionally print the filenames to be compiled @@ -139,7 +142,9 @@ /// Use the CC1 tool callback when available, to avoid creating a new process class CC1Command : public Command { public: - using Command::Command; + CC1Command(const Action &Source, const Tool &Creator, const char *Executable, + const llvm::opt::ArgStringList &Arguments, + ArrayRef Inputs); void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo = nullptr) const override; Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -3753,6 +3753,13 @@ /*TargetDeviceOffloadKind*/ Action::OFK_None); } + // Make sure we clean up after in-process jobs. + for (size_t I = 0, E = C.getJobs().size(); I + 1 < E; I++) { + auto &Job = *C.getJobs().getJobs()[I]; + if (Job.InProcess) + Job.enableFree(); + } + // If the user passed -Qunused-arguments or there were errors, don't warn // about any unused arguments. if (Diags.hasErrorOccurred() || Index: clang/lib/Driver/Job.cpp =================================================================== --- clang/lib/Driver/Job.cpp +++ clang/lib/Driver/Job.cpp @@ -40,7 +40,7 @@ const llvm::opt::ArgStringList &Arguments, ArrayRef Inputs) : Source(Source), Creator(Creator), Executable(Executable), - Arguments(Arguments) { + Arguments(Arguments), PrintInputFilenames(false), InProcess(false) { for (const auto &II : Inputs) if (II.isFilename()) InputFilenames.push_back(II.getFilename()); @@ -315,6 +315,15 @@ Environment.push_back(nullptr); } +// In some cases when running the calling process, we need to clean up if there +// are other Commands being executed after us, to prevent bloating the heap. +void Command::enableFree() { + auto RemoveDisableFree = [](const char *A) { + return StringRef(A).equals("-disable-free"); + }; + llvm::erase_if(Arguments, RemoveDisableFree); +} + void Command::PrintFileNames() const { if (PrintInputFilenames) { for (const char *Arg : InputFilenames) @@ -371,6 +380,14 @@ /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); } +CC1Command::CC1Command(const Action &Source, const Tool &Creator, + const char *Executable, + const llvm::opt::ArgStringList &Arguments, + ArrayRef Inputs) + : Command(Source, Creator, Executable, Arguments, Inputs) { + InProcess = true; +} + void CC1Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo) const { OS << " (in-process)\n"; Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -6148,7 +6148,7 @@ if (Output.getType() == types::TY_Object && Args.hasFlag(options::OPT__SLASH_showFilenames, options::OPT__SLASH_showFilenames_, false)) { - C.getJobs().getJobs().back()->setPrintInputFilenames(true); + C.getJobs().getJobs().back()->PrintInputFilenames = true; } if (Arg *A = Args.getLastArg(options::OPT_pg)) Index: clang/test/Driver/cc1-spawnprocess.c =================================================================== --- clang/test/Driver/cc1-spawnprocess.c +++ clang/test/Driver/cc1-spawnprocess.c @@ -20,3 +20,48 @@ // YES: (in-process) // NO-NOT: (in-process) + +// The following tests ensure that only the last integrated-cc1 has -disable-free + +// 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 + +// Only one TU, one job, thus no cleanup is needed. +// RUN: %clang -fintegrated-cc1 -c %t1.cpp -### 2>&1 | FileCheck %s --check-prefix=CLEAN +// CLEAN: cc1 +// CLEAN-SAME: -disable-free + +// Three jobs, only the last invocation will skip clean up. +// RUN: %clang -fintegrated-cc1 -c %t1.cpp %t2.cpp %t3.cpp -### 2>&1 | FileCheck %s --check-prefix=NOCLEAN-LAST +// NOCLEAN-LAST: cc1 +// NOCLEAN-LAST-NOT: -disable-free +// NOCLEAN-LAST-SAME: {{.*}}1.cpp +// NOCLEAN-LAST: cc1 +// NOCLEAN-LAST-NOT: -disable-free +// NOCLEAN-LAST-SAME: {{.*}}2.cpp +// NOCLEAN-LAST: cc1 +// NOCLEAN-LAST-SAME: -disable-free +// NOCLEAN-LAST-SAME: {{.*}}3.cpp + +// Four jobs, no invocation will skip clean up because the last job is linking. +// RUN: %clang -fintegrated-cc1 %t1.cpp %t2.cpp %t3.cpp -### 2>&1 | FileCheck %s --check-prefix=ALL +// ALL-NOT: -disable-free + +// In no-integrated-cc1 mode, no cleanup is needed, because we're running the +// CC1 tool in a secondary process and the process' heap will be released +// anyway when the process ends. +// RUN: %clang -fno-integrated-cc1 -c %t1.cpp -### 2>&1 | FileCheck %s --check-prefix=CLEAN + +// RUN: %clang -fno-integrated-cc1 -c %t1.cpp %t2.cpp %t3.cpp -### 2>&1 | FileCheck %s --check-prefix=CLEANALL +// CLEANALL: cc1 +// CLEANALL-SAME: -disable-free +// CLEANALL-SAME: {{.*}}1.cpp +// CLEANALL: cc1 +// CLEANALL-SAME: -disable-free +// CLEANALL-SAME: {{.*}}2.cpp +// CLEANALL: cc1 +// CLEANALL-SAME: -disable-free +// CLEANALL-SAME: {{.*}}3.cpp + +// RUN: %clang -fno-integrated-cc1 %t1.cpp %t2.cpp %t3.cpp -### 2>&1 | FileCheck %s --check-prefix=CLEANALL Index: clang/tools/driver/cc1_main.cpp =================================================================== --- clang/tools/driver/cc1_main.cpp +++ clang/tools/driver/cc1_main.cpp @@ -259,6 +259,7 @@ // FIXME(ibiryukov): make profilerOutput flush in destructor instead. profilerOutput->flush(); llvm::timeTraceProfilerCleanup(); + Clang->clearOutputFiles(false); } }