Skip to content

Conversation

@badumbatish
Copy link
Contributor

Fixes #71844

@llvmbot
Copy link
Member

llvmbot commented Aug 6, 2025

@llvm/pr-subscribers-backend-webassembly

Author: Jasmine Tang (badumbatish)

Changes

Fixes #71844


Full diff: https://github.com/llvm/llvm-project/pull/152397.diff

2 Files Affected:

  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp (+18)
  • (modified) llvm/test/CodeGen/WebAssembly/masked-shifts.ll (+15)
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 3f80b2ab2bd6d..325b01eccf67d 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -216,6 +216,7 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
 
     setTargetDAGCombine(ISD::TRUNCATE);
 
+    setTargetDAGCombine(ISD::SHL);
     // Support saturating add/sub for i8x16 and i16x8
     for (auto Op : {ISD::SADDSAT, ISD::UADDSAT, ISD::SSUBSAT, ISD::USUBSAT})
       for (auto T : {MVT::v16i8, MVT::v8i16})
@@ -3562,6 +3563,21 @@ static SDValue performMulCombine(SDNode *N,
       {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30});
 }
 
+static SDValue performSHLCombine(SDNode *N, SelectionDAG &DAG) {
+  assert(N->getOpcode() == ISD::SHL);
+  if (N->getValueType(0) != MVT::i64)
+    return SDValue();
+  using namespace llvm::SDPatternMatch;
+  SDValue A, B;
+  APInt I;
+  if (sd_match(N,
+               m_Shl(m_Value(A), m_ZExt(m_And(m_Value(B), m_ConstInt(I)))))) {
+    if (I.getSExtValue() == 63)
+      return DAG.getNode(ISD::SHL, SDLoc(N), MVT::i64, {A, B});
+  }
+  return SDValue();
+}
+
 SDValue
 WebAssemblyTargetLowering::PerformDAGCombine(SDNode *N,
                                              DAGCombinerInfo &DCI) const {
@@ -3597,5 +3613,7 @@ WebAssemblyTargetLowering::PerformDAGCombine(SDNode *N,
   }
   case ISD::MUL:
     return performMulCombine(N, DCI);
+  case ISD::SHL:
+    return performSHLCombine(N, DCI.DAG);
   }
 }
diff --git a/llvm/test/CodeGen/WebAssembly/masked-shifts.ll b/llvm/test/CodeGen/WebAssembly/masked-shifts.ll
index 5bcb023e546b5..45c79df5f3f2b 100644
--- a/llvm/test/CodeGen/WebAssembly/masked-shifts.ll
+++ b/llvm/test/CodeGen/WebAssembly/masked-shifts.ll
@@ -18,6 +18,21 @@ define i32 @shl_i32(i32 %v, i32 %x) {
   ret i32 %a
 }
 
+define i64 @shl_i64_i32(i64 %v, i32 %x) {
+; CHECK-LABEL: shl_i64_i32:
+; CHECK:         .functype shl_i64_i32 (i64, i32) -> (i64)
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    i64.extend_i32_u
+; CHECK-NEXT:    i64.shl
+; CHECK-NEXT:    # fallthrough-return
+  %m = and i32 %x, 63
+  %z = zext i32 %m to i64
+  %a = shl i64 %v, %z
+  ret i64 %a
+}
+
 define i32 @sra_i32(i32 %v, i32 %x) {
 ; CHECK-LABEL: sra_i32:
 ; CHECK:         .functype sra_i32 (i32, i32) -> (i32)

@topperc
Copy link
Collaborator

topperc commented Aug 6, 2025

This can't be done in DAGCombine. If the shift amount for an ISD::SHL is larger than 63 the result is considered poison. The mask has to stay to keep the shift amount valid. Other targets that remove the mask due to it during isel. RISC-V for example uses a complex pattern that calls RISCVDAGToDAGISel::selectShiftMask. AArch64 has AArch64DAGToDAGISel::tryShiftAmountMod

@badumbatish
Copy link
Contributor Author

i can move this to tablegen but i dont quite understand this point - If the shift amount for an ISD::SHL is larger than 63 the result is considered poison. If I don't change anything for any mask larger than 63, shouldn't it still be ok to be at DAGCombine?

@topperc
Copy link
Collaborator

topperc commented Aug 6, 2025

i can move this to tablegen but i dont quite understand this point - If the shift amount for an ISD::SHL is larger than 63 the result is considered poison. If I don't change anything for any mask larger than 63, shouldn't it still be ok to be at DAGCombine?

If the input to the AND is larger than 63, the mask will clear the upper bits to turn it into a value that is between 0 and 63. If you remove the mask without proving the upper bits are 0, then you might allow a value larger than 63 through to the shift.

@badumbatish
Copy link
Contributor Author

ohh i see, i'll redo the pr

Ret = DAG.getNOT(DL, Ret, MVT::i1);
return DAG.getZExtOrTrunc(Ret, DL, N->getValueType(0));
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what changed in the clang formatting tools but i ran the following commands and this pops up

clang/tools/clang-format/git-clang-format --binary ./build/bin/clang-format HEAD~1

@ppenzin ppenzin self-requested a review November 26, 2025 01:04
This reverts commit 40e9092 and replace
it with a simple constant 63 mask
Copy link
Contributor

@lukel97 lukel97 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks! If you're looking for a follow up PR, I think the i64 sra/srl patterns can get the same optimisation too right?

@badumbatish badumbatish enabled auto-merge (squash) December 1, 2025 11:10
@badumbatish badumbatish merged commit edd1856 into llvm:main Dec 1, 2025
9 of 10 checks passed
aahrun pushed a commit to aahrun/llvm-project that referenced this pull request Dec 1, 2025
@llvm-ci
Copy link
Collaborator

llvm-ci commented Dec 1, 2025

LLVM Buildbot has detected a new failure on builder ppc64le-mlir-rhel-clang running on ppc64le-mlir-rhel-test while building llvm at step 6 "test-build-check-mlir-build-only-check-mlir".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/129/builds/34089

Here is the relevant piece of the build log for the reference
Step 6 (test-build-check-mlir-build-only-check-mlir) failure: 1200 seconds without output running [b'ninja', b'check-mlir'], attempting to kill
...
PASS: MLIR-Unit :: IR/./MLIRIRTests/101/130 (3696 of 3707)
PASS: MLIR-Unit :: IR/./MLIRIRTests/100/130 (3697 of 3707)
PASS: MLIR-Unit :: IR/./MLIRIRTests/39/130 (3698 of 3707)
PASS: MLIR-Unit :: IR/./MLIRIRTests/38/130 (3699 of 3707)
PASS: MLIR-Unit :: Pass/./MLIRPassTests/10/13 (3700 of 3707)
PASS: MLIR-Unit :: Interfaces/./MLIRInterfacesTests/12/22 (3701 of 3707)
PASS: MLIR-Unit :: Interfaces/./MLIRInterfacesTests/13/22 (3702 of 3707)
PASS: MLIR-Unit :: Interfaces/./MLIRInterfacesTests/11/22 (3703 of 3707)
PASS: MLIR-Unit :: IR/./MLIRIRTests/0/130 (3704 of 3707)
PASS: MLIR :: mlir-reduce/dce-test.mlir (3705 of 3707)
command timed out: 1200 seconds without output running [b'ninja', b'check-mlir'], attempting to kill
process killed by signal 9
program finished with exit code -1
elapsedTime=3578.193866

augusto2112 pushed a commit to augusto2112/llvm-project that referenced this pull request Dec 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WASM: Masking 64-bit Shifts After Zext

5 participants