[clang] [Clang][Driver] Create crash reproducers for IR inputs (PR #165572)
Nabeel Omer via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 29 07:22:38 PDT 2025
https://github.com/omern1 created https://github.com/llvm/llvm-project/pull/165572
This patch makes Clang produce the crash reproducer shell script for IR inputs as well.
>From be66c84cc561e52bfc28a8aa5706535eeb9f2d6d Mon Sep 17 00:00:00 2001
From: Nabeel Omer <Nabeel.Omer at sony.com>
Date: Wed, 29 Oct 2025 11:34:51 +0000
Subject: [PATCH] [Clang][Driver] Create crash reproducers for IR inputs
This patch makes Clang produce the crash reproducer shell script for IR
inputs as well.
---
clang/lib/Driver/Driver.cpp | 104 ++++++++++++++++++---------
clang/test/Driver/crash-ir-repro.cpp | 15 ++++
2 files changed, 86 insertions(+), 33 deletions(-)
create mode 100644 clang/test/Driver/crash-ir-repro.cpp
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 40ea513e85427..7f28f7fa0c997 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 <memory>
#include <optional>
#include <set>
+#include <string>
#include <utility>
#if LLVM_ON_UNIX
#include <unistd.h> // getpid
@@ -2041,12 +2043,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)
@@ -2063,7 +2070,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.";
@@ -2086,46 +2093,77 @@ 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<std::pair<int, const Command *>, 4> FailingCommands;
+ C.ExecuteJobs(C.getJobs(), FailingCommands);
- // Generate preprocessed output.
- SmallVector<std::pair<int, const Command *>, 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<std::string> 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<char, 64> 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);
@@ -2142,7 +2180,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..e0e47814a0e78
--- /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 -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;
+}
More information about the cfe-commits
mailing list