[WebAssembly] Combine shuffle and signed extend to extend_high (#179166)

Fold shuffles and bitcasts feeding extend_low_s into extend_high_s.
This enables i32x4.dot_i16x8_s selection and removes redundant shuffles.

Fixed: https://github.com/llvm/llvm-project/issues/179145
This commit is contained in:
hanbeom 2026-02-03 17:02:53 +09:00 committed by GitHub
parent dcfa0ab372
commit 22f53531d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 45 additions and 3 deletions

View file

@ -2294,13 +2294,14 @@ WebAssemblyTargetLowering::LowerSIGN_EXTEND_INREG(SDValue Op,
static SDValue GetExtendHigh(SDValue Op, unsigned UserOpc, EVT VT,
SelectionDAG &DAG) {
if (Op.getOpcode() != ISD::VECTOR_SHUFFLE)
SDValue Source = peekThroughBitcasts(Op);
if (Source.getOpcode() != ISD::VECTOR_SHUFFLE)
return SDValue();
assert((UserOpc == WebAssemblyISD::EXTEND_LOW_U ||
UserOpc == WebAssemblyISD::EXTEND_LOW_S) &&
"expected extend_low");
auto *Shuffle = cast<ShuffleVectorSDNode>(Op.getNode());
auto *Shuffle = cast<ShuffleVectorSDNode>(Source.getNode());
ArrayRef<int> Mask = Shuffle->getMask();
// Look for a shuffle which moves from the high half to the low half.
@ -2315,7 +2316,11 @@ static SDValue GetExtendHigh(SDValue Op, unsigned UserOpc, EVT VT,
unsigned Opc = UserOpc == WebAssemblyISD::EXTEND_LOW_S
? WebAssemblyISD::EXTEND_HIGH_S
: WebAssemblyISD::EXTEND_HIGH_U;
return DAG.getNode(Opc, DL, VT, Shuffle->getOperand(0));
SDValue ShuffleSrc = Shuffle->getOperand(0);
if (Op.getOpcode() == ISD::BITCAST)
ShuffleSrc = DAG.getBitcast(Op.getValueType(), ShuffleSrc);
return DAG.getNode(Opc, DL, VT, ShuffleSrc);
}
SDValue

View file

@ -104,3 +104,40 @@ define <4 x i32> @dot_wrong_shuffle(<8 x i16> %a, <8 x i16> %b) {
%res = add <4 x i32> %shuffle1, %shuffle2
ret <4 x i32> %res
}
define dso_local <4 x i32> @dot_with_bitcast_both(<4 x i32> %a, <4 x i32> %b) unnamed_addr {
; CHECK-LABEL: dot_with_bitcast_both:
; CHECK: .functype dot_with_bitcast_both (v128, v128) -> (v128)
; CHECK-NEXT: # %bb.0: # %start
; CHECK-NEXT: local.get 1
; CHECK-NEXT: local.get 0
; CHECK-NEXT: i32x4.dot_i16x8_s
; CHECK-NEXT: # fallthrough-return
start:
%_4 = bitcast <4 x i32> %a to <8 x i16>
%_5 = bitcast <4 x i32> %b to <8 x i16>
%0 = sext <8 x i16> %_4 to <8 x i32>
%1 = sext <8 x i16> %_5 to <8 x i32>
%2 = mul nsw <8 x i32> %1, %0
%3 = shufflevector <8 x i32> %2, <8 x i32> poison, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
%4 = shufflevector <8 x i32> %2, <8 x i32> poison, <4 x i32> <i32 1, i32 3, i32 5, i32 7>
%5 = add <4 x i32> %3, %4
ret <4 x i32> %5
}
define <4 x i32> @dot_with_bitcast_one(<4 x i32> %a) {
; CHECK-LABEL: dot_with_bitcast_one:
; CHECK: .functype dot_with_bitcast_one (v128) -> (v128)
; CHECK-NEXT: # %bb.0: # %start
; CHECK-NEXT: local.get 0
; CHECK-NEXT: i32x4.extadd_pairwise_i16x8_s
; CHECK-NEXT: # fallthrough-return
start:
%a1 = bitcast <4 x i32> %a to <8 x i16>
%0 = shufflevector <8 x i16> %a1, <8 x i16> %a1, <4 x i32> <i32 0, i32 2, i32 4, i32 6>
%1 = shufflevector <8 x i16> %a1, <8 x i16> %a1, <4 x i32> <i32 1, i32 3, i32 5, i32 7>
%2 = sext <4 x i16> %0 to <4 x i32>
%3 = sext <4 x i16> %1 to <4 x i32>
%4 = add nsw <4 x i32> %2, %3
ret <4 x i32> %4
}