[llvm] [AArch64] InsertSelect: Fold both diamond arms into chained CSEL family instructions (PR #212033)
Mugundan S via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 2 04:10:58 PDT 2026
https://github.com/MGN-GIT updated https://github.com/llvm/llvm-project/pull/212033
>From decaaa41fa02258e2e2abc71839b6491777b4d79 Mon Sep 17 00:00:00 2001
From: Greenie0701 <smugundan12a at gmail.com>
Date: Sat, 25 Jul 2026 21:38:02 +0530
Subject: [PATCH] [AArch64] InsertSelect: fold both diamond arms into chained
CSEL family instructions
---
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 48 +++--
.../early-ifcvt-insert-select-fold.mir | 202 ++++++++++++++++++
2 files changed, 234 insertions(+), 16 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/early-ifcvt-insert-select-fold.mir
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
index b40188e97a71e..6e9c0ebde922b 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
@@ -1039,7 +1039,6 @@ bool AArch64InstrInfo::canInsertSelect(const MachineBasicBlock &MBB,
unsigned ExtraCondLat = Cond.size() != 1;
// GPRs are handled by csel.
- // FIXME: Fold in x+1, -x, and ~x when applicable.
if (AArch64::GPR64allRegClass.hasSubClassEq(RC) ||
AArch64::GPR32allRegClass.hasSubClassEq(RC)) {
// Single-cycle csel, csinc, csinv, and csneg.
@@ -1047,7 +1046,7 @@ bool AArch64InstrInfo::canInsertSelect(const MachineBasicBlock &MBB,
TrueCycles = FalseCycles = 1;
if (canFoldIntoCSel(MRI, TrueReg))
TrueCycles = 0;
- else if (canFoldIntoCSel(MRI, FalseReg))
+ if (canFoldIntoCSel(MRI, FalseReg))
FalseCycles = 0;
return true;
}
@@ -1291,22 +1290,39 @@ void AArch64InstrInfo::insertSelect(MachineBasicBlock &MBB,
// Try folding simple instructions into the csel.
if (TryFold) {
- unsigned NewReg = 0;
- unsigned FoldedOpc = canFoldIntoCSel(MRI, TrueReg, &NewReg);
- if (FoldedOpc) {
- // The folded opcodes csinc, csinc and csneg apply the operation to
- // FalseReg, so we need to invert the condition.
+ unsigned NewTrueReg = 0, NewFalseReg = 0;
+ unsigned TrueFoldOpc = canFoldIntoCSel(MRI, TrueReg, &NewTrueReg);
+ unsigned FalseFoldOpc = canFoldIntoCSel(MRI, FalseReg, &NewFalseReg);
+
+ if (TrueFoldOpc && FalseFoldOpc) {
+ // Both sides are foldable --> Emit two chained CSEL-family instructions:
+ // TmpReg = TrueFoldOpc(FalseReg, NewTrueReg, InvCC) --> folds TrueReg
+ // DstReg = FalseFoldOpc(TmpReg, NewFalseReg, CC) --> folds FalseReg
+ Register TmpReg = MRI.createVirtualRegister(RC);
+ AArch64CC::CondCode InvCC = AArch64CC::getInvertedCondCode(CC);
+ BuildMI(MBB, I, DL, get(TrueFoldOpc), TmpReg)
+ .addReg(FalseReg)
+ .addReg(NewTrueReg)
+ .addImm(InvCC);
+ MRI.clearKillFlags(NewTrueReg);
+ // Set up the second instruction to fold FalseReg.
+ TrueReg = TmpReg;
+ FalseReg = NewFalseReg;
+ Opc = FalseFoldOpc;
+ MRI.clearKillFlags(NewFalseReg);
+ } else if (TrueFoldOpc) {
+ // Only TrueReg is foldable. The fold operation applies to Rm (slot 2),
+ // so invert the condition and swap Rn/Rm to place it correctly.
CC = AArch64CC::getInvertedCondCode(CC);
TrueReg = FalseReg;
- } else
- FoldedOpc = canFoldIntoCSel(MRI, FalseReg, &NewReg);
-
- // Fold the operation. Leave any dead instructions for DCE to clean up.
- if (FoldedOpc) {
- FalseReg = NewReg;
- Opc = FoldedOpc;
- // Extend the live range of NewReg.
- MRI.clearKillFlags(NewReg);
+ FalseReg = NewTrueReg;
+ Opc = TrueFoldOpc;
+ MRI.clearKillFlags(NewTrueReg);
+ } else if (FalseFoldOpc) {
+ // Only FalseReg is foldable. Direct fold into Rm, no swap needed.
+ FalseReg = NewFalseReg;
+ Opc = FalseFoldOpc;
+ MRI.clearKillFlags(NewFalseReg);
}
}
diff --git a/llvm/test/CodeGen/AArch64/early-ifcvt-insert-select-fold.mir b/llvm/test/CodeGen/AArch64/early-ifcvt-insert-select-fold.mir
new file mode 100644
index 0000000000000..9f7f4e6289fe6
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/early-ifcvt-insert-select-fold.mir
@@ -0,0 +1,202 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=aarch64-linux-gnu -run-pass=early-ifcvt -verify-machineinstrs %s -o - | FileCheck %s
+
+# Test insertSelect folding in EarlyIfConversion for the both-foldable case
+# (x+1 -> CSINC, -x -> CSNEG, ~x -> CSINV) and single-side-only cases.
+#
+# Positive tests: both sides foldable -> two chained CSELs, no PHI.
+# Negative tests: only one side foldable -> single CSEL-family, no PHI.
+
+--- |
+ define i32 @test_inc_neg(i32 %cond, i32 %a, i32 %b) {
+ entry:
+ br i1 undef, label %btrue, label %bfalse
+ btrue:
+ br label %tail
+ bfalse:
+ br label %tail
+ tail:
+ ret i32 undef
+ }
+ define i32 @test_inc_inv(i32 %cond, i32 %a, i32 %b) {
+ entry:
+ br i1 undef, label %btrue, label %bfalse
+ btrue:
+ br label %tail
+ bfalse:
+ br label %tail
+ tail:
+ ret i32 undef
+ }
+ define i32 @test_only_true_inc(i32 %cond, i32 %a, i32 %b) {
+ entry:
+ br i1 undef, label %btrue, label %bfalse
+ btrue:
+ br label %tail
+ bfalse:
+ br label %tail
+ tail:
+ ret i32 undef
+ }
+ define i32 @test_only_false_neg(i32 %cond, i32 %a, i32 %b) {
+ entry:
+ br i1 undef, label %btrue, label %bfalse
+ btrue:
+ br label %tail
+ bfalse:
+ br label %tail
+ tail:
+ ret i32 undef
+ }
+...
+
+---
+# Positive: both sides foldable.
+# true = a+1 (CSINC), false = -b (CSNEG).
+# Expected: CSINCWr + CSNEGWr chained, no PHI, no plain CSEL.
+name: test_inc_neg
+tracksRegLiveness: true
+body: |
+ ; CHECK-LABEL: name: test_inc_neg
+ ; CHECK: bb.0.entry:
+ ; CHECK-NOT: PHI
+ ; CHECK-NOT: CSELWr
+ ; CHECK: CSINCWr
+ ; CHECK: CSNEGWr
+ bb.0.entry:
+ successors: %bb.1(0x50000000), %bb.2(0x30000000)
+ liveins: $w0, $w1, $w2
+ %0:gpr32 = COPY $w0
+ %1:gpr32common = COPY $w1
+ %2:gpr32 = COPY $w2
+ CBNZW %0, %bb.1
+ B %bb.2
+
+ bb.1.btrue:
+ successors: %bb.3(0x80000000)
+ %3:gpr32sp = nsw ADDWri %1, 1, 0
+ %6:gpr32 = COPY %3
+ B %bb.3
+
+ bb.2.bfalse:
+ successors: %bb.3(0x80000000)
+ %4:gpr32 = SUBWrr $wzr, %2
+ B %bb.3
+
+ bb.3.tail:
+ %5:gpr32 = PHI %6, %bb.1, %4, %bb.2
+ $w0 = COPY %5
+ RET_ReallyLR implicit $w0
+
+---
+# Positive: both sides foldable.
+# true = a+1 (CSINC), false = ~b (CSINV).
+# Expected: CSINCWr + CSINVWr chained, no PHI, no plain CSEL.
+name: test_inc_inv
+tracksRegLiveness: true
+body: |
+ ; CHECK-LABEL: name: test_inc_inv
+ ; CHECK: bb.0.entry:
+ ; CHECK-NOT: PHI
+ ; CHECK-NOT: CSELWr
+ ; CHECK: CSINCWr
+ ; CHECK: CSINVWr
+ bb.0.entry:
+ successors: %bb.1(0x50000000), %bb.2(0x30000000)
+ liveins: $w0, $w1, $w2
+ %0:gpr32 = COPY $w0
+ %1:gpr32common = COPY $w1
+ %2:gpr32 = COPY $w2
+ CBNZW %0, %bb.1
+ B %bb.2
+
+ bb.1.btrue:
+ successors: %bb.3(0x80000000)
+ %3:gpr32sp = nsw ADDWri %1, 1, 0
+ %6:gpr32 = COPY %3
+ B %bb.3
+
+ bb.2.bfalse:
+ successors: %bb.3(0x80000000)
+ %4:gpr32 = ORNWrr $wzr, %2
+ B %bb.3
+
+ bb.3.tail:
+ %5:gpr32 = PHI %6, %bb.1, %4, %bb.2
+ $w0 = COPY %5
+ RET_ReallyLR implicit $w0
+
+---
+# Negative: only TrueReg foldable.
+# true = a+1 (CSINC), false = plain reg (not foldable).
+# Expected: single CSINCWr with inverted CC, no PHI, no CSNEGWr/CSINVWr.
+name: test_only_true_inc
+tracksRegLiveness: true
+body: |
+ ; CHECK-LABEL: name: test_only_true_inc
+ ; CHECK: bb.0.entry:
+ ; CHECK-NOT: PHI
+ ; CHECK-NOT: CSNEGWr
+ ; CHECK-NOT: CSINVWr
+ ; CHECK: CSINCWr
+ bb.0.entry:
+ successors: %bb.1(0x50000000), %bb.2(0x30000000)
+ liveins: $w0, $w1, $w2
+ %0:gpr32 = COPY $w0
+ %1:gpr32common = COPY $w1
+ %2:gpr32 = COPY $w2
+ CBNZW %0, %bb.1
+ B %bb.2
+
+ bb.1.btrue:
+ successors: %bb.3(0x80000000)
+ %3:gpr32sp = nsw ADDWri %1, 1, 0
+ %6:gpr32 = COPY %3
+ B %bb.3
+
+ bb.2.bfalse:
+ successors: %bb.3(0x80000000)
+ %4:gpr32 = COPY %2
+ B %bb.3
+
+ bb.3.tail:
+ %5:gpr32 = PHI %6, %bb.1, %4, %bb.2
+ $w0 = COPY %5
+ RET_ReallyLR implicit $w0
+
+---
+# Negative: only FalseReg foldable.
+# true = plain reg (not foldable), false = -b (CSNEG).
+# Expected: single CSNEGWr, no PHI, no CSINCWr/CSINVWr.
+name: test_only_false_neg
+tracksRegLiveness: true
+body: |
+ ; CHECK-LABEL: name: test_only_false_neg
+ ; CHECK: bb.0.entry:
+ ; CHECK-NOT: PHI
+ ; CHECK-NOT: CSINCWr
+ ; CHECK-NOT: CSINVWr
+ ; CHECK: CSNEGWr
+ bb.0.entry:
+ successors: %bb.1(0x50000000), %bb.2(0x30000000)
+ liveins: $w0, $w1, $w2
+ %0:gpr32 = COPY $w0
+ %1:gpr32 = COPY $w1
+ %2:gpr32 = COPY $w2
+ CBNZW %0, %bb.1
+ B %bb.2
+
+ bb.1.btrue:
+ successors: %bb.3(0x80000000)
+ %6:gpr32 = COPY %1
+ B %bb.3
+
+ bb.2.bfalse:
+ successors: %bb.3(0x80000000)
+ %4:gpr32 = SUBWrr $wzr, %2
+ B %bb.3
+
+ bb.3.tail:
+ %5:gpr32 = PHI %6, %bb.1, %4, %bb.2
+ $w0 = COPY %5
+ RET_ReallyLR implicit $w0
More information about the llvm-commits
mailing list