[llvm] [AArch64] ConditionOptimizer: widen intra-block path to all select instructions (PR #195266)
Hussam Alhassan via llvm-commits
llvm-commits at lists.llvm.org
Fri May 1 06:55:23 PDT 2026
https://github.com/hussam-alhassan created https://github.com/llvm/llvm-project/pull/195266
The intra-block path previously gated on isCSINCInstruction, restricting optimization to CSINC. Replace the gate with
findCondCodeUseOperandIdxForBranchOrSelect(), which covers the full select family: CSEL, CSINC, CSINV, CSNEG, and FCSEL.
>From ea48e51d1140d58d40104510d7324e68777670af Mon Sep 17 00:00:00 2001
From: Hussam Alhassan <hsm.link at proton.me>
Date: Sun, 12 Apr 2026 21:34:30 +0100
Subject: [PATCH] [AArch64] ConditionOptimizer: widen intra-block path to all
select instructions
The intra-block path previously gated on isCSINCInstruction, restricting
optimization to CSINC. Replace the gate with
findCondCodeUseOperandIdxForBranchOrSelect, which covers the full select
family: CSEL, CSINC, CSINV, CSNEG, and FCSEL.
---
.../AArch64/AArch64ConditionOptimizer.cpp | 13 +-
.../AArch64/aarch64-condopt-select-instrs.mir | 180 ++++++++++++++++++
2 files changed, 183 insertions(+), 10 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/aarch64-condopt-select-instrs.mir
diff --git a/llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp b/llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp
index dfa7dc56a9a30..c5dcaa880ae07 100644
--- a/llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ConditionOptimizer.cpp
@@ -15,7 +15,7 @@
//
// The pass handles:
// * Cross-block: SUBS/ADDS followed by conditional branches
-// * Intra-block: CSINC conditional instructions
+// * Intra-block: Select-family conditional instructions
//
//
// Consider the following example in C:
@@ -57,10 +57,6 @@
// TODO: For cross-block:
// - handle other conditional instructions (e.g. CSET)
// - allow second branching to be anything if it doesn't require adjusting
-// TODO: For intra-block:
-// - handle CINC and CSET (CSINC aliases) as their conditions are inverted
-// compared to CSINC.
-// - handle other non-CSINC conditional instructions
//
//===----------------------------------------------------------------------===//
@@ -240,10 +236,6 @@ static bool isCmpInstruction(unsigned Opc) {
}
}
-static bool isCSINCInstruction(unsigned Opc) {
- return Opc == AArch64::CSINCWr || Opc == AArch64::CSINCXr;
-}
-
// Returns the Bcc terminator if present, otherwise nullptr.
MachineInstr *
AArch64ConditionOptimizerImpl::getBccTerminator(MachineBasicBlock *MBB) {
@@ -577,7 +569,8 @@ bool AArch64ConditionOptimizerImpl::optimizeIntraBlock(MachineBasicBlock &MBB) {
continue;
}
- if (isCSINCInstruction(MI.getOpcode())) {
+ if (AArch64InstrInfo::findCondCodeUseOperandIdxForBranchOrSelect(MI) >= 0 &&
+ !MI.isBranch()) {
if (PendingPair) {
// A second conditional consuming the same CMP would invalidate any
// optimization: modifying the CMP would silently change what both
diff --git a/llvm/test/CodeGen/AArch64/aarch64-condopt-select-instrs.mir b/llvm/test/CodeGen/AArch64/aarch64-condopt-select-instrs.mir
new file mode 100644
index 0000000000000..04ef241768caa
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/aarch64-condopt-select-instrs.mir
@@ -0,0 +1,180 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=aarch64-linux-gnu -run-pass=aarch64-condopt -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=aarch64-linux-gnu -passes=aarch64-condopt %s -o - | FileCheck %s
+
+---
+# CSEL pair — same direction (GT/GT, diff 1). The first CMP is adjusted from
+# #9 to #10 and its CSEL condition relaxed GT -> GE, matching the second pair
+# so CSE can eliminate the duplicate CMP.
+#
+# Input: CMP #9 GT; CMP #10 GT
+# Output: CMP #10 GE; CMP #10 GT
+name: csel_same_direction
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $w0, $w1, $w2, $x3, $x4
+
+ ; CHECK-LABEL: name: csel_same_direction
+ ; CHECK: liveins: $w0, $w1, $w2, $x3, $x4
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32common = COPY $w0
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32 = COPY $w1
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr32 = COPY $w2
+ ; CHECK-NEXT: [[COPY3:%[0-9]+]]:gpr64common = COPY $x3
+ ; CHECK-NEXT: [[COPY4:%[0-9]+]]:gpr64common = COPY $x4
+ ; CHECK-NEXT: [[SUBSWri:%[0-9]+]]:gpr32 = SUBSWri [[COPY]], 10, 0, implicit-def $nzcv
+ ; CHECK-NEXT: [[CSELWr:%[0-9]+]]:gpr32 = CSELWr [[COPY1]], [[COPY2]], 10, implicit $nzcv
+ ; CHECK-NEXT: [[SUBSWri1:%[0-9]+]]:gpr32 = SUBSWri [[COPY]], 10, 0, implicit-def $nzcv
+ ; CHECK-NEXT: [[CSELWr1:%[0-9]+]]:gpr32 = CSELWr [[COPY1]], [[COPY2]], 12, implicit $nzcv
+ ; CHECK-NEXT: STRWui killed [[CSELWr]], [[COPY3]], 0
+ ; CHECK-NEXT: STRWui killed [[CSELWr1]], [[COPY4]], 0
+ ; CHECK-NEXT: RET_ReallyLR
+ %0:gpr32common = COPY $w0
+ %1:gpr32 = COPY $w1
+ %2:gpr32 = COPY $w2
+ %3:gpr64common = COPY $x3
+ %4:gpr64common = COPY $x4
+
+ %5:gpr32 = SUBSWri %0, 9, 0, implicit-def $nzcv
+ %6:gpr32 = CSELWr %1, %2, 12, implicit $nzcv
+
+ %7:gpr32 = SUBSWri %0, 10, 0, implicit-def $nzcv
+ %8:gpr32 = CSELWr %1, %2, 12, implicit $nzcv
+
+ STRWui killed %6, %3, 0
+ STRWui killed %8, %4, 0
+ RET_ReallyLR
+
+...
+---
+# CSINV pair — opposite direction (GT/LT, diff 2). Both CMPs are adjusted to
+# #10, conditions relaxed GT -> GE and LT -> LE.
+#
+# Input: CMP #9 GT; CMP #11 LT
+# Output: CMP #10 GE; CMP #10 LE
+name: csinv_opposite_direction
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $w0, $w1, $w2, $x3, $x4
+
+ ; CHECK-LABEL: name: csinv_opposite_direction
+ ; CHECK: liveins: $w0, $w1, $w2, $x3, $x4
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32common = COPY $w0
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32 = COPY $w1
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr32 = COPY $w2
+ ; CHECK-NEXT: [[COPY3:%[0-9]+]]:gpr64common = COPY $x3
+ ; CHECK-NEXT: [[COPY4:%[0-9]+]]:gpr64common = COPY $x4
+ ; CHECK-NEXT: [[SUBSWri:%[0-9]+]]:gpr32 = SUBSWri [[COPY]], 10, 0, implicit-def $nzcv
+ ; CHECK-NEXT: [[CSINVWr:%[0-9]+]]:gpr32 = CSINVWr [[COPY1]], [[COPY2]], 10, implicit $nzcv
+ ; CHECK-NEXT: [[SUBSWri1:%[0-9]+]]:gpr32 = SUBSWri [[COPY]], 10, 0, implicit-def $nzcv
+ ; CHECK-NEXT: [[CSINVWr1:%[0-9]+]]:gpr32 = CSINVWr [[COPY1]], [[COPY2]], 13, implicit $nzcv
+ ; CHECK-NEXT: STRWui killed [[CSINVWr]], [[COPY3]], 0
+ ; CHECK-NEXT: STRWui killed [[CSINVWr1]], [[COPY4]], 0
+ ; CHECK-NEXT: RET_ReallyLR
+ %0:gpr32common = COPY $w0
+ %1:gpr32 = COPY $w1
+ %2:gpr32 = COPY $w2
+ %3:gpr64common = COPY $x3
+ %4:gpr64common = COPY $x4
+
+ %5:gpr32 = SUBSWri %0, 9, 0, implicit-def $nzcv
+ %6:gpr32 = CSINVWr %1, %2, 12, implicit $nzcv
+
+ %7:gpr32 = SUBSWri %0, 11, 0, implicit-def $nzcv
+ %8:gpr32 = CSINVWr %1, %2, 11, implicit $nzcv
+
+ STRWui killed %6, %3, 0
+ STRWui killed %8, %4, 0
+ RET_ReallyLR
+
+...
+---
+# CSNEG pair — same direction (GT/GT, diff 1).
+#
+# Input: CMP #9 GT; CMP #10 GT
+# Output: CMP #10 GE; CMP #10 GT
+name: csneg_same_direction
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $w0, $w1, $w2, $x3, $x4
+
+ ; CHECK-LABEL: name: csneg_same_direction
+ ; CHECK: liveins: $w0, $w1, $w2, $x3, $x4
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32common = COPY $w0
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32 = COPY $w1
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr32 = COPY $w2
+ ; CHECK-NEXT: [[COPY3:%[0-9]+]]:gpr64common = COPY $x3
+ ; CHECK-NEXT: [[COPY4:%[0-9]+]]:gpr64common = COPY $x4
+ ; CHECK-NEXT: [[SUBSWri:%[0-9]+]]:gpr32 = SUBSWri [[COPY]], 10, 0, implicit-def $nzcv
+ ; CHECK-NEXT: [[CSNEGWr:%[0-9]+]]:gpr32 = CSNEGWr [[COPY1]], [[COPY2]], 10, implicit $nzcv
+ ; CHECK-NEXT: [[SUBSWri1:%[0-9]+]]:gpr32 = SUBSWri [[COPY]], 10, 0, implicit-def $nzcv
+ ; CHECK-NEXT: [[CSNEGWr1:%[0-9]+]]:gpr32 = CSNEGWr [[COPY1]], [[COPY2]], 12, implicit $nzcv
+ ; CHECK-NEXT: STRWui killed [[CSNEGWr]], [[COPY3]], 0
+ ; CHECK-NEXT: STRWui killed [[CSNEGWr1]], [[COPY4]], 0
+ ; CHECK-NEXT: RET_ReallyLR
+ %0:gpr32common = COPY $w0
+ %1:gpr32 = COPY $w1
+ %2:gpr32 = COPY $w2
+ %3:gpr64common = COPY $x3
+ %4:gpr64common = COPY $x4
+
+ %5:gpr32 = SUBSWri %0, 9, 0, implicit-def $nzcv
+ %6:gpr32 = CSNEGWr %1, %2, 12, implicit $nzcv
+
+ %7:gpr32 = SUBSWri %0, 10, 0, implicit-def $nzcv
+ %8:gpr32 = CSNEGWr %1, %2, 12, implicit $nzcv
+
+ STRWui killed %6, %3, 0
+ STRWui killed %8, %4, 0
+ RET_ReallyLR
+
+...
+---
+# Mixed: CSINC and CSEL in the same block. Both pairs should be optimized
+# independently regardless of instruction type.
+#
+# Input: CMP #9 GT (CSINC); CMP #10 GT (CSEL)
+# Output: CMP #10 GE (CSINC); CMP #10 GT (CSEL)
+name: mixed_csinc_csel
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $w0, $w1, $w2, $x3, $x4
+
+ ; CHECK-LABEL: name: mixed_csinc_csel
+ ; CHECK: liveins: $w0, $w1, $w2, $x3, $x4
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32common = COPY $w0
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32 = COPY $w1
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr32 = COPY $w2
+ ; CHECK-NEXT: [[COPY3:%[0-9]+]]:gpr64common = COPY $x3
+ ; CHECK-NEXT: [[COPY4:%[0-9]+]]:gpr64common = COPY $x4
+ ; CHECK-NEXT: [[SUBSWri:%[0-9]+]]:gpr32 = SUBSWri [[COPY]], 10, 0, implicit-def $nzcv
+ ; CHECK-NEXT: [[CSINCWr:%[0-9]+]]:gpr32 = CSINCWr [[COPY1]], [[COPY2]], 10, implicit $nzcv
+ ; CHECK-NEXT: [[SUBSWri1:%[0-9]+]]:gpr32 = SUBSWri [[COPY]], 10, 0, implicit-def $nzcv
+ ; CHECK-NEXT: [[CSELWr:%[0-9]+]]:gpr32 = CSELWr [[COPY1]], [[COPY2]], 12, implicit $nzcv
+ ; CHECK-NEXT: STRWui killed [[CSINCWr]], [[COPY3]], 0
+ ; CHECK-NEXT: STRWui killed [[CSELWr]], [[COPY4]], 0
+ ; CHECK-NEXT: RET_ReallyLR
+ %0:gpr32common = COPY $w0
+ %1:gpr32 = COPY $w1
+ %2:gpr32 = COPY $w2
+ %3:gpr64common = COPY $x3
+ %4:gpr64common = COPY $x4
+
+ %5:gpr32 = SUBSWri %0, 9, 0, implicit-def $nzcv
+ %6:gpr32 = CSINCWr %1, %2, 12, implicit $nzcv
+
+ %7:gpr32 = SUBSWri %0, 10, 0, implicit-def $nzcv
+ %8:gpr32 = CSELWr %1, %2, 12, implicit $nzcv
+
+ STRWui killed %6, %3, 0
+ STRWui killed %8, %4, 0
+ RET_ReallyLR
+
+...
More information about the llvm-commits
mailing list