[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
Tue Jun 9 01:05:43 PDT 2026


https://github.com/he-weiwen updated https://github.com/llvm/llvm-project/pull/197801

>From 9f02f9ba5c23c360f3fed0578a7b3ab6d2c06862 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