[llvm] [llvm] Optimize print-on-crash serialization (PR #205682)

Miguel A. Arroyo via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 2 07:15:49 PDT 2026


https://github.com/mayanez updated https://github.com/llvm/llvm-project/pull/205682

>From 476b6925e123bf57573f454e2ac9094415a8fb7a Mon Sep 17 00:00:00 2001
From: Miguel Arroyo <Miguel.Arroyo at rockstargames.com>
Date: Wed, 24 Jun 2026 14:20:08 -0700
Subject: [PATCH 1/6] [llvm] Optimize print-on-crash serialization

---
 .../llvm/Passes/StandardInstrumentations.h    |  7 +-
 llvm/lib/Passes/StandardInstrumentations.cpp  | 97 ++++++++++++++++++-
 llvm/test/Other/print-on-crash.ll             |  4 +
 3 files changed, 101 insertions(+), 7 deletions(-)

diff --git a/llvm/include/llvm/Passes/StandardInstrumentations.h b/llvm/include/llvm/Passes/StandardInstrumentations.h
index cec8ff266a098..334c06b85d770 100644
--- a/llvm/include/llvm/Passes/StandardInstrumentations.h
+++ b/llvm/include/llvm/Passes/StandardInstrumentations.h
@@ -581,13 +581,16 @@ class LLVM_ABI DotCfgChangeReporter : public ChangeReporter<IRDataT<DCData>> {
 class PrintCrashIRInstrumentation {
 public:
   PrintCrashIRInstrumentation()
-      : SavedIR("*** Dump of IR Before Last Pass Unknown ***") {}
+      : SavedString("*** Dump of IR Before Last Pass Unknown ***") {}
   LLVM_ABI ~PrintCrashIRInstrumentation();
   LLVM_ABI void registerCallbacks(PassInstrumentationCallbacks &PIC);
   LLVM_ABI void reportCrashIR();
+  LLVM_ABI void printToStream(raw_ostream &OS);
 
 protected:
-  std::string SavedIR;
+  std::string SavedString;
+
+  std::unique_ptr<Module> SavedModule;
 
 private:
   // The crash reporter that will report on a crash.
diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index 9a2225807145a..80b13daf039e1 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -40,6 +40,7 @@
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/Utils/Cloning.h"
 #include <utility>
 #include <vector>
 
@@ -324,6 +325,70 @@ void unwrapAndPrint(raw_ostream &OS, IRUnitRef IR) {
   llvm_unreachable("Unknown wrapped IR type");
 }
 
+static std::unique_ptr<Module>
+cloneFunctionsIntoNewModule(ArrayRef<const Function *> FunctionsToClone) {
+  if (FunctionsToClone.empty()) {
+    return nullptr;
+  }
+
+  auto &Context = FunctionsToClone.front()->getContext();
+  auto M = std::make_unique<Module>("temp", Context);
+
+  for (auto *F : FunctionsToClone) {
+    SmallVector<ReturnInst *, 8> Returns;
+    ValueToValueMapTy VMap;
+    auto *NewF = cast<Function>(
+        M->getOrInsertFunction(F->getName(), F->getFunctionType()).getCallee());
+    CloneFunctionInto(NewF, F, VMap, CloneFunctionChangeType::DifferentModule,
+                      Returns);
+  }
+
+  return std::move(M);
+}
+
+std::optional<std::unique_ptr<Module>> unwrapAndSaveClone(IRUnitRef IR) {
+  if (!shouldPrintIR(IR))
+    return std::nullopt;
+
+  auto *OrigM = unwrapModule(IR);
+
+  if (forcePrintModuleIR()) {
+    return CloneModule(*OrigM);
+  }
+
+  if (const auto *_ = dyn_cast<Module>(IR)) {
+    return CloneModule(*OrigM);
+  }
+
+  if (const auto *F = dyn_cast<Function>(IR)) {
+    SmallVector<const Function *, 1> FuncsToSave = {F};
+    return cloneFunctionsIntoNewModule(FuncsToSave);
+  }
+
+  if (auto *C = dyn_cast<LazyCallGraph::SCC>(IR)) {
+    SmallVector<const Function *, 8> FuncsToSave;
+
+    for (LazyCallGraph::Node &N : *C) {
+      Function &F = N.getFunction();
+      if (!F.isDeclaration() && isFunctionInPrintList(F.getName())) {
+        FuncsToSave.push_back(&F);
+      }
+    }
+
+    return cloneFunctionsIntoNewModule(FuncsToSave);
+  }
+
+  if (const auto *_ = dyn_cast<Loop>(IR)) {
+    return std::nullopt;
+  }
+
+  if (const auto *_ = dyn_cast<MachineFunction>(IR)) {
+    return std::nullopt;
+  }
+
+  llvm_unreachable("Unknown wrapped IR type");
+}
+
 // Return true when this is a pass for which changes should be ignored
 bool isIgnored(StringRef PassID) {
   return isSpecialPass(PassID,
@@ -2453,15 +2518,25 @@ StandardInstrumentations::StandardInstrumentations(
 PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter =
     nullptr;
 
+void PrintCrashIRInstrumentation::printToStream(raw_ostream &OS) {
+  // We always print SavedString.
+  OS << SavedString;
+
+  if (SavedModule) {
+    printIR(OS, SavedModule.get());
+  }
+}
+
 void PrintCrashIRInstrumentation::reportCrashIR() {
   if (!PrintOnCrashPath.empty()) {
     std::error_code EC;
     raw_fd_ostream Out(PrintOnCrashPath, EC);
     if (EC)
       report_fatal_error(errorCodeToError(EC));
-    Out << SavedIR;
+
+    printToStream(Out);
   } else {
-    dbgs() << SavedIR;
+    printToStream(dbgs());
   }
 }
 
@@ -2495,8 +2570,10 @@ void PrintCrashIRInstrumentation::registerCallbacks(
 
   PIC.registerBeforeNonSkippedPassCallback(
       [&PIC, this](StringRef PassID, IRUnitRef IR) {
-        SavedIR.clear();
-        raw_string_ostream OS(SavedIR);
+        SavedString.clear();
+        SavedModule.reset();
+        raw_string_ostream OS(SavedString);
+
         OS << formatv("; *** Dump of {0}IR Before Last Pass {1}",
                       llvm::forcePrintModuleIR() ? "Module " : "", PassID);
         if (!isInteresting(IR, PassID, PIC.getPassNameForClassName(PassID))) {
@@ -2504,7 +2581,17 @@ void PrintCrashIRInstrumentation::registerCallbacks(
           return;
         }
         OS << " Started ***\n";
-        unwrapAndPrint(OS, IR);
+
+        // Cloning a Module is significantly faster than serializing to a string
+        // in most situations.
+        auto ClonedModule = unwrapAndSaveClone(IR);
+
+        if (ClonedModule.has_value()) {
+          SavedModule = std::move(ClonedModule.value());
+        } else {
+          // We only rely on SavedString for MachineFunctions & Loops
+          unwrapAndPrint(OS, IR);
+        }
       });
 }
 
diff --git a/llvm/test/Other/print-on-crash.ll b/llvm/test/Other/print-on-crash.ll
index f1e3414fb6352..8ea53b0884372 100644
--- a/llvm/test/Other/print-on-crash.ll
+++ b/llvm/test/Other/print-on-crash.ll
@@ -17,6 +17,8 @@
 
 ; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash-module -filter-passes=blah < %s 2>&1 | FileCheck %s --check-prefix=CHECK_FILTERED
 
+; RUN: not --crash opt -print-on-crash -passes=trigger-crash-function < %s 2>&1 | FileCheck %s --check-prefix=CHECK_FUNCTION
+
 ; CHECK_SIMPLE: ; *** Dump of IR Before Last Pass {{.*}} Started ***
 ; CHECK_SIMPLE: @main
 ; CHECK_SIMPLE: entry:
@@ -24,6 +26,8 @@
 ; CHECK_MODULE: *** Dump of Module IR Before Last Pass {{.*}} Started ***
 ; CHECK_MODULE: ; ModuleID = {{.*}}
 ; CHECK_FILTERED: *** Dump of Module IR Before Last Pass {{.*}} Filtered Out ***
+; CHECK_FUNCTION: *** Dump of IR Before Last Pass {{.*}} Started ***
+; CHECK_FUNCTION: define i32 @main()
 
 define i32 @main() {
 entry:

>From 037572a490cf78720f902062c5b3edaee454bf75 Mon Sep 17 00:00:00 2001
From: Miguel Arroyo <Miguel.Arroyo at rockstargames.com>
Date: Mon, 31 Aug 2026 15:21:03 -0700
Subject: [PATCH 2/6] remove redundant std::move

---
 llvm/lib/Passes/StandardInstrumentations.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index 80b13daf039e1..904476c1ceb62 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -343,7 +343,7 @@ cloneFunctionsIntoNewModule(ArrayRef<const Function *> FunctionsToClone) {
                       Returns);
   }
 
-  return std::move(M);
+  return M;
 }
 
 std::optional<std::unique_ptr<Module>> unwrapAndSaveClone(IRUnitRef IR) {

>From 8dd071877a745c025906400342aeb5627b281108 Mon Sep 17 00:00:00 2001
From: Miguel Arroyo <Miguel.Arroyo at rockstargames.com>
Date: Mon, 31 Aug 2026 15:23:32 -0700
Subject: [PATCH 3/6] Remove redundant isDeclaration() checks

---
 llvm/lib/Passes/StandardInstrumentations.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index 904476c1ceb62..7fb3c7917db4b 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -169,7 +169,7 @@ const Module *unwrapModule(IRUnitRef IR, bool Force = false) {
   if (const auto *C = dyn_cast<LazyCallGraph::SCC>(IR)) {
     for (const LazyCallGraph::Node &N : *C) {
       const Function &F = N.getFunction();
-      if (Force || (!F.isDeclaration() && isFunctionInPrintList(F.getName()))) {
+      if (Force || isFunctionInPrintList(F.getName())) {
         return F.getParent();
       }
     }
@@ -212,7 +212,7 @@ void printIR(raw_ostream &OS, const Module *M) {
 void printIR(raw_ostream &OS, const LazyCallGraph::SCC *C) {
   for (const LazyCallGraph::Node &N : *C) {
     const Function &F = N.getFunction();
-    if (!F.isDeclaration() && isFunctionInPrintList(F.getName())) {
+    if (isFunctionInPrintList(F.getName())) {
       F.print(OS);
     }
   }
@@ -370,7 +370,7 @@ std::optional<std::unique_ptr<Module>> unwrapAndSaveClone(IRUnitRef IR) {
 
     for (LazyCallGraph::Node &N : *C) {
       Function &F = N.getFunction();
-      if (!F.isDeclaration() && isFunctionInPrintList(F.getName())) {
+      if (isFunctionInPrintList(F.getName())) {
         FuncsToSave.push_back(&F);
       }
     }

>From 83219f1607367c1b8c6caf4ae8dc07986d41d4f1 Mon Sep 17 00:00:00 2001
From: Miguel Arroyo <Miguel.Arroyo at rockstargames.com>
Date: Mon, 31 Aug 2026 15:47:02 -0700
Subject: [PATCH 4/6] Add tests for Machine Function & CGSCC

---
 llvm/test/Other/print-on-crash.ll | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/llvm/test/Other/print-on-crash.ll b/llvm/test/Other/print-on-crash.ll
index 8ea53b0884372..e2b942ba3b31d 100644
--- a/llvm/test/Other/print-on-crash.ll
+++ b/llvm/test/Other/print-on-crash.ll
@@ -19,6 +19,11 @@
 
 ; RUN: not --crash opt -print-on-crash -passes=trigger-crash-function < %s 2>&1 | FileCheck %s --check-prefix=CHECK_FUNCTION
 
+; RUN: not --crash opt -print-on-crash -passes=cgscc(trigger-crash-cgscc) < %s 2>&1 | FileCheck %s --check-prefix=CHECK_CGSCC
+
+; RUN: llc -stop-after=machine-cp %s -o %t.mir
+; RUN: not --crash llc -print-on-crash -passes=trigger-crash-machine-function %t.mir 2>&1 | FileCheck %s --check-prefix=CHECK_MACHINE_FUNCTION
+
 ; CHECK_SIMPLE: ; *** Dump of IR Before Last Pass {{.*}} Started ***
 ; CHECK_SIMPLE: @main
 ; CHECK_SIMPLE: entry:
@@ -28,10 +33,20 @@
 ; CHECK_FILTERED: *** Dump of Module IR Before Last Pass {{.*}} Filtered Out ***
 ; CHECK_FUNCTION: *** Dump of IR Before Last Pass {{.*}} Started ***
 ; CHECK_FUNCTION: define i32 @main()
+; CHECK_CGSCC: *** Dump of IR Before Last Pass {{.*}} Started ***
+; CHECK_CGSCC: define i32 @main()
+; CHECK_MACHINE_FUNCTION: *** Dump of IR Before Last Pass {{.*}} Started ***
+; CHECK_MACHINE_FUNCTION: # Machine code for function main
 
 define i32 @main() {
 entry:
   %retval = alloca i32, align 4
   store i32 0, ptr %retval, align 4
+  br label %loop
+
+loop:
+  br label %loop
+
+exit:
   ret i32 0
 }

>From a893c51a9fd28249eaee8bcc5da543e862330842 Mon Sep 17 00:00:00 2001
From: Miguel Arroyo <Miguel.Arroyo at rockstargames.com>
Date: Mon, 31 Aug 2026 19:46:43 -0700
Subject: [PATCH 5/6] Use isa<>

---
 llvm/lib/Passes/StandardInstrumentations.cpp | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index 7fb3c7917db4b..ebea5b211b8a2 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -356,7 +356,7 @@ std::optional<std::unique_ptr<Module>> unwrapAndSaveClone(IRUnitRef IR) {
     return CloneModule(*OrigM);
   }
 
-  if (const auto *_ = dyn_cast<Module>(IR)) {
+  if (isa<Module>(IR)) {
     return CloneModule(*OrigM);
   }
 
@@ -378,11 +378,11 @@ std::optional<std::unique_ptr<Module>> unwrapAndSaveClone(IRUnitRef IR) {
     return cloneFunctionsIntoNewModule(FuncsToSave);
   }
 
-  if (const auto *_ = dyn_cast<Loop>(IR)) {
+  if (isa<Loop>(IR)) {
     return std::nullopt;
   }
 
-  if (const auto *_ = dyn_cast<MachineFunction>(IR)) {
+  if (isa<MachineFunction>(IR)) {
     return std::nullopt;
   }
 
@@ -2584,10 +2584,8 @@ void PrintCrashIRInstrumentation::registerCallbacks(
 
         // Cloning a Module is significantly faster than serializing to a string
         // in most situations.
-        auto ClonedModule = unwrapAndSaveClone(IR);
-
-        if (ClonedModule.has_value()) {
-          SavedModule = std::move(ClonedModule.value());
+        if (auto Module = unwrapAndSaveClone(IR)) {
+          SavedModule = std::move(*Module);
         } else {
           // We only rely on SavedString for MachineFunctions & Loops
           unwrapAndPrint(OS, IR);

>From 91754d866ddbe2067a6b1a8f70eb46a0f55375c3 Mon Sep 17 00:00:00 2001
From: Miguel Arroyo <Miguel.Arroyo at rockstargames.com>
Date: Tue, 1 Sep 2026 12:16:26 -0700
Subject: [PATCH 6/6] VMap when cloning function

---
 llvm/lib/Passes/StandardInstrumentations.cpp | 8 ++++++++
 llvm/test/Other/print-on-crash.ll            | 6 +++---
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index ebea5b211b8a2..01e5620de8b4d 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -339,6 +339,14 @@ cloneFunctionsIntoNewModule(ArrayRef<const Function *> FunctionsToClone) {
     ValueToValueMapTy VMap;
     auto *NewF = cast<Function>(
         M->getOrInsertFunction(F->getName(), F->getFunctionType()).getCallee());
+
+    auto *NewFArgIt = NewF->arg_begin();
+    for (auto &Arg : F->args()) {
+      auto ArgName = Arg.getName();
+      NewFArgIt->setName(ArgName);
+      VMap[&Arg] = &(*NewFArgIt++);
+    }
+
     CloneFunctionInto(NewF, F, VMap, CloneFunctionChangeType::DifferentModule,
                       Returns);
   }
diff --git a/llvm/test/Other/print-on-crash.ll b/llvm/test/Other/print-on-crash.ll
index e2b942ba3b31d..a108aad05fe5f 100644
--- a/llvm/test/Other/print-on-crash.ll
+++ b/llvm/test/Other/print-on-crash.ll
@@ -32,13 +32,13 @@
 ; CHECK_MODULE: ; ModuleID = {{.*}}
 ; CHECK_FILTERED: *** Dump of Module IR Before Last Pass {{.*}} Filtered Out ***
 ; CHECK_FUNCTION: *** Dump of IR Before Last Pass {{.*}} Started ***
-; CHECK_FUNCTION: define i32 @main()
+; CHECK_FUNCTION: define i32 @main
 ; CHECK_CGSCC: *** Dump of IR Before Last Pass {{.*}} Started ***
-; CHECK_CGSCC: define i32 @main()
+; CHECK_CGSCC: define i32 @main
 ; CHECK_MACHINE_FUNCTION: *** Dump of IR Before Last Pass {{.*}} Started ***
 ; CHECK_MACHINE_FUNCTION: # Machine code for function main
 
-define i32 @main() {
+define i32 @main(i32 %argc, ptr %argv) {
 entry:
   %retval = alloca i32, align 4
   store i32 0, ptr %retval, align 4



More information about the llvm-commits mailing list