[clang] [CIR] Only scan globals and constants for block addresses in GotoSolver (PR #232870)

Bruno Cardoso Lopes via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 16 10:54:14 PDT 2026


https://github.com/bcardosolopes updated https://github.com/llvm/llvm-project/pull/232870

>From d1140bfd2293d339745472b5350a43aad0d11f2d Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Wed, 7 Oct 2026 16:42:52 -0700
Subject: [PATCH] [CIR] Only scan globals and constants for block addresses in
 GotoSolver

The pass walked every op in the module checking inherent attributes for
BlockAddrInfoAttr/BlockAddrDiffAttr. Block addresses only appear in
attributes carried by globals and cir.const values (process() collects
cir.block_address ops directly), so scan just those ops and fold the two
attribute walks into one. The scan assumes module scope, so the pass is
declared on ModuleOp like the other lowering passes.

Successor order now follows attribute order: constant labels precede
cir.block_address labels, and mixed attribute kinds in one initializer
follow initializer order instead of grouping by kind. This matches the
order the pass documents; program behavior is unchanged.

Saves most of the 0.3-3.0s GotoSolver time on the slowest self-host TUs
(e.g. 3.0s -> 0.14s on clang/lib/Sema/SemaExpr.cpp).

Assisted-by: Muse Code (Muse).
---
 clang/include/clang/CIR/Dialect/Passes.td     |  2 +-
 .../lib/CIR/Dialect/Transforms/GotoSolver.cpp | 48 +++++++-------
 clang/test/CIR/Transforms/goto_solver.cir     | 63 +++++++++++++++++++
 3 files changed, 91 insertions(+), 22 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/Passes.td b/clang/include/clang/CIR/Dialect/Passes.td
index 6994aa8a4b18ad..9b260af789384c 100644
--- a/clang/include/clang/CIR/Dialect/Passes.td
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -72,7 +72,7 @@ def CIRFlattenCFG : Pass<"cir-flatten-cfg"> {
   let dependentDialects = ["cir::CIRDialect"];
 }
 
-def GotoSolver : Pass<"cir-goto-solver"> {
+def GotoSolver : Pass<"cir-goto-solver", "mlir::ModuleOp"> {
   let summary = "Replaces goto operations with branches";
   let description = [{
     This pass transforms CIR and replaces goto-s with branch
diff --git a/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp b/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
index ad79927fc40aca..08e4d2eb0a5fe3 100644
--- a/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
@@ -115,27 +115,33 @@ static void process(cir::FuncOp func,
 void GotoSolverPass::runOnOperation() {
   llvm::TimeTraceScope scope("Goto Solver");
 
-  // Block addresses can also appear in attributes outside of any function body,
-  // such as global variable initializers.  Collect, per target function and in
-  // initializer order, the labels referenced this way so their LabelOps survive
-  // and join the indirect branch's successors.  A SetVector keeps the first
-  // occurrence in order: a label named more than once across initializers needs
-  // to be a successor only once.
+  // Block addresses also appear in attributes: global initializers and
+  // cir.const values. Collect, per target function and in walk order, the
+  // labels referenced this way so their LabelOps survive and join the
+  // indirect branch's successors. A SetVector keeps the first occurrence
+  // in order: a label named more than once is a successor only once.
+  //
+  // process() collects cir.block_address ops; scan just globals and
+  // constants for the rest.
   llvm::StringMap<llvm::SmallSetVector<StringRef, 4>> globalBlockAddrLabels;
-  getOperation()->walk([&](mlir::Operation *op) {
+  mlir::ModuleOp module = mlir::cast<mlir::ModuleOp>(getOperation());
+  module.walk([&](mlir::Operation *op) {
+    if (!mlir::isa<cir::GlobalOp, cir::ConstantOp>(op))
+      return;
     auto collectBlockAddresses = [&](mlir::Attribute attr) {
-      attr.walk([&](cir::BlockAddrInfoAttr info) {
-        globalBlockAddrLabels[info.getFunc().getValue()].insert(
-            info.getLabel());
-      });
-      // A block-address difference attribute references two labels in the same
-      // function; keep both alive.
-      attr.walk([&](cir::BlockAddrDiffAttr diff) {
-        llvm::SmallSetVector<StringRef, 4> &labels =
-            globalBlockAddrLabels[diff.getFunc().getValue()];
-        labels.insert(diff.getLhsLabel().getValue());
-        labels.insert(diff.getRhsLabel().getValue());
-      });
+      attr.walk(
+          [&](cir::BlockAddrInfoAttr info) {
+            globalBlockAddrLabels[info.getFunc().getValue()].insert(
+                info.getLabel());
+          },
+          [&](cir::BlockAddrDiffAttr diff) {
+            // A block-address difference attribute references two labels in
+            // the same function; keep both alive.
+            llvm::SmallSetVector<StringRef, 4> &labels =
+                globalBlockAddrLabels[diff.getFunc().getValue()];
+            labels.insert(diff.getLhsLabel().getValue());
+            labels.insert(diff.getRhsLabel().getValue());
+          });
     };
     op->getName().walkInherentAttrs(
         op, [&](llvm::StringRef, mlir::Attribute &attr) {
@@ -144,12 +150,12 @@ void GotoSolverPass::runOnOperation() {
   });
 
   static const llvm::SmallVector<StringRef> empty;
-  getOperation()->walk([&](cir::FuncOp func) {
+  for (cir::FuncOp func : module.getOps<cir::FuncOp>()) {
     auto it = globalBlockAddrLabels.find(func.getSymName());
     process(func, it == globalBlockAddrLabels.end()
                       ? llvm::ArrayRef<StringRef>(empty)
                       : it->second.getArrayRef());
-  });
+  }
 }
 
 } // namespace
diff --git a/clang/test/CIR/Transforms/goto_solver.cir b/clang/test/CIR/Transforms/goto_solver.cir
index 26cd9827ae3185..521022f74ef28d 100644
--- a/clang/test/CIR/Transforms/goto_solver.cir
+++ b/clang/test/CIR/Transforms/goto_solver.cir
@@ -1,6 +1,8 @@
 // RUN: cir-opt %s -cir-goto-solver --verify-roundtrip -o - | FileCheck %s
 
 !void = !cir.void
+!s64i = !cir.int<s, 64>
+!rec_S = !cir.struct<"S" {data !s64i, data !cir.ptr<!void>}>
 
 cir.func @a(){
   %0 = cir.alloca "ptr" align(8) init : !cir.ptr<!cir.ptr<!void>>
@@ -88,3 +90,64 @@ cir.func @d() {
 // CHECK:   cir.indirect_br %[[DEST]] : !cir.ptr<!void>, [
 // CHECK:   ^[[L1BB]]
 // CHECK:   ]
+
+cir.func @e() {
+  %0 = cir.alloca "ptr" align(8) init : !cir.ptr<!cir.ptr<!void>>
+  %1 = cir.block_address <@e, "a"> : !cir.ptr<!void>
+  %2 = cir.const #cir.block_addr_info<@e, "b"> : !cir.ptr<!void>
+  cir.store align(8) %1, %0 : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+  %3 = cir.load align(8) %0 : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
+  cir.indirect_goto %3 : !cir.ptr<!void>
+^bb1:
+  cir.label "a"
+  cir.return
+^bb2:
+  cir.label "b"
+  cir.return
+}
+
+// Attribute-carried labels precede cir.block_address labels: the constant's
+// "b" sorts before the block_address op's "a" in the successor list.
+// CHECK: cir.func @e()
+// CHECK: ^[[ABB:bb[0-9]+]]:
+// CHECK:   cir.label "a"
+// CHECK: ^[[BBB:bb[0-9]+]]:
+// CHECK:   cir.label "b"
+// CHECK: ^[[EIGB:bb[0-9]+]](%{{.*}}: !cir.ptr<!void>{{.*}}):
+// CHECK:   cir.indirect_br {{.*}} : !cir.ptr<!void>, [
+// CHECK:   ^[[BBB]]
+// CHECK:   ^[[ABB]]
+// CHECK:   ]
+
+cir.global internal @f.s = #cir.const_record<{#cir.block_addr_diff<@f, "b", "c"> : !s64i, #cir.block_addr_info<@f, "a"> : !cir.ptr<!void>}> : !rec_S
+
+cir.func @f() {
+  %0 = cir.alloca "ptr" align(8) init : !cir.ptr<!cir.ptr<!void>>
+  %1 = cir.load align(8) %0 : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
+  cir.indirect_goto %1 : !cir.ptr<!void>
+^bb1:
+  cir.label "a"
+  cir.return
+^bb2:
+  cir.label "b"
+  cir.return
+^bb3:
+  cir.label "c"
+  cir.return
+}
+
+// Mixed attribute kinds in one initializer follow attribute order: the
+// diff's "b", "c" precede the info's "a".
+// CHECK: cir.func @f()
+// CHECK: ^[[FABB:bb[0-9]+]]:
+// CHECK:   cir.label "a"
+// CHECK: ^[[FBBB:bb[0-9]+]]:
+// CHECK:   cir.label "b"
+// CHECK: ^[[FCBB:bb[0-9]+]]:
+// CHECK:   cir.label "c"
+// CHECK: ^[[FIGB:bb[0-9]+]](%{{.*}}: !cir.ptr<!void>{{.*}}):
+// CHECK:   cir.indirect_br {{.*}} : !cir.ptr<!void>, [
+// CHECK:   ^[[FBBB]]
+// CHECK:   ^[[FCBB]]
+// CHECK:   ^[[FABB]]
+// CHECK:   ]



More information about the cfe-commits mailing list