[llvm] [LLVM][CodeGen][AArch64] Increase opportunities to fold shift-of-extend into sshll/ushll. (PR #213989)

Paul Walker via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 5 08:44:16 PDT 2026


https://github.com/paulwalker-arm updated https://github.com/llvm/llvm-project/pull/213989

>From 83faa3d1489d77e777f0b50457f8e89e6574db30 Mon Sep 17 00:00:00 2001
From: Paul Walker <paul.walker at arm.com>
Date: Tue, 4 Aug 2026 16:43:14 +0100
Subject: [PATCH 1/5] Add tests.

---
 .../CodeGen/AArch64/neon-shift-left-long.ll   | 139 ++++++++++++++++++
 1 file changed, 139 insertions(+)

diff --git a/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll b/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll
index a8c55b476b810..6c879026f5e90 100644
--- a/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll
+++ b/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll
@@ -473,3 +473,142 @@ define <8 x i16> @test_ushll_cmp(<8 x i8> %a, <8 x i8> %b) #0 {
   %vmovl.i.i.i = zext <8 x i8> %vcgtz.i.i to <8 x i16>
   ret <8 x i16> %vmovl.i.i.i
 }
+
+define  <8 x i16> @mul_of_shl_zext(<8 x i8> %a, <8 x i16> %b) {
+; CHECK-SD-LABEL: mul_of_shl_zext:
+; CHECK-SD:       // %bb.0:
+; CHECK-SD-NEXT:    ushll v0.8h, v0.8b, #0
+; CHECK-SD-NEXT:    mul v0.8h, v0.8h, v1.8h
+; CHECK-SD-NEXT:    add v0.8h, v0.8h, v0.8h
+; CHECK-SD-NEXT:    ret
+;
+; CHECK-GI-LABEL: mul_of_shl_zext:
+; CHECK-GI:       // %bb.0:
+; CHECK-GI-NEXT:    ushll v0.8h, v0.8b, #1
+; CHECK-GI-NEXT:    mul v0.8h, v0.8h, v1.8h
+; CHECK-GI-NEXT:    ret
+  %a.ext = zext <8 x i8> %a to <8 x i16>
+  %shl = shl <8 x i16> %a.ext, splat (i16 1)
+  %mul = mul <8 x i16> %shl, %b
+  ret <8 x i16> %mul
+}
+
+define  <8 x i16> @shl_of_mul_zext(<8 x i8> %a, <8 x i16> %b) {
+; CHECK-SD-LABEL: shl_of_mul_zext:
+; CHECK-SD:       // %bb.0:
+; CHECK-SD-NEXT:    ushll v0.8h, v0.8b, #0
+; CHECK-SD-NEXT:    mul v0.8h, v0.8h, v1.8h
+; CHECK-SD-NEXT:    add v0.8h, v0.8h, v0.8h
+; CHECK-SD-NEXT:    ret
+;
+; CHECK-GI-LABEL: shl_of_mul_zext:
+; CHECK-GI:       // %bb.0:
+; CHECK-GI-NEXT:    ushll v0.8h, v0.8b, #0
+; CHECK-GI-NEXT:    mul v0.8h, v0.8h, v1.8h
+; CHECK-GI-NEXT:    shl v0.8h, v0.8h, #1
+; CHECK-GI-NEXT:    ret
+  %a.ext = zext <8 x i8> %a to <8 x i16>
+  %mul = mul <8 x i16> %a.ext, %b
+  %shl = shl <8 x i16> %mul, splat (i16 1)
+  ret <8 x i16> %shl
+}
+
+define  <8 x i16> @shl_of_mul_zext_zext(<8 x i8> %a, <8 x i8> %b) {
+; CHECK-SD-LABEL: shl_of_mul_zext_zext:
+; CHECK-SD:       // %bb.0:
+; CHECK-SD-NEXT:    umull v0.8h, v0.8b, v1.8b
+; CHECK-SD-NEXT:    add v0.8h, v0.8h, v0.8h
+; CHECK-SD-NEXT:    ret
+;
+; CHECK-GI-LABEL: shl_of_mul_zext_zext:
+; CHECK-GI:       // %bb.0:
+; CHECK-GI-NEXT:    umull v0.8h, v0.8b, v1.8b
+; CHECK-GI-NEXT:    shl v0.8h, v0.8h, #1
+; CHECK-GI-NEXT:    ret
+  %a.ext = zext <8 x i8> %a to <8 x i16>
+  %b.ext = zext <8 x i8> %b to <8 x i16>
+  %mul = mul <8 x i16> %a.ext, %b.ext
+  %shl = shl <8 x i16> %mul, splat (i16 1)
+  ret <8 x i16> %shl
+}
+
+define  <8 x i16> @mul_of_shl_sext(<8 x i8> %a, <8 x i16> %b) {
+; CHECK-SD-LABEL: mul_of_shl_sext:
+; CHECK-SD:       // %bb.0:
+; CHECK-SD-NEXT:    sshll v0.8h, v0.8b, #0
+; CHECK-SD-NEXT:    mul v0.8h, v0.8h, v1.8h
+; CHECK-SD-NEXT:    add v0.8h, v0.8h, v0.8h
+; CHECK-SD-NEXT:    ret
+;
+; CHECK-GI-LABEL: mul_of_shl_sext:
+; CHECK-GI:       // %bb.0:
+; CHECK-GI-NEXT:    sshll v0.8h, v0.8b, #1
+; CHECK-GI-NEXT:    mul v0.8h, v0.8h, v1.8h
+; CHECK-GI-NEXT:    ret
+  %a.ext = sext <8 x i8> %a to <8 x i16>
+  %shl = shl <8 x i16> %a.ext, splat (i16 1)
+  %mul = mul <8 x i16> %shl, %b
+  ret <8 x i16> %mul
+}
+
+define  <8 x i16> @shl_of_mul_sext(<8 x i8> %a, <8 x i16> %b) {
+; CHECK-SD-LABEL: shl_of_mul_sext:
+; CHECK-SD:       // %bb.0:
+; CHECK-SD-NEXT:    sshll v0.8h, v0.8b, #0
+; CHECK-SD-NEXT:    mul v0.8h, v0.8h, v1.8h
+; CHECK-SD-NEXT:    add v0.8h, v0.8h, v0.8h
+; CHECK-SD-NEXT:    ret
+;
+; CHECK-GI-LABEL: shl_of_mul_sext:
+; CHECK-GI:       // %bb.0:
+; CHECK-GI-NEXT:    sshll v0.8h, v0.8b, #0
+; CHECK-GI-NEXT:    mul v0.8h, v0.8h, v1.8h
+; CHECK-GI-NEXT:    shl v0.8h, v0.8h, #1
+; CHECK-GI-NEXT:    ret
+  %a.ext = sext <8 x i8> %a to <8 x i16>
+  %mul = mul <8 x i16> %a.ext, %b
+  %shl = shl <8 x i16> %mul, splat (i16 1)
+  ret <8 x i16> %shl
+}
+
+define  <8 x i16> @shl_of_mul_sext_sext(<8 x i8> %a, <8 x i8> %b) {
+; CHECK-SD-LABEL: shl_of_mul_sext_sext:
+; CHECK-SD:       // %bb.0:
+; CHECK-SD-NEXT:    smull v0.8h, v0.8b, v1.8b
+; CHECK-SD-NEXT:    add v0.8h, v0.8h, v0.8h
+; CHECK-SD-NEXT:    ret
+;
+; CHECK-GI-LABEL: shl_of_mul_sext_sext:
+; CHECK-GI:       // %bb.0:
+; CHECK-GI-NEXT:    smull v0.8h, v0.8b, v1.8b
+; CHECK-GI-NEXT:    shl v0.8h, v0.8h, #1
+; CHECK-GI-NEXT:    ret
+  %a.ext = sext <8 x i8> %a to <8 x i16>
+  %b.ext = sext <8 x i8> %b to <8 x i16>
+  %mul = mul <8 x i16> %a.ext, %b.ext
+  %shl = shl <8 x i16> %mul, splat (i16 1)
+  ret <8 x i16> %shl
+}
+
+define  <8 x i16> @shl_of_multi_use_mul_sext(<8 x i8> %a, <8 x i16> %b) {
+; CHECK-SD-LABEL: shl_of_multi_use_mul_sext:
+; CHECK-SD:       // %bb.0:
+; CHECK-SD-NEXT:    sshll v0.8h, v0.8b, #0
+; CHECK-SD-NEXT:    mul v1.8h, v0.8h, v1.8h
+; CHECK-SD-NEXT:    add v0.8h, v1.8h, v1.8h
+; CHECK-SD-NEXT:    // fake_use: $q1
+; CHECK-SD-NEXT:    ret
+;
+; CHECK-GI-LABEL: shl_of_multi_use_mul_sext:
+; CHECK-GI:       // %bb.0:
+; CHECK-GI-NEXT:    sshll v0.8h, v0.8b, #0
+; CHECK-GI-NEXT:    mul v1.8h, v0.8h, v1.8h
+; CHECK-GI-NEXT:    shl v0.8h, v1.8h, #1
+; CHECK-GI-NEXT:    // fake_use: $q1
+; CHECK-GI-NEXT:    ret
+  %a.ext = sext <8 x i8> %a to <8 x i16>
+  %mul = mul <8 x i16> %a.ext, %b
+  %shl = shl <8 x i16> %mul, splat (i16 1)
+  call void (...) @llvm.fake.use(<8 x i16> %mul)
+  ret <8 x i16> %shl
+}

