[llvm] [GlobalISel] [AArch64] Skip redundant shift amount masking during isel (PR #223136)

Deepak Shirke via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 30 03:36:20 PDT 2026


https://github.com/deepakshirkem updated https://github.com/llvm/llvm-project/pull/223136

>From 65b95b341f0a8bf7fab0d7dd37fb2da8bf6a35a0 Mon Sep 17 00:00:00 2001
From: deepakshirkem <deepakshirke509 at gmail.com>
Date: Mon, 14 Sep 2026 18:14:17 +0530
Subject: [PATCH 1/2] AArch64: Skip redundant shift amount masking using isel
 ComplexPattern

AArch64 shift instructions (LSL/LSR/ASR) only use the low 5 bits (i32)
or 6 bits (i64) of the shift amount. Add SelectShiftMask ComplexPattern
and GIComplexOperandMatcher that strip redundant AND masks or zero-extensions
from shift amounts during instruction selection, when the mask exactly
covers a narrow type (byte, halfword, or word).

This mirrors what AArch64DAGToDAGISel::tryShiftAmountMod does, and adds
equivalent GlobalISel support via GIComplexPatternEquiv.
---
 .../Target/AArch64/AArch64ISelDAGToDAG.cpp    | 21 ++++
 .../lib/Target/AArch64/AArch64InstrFormats.td | 15 +++
 .../GISel/AArch64InstructionSelector.cpp      | 41 ++++++++
 ...st-and-by-const-from-lshr-in-eqcmp-zero.ll | 47 +++------
 ...ist-and-by-const-from-shl-in-eqcmp-zero.ll | 24 ++---
 llvm/test/CodeGen/AArch64/select_const.ll     |  3 -
 llvm/test/CodeGen/AArch64/shift-mod.ll        | 74 ++++++++++++++
 llvm/test/CodeGen/AArch64/shift.ll            | 96 ++++++-------------
 8 files changed, 202 insertions(+), 119 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index feb8f465837940..9d5722e1961734 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -89,6 +89,9 @@ class AArch64DAGToDAGISel : public SelectionDAGISel {
   bool SelectLogicalShiftedRegister(SDValue N, SDValue &Reg, SDValue &Shift) {
     return SelectShiftedRegister(N, true, Reg, Shift);
   }
+  template <unsigned ShiftWidth>
+  bool SelectShiftMask(SDValue N, SDValue &ShAmt);
+
   bool SelectAddrModeIndexed7S8(SDValue N, SDValue &Base, SDValue &OffImm) {
     return SelectAddrModeIndexed7S(N, 1, Base, OffImm);
   }
@@ -766,6 +769,24 @@ bool AArch64DAGToDAGISel::SelectInlineAsmMemoryOperand(
   return true;
 }
 
+template <unsigned ShiftWidth>
+bool AArch64DAGToDAGISel::SelectShiftMask(SDValue N, SDValue &ShAmt) {
+  // AArch64 shift instructions only use the low log2(ShiftWidth) bits of the
+  // shift amount. If the shift amount has a redundant AND mask that covers
+  // those bits, we can remove it. Return false if nothing was combined so
+  // other patterns (e.g. zext/sext GPR32 → SUBREG_TO_REG) can match.
+  if (N.getOpcode() == ISD::AND && isa<ConstantSDNode>(N.getOperand(1)) &&
+      N.getValueType() == (ShiftWidth == 32 ? MVT::i32 : MVT::i64)) {
+    uint64_t Mask = N.getConstantOperandVal(1);
+    // Remove AND if the mask covers the low log2(ShiftWidth) bits.
+    if ((Mask & (ShiftWidth - 1)) == (ShiftWidth - 1)) {
+      ShAmt = N.getOperand(0);
+      return true;
+    }
+  }
+  return false;
+}
+
 /// SelectArithImmed - Select an immediate value that can be represented as
 /// a 12-bit value shifted left by either 0 or 12.  If so, return true with
 /// Val set to the 12-bit value and Shift set to the shifter operand.
diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
index ea5164c9569928..27d2a391aa3fae 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
@@ -2749,6 +2749,16 @@ class BaseShift<bit size, bits<2> shift_type, RegisterClass regtype, string asm,
   let Inst{11-10} = shift_type;
 }
 
+// ComplexPattern to match shift amounts with redundant AND masks.
+// AArch64 shift instructions only use the low 5 bits (i32) or 6 bits (i64),
+// so AND masks that exactly cover a narrow type are redundant.
+def shiftMask32 : ComplexPattern<i32, 1, "SelectShiftMask<32>", [], [], 0>;
+def gi_shift_mask32 : GIComplexOperandMatcher<s32, "selectShiftMask<32>">,
+                      GIComplexPatternEquiv<shiftMask32>;
+def shiftMask64 : ComplexPattern<i64, 1, "SelectShiftMask<64>", [], [], 0>;
+def gi_shift_mask64 : GIComplexOperandMatcher<s64, "selectShiftMask<64>">,
+                      GIComplexPatternEquiv<shiftMask64>;
+
 multiclass Shift<bits<2> shift_type, string asm, SDNode OpNode> {
   def Wr : BaseShift<0b0, shift_type, GPR32, asm>;
 
@@ -2774,6 +2784,11 @@ multiclass Shift<bits<2> shift_type, string asm, SDNode OpNode> {
   def : Pat<(i64 (OpNode GPR64:$Rn, (i64 (zext GPR32:$Rm)))),
             (!cast<Instruction>(NAME # "Xr") GPR64:$Rn,
                 (SUBREG_TO_REG GPR32:$Rm, sub_32))>;
+
+  def : Pat<(i32 (OpNode GPR32:$Rn, shiftMask32:$Rm)),
+            (!cast<Instruction>(NAME # "Wr") GPR32:$Rn, shiftMask32:$Rm)>;
+  def : Pat<(i64 (OpNode GPR64:$Rn, shiftMask64:$Rm)),
+            (!cast<Instruction>(NAME # "Xr") GPR64:$Rn, shiftMask64:$Rm)>;
 }
 
 class ShiftAlias<string asm, Instruction inst, RegisterClass regtype>
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
index f42e1864d41b54..5073b52b2a3c51 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
@@ -384,6 +384,8 @@ class AArch64InstructionSelector : public InstructionSelector {
   ComplexRendererFns selectShiftA_64(const MachineOperand &Root) const;
   ComplexRendererFns selectShiftB_64(const MachineOperand &Root) const;
 
+  template <unsigned ShiftWidth>
+  ComplexRendererFns selectShiftMask(MachineOperand &Root) const;
   ComplexRendererFns select12BitValueWithLeftShift(uint64_t Immed) const;
   ComplexRendererFns selectArithImmed(MachineOperand &Root) const;
   ComplexRendererFns selectNegArithImmed(MachineOperand &Root) const;
@@ -7301,6 +7303,45 @@ AArch64InstructionSelector::selectShiftB_64(const MachineOperand &Root) const {
   return {{[=](MachineInstrBuilder &MIB) { MIB.addImm(Enc); }}};
 }
 
+template <unsigned ShiftWidth>
+InstructionSelector::ComplexRendererFns
+AArch64InstructionSelector::selectShiftMask(MachineOperand &Root) const {
+  if (!Root.isReg())
+    return std::nullopt;
+
+  MachineRegisterInfo &MRI =
+      Root.getParent()->getParent()->getParent()->getRegInfo();
+
+  Register ShAmtReg = Root.getReg();
+
+  // Peek through zext for i32 shifts only. For i64 shifts the zext case
+  // is already handled by existing patterns in the Shift multiclass.
+  if (ShiftWidth == 32) {
+    Register ZExtSrcReg;
+    if (mi_match(ShAmtReg, MRI, m_GZExt(m_Reg(ZExtSrcReg))))
+      ShAmtReg = ZExtSrcReg;
+  }
+
+  // Remove redundant AND mask that exactly covers a narrow type (byte,
+  // halfword, word) and whose result type matches the shift width. This
+  // avoids removing intentional masks used in fshl/fshr computations.
+  APInt AndMask;
+  Register AndSrcReg;
+  if (mi_match(ShAmtReg, MRI, m_GAnd(m_Reg(AndSrcReg), m_ICst(AndMask))) &&
+      MRI.getType(ShAmtReg).getSizeInBits() == ShiftWidth) {
+    uint64_t UMask = AndMask.getZExtValue();
+    if (UMask == 0xff || UMask == 0xffff || UMask == 0xffffffff)
+      ShAmtReg = AndSrcReg;
+  }
+
+  // Only succeed if we changed the shift amount; otherwise let other
+  // patterns (e.g. zext GPR32 -> SUBREG_TO_REG) match instead.
+  if (ShAmtReg == Root.getReg())
+    return std::nullopt;
+
+  return {{[=](MachineInstrBuilder &MIB) { MIB.addReg(ShAmtReg); }}};
+}
+
 /// Helper to select an immediate value that can be represented as a 12-bit
 /// value shifted left by either 0 or 12. If it is possible to do so, return
 /// the immediate and shift value. If not, return std::nullopt.
diff --git a/llvm/test/CodeGen/AArch64/hoist-and-by-const-from-lshr-in-eqcmp-zero.ll b/llvm/test/CodeGen/AArch64/hoist-and-by-const-from-lshr-in-eqcmp-zero.ll
index 7d8ff9ac11e307..78efedb0c93170 100644
--- a/llvm/test/CodeGen/AArch64/hoist-and-by-const-from-lshr-in-eqcmp-zero.ll
+++ b/llvm/test/CodeGen/AArch64/hoist-and-by-const-from-lshr-in-eqcmp-zero.ll
@@ -24,8 +24,7 @@ define i1 @scalar_i8_signbit_eq(i8 %x, i8 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i8_signbit_eq:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #128 // =0x80
-; CHECK-GI-NEXT:    and w9, w1, #0xff
-; CHECK-GI-NEXT:    lsr w8, w8, w9
+; CHECK-GI-NEXT:    lsr w8, w8, w1
 ; CHECK-GI-NEXT:    tst w8, w0
 ; CHECK-GI-NEXT:    cset w0, eq
 ; CHECK-GI-NEXT:    ret
@@ -46,8 +45,7 @@ define i1 @scalar_i8_lowestbit_eq(i8 %x, i8 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i8_lowestbit_eq:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #1 // =0x1
-; CHECK-GI-NEXT:    and w9, w1, #0xff
-; CHECK-GI-NEXT:    lsr w8, w8, w9
+; CHECK-GI-NEXT:    lsr w8, w8, w1
 ; CHECK-GI-NEXT:    tst w8, w0
 ; CHECK-GI-NEXT:    cset w0, eq
 ; CHECK-GI-NEXT:    ret
@@ -68,8 +66,7 @@ define i1 @scalar_i8_bitsinmiddle_eq(i8 %x, i8 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i8_bitsinmiddle_eq:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #24 // =0x18
-; CHECK-GI-NEXT:    and w9, w1, #0xff
-; CHECK-GI-NEXT:    lsr w8, w8, w9
+; CHECK-GI-NEXT:    lsr w8, w8, w1
 ; CHECK-GI-NEXT:    tst w8, w0
 ; CHECK-GI-NEXT:    cset w0, eq
 ; CHECK-GI-NEXT:    ret
@@ -92,8 +89,7 @@ define i1 @scalar_i16_signbit_eq(i16 %x, i16 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i16_signbit_eq:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #32768 // =0x8000
-; CHECK-GI-NEXT:    and w9, w1, #0xffff
-; CHECK-GI-NEXT:    lsr w8, w8, w9
+; CHECK-GI-NEXT:    lsr w8, w8, w1
 ; CHECK-GI-NEXT:    tst w8, w0
 ; CHECK-GI-NEXT:    cset w0, eq
 ; CHECK-GI-NEXT:    ret
@@ -114,8 +110,7 @@ define i1 @scalar_i16_lowestbit_eq(i16 %x, i16 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i16_lowestbit_eq:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #1 // =0x1
-; CHECK-GI-NEXT:    and w9, w1, #0xffff
-; CHECK-GI-NEXT:    lsr w8, w8, w9
+; CHECK-GI-NEXT:    lsr w8, w8, w1
 ; CHECK-GI-NEXT:    tst w8, w0
 ; CHECK-GI-NEXT:    cset w0, eq
 ; CHECK-GI-NEXT:    ret
@@ -136,8 +131,7 @@ define i1 @scalar_i16_bitsinmiddle_eq(i16 %x, i16 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i16_bitsinmiddle_eq:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #4080 // =0xff0
-; CHECK-GI-NEXT:    and w9, w1, #0xffff
-; CHECK-GI-NEXT:    lsr w8, w8, w9
+; CHECK-GI-NEXT:    lsr w8, w8, w1
 ; CHECK-GI-NEXT:    tst w8, w0
 ; CHECK-GI-NEXT:    cset w0, eq
 ; CHECK-GI-NEXT:    ret
@@ -425,8 +419,7 @@ define i1 @scalar_i8_signbit_ne(i8 %x, i8 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i8_signbit_ne:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #128 // =0x80
-; CHECK-GI-NEXT:    and w9, w1, #0xff
-; CHECK-GI-NEXT:    lsr w8, w8, w9
+; CHECK-GI-NEXT:    lsr w8, w8, w1
 ; CHECK-GI-NEXT:    tst w8, w0
 ; CHECK-GI-NEXT:    cset w0, ne
 ; CHECK-GI-NEXT:    ret
@@ -480,24 +473,14 @@ define i1 @scalar_i8_bitsinmiddle_slt(i8 %x, i8 %y) nounwind {
 }
 
 define i1 @scalar_i8_signbit_eq_with_nonzero(i8 %x, i8 %y) nounwind {
-; CHECK-SD-LABEL: scalar_i8_signbit_eq_with_nonzero:
-; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    mov w8, #128 // =0x80
-; CHECK-SD-NEXT:    lsr w8, w8, w1
-; CHECK-SD-NEXT:    and w8, w8, w0
-; CHECK-SD-NEXT:    cmp w8, #1
-; CHECK-SD-NEXT:    cset w0, eq
-; CHECK-SD-NEXT:    ret
-;
-; CHECK-GI-LABEL: scalar_i8_signbit_eq_with_nonzero:
-; CHECK-GI:       // %bb.0:
-; CHECK-GI-NEXT:    mov w8, #128 // =0x80
-; CHECK-GI-NEXT:    and w9, w1, #0xff
-; CHECK-GI-NEXT:    lsr w8, w8, w9
-; CHECK-GI-NEXT:    and w8, w8, w0
-; CHECK-GI-NEXT:    cmp w8, #1
-; CHECK-GI-NEXT:    cset w0, eq
-; CHECK-GI-NEXT:    ret
+; CHECK-LABEL: scalar_i8_signbit_eq_with_nonzero:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #128 // =0x80
+; CHECK-NEXT:    lsr w8, w8, w1
+; CHECK-NEXT:    and w8, w8, w0
+; CHECK-NEXT:    cmp w8, #1
+; CHECK-NEXT:    cset w0, eq
+; CHECK-NEXT:    ret
   %t0 = lshr i8 128, %y
   %t1 = and i8 %t0, %x
   %res = icmp eq i8 %t1, 1 ; should be comparing with 0
diff --git a/llvm/test/CodeGen/AArch64/hoist-and-by-const-from-shl-in-eqcmp-zero.ll b/llvm/test/CodeGen/AArch64/hoist-and-by-const-from-shl-in-eqcmp-zero.ll
index f61e4303ca2be8..b55efeb580ee73 100644
--- a/llvm/test/CodeGen/AArch64/hoist-and-by-const-from-shl-in-eqcmp-zero.ll
+++ b/llvm/test/CodeGen/AArch64/hoist-and-by-const-from-shl-in-eqcmp-zero.ll
@@ -25,8 +25,7 @@ define i1 @scalar_i8_signbit_eq(i8 %x, i8 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i8_signbit_eq:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #-128 // =0xffffff80
-; CHECK-GI-NEXT:    and w9, w1, #0xff
-; CHECK-GI-NEXT:    lsl w8, w8, w9
+; CHECK-GI-NEXT:    lsl w8, w8, w1
 ; CHECK-GI-NEXT:    and w8, w8, w0
 ; CHECK-GI-NEXT:    tst w8, #0xff
 ; CHECK-GI-NEXT:    cset w0, eq
@@ -49,8 +48,7 @@ define i1 @scalar_i8_lowestbit_eq(i8 %x, i8 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i8_lowestbit_eq:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #1 // =0x1
-; CHECK-GI-NEXT:    and w9, w1, #0xff
-; CHECK-GI-NEXT:    lsl w8, w8, w9
+; CHECK-GI-NEXT:    lsl w8, w8, w1
 ; CHECK-GI-NEXT:    and w8, w8, w0
 ; CHECK-GI-NEXT:    tst w8, #0xff
 ; CHECK-GI-NEXT:    cset w0, eq
@@ -73,8 +71,7 @@ define i1 @scalar_i8_bitsinmiddle_eq(i8 %x, i8 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i8_bitsinmiddle_eq:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #24 // =0x18
-; CHECK-GI-NEXT:    and w9, w1, #0xff
-; CHECK-GI-NEXT:    lsl w8, w8, w9
+; CHECK-GI-NEXT:    lsl w8, w8, w1
 ; CHECK-GI-NEXT:    and w8, w8, w0
 ; CHECK-GI-NEXT:    tst w8, #0xff
 ; CHECK-GI-NEXT:    cset w0, eq
@@ -99,8 +96,7 @@ define i1 @scalar_i16_signbit_eq(i16 %x, i16 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i16_signbit_eq:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #-32768 // =0xffff8000
-; CHECK-GI-NEXT:    and w9, w1, #0xffff
-; CHECK-GI-NEXT:    lsl w8, w8, w9
+; CHECK-GI-NEXT:    lsl w8, w8, w1
 ; CHECK-GI-NEXT:    and w8, w8, w0
 ; CHECK-GI-NEXT:    tst w8, #0xffff
 ; CHECK-GI-NEXT:    cset w0, eq
@@ -123,8 +119,7 @@ define i1 @scalar_i16_lowestbit_eq(i16 %x, i16 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i16_lowestbit_eq:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #1 // =0x1
-; CHECK-GI-NEXT:    and w9, w1, #0xffff
-; CHECK-GI-NEXT:    lsl w8, w8, w9
+; CHECK-GI-NEXT:    lsl w8, w8, w1
 ; CHECK-GI-NEXT:    and w8, w8, w0
 ; CHECK-GI-NEXT:    tst w8, #0xffff
 ; CHECK-GI-NEXT:    cset w0, eq
@@ -147,8 +142,7 @@ define i1 @scalar_i16_bitsinmiddle_eq(i16 %x, i16 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i16_bitsinmiddle_eq:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #4080 // =0xff0
-; CHECK-GI-NEXT:    and w9, w1, #0xffff
-; CHECK-GI-NEXT:    lsl w8, w8, w9
+; CHECK-GI-NEXT:    lsl w8, w8, w1
 ; CHECK-GI-NEXT:    and w8, w8, w0
 ; CHECK-GI-NEXT:    tst w8, #0xffff
 ; CHECK-GI-NEXT:    cset w0, eq
@@ -430,8 +424,7 @@ define i1 @scalar_i8_signbit_ne(i8 %x, i8 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i8_signbit_ne:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #-128 // =0xffffff80
-; CHECK-GI-NEXT:    and w9, w1, #0xff
-; CHECK-GI-NEXT:    lsl w8, w8, w9
+; CHECK-GI-NEXT:    lsl w8, w8, w1
 ; CHECK-GI-NEXT:    and w8, w8, w0
 ; CHECK-GI-NEXT:    tst w8, #0xff
 ; CHECK-GI-NEXT:    cset w0, ne
@@ -488,8 +481,7 @@ define i1 @scalar_i8_bitsinmiddle_slt(i8 %x, i8 %y) nounwind {
 ; CHECK-GI-LABEL: scalar_i8_bitsinmiddle_slt:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    mov w8, #24 // =0x18
-; CHECK-GI-NEXT:    and w9, w1, #0xff
-; CHECK-GI-NEXT:    lsl w8, w8, w9
+; CHECK-GI-NEXT:    lsl w8, w8, w1
 ; CHECK-GI-NEXT:    and w8, w8, w0
 ; CHECK-GI-NEXT:    sxtb w8, w8
 ; CHECK-GI-NEXT:    cmp w8, #0
diff --git a/llvm/test/CodeGen/AArch64/select_const.ll b/llvm/test/CodeGen/AArch64/select_const.ll
index daa9971dbcd58f..c67d527dbfed11 100644
--- a/llvm/test/CodeGen/AArch64/select_const.ll
+++ b/llvm/test/CodeGen/AArch64/select_const.ll
@@ -672,7 +672,6 @@ define i8 @shl_constant_sel_constants(i1 %cond) {
 ; CHECK-GI-NEXT:    sbfx w9, w0, #0, #1
 ; CHECK-GI-NEXT:    mov w8, #1 // =0x1
 ; CHECK-GI-NEXT:    add w9, w9, #3
-; CHECK-GI-NEXT:    and w9, w9, #0xff
 ; CHECK-GI-NEXT:    lsl w0, w8, w9
 ; CHECK-GI-NEXT:    ret
   %sel = select i1 %cond, i8 2, i8 3
@@ -714,7 +713,6 @@ define i8 @lshr_constant_sel_constants(i1 %cond) {
 ; CHECK-GI-NEXT:    sbfx w9, w0, #0, #1
 ; CHECK-GI-NEXT:    mov w8, #64 // =0x40
 ; CHECK-GI-NEXT:    add w9, w9, #3
-; CHECK-GI-NEXT:    and w9, w9, #0xff
 ; CHECK-GI-NEXT:    lsr w0, w8, w9
 ; CHECK-GI-NEXT:    ret
   %sel = select i1 %cond, i8 2, i8 3
@@ -747,7 +745,6 @@ define i8 @ashr_constant_sel_constants(i1 %cond) {
 ; CHECK-GI-NEXT:    sbfx w9, w0, #0, #1
 ; CHECK-GI-NEXT:    mov w8, #-128 // =0xffffff80
 ; CHECK-GI-NEXT:    add w9, w9, #3
-; CHECK-GI-NEXT:    and w9, w9, #0xff
 ; CHECK-GI-NEXT:    asr w0, w8, w9
 ; CHECK-GI-NEXT:    ret
   %sel = select i1 %cond, i8 2, i8 3
diff --git a/llvm/test/CodeGen/AArch64/shift-mod.ll b/llvm/test/CodeGen/AArch64/shift-mod.ll
index a235b3628ded5e..8ba17c73e20933 100644
--- a/llvm/test/CodeGen/AArch64/shift-mod.ll
+++ b/llvm/test/CodeGen/AArch64/shift-mod.ll
@@ -191,3 +191,77 @@ define i64 @ashr_add_shl_mismatch_shifts2(i64 %r) {
   %conv1 = ashr i64 %sext, 8
   ret i64 %conv1
 }
+
+; Test that narrow shift amounts (i8/i16) don't generate redundant AND.
+define i32 @shl_i32_i8(i32 %x, i8 %amt) {
+; CHECK-LABEL: shl_i32_i8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    lsl w0, w0, w1
+; CHECK-NEXT:    ret
+  %ext = zext i8 %amt to i32
+  %r = shl i32 %x, %ext
+  ret i32 %r
+}
+
+define i32 @lshr_i32_i8(i32 %x, i8 %amt) {
+; CHECK-LABEL: lshr_i32_i8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    lsr w0, w0, w1
+; CHECK-NEXT:    ret
+  %ext = zext i8 %amt to i32
+  %r = lshr i32 %x, %ext
+  ret i32 %r
+}
+
+define i32 @ashr_i32_i8(i32 %x, i8 %amt) {
+; CHECK-LABEL: ashr_i32_i8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    asr w0, w0, w1
+; CHECK-NEXT:    ret
+  %ext = zext i8 %amt to i32
+  %r = ashr i32 %x, %ext
+  ret i32 %r
+}
+
+define i32 @shl_i32_i16(i32 %x, i16 %amt) {
+; CHECK-LABEL: shl_i32_i16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    lsl w0, w0, w1
+; CHECK-NEXT:    ret
+  %ext = zext i16 %amt to i32
+  %r = shl i32 %x, %ext
+  ret i32 %r
+}
+
+define i64 @shl_i64_i8(i64 %x, i8 %amt) {
+; CHECK-LABEL: shl_i64_i8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    // kill: def $w1 killed $w1 def $x1
+; CHECK-NEXT:    lsl x0, x0, x1
+; CHECK-NEXT:    ret
+  %ext = zext i8 %amt to i64
+  %r = shl i64 %x, %ext
+  ret i64 %r
+}
+
+define i64 @lshr_i64_i8(i64 %x, i8 %amt) {
+; CHECK-LABEL: lshr_i64_i8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    // kill: def $w1 killed $w1 def $x1
+; CHECK-NEXT:    lsr x0, x0, x1
+; CHECK-NEXT:    ret
+  %ext = zext i8 %amt to i64
+  %r = lshr i64 %x, %ext
+  ret i64 %r
+}
+
+define i64 @ashr_i64_i16(i64 %x, i16 %amt) {
+; CHECK-LABEL: ashr_i64_i16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    // kill: def $w1 killed $w1 def $x1
+; CHECK-NEXT:    asr x0, x0, x1
+; CHECK-NEXT:    ret
+  %ext = zext i16 %amt to i64
+  %r = ashr i64 %x, %ext
+  ret i64 %r
+}
diff --git a/llvm/test/CodeGen/AArch64/shift.ll b/llvm/test/CodeGen/AArch64/shift.ll
index 5d7935474c9035..eb5f56f771a0f2 100644
--- a/llvm/test/CodeGen/AArch64/shift.ll
+++ b/llvm/test/CodeGen/AArch64/shift.ll
@@ -19,31 +19,19 @@ define i1 @shl_i1(i1 %0, i1 %1){
 }
 
 define i8 @shl_i8(i8 %0, i8 %1){
-; CHECK-SD-LABEL: shl_i8:
-; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    lsl w0, w0, w1
-; CHECK-SD-NEXT:    ret
-;
-; CHECK-GI-LABEL: shl_i8:
-; CHECK-GI:       // %bb.0:
-; CHECK-GI-NEXT:    and w8, w1, #0xff
-; CHECK-GI-NEXT:    lsl w0, w0, w8
-; CHECK-GI-NEXT:    ret
+; CHECK-LABEL: shl_i8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    lsl w0, w0, w1
+; CHECK-NEXT:    ret
     %3 = shl i8 %0, %1
     ret i8 %3
 }
 
 define i16 @shl_i16(i16 %0, i16 %1){
-; CHECK-SD-LABEL: shl_i16:
-; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    lsl w0, w0, w1
-; CHECK-SD-NEXT:    ret
-;
-; CHECK-GI-LABEL: shl_i16:
-; CHECK-GI:       // %bb.0:
-; CHECK-GI-NEXT:    and w8, w1, #0xffff
-; CHECK-GI-NEXT:    lsl w0, w0, w8
-; CHECK-GI-NEXT:    ret
+; CHECK-LABEL: shl_i16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    lsl w0, w0, w1
+; CHECK-NEXT:    ret
     %3 = shl i16 %0, %1
     ret i16 %3
 }
@@ -118,35 +106,21 @@ define i1 @ashr_i1(i1 %0, i1 %1){
 }
 
 define i8 @ashr_i8(i8 %0, i8 %1){
-; CHECK-SD-LABEL: ashr_i8:
-; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    sxtb w8, w0
-; CHECK-SD-NEXT:    asr w0, w8, w1
-; CHECK-SD-NEXT:    ret
-;
-; CHECK-GI-LABEL: ashr_i8:
-; CHECK-GI:       // %bb.0:
-; CHECK-GI-NEXT:    sxtb w8, w0
-; CHECK-GI-NEXT:    and w9, w1, #0xff
-; CHECK-GI-NEXT:    asr w0, w8, w9
-; CHECK-GI-NEXT:    ret
+; CHECK-LABEL: ashr_i8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    sxtb w8, w0
+; CHECK-NEXT:    asr w0, w8, w1
+; CHECK-NEXT:    ret
     %3 = ashr i8 %0, %1
     ret i8 %3
 }
 
 define i16 @ashr_i16(i16 %0, i16 %1){
-; CHECK-SD-LABEL: ashr_i16:
-; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    sxth w8, w0
-; CHECK-SD-NEXT:    asr w0, w8, w1
-; CHECK-SD-NEXT:    ret
-;
-; CHECK-GI-LABEL: ashr_i16:
-; CHECK-GI:       // %bb.0:
-; CHECK-GI-NEXT:    sxth w8, w0
-; CHECK-GI-NEXT:    and w9, w1, #0xffff
-; CHECK-GI-NEXT:    asr w0, w8, w9
-; CHECK-GI-NEXT:    ret
+; CHECK-LABEL: ashr_i16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    sxth w8, w0
+; CHECK-NEXT:    asr w0, w8, w1
+; CHECK-NEXT:    ret
     %3 = ashr i16 %0, %1
     ret i16 %3
 }
@@ -223,35 +197,21 @@ define i1 @lshr_i1(i1 %0, i1 %1){
 }
 
 define i8 @lshr_i8(i8 %0, i8 %1){
-; CHECK-SD-LABEL: lshr_i8:
-; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    and w8, w0, #0xff
-; CHECK-SD-NEXT:    lsr w0, w8, w1
-; CHECK-SD-NEXT:    ret
-;
-; CHECK-GI-LABEL: lshr_i8:
-; CHECK-GI:       // %bb.0:
-; CHECK-GI-NEXT:    and w8, w1, #0xff
-; CHECK-GI-NEXT:    and w9, w0, #0xff
-; CHECK-GI-NEXT:    lsr w0, w9, w8
-; CHECK-GI-NEXT:    ret
+; CHECK-LABEL: lshr_i8:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    and w8, w0, #0xff
+; CHECK-NEXT:    lsr w0, w8, w1
+; CHECK-NEXT:    ret
     %3 = lshr i8 %0, %1
     ret i8 %3
 }
 
 define i16 @lshr_i16(i16 %0, i16 %1){
-; CHECK-SD-LABEL: lshr_i16:
-; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    and w8, w0, #0xffff
-; CHECK-SD-NEXT:    lsr w0, w8, w1
-; CHECK-SD-NEXT:    ret
-;
-; CHECK-GI-LABEL: lshr_i16:
-; CHECK-GI:       // %bb.0:
-; CHECK-GI-NEXT:    and w8, w1, #0xffff
-; CHECK-GI-NEXT:    and w9, w0, #0xffff
-; CHECK-GI-NEXT:    lsr w0, w9, w8
-; CHECK-GI-NEXT:    ret
+; CHECK-LABEL: lshr_i16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    and w8, w0, #0xffff
+; CHECK-NEXT:    lsr w0, w8, w1
+; CHECK-NEXT:    ret
     %3 = lshr i16 %0, %1
     ret i16 %3
 }

>From be083b474411d8fe3df89ac17ae346f66eba048c Mon Sep 17 00:00:00 2001
From: deepakshirkem <deepakshirke509 at gmail.com>
Date: Wed, 30 Sep 2026 15:37:37 +0530
Subject: [PATCH 2/2] Use countr_one for shift mask check

Address review feedback: use countr_one() >= Log2_32(ShiftWidth) instead
of explicit mask comparisons, matching the check that tryShiftAmountMod used.
---
 llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp              | 4 ++--
 llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp | 3 +--
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index 9d5722e1961734..02a69874e4bcba 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -778,8 +778,8 @@ bool AArch64DAGToDAGISel::SelectShiftMask(SDValue N, SDValue &ShAmt) {
   if (N.getOpcode() == ISD::AND && isa<ConstantSDNode>(N.getOperand(1)) &&
       N.getValueType() == (ShiftWidth == 32 ? MVT::i32 : MVT::i64)) {
     uint64_t Mask = N.getConstantOperandVal(1);
-    // Remove AND if the mask covers the low log2(ShiftWidth) bits.
-    if ((Mask & (ShiftWidth - 1)) == (ShiftWidth - 1)) {
+    // Remove AND if the mask covers at least the low log2(ShiftWidth) bits.
+    if ((unsigned)llvm::countr_one(Mask) >= Log2_32(ShiftWidth)) {
       ShAmt = N.getOperand(0);
       return true;
     }
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
index 5073b52b2a3c51..1ab07ca648bd64 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
@@ -7329,8 +7329,7 @@ AArch64InstructionSelector::selectShiftMask(MachineOperand &Root) const {
   Register AndSrcReg;
   if (mi_match(ShAmtReg, MRI, m_GAnd(m_Reg(AndSrcReg), m_ICst(AndMask))) &&
       MRI.getType(ShAmtReg).getSizeInBits() == ShiftWidth) {
-    uint64_t UMask = AndMask.getZExtValue();
-    if (UMask == 0xff || UMask == 0xffff || UMask == 0xffffffff)
+    if (AndMask.countr_one() >= Log2_32(ShiftWidth))
       ShAmtReg = AndSrcReg;
   }
 



More information about the llvm-commits mailing list