[llvm] [OPT] Fixes print-changed=dot-cfg for unnamed basic blocks. (PR #148582)
Siliang Qin via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 14 01:18:28 PDT 2025
https://github.com/5c4lar created https://github.com/llvm/llvm-project/pull/148582
Fixes #147659. Introduce a new template function `getBlockName` to streamline the process of obtaining block names for both `BasicBlock` and `MachineBasicBlock`. Update the `BlockDataT` constructor and related functions to utilize this new method, ensuring consistent handling of unnamed blocks. This change prevents crashes when handling basic blocks without names.
>From 657cc452dbdacb6720d640f05aeefd19694bd5b6 Mon Sep 17 00:00:00 2001
From: 5c4lar <qinsiliang18 at mails.ucas.ac.cn>
Date: Mon, 14 Jul 2025 15:06:41 +0800
Subject: [PATCH] Refactor block name retrieval in StandardInstrumentations
Introduce a new template function `getBlockName` to streamline the process of obtaining block names for both `BasicBlock` and `MachineBasicBlock`. Update the `BlockDataT` constructor and related functions to utilize this new method, ensuring consistent handling of unnamed blocks. This change prevents crashes when handling basic blocks without names.
---
.../llvm/Passes/StandardInstrumentations.h | 12 ++-
llvm/lib/Passes/StandardInstrumentations.cpp | 24 +++---
.../DotCfg/print-changed-dot-cfg-unnamed.ll | 74 +++++++++++++++++++
3 files changed, 94 insertions(+), 16 deletions(-)
create mode 100644 llvm/test/Other/ChangePrinters/DotCfg/print-changed-dot-cfg-unnamed.ll
diff --git a/llvm/include/llvm/Passes/StandardInstrumentations.h b/llvm/include/llvm/Passes/StandardInstrumentations.h
index 4ee5ab2554868..c7fe2fd840103 100644
--- a/llvm/include/llvm/Passes/StandardInstrumentations.h
+++ b/llvm/include/llvm/Passes/StandardInstrumentations.h
@@ -325,16 +325,24 @@ class LLVM_ABI IRChangedTester : public IRChangedPrinter {
Any) override;
};
+template <typename T> static inline std::string getBlockName(const T &B) {
+ if (B.hasName())
+ return B.getName().str();
+ return std::to_string(B.getNumber());
+}
+
// Information that needs to be saved for a basic block in order to compare
// before and after the pass to determine if it was changed by a pass.
template <typename T> class BlockDataT {
public:
- BlockDataT(const BasicBlock &B) : Label(B.getName().str()), Data(B) {
+ BlockDataT(const BasicBlock &B) : Label(getBlockName(B)), Data(B) {
+ assert(!Label.empty() && "Expected block to have name");
raw_string_ostream SS(Body);
B.print(SS, nullptr, true, true);
}
- BlockDataT(const MachineBasicBlock &B) : Label(B.getName().str()), Data(B) {
+ BlockDataT(const MachineBasicBlock &B) : Label(getBlockName(B)), Data(B) {
+ assert(!Label.empty() && "Expected block to have name");
raw_string_ostream SS(Body);
B.print(SS);
}
diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index 0623e66772047..d5e81aa8f6a11 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -727,14 +727,10 @@ template <typename T>
template <typename FunctionT>
bool IRComparer<T>::generateFunctionData(IRDataT<T> &Data, const FunctionT &F) {
if (shouldGenerateData(F)) {
- FuncDataT<T> FD(F.front().getName().str());
- int I = 0;
+ std::string EntryBlockName = getBlockName(F.front());
+ FuncDataT<T> FD(EntryBlockName);
for (const auto &B : F) {
- std::string BBName = B.getName().str();
- if (BBName.empty()) {
- BBName = formatv("{0}", I);
- ++I;
- }
+ std::string BBName = getBlockName(B);
FD.getOrder().emplace_back(BBName);
FD.getData().insert({BBName, B});
}
@@ -2190,27 +2186,27 @@ DCData::DCData(const BasicBlock &B) {
const Instruction *Term = B.getTerminator();
if (const BranchInst *Br = dyn_cast<const BranchInst>(Term))
if (Br->isUnconditional())
- addSuccessorLabel(Br->getSuccessor(0)->getName().str(), "");
+ addSuccessorLabel(getBlockName(*Br->getSuccessor(0)), "");
else {
- addSuccessorLabel(Br->getSuccessor(0)->getName().str(), "true");
- addSuccessorLabel(Br->getSuccessor(1)->getName().str(), "false");
+ addSuccessorLabel(getBlockName(*Br->getSuccessor(0)), "true");
+ addSuccessorLabel(getBlockName(*Br->getSuccessor(1)), "false");
}
else if (const SwitchInst *Sw = dyn_cast<const SwitchInst>(Term)) {
- addSuccessorLabel(Sw->case_default()->getCaseSuccessor()->getName().str(),
+ addSuccessorLabel(getBlockName(*Sw->case_default()->getCaseSuccessor()),
"default");
for (auto &C : Sw->cases()) {
assert(C.getCaseValue() && "Expected to find case value.");
SmallString<20> Value = formatv("{0}", C.getCaseValue()->getSExtValue());
- addSuccessorLabel(C.getCaseSuccessor()->getName().str(), Value);
+ addSuccessorLabel(getBlockName(*C.getCaseSuccessor()), Value);
}
} else
for (const BasicBlock *Succ : successors(&B))
- addSuccessorLabel(Succ->getName().str(), "");
+ addSuccessorLabel(getBlockName(*Succ), "");
}
DCData::DCData(const MachineBasicBlock &B) {
for (const MachineBasicBlock *Succ : successors(&B))
- addSuccessorLabel(Succ->getName().str(), "");
+ addSuccessorLabel(getBlockName(*Succ), "");
}
DotCfgChangeReporter::DotCfgChangeReporter(bool Verbose)
diff --git a/llvm/test/Other/ChangePrinters/DotCfg/print-changed-dot-cfg-unnamed.ll b/llvm/test/Other/ChangePrinters/DotCfg/print-changed-dot-cfg-unnamed.ll
new file mode 100644
index 0000000000000..f3b75fc3cdc33
--- /dev/null
+++ b/llvm/test/Other/ChangePrinters/DotCfg/print-changed-dot-cfg-unnamed.ll
@@ -0,0 +1,74 @@
+; Simple checks of -print-changed=dot-cfg with basic blocks that have no names
+;
+; Note that (mostly) only the banners are checked.
+;
+; Simple functionality check with SimplifyCFGPass on function with unnamed basic blocks.
+; RUN: rm -rf %t && mkdir -p %t
+; RUN: opt -disable-verify -S -print-changed=dot-cfg -passes=simplifycfg -dot-cfg-dir=%t < %s -o /dev/null
+; RUN: ls %t/*.pdf %t/passes.html | count 3
+; RUN: FileCheck %s -input-file=%t/passes.html --check-prefix=CHECK-DOT-CFG-NO-NAME-SIMPLE
+
+; Check that only the passes that change the IR are printed for unnamed basic blocks.
+; RUN: rm -rf %t && mkdir -p %t
+; RUN: opt -disable-verify -S -print-changed=dot-cfg -passes=simplifycfg -filter-print-funcs=g -dot-cfg-dir=%t < %s -o /dev/null
+; RUN: ls %t/*.pdf %t/passes.html | count 3
+; RUN: FileCheck %s -input-file=%t/passes.html --check-prefix=CHECK-DOT-CFG-NO-NAME-FUNC-FILTER
+
+; Check that the reporting works with -print-module-scope for unnamed basic blocks
+; RUN: rm -rf %t && mkdir -p %t
+; RUN: opt -disable-verify -S -print-changed=dot-cfg -passes=simplifycfg -print-module-scope -dot-cfg-dir=%t < %s -o /dev/null
+; RUN: ls %t/*.pdf %t/passes.html | count 3
+; RUN: FileCheck %s -input-file=%t/passes.html --check-prefix=CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE
+
+define void @g(i32 %0) {
+ %2 = icmp sgt i32 %0, 0
+ br i1 %2, label %3, label %4
+
+3: ; preds = %1
+ br label %4
+
+4: ; preds = %3, %1
+ ret void
+}
+
+; CHECK-DOT-CFG-NO-NAME-SIMPLE: <!doctype html><html><head><style>.collapsible { background-color: #777; color: white; cursor: pointer; padding: 18px; width: 100%; border: none; text-align: left; outline: none; font-size: 15px;} .active, .collapsible:hover { background-color: #555;} .content { padding: 0 18px; display: none; overflow: hidden; background-color: #f1f1f1;}</style><title>passes.html</title></head>
+; CHECK-DOT-CFG-NO-NAME-SIMPLE-NEXT: <body><button type="button" class="collapsible">0. Initial IR (by function)</button>
+; CHECK-DOT-CFG-NO-NAME-SIMPLE-NEXT: <div class="content">
+; CHECK-DOT-CFG-NO-NAME-SIMPLE-NEXT: <p>
+; CHECK-DOT-CFG-NO-NAME-SIMPLE-NEXT: <a href="diff_0_0.pdf" target="_blank">0.0. Initial IR</a><br/>
+; CHECK-DOT-CFG-NO-NAME-SIMPLE-NEXT: </p>
+; CHECK-DOT-CFG-NO-NAME-SIMPLE-NEXT: </div><br/>
+; CHECK-DOT-CFG-NO-NAME-SIMPLE-NEXT: <a href="diff_1.pdf" target="_blank">1. Pass SimplifyCFGPass on g</a><br/>
+; CHECK-DOT-CFG-NO-NAME-SIMPLE-NEXT: </p></div>
+; CHECK-DOT-CFG-NO-NAME-SIMPLE-NEXT: <a>2. PassManager{{.*}} on g ignored</a><br/>
+; CHECK-DOT-CFG-NO-NAME-SIMPLE-NEXT: <a>3. ModuleToFunctionPassAdaptor on [module] ignored</a><br/>
+; CHECK-DOT-CFG-NO-NAME-SIMPLE-NEXT: <a>4. PrintModulePass on [module] ignored</a><br/>
+; CHECK-DOT-CFG-NO-NAME-SIMPLE-NEXT: <script>var coll = document.getElementsByClassName("collapsible");var i;for (i = 0; i < coll.length; i++) {coll[i].addEventListener("click", function() { this.classList.toggle("active"); var content = this.nextElementSibling; if (content.style.display === "block"){ content.style.display = "none"; } else { content.style.display= "block"; } }); }</script></body></html>
+
+; CHECK-DOT-CFG-NO-NAME-FUNC-FILTER: <!doctype html><html><head><style>.collapsible { background-color: #777; color: white; cursor: pointer; padding: 18px; width: 100%; border: none; text-align: left; outline: none; font-size: 15px;} .active, .collapsible:hover { background-color: #555;} .content { padding: 0 18px; display: none; overflow: hidden; background-color: #f1f1f1;}</style><title>passes.html</title></head>
+; CHECK-DOT-CFG-NO-NAME-FUNC-FILTER-NEXT: <body><button type="button" class="collapsible">0. Initial IR (by function)</button>
+; CHECK-DOT-CFG-NO-NAME-FUNC-FILTER-NEXT: <div class="content">
+; CHECK-DOT-CFG-NO-NAME-FUNC-FILTER-NEXT: <p>
+; CHECK-DOT-CFG-NO-NAME-FUNC-FILTER-NEXT: <a href="diff_0_0.pdf" target="_blank">0.0. Initial IR</a><br/>
+; CHECK-DOT-CFG-NO-NAME-FUNC-FILTER-NEXT: </p>
+; CHECK-DOT-CFG-NO-NAME-FUNC-FILTER-NEXT: </div><br/>
+; CHECK-DOT-CFG-NO-NAME-FUNC-FILTER-NEXT: <a href="diff_1.pdf" target="_blank">1. Pass SimplifyCFGPass on g</a><br/>
+; CHECK-DOT-CFG-NO-NAME-FUNC-FILTER-NEXT: </p></div>
+; CHECK-DOT-CFG-NO-NAME-FUNC-FILTER-NEXT: <a>2. PassManager{{.*}} on g ignored</a><br/>
+; CHECK-DOT-CFG-NO-NAME-FUNC-FILTER-NEXT: <a>3. ModuleToFunctionPassAdaptor on [module] ignored</a><br/>
+; CHECK-DOT-CFG-NO-NAME-FUNC-FILTER-NEXT: <a>4. PrintModulePass on [module] ignored</a><br/>
+; CHECK-DOT-CFG-NO-NAME-FUNC-FILTER-NEXT: <script>var coll = document.getElementsByClassName("collapsible");var i;for (i = 0; i < coll.length; i++) {coll[i].addEventListener("click", function() { this.classList.toggle("active"); var content = this.nextElementSibling; if (content.style.display === "block"){ content.style.display = "none"; } else { content.style.display= "block"; } }); }</script></body></html>
+
+; CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE: <!doctype html><html><head><style>.collapsible { background-color: #777; color: white; cursor: pointer; padding: 18px; width: 100%; border: none; text-align: left; outline: none; font-size: 15px;} .active, .collapsible:hover { background-color: #555;} .content { padding: 0 18px; display: none; overflow: hidden; background-color: #f1f1f1;}</style><title>passes.html</title></head>
+; CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE-NEXT: <body><button type="button" class="collapsible">0. Initial IR (by function)</button>
+; CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE-NEXT: <div class="content">
+; CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE-NEXT: <p>
+; CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE-NEXT: <a href="diff_0_0.pdf" target="_blank">0.0. Initial IR</a><br/>
+; CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE-NEXT: </p>
+; CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE-NEXT: </div><br/>
+; CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE-NEXT: <a href="diff_1.pdf" target="_blank">1. Pass SimplifyCFGPass on g</a><br/>
+; CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE-NEXT: </p></div>
+; CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE-NEXT: <a>2. PassManager{{.*}} on g ignored</a><br/>
+; CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE-NEXT: <a>3. ModuleToFunctionPassAdaptor on [module] ignored</a><br/>
+; CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE-NEXT: <a>4. PrintModulePass on [module] ignored</a><br/>
+; CHECK-DOT-CFG-NO-NAME-PRINT-MOD-SCOPE-NEXT: <script>var coll = document.getElementsByClassName("collapsible");var i;for (i = 0; i < coll.length; i++) {coll[i].addEventListener("click", function() { this.classList.toggle("active"); var content = this.nextElementSibling; if (content.style.display === "block"){ content.style.display = "none"; } else { content.style.display= "block"; } }); }</script></body></html>
More information about the llvm-commits
mailing list