[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 18:58:51 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 1/2] [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: ]
>From 69d6611fcac862559f9341650e38eae43739f732 Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Fri, 16 Oct 2026 18:52:54 -0700
Subject: [PATCH 2/2] Address review: use the const, reference the global,
explain the scan list
---
clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp | 6 ++++--
clang/test/CIR/Transforms/goto_solver.cir | 2 ++
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp b/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
index 08e4d2eb0a5fe3..6d1b451e1a1e3f 100644
--- a/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp
@@ -121,8 +121,10 @@ void GotoSolverPass::runOnOperation() {
// 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.
+ // process() collects cir.block_address ops in IR order; this scan covers
+ // only attribute-carried addresses (globals, constants) that no
+ // function-body walk sees. BlockAddressOp stays out so op labels keep
+ // their IR-order phase instead of sorting with the attributes.
llvm::StringMap<llvm::SmallSetVector<StringRef, 4>> globalBlockAddrLabels;
mlir::ModuleOp module = mlir::cast<mlir::ModuleOp>(getOperation());
module.walk([&](mlir::Operation *op) {
diff --git a/clang/test/CIR/Transforms/goto_solver.cir b/clang/test/CIR/Transforms/goto_solver.cir
index 521022f74ef28d..2a673180af4823 100644
--- a/clang/test/CIR/Transforms/goto_solver.cir
+++ b/clang/test/CIR/Transforms/goto_solver.cir
@@ -96,6 +96,7 @@ cir.func @e() {
%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>>
+ cir.store align(8) %2, %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:
@@ -123,6 +124,7 @@ cir.global internal @f.s = #cir.const_record<{#cir.block_addr_diff<@f, "b", "c">
cir.func @f() {
%0 = cir.alloca "ptr" align(8) init : !cir.ptr<!cir.ptr<!void>>
+ %g = cir.get_global @f.s : !cir.ptr<!rec_S>
%1 = cir.load align(8) %0 : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
cir.indirect_goto %1 : !cir.ptr<!void>
^bb1:
More information about the cfe-commits
mailing list