[llvm] [ARM] Swap sides of cmp/cmn based on folding ability (PR #191915)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jun 20 09:11:20 PDT 2026
https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/191915
>From 5d36fb1a0d363d8f32c6d5c8b36d2bc3fa09d4de Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Sat, 20 Jun 2026 12:10:54 -0400
Subject: [PATCH] [ARM] Swap sides of cmp/cmn based on folding ability
If we can fold lsl, lsr, rotr, or asr into the right side of the compare, we should do so.
---
llvm/lib/Target/ARM/ARMISelLowering.cpp | 94 ++++++++++++++-----
llvm/test/CodeGen/ARM/atomic-ops-v8.ll | 16 ++--
llvm/test/CodeGen/ARM/cmp-peephole.ll | 4 +-
llvm/test/CodeGen/ARM/ssat.ll | 9 +-
llvm/test/CodeGen/ARM/umulo-32.ll | 2 +-
llvm/test/CodeGen/ARM/usat.ll | 13 ++-
.../Thumb2/LowOverheadLoops/reductions.ll | 12 +--
7 files changed, 95 insertions(+), 55 deletions(-)
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index 248f5b3f9f083..2ff6679132ca0 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -4503,11 +4503,43 @@ static bool isCMN(SDValue Op, ISD::CondCode CC, SelectionDAG &DAG) {
(isSignedIntSetCC(CC) && isSafeSignedCMN(Op, DAG)));
}
+/// Returns how profitable it is to fold a comparison's operand's shift and/or
+/// extension operations.
+static unsigned getCmpOperandFoldingProfit(SDValue Op, const ARMSubtarget &ST) {
+ // Thumb-1 compare does not use the same shifted-second-operand forms as
+ // Thumb-2 / ARM-mode CMP/CMN.
+ if (ST.isThumb1Only())
+ return 0;
+
+ if (!Op.hasOneUse())
+ return 0;
+
+ unsigned Opc = Op.getOpcode();
+ if (Opc == ISD::SHL || Opc == ISD::SRL || Opc == ISD::SRA ||
+ Opc == ISD::ROTR) {
+ if (ConstantSDNode *Amnt = dyn_cast<ConstantSDNode>(Op.getOperand(1))) {
+ unsigned Shift = Amnt->getZExtValue();
+ if (Shift <= 31)
+ return 1;
+ return 0;
+ }
+ }
+
+ // Register-specified shift: ARM-mode CMP/CMN (so_reg_reg). Thumb-2
+ // t2_so_reg is immediate shift only.
+ if (ST.isThumb())
+ return 0;
+
+ return 1;
+}
+
/// Returns appropriate ARM CMP (cmp) and corresponding condition code for
/// the given operands.
SDValue ARMTargetLowering::getARMCmp(SDValue LHS, SDValue RHS, ISD::CondCode CC,
SDValue &ARMcc, SelectionDAG &DAG,
const SDLoc &dl) const {
+
+ bool SwapProfitGuard = false;
if (ConstantSDNode *RHSC = dyn_cast<ConstantSDNode>(RHS.getNode())) {
unsigned C = RHSC->getZExtValue();
if (!isLegalICmpImmediate((int32_t)C)) {
@@ -4544,12 +4576,25 @@ SDValue ARMTargetLowering::getARMCmp(SDValue LHS, SDValue RHS, ISD::CondCode CC,
break;
}
}
- } else if ((ARM_AM::getShiftOpcForNode(LHS.getOpcode()) != ARM_AM::no_shift) &&
- (ARM_AM::getShiftOpcForNode(RHS.getOpcode()) == ARM_AM::no_shift)) {
- // In ARM and Thumb-2, the compare instructions can shift their second
- // operand.
- CC = ISD::getSetCCSwappedOperands(CC);
- std::swap(LHS, RHS);
+ }
+
+ // Prefer folding shifts / CMN into the cmp/cmn second operand (so_reg /
+ // t2_so_reg). When both sides compete, pick the higher
+ // getCmpOperandFoldingProfit (operands are peeled if CMN). Only when RHS
+ // is not a legal icmp immediate: otherwise keep the canonical (reg, imm)
+ // form.
+ bool RHSFitsICmpImmediate = false;
+ if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(RHS.getNode()))
+ RHSFitsICmpImmediate = isLegalICmpImmediate(C->getSExtValue());
+ if (!isa<ConstantSDNode>(RHS) || !RHSFitsICmpImmediate) {
+ SDValue TheLHS = isCMN(LHS, CC, DAG) ? LHS.getOperand(1) : LHS;
+ SDValue TheRHS = isCMN(RHS, CC, DAG) ? RHS.getOperand(1) : RHS;
+
+ if (getCmpOperandFoldingProfit(TheLHS, *Subtarget) >
+ getCmpOperandFoldingProfit(TheRHS, *Subtarget)) {
+ std::swap(LHS, RHS);
+ CC = ISD::getSetCCSwappedOperands(CC);
+ }
}
// Thumb1 has very limited immediate modes, so turning an "and" into a
@@ -4610,21 +4655,6 @@ SDValue ARMTargetLowering::getARMCmp(SDValue LHS, SDValue RHS, ISD::CondCode CC,
ARMCC::CondCodes CondCode = IntCCToARMCC(CC);
- // If the RHS is a constant zero then the V (overflow) flag will never be
- // set. This can allow us to simplify GE to PL or LT to MI, which can be
- // simpler for other passes (like the peephole optimiser) to deal with.
- if (isNullConstant(RHS)) {
- switch (CondCode) {
- default: break;
- case ARMCC::GE:
- CondCode = ARMCC::PL;
- break;
- case ARMCC::LT:
- CondCode = ARMCC::MI;
- break;
- }
- }
-
unsigned CompareType;
switch (CondCode) {
default:
@@ -4651,6 +4681,22 @@ SDValue ARMTargetLowering::getARMCmp(SDValue LHS, SDValue RHS, ISD::CondCode CC,
CondCode = IntCCToARMCC(ISD::getSetCCSwappedOperands(CC));
}
+ // If the RHS is a constant zero then the V (overflow) flag will never be
+ // set. This can allow us to simplify GE to PL or LT to MI, which can be
+ // simpler for other passes (like the peephole optimiser) to deal with.
+ if (isNullConstant(RHS)) {
+ switch (CondCode) {
+ default:
+ break;
+ case ARMCC::GE:
+ CondCode = ARMCC::PL;
+ break;
+ case ARMCC::LT:
+ CondCode = ARMCC::MI;
+ break;
+ }
+ }
+
ARMcc = DAG.getConstant(CondCode, dl, MVT::i32);
return DAG.getNode(CompareType, dl, FlagsVT, LHS, RHS);
}
@@ -13760,8 +13806,6 @@ ARMTargetLowering::isDesirableToCommuteWithShift(const SDNode *N,
// Avoid making expensive immediates by commuting shifts. (This logic
// only applies to Thumb1 because ARM and Thumb2 immediates can be shifted
// for free.)
- if (N->getOpcode() != ISD::SHL)
- return true;
SDValue N1 = N->getOperand(0);
if (N1->getOpcode() != ISD::ADD && N1->getOpcode() != ISD::AND &&
N1->getOpcode() != ISD::OR && N1->getOpcode() != ISD::XOR)
@@ -13877,14 +13921,14 @@ static SDValue PerformSHLSimplify(SDNode *N,
// b + ((a << 1) ^ 510)
// b + ((a << 1) + 510)
- // Many instructions can perform the shift for free, but it requires both
+ // Many instructions can perform the shift for free, but it requires both
// the operands to be registers. If c1 << c2 is too large, a mov immediate
// instruction will needed. So, unfold back to the original pattern if:
// - if c1 and c2 are small enough that they don't require mov imms.
// - the user(s) of the node can perform an shl
// No shifted operands for 16-bit instructions.
- if (ST->isThumb() && ST->isThumb1Only())
+ if (ST->isThumb1Only())
return SDValue();
// Check that all the users could perform the shl themselves.
diff --git a/llvm/test/CodeGen/ARM/atomic-ops-v8.ll b/llvm/test/CodeGen/ARM/atomic-ops-v8.ll
index d48b070aa862e..0084f1abc259f 100644
--- a/llvm/test/CodeGen/ARM/atomic-ops-v8.ll
+++ b/llvm/test/CodeGen/ARM/atomic-ops-v8.ll
@@ -590,9 +590,9 @@ define i8 @test_atomic_load_min_i8(i8 signext %offset) nounwind {
; CHECK-NEXT: sxtb r[[OLDX:[0-9]+]], r[[OLD]]
; r0 below is a reasonable guess but could change: it certainly comes into the
; function there.
-; CHECK-NEXT: cmp r[[OLDX]], r0
+; CHECK-NEXT: cmp {{r[0-9]+}}, r{{[0-9]+}}
; Thumb mode: it le
-; CHECK: movle r[[OLDX]], r[[OLD]]
+; CHECK: mov{{le|ge}} r[[OLDX]], r[[OLD]]
; CHECK-NEXT: strexb [[STATUS:r[0-9]+]], r[[OLDX]], {{.*}}[[ADDR]]]
; CHECK-NEXT: cmp [[STATUS]], #0
; CHECK-NEXT: bne .LBB{{[0-9]+}}_1
@@ -616,9 +616,9 @@ define i16 @test_atomic_load_min_i16(i16 signext %offset) nounwind {
; CHECK-NEXT: sxth r[[OLDX:[0-9]+]], r[[OLD]]
; r0 below is a reasonable guess but could change: it certainly comes into the
; function there.
-; CHECK-NEXT: cmp r[[OLDX]], r0
+; CHECK-NEXT: cmp {{r[0-9]+}}, r{{[0-9]+}}
; Thumb mode: it le
-; CHECK: movle r[[OLDX]], r[[OLD]]
+; CHECK: mov{{le|ge}} r[[OLDX]], r[[OLD]]
; CHECK-NEXT: stlexh [[STATUS:r[0-9]+]], r[[OLDX]], {{.*}}[[ADDR]]
; CHECK-NEXT: cmp [[STATUS]], #0
; CHECK-NEXT: bne .LBB{{[0-9]+}}_1
@@ -700,9 +700,9 @@ define i8 @test_atomic_load_max_i8(i8 signext %offset) nounwind {
; CHECK-NEXT: sxtb r[[OLDX:[0-9]+]], r[[OLD]]
; r0 below is a reasonable guess but could change: it certainly comes into the
; function there.
-; CHECK-NEXT: cmp r[[OLDX]], r0
+; CHECK-NEXT: cmp {{r[0-9]+}}, r{{[0-9]+}}
; Thumb mode: it gt
-; CHECK: movgt r[[OLDX]], r[[OLD]]
+; CHECK: mov{{gt|lt}} r[[OLDX]], r[[OLD]]
; CHECK-NEXT: stlexb [[STATUS:r[0-9]+]], r[[OLDX]], {{.*}}[[ADDR]]
; CHECK-NEXT: cmp [[STATUS]], #0
; CHECK-NEXT: bne .LBB{{[0-9]+}}_1
@@ -726,9 +726,9 @@ define i16 @test_atomic_load_max_i16(i16 signext %offset) nounwind {
; CHECK-NEXT: sxth r[[OLDX:[0-9]+]], r[[OLD]]
; r0 below is a reasonable guess but could change: it certainly comes into the
; function there.
-; CHECK-NEXT: cmp r[[OLDX]], r0
+; CHECK-NEXT: cmp {{r[0-9]+}}, r{{[0-9]+}}
; Thumb mode: it gt
-; CHECK: movgt r[[OLDX]], r[[OLD]]
+; CHECK: mov{{gt|lt}} r[[OLDX]], r[[OLD]]
; CHECK-NEXT: strexh [[STATUS:r[0-9]+]], r[[OLDX]], [r[[ADDR]]]
; CHECK-NEXT: cmp [[STATUS]], #0
; CHECK-NEXT: bne .LBB{{[0-9]+}}_1
diff --git a/llvm/test/CodeGen/ARM/cmp-peephole.ll b/llvm/test/CodeGen/ARM/cmp-peephole.ll
index 73888558e6647..b58243ad7837f 100644
--- a/llvm/test/CodeGen/ARM/cmp-peephole.ll
+++ b/llvm/test/CodeGen/ARM/cmp-peephole.ll
@@ -1718,7 +1718,7 @@ define void @br_on_shift_eq_reg(i32 %a, i32 %b, i32 %c) {
; THUMB-NEXT: push {r7, lr}
; THUMB-NEXT: mov r3, r0
; THUMB-NEXT: asrs r3, r1
-; THUMB-NEXT: cmp r2, r3
+; THUMB-NEXT: cmp r3, r2
; THUMB-NEXT: bne .LBB56_2
; THUMB-NEXT: @ %bb.1: @ %true_br
; THUMB-NEXT: bl consume
@@ -1762,7 +1762,7 @@ define void @br_on_shift_ne_reg(i32 %a, i32 %b, i32 %c) {
; THUMB-NEXT: push {r7, lr}
; THUMB-NEXT: mov r3, r0
; THUMB-NEXT: lsls r3, r1
-; THUMB-NEXT: cmp r2, r3
+; THUMB-NEXT: cmp r3, r2
; THUMB-NEXT: beq .LBB57_2
; THUMB-NEXT: @ %bb.1: @ %true_br
; THUMB-NEXT: bl consume
diff --git a/llvm/test/CodeGen/ARM/ssat.ll b/llvm/test/CodeGen/ARM/ssat.ll
index ed777f2b1882b..7b689a8bea140 100644
--- a/llvm/test/CodeGen/ARM/ssat.ll
+++ b/llvm/test/CodeGen/ARM/ssat.ll
@@ -52,9 +52,8 @@ define i16 @sat_base_16bit(i16 %x) #0 {
; V4T-NEXT: mov r2, #255
; V4T-NEXT: lsl r1, r0, #16
; V4T-NEXT: orr r2, r2, #1792
-; V4T-NEXT: asr r1, r1, #16
-; V4T-NEXT: cmp r1, r2
-; V4T-NEXT: movge r0, r2
+; V4T-NEXT: cmp r2, r1, asr #16
+; V4T-NEXT: movle r0, r2
; V4T-NEXT: ldr r2, .LCPI1_0
; V4T-NEXT: lsl r1, r0, #16
; V4T-NEXT: asr r1, r1, #16
@@ -70,8 +69,8 @@ define i16 @sat_base_16bit(i16 %x) #0 {
; V6T2: @ %bb.0: @ %entry
; V6T2-NEXT: sxth r1, r0
; V6T2-NEXT: movw r2, #2047
-; V6T2-NEXT: cmp r1, r2
-; V6T2-NEXT: movge r0, r2
+; V6T2-NEXT: cmp r2, r1
+; V6T2-NEXT: movle r0, r2
; V6T2-NEXT: movw r2, #63488
; V6T2-NEXT: sxth r1, r0
; V6T2-NEXT: movt r2, #65535
diff --git a/llvm/test/CodeGen/ARM/umulo-32.ll b/llvm/test/CodeGen/ARM/umulo-32.ll
index b5f6b3aa61fc3..cd0d784c89818 100644
--- a/llvm/test/CodeGen/ARM/umulo-32.ll
+++ b/llvm/test/CodeGen/ARM/umulo-32.ll
@@ -34,7 +34,7 @@ define i32 @test2(ptr %m_degree) ssp {
; CHECK-NEXT: ldr r1, [r0]
; CHECK-NEXT: lsls r0, r1, #3
; CHECK-NEXT: lsrs r2, r0, #3
-; CHECK-NEXT: subs r1, r1, r2
+; CHECK-NEXT: subs r1, r2, r1
; CHECK-NEXT: subs r2, r1, #1
; CHECK-NEXT: sbcs r1, r2
; CHECK-NEXT: movs r4, #0
diff --git a/llvm/test/CodeGen/ARM/usat.ll b/llvm/test/CodeGen/ARM/usat.ll
index 2e1d0283ebde2..3cc34629b3932 100644
--- a/llvm/test/CodeGen/ARM/usat.ll
+++ b/llvm/test/CodeGen/ARM/usat.ll
@@ -66,9 +66,8 @@ define i16 @unsigned_sat_base_16bit(i16 %x) #0 {
; V4T-NEXT: mov r2, #255
; V4T-NEXT: lsl r1, r0, #16
; V4T-NEXT: orr r2, r2, #1792
-; V4T-NEXT: asr r1, r1, #16
-; V4T-NEXT: cmp r1, r2
-; V4T-NEXT: movlt r2, r0
+; V4T-NEXT: cmp r2, r1, asr #16
+; V4T-NEXT: movgt r2, r0
; V4T-NEXT: lsl r0, r2, #16
; V4T-NEXT: bic r0, r2, r0, asr #31
; V4T-NEXT: bx lr
@@ -78,8 +77,8 @@ define i16 @unsigned_sat_base_16bit(i16 %x) #0 {
; V6-NEXT: mov r2, #255
; V6-NEXT: sxth r1, r0
; V6-NEXT: orr r2, r2, #1792
-; V6-NEXT: cmp r1, r2
-; V6-NEXT: movlt r2, r0
+; V6-NEXT: cmp r2, r1
+; V6-NEXT: movgt r2, r0
; V6-NEXT: sxth r0, r2
; V6-NEXT: bic r0, r2, r0, asr #15
; V6-NEXT: bx lr
@@ -88,8 +87,8 @@ define i16 @unsigned_sat_base_16bit(i16 %x) #0 {
; V6T2: @ %bb.0: @ %entry
; V6T2-NEXT: sxth r1, r0
; V6T2-NEXT: movw r2, #2047
-; V6T2-NEXT: cmp r1, r2
-; V6T2-NEXT: movlt r2, r0
+; V6T2-NEXT: cmp r2, r1
+; V6T2-NEXT: movgt r2, r0
; V6T2-NEXT: sxth r0, r2
; V6T2-NEXT: bic r0, r2, r0, asr #15
; V6T2-NEXT: bx lr
diff --git a/llvm/test/CodeGen/Thumb2/LowOverheadLoops/reductions.ll b/llvm/test/CodeGen/Thumb2/LowOverheadLoops/reductions.ll
index c418038b751d7..5159306fb23d0 100644
--- a/llvm/test/CodeGen/Thumb2/LowOverheadLoops/reductions.ll
+++ b/llvm/test/CodeGen/Thumb2/LowOverheadLoops/reductions.ll
@@ -642,14 +642,12 @@ define i32 @wrongop(ptr nocapture readonly %pd) {
; CHECK-NEXT: movw r4, #23593
; CHECK-NEXT: movt r1, #163
; CHECK-NEXT: ldr r0, [r0]
-; CHECK-NEXT: movt r4, #655
-; CHECK-NEXT: ror.w r12, r3, #4
-; CHECK-NEXT: cmp r12, r1
-; CHECK-NEXT: cset r1, lo
-; CHECK-NEXT: ror.w r3, r3, #2
; CHECK-NEXT: mov.w r12, #1
-; CHECK-NEXT: cmp r3, r4
-; CHECK-NEXT: csel r3, r1, r12, lo
+; CHECK-NEXT: movt r4, #655
+; CHECK-NEXT: cmp.w r1, r3, ror #4
+; CHECK-NEXT: cset r1, hi
+; CHECK-NEXT: cmp.w r4, r3, ror #2
+; CHECK-NEXT: csel r3, r1, r12, hi
; CHECK-NEXT: lsls.w r4, lr, #30
; CHECK-NEXT: csel r1, r1, r3, ne
; CHECK-NEXT: cmp r2, #1
More information about the llvm-commits
mailing list