[llvm] [X86] Shrink i32 unsigned compares to i8/i16 when high bits are zero (PR #197801)
Weiwen He via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 8 09:36:30 PDT 2026
https://github.com/he-weiwen updated https://github.com/llvm/llvm-project/pull/197801
>From eed988979e5d2bae66c5dfee986fa2dbf6cf1675 Mon Sep 17 00:00:00 2001
From: he-weiwen <he.weiwen at outlook.com>
Date: Thu, 14 May 2026 21:01:51 +0100
Subject: [PATCH] [X86] Shrink i32 unsigned compares to i8/i16 when high bits
are zero
---
llvm/lib/Target/X86/X86ISelLowering.cpp | 26 ++++
llvm/test/CodeGen/X86/cmp-shiftX-maskX.ll | 8 +-
llvm/test/CodeGen/X86/cmp-shrink-unsigned.ll | 118 +++++++++++++++++++
3 files changed, 148 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/CodeGen/X86/cmp-shrink-unsigned.ll
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index a8236f02f8df4..51fd3c473d7b8 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -24309,6 +24309,7 @@ static SDValue EmitTest(SDValue Op, X86::CondCode X86CC, const SDLoc &dl,
static SDValue EmitCmp(SDValue Op0, SDValue Op1, X86::CondCode X86CC,
const SDLoc &dl, SelectionDAG &DAG,
const X86Subtarget &Subtarget) {
+ using namespace SDPatternMatch;
if (isNullConstant(Op1))
return EmitTest(Op0, X86CC, dl, DAG, Subtarget);
@@ -24369,6 +24370,31 @@ static SDValue EmitCmp(SDValue Op0, SDValue Op1, X86::CondCode X86CC,
Op1 = DAG.getNode(ISD::TRUNCATE, dl, CmpVT, Op1);
}
+ // Try to shrink i32 unsigned compares to i8 or i16 when both operands'
+ // high bits are known zero and neither operand is a constant. Eligible
+ // i64 compares are shrunk to i32 above.
+ if (CmpVT == MVT::i32 && !isX86CCSigned(X86CC) &&
+ Op0.hasOneUse() && // Hacky way to not break CSE opportunities with sub.
+ !isa<ConstantSDNode>(Op0) && !isa<ConstantSDNode>(Op1)) {
+ auto BothOpsFit = [&](unsigned Bits) {
+ APInt Hi = APInt::getHighBitsSet(32, 32 - Bits);
+ return DAG.MaskedValueIsZero(Op0, Hi) && DAG.MaskedValueIsZero(Op1, Hi);
+ };
+ // Narrowing to i16 is only when this enables an (and x, 0xFFFF) in an
+ // operand to be removed downstream
+ auto IsI16Eliminable = [](SDValue Op) {
+ return sd_match(Op, m_And(m_Value(), m_SpecificInt(0xFFFF)));
+ };
+ if (BothOpsFit(8))
+ CmpVT = MVT::i8;
+ else if (BothOpsFit(16) && (IsI16Eliminable(Op0) || IsI16Eliminable(Op1)))
+ CmpVT = MVT::i16;
+ if (CmpVT != MVT::i32) {
+ Op0 = DAG.getNode(ISD::TRUNCATE, dl, CmpVT, Op0);
+ Op1 = DAG.getNode(ISD::TRUNCATE, dl, CmpVT, Op1);
+ }
+ }
+
// 0-x == y --> x+y == 0
// 0-x != y --> x+y != 0
if (Op0.getOpcode() == ISD::SUB && isNullConstant(Op0.getOperand(0)) &&
diff --git a/llvm/test/CodeGen/X86/cmp-shiftX-maskX.ll b/llvm/test/CodeGen/X86/cmp-shiftX-maskX.ll
index 227de9ad0ab69..f85b3acaabbd5 100644
--- a/llvm/test/CodeGen/X86/cmp-shiftX-maskX.ll
+++ b/llvm/test/CodeGen/X86/cmp-shiftX-maskX.ll
@@ -106,9 +106,9 @@ define i1 @shl_to_shr_eq_i32_s3_fail(i32 %x) {
define i1 @shl_to_shr_ne_i32_s16(i32 %x) {
; CHECK-NOBMI-LABEL: shl_to_shr_ne_i32_s16:
; CHECK-NOBMI: # %bb.0:
-; CHECK-NOBMI-NEXT: movzwl %di, %eax
+; CHECK-NOBMI-NEXT: movl %edi, %eax
; CHECK-NOBMI-NEXT: shrl $16, %edi
-; CHECK-NOBMI-NEXT: cmpl %edi, %eax
+; CHECK-NOBMI-NEXT: cmpw %di, %ax
; CHECK-NOBMI-NEXT: setne %al
; CHECK-NOBMI-NEXT: retq
;
@@ -286,9 +286,9 @@ define i1 @shr_to_shl_eq_i64_s7(i64 %x) {
define i1 @shl_to_shr_ne_i32_s24(i32 %x) {
; CHECK-LABEL: shl_to_shr_ne_i32_s24:
; CHECK: # %bb.0:
-; CHECK-NEXT: movzbl %dil, %eax
+; CHECK-NEXT: movl %edi, %eax
; CHECK-NEXT: shrl $24, %edi
-; CHECK-NEXT: cmpl %edi, %eax
+; CHECK-NEXT: cmpb %dil, %al
; CHECK-NEXT: setne %al
; CHECK-NEXT: retq
%shl = shl i32 %x, 24
diff --git a/llvm/test/CodeGen/X86/cmp-shrink-unsigned.ll b/llvm/test/CodeGen/X86/cmp-shrink-unsigned.ll
new file mode 100644
index 0000000000000..698d16766030e
--- /dev/null
+++ b/llvm/test/CodeGen/X86/cmp-shrink-unsigned.ll
@@ -0,0 +1,118 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=i686-- | FileCheck %s --check-prefixes=X86
+; RUN: llc < %s -mtriple=x86_64-- | FileCheck %s --check-prefixes=X64
+
+declare void @ext()
+
+define void @gt_u8(i32 %a, i32 %b) {
+; X86-LABEL: gt_u8:
+; X86: # %bb.0: # %entry
+; X86-NEXT: movzbl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: cmpb {{[0-9]+}}(%esp), %al
+; X86-NEXT: ja ext at PLT # TAILCALL
+; X86-NEXT: # %bb.1: # %end
+; X86-NEXT: retl
+;
+; X64-LABEL: gt_u8:
+; X64: # %bb.0: # %entry
+; X64-NEXT: cmpb %sil, %dil
+; X64-NEXT: ja ext at PLT # TAILCALL
+; X64-NEXT: # %bb.1: # %end
+; X64-NEXT: retq
+entry:
+ %ca = and i32 %a, 255
+ %cb = and i32 %b, 255
+ %cmp = icmp ugt i32 %ca, %cb
+ br i1 %cmp, label %then, label %end
+then:
+ tail call void @ext()
+ br label %end
+end:
+ ret void
+}
+
+define void @gt_u16(i32 %a, i32 %b) {
+; X86-LABEL: gt_u16:
+; X86: # %bb.0: # %entry
+; X86-NEXT: movzwl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movzwl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: cmpl %eax, %ecx
+; X86-NEXT: ja ext at PLT # TAILCALL
+; X86-NEXT: # %bb.1: # %end
+; X86-NEXT: retl
+;
+; X64-LABEL: gt_u16:
+; X64: # %bb.0: # %entry
+; X64-NEXT: cmpw %si, %di
+; X64-NEXT: ja ext at PLT # TAILCALL
+; X64-NEXT: # %bb.1: # %end
+; X64-NEXT: retq
+entry:
+ %ca = and i32 %a, 65535
+ %cb = and i32 %b, 65535
+ %cmp = icmp ugt i32 %ca, %cb
+ br i1 %cmp, label %then, label %end
+then:
+ tail call void @ext()
+ br label %end
+end:
+ ret void
+}
+
+define void @gt_u16_zext(i16 %a, i16 %b) {
+; X86-LABEL: gt_u16_zext:
+; X86: # %bb.0: # %entry
+; X86-NEXT: movzwl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movzwl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: cmpl %eax, %ecx
+; X86-NEXT: ja ext at PLT # TAILCALL
+; X86-NEXT: # %bb.1: # %end
+; X86-NEXT: retl
+;
+; X64-LABEL: gt_u16_zext:
+; X64: # %bb.0: # %entry
+; X64-NEXT: cmpw %si, %di
+; X64-NEXT: ja ext at PLT # TAILCALL
+; X64-NEXT: # %bb.1: # %end
+; X64-NEXT: retq
+entry:
+ %za = zext i16 %a to i32
+ %zb = zext i16 %b to i32
+ %cmp = icmp ugt i32 %za, %zb
+ br i1 %cmp, label %then, label %end
+then:
+ tail call void @ext()
+ br label %end
+end:
+ ret void
+}
+
+define void @gt_u16_wide_mask(i32 %a, i32 %b) {
+; X86-LABEL: gt_u16_wide_mask:
+; X86: # %bb.0: # %entry
+; X86-NEXT: movzwl {{[0-9]+}}(%esp), %eax
+; X86-NEXT: movzwl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT: cmpl %eax, %ecx
+; X86-NEXT: ja ext at PLT # TAILCALL
+; X86-NEXT: # %bb.1: # %end
+; X86-NEXT: retl
+;
+; X64-LABEL: gt_u16_wide_mask:
+; X64: # %bb.0: # %entry
+; X64-NEXT: cmpw %si, %di
+; X64-NEXT: ja ext at PLT # TAILCALL
+; X64-NEXT: # %bb.1: # %end
+; X64-NEXT: retq
+entry:
+ %a16 = and i32 %a, 65535
+ %ca = and i32 %a16, 131071 ; 0x1FFFF (17 trailing 1 bits)
+ %cb = and i32 %b, 65535
+ %cmp = icmp ugt i32 %ca, %cb
+ br i1 %cmp, label %then, label %end
+then:
+ tail call void @ext()
+ br label %end
+end:
+ ret void
+}
+
More information about the llvm-commits
mailing list