[llvm] [X86] Use BMI2 shrx for range-known zero tests (PR #207904)

Jaeuk Lee via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 7 01:07:31 PDT 2026


https://github.com/skku970412 updated https://github.com/llvm/llvm-project/pull/207904

>From 457923193e46bc429bfa626bc9a11f44fd07151a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EC=9D=B4=EC=9E=AC=EC=9A=B1?=
 <126692701+skku970412 at users.noreply.github.com>
Date: Tue, 7 Jul 2026 15:35:07 +0900
Subject: [PATCH 1/2] [X86] Use BMI2 shrx for range-known zero tests

---
 llvm/lib/Target/X86/X86ISelLowering.cpp | 50 +++++++++++++
 llvm/test/CodeGen/X86/setcc.ll          | 94 ++++++++++++++++++++++++-
 2 files changed, 143 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 9209134a055c6..1a830690761df 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -57313,6 +57313,53 @@ static SDValue combineExtSetcc(SDNode *N, SelectionDAG &DAG,
   return Res;
 }
 
+static SDValue combineZextSetccEqZeroToShift(SDNode *N, SelectionDAG &DAG,
+                                             const X86Subtarget &Subtarget) {
+  if (!Subtarget.hasBMI2() || !Subtarget.is64Bit() ||
+      N->getOpcode() != ISD::ZERO_EXTEND)
+    return SDValue();
+
+  SDValue N0 = N->getOperand(0);
+  if (N0.getOpcode() != ISD::SETCC || !N0.hasOneUse())
+    return SDValue();
+
+  ISD::CondCode CC = cast<CondCodeSDNode>(N0.getOperand(2))->get();
+  if (CC != ISD::SETEQ)
+    return SDValue();
+
+  SDValue X = N0.getOperand(0);
+  SDValue Y = N0.getOperand(1);
+  if (isNullConstant(X))
+    std::swap(X, Y);
+  else if (!isNullConstant(Y))
+    return SDValue();
+
+  EVT ShiftVT = X.getValueType();
+  EVT VT = N->getValueType(0);
+  if (!ShiftVT.isSimple() || !ShiftVT.isScalarInteger() ||
+      !VT.isSimple() || !VT.isScalarInteger())
+    return SDValue();
+
+  MVT ShiftMVT = ShiftVT.getSimpleVT();
+  if (ShiftMVT != MVT::i32 && ShiftMVT != MVT::i64)
+    return SDValue();
+  MVT ResultMVT = VT.getSimpleVT();
+  if (ResultMVT != MVT::i32 && ResultMVT != MVT::i64)
+    return SDValue();
+
+  unsigned BitWidth = ShiftMVT.getScalarSizeInBits();
+  if (!DAG.computeKnownBits(X).getMaxValue().ult(BitWidth))
+    return SDValue();
+
+  // SHRX masks its shift count, so this is only equivalent to zext(X == 0)
+  // when the shift amount is known to be in range. Under that condition,
+  // (1 >> X) produces 1 iff X is zero and 0 otherwise.
+  SDLoc DL(N);
+  SDValue One = DAG.getConstant(1, DL, ShiftMVT);
+  SDValue Shift = DAG.getNode(ISD::SRL, DL, ShiftMVT, One, X);
+  return DAG.getZExtOrTrunc(Shift, DL, VT);
+}
+
 static SDValue combineSext(SDNode *N, SelectionDAG &DAG,
                            TargetLowering::DAGCombinerInfo &DCI,
                            const X86Subtarget &Subtarget) {
@@ -57643,6 +57690,9 @@ static SDValue combineZext(SDNode *N, SelectionDAG &DAG,
     if (SDValue V = combineExtSetcc(N, DAG, Subtarget))
       return V;
 
+  if (SDValue V = combineZextSetccEqZeroToShift(N, DAG, Subtarget))
+    return V;
+
   if (SDValue V = combineToExtendBoolVectorInReg(N->getOpcode(), dl, VT, N0,
                                                  DAG, DCI, Subtarget))
     return V;
diff --git a/llvm/test/CodeGen/X86/setcc.ll b/llvm/test/CodeGen/X86/setcc.ll
index 60ac6df3f77af..3ab8a15ed54d4 100644
--- a/llvm/test/CodeGen/X86/setcc.ll
+++ b/llvm/test/CodeGen/X86/setcc.ll
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
 ; RUN: llc < %s -mtriple=i686-apple-darwin | FileCheck %s --check-prefixes=X86
 ; RUN: llc < %s -mtriple=x86_64-apple-darwin | FileCheck %s --check-prefixes=X64,X64-NOTBM,X64-NOBMI2
 ; RUN: llc < %s -mtriple=x86_64-apple-darwin -mattr=+tbm | FileCheck %s --check-prefixes=X64,X64-NOBMI2,X64-TBM
@@ -354,3 +354,95 @@ define i64 @pr63055(double %arg) {
   %ext = zext i1 %fcmp to i64
   ret i64 %ext
 }
+
+define i32 @zext_eq_zero_i32(i32 %a) {
+; X86-LABEL: zext_eq_zero_i32:
+; X86:       ## %bb.0:
+; X86-NEXT:    xorl %eax, %eax
+; X86-NEXT:    cmpl $0, {{[0-9]+}}(%esp)
+; X86-NEXT:    sete %al
+; X86-NEXT:    retl
+;
+; X64-LABEL: zext_eq_zero_i32:
+; X64:       ## %bb.0:
+; X64-NEXT:    xorl %eax, %eax
+; X64-NEXT:    testl %edi, %edi
+; X64-NEXT:    sete %al
+; X64-NEXT:    retq
+  %cmp = icmp eq i32 %a, 0
+  %conv = zext i1 %cmp to i32
+  ret i32 %conv
+}
+
+define i32 @zext_and31_eq_zero_i32(i32 %a) {
+; X86-LABEL: zext_and31_eq_zero_i32:
+; X86:       ## %bb.0:
+; X86-NEXT:    xorl %eax, %eax
+; X86-NEXT:    testb $31, {{[0-9]+}}(%esp)
+; X86-NEXT:    sete %al
+; X86-NEXT:    retl
+;
+; X64-NOBMI2-LABEL: zext_and31_eq_zero_i32:
+; X64-NOBMI2:       ## %bb.0:
+; X64-NOBMI2-NEXT:    xorl %eax, %eax
+; X64-NOBMI2-NEXT:    testb $31, %dil
+; X64-NOBMI2-NEXT:    sete %al
+; X64-NOBMI2-NEXT:    retq
+;
+; X64-BMI2-LABEL: zext_and31_eq_zero_i32:
+; X64-BMI2:       ## %bb.0:
+; X64-BMI2-NEXT:    movl $1, %eax
+; X64-BMI2-NEXT:    shrxl %edi, %eax, %eax
+; X64-BMI2-NEXT:    retq
+  %and = and i32 %a, 31
+  %cmp = icmp eq i32 %and, 0
+  %conv = zext i1 %cmp to i32
+  ret i32 %conv
+}
+
+define i64 @zext_and63_eq_zero_i64(i64 %a) {
+; X86-LABEL: zext_and63_eq_zero_i64:
+; X86:       ## %bb.0:
+; X86-NEXT:    xorl %eax, %eax
+; X86-NEXT:    testb $63, {{[0-9]+}}(%esp)
+; X86-NEXT:    sete %al
+; X86-NEXT:    xorl %edx, %edx
+; X86-NEXT:    retl
+;
+; X64-NOBMI2-LABEL: zext_and63_eq_zero_i64:
+; X64-NOBMI2:       ## %bb.0:
+; X64-NOBMI2-NEXT:    xorl %eax, %eax
+; X64-NOBMI2-NEXT:    testb $63, %dil
+; X64-NOBMI2-NEXT:    sete %al
+; X64-NOBMI2-NEXT:    retq
+;
+; X64-BMI2-LABEL: zext_and63_eq_zero_i64:
+; X64-BMI2:       ## %bb.0:
+; X64-BMI2-NEXT:    movl $1, %eax
+; X64-BMI2-NEXT:    shrxq %rdi, %rax, %rax
+; X64-BMI2-NEXT:    retq
+  %and = and i64 %a, 63
+  %cmp = icmp eq i64 %and, 0
+  %conv = zext i1 %cmp to i64
+  ret i64 %conv
+}
+
+define i32 @zext_and31_ne_zero_i32(i32 %a) {
+; X86-LABEL: zext_and31_ne_zero_i32:
+; X86:       ## %bb.0:
+; X86-NEXT:    xorl %eax, %eax
+; X86-NEXT:    testb $31, {{[0-9]+}}(%esp)
+; X86-NEXT:    setne %al
+; X86-NEXT:    retl
+;
+; X64-LABEL: zext_and31_ne_zero_i32:
+; X64:       ## %bb.0:
+; X64-NEXT:    xorl %eax, %eax
+; X64-NEXT:    testb $31, %dil
+; X64-NEXT:    setne %al
+; X64-NEXT:    retq
+  %and = and i32 %a, 31
+  %cmp = icmp ne i32 %and, 0
+  %conv = zext i1 %cmp to i32
+  ret i32 %conv
+}

>From 61cc7979d54771f6e002c3b5f5b266bdf9fca341 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EC=9D=B4=EC=9E=AC=EC=9A=B1?=
 <126692701+skku970412 at users.noreply.github.com>
Date: Tue, 7 Jul 2026 17:06:44 +0900
Subject: [PATCH 2/2] [X86] Address BMI2 zero-test review

---
 llvm/lib/Target/X86/X86ISelLowering.cpp | 21 ++++++---------------
 1 file changed, 6 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 1a830690761df..96658f227b5c5 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -57315,23 +57315,14 @@ static SDValue combineExtSetcc(SDNode *N, SelectionDAG &DAG,
 
 static SDValue combineZextSetccEqZeroToShift(SDNode *N, SelectionDAG &DAG,
                                              const X86Subtarget &Subtarget) {
-  if (!Subtarget.hasBMI2() || !Subtarget.is64Bit() ||
-      N->getOpcode() != ISD::ZERO_EXTEND)
+  if (!Subtarget.hasBMI2() || N->getOpcode() != ISD::ZERO_EXTEND)
     return SDValue();
 
-  SDValue N0 = N->getOperand(0);
-  if (N0.getOpcode() != ISD::SETCC || !N0.hasOneUse())
-    return SDValue();
-
-  ISD::CondCode CC = cast<CondCodeSDNode>(N0.getOperand(2))->get();
-  if (CC != ISD::SETEQ)
-    return SDValue();
-
-  SDValue X = N0.getOperand(0);
-  SDValue Y = N0.getOperand(1);
-  if (isNullConstant(X))
-    std::swap(X, Y);
-  else if (!isNullConstant(Y))
+  using namespace SDPatternMatch;
+  SDValue X;
+  if (!sd_match(N->getOperand(0),
+                m_OneUse(m_c_SetCC(m_Value(X), m_Zero(),
+                                    m_SpecificCondCode(ISD::SETEQ)))))
     return SDValue();
 
   EVT ShiftVT = X.getValueType();



More information about the llvm-commits mailing list