[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