[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