[llvm] [X86] Remove redundant TEST after shifts when count is non-zero (PR #169069)

via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 25 10:52:02 PST 2025


https://github.com/GrumpyPigSkin updated https://github.com/llvm/llvm-project/pull/169069

>From c43871f2418f28520b77adcdd5c4de5a25c9a821 Mon Sep 17 00:00:00 2001
From: GrumpyPigSkin <oliver61 at live.co.uk>
Date: Fri, 21 Nov 2025 16:52:26 +0000
Subject: [PATCH 01/10] [X86] Added new ISDX86 nodes for SHL/SHR/SAR opcodes.

---
 llvm/lib/Target/X86/X86ISelLowering.h      |  3 ++
 llvm/lib/Target/X86/X86InstrShiftRotate.td | 49 ++++++++++++++++++++++
 2 files changed, 52 insertions(+)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.h b/llvm/lib/Target/X86/X86ISelLowering.h
index b7151f65942b4..00830804213f7 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.h
+++ b/llvm/lib/Target/X86/X86ISelLowering.h
@@ -996,6 +996,9 @@ namespace llvm {
     CLOAD,
     CSTORE,
     LAST_MEMORY_OPCODE = CSTORE,
+    SHL,
+    SRL,
+    SRA,
   };
   } // end namespace X86ISD
 
diff --git a/llvm/lib/Target/X86/X86InstrShiftRotate.td b/llvm/lib/Target/X86/X86InstrShiftRotate.td
index 2a5488847e648..e81bc33d3a459 100644
--- a/llvm/lib/Target/X86/X86InstrShiftRotate.td
+++ b/llvm/lib/Target/X86/X86InstrShiftRotate.td
@@ -689,3 +689,52 @@ let Predicates = [HasBMI2, HasEGPR] in {
   defm SHRX : ShiftX_Pats<srl, "_EVEX">;
   defm SHLX : ShiftX_Pats<shl, "_EVEX">;
 }
+
+def SDTX86ShiftWithFlags : SDTypeProfile<2, 1, [
+  SDTCisSameAs<0, 2>,
+  SDTCisVT<1, i32>,
+]>;
+
+def X86shl : SDNode<"X86ISD::SHL", SDTX86ShiftWithFlags, [SDNPInGlue]>;
+def X86srl : SDNode<"X86ISD::SRL", SDTX86ShiftWithFlags, [SDNPInGlue]>;
+def X86sra : SDNode<"X86ISD::SRA", SDTX86ShiftWithFlags, [SDNPInGlue]>;
+
+let Predicates = [NoNDD] in {
+  // SHL
+  def : Pat<(X86shl GR8:$src1),  (SHL8rCL  GR8:$src1)>;
+  def : Pat<(X86shl GR16:$src1), (SHL16rCL GR16:$src1)>;
+  def : Pat<(X86shl GR32:$src1), (SHL32rCL GR32:$src1)>;
+  def : Pat<(X86shl GR64:$src1), (SHL64rCL GR64:$src1)>;
+
+  // SHR
+  def : Pat<(X86srl GR8:$src1),  (SHR8rCL  GR8:$src1)>;
+  def : Pat<(X86srl GR16:$src1), (SHR16rCL GR16:$src1)>;
+  def : Pat<(X86srl GR32:$src1), (SHR32rCL GR32:$src1)>;
+  def : Pat<(X86srl GR64:$src1), (SHR64rCL GR64:$src1)>;
+
+  // SAR
+  def : Pat<(X86sra GR8:$src1),  (SAR8rCL  GR8:$src1)>;
+  def : Pat<(X86sra GR16:$src1), (SAR16rCL GR16:$src1)>;
+  def : Pat<(X86sra GR32:$src1), (SAR32rCL GR32:$src1)>;
+  def : Pat<(X86sra GR64:$src1), (SAR64rCL GR64:$src1)>;
+}
+
+let Predicates = [HasNDD, In64BitMode] in {
+  // SHL
+  def : Pat<(X86shl GR8:$src1),  (SHL8rCL_ND  GR8:$src1)>;
+  def : Pat<(X86shl GR16:$src1), (SHL16rCL_ND GR16:$src1)>;
+  def : Pat<(X86shl GR32:$src1), (SHL32rCL_ND GR32:$src1)>;
+  def : Pat<(X86shl GR64:$src1), (SHL64rCL_ND GR64:$src1)>;
+
+  // SHR
+  def : Pat<(X86srl GR8:$src1),  (SHR8rCL_ND  GR8:$src1)>;
+  def : Pat<(X86srl GR16:$src1), (SHR16rCL_ND GR16:$src1)>;
+  def : Pat<(X86srl GR32:$src1), (SHR32rCL_ND GR32:$src1)>;
+  def : Pat<(X86srl GR64:$src1), (SHR64rCL_ND GR64:$src1)>;
+
+  // SAR
+  def : Pat<(X86sra GR8:$src1),  (SAR8rCL_ND  GR8:$src1)>;
+  def : Pat<(X86sra GR16:$src1), (SAR16rCL_ND GR16:$src1)>;
+  def : Pat<(X86sra GR32:$src1), (SAR32rCL_ND GR32:$src1)>;
+  def : Pat<(X86sra GR64:$src1), (SAR64rCL_ND GR64:$src1)>;
+}

>From 8e0c2675dbd4abbbb004e3ea039cc63847cf8681 Mon Sep 17 00:00:00 2001
From: GrumpyPigSkin <oliver61 at live.co.uk>
Date: Fri, 21 Nov 2025 16:54:53 +0000
Subject: [PATCH 02/10] [X86] Added check to allow the removal of test
 instruction when the shift amt > 0

---
 llvm/lib/Target/X86/X86ISelLowering.cpp | 51 +++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index dc84025c166a3..87e637ee4c272 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -23625,6 +23625,54 @@ static SDValue EmitTest(SDValue Op, X86::CondCode X86CC, const SDLoc &dl,
     return DAG.getNode(X86ISD::SUB, dl, VTs, Op->getOperand(0),
                        Op->getOperand(1)).getValue(1);
   }
