[llvm] [ARM] Swap sides of cmp/cmn based on folding ability (PR #191915)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 13 20:01:52 PDT 2026
https://github.com/SiliconA-Z updated https://github.com/llvm/llvm-project/pull/191915
>From 417d5cd1187f4cee83ed27a73d01a888b903055b Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Mon, 13 Apr 2026 19:36:05 -0400
Subject: [PATCH] [ARM] Swap sides of cmp/cmn based on folding ability
To do this, I also had to remove from TableGen matches and have getArmCmp fold them instead.
---
llvm/lib/Target/ARM/ARMISelLowering.cpp | 73 ++++++++++++++++---
llvm/lib/Target/ARM/ARMInstrInfo.td | 4 -
llvm/lib/Target/ARM/ARMInstrThumb2.td | 6 --
llvm/test/CodeGen/ARM/atomic-ops-v8.ll | 16 ++--
llvm/test/CodeGen/ARM/cmp-peephole.ll | 4 +-
llvm/test/CodeGen/ARM/cmp-to-cmn.ll | 6 +-
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 ++-
10 files changed, 92 insertions(+), 53 deletions(-)
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index 0ad6c07f36868..dc5cb42ba29e9 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -4482,11 +4482,45 @@ static bool isCMN(SDValue Op, ISD::CondCode CC, SelectionDAG &DAG) {
(isSignedIntSetCC(CC) && isSafeSignedCMN(Op, DAG)));
}
+/// Returns a relative score for putting \p Op on the cmp/cmn **second** operand
+/// (ARM \p so_reg_imm / Thumb-2 \p t2_so_reg). Unlike AArch64, ARM does **not**
+/// fold zero/sign extends (uxtb/uxth style) into that operand—TableGen only
+/// matches \p shl, \p srl, \p sra, \p rotr for those ComplexPatterns.
+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))) {
+ uint32_t 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 (ARMInstrThumb2.td).
+ if (!ST.isThumb())
+ return 1;
+
+ return 0;
+}
+
/// 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)) {
@@ -4523,12 +4557,37 @@ 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)) {
+ } else if (!Subtarget->isThumb1Only() &&
+ (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);
+ SwapProfitGuard = true;
+ }
+
+ // 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.
+ if (!SwapProfitGuard) {
+ 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
@@ -4616,18 +4675,12 @@ SDValue ARMTargetLowering::getARMCmp(SDValue LHS, SDValue RHS, ISD::CondCode CC,
break;
}
- // TODO: Remove CMPZ check once we generalize and remove the CMPZ enum from
- // the codebase.
-
- // TODO: When we have a solution to the vselect predicate not allowing pl/mi
- // all the time, allow those cases to be cmn too no matter what.
- if (CompareType != ARMISD::CMPZ && isCMN(RHS, CC, DAG)) {
+ if (isCMN(RHS, CC, DAG)) {
CompareType = ARMISD::CMN;
RHS = RHS.getOperand(1);
- } else if (CompareType != ARMISD::CMPZ && isCMN(LHS, CC, DAG)) {
+ } else if (CompareType == ARMISD::CMPZ && isCMN(LHS, CC, DAG)) {
CompareType = ARMISD::CMN;
LHS = LHS.getOperand(1);
- CondCode = IntCCToARMCC(ISD::getSetCCSwappedOperands(CC));
}
ARMcc = DAG.getConstant(CondCode, dl, MVT::i32);
diff --git a/llvm/lib/Target/ARM/ARMInstrInfo.td b/llvm/lib/Target/ARM/ARMInstrInfo.td
index c74c84a2602b1..b003fea50795d 100644
--- a/llvm/lib/Target/ARM/ARMInstrInfo.td
+++ b/llvm/lib/Target/ARM/ARMInstrInfo.td
@@ -5140,12 +5140,8 @@ def : ARMPat<(ARMcmp GPR:$src, mod_imm_neg:$imm),
(CMNri GPR:$src, mod_imm_neg:$imm)>;
def : ARMPat<(ARMcmpZ GPR:$src, mod_imm_neg:$imm),
(CMNri GPR:$src, mod_imm_neg:$imm)>;
-def : ARMPat<(ARMcmpZ GPR:$src, (ineg GPR:$rhs)),
- (CMNrr GPR:$src, GPR:$rhs)>;
def : ARMPat<(ARMcmpZ GPR:$src, (ineg so_reg_imm:$rhs)),
(CMNrsi GPR:$src, so_reg_imm:$rhs)>;
-def : ARMPat<(ARMcmpZ GPRnopc:$src, (ineg so_reg_reg:$rhs)),
- (CMNrsr GPRnopc:$src, so_reg_reg:$rhs)>;
// Note that TST/TEQ don't set all the same flags that CMP does!
defm TST : AI1_cmp_irs<0b1000, "tst",
diff --git a/llvm/lib/Target/ARM/ARMInstrThumb2.td b/llvm/lib/Target/ARM/ARMInstrThumb2.td
index 4f0cea8b00aac..35aa5babbe7a3 100644
--- a/llvm/lib/Target/ARM/ARMInstrThumb2.td
+++ b/llvm/lib/Target/ARM/ARMInstrThumb2.td
@@ -3551,12 +3551,6 @@ def : T2Pat<(ARMcmp GPR:$src, t2_so_imm_neg:$imm),
def : T2Pat<(ARMcmpZ GPRnopc:$src, t2_so_imm_neg:$imm),
(t2CMNri GPRnopc:$src, t2_so_imm_neg:$imm)>;
-// Fold compare-to-zero of a negated register into CMN register forms.
-def : T2Pat<(ARMcmpZ GPRnopc:$Rn, (ineg rGPR:$Rm)),
- (t2CMNrr GPRnopc:$Rn, rGPR:$Rm)>;
-def : T2Pat<(ARMcmpZ GPRnopc:$Rn, (ineg t2_so_reg:$ShiftedRm)),
- (t2CMNrs GPRnopc:$Rn, t2_so_reg:$ShiftedRm)>;
-
defm t2TST : T2I_cmp_irs<0b0000, "tst", rGPR,
IIC_iTSTi, IIC_iTSTr, IIC_iTSTsi,
BinOpFrag<(ARMcmpZ (and_su node:$LHS, node:$RHS), 0)>>;
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/cmp-to-cmn.ll b/llvm/test/CodeGen/ARM/cmp-to-cmn.ll
index 60ab6473dce12..82e4ea9105838 100644
--- a/llvm/test/CodeGen/ARM/cmp-to-cmn.ll
+++ b/llvm/test/CodeGen/ARM/cmp-to-cmn.ll
@@ -442,14 +442,14 @@ define i1 @cmn_swap(i32 %a, i32 %b) {
; CHECK-ARM-LABEL: cmn_swap:
; CHECK-ARM: @ %bb.0:
; CHECK-ARM-NEXT: mov r2, #0
-; CHECK-ARM-NEXT: cmn r1, r0
+; CHECK-ARM-NEXT: cmn r0, r1
; CHECK-ARM-NEXT: movwlt r2, #1
; CHECK-ARM-NEXT: mov r0, r2
; CHECK-ARM-NEXT: bx lr
;
; CHECK-T1-LABEL: cmn_swap:
; CHECK-T1: @ %bb.0:
-; CHECK-T1-NEXT: cmn r1, r0
+; CHECK-T1-NEXT: cmn r0, r1
; CHECK-T1-NEXT: blt .LBB11_2
; CHECK-T1-NEXT: @ %bb.1:
; CHECK-T1-NEXT: movs r0, #0
@@ -461,7 +461,7 @@ define i1 @cmn_swap(i32 %a, i32 %b) {
; CHECK-T2-LABEL: cmn_swap:
; CHECK-T2: @ %bb.0:
; CHECK-T2-NEXT: movs r2, #0
-; CHECK-T2-NEXT: cmn r1, r0
+; CHECK-T2-NEXT: cmn r0, r1
; CHECK-T2-NEXT: it lt
; CHECK-T2-NEXT: movlt r2, #1
; CHECK-T2-NEXT: mov r0, r2
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