[llvm] [llvm] Optimize print-on-crash serialization (PR #205682)
Miguel A. Arroyo via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 26 07:49:09 PDT 2026
https://github.com/mayanez updated https://github.com/llvm/llvm-project/pull/205682
>From f7134dfda4c89348464c47ce058700f3d987dba6 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] [llvm] Optimize print-on-crash serialization
---
.../llvm/Passes/StandardInstrumentations.h | 8 +-
llvm/lib/Passes/StandardInstrumentations.cpp | 88 +++++++++++++++++--
llvm/test/Other/print-on-crash.ll | 4 +
3 files changed, 93 insertions(+), 7 deletions(-)
diff --git a/llvm/include/llvm/Passes/StandardInstrumentations.h b/llvm/include/llvm/Passes/StandardInstrumentations.h
index 4ee5ab2554868..f107e757f96b4 100644
--- a/llvm/include/llvm/Passes/StandardInstrumentations.h
+++ b/llvm/include/llvm/Passes/StandardInstrumentations.h
@@ -579,13 +579,17 @@ 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;
+ std::vector<llvm::Function *> SavedFunctions;
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 075cd56402ca9..4ce78bbd64bfe 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -42,6 +42,7 @@
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/xxhash.h"
+#include "llvm/Transforms/Utils/Cloning.h"
#include <utility>
#include <vector>
@@ -331,6 +332,51 @@ void unwrapAndPrint(raw_ostream &OS, Any IR) {
llvm_unreachable("Unknown wrapped IR type");
}
+std::optional<std::pair<std::unique_ptr<Module>, std::vector<Function *>>>
+unwrapAndSaveClone(Any IR) {
+ std::vector<Function *> ValuesToSave;
+
+ if (!shouldPrintIR(IR))
+ return std::nullopt;
+
+ auto *OrigM = unwrapModule(IR);
+ auto M = CloneModule(*OrigM);
+
+ if (forcePrintModuleIR()) {
+ return {{std::move(M), ValuesToSave}};
+ }
+
+ if (const auto *_ = unwrapIR<Module>(IR)) {
+ return {{std::move(M), ValuesToSave}};
+ }
+
+ if (const auto *F = unwrapIR<Function>(IR)) {
+ auto *SaveF = M->getFunction(F->getName());
+ ValuesToSave.push_back(SaveF);
+ return {{std::move(M), ValuesToSave}};
+ }
+
+ if (auto *C = unwrapIR<LazyCallGraph::SCC>(IR)) {
+ for (LazyCallGraph::Node &N : *C) {
+ Function &F = N.getFunction();
+ if (!F.isDeclaration() && isFunctionInPrintList(F.getName())) {
+ ValuesToSave.push_back(&F);
+ }
+ }
+ return {{std::move(M), ValuesToSave}};
+ }
+
+ if (const auto *_ = unwrapIR<Loop>(IR)) {
+ return std::nullopt;
+ }
+
+ if (const auto *_ = unwrapIR<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,
@@ -2452,15 +2498,34 @@ StandardInstrumentations::StandardInstrumentations(
PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter =
nullptr;
+void PrintCrashIRInstrumentation::printToStream(raw_ostream &OS) {
+ // We always print SavedString.
+ OS << SavedString;
+
+ if (SavedModule) {
+ // We avoid pushing all functions to ValuesToSave
+ // to avoid unnecessary memory pressure. We treat
+ // empty to mean all functions.
+ if (SavedFunctions.empty()) {
+ printIR(OS, SavedModule.get());
+ } else {
+ for (auto *GV : SavedFunctions) {
+ printIR(OS, cast<Function>(GV));
+ }
+ }
+ }
+}
+
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());
}
}
@@ -2494,8 +2559,10 @@ void PrintCrashIRInstrumentation::registerCallbacks(
PIC.registerBeforeNonSkippedPassCallback(
[&PIC, this](StringRef PassID, Any IR) {
- SavedIR.clear();
- raw_string_ostream OS(SavedIR);
+ SavedModule.reset();
+ SavedString.clear();
+ 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))) {
@@ -2503,7 +2570,18 @@ 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 UseClonePair = unwrapAndSaveClone(IR);
+
+ if (UseClonePair.has_value()) {
+ SavedModule = std::move(UseClonePair->first);
+ SavedFunctions = UseClonePair->second;
+ } 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:
More information about the llvm-commits
mailing list