+  case ISD::SHL:
+  case ISD::SRL:
+  case ISD::SRA: {
+    SDValue Amt = ArithOp.getOperand(1);
+
+    // Skip Constants
+    if (isa<ConstantSDNode>(Amt))
+      break;
+
+    // Check we can make this optimization
+    if (ArithOp->getFlags().hasNoZero() || DAG.isKnownNeverZero(Amt)) {
+      SDLoc DL(ArithOp);
+
+      // CopyToReg(CL, Amt)
+      SDValue Chain = DAG.getEntryNode();
+      SDValue Glue;
+
+      Chain = DAG.getCopyToReg(Chain, DL, X86::CL, Amt, Glue);
+      Glue = Chain.getValue(1);
+
+      // Select Opcode
+      unsigned X86Opcode;
+      switch (ArithOp.getOpcode()) {
+      case ISD::SHL:
+        X86Opcode = X86ISD::SHL;
+        break;
+      case ISD::SRL:
+        X86Opcode = X86ISD::SRL;
+        break;
+      case ISD::SRA:
+        X86Opcode = X86ISD::SRA;
+        break;
+      default:
+        llvm_unreachable("Unexpected shift opcode");
+      }
+
+      // Create Node [ValueToShift, Glue]
+      SDVTList VTs = DAG.getVTList(ArithOp.getValueType(), MVT::i32);
+      SDValue Ops[] = {ArithOp.getOperand(0), Glue};
+
+      SDValue NewNode = DAG.getNode(X86Opcode, DL, VTs, Ops);
+
+      // Replace and Return
+      DAG.ReplaceAllUsesOfValueWith(ArithOp, NewNode.getValue(0));
+      return NewNode.getValue(1);
+    }
+    break;
+  }
   default:
     break;
   }
@@ -35467,6 +35515,9 @@ const char *X86TargetLowering::getTargetNodeName(unsigned Opcode) const {
   NODE_NAME_CASE(CVTTP2UIS)
   NODE_NAME_CASE(MCVTTP2UIS)
   NODE_NAME_CASE(POP_FROM_X87_REG)
+  NODE_NAME_CASE(SHL)
+  NODE_NAME_CASE(SRL)
+  NODE_NAME_CASE(SRA)
   }
   return nullptr;
 #undef NODE_NAME_CASE

>From d44bb095654053ee5fba13759ddf7c8930a08910 Mon Sep 17 00:00:00 2001
From: GrumpyPigSkin <oliver61 at live.co.uk>
Date: Fri, 21 Nov 2025 16:57:25 +0000
Subject: [PATCH 03/10] [X86] Added new flag to SDNodeFlags to indicate when a
 value isn't 0

---
 llvm/include/llvm/CodeGen/SelectionDAGNodes.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h
