[llvm] [WebAssembly] Lower wide vector shifts by constant to extmul pairs (PR #184007)

via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 9 03:31:54 PDT 2026


https://github.com/ParkHanbum updated https://github.com/llvm/llvm-project/pull/184007

>From be86f0f5aa4de173a7a7d82429326f613cc1e5f0 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Sun, 1 Mar 2026 18:26:51 +0900
Subject: [PATCH 1/9] add testcase for upcoming patch

---
 .../test/CodeGen/WebAssembly/wide-simd-mul.ll | 152 ++++++++++++++++++
 1 file changed, 152 insertions(+)

diff --git a/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll b/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
index 94aa197bfd564..7d9c0388ad605 100644
--- a/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
+++ b/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
@@ -195,3 +195,155 @@ define <8 x i32> @zext_sext_mul_v8i16(<8 x i16> %a, <8 x i16> %b) {
   %mul = mul <8 x i32> %wide.a, %wide.b
   ret <8 x i32> %mul
 }
+
+define <4 x i32> @sext_mul_v8i16_with_symmetric_constant_vector(<8 x i16> %v) {
+; CHECK-LABEL: sext_mul_v8i16_with_symmetric_constant_vector:
+; CHECK:         .functype sext_mul_v8i16_with_symmetric_constant_vector (v128) -> (v128)
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    i32x4.extend_low_i16x8_s $push24=, $0
+; CHECK-NEXT:    local.tee $push23=, $1=, $pop24
+; CHECK-NEXT:    i32x4.extract_lane $push8=, $1, 0
+; CHECK-NEXT:    i32.const $push1=, 12
+; CHECK-NEXT:    i32.shl $push9=, $pop8, $pop1
+; CHECK-NEXT:    i32x4.replace_lane $push10=, $pop23, 0, $pop9
+; CHECK-NEXT:    i32x4.extract_lane $push6=, $1, 2
+; CHECK-NEXT:    i32.const $push22=, 12
+; CHECK-NEXT:    i32.shl $push7=, $pop6, $pop22
+; CHECK-NEXT:    i32x4.replace_lane $push21=, $pop10, 2, $pop7
+; CHECK-NEXT:    local.tee $push20=, $1=, $pop21
+; CHECK-NEXT:    i32x4.extend_high_i16x8_s $push19=, $0
+; CHECK-NEXT:    local.tee $push18=, $0=, $pop19
+; CHECK-NEXT:    i32x4.extract_lane $push3=, $0, 0
+; CHECK-NEXT:    i32.const $push17=, 12
+; CHECK-NEXT:    i32.shl $push4=, $pop3, $pop17
+; CHECK-NEXT:    i32x4.replace_lane $push5=, $pop18, 0, $pop4
+; CHECK-NEXT:    i32x4.extract_lane $push0=, $0, 2
+; CHECK-NEXT:    i32.const $push16=, 12
+; CHECK-NEXT:    i32.shl $push2=, $pop0, $pop16
+; CHECK-NEXT:    i32x4.replace_lane $push15=, $pop5, 2, $pop2
+; CHECK-NEXT:    local.tee $push14=, $0=, $pop15
+; CHECK-NEXT:    i8x16.shuffle $push12=, $pop20, $pop14, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
+; CHECK-NEXT:    i8x16.shuffle $push11=, $1, $0, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
+; CHECK-NEXT:    i32x4.add $push13=, $pop12, $pop11
+; CHECK-NEXT:    return $pop13
+  %sext = sext <8 x i16> %v to <8 x i32>
+  %1 = mul nsw <8 x i32> %sext, <i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1>
+  %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
+  %3 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 1, i32 3, i32 5, i32 7>
+  %4 = add <4 x i32> %2, %3
+  ret <4 x i32> %4
+}
+
+define <4 x i32> @sext_mul_v8i16_with_symmetric_constant_vector_comm(<8 x i16> %v) {
+; CHECK-LABEL: sext_mul_v8i16_with_symmetric_constant_vector_comm:
+; CHECK:         .functype sext_mul_v8i16_with_symmetric_constant_vector_comm (v128) -> (v128)
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    i32x4.extend_low_i16x8_s $push24=, $0
+; CHECK-NEXT:    local.tee $push23=, $1=, $pop24
+; CHECK-NEXT:    i32x4.extract_lane $push8=, $1, 0
+; CHECK-NEXT:    i32.const $push1=, 12
+; CHECK-NEXT:    i32.shl $push9=, $pop8, $pop1
+; CHECK-NEXT:    i32x4.replace_lane $push10=, $pop23, 0, $pop9
+; CHECK-NEXT:    i32x4.extract_lane $push6=, $1, 2
+; CHECK-NEXT:    i32.const $push22=, 12
+; CHECK-NEXT:    i32.shl $push7=, $pop6, $pop22
+; CHECK-NEXT:    i32x4.replace_lane $push21=, $pop10, 2, $pop7
+; CHECK-NEXT:    local.tee $push20=, $1=, $pop21
+; CHECK-NEXT:    i32x4.extend_high_i16x8_s $push19=, $0
+; CHECK-NEXT:    local.tee $push18=, $0=, $pop19
+; CHECK-NEXT:    i32x4.extract_lane $push3=, $0, 0
+; CHECK-NEXT:    i32.const $push17=, 12
+; CHECK-NEXT:    i32.shl $push4=, $pop3, $pop17
+; CHECK-NEXT:    i32x4.replace_lane $push5=, $pop18, 0, $pop4
+; CHECK-NEXT:    i32x4.extract_lane $push0=, $0, 2
+; CHECK-NEXT:    i32.const $push16=, 12
+; CHECK-NEXT:    i32.shl $push2=, $pop0, $pop16
+; CHECK-NEXT:    i32x4.replace_lane $push15=, $pop5, 2, $pop2
+; CHECK-NEXT:    local.tee $push14=, $0=, $pop15
+; CHECK-NEXT:    i8x16.shuffle $push12=, $pop20, $pop14, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
+; CHECK-NEXT:    i8x16.shuffle $push11=, $1, $0, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
+; CHECK-NEXT:    i32x4.add $push13=, $pop12, $pop11
+; CHECK-NEXT:    return $pop13
+  %sext = sext <8 x i16> %v to <8 x i32>
+  %1 = mul nsw <8 x i32> <i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1>, %sext
+  %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
+  %3 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 1, i32 3, i32 5, i32 7>
+  %4 = add <4 x i32> %3, %2
+  ret <4 x i32> %4
+}
+
+define <4 x i32> @sext_mul_v8i16_with_constant(<8 x i16> %v) {
+; CHECK-LABEL: sext_mul_v8i16_with_constant:
+; CHECK:         .functype sext_mul_v8i16_with_constant (v128) -> (v128)
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    i32x4.extend_low_i16x8_s $push24=, $0
+; CHECK-NEXT:    local.tee $push23=, $1=, $pop24
+; CHECK-NEXT:    i32x4.extract_lane $push8=, $1, 0
+; CHECK-NEXT:    i32.const $push1=, 12
+; CHECK-NEXT:    i32.shl $push9=, $pop8, $pop1
+; CHECK-NEXT:    i32x4.replace_lane $push10=, $pop23, 0, $pop9
+; CHECK-NEXT:    i32x4.extract_lane $push6=, $1, 2
+; CHECK-NEXT:    i32.const $push22=, 12
+; CHECK-NEXT:    i32.shl $push7=, $pop6, $pop22
+; CHECK-NEXT:    i32x4.replace_lane $push21=, $pop10, 2, $pop7
+; CHECK-NEXT:    local.tee $push20=, $1=, $pop21
+; CHECK-NEXT:    i32x4.extend_high_i16x8_s $push19=, $0
+; CHECK-NEXT:    local.tee $push18=, $0=, $pop19
+; CHECK-NEXT:    i32x4.extract_lane $push3=, $0, 0
+; CHECK-NEXT:    i32.const $push17=, 12
+; CHECK-NEXT:    i32.shl $push4=, $pop3, $pop17
+; CHECK-NEXT:    i32x4.replace_lane $push5=, $pop18, 0, $pop4
+; CHECK-NEXT:    i32x4.extract_lane $push0=, $0, 2
+; CHECK-NEXT:    i32.const $push16=, 12
+; CHECK-NEXT:    i32.shl $push2=, $pop0, $pop16
+; CHECK-NEXT:    i32x4.replace_lane $push15=, $pop5, 2, $pop2
+; CHECK-NEXT:    local.tee $push14=, $0=, $pop15
+; CHECK-NEXT:    i8x16.shuffle $push12=, $pop20, $pop14, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
+; CHECK-NEXT:    i8x16.shuffle $push11=, $1, $0, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
+; CHECK-NEXT:    i32x4.add $push13=, $pop12, $pop11
+; CHECK-NEXT:    return $pop13
+  %sext = sext <8 x i16> %v to <8 x i32>
+  %1 = mul nsw <8 x i32> %sext, <i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1>
+  %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
+  %3 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 1, i32 3, i32 5, i32 7>
+  %4 = add <4 x i32> %2, %3
+  ret <4 x i32> %4
+}
+
+define <4 x i32> @sext_mul_v8i16_with_constant_comm(<8 x i16> %v) {
+; CHECK-LABEL: sext_mul_v8i16_with_constant_comm:
+; CHECK:         .functype sext_mul_v8i16_with_constant_comm (v128) -> (v128)
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    i32x4.extend_low_i16x8_s $push24=, $0
+; CHECK-NEXT:    local.tee $push23=, $1=, $pop24
+; CHECK-NEXT:    i32x4.extract_lane $push8=, $1, 0
+; CHECK-NEXT:    i32.const $push1=, 12
+; CHECK-NEXT:    i32.shl $push9=, $pop8, $pop1
+; CHECK-NEXT:    i32x4.replace_lane $push10=, $pop23, 0, $pop9
+; CHECK-NEXT:    i32x4.extract_lane $push6=, $1, 2
+; CHECK-NEXT:    i32.const $push22=, 12
+; CHECK-NEXT:    i32.shl $push7=, $pop6, $pop22
+; CHECK-NEXT:    i32x4.replace_lane $push21=, $pop10, 2, $pop7
+; CHECK-NEXT:    local.tee $push20=, $1=, $pop21
+; CHECK-NEXT:    i32x4.extend_high_i16x8_s $push19=, $0
+; CHECK-NEXT:    local.tee $push18=, $0=, $pop19
+; CHECK-NEXT:    i32x4.extract_lane $push3=, $0, 0
+; CHECK-NEXT:    i32.const $push17=, 12
+; CHECK-NEXT:    i32.shl $push4=, $pop3, $pop17
+; CHECK-NEXT:    i32x4.replace_lane $push5=, $pop18, 0, $pop4
+; CHECK-NEXT:    i32x4.extract_lane $push0=, $0, 2
+; CHECK-NEXT:    i32.const $push16=, 12
+; CHECK-NEXT:    i32.shl $push2=, $pop0, $pop16
+; CHECK-NEXT:    i32x4.replace_lane $push15=, $pop5, 2, $pop2
+; CHECK-NEXT:    local.tee $push14=, $0=, $pop15
+; CHECK-NEXT:    i8x16.shuffle $push12=, $pop20, $pop14, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
+; CHECK-NEXT:    i8x16.shuffle $push11=, $1, $0, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
+; CHECK-NEXT:    i32x4.add $push13=, $pop12, $pop11
+; CHECK-NEXT:    return $pop13
+  %sext = sext <8 x i16> %v to <8 x i32>
+  %1 = mul nsw <8 x i32> <i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1>, %sext
+  %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
+  %3 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 1, i32 3, i32 5, i32 7>
+  %4 = add <4 x i32> %3, %2
+  ret <4 x i32> %4
+}

>From 090768c2602fc5ca6622bec458cc5c9f457a9c4e Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Sun, 1 Mar 2026 23:06:40 +0900
Subject: [PATCH 2/9] [WebAssembly] Fold extended vector shifts by constant to
 extmul

Vector shifts of extended operands by a constant vector were lowered
into independent extend and shift nodes.
Example: `shl (WebAssemblyISD::EXTEND_LOW_S t1), <12, 0, 12, 0>`

WebAssembly SIMD lacks extended shifts but supports extended
multiplications. Converting the shift constant into a multiplier and
wrapping it in an extend node normalizes the DAG for extmul selection.

The selector matches the mul(ext, ext) structure into extmul, using
explicit undef padding to fulfill the 128-bit register constraint.

Fixed: #179143
---
 .../WebAssembly/WebAssemblyISelLowering.cpp   | 72 +++++++++++++++++++
 .../test/CodeGen/WebAssembly/wide-simd-mul.ll | 72 ++++++-------------
 2 files changed, 92 insertions(+), 52 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index faea931aeccdc..2b797101e927b 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -2797,9 +2797,81 @@ static SDValue unrollVectorShift(SDValue Op, SelectionDAG &DAG) {
   return DAG.getBuildVector(Op.getValueType(), DL, UnrolledOps);
 }
 
+/// Convert a vector shift of an extended value into a multiplication of
+/// extended values. By converting the shift amount to a multiplier (1 << C)
+/// and wrapping it in a matching extend node, we enable the instruction
+/// selector to match the pattern to WebAssembly extended multiplication
+/// instructions (e.g., i32x4.extmul_low_i16x8_s). Inactive lanes in the
+/// multiplier vector are populated with undefs.
+///
+/// Example transformation:
+/// Before:
+///   t1: v8i16 = ...
+///   t2: v4i32 = WebAssemblyISD::EXTEND_LOW_S t1
+///   t3: v4i32 = BUILD_VECTOR Constant:i32<12>, Constant:i32<0>, ...
+///   t4: v4i32 = shl t2, t3
+///
+/// After:
+///   t1: v8i16 = ...
+///   t2: v4i32 = WebAssemblyISD::EXTEND_LOW_S t1
+///   t3: v8i16 = BUILD_VECTOR Constant:i16<4096>, Constant:i16<1>, undef, ...
+///   t4: v4i32 = WebAssemblyISD::EXTEND_LOW_S t3 t5: v4i32 = mul t2, t4
+static SDValue foldShiftByConstantToExtMul(SDValue Op, SelectionDAG &DAG) {
+  if (Op.getOpcode() != ISD::SHL || !Op.getValueType().isVector())
+    return SDValue();
+
+  SDValue RHS = Op.getOperand(1);
+  if (RHS.getOpcode() != ISD::BUILD_VECTOR)
+    return SDValue();
+
+  for (SDValue LaneOp : RHS->ops()) {
+    if (!isa<ConstantSDNode>(LaneOp))
+      return SDValue();
+  }
+
+  SDLoc DL(Op);
+  SDValue LHS = Op.getOperand(0);
+  unsigned ExtOpc = LHS.getOpcode();
+  bool IsLow = false;
+  if (ExtOpc == WebAssemblyISD::EXTEND_LOW_S ||
+      ExtOpc == WebAssemblyISD::EXTEND_HIGH_S) {
+    IsLow = (ExtOpc == WebAssemblyISD::EXTEND_LOW_S);
+  } else if (ExtOpc == WebAssemblyISD::EXTEND_LOW_U ||
+             ExtOpc == WebAssemblyISD::EXTEND_HIGH_U) {
+    IsLow = (ExtOpc == WebAssemblyISD::EXTEND_LOW_U);
+  } else {
+    return SDValue();
+  }
+
+  SDValue SrcVec = LHS.getOperand(0);
+  EVT SrcVecTy = SrcVec.getValueType();
+  unsigned SrcVecEltNum = SrcVecTy.getVectorNumElements();
+  unsigned ConstVecEltNum = SrcVecEltNum / 2;
+  SmallVector<SDValue, 16> MulConsts(SrcVecEltNum,
+                                     DAG.getUNDEF(SrcVecTy.getScalarType()));
+  unsigned StartIdx = IsLow ? 0 : ConstVecEltNum;
+  for (unsigned I = 0; I < ConstVecEltNum; ++I) {
+    auto *C = cast<ConstantSDNode>(RHS.getOperand(I));
+    uint64_t ShiftAmt = C->getZExtValue();
+    if (ShiftAmt >= SrcVecTy.getScalarSizeInBits())
+      return SDValue();
+
+    uint64_t MulAmt = 1ULL << ShiftAmt;
+    MulConsts[StartIdx + I] =
+        DAG.getConstant(MulAmt, DL, SrcVecTy.getScalarType());
+  }
+
+  SDValue ConstVec = DAG.getBuildVector(SrcVecTy, DL, MulConsts);
+  SDValue ExtConstVec = DAG.getNode(ExtOpc, DL, Op.getValueType(), ConstVec);
+
+  return DAG.getNode(ISD::MUL, DL, Op.getValueType(), LHS, ExtConstVec);
+}
+
 SDValue WebAssemblyTargetLowering::LowerShift(SDValue Op,
                                               SelectionDAG &DAG) const {
   SDLoc DL(Op);
+  if (SDValue FoldedExtMul = foldShiftByConstantToExtMul(Op, DAG))
+    return FoldedExtMul;
 
   // Only manually lower vector shifts
   assert(Op.getSimpleValueType().isVector());
diff --git a/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll b/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
index 7d9c0388ad605..565d1134c5727 100644
--- a/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
+++ b/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
@@ -200,32 +200,16 @@ define <4 x i32> @sext_mul_v8i16_with_symmetric_constant_vector(<8 x i16> %v) {
 ; CHECK-LABEL: sext_mul_v8i16_with_symmetric_constant_vector:
 ; CHECK:         .functype sext_mul_v8i16_with_symmetric_constant_vector (v128) -> (v128)
 ; CHECK-NEXT:  # %bb.0:
-; CHECK-NEXT:    i32x4.extend_low_i16x8_s $push24=, $0
-; CHECK-NEXT:    local.tee $push23=, $1=, $pop24
-; CHECK-NEXT:    i32x4.extract_lane $push8=, $1, 0
-; CHECK-NEXT:    i32.const $push1=, 12
-; CHECK-NEXT:    i32.shl $push9=, $pop8, $pop1
-; CHECK-NEXT:    i32x4.replace_lane $push10=, $pop23, 0, $pop9
-; CHECK-NEXT:    i32x4.extract_lane $push6=, $1, 2
-; CHECK-NEXT:    i32.const $push22=, 12
-; CHECK-NEXT:    i32.shl $push7=, $pop6, $pop22
-; CHECK-NEXT:    i32x4.replace_lane $push21=, $pop10, 2, $pop7
-; CHECK-NEXT:    local.tee $push20=, $1=, $pop21
-; CHECK-NEXT:    i32x4.extend_high_i16x8_s $push19=, $0
-; CHECK-NEXT:    local.tee $push18=, $0=, $pop19
-; CHECK-NEXT:    i32x4.extract_lane $push3=, $0, 0
-; CHECK-NEXT:    i32.const $push17=, 12
-; CHECK-NEXT:    i32.shl $push4=, $pop3, $pop17
-; CHECK-NEXT:    i32x4.replace_lane $push5=, $pop18, 0, $pop4
-; CHECK-NEXT:    i32x4.extract_lane $push0=, $0, 2
-; CHECK-NEXT:    i32.const $push16=, 12
-; CHECK-NEXT:    i32.shl $push2=, $pop0, $pop16
-; CHECK-NEXT:    i32x4.replace_lane $push15=, $pop5, 2, $pop2
-; CHECK-NEXT:    local.tee $push14=, $0=, $pop15
-; CHECK-NEXT:    i8x16.shuffle $push12=, $pop20, $pop14, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
-; CHECK-NEXT:    i8x16.shuffle $push11=, $1, $0, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
-; CHECK-NEXT:    i32x4.add $push13=, $pop12, $pop11
-; CHECK-NEXT:    return $pop13
+; CHECK-NEXT:    v128.const $push1=, 4096, 1, 4096, 1, 0, 0, 0, 0
+; CHECK-NEXT:    i32x4.extmul_low_i16x8_s $push8=, $0, $pop1
+; CHECK-NEXT:    local.tee $push7=, $1=, $pop8
+; CHECK-NEXT:    v128.const $push0=, 0, 0, 0, 0, 4096, 1, 4096, 1
+; CHECK-NEXT:    i32x4.extmul_high_i16x8_s $push6=, $0, $pop0
+; CHECK-NEXT:    local.tee $push5=, $0=, $pop6
+; CHECK-NEXT:    i8x16.shuffle $push3=, $pop7, $pop5, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
+; CHECK-NEXT:    i8x16.shuffle $push2=, $1, $0, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
+; CHECK-NEXT:    i32x4.add $push4=, $pop3, $pop2
+; CHECK-NEXT:    return $pop4
   %sext = sext <8 x i16> %v to <8 x i32>
   %1 = mul nsw <8 x i32> %sext, <i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1>
   %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
@@ -276,32 +260,16 @@ define <4 x i32> @sext_mul_v8i16_with_constant(<8 x i16> %v) {
 ; CHECK-LABEL: sext_mul_v8i16_with_constant:
 ; CHECK:         .functype sext_mul_v8i16_with_constant (v128) -> (v128)
 ; CHECK-NEXT:  # %bb.0:
-; CHECK-NEXT:    i32x4.extend_low_i16x8_s $push24=, $0
-; CHECK-NEXT:    local.tee $push23=, $1=, $pop24
-; CHECK-NEXT:    i32x4.extract_lane $push8=, $1, 0
-; CHECK-NEXT:    i32.const $push1=, 12
-; CHECK-NEXT:    i32.shl $push9=, $pop8, $pop1
-; CHECK-NEXT:    i32x4.replace_lane $push10=, $pop23, 0, $pop9
-; CHECK-NEXT:    i32x4.extract_lane $push6=, $1, 2
-; CHECK-NEXT:    i32.const $push22=, 12
-; CHECK-NEXT:    i32.shl $push7=, $pop6, $pop22
-; CHECK-NEXT:    i32x4.replace_lane $push21=, $pop10, 2, $pop7
-; CHECK-NEXT:    local.tee $push20=, $1=, $pop21
-; CHECK-NEXT:    i32x4.extend_high_i16x8_s $push19=, $0
-; CHECK-NEXT:    local.tee $push18=, $0=, $pop19
-; CHECK-NEXT:    i32x4.extract_lane $push3=, $0, 0
-; CHECK-NEXT:    i32.const $push17=, 12
-; CHECK-NEXT:    i32.shl $push4=, $pop3, $pop17
-; CHECK-NEXT:    i32x4.replace_lane $push5=, $pop18, 0, $pop4
-; CHECK-NEXT:    i32x4.extract_lane $push0=, $0, 2
-; CHECK-NEXT:    i32.const $push16=, 12
-; CHECK-NEXT:    i32.shl $push2=, $pop0, $pop16
-; CHECK-NEXT:    i32x4.replace_lane $push15=, $pop5, 2, $pop2
-; CHECK-NEXT:    local.tee $push14=, $0=, $pop15
-; CHECK-NEXT:    i8x16.shuffle $push12=, $pop20, $pop14, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
-; CHECK-NEXT:    i8x16.shuffle $push11=, $1, $0, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
-; CHECK-NEXT:    i32x4.add $push13=, $pop12, $pop11
-; CHECK-NEXT:    return $pop13
+; CHECK-NEXT:    v128.const $push1=, 4096, 1, 4096, 1, 0, 0, 0, 0
+; CHECK-NEXT:    i32x4.extmul_low_i16x8_s $push8=, $0, $pop1
+; CHECK-NEXT:    local.tee $push7=, $1=, $pop8
+; CHECK-NEXT:    v128.const $push0=, 0, 0, 0, 0, 4096, 1, 4096, 1
+; CHECK-NEXT:    i32x4.extmul_high_i16x8_s $push6=, $0, $pop0
+; CHECK-NEXT:    local.tee $push5=, $0=, $pop6
+; CHECK-NEXT:    i8x16.shuffle $push3=, $pop7, $pop5, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
+; CHECK-NEXT:    i8x16.shuffle $push2=, $1, $0, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
+; CHECK-NEXT:    i32x4.add $push4=, $pop3, $pop2
+; CHECK-NEXT:    return $pop4
   %sext = sext <8 x i16> %v to <8 x i32>
   %1 = mul nsw <8 x i32> %sext, <i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1>
   %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>

>From 080ebb87f5175b22cf9420da7ff8de823d769766 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Mon, 2 Mar 2026 21:19:41 +0900
Subject: [PATCH 3/9] change UNDEF to POISON

---
 llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 2b797101e927b..26df436e8c794 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -2848,7 +2848,7 @@ static SDValue foldShiftByConstantToExtMul(SDValue Op, SelectionDAG &DAG) {
   unsigned SrcVecEltNum = SrcVecTy.getVectorNumElements();
   unsigned ConstVecEltNum = SrcVecEltNum / 2;
   SmallVector<SDValue, 16> MulConsts(SrcVecEltNum,
-                                     DAG.getUNDEF(SrcVecTy.getScalarType()));
+                                     DAG.getPOISON(SrcVecTy.getScalarType()));
   unsigned StartIdx = IsLow ? 0 : ConstVecEltNum;
   for (unsigned I = 0; I < ConstVecEltNum; ++I) {
     auto *C = cast<ConstantSDNode>(RHS.getOperand(I));

>From 119160ba65f98824bcf24f6af1414117264ddeb5 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Mon, 2 Mar 2026 21:39:58 +0900
Subject: [PATCH 4/9] Update the commutative testcases added by force push

---
 .../test/CodeGen/WebAssembly/wide-simd-mul.ll | 72 ++++++-------------
 1 file changed, 20 insertions(+), 52 deletions(-)

diff --git a/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll b/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
index 565d1134c5727..37f9448bf1648 100644
--- a/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
+++ b/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
@@ -222,32 +222,16 @@ define <4 x i32> @sext_mul_v8i16_with_symmetric_constant_vector_comm(<8 x i16> %
 ; CHECK-LABEL: sext_mul_v8i16_with_symmetric_constant_vector_comm:
 ; CHECK:         .functype sext_mul_v8i16_with_symmetric_constant_vector_comm (v128) -> (v128)
 ; CHECK-NEXT:  # %bb.0:
-; CHECK-NEXT:    i32x4.extend_low_i16x8_s $push24=, $0
-; CHECK-NEXT:    local.tee $push23=, $1=, $pop24
-; CHECK-NEXT:    i32x4.extract_lane $push8=, $1, 0
-; CHECK-NEXT:    i32.const $push1=, 12
-; CHECK-NEXT:    i32.shl $push9=, $pop8, $pop1
-; CHECK-NEXT:    i32x4.replace_lane $push10=, $pop23, 0, $pop9
-; CHECK-NEXT:    i32x4.extract_lane $push6=, $1, 2
-; CHECK-NEXT:    i32.const $push22=, 12
-; CHECK-NEXT:    i32.shl $push7=, $pop6, $pop22
-; CHECK-NEXT:    i32x4.replace_lane $push21=, $pop10, 2, $pop7
-; CHECK-NEXT:    local.tee $push20=, $1=, $pop21
-; CHECK-NEXT:    i32x4.extend_high_i16x8_s $push19=, $0
-; CHECK-NEXT:    local.tee $push18=, $0=, $pop19
-; CHECK-NEXT:    i32x4.extract_lane $push3=, $0, 0
-; CHECK-NEXT:    i32.const $push17=, 12
-; CHECK-NEXT:    i32.shl $push4=, $pop3, $pop17
-; CHECK-NEXT:    i32x4.replace_lane $push5=, $pop18, 0, $pop4
-; CHECK-NEXT:    i32x4.extract_lane $push0=, $0, 2
-; CHECK-NEXT:    i32.const $push16=, 12
-; CHECK-NEXT:    i32.shl $push2=, $pop0, $pop16
-; CHECK-NEXT:    i32x4.replace_lane $push15=, $pop5, 2, $pop2
-; CHECK-NEXT:    local.tee $push14=, $0=, $pop15
-; CHECK-NEXT:    i8x16.shuffle $push12=, $pop20, $pop14, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
-; CHECK-NEXT:    i8x16.shuffle $push11=, $1, $0, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
-; CHECK-NEXT:    i32x4.add $push13=, $pop12, $pop11
-; CHECK-NEXT:    return $pop13
+; CHECK-NEXT:    v128.const $push1=, 4096, 1, 4096, 1, 0, 0, 0, 0
+; CHECK-NEXT:    i32x4.extmul_low_i16x8_s $push8=, $0, $pop1
+; CHECK-NEXT:    local.tee $push7=, $1=, $pop8
+; CHECK-NEXT:    v128.const $push0=, 0, 0, 0, 0, 4096, 1, 4096, 1
+; CHECK-NEXT:    i32x4.extmul_high_i16x8_s $push6=, $0, $pop0
+; CHECK-NEXT:    local.tee $push5=, $0=, $pop6
+; CHECK-NEXT:    i8x16.shuffle $push3=, $pop7, $pop5, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
+; CHECK-NEXT:    i8x16.shuffle $push2=, $1, $0, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
+; CHECK-NEXT:    i32x4.add $push4=, $pop3, $pop2
+; CHECK-NEXT:    return $pop4
   %sext = sext <8 x i16> %v to <8 x i32>
   %1 = mul nsw <8 x i32> <i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1>, %sext
   %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
@@ -282,32 +266,16 @@ define <4 x i32> @sext_mul_v8i16_with_constant_comm(<8 x i16> %v) {
 ; CHECK-LABEL: sext_mul_v8i16_with_constant_comm:
 ; CHECK:         .functype sext_mul_v8i16_with_constant_comm (v128) -> (v128)
 ; CHECK-NEXT:  # %bb.0:
-; CHECK-NEXT:    i32x4.extend_low_i16x8_s $push24=, $0
-; CHECK-NEXT:    local.tee $push23=, $1=, $pop24
-; CHECK-NEXT:    i32x4.extract_lane $push8=, $1, 0
-; CHECK-NEXT:    i32.const $push1=, 12
-; CHECK-NEXT:    i32.shl $push9=, $pop8, $pop1
-; CHECK-NEXT:    i32x4.replace_lane $push10=, $pop23, 0, $pop9
-; CHECK-NEXT:    i32x4.extract_lane $push6=, $1, 2
-; CHECK-NEXT:    i32.const $push22=, 12
-; CHECK-NEXT:    i32.shl $push7=, $pop6, $pop22
-; CHECK-NEXT:    i32x4.replace_lane $push21=, $pop10, 2, $pop7
-; CHECK-NEXT:    local.tee $push20=, $1=, $pop21
-; CHECK-NEXT:    i32x4.extend_high_i16x8_s $push19=, $0
-; CHECK-NEXT:    local.tee $push18=, $0=, $pop19
-; CHECK-NEXT:    i32x4.extract_lane $push3=, $0, 0
-; CHECK-NEXT:    i32.const $push17=, 12
-; CHECK-NEXT:    i32.shl $push4=, $pop3, $pop17
-; CHECK-NEXT:    i32x4.replace_lane $push5=, $pop18, 0, $pop4
-; CHECK-NEXT:    i32x4.extract_lane $push0=, $0, 2
-; CHECK-NEXT:    i32.const $push16=, 12
-; CHECK-NEXT:    i32.shl $push2=, $pop0, $pop16
-; CHECK-NEXT:    i32x4.replace_lane $push15=, $pop5, 2, $pop2
-; CHECK-NEXT:    local.tee $push14=, $0=, $pop15
-; CHECK-NEXT:    i8x16.shuffle $push12=, $pop20, $pop14, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
-; CHECK-NEXT:    i8x16.shuffle $push11=, $1, $0, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
-; CHECK-NEXT:    i32x4.add $push13=, $pop12, $pop11
-; CHECK-NEXT:    return $pop13
+; CHECK-NEXT:    v128.const $push1=, 4096, 1, 4096, 1, 0, 0, 0, 0
+; CHECK-NEXT:    i32x4.extmul_low_i16x8_s $push8=, $0, $pop1
+; CHECK-NEXT:    local.tee $push7=, $1=, $pop8
+; CHECK-NEXT:    v128.const $push0=, 0, 0, 0, 0, 4096, 1, 4096, 1
+; CHECK-NEXT:    i32x4.extmul_high_i16x8_s $push6=, $0, $pop0
+; CHECK-NEXT:    local.tee $push5=, $0=, $pop6
+; CHECK-NEXT:    i8x16.shuffle $push3=, $pop7, $pop5, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
+; CHECK-NEXT:    i8x16.shuffle $push2=, $1, $0, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
+; CHECK-NEXT:    i32x4.add $push4=, $pop3, $pop2
+; CHECK-NEXT:    return $pop4
   %sext = sext <8 x i16> %v to <8 x i32>
   %1 = mul nsw <8 x i32> <i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1>, %sext
   %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>

>From 65fda080f2e4b231728860836e06855179d3a47c Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Tue, 3 Mar 2026 00:40:28 +0900
Subject: [PATCH 5/9] better logic to check if current operand is Extended low
 or high

---
 .../lib/Target/WebAssembly/WebAssemblyISelLowering.cpp | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 26df436e8c794..e42819c5e7bc8 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -2834,12 +2834,10 @@ static SDValue foldShiftByConstantToExtMul(SDValue Op, SelectionDAG &DAG) {
   unsigned ExtOpc = LHS.getOpcode();
   bool IsLow = false;
   if (ExtOpc == WebAssemblyISD::EXTEND_LOW_S ||
-      ExtOpc == WebAssemblyISD::EXTEND_HIGH_S) {
-    IsLow = (ExtOpc == WebAssemblyISD::EXTEND_LOW_S);
-  } else if (ExtOpc == WebAssemblyISD::EXTEND_LOW_U ||
-             ExtOpc == WebAssemblyISD::EXTEND_HIGH_U) {
-    IsLow = (ExtOpc == WebAssemblyISD::EXTEND_LOW_U);
-  } else {
+      ExtOpc == WebAssemblyISD::EXTEND_LOW_U) {
+    IsLow = true;
+  } else if (ExtOpc != WebAssemblyISD::EXTEND_HIGH_S &&
+             ExtOpc != WebAssemblyISD::EXTEND_HIGH_U) {
     return SDValue();
   }
 

>From 1f51dfca98417fe3207915dbd2aaf9f531bc9539 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Tue, 3 Mar 2026 01:09:14 +0900
Subject: [PATCH 6/9] Revert "better logic to check if current operand is
 Extended low or high"

This reverts commit 65fda080f2e4b231728860836e06855179d3a47c.
---
 .../lib/Target/WebAssembly/WebAssemblyISelLowering.cpp | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index e42819c5e7bc8..26df436e8c794 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -2834,10 +2834,12 @@ static SDValue foldShiftByConstantToExtMul(SDValue Op, SelectionDAG &DAG) {
   unsigned ExtOpc = LHS.getOpcode();
   bool IsLow = false;
   if (ExtOpc == WebAssemblyISD::EXTEND_LOW_S ||
-      ExtOpc == WebAssemblyISD::EXTEND_LOW_U) {
-    IsLow = true;
-  } else if (ExtOpc != WebAssemblyISD::EXTEND_HIGH_S &&
-             ExtOpc != WebAssemblyISD::EXTEND_HIGH_U) {
+      ExtOpc == WebAssemblyISD::EXTEND_HIGH_S) {
+    IsLow = (ExtOpc == WebAssemblyISD::EXTEND_LOW_S);
+  } else if (ExtOpc == WebAssemblyISD::EXTEND_LOW_U ||
+             ExtOpc == WebAssemblyISD::EXTEND_HIGH_U) {
+    IsLow = (ExtOpc == WebAssemblyISD::EXTEND_LOW_U);
+  } else {
     return SDValue();
   }
 

>From b2ac3a27db4b736d5b413a2351ff48f3d74ff5df Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Tue, 3 Mar 2026 01:46:26 +0900
Subject: [PATCH 7/9] Bail out if the shift amount causes an overflow

---
 .../WebAssembly/WebAssemblyISelLowering.cpp   |  9 ++-
 .../test/CodeGen/WebAssembly/wide-simd-mul.ll | 78 +++++++++++++++++++
 2 files changed, 85 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 26df436e8c794..9d2911c694e00 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -2832,10 +2832,11 @@ static SDValue foldShiftByConstantToExtMul(SDValue Op, SelectionDAG &DAG) {
   SDLoc DL(Op);
   SDValue LHS = Op.getOperand(0);
   unsigned ExtOpc = LHS.getOpcode();
-  bool IsLow = false;
+  bool IsLow = false, IsSigned = false;
   if (ExtOpc == WebAssemblyISD::EXTEND_LOW_S ||
       ExtOpc == WebAssemblyISD::EXTEND_HIGH_S) {
     IsLow = (ExtOpc == WebAssemblyISD::EXTEND_LOW_S);
+    IsSigned = true;
   } else if (ExtOpc == WebAssemblyISD::EXTEND_LOW_U ||
              ExtOpc == WebAssemblyISD::EXTEND_HIGH_U) {
     IsLow = (ExtOpc == WebAssemblyISD::EXTEND_LOW_U);
@@ -2847,13 +2848,17 @@ static SDValue foldShiftByConstantToExtMul(SDValue Op, SelectionDAG &DAG) {
   EVT SrcVecTy = SrcVec.getValueType();
   unsigned SrcVecEltNum = SrcVecTy.getVectorNumElements();
   unsigned ConstVecEltNum = SrcVecEltNum / 2;
+  // Cap shift amount to prevent the multiplier from hitting the sign bit
+  // and becoming negative during signed extension (e.g., 1 << 15 for i16).
+  unsigned ScalarBits = SrcVecTy.getScalarSizeInBits();
+  unsigned MaxShiftAmt = IsSigned ? (ScalarBits - 1) : ScalarBits;
   SmallVector<SDValue, 16> MulConsts(SrcVecEltNum,
                                      DAG.getPOISON(SrcVecTy.getScalarType()));
   unsigned StartIdx = IsLow ? 0 : ConstVecEltNum;
   for (unsigned I = 0; I < ConstVecEltNum; ++I) {
     auto *C = cast<ConstantSDNode>(RHS.getOperand(I));
     uint64_t ShiftAmt = C->getZExtValue();
-    if (ShiftAmt >= SrcVecTy.getScalarSizeInBits())
+    if (ShiftAmt >= MaxShiftAmt)
       return SDValue();
 
     uint64_t MulAmt = 1ULL << ShiftAmt;
diff --git a/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll b/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
index 37f9448bf1648..9d10da2edc7ea 100644
--- a/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
+++ b/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
@@ -283,3 +283,81 @@ define <4 x i32> @sext_mul_v8i16_with_constant_comm(<8 x i16> %v) {
   %4 = add <4 x i32> %3, %2
   ret <4 x i32> %4
 }
+
+; Negative - shifts by 15 overflow
+define <4 x i32> @combine_with_shl_signed_non_overflow(<8 x i16> %v) {
+; CHECK-LABEL: combine_with_shl_signed_non_overflow:
+; CHECK:         .functype combine_with_shl_signed_non_overflow (v128) -> (v128)
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    i32x4.extend_low_i16x8_s $push24=, $0
+; CHECK-NEXT:    local.tee $push23=, $1=, $pop24
+; CHECK-NEXT:    i32x4.extract_lane $push8=, $1, 1
+; CHECK-NEXT:    i32.const $push1=, 15
+; CHECK-NEXT:    i32.shl $push9=, $pop8, $pop1
+; CHECK-NEXT:    i32x4.replace_lane $push10=, $pop23, 1, $pop9
+; CHECK-NEXT:    i32x4.extract_lane $push6=, $1, 3
+; CHECK-NEXT:    i32.const $push22=, 15
+; CHECK-NEXT:    i32.shl $push7=, $pop6, $pop22
+; CHECK-NEXT:    i32x4.replace_lane $push21=, $pop10, 3, $pop7
+; CHECK-NEXT:    local.tee $push20=, $1=, $pop21
+; CHECK-NEXT:    i32x4.extend_high_i16x8_s $push19=, $0
+; CHECK-NEXT:    local.tee $push18=, $0=, $pop19
+; CHECK-NEXT:    i32x4.extract_lane $push3=, $0, 1
+; CHECK-NEXT:    i32.const $push17=, 15
+; CHECK-NEXT:    i32.shl $push4=, $pop3, $pop17
+; CHECK-NEXT:    i32x4.replace_lane $push5=, $pop18, 1, $pop4
+; CHECK-NEXT:    i32x4.extract_lane $push0=, $0, 3
+; CHECK-NEXT:    i32.const $push16=, 15
+; CHECK-NEXT:    i32.shl $push2=, $pop0, $pop16
+; CHECK-NEXT:    i32x4.replace_lane $push15=, $pop5, 3, $pop2
+; CHECK-NEXT:    local.tee $push14=, $0=, $pop15
+; CHECK-NEXT:    i8x16.shuffle $push12=, $pop20, $pop14, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
+; CHECK-NEXT:    i8x16.shuffle $push11=, $1, $0, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
+; CHECK-NEXT:    i32x4.add $push13=, $pop12, $pop11
+; CHECK-NEXT:    return $pop13
+  %sext = sext <8 x i16> %v to <8 x i32>
+  %1 = mul <8 x i32> %sext, <i32 1, i32 32768, i32 1, i32 32768, i32 1, i32 32768, i32 1, i32 32768>
+  %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
+  %3 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 1, i32 3, i32 5, i32 7>
+  %4 = add <4 x i32> %2, %3
+  ret <4 x i32> %4
+}
+
+; Negative - shifts by 16 overflow
+define <4 x i32> @combine_with_shl_unsigned_non_overflow(<8 x i16> %v) {
+; CHECK-LABEL: combine_with_shl_unsigned_non_overflow:
+; CHECK:         .functype combine_with_shl_unsigned_non_overflow (v128) -> (v128)
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    i32x4.extend_low_i16x8_u $push24=, $0
+; CHECK-NEXT:    local.tee $push23=, $1=, $pop24
+; CHECK-NEXT:    i32x4.extract_lane $push8=, $1, 1
+; CHECK-NEXT:    i32.const $push1=, 16
+; CHECK-NEXT:    i32.shl $push9=, $pop8, $pop1
+; CHECK-NEXT:    i32x4.replace_lane $push10=, $pop23, 1, $pop9
+; CHECK-NEXT:    i32x4.extract_lane $push6=, $1, 3
+; CHECK-NEXT:    i32.const $push22=, 16
+; CHECK-NEXT:    i32.shl $push7=, $pop6, $pop22
+; CHECK-NEXT:    i32x4.replace_lane $push21=, $pop10, 3, $pop7
+; CHECK-NEXT:    local.tee $push20=, $1=, $pop21
+; CHECK-NEXT:    i32x4.extend_high_i16x8_u $push19=, $0
+; CHECK-NEXT:    local.tee $push18=, $0=, $pop19
+; CHECK-NEXT:    i32x4.extract_lane $push3=, $0, 1
+; CHECK-NEXT:    i32.const $push17=, 16
+; CHECK-NEXT:    i32.shl $push4=, $pop3, $pop17
+; CHECK-NEXT:    i32x4.replace_lane $push5=, $pop18, 1, $pop4
+; CHECK-NEXT:    i32x4.extract_lane $push0=, $0, 3
+; CHECK-NEXT:    i32.const $push16=, 16
+; CHECK-NEXT:    i32.shl $push2=, $pop0, $pop16
+; CHECK-NEXT:    i32x4.replace_lane $push15=, $pop5, 3, $pop2
+; CHECK-NEXT:    local.tee $push14=, $0=, $pop15
+; CHECK-NEXT:    i8x16.shuffle $push12=, $pop20, $pop14, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
+; CHECK-NEXT:    i8x16.shuffle $push11=, $1, $0, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
+; CHECK-NEXT:    v128.or $push13=, $pop12, $pop11
+; CHECK-NEXT:    return $pop13
+  %zext = zext <8 x i16> %v to <8 x i32>
+  %1 = mul <8 x i32> %zext, <i32 1, i32 65536, i32 1, i32 65536, i32 1, i32 65536, i32 1, i32 65536>
+  %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
+  %3 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 1, i32 3, i32 5, i32 7>
+  %4 = add <4 x i32> %2, %3
+  ret <4 x i32> %4
+}

>From 1e75f5f7d357ceda2e2871937065fd14808e69d7 Mon Sep 17 00:00:00 2001
From: hanbeom <kese111 at gmail.com>
Date: Sat, 7 Mar 2026 16:15:03 +0900
Subject: [PATCH 8/9] directly lowers a wide vector shift (v8i32 shl) node into
 a pair of 128-bit target-native extended multiplications (extmul_low,
 extmul_high) and merges them using concat_vectors

---
 .../WebAssembly/WebAssemblyISelLowering.cpp   | 148 +++++++++---------
 .../test/CodeGen/WebAssembly/wide-simd-mul.ll |  52 ++----
 2 files changed, 82 insertions(+), 118 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 9d2911c694e00..ed1832e4b0d91 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -199,6 +199,7 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
 
     // Combine wide-vector muls, with extend inputs, to extmul_half.
     setTargetDAGCombine(ISD::MUL);
+    setTargetDAGCombine(ISD::SHL);
 
     // Combine vector mask reductions into alltrue/anytrue
     setTargetDAGCombine(ISD::SETCC);
@@ -2797,87 +2798,9 @@ static SDValue unrollVectorShift(SDValue Op, SelectionDAG &DAG) {
   return DAG.getBuildVector(Op.getValueType(), DL, UnrolledOps);
 }
 
-/// Convert a vector shift of an extended value into a multiplication of
-/// extended values. By converting the shift amount to a multiplier (1 << C)
-/// and wrapping it in a matching extend node, we enable the instruction
-/// selector to match the pattern to WebAssembly extended multiplication
-/// instructions (e.g., i32x4.extmul_low_i16x8_s). Inactive lanes in the
-/// multiplier vector are populated with undefs.
-///
-/// Example transformation:
-/// Before:
-///   t1: v8i16 = ...
-///   t2: v4i32 = WebAssemblyISD::EXTEND_LOW_S t1
-///   t3: v4i32 = BUILD_VECTOR Constant:i32<12>, Constant:i32<0>, ...
-///   t4: v4i32 = shl t2, t3
-///
-/// After:
-///   t1: v8i16 = ...
-///   t2: v4i32 = WebAssemblyISD::EXTEND_LOW_S t1
-///   t3: v8i16 = BUILD_VECTOR Constant:i16<4096>, Constant:i16<1>, undef, ...
-///   t4: v4i32 = WebAssemblyISD::EXTEND_LOW_S t3 t5: v4i32 = mul t2, t4
-static SDValue foldShiftByConstantToExtMul(SDValue Op, SelectionDAG &DAG) {
-  if (Op.getOpcode() != ISD::SHL || !Op.getValueType().isVector())
-    return SDValue();
-
-  SDValue RHS = Op.getOperand(1);
-  if (RHS.getOpcode() != ISD::BUILD_VECTOR)
-    return SDValue();
-
-  for (SDValue LaneOp : RHS->ops()) {
-    if (!isa<ConstantSDNode>(LaneOp))
-      return SDValue();
-  }
-
-  SDLoc DL(Op);
-  SDValue LHS = Op.getOperand(0);
-  unsigned ExtOpc = LHS.getOpcode();
-  bool IsLow = false, IsSigned = false;
-  if (ExtOpc == WebAssemblyISD::EXTEND_LOW_S ||
-      ExtOpc == WebAssemblyISD::EXTEND_HIGH_S) {
-    IsLow = (ExtOpc == WebAssemblyISD::EXTEND_LOW_S);
-    IsSigned = true;
-  } else if (ExtOpc == WebAssemblyISD::EXTEND_LOW_U ||
-             ExtOpc == WebAssemblyISD::EXTEND_HIGH_U) {
-    IsLow = (ExtOpc == WebAssemblyISD::EXTEND_LOW_U);
-  } else {
-    return SDValue();
-  }
-
-  SDValue SrcVec = LHS.getOperand(0);
-  EVT SrcVecTy = SrcVec.getValueType();
-  unsigned SrcVecEltNum = SrcVecTy.getVectorNumElements();
-  unsigned ConstVecEltNum = SrcVecEltNum / 2;
-  // Cap shift amount to prevent the multiplier from hitting the sign bit
-  // and becoming negative during signed extension (e.g., 1 << 15 for i16).
-  unsigned ScalarBits = SrcVecTy.getScalarSizeInBits();
-  unsigned MaxShiftAmt = IsSigned ? (ScalarBits - 1) : ScalarBits;
-  SmallVector<SDValue, 16> MulConsts(SrcVecEltNum,
-                                     DAG.getPOISON(SrcVecTy.getScalarType()));
-  unsigned StartIdx = IsLow ? 0 : ConstVecEltNum;
-  for (unsigned I = 0; I < ConstVecEltNum; ++I) {
-    auto *C = cast<ConstantSDNode>(RHS.getOperand(I));
-    uint64_t ShiftAmt = C->getZExtValue();
-    if (ShiftAmt >= MaxShiftAmt)
-      return SDValue();
-
-    uint64_t MulAmt = 1ULL << ShiftAmt;
-    MulConsts[StartIdx + I] =
-        DAG.getConstant(MulAmt, DL, SrcVecTy.getScalarType());
-  }
-
-  SDValue ConstVec = DAG.getBuildVector(SrcVecTy, DL, MulConsts);
-  SDValue ExtConstVec = DAG.getNode(ExtOpc, DL, Op.getValueType(), ConstVec);
-
-  return DAG.getNode(ISD::MUL, DL, Op.getValueType(), LHS, ExtConstVec);
-}
-
 SDValue WebAssemblyTargetLowering::LowerShift(SDValue Op,
                                               SelectionDAG &DAG) const {
   SDLoc DL(Op);
-  if (SDValue FoldedExtMul = foldShiftByConstantToExtMul(Op, DAG))
-    return FoldedExtMul;
-
   // Only manually lower vector shifts
   assert(Op.getSimpleValueType().isVector());
 
@@ -3791,6 +3714,73 @@ SDValue performConvertFPCombine(SDNode *N, SelectionDAG &DAG) {
   return SDValue();
 }
 
+// Wide vector shift operations such as v8i32 with sign-extended
+// operands cause Type Legalizer crashes because the target-specific
+// extension nodes cannot be directly mapped to the 256-bit size.
+//
+// To resolve the crash and optimize performance, we intercept the
+// illegal v8i32 shift in DAGCombine. We convert the shift amounts
+// into multipliers and manually split the vector into two v4i32 halves.
+//
+// Before: t1: v8i32 = shl (sign_extend v8i16), const_vec
+// After : t2: v4i32 = mul (ext_low_s v8i16), (ext_low_s narrow_vec)
+//         t3: v4i32 = mul (ext_high_s v8i16), (ext_high_s narrow_vec)
+//         t4: v8i32 = concat_vectors t2, t3
+static SDValue performShiftCombine(SDNode *N,
+                                   TargetLowering::DAGCombinerInfo &DCI) {
+  SelectionDAG &DAG = DCI.DAG;
+  assert(N->getOpcode() == ISD::SHL);
+  EVT VT = N->getValueType(0);
+  if (VT != MVT::v8i32)
+    return SDValue();
+
+  SDValue LHS = N->getOperand(0);
+  SDValue RHS = N->getOperand(1);
+  unsigned ExtOpc = LHS.getOpcode();
+  if (ExtOpc != ISD::SIGN_EXTEND && ExtOpc != ISD::ZERO_EXTEND)
+    return SDValue();
+
+  if (RHS.getOpcode() != ISD::BUILD_VECTOR)
+    return SDValue();
+
+  SDLoc DL(N);
+  SDValue ExtendIn = LHS.getOperand(0);
+  EVT FromVT = ExtendIn.getValueType();
+  unsigned NumElts = VT.getVectorNumElements();
+  unsigned BitWidth = FromVT.getScalarSizeInBits();
+  bool IsSigned = (ExtOpc == ISD::SIGN_EXTEND);
+  unsigned MaxValidShift = IsSigned ? (BitWidth - 1) : BitWidth;
+  SmallVector<SDValue, 16> MulConsts;
+  for (unsigned I = 0; I < NumElts; ++I) {
+    auto *C = dyn_cast<ConstantSDNode>(RHS.getOperand(I));
+    if (!C)
+      return SDValue();
+
+    const APInt &ShiftAmt = C->getAPIntValue();
+    if (ShiftAmt.uge(MaxValidShift))
+      return SDValue();
+
+    APInt MulAmt = APInt(BitWidth, 1).shl(ShiftAmt);
+    MulConsts.push_back(DAG.getConstant(MulAmt, DL, FromVT.getScalarType(),
+                                        /*isTarget=*/false, /*isOpaque=*/true));
+  }
+
+  SDValue NarrowConst = DAG.getBuildVector(FromVT, DL, MulConsts);
+  unsigned ExtLowOpc =
+      IsSigned ? WebAssemblyISD::EXTEND_LOW_S : WebAssemblyISD::EXTEND_LOW_U;
+  unsigned ExtHighOpc =
+      IsSigned ? WebAssemblyISD::EXTEND_HIGH_S : WebAssemblyISD::EXTEND_HIGH_U;
+
+  EVT HalfVT = MVT::v4i32;
+  SDValue LHSLo = DAG.getNode(ExtLowOpc, DL, HalfVT, ExtendIn);
+  SDValue LHSHi = DAG.getNode(ExtHighOpc, DL, HalfVT, ExtendIn);
+  SDValue RHSLo = DAG.getNode(ExtLowOpc, DL, HalfVT, NarrowConst);
+  SDValue RHSHi = DAG.getNode(ExtHighOpc, DL, HalfVT, NarrowConst);
+  SDValue MulLo = DAG.getNode(ISD::MUL, DL, HalfVT, LHSLo, RHSLo);
+  SDValue MulHi = DAG.getNode(ISD::MUL, DL, HalfVT, LHSHi, RHSHi);
+  return DAG.getNode(ISD::CONCAT_VECTORS, DL, VT, MulLo, MulHi);
+}
+
 SDValue
 WebAssemblyTargetLowering::PerformDAGCombine(SDNode *N,
                                              DAGCombinerInfo &DCI) const {
@@ -3826,5 +3816,7 @@ WebAssemblyTargetLowering::PerformDAGCombine(SDNode *N,
     return performAnyAllCombine(N, DCI.DAG);
   case ISD::MUL:
     return performMulCombine(N, DCI);
+  case ISD::SHL:
+    return performShiftCombine(N, DCI);
   }
 }
diff --git a/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll b/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
index 9d10da2edc7ea..0d14858919569 100644
--- a/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
+++ b/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
@@ -200,16 +200,9 @@ define <4 x i32> @sext_mul_v8i16_with_symmetric_constant_vector(<8 x i16> %v) {
 ; CHECK-LABEL: sext_mul_v8i16_with_symmetric_constant_vector:
 ; CHECK:         .functype sext_mul_v8i16_with_symmetric_constant_vector (v128) -> (v128)
 ; CHECK-NEXT:  # %bb.0:
-; CHECK-NEXT:    v128.const $push1=, 4096, 1, 4096, 1, 0, 0, 0, 0
-; CHECK-NEXT:    i32x4.extmul_low_i16x8_s $push8=, $0, $pop1
-; CHECK-NEXT:    local.tee $push7=, $1=, $pop8
-; CHECK-NEXT:    v128.const $push0=, 0, 0, 0, 0, 4096, 1, 4096, 1
-; CHECK-NEXT:    i32x4.extmul_high_i16x8_s $push6=, $0, $pop0
-; CHECK-NEXT:    local.tee $push5=, $0=, $pop6
-; CHECK-NEXT:    i8x16.shuffle $push3=, $pop7, $pop5, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
-; CHECK-NEXT:    i8x16.shuffle $push2=, $1, $0, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
-; CHECK-NEXT:    i32x4.add $push4=, $pop3, $pop2
-; CHECK-NEXT:    return $pop4
+; CHECK-NEXT:    v128.const $push0=, 4096, 1, 4096, 1, 4096, 1, 4096, 1
+; CHECK-NEXT:    i32x4.dot_i16x8_s $push1=, $0, $pop0
+; CHECK-NEXT:    return $pop1
   %sext = sext <8 x i16> %v to <8 x i32>
   %1 = mul nsw <8 x i32> %sext, <i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1>
   %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
@@ -222,16 +215,9 @@ define <4 x i32> @sext_mul_v8i16_with_symmetric_constant_vector_comm(<8 x i16> %
 ; CHECK-LABEL: sext_mul_v8i16_with_symmetric_constant_vector_comm:
 ; CHECK:         .functype sext_mul_v8i16_with_symmetric_constant_vector_comm (v128) -> (v128)
 ; CHECK-NEXT:  # %bb.0:
-; CHECK-NEXT:    v128.const $push1=, 4096, 1, 4096, 1, 0, 0, 0, 0
-; CHECK-NEXT:    i32x4.extmul_low_i16x8_s $push8=, $0, $pop1
-; CHECK-NEXT:    local.tee $push7=, $1=, $pop8
-; CHECK-NEXT:    v128.const $push0=, 0, 0, 0, 0, 4096, 1, 4096, 1
-; CHECK-NEXT:    i32x4.extmul_high_i16x8_s $push6=, $0, $pop0
-; CHECK-NEXT:    local.tee $push5=, $0=, $pop6
-; CHECK-NEXT:    i8x16.shuffle $push3=, $pop7, $pop5, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
-; CHECK-NEXT:    i8x16.shuffle $push2=, $1, $0, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
-; CHECK-NEXT:    i32x4.add $push4=, $pop3, $pop2
-; CHECK-NEXT:    return $pop4
+; CHECK-NEXT:    v128.const $push0=, 4096, 1, 4096, 1, 4096, 1, 4096, 1
+; CHECK-NEXT:    i32x4.dot_i16x8_s $push1=, $0, $pop0
+; CHECK-NEXT:    return $pop1
   %sext = sext <8 x i16> %v to <8 x i32>
   %1 = mul nsw <8 x i32> <i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1>, %sext
   %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
@@ -244,16 +230,9 @@ define <4 x i32> @sext_mul_v8i16_with_constant(<8 x i16> %v) {
 ; CHECK-LABEL: sext_mul_v8i16_with_constant:
 ; CHECK:         .functype sext_mul_v8i16_with_constant (v128) -> (v128)
 ; CHECK-NEXT:  # %bb.0:
-; CHECK-NEXT:    v128.const $push1=, 4096, 1, 4096, 1, 0, 0, 0, 0
-; CHECK-NEXT:    i32x4.extmul_low_i16x8_s $push8=, $0, $pop1
-; CHECK-NEXT:    local.tee $push7=, $1=, $pop8
-; CHECK-NEXT:    v128.const $push0=, 0, 0, 0, 0, 4096, 1, 4096, 1
-; CHECK-NEXT:    i32x4.extmul_high_i16x8_s $push6=, $0, $pop0
-; CHECK-NEXT:    local.tee $push5=, $0=, $pop6
-; CHECK-NEXT:    i8x16.shuffle $push3=, $pop7, $pop5, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
-; CHECK-NEXT:    i8x16.shuffle $push2=, $1, $0, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
-; CHECK-NEXT:    i32x4.add $push4=, $pop3, $pop2
-; CHECK-NEXT:    return $pop4
+; CHECK-NEXT:    v128.const $push0=, 4096, 1, 4096, 1, 4096, 1, 4096, 1
+; CHECK-NEXT:    i32x4.dot_i16x8_s $push1=, $0, $pop0
+; CHECK-NEXT:    return $pop1
   %sext = sext <8 x i16> %v to <8 x i32>
   %1 = mul nsw <8 x i32> %sext, <i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1>
   %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
@@ -266,16 +245,9 @@ define <4 x i32> @sext_mul_v8i16_with_constant_comm(<8 x i16> %v) {
 ; CHECK-LABEL: sext_mul_v8i16_with_constant_comm:
 ; CHECK:         .functype sext_mul_v8i16_with_constant_comm (v128) -> (v128)
 ; CHECK-NEXT:  # %bb.0:
-; CHECK-NEXT:    v128.const $push1=, 4096, 1, 4096, 1, 0, 0, 0, 0
-; CHECK-NEXT:    i32x4.extmul_low_i16x8_s $push8=, $0, $pop1
-; CHECK-NEXT:    local.tee $push7=, $1=, $pop8
-; CHECK-NEXT:    v128.const $push0=, 0, 0, 0, 0, 4096, 1, 4096, 1
-; CHECK-NEXT:    i32x4.extmul_high_i16x8_s $push6=, $0, $pop0
-; CHECK-NEXT:    local.tee $push5=, $0=, $pop6
-; CHECK-NEXT:    i8x16.shuffle $push3=, $pop7, $pop5, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
-; CHECK-NEXT:    i8x16.shuffle $push2=, $1, $0, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
-; CHECK-NEXT:    i32x4.add $push4=, $pop3, $pop2
-; CHECK-NEXT:    return $pop4
+; CHECK-NEXT:    v128.const $push0=, 4096, 1, 4096, 1, 4096, 1, 4096, 1
+; CHECK-NEXT:    i32x4.dot_i16x8_s $push1=, $0, $pop0
+; CHECK-NEXT:    return $pop1
   %sext = sext <8 x i16> %v to <8 x i32>
   %1 = mul nsw <8 x i32> <i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1, i32 4096, i32 1>, %sext
   %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>

>From 67bc496d35299f49cd2d509ca473b5b4cf9a551e Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Mon, 9 Mar 2026 19:31:36 +0900
Subject: [PATCH 9/9] Restrict wide shift to extmul combine to v8i16 inputs

---
 .../WebAssembly/WebAssemblyISelLowering.cpp   |  3 +
 .../test/CodeGen/WebAssembly/wide-simd-mul.ll | 72 +++++++++++++++++++
 2 files changed, 75 insertions(+)

diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index ed1832e4b0d91..fd13e59373661 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -3746,6 +3746,9 @@ static SDValue performShiftCombine(SDNode *N,
   SDLoc DL(N);
   SDValue ExtendIn = LHS.getOperand(0);
   EVT FromVT = ExtendIn.getValueType();
+  if (FromVT != MVT::v8i16)
+    return SDValue();
+
   unsigned NumElts = VT.getVectorNumElements();
   unsigned BitWidth = FromVT.getScalarSizeInBits();
   bool IsSigned = (ExtOpc == ISD::SIGN_EXTEND);
diff --git a/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll b/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
index 0d14858919569..25dc1efedfb95 100644
--- a/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
+++ b/llvm/test/CodeGen/WebAssembly/wide-simd-mul.ll
@@ -256,6 +256,78 @@ define <4 x i32> @sext_mul_v8i16_with_constant_comm(<8 x i16> %v) {
   ret <4 x i32> %4
 }
 
+; Negative - unsupported type
+define <4 x i32> @sext_mul_v8i1_pow2(<8 x i1> %v) {
+; CHECK-LABEL: sext_mul_v8i1_pow2:
+; CHECK:         .functype sext_mul_v8i1_pow2 (v128) -> (v128)
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    i32x4.extend_low_i16x8_u $push3=, $0
+; CHECK-NEXT:    i32.const $push1=, 31
+; CHECK-NEXT:    i32x4.shl $push4=, $pop3, $pop1
+; CHECK-NEXT:    i32.const $push14=, 31
+; CHECK-NEXT:    i32x4.shr_s $push13=, $pop4, $pop14
+; CHECK-NEXT:    local.tee $push12=, $1=, $pop13
+; CHECK-NEXT:    i32x4.extend_high_i16x8_u $push0=, $0
+; CHECK-NEXT:    i32.const $push11=, 31
+; CHECK-NEXT:    i32x4.shl $push2=, $pop0, $pop11
+; CHECK-NEXT:    i32.const $push10=, 31
+; CHECK-NEXT:    i32x4.shr_s $push9=, $pop2, $pop10
+; CHECK-NEXT:    local.tee $push8=, $0=, $pop9
+; CHECK-NEXT:    i8x16.shuffle $push6=, $pop12, $pop8, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
+; CHECK-NEXT:    i8x16.shuffle $push5=, $1, $0, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
+; CHECK-NEXT:    i32x4.add $push7=, $pop6, $pop5
+; CHECK-NEXT:    return $pop7
+  %sext = sext <8 x i1> %v to <8 x i32>
+  %1 = mul nsw <8 x i32> <i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1>, %sext
+  %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
+  %3 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 1, i32 3, i32 5, i32 7>
+  %4 = add <4 x i32> %3, %2
+  ret <4 x i32> %4
+}
+
+; Negative - unsupported type
+define <4 x i32> @sext_mul_v8i8_pow2(<8 x i8> %v) {
+; CHECK-LABEL: sext_mul_v8i8_pow2:
+; CHECK:         .functype sext_mul_v8i8_pow2 (v128) -> (v128)
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    i16x8.extend_low_i8x16_s $push17=, $0
+; CHECK-NEXT:    i32x4.extend_low_i16x8_s $push18=, $pop17
+; CHECK-NEXT:    v128.const $push19=, 0, 1, 2, 4
+; CHECK-NEXT:    i32x4.mul $push28=, $pop18, $pop19
+; CHECK-NEXT:    local.tee $push27=, $1=, $pop28
+; CHECK-NEXT:    i8x16.shuffle $push0=, $0, $0, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+; CHECK-NEXT:    i16x8.extend_low_i8x16_s $push1=, $pop0
+; CHECK-NEXT:    i32x4.extend_low_i16x8_s $push26=, $pop1
+; CHECK-NEXT:    local.tee $push25=, $0=, $pop26
+; CHECK-NEXT:    i32x4.extract_lane $push5=, $pop25, 0
+; CHECK-NEXT:    i32.const $push6=, 3
+; CHECK-NEXT:    i32.shl $push7=, $pop5, $pop6
+; CHECK-NEXT:    i32x4.splat $push8=, $pop7
+; CHECK-NEXT:    i32x4.extract_lane $push2=, $0, 1
+; CHECK-NEXT:    i32.const $push3=, 4
+; CHECK-NEXT:    i32.shl $push4=, $pop2, $pop3
+; CHECK-NEXT:    i32x4.replace_lane $push9=, $pop8, 1, $pop4
+; CHECK-NEXT:    i32x4.extract_lane $push10=, $0, 2
+; CHECK-NEXT:    i32.const $push11=, 5
+; CHECK-NEXT:    i32.shl $push12=, $pop10, $pop11
+; CHECK-NEXT:    i32x4.replace_lane $push13=, $pop9, 2, $pop12
+; CHECK-NEXT:    i32x4.extract_lane $push14=, $0, 3
+; CHECK-NEXT:    i32.const $push15=, 6
+; CHECK-NEXT:    i32.shl $push16=, $pop14, $pop15
+; CHECK-NEXT:    i32x4.replace_lane $push24=, $pop13, 3, $pop16
+; CHECK-NEXT:    local.tee $push23=, $0=, $pop24
+; CHECK-NEXT:    i8x16.shuffle $push21=, $pop27, $pop23, 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31
+; CHECK-NEXT:    i8x16.shuffle $push20=, $1, $0, 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27
+; CHECK-NEXT:    i32x4.add $push22=, $pop21, $pop20
+; CHECK-NEXT:    return $pop22
+  %sext = sext <8 x i8> %v to <8 x i32>
+  %1 = mul nsw <8 x i32> <i32 0, i32 1, i32 2, i32 4, i32 8, i32 16, i32 32, i32 64>, %sext
+  %2 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
+  %3 = shufflevector <8 x i32> %1, <8 x i32> poison, <4 x i32> <i32 1, i32 3, i32 5, i32 7>
+  %4 = add <4 x i32> %3, %2
+  ret <4 x i32> %4
+}
+
 ; Negative - shifts by 15 overflow
 define <4 x i32> @combine_with_shl_signed_non_overflow(<8 x i16> %v) {
 ; CHECK-LABEL: combine_with_shl_signed_non_overflow:



More information about the llvm-commits mailing list