>From e918d162a164dad06d894d37c31781aa9ce04026 Mon Sep 17 00:00:00 2001
From: Paul Walker <paul.walker at arm.com>
Date: Tue, 4 Aug 2026 15:59:19 +0100
Subject: [PATCH 2/5] [LLVM][CodeGen][AArch64] Fold shift-of-extend into
 sshll/ushll.

Add PreprocessISelDAG logic to undo mul-of-shl to shl-of-mul
canonicalisation when either operand is sign/zero extended because the
extension can be folded into the shift.
---
 .../Target/AArch64/AArch64ISelDAGToDAG.cpp    | 17 +++++++
 .../CodeGen/AArch64/neon-shift-left-long.ll   | 49 +++++++------------
 2 files changed, 34 insertions(+), 32 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index f6b90594b064e..6ba1c6c357edb 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -17,6 +17,7 @@
 #include "MCTargetDesc/AArch64AddressingModes.h"
 #include "llvm/ADT/APSInt.h"
 #include "llvm/CodeGen/ISDOpcodes.h"
+#include "llvm/CodeGen/SDPatternMatch.h"
 #include "llvm/CodeGen/SelectionDAGISel.h"
 #include "llvm/IR/Function.h" // To access function attributes.
 #include "llvm/IR/GlobalValue.h"