index cfc8a4243e894..082e7b4371d27 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h
@@ -427,6 +427,7 @@ struct SDNodeFlags {
     // implied by its producer as, e.g, operations between producer and PTRADD
     // that affect the provenance may have been optimized away.
     InBounds = 1 << 15,
+    NoZero = 1 << 16,
 
     // NOTE: Please update LargestValue in LLVM_DECLARE_ENUM_AS_BITMASK below
     // the class definition when adding new flags.
@@ -468,6 +469,7 @@ struct SDNodeFlags {
   void setNoFPExcept(bool b) { setFlag<NoFPExcept>(b); }
   void setUnpredictable(bool b) { setFlag<Unpredictable>(b); }
   void setInBounds(bool b) { setFlag<InBounds>(b); }
+  void setNoZero(bool b) { setFlag<NoZero>(b); }
 
   // These are accessors for each flag.
   bool hasNoUnsignedWrap() const { return Flags & NoUnsignedWrap; }
@@ -486,6 +488,7 @@ struct SDNodeFlags {
   bool hasNoFPExcept() const { return Flags & NoFPExcept; }
   bool hasUnpredictable() const { return Flags & Unpredictable; }
   bool hasInBounds() const { return Flags & InBounds; }
+  bool hasNoZero() const { return Flags & NoZero; }
 
   bool operator==(const SDNodeFlags &Other) const {
     return Flags == Other.Flags;

>From 9d2d01e28e28df17551cc3cc3b86fb0ecce586ab Mon Sep 17 00:00:00 2001
From: GrumpyPigSkin <oliver61 at live.co.uk>
Date: Fri, 21 Nov 2025 16:58:53 +0000
Subject: [PATCH 04/10] [X86] Added check for constant range to pass additional
 information down to lowering.

---
 .../SelectionDAG/SelectionDAGBuilder.cpp      | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 985a54ca83256..51d3fc0d78975 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -3687,6 +3687,7 @@ void SelectionDAGBuilder::visitShift(const User &I, unsigned Opcode) {
   bool nuw = false;
   bool nsw = false;
   bool exact = false;
+  bool noZero = false;
 
   if (Opcode == ISD::SRL || Opcode == ISD::SRA || Opcode == ISD::SHL) {
 
@@ -3698,11 +3699,30 @@ void SelectionDAGBuilder::visitShift(const User &I, unsigned Opcode) {
     if (const PossiblyExactOperator *ExactOp =
             dyn_cast<const PossiblyExactOperator>(&I))
       exact = ExactOp->isExact();
+
+    const Value *ShiftAmt = I.getOperand(1);
+
+    // Look through zext as computeConstantRange does not do this.
+    const Value *InnerVal = ShiftAmt;
+    if (auto *ZExt = dyn_cast<ZExtInst>(ShiftAmt)) {
+      InnerVal = ZExt->getOperand(0);
+    }
+
+    // Get the constant range and check it excludes 0
+    ConstantRange CR = llvm::computeConstantRange(
+        InnerVal, true, true, AC, dyn_cast<Instruction>(&I), nullptr);
+
+    if (!CR.isEmptySet() && !CR.contains(APInt(CR.getBitWidth(), 0))) {
+      // We can guarantee that that we will not be shifted by 0
+      noZero = true;
+    }
   }
+
   SDNodeFlags Flags;
   Flags.setExact(exact);
   Flags.setNoSignedWrap(nsw);
   Flags.setNoUnsignedWrap(nuw);
+  Flags.setNoZero(noZero);
   SDValue Res = DAG.getNode(Opcode, getCurSDLoc(), Op1.getValueType(), Op1, Op2,
                             Flags);
   setValue(&I, Res);

>From 5cc8c2ca0f065a70c82f124795c969161db0915f Mon Sep 17 00:00:00 2001
From: GrumpyPigSkin <oliver61 at live.co.uk>
Date: Fri, 21 Nov 2025 17:00:54 +0000
Subject: [PATCH 05/10] [X86] Preserved some high level data when assumptions
 are eliminated, allowing for future optimizations.

---
 llvm/lib/CodeGen/CodeGenPrepare.cpp | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp
index 587c1372b19cb..b4b4879c515c5 100644
--- a/llvm/lib/CodeGen/CodeGenPrepare.cpp
+++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp
@@ -786,8 +786,32 @@ bool CodeGenPrepare::eliminateAssumptions(Function &F) {
       if (auto *Assume = dyn_cast<AssumeInst>(I)) {
         MadeChange = true;
         Value *Operand = Assume->getOperand(0);
+        if (ICmpInst *Cmp = dyn_cast<ICmpInst>(Operand)) {
+          // Check if we are comparing an Argument against a Constant
+          if (Argument *Arg = dyn_cast<Argument>(Cmp->getOperand(0))) {
+            if (ConstantInt *C = dyn_cast<ConstantInt>(Cmp->getOperand(1))) {
+              // We found "assume(Arg <pred> Constant)"
+              ConstantRange Constraint = ConstantRange::makeAllowedICmpRegion(
+                  Cmp->getPredicate(), C->getValue());
+              // If the argument already has a range attribute, intersect with
+              // it
+              ConstantRange FinalRange = Constraint;
+              Attribute ExistingRangeAttr = Arg->getAttribute(Attribute::Range);
+              if (ExistingRangeAttr.isValid()) {
+                FinalRange =
+                    FinalRange.intersectWith(ExistingRangeAttr.getRange());
+              }
+              // If the range provides new information (and isn't empty), save
+              // it.
+              if (!FinalRange.isFullSet() && !FinalRange.isEmptySet()) {
+                AttrBuilder AB(Arg->getContext());
+                AB.addRangeAttr(FinalRange);
+                Arg->addAttrs(AB);
+              }
+            }
+          }
+        }
         Assume->eraseFromParent();
-
         resetIteratorIfInvalidatedWhileCalling(&BB, [&]() {
           RecursivelyDeleteTriviallyDeadInstructions(Operand, TLInfo, nullptr);
         });

>From a9a6a0eafa36f178dfa6ac49a5e3f37decefbcbc Mon Sep 17 00:00:00 2001
From: GrumpyPigSkin <oliver61 at live.co.uk>
Date: Fri, 21 Nov 2025 17:01:09 +0000
Subject: [PATCH 06/10] [X86] Updated tests

---
 llvm/test/CodeGen/X86/apx/shift-eflags.ll | 23 +++++++++++++++++------
 llvm/test/CodeGen/X86/shift-eflags.ll     | 23 +++++++++++++++++------
 2 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/llvm/test/CodeGen/X86/apx/shift-eflags.ll b/llvm/test/CodeGen/X86/apx/shift-eflags.ll
index 2659f8031ef77..01f4689369eac 100644
--- a/llvm/test/CodeGen/X86/apx/shift-eflags.ll
+++ b/llvm/test/CodeGen/X86/apx/shift-eflags.ll
@@ -265,7 +265,6 @@ define i32 @ashr_var_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
 ; CHECK-NEXT:    movl %ecx, %eax
 ; CHECK-NEXT:    orb $1, %sil, %cl
 ; CHECK-NEXT:    sarl %cl, %edi
-; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    cmovel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -282,7 +281,6 @@ define i32 @lshr_var_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
 ; CHECK-NEXT:    movl %ecx, %eax
 ; CHECK-NEXT:    orb $1, %sil, %cl
 ; CHECK-NEXT:    shrl %cl, %edi
-; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    cmovel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -299,7 +297,6 @@ define i32 @shl_var_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
 ; CHECK-NEXT:    movl %ecx, %eax
 ; CHECK-NEXT:    orb $1, %sil, %cl
 ; CHECK-NEXT:    shll %cl, %edi
-; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    cmovel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -315,7 +312,6 @@ define i32 @ashr_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    orb $1, %sil, %cl
 ; CHECK-NEXT:    shrl %cl, %edi, %eax
-; CHECK-NEXT:    testl %eax, %eax
 ; CHECK-NEXT:    cmovnel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -331,7 +327,6 @@ define i32 @lshr_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    orb $1, %sil, %cl
 ; CHECK-NEXT:    shrl %cl, %edi, %eax
-; CHECK-NEXT:    testl %eax, %eax
 ; CHECK-NEXT:    cmovnel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -347,7 +342,6 @@ define i32 @shl_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    orb $1, %sil, %cl
 ; CHECK-NEXT:    shrl %cl, %edi, %eax
-; CHECK-NEXT:    testl %eax, %eax
 ; CHECK-NEXT:    cmovnel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -356,3 +350,20 @@ define i32 @shl_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a
   %r = select i1 %c, i32 %s, i32 %a2
   ret i32 %r
 }
+
+define range(i8 0, 2) i8 @no_test_emitted_when_assume_shift_amt_gt_zero(i64 noundef %0, i32 noundef %1) {
+; CHECK-LABEL: no_test_emitted_when_assume_shift_amt_gt_zero:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movl %esi, %ecx
+; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
+; CHECK-NEXT:    shlq %cl, %rdi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    retq
+  %3 = icmp sgt i32 %1, 0
+  tail call void @llvm.assume(i1 %3)
+  %4 = zext nneg i32 %1 to i64
+  %5 = shl i64 %0, %4
+  %6 = icmp eq i64 %5, 0
+  %7 = zext i1 %6 to i8
+  ret i8 %7
+}
diff --git a/llvm/test/CodeGen/X86/shift-eflags.ll b/llvm/test/CodeGen/X86/shift-eflags.ll
index 6eddf50ce5c9d..badcf5470ab64 100644
--- a/llvm/test/CodeGen/X86/shift-eflags.ll
+++ b/llvm/test/CodeGen/X86/shift-eflags.ll
@@ -282,7 +282,6 @@ define i32 @ashr_var_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
 ; CHECK-NEXT:    orb $1, %cl
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
 ; CHECK-NEXT:    sarl %cl, %edi
-; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    cmovel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -301,7 +300,6 @@ define i32 @lshr_var_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
 ; CHECK-NEXT:    orb $1, %cl
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
 ; CHECK-NEXT:    shrl %cl, %edi
-; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    cmovel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -320,7 +318,6 @@ define i32 @shl_var_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
 ; CHECK-NEXT:    orb $1, %cl
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
 ; CHECK-NEXT:    shll %cl, %edi
-; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    cmovel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -339,7 +336,6 @@ define i32 @ashr_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %
 ; CHECK-NEXT:    orb $1, %cl
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
 ; CHECK-NEXT:    shrl %cl, %eax
-; CHECK-NEXT:    testl %eax, %eax
 ; CHECK-NEXT:    cmovnel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -358,7 +354,6 @@ define i32 @lshr_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %
 ; CHECK-NEXT:    orb $1, %cl
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
 ; CHECK-NEXT:    shrl %cl, %eax
-; CHECK-NEXT:    testl %eax, %eax
 ; CHECK-NEXT:    cmovnel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -377,7 +372,6 @@ define i32 @shl_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a
 ; CHECK-NEXT:    orb $1, %cl
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
 ; CHECK-NEXT:    shrl %cl, %eax
-; CHECK-NEXT:    testl %eax, %eax
 ; CHECK-NEXT:    cmovnel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -386,3 +380,20 @@ define i32 @shl_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a
   %r = select i1 %c, i32 %s, i32 %a2
   ret i32 %r
 }
+
+define range(i8 0, 2) i8 @no_test_emitted_when_assume_shift_amt_gt_zero(i64 noundef %0, i32 noundef %1) {
+; CHECK-LABEL: no_test_emitted_when_assume_shift_amt_gt_zero:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movl %esi, %ecx
+; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
+; CHECK-NEXT:    shlq %cl, %rdi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    retq
+  %3 = icmp sgt i32 %1, 0
+  tail call void @llvm.assume(i1 %3)
+  %4 = zext nneg i32 %1 to i64
+  %5 = shl i64 %0, %4
+  %6 = icmp eq i64 %5, 0
+  %7 = zext i1 %6 to i8
+  ret i8 %7
+}

>From 808c7713f9a2fe12224a366667bf4a8fa0f05081 Mon Sep 17 00:00:00 2001
From: GrumpyPigSkin <oliver61 at live.co.uk>
Date: Sat, 22 Nov 2025 21:49:04 +0000
Subject: [PATCH 07/10] [X86] Reverted change

---
 llvm/include/llvm/CodeGen/SelectionDAGNodes.h |  3 ---
 llvm/lib/CodeGen/CodeGenPrepare.cpp           | 26 +------------------
 .../SelectionDAG/SelectionDAGBuilder.cpp      | 20 --------------
 3 files changed, 1 insertion(+), 48 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h
index 082e7b4371d27..cfc8a4243e894 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h
@@ -427,7 +427,6 @@ struct SDNodeFlags {
     // implied by its producer as, e.g, operations between producer and PTRADD
     // that affect the provenance may have been optimized away.
     InBounds = 1 << 15,
-    NoZero = 1 << 16,
 
     // NOTE: Please update LargestValue in LLVM_DECLARE_ENUM_AS_BITMASK below
     // the class definition when adding new flags.
@@ -469,7 +468,6 @@ struct SDNodeFlags {
   void setNoFPExcept(bool b) { setFlag<NoFPExcept>(b); }
   void setUnpredictable(bool b) { setFlag<Unpredictable>(b); }
   void setInBounds(bool b) { setFlag<InBounds>(b); }
-  void setNoZero(bool b) { setFlag<NoZero>(b); }
 
   // These are accessors for each flag.
   bool hasNoUnsignedWrap() const { return Flags & NoUnsignedWrap; }
@@ -488,7 +486,6 @@ struct SDNodeFlags {
   bool hasNoFPExcept() const { return Flags & NoFPExcept; }
   bool hasUnpredictable() const { return Flags & Unpredictable; }
   bool hasInBounds() const { return Flags & InBounds; }
-  bool hasNoZero() const { return Flags & NoZero; }
 
   bool operator==(const SDNodeFlags &Other) const {
     return Flags == Other.Flags;
diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp
index b4b4879c515c5..587c1372b19cb 100644
--- a/llvm/lib/CodeGen/CodeGenPrepare.cpp
+++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp
@@ -786,32 +786,8 @@ bool CodeGenPrepare::eliminateAssumptions(Function &F) {
       if (auto *Assume = dyn_cast<AssumeInst>(I)) {
         MadeChange = true;
         Value *Operand = Assume->getOperand(0);
-        if (ICmpInst *Cmp = dyn_cast<ICmpInst>(Operand)) {
-          // Check if we are comparing an Argument against a Constant
-          if (Argument *Arg = dyn_cast<Argument>(Cmp->getOperand(0))) {
-            if (ConstantInt *C = dyn_cast<ConstantInt>(Cmp->getOperand(1))) {
-              // We found "assume(Arg <pred> Constant)"
-              ConstantRange Constraint = ConstantRange::makeAllowedICmpRegion(
-                  Cmp->getPredicate(), C->getValue());
-              // If the argument already has a range attribute, intersect with
-              // it
-              ConstantRange FinalRange = Constraint;
-              Attribute ExistingRangeAttr = Arg->getAttribute(Attribute::Range);
-              if (ExistingRangeAttr.isValid()) {
-                FinalRange =
-                    FinalRange.intersectWith(ExistingRangeAttr.getRange());
-              }
-              // If the range provides new information (and isn't empty), save
-              // it.
-              if (!FinalRange.isFullSet() && !FinalRange.isEmptySet()) {
-                AttrBuilder AB(Arg->getContext());
-                AB.addRangeAttr(FinalRange);
-                Arg->addAttrs(AB);
-              }
-            }
-          }
-        }
         Assume->eraseFromParent();
+
         resetIteratorIfInvalidatedWhileCalling(&BB, [&]() {
           RecursivelyDeleteTriviallyDeadInstructions(Operand, TLInfo, nullptr);
         });
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 51d3fc0d78975..985a54ca83256 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -3687,7 +3687,6 @@ void SelectionDAGBuilder::visitShift(const User &I, unsigned Opcode) {
   bool nuw = false;
   bool nsw = false;
   bool exact = false;
-  bool noZero = false;
 
   if (Opcode == ISD::SRL || Opcode == ISD::SRA || Opcode == ISD::SHL) {
 
@@ -3699,30 +3698,11 @@ void SelectionDAGBuilder::visitShift(const User &I, unsigned Opcode) {
     if (const PossiblyExactOperator *ExactOp =
             dyn_cast<const PossiblyExactOperator>(&I))
       exact = ExactOp->isExact();
-
-    const Value *ShiftAmt = I.getOperand(1);
-
-    // Look through zext as computeConstantRange does not do this.
-    const Value *InnerVal = ShiftAmt;
-    if (auto *ZExt = dyn_cast<ZExtInst>(ShiftAmt)) {
-      InnerVal = ZExt->getOperand(0);
-    }
-
-    // Get the constant range and check it excludes 0
-    ConstantRange CR = llvm::computeConstantRange(
-        InnerVal, true, true, AC, dyn_cast<Instruction>(&I), nullptr);
-
-    if (!CR.isEmptySet() && !CR.contains(APInt(CR.getBitWidth(), 0))) {
-      // We can guarantee that that we will not be shifted by 0
-      noZero = true;
-    }
   }
-
   SDNodeFlags Flags;
   Flags.setExact(exact);
   Flags.setNoSignedWrap(nsw);
   Flags.setNoUnsignedWrap(nuw);
-  Flags.setNoZero(noZero);
   SDValue Res = DAG.getNode(Opcode, getCurSDLoc(), Op1.getValueType(), Op1, Op2,
                             Flags);
   setValue(&I, Res);

>From e94fc1740fb88f7763786e750bc1f28be26dbf6b Mon Sep 17 00:00:00 2001
From: GrumpyPigSkin <oliver61 at live.co.uk>
Date: Sat, 22 Nov 2025 23:09:30 +0000
Subject: [PATCH 08/10] [X86] Changed so test instruction is removed only when
 targeting min size.

---
 llvm/lib/Target/X86/X86ISelLowering.cpp   |  6 +-
 llvm/test/CodeGen/X86/apx/shift-eflags.ll | 90 ++++++++++++++++++---
 llvm/test/CodeGen/X86/shift-eflags.ll     | 96 ++++++++++++++++++++---
 3 files changed, 170 insertions(+), 22 deletions(-)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 87e637ee4c272..48cddbbd408a2 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -23634,8 +23634,10 @@ static SDValue EmitTest(SDValue Op, X86::CondCode X86CC, const SDLoc &dl,
     if (isa<ConstantSDNode>(Amt))
       break;
 
-    // Check we can make this optimization
-    if (ArithOp->getFlags().hasNoZero() || DAG.isKnownNeverZero(Amt)) {
+    // If optimising for size and can guarantee the shift amt is never zero
+    // the test.
+    bool OptForSize = DAG.getMachineFunction().getFunction().hasOptSize();
+    if (OptForSize && DAG.isKnownNeverZero(Amt)) {
       SDLoc DL(ArithOp);
 
       // CopyToReg(CL, Amt)
diff --git a/llvm/test/CodeGen/X86/apx/shift-eflags.ll b/llvm/test/CodeGen/X86/apx/shift-eflags.ll
index 01f4689369eac..f36bdc311c727 100644
--- a/llvm/test/CodeGen/X86/apx/shift-eflags.ll
+++ b/llvm/test/CodeGen/X86/apx/shift-eflags.ll
@@ -265,6 +265,7 @@ define i32 @ashr_var_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
 ; CHECK-NEXT:    movl %ecx, %eax
 ; CHECK-NEXT:    orb $1, %sil, %cl
 ; CHECK-NEXT:    sarl %cl, %edi
+; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    cmovel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -281,6 +282,7 @@ define i32 @lshr_var_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
 ; CHECK-NEXT:    movl %ecx, %eax
 ; CHECK-NEXT:    orb $1, %sil, %cl
 ; CHECK-NEXT:    shrl %cl, %edi
+; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    cmovel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -297,6 +299,7 @@ define i32 @shl_var_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
 ; CHECK-NEXT:    movl %ecx, %eax
 ; CHECK-NEXT:    orb $1, %sil, %cl
 ; CHECK-NEXT:    shll %cl, %edi
+; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    cmovel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -312,6 +315,7 @@ define i32 @ashr_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    orb $1, %sil, %cl
 ; CHECK-NEXT:    shrl %cl, %edi, %eax
+; CHECK-NEXT:    testl %eax, %eax
 ; CHECK-NEXT:    cmovnel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -327,6 +331,7 @@ define i32 @lshr_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    orb $1, %sil, %cl
 ; CHECK-NEXT:    shrl %cl, %edi, %eax
+; CHECK-NEXT:    testl %eax, %eax
 ; CHECK-NEXT:    cmovnel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -342,6 +347,7 @@ define i32 @shl_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    orb $1, %sil, %cl
 ; CHECK-NEXT:    shrl %cl, %edi, %eax
+; CHECK-NEXT:    testl %eax, %eax
 ; CHECK-NEXT:    cmovnel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -351,19 +357,83 @@ define i32 @shl_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a
   ret i32 %r
 }
 
-define range(i8 0, 2) i8 @no_test_emitted_when_assume_shift_amt_gt_zero(i64 noundef %0, i32 noundef %1) {
-; CHECK-LABEL: no_test_emitted_when_assume_shift_amt_gt_zero:
+define i1 @shl_optsize_nonzero_removes_test(i32 %val, i32 %amt) optsize {
+; CHECK-LABEL: shl_optsize_nonzero_removes_test:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    orb $1, %sil, %cl
+; CHECK-NEXT:    shll %cl, %edi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    retq
+  %amt.nz = or i32 %amt, 1
+  %shl = shl i32 %val, %amt.nz
+  %cmp = icmp eq i32 %shl, 0
+  ret i1 %cmp
+}
+
+define i1 @shl_optsize_maybezero_keeps_test(i32 %val, i32 %amt) optsize {
+; CHECK-LABEL: shl_optsize_maybezero_keeps_test:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    movl %esi, %ecx
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
-; CHECK-NEXT:    shlq %cl, %rdi
+; CHECK-NEXT:    shll %cl, %edi
+; CHECK-NEXT:    testl %edi, %edi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    retq
+  %shl = shl i32 %val, %amt
+  %cmp = icmp eq i32 %shl, 0
+  ret i1 %cmp
+}
+
+define i1 @lshr_optsize_nonezero_removes_test(i32 %val, i32 %amt) optsize {
+; CHECK-LABEL: lshr_optsize_nonezero_removes_test:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    orb $1, %sil, %cl
+; CHECK-NEXT:    shrl %cl, %edi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    retq
+  %amt.nz = or i32 %amt, 1
+  %shr = lshr i32 %val, %amt.nz
+  %cmp = icmp eq i32 %shr, 0
+  ret i1 %cmp
+}
+
+define i1 @lshr_optsize_maybezero_keeps_test(i32 %val, i32 %amt) optsize {
+; CHECK-LABEL: lshr_optsize_maybezero_keeps_test:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movl %esi, %ecx
+; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
+; CHECK-NEXT:    shrl %cl, %edi
+; CHECK-NEXT:    testl %edi, %edi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    retq
+  %shr = lshr i32 %val, %amt
+  %cmp = icmp eq i32 %shr, 0
+  ret i1 %cmp
+}
+
+define i1 @ashr_optsize_nonezero_removes_test(i32 %val, i32 %amt) optsize {
+; CHECK-LABEL: ashr_optsize_nonezero_removes_test:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    orb $1, %sil, %cl
+; CHECK-NEXT:    sarl %cl, %edi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    retq
+  %amt.nz = or i32 %amt, 1
+  %sar = ashr i32 %val, %amt.nz
+  %cmp = icmp eq i32 %sar, 0
+  ret i1 %cmp
+}
+
+define i1 @ashr_optsize_maybezero_keeps_test(i32 %val, i32 %amt) optsize {
+; CHECK-LABEL: ashr_optsize_maybezero_keeps_test:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movl %esi, %ecx
+; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
+; CHECK-NEXT:    sarl %cl, %edi
+; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    sete %al
 ; CHECK-NEXT:    retq
-  %3 = icmp sgt i32 %1, 0
-  tail call void @llvm.assume(i1 %3)
-  %4 = zext nneg i32 %1 to i64
-  %5 = shl i64 %0, %4
-  %6 = icmp eq i64 %5, 0
-  %7 = zext i1 %6 to i8
-  ret i8 %7
+  %sar = ashr i32 %val, %amt
+  %cmp = icmp eq i32 %sar, 0
+  ret i1 %cmp
 }
diff --git a/llvm/test/CodeGen/X86/shift-eflags.ll b/llvm/test/CodeGen/X86/shift-eflags.ll
index badcf5470ab64..d2968e64b4aa0 100644
--- a/llvm/test/CodeGen/X86/shift-eflags.ll
+++ b/llvm/test/CodeGen/X86/shift-eflags.ll
@@ -282,6 +282,7 @@ define i32 @ashr_var_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
 ; CHECK-NEXT:    orb $1, %cl
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
 ; CHECK-NEXT:    sarl %cl, %edi
+; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    cmovel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -300,6 +301,7 @@ define i32 @lshr_var_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
 ; CHECK-NEXT:    orb $1, %cl
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
 ; CHECK-NEXT:    shrl %cl, %edi
+; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    cmovel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -318,6 +320,7 @@ define i32 @shl_var_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
 ; CHECK-NEXT:    orb $1, %cl
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
 ; CHECK-NEXT:    shll %cl, %edi
+; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    cmovel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -336,6 +339,7 @@ define i32 @ashr_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %
 ; CHECK-NEXT:    orb $1, %cl
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
 ; CHECK-NEXT:    shrl %cl, %eax
+; CHECK-NEXT:    testl %eax, %eax
 ; CHECK-NEXT:    cmovnel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -354,6 +358,7 @@ define i32 @lshr_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %
 ; CHECK-NEXT:    orb $1, %cl
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
 ; CHECK-NEXT:    shrl %cl, %eax
+; CHECK-NEXT:    testl %eax, %eax
 ; CHECK-NEXT:    cmovnel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -372,6 +377,7 @@ define i32 @shl_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a
 ; CHECK-NEXT:    orb $1, %cl
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
 ; CHECK-NEXT:    shrl %cl, %eax
+; CHECK-NEXT:    testl %eax, %eax
 ; CHECK-NEXT:    cmovnel %edx, %eax
 ; CHECK-NEXT:    retq
   %a = or i32 %a1, 1
@@ -381,19 +387,89 @@ define i32 @shl_var_self_select_amt_never_zero(i32 %a0, i32 %a1, i32 %a2, i32 %a
   ret i32 %r
 }
 
-define range(i8 0, 2) i8 @no_test_emitted_when_assume_shift_amt_gt_zero(i64 noundef %0, i32 noundef %1) {
-; CHECK-LABEL: no_test_emitted_when_assume_shift_amt_gt_zero:
+define i1 @shl_optsize_nonzero_removes_test(i32 %val, i32 %amt) optsize {
+; CHECK-LABEL: shl_optsize_nonzero_removes_test:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    movl %esi, %ecx
+; CHECK-NEXT:    orb $1, %cl
 ; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
-; CHECK-NEXT:    shlq %cl, %rdi
+; CHECK-NEXT:    shll %cl, %edi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    retq
+  %amt.nz = or i32 %amt, 1
+  %shl = shl i32 %val, %amt.nz
+  %cmp = icmp eq i32 %shl, 0
+  ret i1 %cmp
+}
+
+define i1 @shl_optsize_maybezero_keeps_test(i32 %val, i32 %amt) optsize {
+; CHECK-LABEL: shl_optsize_maybezero_keeps_test:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movl %esi, %ecx
+; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
+; CHECK-NEXT:    shll %cl, %edi
+; CHECK-NEXT:    testl %edi, %edi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    retq
+  %shl = shl i32 %val, %amt
+  %cmp = icmp eq i32 %shl, 0
+  ret i1 %cmp
+}
+
+define i1 @lshr_optsize_nonezero_removes_test(i32 %val, i32 %amt) optsize {
+; CHECK-LABEL: lshr_optsize_nonezero_removes_test:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movl %esi, %ecx
+; CHECK-NEXT:    orb $1, %cl
+; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
+; CHECK-NEXT:    shrl %cl, %edi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    retq
+  %amt.nz = or i32 %amt, 1
+  %shr = lshr i32 %val, %amt.nz
+  %cmp = icmp eq i32 %shr, 0
+  ret i1 %cmp
+}
+
+define i1 @lshr_optsize_maybezero_keeps_test(i32 %val, i32 %amt) optsize {
+; CHECK-LABEL: lshr_optsize_maybezero_keeps_test:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movl %esi, %ecx
+; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
+; CHECK-NEXT:    shrl %cl, %edi
+; CHECK-NEXT:    testl %edi, %edi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    retq
+  %shr = lshr i32 %val, %amt
+  %cmp = icmp eq i32 %shr, 0
+  ret i1 %cmp
+}
+
+define i1 @ashr_optsize_nonezero_removes_test(i32 %val, i32 %amt) optsize {
+; CHECK-LABEL: ashr_optsize_nonezero_removes_test:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movl %esi, %ecx
+; CHECK-NEXT:    orb $1, %cl
+; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
+; CHECK-NEXT:    sarl %cl, %edi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    retq
+  %amt.nz = or i32 %amt, 1
+  %sar = ashr i32 %val, %amt.nz
+  %cmp = icmp eq i32 %sar, 0
+  ret i1 %cmp
+}
+
+define i1 @ashr_optsize_maybezero_keeps_test(i32 %val, i32 %amt) optsize {
+; CHECK-LABEL: ashr_optsize_maybezero_keeps_test:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movl %esi, %ecx
+; CHECK-NEXT:    # kill: def $cl killed $cl killed $ecx
+; CHECK-NEXT:    sarl %cl, %edi
+; CHECK-NEXT:    testl %edi, %edi
 ; CHECK-NEXT:    sete %al
 ; CHECK-NEXT:    retq
-  %3 = icmp sgt i32 %1, 0
-  tail call void @llvm.assume(i1 %3)
-  %4 = zext nneg i32 %1 to i64
-  %5 = shl i64 %0, %4
-  %6 = icmp eq i64 %5, 0
-  %7 = zext i1 %6 to i8
-  ret i8 %7
+  %sar = ashr i32 %val, %amt
+  %cmp = icmp eq i32 %sar, 0
+  ret i1 %cmp
 }

>From 95fadc5eaed7432651761a127c82c1e478d16033 Mon Sep 17 00:00:00 2001
From: GrumpyPigSkin <oliver61 at live.co.uk>
Date: Mon, 24 Nov 2025 21:46:49 +0000
Subject: [PATCH 09/10] [X86] Changes made for code review comments

---
 llvm/lib/Target/X86/X86ISelLowering.cpp    | 64 ++++++++++++----------
 llvm/lib/Target/X86/X86ISelLowering.h      |  6 +-
 llvm/lib/Target/X86/X86InstrFragments.td   |  9 +++
 llvm/lib/Target/X86/X86InstrShiftRotate.td |  9 ---
 4 files changed, 46 insertions(+), 42 deletions(-)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 48cddbbd408a2..284e3e5853dad 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -23637,43 +23637,47 @@ static SDValue EmitTest(SDValue Op, X86::CondCode X86CC, const SDLoc &dl,
     // If optimising for size and can guarantee the shift amt is never zero
     // the test.
     bool OptForSize = DAG.getMachineFunction().getFunction().hasOptSize();
-    if (OptForSize && DAG.isKnownNeverZero(Amt)) {
-      SDLoc DL(ArithOp);
 
-      // CopyToReg(CL, Amt)
-      SDValue Chain = DAG.getEntryNode();
-      SDValue Glue;
+    if (!OptForSize)
+      break;
 
-      Chain = DAG.getCopyToReg(Chain, DL, X86::CL, Amt, Glue);
-      Glue = Chain.getValue(1);
+    if (!DAG.isKnownNeverZero(Amt))
+      break;
 
-      // Select Opcode
-      unsigned X86Opcode;
-      switch (ArithOp.getOpcode()) {
-      case ISD::SHL:
-        X86Opcode = X86ISD::SHL;
-        break;
-      case ISD::SRL:
-        X86Opcode = X86ISD::SRL;
-        break;
-      case ISD::SRA:
-        X86Opcode = X86ISD::SRA;
-        break;
-      default:
-        llvm_unreachable("Unexpected shift opcode");
-      }
+    SDLoc DL(ArithOp);
 
-      // Create Node [ValueToShift, Glue]
-      SDVTList VTs = DAG.getVTList(ArithOp.getValueType(), MVT::i32);
-      SDValue Ops[] = {ArithOp.getOperand(0), Glue};
+    // CopyToReg(CL, Amt)
+    SDValue Chain = DAG.getEntryNode();
+    SDValue Glue;
 
-      SDValue NewNode = DAG.getNode(X86Opcode, DL, VTs, Ops);
+    Chain = DAG.getCopyToReg(Chain, DL, X86::CL, Amt, Glue);
+    Glue = Chain.getValue(1);
 
-      // Replace and Return
-      DAG.ReplaceAllUsesOfValueWith(ArithOp, NewNode.getValue(0));
-      return NewNode.getValue(1);
+    // Select Opcode
+    unsigned X86Opcode;
+    switch (ArithOp.getOpcode()) {
+    case ISD::SHL:
+      X86Opcode = X86ISD::SHL;
+      break;
+    case ISD::SRL:
+      X86Opcode = X86ISD::SRL;
+      break;
+    case ISD::SRA:
+      X86Opcode = X86ISD::SRA;
+      break;
+    default:
+      llvm_unreachable("Unexpected shift opcode");
     }
-    break;
+
+    // Create Node [ValueToShift, Glue]
+    SDVTList VTs = DAG.getVTList(ArithOp.getValueType(), MVT::i32);
+    SDValue Ops[] = {ArithOp.getOperand(0), Glue};
+
+    SDValue NewNode = DAG.getNode(X86Opcode, DL, VTs, Ops);
+
+    // Replace and Return
+    DAG.ReplaceAllUsesOfValueWith(ArithOp, NewNode.getValue(0));
+    return NewNode.getValue(1);
   }
   default:
     break;
diff --git a/llvm/lib/Target/X86/X86ISelLowering.h b/llvm/lib/Target/X86/X86ISelLowering.h
index 00830804213f7..af8a63f120226 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.h
+++ b/llvm/lib/Target/X86/X86ISelLowering.h
@@ -425,6 +425,9 @@ namespace llvm {
     OR,
     XOR,
     AND,
+    SHL,
+    SRL,
+    SRA,
 
     // Bit field extract.
     BEXTR,
@@ -996,9 +999,6 @@ namespace llvm {
     CLOAD,
     CSTORE,
     LAST_MEMORY_OPCODE = CSTORE,
-    SHL,
-    SRL,
-    SRA,
   };
   } // end namespace X86ISD
 
diff --git a/llvm/lib/Target/X86/X86InstrFragments.td b/llvm/lib/Target/X86/X86InstrFragments.td
index 116986a0fffea..3ea71ce24c7f7 100644
--- a/llvm/lib/Target/X86/X86InstrFragments.td
+++ b/llvm/lib/Target/X86/X86InstrFragments.td
@@ -276,6 +276,15 @@ def X86xor_flag  : SDNode<"X86ISD::XOR",  SDTBinaryArithWithFlags,
 def X86and_flag  : SDNode<"X86ISD::AND",  SDTBinaryArithWithFlags,
                           [SDNPCommutative]>;
 
+def SDTX86ShiftWithFlags : SDTypeProfile<2, 1, [
+  SDTCisSameAs<0, 2>,
+  SDTCisVT<1, i32>,
+]>;
+
+def X86shl : SDNode<"X86ISD::SHL", SDTX86ShiftWithFlags, [SDNPInGlue]>;
+def X86srl : SDNode<"X86ISD::SRL", SDTX86ShiftWithFlags, [SDNPInGlue]>;
+def X86sra : SDNode<"X86ISD::SRA", SDTX86ShiftWithFlags, [SDNPInGlue]>;
+
 def X86lock_add  : SDNode<"X86ISD::LADD",  SDTLockBinaryArithWithFlags,
                           [SDNPHasChain, SDNPMayStore, SDNPMayLoad,
                            SDNPMemOperand]>;
diff --git a/llvm/lib/Target/X86/X86InstrShiftRotate.td b/llvm/lib/Target/X86/X86InstrShiftRotate.td
index e81bc33d3a459..7a2cd3101b342 100644
--- a/llvm/lib/Target/X86/X86InstrShiftRotate.td
+++ b/llvm/lib/Target/X86/X86InstrShiftRotate.td
@@ -690,15 +690,6 @@ let Predicates = [HasBMI2, HasEGPR] in {
   defm SHLX : ShiftX_Pats<shl, "_EVEX">;
 }
 
-def SDTX86ShiftWithFlags : SDTypeProfile<2, 1, [
-  SDTCisSameAs<0, 2>,
-  SDTCisVT<1, i32>,
-]>;
-
-def X86shl : SDNode<"X86ISD::SHL", SDTX86ShiftWithFlags, [SDNPInGlue]>;
-def X86srl : SDNode<"X86ISD::SRL", SDTX86ShiftWithFlags, [SDNPInGlue]>;
-def X86sra : SDNode<"X86ISD::SRA", SDTX86ShiftWithFlags, [SDNPInGlue]>;
-
 let Predicates = [NoNDD] in {
   // SHL
   def : Pat<(X86shl GR8:$src1),  (SHL8rCL  GR8:$src1)>;

>From 93a699f85e5cbdf6668962cc32e6d58a8ca4884c Mon Sep 17 00:00:00 2001
From: GrumpyPigSkin <oliver61 at live.co.uk>
Date: Tue, 25 Nov 2025 18:51:43 +0000
Subject: [PATCH 10/10] [X86] Additional code review comments

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

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 284e3e5853dad..b02c6d16337c0 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -23636,9 +23636,7 @@ static SDValue EmitTest(SDValue Op, X86::CondCode X86CC, const SDLoc &dl,
 
     // If optimising for size and can guarantee the shift amt is never zero
     // the test.
-    bool OptForSize = DAG.getMachineFunction().getFunction().hasOptSize();
-
-    if (!OptForSize)
+    if (!DAG.getMachineFunction().getFunction().hasOptSize())
       break;
 
     if (!DAG.isKnownNeverZero(Amt))
@@ -35223,6 +35221,9 @@ const char *X86TargetLowering::getTargetNodeName(unsigned Opcode) const {
   NODE_NAME_CASE(OR)
   NODE_NAME_CASE(XOR)
   NODE_NAME_CASE(AND)
+  NODE_NAME_CASE(SHL)
+  NODE_NAME_CASE(SRL)
+  NODE_NAME_CASE(SRA)
   NODE_NAME_CASE(BEXTR)
   NODE_NAME_CASE(BEXTRI)
   NODE_NAME_CASE(BZHI)
@@ -35521,9 +35522,6 @@ const char *X86TargetLowering::getTargetNodeName(unsigned Opcode) const {
   NODE_NAME_CASE(CVTTP2UIS)
   NODE_NAME_CASE(MCVTTP2UIS)
   NODE_NAME_CASE(POP_FROM_X87_REG)
-  NODE_NAME_CASE(SHL)
-  NODE_NAME_CASE(SRL)
-  NODE_NAME_CASE(SRA)
   }
   return nullptr;
 #undef NODE_NAME_CASE



More information about the llvm-commits mailing list