[llvm] [AArch64] Add sext(shl x, imm)/sext(sra x, imm) selection patterns (PR #226065)

via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 24 01:16:35 PDT 2026


https://github.com/KRM7 created https://github.com/llvm/llvm-project/pull/226065

Currently there are no selection patterns for code where the result of a constant shift is sign-extended, even though these can be selected to a single sbfiz/sbfx instruction. For example, at the moment, 2 instructions are generated for the following C function:
```C
  int64_t shl_ext(int8_t x) { return x * 2; }
```

>From b98f960ca680c62f725816ec87ed4ee09f3d971e Mon Sep 17 00:00:00 2001
From: Krisztian Rugasi <krisztian.rugasi at arm.com>
Date: Thu, 24 Sep 2026 07:45:45 +0000
Subject: [PATCH] [AArch64] Add sext(shl x, imm)/sext(sra x, imm) selection
 patterns

Currently there are no selection patterns for code where the result
of a constant shift is sign-extended, even though these can be
selected to a single sbfiz/sbfx instruction. For example, at the
moment, 2 instructions are generated for the following C function:

  int64_t shl_ext(int8_t x) { return x * 2; }
---
 .../Target/AArch64/AArch64ISelDAGToDAG.cpp    |  30 ----
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  27 ++++
 .../CodeGen/AArch64/arm64-shifted-sext.ll     | 140 ++++++++++++++++++
 llvm/test/CodeGen/AArch64/lslfast.ll          |   4 +-
 4 files changed, 169 insertions(+), 32 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index feb8f46583794..ba8d2eb61c2b0 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -441,7 +441,6 @@ class AArch64DAGToDAGISel : public SelectionDAGISel {
                            unsigned Scale);
 
   bool tryBitfieldExtractOp(SDNode *N);
-  bool tryBitfieldExtractOpFromSExt(SDNode *N);
   bool tryBitfieldInsertOp(SDNode *N);
   bool tryBitfieldInsertInZeroOp(SDNode *N);
   bool tryShiftAmountMod(SDNode *N);
@@ -3079,30 +3078,6 @@ static bool isBitfieldExtractOpFromShr(SDNode *N, unsigned &Opc, SDValue &Opd0,
   return true;
 }
 
-bool AArch64DAGToDAGISel::tryBitfieldExtractOpFromSExt(SDNode *N) {
-  assert(N->getOpcode() == ISD::SIGN_EXTEND);
-
-  EVT VT = N->getValueType(0);
-  EVT NarrowVT = N->getOperand(0)->getValueType(0);
-  if (VT != MVT::i64 || NarrowVT != MVT::i32)
-    return false;
-
-  uint64_t ShiftImm;
-  SDValue Op = N->getOperand(0);
-  if (!isOpcWithIntImmediate(Op.getNode(), ISD::SRA, ShiftImm))
-    return false;
-
-  SDLoc dl(N);
-  // Extend the incoming operand of the shift to 64-bits.
-  SDValue Opd0 = Widen(CurDAG, Op.getOperand(0));
-  unsigned Immr = ShiftImm;
-  unsigned Imms = NarrowVT.getSizeInBits() - 1;
-  SDValue Ops[] = {Opd0, CurDAG->getTargetConstant(Immr, dl, VT),
-                   CurDAG->getTargetConstant(Imms, dl, VT)};
-  CurDAG->SelectNodeTo(N, AArch64::SBFMXri, VT, Ops);
-  return true;
-}
-
 static bool isBitfieldExtractOp(SelectionDAG *CurDAG, SDNode *N, unsigned &Opc,
                                 SDValue &Opd0, unsigned &Immr, unsigned &Imms,
                                 unsigned NumberOfIgnoredLowBits = 0,
@@ -5213,11 +5188,6 @@ void AArch64DAGToDAGISel::Select(SDNode *Node) {
       return;
     break;
 
-  case ISD::SIGN_EXTEND:
-    if (tryBitfieldExtractOpFromSExt(Node))
-      return;
-    break;
-
   case ISD::OR:
     if (tryBitfieldInsertOp(Node))
       return;
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 4a65c2ebb2906..ed11428b44dd2 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -10914,6 +10914,21 @@ def : Pat<(shl (i64 (zext GPR32:$Rn)), (i64 imm0_63:$imm)),
                    (i64 (i64shift_a        imm0_63:$imm)),
                    (i64 (i64shift_sext_i32 imm0_63:$imm)))>;
 
+def : Pat<(i64 (sext (shl GPR32:$Rn, (i64 imm0_31:$imm)))),
+          (SBFMXri (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GPR32:$Rn, sub_32),
+                   (i64 (i64shift_a imm0_31:$imm)),
+                   (i64 (i32shift_b imm0_31:$imm)))>;
+
+def : Pat<(i64 (sext (shl (sext_inreg GPR32:$Rn, i8), (i64 imm0_31:$imm)))),
+          (SBFMXri (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GPR32:$Rn, sub_32),
+                   (i64 (i64shift_a imm0_31:$imm)),
+                   (i64 (i32shift_sext_i8 imm0_31:$imm)))>;
+
+def : Pat<(i64 (sext (shl (sext_inreg GPR32:$Rn, i16), (i64 imm0_31:$imm)))),
+          (SBFMXri (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GPR32:$Rn, sub_32),
+                   (i64 (i64shift_a imm0_31:$imm)),
+                   (i64 (i32shift_sext_i16 imm0_31:$imm)))>;
+
 // sra patterns have an AddedComplexity of 10, so make sure we have a higher
 // AddedComplexity for the following patterns since we want to match sext + sra
 // patterns before we attempt to match a single sra node.
@@ -10936,6 +10951,18 @@ def : Pat<(sra (i64 (sext GPR32:$Rn)), (i64 imm0_31:$imm)),
                    (i64 imm0_31:$imm), 31)>;
 } // AddedComplexity = 20
 
