diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index a0b82cec9a372..c12c253aa727a 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -70,6 +70,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" @@ -103,6 +104,7 @@ #include #include #include +#include #include #if LLVM_ON_UNIX #include // getpid @@ -2050,12 +2052,17 @@ void Driver::generateCompilationDiagnostics( InputList Inputs; BuildInputs(C.getDefaultToolChain(), C.getArgs(), Inputs); + ArgStringList IRInputs; for (InputList::iterator it = Inputs.begin(), ie = Inputs.end(); it != ie;) { bool IgnoreInput = false; - // Ignore input from stdin or any inputs that cannot be preprocessed. - // Check type first as not all linker inputs have a value. - if (types::getPreprocessedType(it->first) == types::TY_INVALID) { + // Save IR inputs separately, ignore input from stdin or any other inputs + // that cannot be preprocessed. Check type first as not all linker inputs + // have a value. + if (types::isLLVMIR(it->first)) { + IRInputs.push_back(it->second->getValue()); + IgnoreInput = true; + } else if (types::getPreprocessedType(it->first) == types::TY_INVALID) { IgnoreInput = true; } else if (!strcmp(it->second->getValue(), "-")) { Diag(clang::diag::note_drv_command_failed_diag_msg) @@ -2072,7 +2079,7 @@ void Driver::generateCompilationDiagnostics( } } - if (Inputs.empty()) { + if (Inputs.empty() && IRInputs.empty()) { Diag(clang::diag::note_drv_command_failed_diag_msg) << "Error generating preprocessed source(s) - " "no preprocessable inputs."; @@ -2095,46 +2102,76 @@ void Driver::generateCompilationDiagnostics( return; } - // Construct the list of abstract actions to perform for this compilation. On - // Darwin OSes this uses the driver-driver and builds universal actions. - const ToolChain &TC = C.getDefaultToolChain(); - if (TC.getTriple().isOSBinFormatMachO()) - BuildUniversalActions(C, TC, Inputs); - else - BuildActions(C, C.getArgs(), Inputs, C.getActions()); + // If we only have IR inputs there's no need for preprocessing. + if (!Inputs.empty()) { + // Construct the list of abstract actions to perform for this compilation. + // On Darwin OSes this uses the driver-driver and builds universal actions. + const ToolChain &TC = C.getDefaultToolChain(); + if (TC.getTriple().isOSBinFormatMachO()) + BuildUniversalActions(C, TC, Inputs); + else + BuildActions(C, C.getArgs(), Inputs, C.getActions()); - BuildJobs(C); + BuildJobs(C); - // If there were errors building the compilation, quit now. - if (Trap.hasErrorOccurred()) { - Diag(clang::diag::note_drv_command_failed_diag_msg) - << "Error generating preprocessed source(s)."; - return; - } + // If there were errors building the compilation, quit now. + if (Trap.hasErrorOccurred()) { + Diag(clang::diag::note_drv_command_failed_diag_msg) + << "Error generating preprocessed source(s)."; + return; + } + // Generate preprocessed output. + SmallVector, 4> FailingCommands; + C.ExecuteJobs(C.getJobs(), FailingCommands); - // Generate preprocessed output. - SmallVector, 4> FailingCommands; - C.ExecuteJobs(C.getJobs(), FailingCommands); + // If any of the preprocessing commands failed, clean up and exit. + if (!FailingCommands.empty()) { + Diag(clang::diag::note_drv_command_failed_diag_msg) + << "Error generating preprocessed source(s)."; + return; + } - // If any of the preprocessing commands failed, clean up and exit. - if (!FailingCommands.empty()) { - Diag(clang::diag::note_drv_command_failed_diag_msg) - << "Error generating preprocessed source(s)."; - return; + const ArgStringList &TempFiles = C.getTempFiles(); + if (TempFiles.empty()) { + Diag(clang::diag::note_drv_command_failed_diag_msg) + << "Error generating preprocessed source(s)."; + return; + } } - const ArgStringList &TempFiles = C.getTempFiles(); - if (TempFiles.empty()) { - Diag(clang::diag::note_drv_command_failed_diag_msg) - << "Error generating preprocessed source(s)."; - return; + // Copying filenames due to ownership. + const ArgStringList &Files = C.getTempFiles(); + SmallVector TempFiles(Files.begin(), Files.end()); + + // We'd like to copy the IR input file into our own temp file + // because the build system might try to clean-up after itself. + for (auto const *Input : IRInputs) { + int FD; + llvm::SmallVector Path; + + StringRef extension = llvm::sys::path::extension(Input); + if (!extension.empty()) + extension = extension.drop_front(); + + std::error_code EC = llvm::sys::fs::createTemporaryFile( + llvm::sys::path::stem(Input), extension, FD, Path); + + if (EC) { + Diag(clang::diag::note_drv_command_failed_diag_msg) + << "Error generating run script: " << "Failed copying IR input files" + << " " << EC.message(); + return; + } + llvm::sys::fs::copy_file(Input, FD); + + TempFiles.push_back(std::string(Path.begin(), Path.end())); } Diag(clang::diag::note_drv_command_failed_diag_msg) << BugReporMsg; SmallString<128> VFS; SmallString<128> ReproCrashFilename; - for (const char *TempFile : TempFiles) { + for (std::string &TempFile : TempFiles) { Diag(clang::diag::note_drv_command_failed_diag_msg) << TempFile; if (Report) Report->TemporaryFiles.push_back(TempFile); @@ -2151,7 +2188,7 @@ void Driver::generateCompilationDiagnostics( } for (const char *TempFile : SavedTemps) - C.addTempFile(TempFile); + TempFiles.push_back(TempFile); // Assume associated files are based off of the first temporary file. CrashReportInfo CrashInfo(TempFiles[0], VFS); diff --git a/clang/test/Driver/crash-ir-repro.cpp b/clang/test/Driver/crash-ir-repro.cpp new file mode 100644 index 0000000000000..1f31a5ca1bb34 --- /dev/null +++ b/clang/test/Driver/crash-ir-repro.cpp @@ -0,0 +1,15 @@ +// RUN: %clang -S -emit-llvm -o %t.ll %s +// RUN: not %clang -S -DCRASH %s %t.ll 2>&1 | FileCheck %s + +// CHECK: Preprocessed source(s) and associated run script(s) are located at: +// CHECK-NEXT: clang: note: diagnostic msg: {{.*}}.cpp +// CHECK-NEXT: clang: note: diagnostic msg: {{.*}}.ll +// CHECK-NEXT: clang: note: diagnostic msg: {{.*}}.sh + +#ifdef CRASH +#pragma clang __debug parser_crash +#endif + +int main() { + return 0; +}