https://github.com/jroelofs created 
https://github.com/llvm/llvm-project/pull/197600

rdar://176327788

>From 01decba28cf178ae108e1e0ea43bd68745e45ba1 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <[email protected]>
Date: Wed, 13 May 2026 17:25:58 -0700
Subject: [PATCH] [clang] Enable crash reproducers when multiple -arch flags
 are involved

rdar://176327788
---
 clang/include/clang/Driver/Compilation.h    | 12 ++-
 clang/include/clang/Driver/Job.h            |  8 ++
 clang/lib/Driver/Driver.cpp                 | 23 +++++-
 clang/test/Driver/crash-report-multi-arch.c | 86 +++++++++++++++++++++
 4 files changed, 124 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/Driver/crash-report-multi-arch.c

diff --git a/clang/include/clang/Driver/Compilation.h 
b/clang/include/clang/Driver/Compilation.h
index 26781fc676832..4ad2dc34a1f85 100644
--- a/clang/include/clang/Driver/Compilation.h
+++ b/clang/include/clang/Driver/Compilation.h
@@ -126,6 +126,10 @@ class Compilation {
   /// Whether to keep temporary files regardless of -save-temps.
   bool ForceKeepTempFiles = false;
 
+  /// The bound architecture currently being built, if any. Set around
+  /// ConstructJob calls so addCommand can stamp it onto each new Command.
+  StringRef CurrentBoundArch;
+
 public:
   Compilation(const Driver &D, const ToolChain &DefaultToolChain,
               llvm::opt::InputArgList *Args,
@@ -211,7 +215,13 @@ class Compilation {
   JobList &getJobs() { return Jobs; }
   const JobList &getJobs() const { return Jobs; }
 
-  void addCommand(std::unique_ptr<Command> C) { Jobs.addJob(std::move(C)); }
+  void addCommand(std::unique_ptr<Command> Cmd) {
+    Cmd->setBoundArch(CurrentBoundArch);
+    Jobs.addJob(std::move(Cmd));
+  }
+
+  StringRef getCurrentBoundArch() const { return CurrentBoundArch; }
+  void setCurrentBoundArch(StringRef Arch) { CurrentBoundArch = Arch; }
 
   llvm::opt::ArgStringList &getTempFiles() { return TempFiles; }
   const llvm::opt::ArgStringList &getTempFiles() const { return TempFiles; }
diff --git a/clang/include/clang/Driver/Job.h b/clang/include/clang/Driver/Job.h
index c5934498d4241..116254f79ae6f 100644
--- a/clang/include/clang/Driver/Job.h
+++ b/clang/include/clang/Driver/Job.h
@@ -150,6 +150,10 @@ class Command {
   /// Information on executable run provided by OS.
   mutable std::optional<llvm::sys::ProcessStatistics> ProcStat;
 
+  /// The bound architecture for this command (e.g. "arm64", "x86_64").
+  /// Non-empty only for Darwin multi-arch builds.
+  std::string BoundArch;
+
   /// When a response file is needed, we try to put most arguments in an
   /// exclusive file, while others remains as regular command line arguments.
   /// This functions fills a vector with the regular command line arguments,
@@ -190,6 +194,10 @@ class Command {
   /// getCreator - Return the Tool which caused the creation of this job.
   const Tool &getCreator() const { return Creator; }
 
+  /// Return the bound architecture for this command, if any.
+  StringRef getBoundArch() const { return BoundArch; }
+  void setBoundArch(StringRef Arch) { BoundArch = std::string(Arch); }
+
   /// Returns the kind of response file supported by the current invocation.
   const ResponseFileSupport &getResponseFileSupport() {
     return ResponseSupport;
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index ed3ddd130d6c7..a06044953daec 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -75,6 +75,7 @@
 #include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU 
pragma: keep
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
@@ -2139,10 +2140,17 @@ void Driver::generateCompilationDiagnostics(
     }
   }
   if (ArchNames.size() > 1) {
-    Diag(clang::diag::note_drv_command_failed_diag_msg)
-        << "Error generating preprocessed source(s) - cannot generate "
-           "preprocessed source with multiple -arch options.";
-    return;
+    // Build a reproducer only for the bound arch that crashed.
+    StringRef FailingArch = Cmd.getBoundArch();
+    if (FailingArch.empty()) {
+      Diag(clang::diag::note_drv_command_failed_diag_msg)
+          << "Error generating preprocessed source(s) - cannot generate "
+             "preprocessed source with multiple -arch options.";
+      return;
+    }
+    C.getArgs().eraseArg(options::OPT_arch);
+    C.getArgs().AddJoinedArg(nullptr, getOpts().getOption(options::OPT_arch),
+                              FailingArch);
   }
 
   // If we only have IR inputs there's no need for preprocessing.
@@ -6062,6 +6070,13 @@ InputInfoList Driver::BuildJobsForActionNoCache(
     Action::OffloadKind TargetDeviceOffloadKind) const {
   llvm::PrettyStackTraceString CrashInfo("Building compilation jobs");
 
+  // Track the bound arch for commands constructed in this scope so
+  // generateCompilationDiagnostics can identify the crashing arch.
+  StringRef SavedBoundArch = C.getCurrentBoundArch();
+  C.setCurrentBoundArch(BoundArch);
+  auto RestoreBoundArch =
+      llvm::scope_exit([&] { C.setCurrentBoundArch(SavedBoundArch); });
+
   InputInfoList OffloadDependencesInputInfo;
   bool BuildingForOffloadDevice = TargetDeviceOffloadKind != Action::OFK_None;
   if (const OffloadAction *OA = dyn_cast<OffloadAction>(A)) {
diff --git a/clang/test/Driver/crash-report-multi-arch.c 
b/clang/test/Driver/crash-report-multi-arch.c
new file mode 100644
index 0000000000000..b12a50cd75b12
--- /dev/null
+++ b/clang/test/Driver/crash-report-multi-arch.c
@@ -0,0 +1,86 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+
+// Verify that crash diagnostics are generated for just the crashing arch when
+// multiple -arch options are present, rather than bailing out entirely.
+
+// RUN: env CLANG_CRASH_DIAGNOSTICS_DIR=%t \
+// RUN:   not %crash_opt %clang %s -arch arm64 -arch arm64e -arch x86_64 
-fsyntax-only -DCRASH_x86_64 2>&1 | \
+// RUN:   FileCheck --check-prefix=CHECK %s
+// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH-x86_64 %s
+// RUN: rm -f %t/crash-report-*
+
+// RUN: env CLANG_CRASH_DIAGNOSTICS_DIR=%t \
+// RUN:   not %crash_opt %clang %s -arch arm64 -arch arm64e -arch x86_64 
-fsyntax-only -DCRASH_arm64 2>&1 | \
+// RUN:   FileCheck --check-prefix=CHECK %s
+// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH-arm64 %s
+// RUN: rm -f %t/crash-report-*
+
+// RUN: env CLANG_CRASH_DIAGNOSTICS_DIR=%t \
+// RUN:   not %crash_opt %clang %s -arch arm64 -arch arm64e -arch x86_64 
-fsyntax-only -DCRASH_arm64e 2>&1 | \
+// RUN:   FileCheck --check-prefix=CHECK %s
+// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH-arm64e %s
+// RUN: rm -f %t/crash-report-*
+
+// If multiple arches crash, capture the reproducer for the first one.
+// one in the list.
+// RUN: env CLANG_CRASH_DIAGNOSTICS_DIR=%t \
+// RUN:   not %crash_opt %clang %s -arch arm64 -arch arm64e -arch x86_64 
-fsyntax-only -DCRASH_arm64 -DCRASH_arm64e 2>&1 | \
+// RUN:   FileCheck --check-prefix=CHECK %s
+// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH-arm64 %s
+// RUN: rm -f %t/crash-report-*
+
+// Likewise for -gen-reproducer, the reproducer script should target the first 
arch.
+// RUN: env CLANG_CRASH_DIAGNOSTICS_DIR=%t \
+// RUN:   not %clang %s -arch arm64 -arch arm64e -fsyntax-only -gen-reproducer 
2>&1 | \
+// RUN:   FileCheck --check-prefix=CHECK %s
+// RUN: cat %t/crash-report-*.sh | FileCheck --check-prefix=CHECKSH-arm64 %s
+
+// REQUIRES: crash-recovery, system-darwin
+// REQUIRES: aarch64-registered-target, x86-registered-target
+
+// CHECK: Preprocessed source(s) and associated run script(s) are located at:
+// CHECK-NEXT: note: diagnostic msg: {{.*}}crash-report-{{.*}}.c
+
+// CHECKSH-x86_64: # Crash reproducer
+// CHECKSH-x86_64: "-cc1"
+// CHECKSH-x86_64: "-triple" "x86_64{{[^"]*}}"
+// CHECKSH-x86_64-NOT: "-triple" "arm64{{[^"]*}}"
+// CHECKSH-x86_64-NOT: "-arch" "arm64"
+// CHECKSH-x86_64-NOT: "-arch" "arm64e"
+
+// CHECKSH-arm64: # Crash reproducer
+// CHECKSH-arm64: "-cc1"
+// CHECKSH-arm64: "-triple" "arm64-{{[^"]*}}"
+// CHECKSH-arm64-NOT: "-triple" "arm64e{{[^"]*}}"
+// CHECKSH-arm64-NOT: "-triple" "x86_64{{[^"]*}}"
+// CHECKSH-arm64-NOT: "-arch" "arm64e"
+// CHECKSH-arm64-NOT: "-arch" "x86_64"
+
+// CHECKSH-arm64e: # Crash reproducer
+// CHECKSH-arm64e: "-cc1"
+// CHECKSH-arm64e: "-triple" "arm64e{{[^"]*}}"
+// CHECKSH-arm64e-NOT: "-triple" "arm64-{{[^"]*}}"
+// CHECKSH-arm64e-NOT: "-triple" "x86_64{{[^"]*}}"
+// CHECKSH-arm64e-NOT: "-arch" "arm64"
+// CHECKSH-arm64e-NOT: "-arch" "x86_64"
+
+#ifdef CRASH_x86_64
+# ifdef __x86_64__
+#  pragma clang __debug crash
+# endif
+#endif
+
+#ifdef CRASH_arm64
+# if defined(__aarch64__) && !defined(__arm64e__)
+#  pragma clang __debug crash
+# endif
+#endif
+
+#ifdef CRASH_arm64e
+# ifdef __arm64e__
+#  pragma clang __debug crash
+# endif
+#endif
+
+int x;

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to