+def : Pat<(i64 (sext (sra GPR32:$Rn, (i64 imm0_31:$imm)))),
+          (SBFMXri (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GPR32:$Rn, sub_32),
+                   (i64 imm0_31:$imm), 31)>;
+
+def : Pat<(i64 (sext (sra (sext_inreg GPR32:$Rn, i8), (i64 imm0_31:$imm)))),
+          (SBFMXri (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GPR32:$Rn, sub_32),
+                   (i64 imm0_31:$imm), 7)>;
+
+def : Pat<(i64 (sext (sra (sext_inreg GPR32:$Rn, i16), (i64 imm0_31:$imm)))),
+          (SBFMXri (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GPR32:$Rn, sub_32),
+                   (i64 imm0_31:$imm), 15)>;
+
 // To truncate, we can simply extract from a subregister.
 def : Pat<(i32 (trunc GPR64sp:$src)),
           (i32 (EXTRACT_SUBREG GPR64sp:$src, sub_32))>;
diff --git a/llvm/test/CodeGen/AArch64/arm64-shifted-sext.ll b/llvm/test/CodeGen/AArch64/arm64-shifted-sext.ll
index da6499b7daa82..ef8243960c534 100644
--- a/llvm/test/CodeGen/AArch64/arm64-shifted-sext.ll
+++ b/llvm/test/CodeGen/AArch64/arm64-shifted-sext.ll
@@ -339,3 +339,143 @@ define i64 @sign_extend_inreg_isdef32(i64) {
   %6 = zext i32 %5 to i64
   ret i64 %6
 }
