[llvm] [TargetLowering] Optimize scmp with 0 using (x ashr 31) | (-x shr 31) (PR #205398)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jun 24 07:36:44 PDT 2026
https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/205398
>From d20af5f730739250b3c16f80d2efa11dee6a2844 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Tue, 23 Jun 2026 13:23:00 -0400
Subject: [PATCH 1/2] Pre-commit tests (NFC)
---
llvm/test/CodeGen/ARM/scmp.ll | 36 ++++++++++
llvm/test/CodeGen/PowerPC/scmp.ll | 40 +++++++++++
llvm/test/CodeGen/Thumb/scmp.ll | 116 ++++++++++++++++++++++++++++++
3 files changed, 192 insertions(+)
diff --git a/llvm/test/CodeGen/ARM/scmp.ll b/llvm/test/CodeGen/ARM/scmp.ll
index 9189aee6aaf43..f7f0cd6d396c3 100644
--- a/llvm/test/CodeGen/ARM/scmp.ll
+++ b/llvm/test/CodeGen/ARM/scmp.ll
@@ -141,3 +141,39 @@ define i64 @scmp_64_64(i64 %x, i64 %y) nounwind {
%1 = call i64 @llvm.scmp(i64 %x, i64 %y)
ret i64 %1
}
+
+define i8 @scmp_8_8_zero(i8 signext %x) nounwind {
+; CHECK-LABEL: scmp_8_8_zero:
+; CHECK: @ %bb.0:
+; CHECK-NEXT: subs r0, r0, #0
+; CHECK-NEXT: movwgt r0, #1
+; CHECK-NEXT: mvnlt r0, #0
+; CHECK-NEXT: bx lr
+ %1 = call i8 @llvm.scmp(i8 %x, i8 0)
+ ret i8 %1
+}
+
+define i32 @scmp_32_32_zero(i32 %x) nounwind {
+; CHECK-LABEL: scmp_32_32_zero:
+; CHECK: @ %bb.0:
+; CHECK-NEXT: subs r0, r0, #0
+; CHECK-NEXT: movwgt r0, #1
+; CHECK-NEXT: mvnlt r0, #0
+; CHECK-NEXT: bx lr
+ %1 = call i32 @llvm.scmp(i32 %x, i32 0)
+ ret i32 %1
+}
+
+define i64 @scmp_64_64_zero(i64 %x) nounwind {
+; CHECK-LABEL: scmp_64_64_zero:
+; CHECK: @ %bb.0:
+; CHECK-NEXT: rsbs r0, r0, #0
+; CHECK-NEXT: mov r2, #0
+; CHECK-NEXT: rscs r0, r1, #0
+; CHECK-NEXT: movwlt r2, #1
+; CHECK-NEXT: sub r0, r2, r1, lsr #31
+; CHECK-NEXT: asr r1, r0, #31
+; CHECK-NEXT: bx lr
+ %1 = call i64 @llvm.scmp(i64 %x, i64 0)
+ ret i64 %1
+}
diff --git a/llvm/test/CodeGen/PowerPC/scmp.ll b/llvm/test/CodeGen/PowerPC/scmp.ll
index 107137c0bea7c..7fc1faa5bde4b 100644
--- a/llvm/test/CodeGen/PowerPC/scmp.ll
+++ b/llvm/test/CodeGen/PowerPC/scmp.ll
@@ -125,3 +125,43 @@ define i64 @scmp_64_64(i64 %x, i64 %y) nounwind {
%1 = call i64 @llvm.scmp(i64 %x, i64 %y)
ret i64 %1
}
+
+define i8 @scmp_8_8_zero(i8 signext %x) nounwind {
+; CHECK-LABEL: scmp_8_8_zero:
+; CHECK: # %bb.0:
+; CHECK-NEXT: cmpwi 3, 0
+; CHECK-NEXT: neg 4, 3
+; CHECK-NEXT: li 3, -1
+; CHECK-NEXT: rldicl 4, 4, 1, 63
+; CHECK-NEXT: isellt 3, 3, 4
+; CHECK-NEXT: blr
+ %1 = call i8 @llvm.scmp(i8 %x, i8 0)
+ ret i8 %1
+}
+
+define i32 @scmp_32_32_zero(i32 %x) nounwind {
+; CHECK-LABEL: scmp_32_32_zero:
+; CHECK: # %bb.0:
+; CHECK-NEXT: extsw. 3, 3
+; CHECK-NEXT: li 4, -1
+; CHECK-NEXT: neg 3, 3
+; CHECK-NEXT: rldicl 3, 3, 1, 63
+; CHECK-NEXT: isellt 3, 4, 3
+; CHECK-NEXT: blr
+ %1 = call i32 @llvm.scmp(i32 %x, i32 0)
+ ret i32 %1
+}
+
+define i64 @scmp_64_64_zero(i64 %x) nounwind {
+; CHECK-LABEL: scmp_64_64_zero:
+; CHECK: # %bb.0:
+; CHECK-NEXT: addi 4, 3, -1
+; CHECK-NEXT: cmpdi 3, 0
+; CHECK-NEXT: nor 4, 4, 3
+; CHECK-NEXT: li 3, -1
+; CHECK-NEXT: rldicl 4, 4, 1, 63
+; CHECK-NEXT: isellt 3, 3, 4
+; CHECK-NEXT: blr
+ %1 = call i64 @llvm.scmp(i64 %x, i64 0)
+ ret i64 %1
+}
diff --git a/llvm/test/CodeGen/Thumb/scmp.ll b/llvm/test/CodeGen/Thumb/scmp.ll
index c0024492b3a6d..beb58b47742cb 100644
--- a/llvm/test/CodeGen/Thumb/scmp.ll
+++ b/llvm/test/CodeGen/Thumb/scmp.ll
@@ -418,3 +418,119 @@ define i64 @scmp_64_64(i64 %x, i64 %y) nounwind {
%1 = call i64 @llvm.scmp(i64 %x, i64 %y)
ret i64 %1
}
+
+define i8 @scmp_8_8_zero(i8 signext %x) nounwind {
+; THUMB1-LABEL: scmp_8_8_zero:
+; THUMB1: @ %bb.0:
+; THUMB1-NEXT: cmp r0, #0
+; THUMB1-NEXT: bgt .LBB8_2
+; THUMB1-NEXT: @ %bb.1:
+; THUMB1-NEXT: movs r1, #0
+; THUMB1-NEXT: b .LBB8_3
+; THUMB1-NEXT: .LBB8_2:
+; THUMB1-NEXT: movs r1, #1
+; THUMB1-NEXT: .LBB8_3:
+; THUMB1-NEXT: lsrs r0, r0, #31
+; THUMB1-NEXT: subs r0, r1, r0
+; THUMB1-NEXT: bx lr
+;
+; THUMB2-LABEL: scmp_8_8_zero:
+; THUMB2: @ %bb.0:
+; THUMB2-NEXT: subs r0, #0
+; THUMB2-NEXT: it gt
+; THUMB2-NEXT: movgt r0, #1
+; THUMB2-NEXT: it lt
+; THUMB2-NEXT: movlt.w r0, #-1
+; THUMB2-NEXT: bx lr
+;
+; V81M-LABEL: scmp_8_8_zero:
+; V81M: @ %bb.0:
+; V81M-NEXT: cmp r0, #0
+; V81M-NEXT: mov.w r2, #0
+; V81M-NEXT: cset r1, gt
+; V81M-NEXT: cmp.w r2, r0, lsr #31
+; V81M-NEXT: it ne
+; V81M-NEXT: movne.w r1, #-1
+; V81M-NEXT: mov r0, r1
+; V81M-NEXT: bx lr
+ %1 = call i8 @llvm.scmp(i8 %x, i8 0)
+ ret i8 %1
+}
+
+define i32 @scmp_32_32_zero(i32 %x) nounwind {
+; THUMB1-LABEL: scmp_32_32_zero:
+; THUMB1: @ %bb.0:
+; THUMB1-NEXT: cmp r0, #0
+; THUMB1-NEXT: bgt .LBB9_2
+; THUMB1-NEXT: @ %bb.1:
+; THUMB1-NEXT: movs r1, #0
+; THUMB1-NEXT: b .LBB9_3
+; THUMB1-NEXT: .LBB9_2:
+; THUMB1-NEXT: movs r1, #1
+; THUMB1-NEXT: .LBB9_3:
+; THUMB1-NEXT: lsrs r0, r0, #31
+; THUMB1-NEXT: subs r0, r1, r0
+; THUMB1-NEXT: bx lr
+;
+; THUMB2-LABEL: scmp_32_32_zero:
+; THUMB2: @ %bb.0:
+; THUMB2-NEXT: subs r0, #0
+; THUMB2-NEXT: it gt
+; THUMB2-NEXT: movgt r0, #1
+; THUMB2-NEXT: it lt
+; THUMB2-NEXT: movlt.w r0, #-1
+; THUMB2-NEXT: bx lr
+;
+; V81M-LABEL: scmp_32_32_zero:
+; V81M: @ %bb.0:
+; V81M-NEXT: cmp r0, #0
+; V81M-NEXT: mov.w r2, #0
+; V81M-NEXT: cset r1, gt
+; V81M-NEXT: cmp.w r2, r0, lsr #31
+; V81M-NEXT: it ne
+; V81M-NEXT: movne.w r1, #-1
+; V81M-NEXT: mov r0, r1
+; V81M-NEXT: bx lr
+ %1 = call i32 @llvm.scmp(i32 %x, i32 0)
+ ret i32 %1
+}
+
+define i64 @scmp_64_64_zero(i64 %x) nounwind {
+; THUMB1-LABEL: scmp_64_64_zero:
+; THUMB1: @ %bb.0:
+; THUMB1-NEXT: movs r2, #0
+; THUMB1-NEXT: rsbs r0, r0, #0
+; THUMB1-NEXT: mov r0, r2
+; THUMB1-NEXT: sbcs r0, r1
+; THUMB1-NEXT: bge .LBB10_2
+; THUMB1-NEXT: @ %bb.1:
+; THUMB1-NEXT: movs r2, #1
+; THUMB1-NEXT: .LBB10_2:
+; THUMB1-NEXT: lsrs r0, r1, #31
+; THUMB1-NEXT: subs r0, r2, r0
+; THUMB1-NEXT: asrs r1, r0, #31
+; THUMB1-NEXT: bx lr
+;
+; THUMB2-LABEL: scmp_64_64_zero:
+; THUMB2: @ %bb.0:
+; THUMB2-NEXT: rsbs r0, r0, #0
+; THUMB2-NEXT: mov.w r2, #0
+; THUMB2-NEXT: sbcs.w r0, r2, r1
+; THUMB2-NEXT: it lt
+; THUMB2-NEXT: movlt r2, #1
+; THUMB2-NEXT: sub.w r0, r2, r1, lsr #31
+; THUMB2-NEXT: asrs r1, r0, #31
+; THUMB2-NEXT: bx lr
+;
+; V81M-LABEL: scmp_64_64_zero:
+; V81M: @ %bb.0:
+; V81M-NEXT: rsbs r0, r0, #0
+; V81M-NEXT: mov.w r2, #0
+; V81M-NEXT: sbcs.w r0, r2, r1
+; V81M-NEXT: cset r0, lt
+; V81M-NEXT: sub.w r0, r0, r1, lsr #31
+; V81M-NEXT: asrs r1, r0, #31
+; V81M-NEXT: bx lr
+ %1 = call i64 @llvm.scmp(i64 %x, i64 0)
+ ret i64 %1
+}
>From 71700468ee6a7e7f20178e902f5a735a359d3207 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Tue, 23 Jun 2026 11:49:11 -0400
Subject: [PATCH 2/2] [TargetLowering] Optimize scmp with 0 using (x ashr 31) |
(-x shr 31)
Only enabled for platforms where this benefits.
---
llvm/include/llvm/CodeGen/TargetLowering.h | 4 ++
.../CodeGen/SelectionDAG/TargetLowering.cpp | 16 +++++++-
llvm/lib/Target/ARM/ARMISelLowering.cpp | 4 ++
llvm/lib/Target/ARM/ARMISelLowering.h | 2 +
llvm/lib/Target/PowerPC/PPCISelLowering.h | 2 +
llvm/test/CodeGen/PowerPC/scmp.ll | 26 ++++++-------
llvm/test/CodeGen/Thumb/scmp.ll | 37 +++++--------------
7 files changed, 48 insertions(+), 43 deletions(-)
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 7a10a693934fd..fb1c7df191d29 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -3605,6 +3605,10 @@ class LLVM_ABI TargetLoweringBase {
return isOperationLegalOrCustom(Op, VT);
}
+ /// scmp(x, 0) Can be specially lowered using bitwise shifts.
+ /// On some platforms, this is better than shifts or boolean arithmetic.
+ virtual bool preferBitwiseSignum() const { return false; }
+
/// Should we prefer selects to doing arithmetic on boolean types
virtual bool preferSelectsOverBooleanArithmetic(EVT VT) const {
return false;
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index cc3deaa83f63b..29de3fd7c6495 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -12563,11 +12563,25 @@ SDValue TargetLowering::expandCMP(SDNode *Node, SelectionDAG &DAG) const {
unsigned Opcode = Node->getOpcode();
SDValue LHS = Node->getOperand(0);
SDValue RHS = Node->getOperand(1);
+
EVT VT = LHS.getValueType();
EVT ResVT = Node->getValueType(0);
- EVT BoolVT = getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), VT);
SDLoc dl(Node);
+ if (Opcode == ISD::SCMP && isNullConstant(RHS) && preferBitwiseSignum()) {
+ LHS = DAG.getFreeze(LHS);
+ unsigned BitWidth = VT.getScalarSizeInBits();
+ SDValue ShiftAmount = DAG.getShiftAmountConstant(BitWidth - 1, VT, dl);
+ SDValue SignShift = DAG.getNode(ISD::SRA, dl, VT, LHS, ShiftAmount);
+ SDValue NegX =
+ DAG.getNode(ISD::SUB, dl, VT, DAG.getConstant(0, dl, VT), LHS);
+ SDValue NegShift = DAG.getNode(ISD::SRL, dl, VT, NegX, ShiftAmount);
+ SDValue Or = DAG.getNode(ISD::OR, dl, VT, SignShift, NegShift);
+ return DAG.getSExtOrTrunc(Or, dl, ResVT);
+ }
+
+ EVT BoolVT = getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), VT);
+
auto LTPredicate = (Opcode == ISD::UCMP ? ISD::SETULT : ISD::SETLT);
auto GTPredicate = (Opcode == ISD::UCMP ? ISD::SETUGT : ISD::SETGT);
SDValue IsLT = DAG.getSetCC(dl, BoolVT, LHS, RHS, LTPredicate);
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index 248f5b3f9f083..2c7f967f62914 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -1450,6 +1450,10 @@ bool ARMTargetLowering::preferSelectsOverBooleanArithmetic(EVT VT) const {
return !Subtarget->isThumb1Only() && VT.getSizeInBits() <= 32;
}
+bool ARMTargetLowering::preferBitwiseSignum() const {
+ return Subtarget->isThumb1Only();
+}
+
// FIXME: It might make sense to define the representative register class as the
// nearest super-register that has a non-null superset. For example, DPR_VFP2 is
// a super-register of SPR, and DPR is a superset if DPR_VFP2. Consequently,
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.h b/llvm/lib/Target/ARM/ARMISelLowering.h
index 10f5442d7429b..1c75d83bad31e 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.h
+++ b/llvm/lib/Target/ARM/ARMISelLowering.h
@@ -300,6 +300,8 @@ class VectorType;
bool preferSelectsOverBooleanArithmetic(EVT VT) const override;
+ bool preferBitwiseSignum() const override;
+
bool isMaskAndCmp0FoldingBeneficial(const Instruction &AndI) const override;
bool hasAndNotCompare(SDValue V) const override {
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.h b/llvm/lib/Target/PowerPC/PPCISelLowering.h
index 64be786df7096..06101f7f9e826 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.h
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.h
@@ -249,6 +249,8 @@ namespace llvm {
return VT.isScalarInteger();
}
+ bool preferBitwiseSignum() const override { return true; }
+
SDValue getNegatedExpression(SDValue Op, SelectionDAG &DAG, bool LegalOps,
bool OptForSize, NegatibleCost &Cost,
unsigned Depth = 0) const override;
diff --git a/llvm/test/CodeGen/PowerPC/scmp.ll b/llvm/test/CodeGen/PowerPC/scmp.ll
index 7fc1faa5bde4b..b45e8f79697e9 100644
--- a/llvm/test/CodeGen/PowerPC/scmp.ll
+++ b/llvm/test/CodeGen/PowerPC/scmp.ll
@@ -129,11 +129,10 @@ define i64 @scmp_64_64(i64 %x, i64 %y) nounwind {
define i8 @scmp_8_8_zero(i8 signext %x) nounwind {
; CHECK-LABEL: scmp_8_8_zero:
; CHECK: # %bb.0:
-; CHECK-NEXT: cmpwi 3, 0
-; CHECK-NEXT: neg 4, 3
-; CHECK-NEXT: li 3, -1
-; CHECK-NEXT: rldicl 4, 4, 1, 63
-; CHECK-NEXT: isellt 3, 3, 4
+; CHECK-NEXT: srawi 4, 3, 31
+; CHECK-NEXT: neg 3, 3
+; CHECK-NEXT: srwi 3, 3, 31
+; CHECK-NEXT: or 3, 4, 3
; CHECK-NEXT: blr
%1 = call i8 @llvm.scmp(i8 %x, i8 0)
ret i8 %1
@@ -142,11 +141,10 @@ define i8 @scmp_8_8_zero(i8 signext %x) nounwind {
define i32 @scmp_32_32_zero(i32 %x) nounwind {
; CHECK-LABEL: scmp_32_32_zero:
; CHECK: # %bb.0:
-; CHECK-NEXT: extsw. 3, 3
-; CHECK-NEXT: li 4, -1
+; CHECK-NEXT: srawi 4, 3, 31
; CHECK-NEXT: neg 3, 3
-; CHECK-NEXT: rldicl 3, 3, 1, 63
-; CHECK-NEXT: isellt 3, 4, 3
+; CHECK-NEXT: srwi 3, 3, 31
+; CHECK-NEXT: or 3, 4, 3
; CHECK-NEXT: blr
%1 = call i32 @llvm.scmp(i32 %x, i32 0)
ret i32 %1
@@ -155,12 +153,10 @@ define i32 @scmp_32_32_zero(i32 %x) nounwind {
define i64 @scmp_64_64_zero(i64 %x) nounwind {
; CHECK-LABEL: scmp_64_64_zero:
; CHECK: # %bb.0:
-; CHECK-NEXT: addi 4, 3, -1
-; CHECK-NEXT: cmpdi 3, 0
-; CHECK-NEXT: nor 4, 4, 3
-; CHECK-NEXT: li 3, -1
-; CHECK-NEXT: rldicl 4, 4, 1, 63
-; CHECK-NEXT: isellt 3, 3, 4
+; CHECK-NEXT: sradi 4, 3, 63
+; CHECK-NEXT: neg 3, 3
+; CHECK-NEXT: rldicl 3, 3, 1, 63
+; CHECK-NEXT: or 3, 4, 3
; CHECK-NEXT: blr
%1 = call i64 @llvm.scmp(i64 %x, i64 0)
ret i64 %1
diff --git a/llvm/test/CodeGen/Thumb/scmp.ll b/llvm/test/CodeGen/Thumb/scmp.ll
index beb58b47742cb..62ca9d158b60c 100644
--- a/llvm/test/CodeGen/Thumb/scmp.ll
+++ b/llvm/test/CodeGen/Thumb/scmp.ll
@@ -422,16 +422,10 @@ define i64 @scmp_64_64(i64 %x, i64 %y) nounwind {
define i8 @scmp_8_8_zero(i8 signext %x) nounwind {
; THUMB1-LABEL: scmp_8_8_zero:
; THUMB1: @ %bb.0:
-; THUMB1-NEXT: cmp r0, #0
-; THUMB1-NEXT: bgt .LBB8_2
-; THUMB1-NEXT: @ %bb.1:
-; THUMB1-NEXT: movs r1, #0
-; THUMB1-NEXT: b .LBB8_3
-; THUMB1-NEXT: .LBB8_2:
-; THUMB1-NEXT: movs r1, #1
-; THUMB1-NEXT: .LBB8_3:
+; THUMB1-NEXT: asrs r1, r0, #31
+; THUMB1-NEXT: rsbs r0, r0, #0
; THUMB1-NEXT: lsrs r0, r0, #31
-; THUMB1-NEXT: subs r0, r1, r0
+; THUMB1-NEXT: orrs r0, r1
; THUMB1-NEXT: bx lr
;
; THUMB2-LABEL: scmp_8_8_zero:
@@ -460,16 +454,10 @@ define i8 @scmp_8_8_zero(i8 signext %x) nounwind {
define i32 @scmp_32_32_zero(i32 %x) nounwind {
; THUMB1-LABEL: scmp_32_32_zero:
; THUMB1: @ %bb.0:
-; THUMB1-NEXT: cmp r0, #0
-; THUMB1-NEXT: bgt .LBB9_2
-; THUMB1-NEXT: @ %bb.1:
-; THUMB1-NEXT: movs r1, #0
-; THUMB1-NEXT: b .LBB9_3
-; THUMB1-NEXT: .LBB9_2:
-; THUMB1-NEXT: movs r1, #1
-; THUMB1-NEXT: .LBB9_3:
+; THUMB1-NEXT: asrs r1, r0, #31
+; THUMB1-NEXT: rsbs r0, r0, #0
; THUMB1-NEXT: lsrs r0, r0, #31
-; THUMB1-NEXT: subs r0, r1, r0
+; THUMB1-NEXT: orrs r0, r1
; THUMB1-NEXT: bx lr
;
; THUMB2-LABEL: scmp_32_32_zero:
@@ -500,15 +488,10 @@ define i64 @scmp_64_64_zero(i64 %x) nounwind {
; THUMB1: @ %bb.0:
; THUMB1-NEXT: movs r2, #0
; THUMB1-NEXT: rsbs r0, r0, #0
-; THUMB1-NEXT: mov r0, r2
-; THUMB1-NEXT: sbcs r0, r1
-; THUMB1-NEXT: bge .LBB10_2
-; THUMB1-NEXT: @ %bb.1:
-; THUMB1-NEXT: movs r2, #1
-; THUMB1-NEXT: .LBB10_2:
-; THUMB1-NEXT: lsrs r0, r1, #31
-; THUMB1-NEXT: subs r0, r2, r0
-; THUMB1-NEXT: asrs r1, r0, #31
+; THUMB1-NEXT: sbcs r2, r1
+; THUMB1-NEXT: lsrs r0, r2, #31
+; THUMB1-NEXT: asrs r1, r1, #31
+; THUMB1-NEXT: orrs r0, r1
; THUMB1-NEXT: bx lr
;
; THUMB2-LABEL: scmp_64_64_zero:
More information about the llvm-commits
mailing list