@@ -29,6 +30,7 @@
 #include "llvm/Support/raw_ostream.h"
 
 using namespace llvm;
+using namespace llvm::SDPatternMatch;
 
 #define DEBUG_TYPE "aarch64-isel"
 #define PASS_NAME "AArch64 Instruction Selection"
@@ -8300,6 +8302,21 @@ void AArch64DAGToDAGISel::PreprocessISelDAG() {
     case AArch64ISD::CSEL:
       Result = tryFoldCselToFMaxMin(N);
       break;
+    case AArch64ISD::VSHL: {
+      SDValue LHS, RHS;
+      // Undo mul(shl(A,C),B) -> shl(mul(A,B),C) canonicalisation when A is an
+      // extend that can be folded into the shift.
+      if (sd_match(
+              N.getOperand(0),
+              m_Mul(m_Value(LHS, m_AnyOf(m_ZExt(m_Value()), m_SExt(m_Value()))),
+                    m_Value(RHS)))) {
+        SDLoc DL(&N);
+        SDValue SHL = CurDAG->getNode(AArch64ISD::VSHL, DL, N.getValueType(0),
+                                      LHS, N.getOperand(1));
+        Result = CurDAG->getNode(ISD::MUL, DL, N.getValueType(0), SHL, RHS);
+      }
+      break;
+    }
     default:
       break;
     }