+
+define i64 @shl1_sext_i32(i32 %val) nounwind {
+; CHECK-LABEL: shl1_sext_i32:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    ; kill: def $w0 killed $w0 def $x0
+; CHECK-NEXT:    sbfiz x0, x0, #1, #31
+; CHECK-NEXT:    ret
+  %1 = shl i32 %val, 1
+  %2 = sext i32 %1 to i64
+  ret i64 %2
+}
+
+define i64 @shl31_sext_i32(i32 %val) nounwind {
+; CHECK-LABEL: shl31_sext_i32:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    ; kill: def $w0 killed $w0 def $x0
+; CHECK-NEXT:    sbfiz x0, x0, #31, #1
+; CHECK-NEXT:    ret
+  %1 = shl i32 %val, 31
+  %2 = sext i32 %1 to i64
+  ret i64 %2
+}
+
+define i64 @sext_shl1_sext_i8(i8 %val) nounwind {
+; CHECK-LABEL: sext_shl1_sext_i8:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    ; kill: def $w0 killed $w0 def $x0
+; CHECK-NEXT:    sbfiz x0, x0, #1, #8
+; CHECK-NEXT:    ret
+  %1 = sext i8 %val to i32
+  %2 = shl i32 %1, 1
+  %3 = sext i32 %2 to i64
+  ret i64 %3
+}
+
+define i64 @sext_shl31_sext_i8(i8 %val) nounwind {
+; CHECK-LABEL: sext_shl31_sext_i8:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    ; kill: def $w0 killed $w0 def $x0
+; CHECK-NEXT:    sbfiz x0, x0, #31, #1
+; CHECK-NEXT:    ret
+  %1 = sext i8 %val to i32
+  %2 = shl i32 %1, 31
+  %3 = sext i32 %2 to i64
+  ret i64 %3
+}
+
+define i64 @sext_shl1_sext_i16(i16 %val) nounwind {
+; CHECK-LABEL: sext_shl1_sext_i16:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    ; kill: def $w0 killed $w0 def $x0
+; CHECK-NEXT:    sbfiz x0, x0, #1, #16
+; CHECK-NEXT:    ret
+  %1 = sext i16 %val to i32
+  %2 = shl i32 %1, 1
+  %3 = sext i32 %2 to i64
+  ret i64 %3
+}
+
+define i64 @sext_shl31_sext_i16(i16 %val) nounwind {
+; CHECK-LABEL: sext_shl31_sext_i16:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    ; kill: def $w0 killed $w0 def $x0
+; CHECK-NEXT:    sbfiz x0, x0, #31, #1
+; CHECK-NEXT:    ret
+  %1 = sext i16 %val to i32
+  %2 = shl i32 %1, 31
+  %3 = sext i32 %2 to i64
+  ret i64 %3
+}
+
+define i64 @ashr1_sext_i32(i32 %val) nounwind {
+; CHECK-LABEL: ashr1_sext_i32:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    ; kill: def $w0 killed $w0 def $x0
+; CHECK-NEXT:    sbfx x0, x0, #1, #31
+; CHECK-NEXT:    ret
+  %1 = ashr i32 %val, 1
+  %2 = sext i32 %1 to i64
+  ret i64 %2
+}
+
+define i64 @ashr31_sext_i32(i32 %val) nounwind {
+; CHECK-LABEL: ashr31_sext_i32:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    ; kill: def $w0 killed $w0 def $x0
+; CHECK-NEXT:    sbfx x0, x0, #31, #1
+; CHECK-NEXT:    ret
+  %1 = ashr i32 %val, 31
+  %2 = sext i32 %1 to i64
+  ret i64 %2
+}
+
+define i64 @sext_ashr1_sext_i8(i8 %val) nounwind {
+; CHECK-LABEL: sext_ashr1_sext_i8:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    ; kill: def $w0 killed $w0 def $x0
+; CHECK-NEXT:    sbfx x0, x0, #1, #7
+; CHECK-NEXT:    ret
+  %1 = sext i8 %val to i32
+  %2 = ashr i32 %1, 1
+  %3 = sext i32 %2 to i64
+  ret i64 %3
+}
+
+define i64 @sext_ashr7_sext_i8(i8 %val) nounwind {
+; CHECK-LABEL: sext_ashr7_sext_i8:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    ; kill: def $w0 killed $w0 def $x0
+; CHECK-NEXT:    sbfx x0, x0, #7, #1
+; CHECK-NEXT:    ret
+  %1 = sext i8 %val to i32
+  %2 = ashr i32 %1, 7
+  %3 = sext i32 %2 to i64
+  ret i64 %3
+}
+
+define i64 @sext_ashr1_sext_i16(i16 %val) nounwind {
+; CHECK-LABEL: sext_ashr1_sext_i16:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    ; kill: def $w0 killed $w0 def $x0
+; CHECK-NEXT:    sbfx x0, x0, #1, #15
+; CHECK-NEXT:    ret
+  %1 = sext i16 %val to i32
+  %2 = ashr i32 %1, 1
+  %3 = sext i32 %2 to i64
+  ret i64 %3
+}
+
+define i64 @sext_ashr15_sext_i16(i16 %val) nounwind {
+; CHECK-LABEL: sext_ashr15_sext_i16:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    ; kill: def $w0 killed $w0 def $x0
+; CHECK-NEXT:    sbfx x0, x0, #15, #1
+; CHECK-NEXT:    ret
+  %1 = sext i16 %val to i32
+  %2 = ashr i32 %1, 15
+  %3 = sext i32 %2 to i64
+  ret i64 %3
+}
diff --git a/llvm/test/CodeGen/AArch64/lslfast.ll b/llvm/test/CodeGen/AArch64/lslfast.ll
index 5ec70b5f22975..239de3efc5161 100644
--- a/llvm/test/CodeGen/AArch64/lslfast.ll
+++ b/llvm/test/CodeGen/AArch64/lslfast.ll
@@ -96,8 +96,8 @@ entry:
 define i64 @test3sext(i32 noundef %x, i64 noundef %y, i64 noundef %z) {
 ; CHECK-LABEL: test3sext:
 ; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    lsl w8, w0, #3
-; CHECK-NEXT:    sxtw x8, w8
+; CHECK-NEXT:    // kill: def $w0 killed $w0 def $x0
+; CHECK-NEXT:    sbfiz x8, x0, #3, #29
 ; CHECK-NEXT:    add x9, x8, x1
 ; CHECK-NEXT:    add x8, x8, x2
 ; CHECK-NEXT:    mul x0, x9, x8



More information about the llvm-commits mailing list