diff --git a/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll b/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll
index 6c879026f5e90..0dd0528969f0a 100644
--- a/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll
+++ b/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll
@@ -475,18 +475,11 @@ define <8 x i16> @test_ushll_cmp(<8 x i8> %a, <8 x i8> %b) #0 {
 }
 
 define  <8 x i16> @mul_of_shl_zext(<8 x i8> %a, <8 x i16> %b) {
-; CHECK-SD-LABEL: mul_of_shl_zext:
-; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    ushll v0.8h, v0.8b, #0
-; CHECK-SD-NEXT:    mul v0.8h, v0.8h, v1.8h
-; CHECK-SD-NEXT:    add v0.8h, v0.8h, v0.8h
-; CHECK-SD-NEXT:    ret
-;
-; CHECK-GI-LABEL: mul_of_shl_zext:
-; CHECK-GI:       // %bb.0:
-; CHECK-GI-NEXT:    ushll v0.8h, v0.8b, #1
-; CHECK-GI-NEXT:    mul v0.8h, v0.8h, v1.8h
-; CHECK-GI-NEXT:    ret
+; CHECK-LABEL: mul_of_shl_zext:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    ushll v0.8h, v0.8b, #1
+; CHECK-NEXT:    mul v0.8h, v0.8h, v1.8h
+; CHECK-NEXT:    ret
   %a.ext = zext <8 x i8> %a to <8 x i16>
   %shl = shl <8 x i16> %a.ext, splat (i16 1)
   %mul = mul <8 x i16> %shl, %b
@@ -496,9 +489,8 @@ define  <8 x i16> @mul_of_shl_zext(<8 x i8> %a, <8 x i16> %b) {
 define  <8 x i16> @shl_of_mul_zext(<8 x i8> %a, <8 x i16> %b) {
 ; CHECK-SD-LABEL: shl_of_mul_zext:
 ; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    ushll v0.8h, v0.8b, #0
+; CHECK-SD-NEXT:    ushll v0.8h, v0.8b, #1
 ; CHECK-SD-NEXT:    mul v0.8h, v0.8h, v1.8h
-; CHECK-SD-NEXT:    add v0.8h, v0.8h, v0.8h
 ; CHECK-SD-NEXT:    ret
 ;
 ; CHECK-GI-LABEL: shl_of_mul_zext:
@@ -533,18 +525,11 @@ define  <8 x i16> @shl_of_mul_zext_zext(<8 x i8> %a, <8 x i8> %b) {
 }
 
 define  <8 x i16> @mul_of_shl_sext(<8 x i8> %a, <8 x i16> %b) {
-; CHECK-SD-LABEL: mul_of_shl_sext:
-; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    sshll v0.8h, v0.8b, #0
-; CHECK-SD-NEXT:    mul v0.8h, v0.8h, v1.8h
-; CHECK-SD-NEXT:    add v0.8h, v0.8h, v0.8h
-; CHECK-SD-NEXT:    ret
-;
-; CHECK-GI-LABEL: mul_of_shl_sext:
-; CHECK-GI:       // %bb.0:
-; CHECK-GI-NEXT:    sshll v0.8h, v0.8b, #1
-; CHECK-GI-NEXT:    mul v0.8h, v0.8h, v1.8h
-; CHECK-GI-NEXT:    ret
+; CHECK-LABEL: mul_of_shl_sext:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    sshll v0.8h, v0.8b, #1
+; CHECK-NEXT:    mul v0.8h, v0.8h, v1.8h
+; CHECK-NEXT:    ret
   %a.ext = sext <8 x i8> %a to <8 x i16>
   %shl = shl <8 x i16> %a.ext, splat (i16 1)
   %mul = mul <8 x i16> %shl, %b
@@ -554,9 +539,8 @@ define  <8 x i16> @mul_of_shl_sext(<8 x i8> %a, <8 x i16> %b) {
 define  <8 x i16> @shl_of_mul_sext(<8 x i8> %a, <8 x i16> %b) {
 ; CHECK-SD-LABEL: shl_of_mul_sext:
 ; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    sshll v0.8h, v0.8b, #0
+; CHECK-SD-NEXT:    sshll v0.8h, v0.8b, #1
 ; CHECK-SD-NEXT:    mul v0.8h, v0.8h, v1.8h
-; CHECK-SD-NEXT:    add v0.8h, v0.8h, v0.8h
 ; CHECK-SD-NEXT:    ret
 ;
 ; CHECK-GI-LABEL: shl_of_mul_sext:
@@ -593,10 +577,11 @@ define  <8 x i16> @shl_of_mul_sext_sext(<8 x i8> %a, <8 x i8> %b) {
 define  <8 x i16> @shl_of_multi_use_mul_sext(<8 x i8> %a, <8 x i16> %b) {
 ; CHECK-SD-LABEL: shl_of_multi_use_mul_sext:
 ; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    sshll v0.8h, v0.8b, #0
-; CHECK-SD-NEXT:    mul v1.8h, v0.8h, v1.8h
-; CHECK-SD-NEXT:    add v0.8h, v1.8h, v1.8h
-; CHECK-SD-NEXT:    // fake_use: $q1
+; CHECK-SD-NEXT:    sshll v2.8h, v0.8b, #0
+; CHECK-SD-NEXT:    sshll v0.8h, v0.8b, #1
+; CHECK-SD-NEXT:    mul v2.8h, v2.8h, v1.8h
+; CHECK-SD-NEXT:    mul v0.8h, v0.8h, v1.8h
+; CHECK-SD-NEXT:    // fake_use: $q2
 ; CHECK-SD-NEXT:    ret
 ;
 ; CHECK-GI-LABEL: shl_of_multi_use_mul_sext:

>From a21c2b6db81efd28249c2f905c1bec69d16efe0b Mon Sep 17 00:00:00 2001
From: Paul Walker <paul.walker at arm.com>
Date: Wed, 5 Aug 2026 10:14:45 +0000
Subject: [PATCH 3/5] Match variable names to the documentation.

---
 llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index 6ba1c6c357edb..678ec7f4ce2ac 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -8303,17 +8303,17 @@ void AArch64DAGToDAGISel::PreprocessISelDAG() {
       Result = tryFoldCselToFMaxMin(N);
       break;
     case AArch64ISD::VSHL: {
-      SDValue LHS, RHS;
       // Undo mul(shl(A,C),B) -> shl(mul(A,B),C) canonicalisation when A is an
       // extend that can be folded into the shift.
+      EVT VT = N.getValueType(0);
+      SDValue A, B, C = N.getOperand(1);
       if (sd_match(
               N.getOperand(0),
-              m_Mul(m_Value(LHS, m_AnyOf(m_ZExt(m_Value()), m_SExt(m_Value()))),
-                    m_Value(RHS)))) {
+              m_Mul(m_Value(A, m_AnyOf(m_ZExt(m_Value()), m_SExt(m_Value()))),
+                    m_Value(B)))) {
         SDLoc DL(&N);
-        SDValue SHL = CurDAG->getNode(AArch64ISD::VSHL, DL, N.getValueType(0),
-                                      LHS, N.getOperand(1));
-        Result = CurDAG->getNode(ISD::MUL, DL, N.getValueType(0), SHL, RHS);
+        SDValue SHL = CurDAG->getNode(AArch64ISD::VSHL, DL, VT, A, C);
+        Result = CurDAG->getNode(ISD::MUL, DL, VT, SHL, B);
       }
       break;
     }

>From 4787b02aba11da38fb5dac30a2da86b425a66df3 Mon Sep 17 00:00:00 2001
From: Paul Walker <paul.walker at arm.com>
Date: Wed, 5 Aug 2026 10:56:13 +0000
Subject: [PATCH 4/5] Limit transformation to single use multiplies.

---
 llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp   | 8 ++++----
 llvm/test/CodeGen/AArch64/neon-shift-left-long.ll | 9 ++++-----
 2 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index 678ec7f4ce2ac..354dc75220e0c 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -8307,10 +8307,10 @@ void AArch64DAGToDAGISel::PreprocessISelDAG() {
       // extend that can be folded into the shift.
       EVT VT = N.getValueType(0);
       SDValue A, B, C = N.getOperand(1);
-      if (sd_match(
-              N.getOperand(0),
-              m_Mul(m_Value(A, m_AnyOf(m_ZExt(m_Value()), m_SExt(m_Value()))),
-                    m_Value(B)))) {
+      if (sd_match(N.getOperand(0),
+                   m_OneUse(m_Mul(m_Value(A, m_AnyOf(m_ZExt(m_Value()),
+                                                     m_SExt(m_Value()))),
+                                  m_Value(B))))) {
         SDLoc DL(&N);
         SDValue SHL = CurDAG->getNode(AArch64ISD::VSHL, DL, VT, A, C);
         Result = CurDAG->getNode(ISD::MUL, DL, VT, SHL, B);
diff --git a/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll b/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll
index 0dd0528969f0a..afb21c137c2b7 100644
--- a/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll
+++ b/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll
@@ -577,11 +577,10 @@ define  <8 x i16> @shl_of_mul_sext_sext(<8 x i8> %a, <8 x i8> %b) {
 define  <8 x i16> @shl_of_multi_use_mul_sext(<8 x i8> %a, <8 x i16> %b) {
 ; CHECK-SD-LABEL: shl_of_multi_use_mul_sext:
 ; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    sshll v2.8h, v0.8b, #0
-; CHECK-SD-NEXT:    sshll v0.8h, v0.8b, #1
-; CHECK-SD-NEXT:    mul v2.8h, v2.8h, v1.8h
-; CHECK-SD-NEXT:    mul v0.8h, v0.8h, v1.8h
-; CHECK-SD-NEXT:    // fake_use: $q2
+; CHECK-SD-NEXT:    sshll v0.8h, v0.8b, #0
+; CHECK-SD-NEXT:    mul v1.8h, v0.8h, v1.8h
+; CHECK-SD-NEXT:    add v0.8h, v1.8h, v1.8h
+; CHECK-SD-NEXT:    // fake_use: $q1
 ; CHECK-SD-NEXT:    ret
 ;
 ; CHECK-GI-LABEL: shl_of_multi_use_mul_sext:

>From 9e3c44b89fbcaa97b19cf0ced1a646e49f5e668e Mon Sep 17 00:00:00 2001
From: Paul Walker <paul.walker at arm.com>
Date: Wed, 5 Aug 2026 15:39:38 +0000
Subject: [PATCH 5/5] Preserve the smull/umull idiom.

---
 llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index 354dc75220e0c..44c5ab4e96520 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -8311,6 +8311,9 @@ void AArch64DAGToDAGISel::PreprocessISelDAG() {
                    m_OneUse(m_Mul(m_Value(A, m_AnyOf(m_ZExt(m_Value()),
                                                      m_SExt(m_Value()))),
                                   m_Value(B))))) {
+        // If both mul operands are extended, preserve the smull/umull idiom.
+        if (B.getOpcode() == A.getOpcode())
+          break;
         SDLoc DL(&N);
         SDValue SHL = CurDAG->getNode(AArch64ISD::VSHL, DL, VT, A, C);
         Result = CurDAG->getNode(ISD::MUL, DL, VT, SHL, B);



More information about the llvm-commits mailing list