Skip to content

[WebAssembly] Don't stackify multi-def instructions - #200429

Merged
QuantumSegfault merged 1 commit into
llvm:mainfrom
alexcrichton:wasm-disable-stackify-multi-ret
Jun 8, 2026
Merged

[WebAssembly] Don't stackify multi-def instructions#200429
QuantumSegfault merged 1 commit into
llvm:mainfrom
alexcrichton:wasm-disable-stackify-multi-ret

Conversation

@alexcrichton

Copy link
Copy Markdown
Contributor

This commit updates the WebAssemblyRegStackify.cpp pass to specifically exclude attempting to stackify the first def of a multi-def instruction. As the previous comments indicate this is possible to do in some situations, but the current logic is incomplete and has led to miscompilations such as #98323 and #199910. One option would be to make the logic more robust, but in lieu of that in the meantime the change here is to completely disable stackification in these situations. This provides at least a "known working" base to build on later and fixes the known regressions around this.

Closes #98323
Closes #199910

This commit updates the `WebAssemblyRegStackify.cpp` pass to
specifically exclude attempting to stackify the first def of a multi-def
instruction. As the previous comments indicate this is possible to do in
some situations, but the current logic is incomplete and has led to
miscompilations such as llvm#98323 and llvm#199910. One option would be to make
the logic more robust, but in lieu of that in the meantime the change
here is to completely disable stackification in these situations. This
provides at least a "known working" base to build on later and fixes the
known regressions around this.

Closes llvm#98323
Closes llvm#199910
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-backend-webassembly

Author: Alex Crichton (alexcrichton)

Changes

This commit updates the WebAssemblyRegStackify.cpp pass to specifically exclude attempting to stackify the first def of a multi-def instruction. As the previous comments indicate this is possible to do in some situations, but the current logic is incomplete and has led to miscompilations such as #98323 and #199910. One option would be to make the logic more robust, but in lieu of that in the meantime the change here is to completely disable stackification in these situations. This provides at least a "known working" base to build on later and fixes the known regressions around this.

Closes #98323
Closes #199910


Patch is 40.70 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/200429.diff

6 Files Affected:

  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp (+8-30)
  • (added) llvm/test/CodeGen/WebAssembly/multivalue-do-not-stackify.ll (+33)
  • (modified) llvm/test/CodeGen/WebAssembly/multivalue-stackify.ll (+204-118)
  • (modified) llvm/test/CodeGen/WebAssembly/multivalue.ll (+30-8)
  • (modified) llvm/test/CodeGen/WebAssembly/multivalue_libcall.ll (+12)
  • (modified) llvm/test/CodeGen/WebAssembly/wide-arithmetic.ll (+40-7)
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
index 9015ceab87fb7..69f66858c20d1 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
@@ -355,38 +355,16 @@ static bool isSafeToMove(const MachineOperand *Def, const MachineOperand *Use,
   assert(DefI->getParent() == Insert->getParent());
   assert(UseI->getParent() == Insert->getParent());
 
-  // The first def of a multivalue instruction can be stackified by moving,
-  // since the later defs can always be placed into locals if necessary. Later
-  // defs can only be stackified if all previous defs are already stackified
-  // since ExplicitLocals will not know how to place a def in a local if a
-  // subsequent def is stackified. But only one def can be stackified by moving
-  // the instruction, so it must be the first one.
-  //
-  // TODO: This could be loosened to be the first *live* def, but care would
-  // have to be taken to ensure the drops of the initial dead defs can be
-  // placed. This would require checking that no previous defs are used in the
-  // same instruction as subsequent defs.
-  if (Def != DefI->defs().begin())
+  // For now avoid stackifying any multi-def instructions. While it's
+  // theoretically possible to do so for the first def in some cases this has
+  // historically led to bugs such as #199910 and #98323. For now this
+  // conservatively skips all multi-def instructions as a consequence. Note that
+  // multi-def instructions are expected to be not all that common so this in
+  // theory doesn't have a massive impact, but nevertheless this'd still be
+  // something to optimize better in the future.
+  if (DefI->getNumExplicitDefs() > 1)
     return false;
 
-  // If any subsequent def is used prior to the current value by the same
-  // instruction in which the current value is used, we cannot
-  // stackify. Stackifying in this case would require that def moving below the
-  // current def in the stack, which cannot be achieved, even with locals.
-  // Also ensure we don't sink the def past any other prior uses.
-  for (const auto &SubsequentDef : drop_begin(DefI->defs())) {
-    auto I = std::next(MachineBasicBlock::const_iterator(DefI));
-    auto E = std::next(MachineBasicBlock::const_iterator(UseI));
-    for (; I != E; ++I) {
-      for (const auto &PriorUse : I->uses()) {
-        if (&PriorUse == Use)
-          break;
-        if (PriorUse.isReg() && SubsequentDef.getReg() == PriorUse.getReg())
-          return false;
-      }
-    }
-  }
-
   // If moving is a semantic nop, it is always allowed
   const MachineBasicBlock *MBB = DefI->getParent();
   auto NextI = std::next(MachineBasicBlock::const_iterator(DefI));
diff --git a/llvm/test/CodeGen/WebAssembly/multivalue-do-not-stackify.ll b/llvm/test/CodeGen/WebAssembly/multivalue-do-not-stackify.ll
new file mode 100644
index 0000000000000..53b36fb3bde0c
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/multivalue-do-not-stackify.ll
@@ -0,0 +1,33 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+
+; RUN: llc < %s -verify-machineinstrs -mattr=+multivalue -target-abi=experimental-mv -O2 | FileCheck %s
+
+target triple = "wasm32-unknown-unknown"
+
+; Regression test for #98323 where attempting to stackify the call to `@foo`
+; historically led to a miscompile.
+
+define i64 @test() {
+; CHECK-LABEL: test:
+; CHECK:         .functype test () -> (i64)
+; CHECK-NEXT:    .local i64, i64
+; CHECK-NEXT:  # %bb.0: # %entry
+; CHECK-NEXT:    call foo
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    i64.const 42
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    i64.eqz
+; CHECK-NEXT:    i64.select
+; CHECK-NEXT:    # fallthrough-return
+entry:
+  %pair = call { i64, i64 } @foo()
+  %v0 = extractvalue { i64, i64 } %pair, 0
+  %1 = icmp eq i64 %v0, 0
+  %v1 = extractvalue { i64, i64 } %pair, 1
+  %_0.sroa.0.0 = select i1 %1, i64 42, i64 %v1
+  ret i64 %_0.sroa.0.0
+}
+
+declare { i64, i64 } @foo()
diff --git a/llvm/test/CodeGen/WebAssembly/multivalue-stackify.ll b/llvm/test/CodeGen/WebAssembly/multivalue-stackify.ll
index 0b5a304589aa6..82a8ea739493b 100644
--- a/llvm/test/CodeGen/WebAssembly/multivalue-stackify.ll
+++ b/llvm/test/CodeGen/WebAssembly/multivalue-stackify.ll
@@ -47,9 +47,12 @@ define void @f3() {
 define void @f12() {
 ; CHECK-LABEL: f12:
 ; CHECK:         .functype f12 () -> ()
+; CHECK-NEXT:    .local i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_2
 ; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32} @op_0_to_2()
@@ -82,7 +85,8 @@ define void @f14() {
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_2
 ; CHECK-NEXT:    drop
-; CHECK-NEXT:    local.tee 0
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
@@ -96,8 +100,13 @@ define void @f14() {
 define void @f15() {
 ; CHECK-LABEL: f15:
 ; CHECK:         .functype f15 () -> ()
+; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32} @op_0_to_2()
@@ -148,10 +157,13 @@ define void @f17() {
 define void @f25() {
 ; CHECK-LABEL: f25:
 ; CHECK:         .functype f25 () -> ()
+; CHECK-NEXT:    .local i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
 ; CHECK-NEXT:    drop
 ; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -204,7 +216,8 @@ define void @f28() {
 ; CHECK-NEXT:    call op_0_to_3
 ; CHECK-NEXT:    drop
 ; CHECK-NEXT:    drop
-; CHECK-NEXT:    local.tee 0
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
@@ -218,9 +231,14 @@ define void @f28() {
 define void @f29() {
 ; CHECK-LABEL: f29:
 ; CHECK:         .functype f29 () -> ()
+; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
 ; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -233,12 +251,14 @@ define void @f29() {
 define void @f30() {
 ; CHECK-LABEL: f30:
 ; CHECK:         .functype f30 () -> ()
-; CHECK-NEXT:    .local i32
+; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
-; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 0
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -371,13 +391,15 @@ define void @f36() {
 define void @f129() {
 ; CHECK-LABEL: f129:
 ; CHECK:         .functype f129 () -> ()
-; CHECK-NEXT:    .local i32
+; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32} @op_0_to_2()
   %t1 = extractvalue {i32, i32} %t0, 0
@@ -393,11 +415,12 @@ define void @f131() {
 ; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_1_to_0
-; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32} @op_0_to_2()
@@ -415,11 +438,12 @@ define void @f132() {
 ; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    local.tee 1
-; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32} @op_0_to_2()
@@ -434,13 +458,15 @@ define void @f132() {
 define void @f133() {
 ; CHECK-LABEL: f133:
 ; CHECK:         .functype f133 () -> ()
-; CHECK-NEXT:    .local i32
+; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    call op_1_to_0
-; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32} @op_0_to_2()
@@ -548,11 +574,12 @@ define void @f155() {
 ; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    local.tee 1
-; CHECK-NEXT:    local.get 1
-; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32} @op_0_to_2()
@@ -570,13 +597,14 @@ define void @f159() {
 ; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    local.tee 1
-; CHECK-NEXT:    local.get 1
-; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32} @op_0_to_2()
   %t1 = extractvalue {i32, i32} %t0, 0
@@ -594,11 +622,12 @@ define void @f167() {
 ; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    local.tee 1
 ; CHECK-NEXT:    local.get 0
-; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32} @op_0_to_2()
@@ -613,13 +642,15 @@ define void @f167() {
 define void @f168() {
 ; CHECK-LABEL: f168:
 ; CHECK:         .functype f168 () -> ()
-; CHECK-NEXT:    .local i32
+; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_2_to_0
-; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32} @op_0_to_2()
@@ -637,12 +668,13 @@ define void @f171() {
 ; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_2
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    local.tee 1
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_2_to_0
-; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32} @op_0_to_2()
@@ -777,14 +809,16 @@ define void @f195() {
 define void @f291() {
 ; CHECK-LABEL: f291:
 ; CHECK:         .functype f291 () -> ()
-; CHECK-NEXT:    .local i32
+; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
 ; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
   %t1 = extractvalue {i32, i32, i32} %t0, 0
@@ -797,14 +831,16 @@ define void @f291() {
 define void @f292() {
 ; CHECK-LABEL: f292:
 ; CHECK:         .functype f292 () -> ()
-; CHECK-NEXT:    .local i32
+; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
-; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    drop
-; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.set 0
 ; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
   %t1 = extractvalue {i32, i32, i32} %t0, 0
@@ -821,11 +857,12 @@ define void @f294() {
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
 ; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_1_to_0
-; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -843,12 +880,13 @@ define void @f295() {
 ; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
-; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    drop
-; CHECK-NEXT:    local.tee 1
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_1_to_0
-; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -867,11 +905,12 @@ define void @f296() {
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
 ; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    local.tee 1
-; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -886,14 +925,16 @@ define void @f296() {
 define void @f297() {
 ; CHECK-LABEL: f297:
 ; CHECK:         .functype f297 () -> ()
-; CHECK-NEXT:    .local i32
+; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
 ; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    call op_1_to_0
-; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -908,14 +949,16 @@ define void @f297() {
 define void @f298() {
 ; CHECK-LABEL: f298:
 ; CHECK:         .functype f298 () -> ()
-; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:    .local i32, i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
 ; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 2
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -933,12 +976,13 @@ define void @f299() {
 ; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
-; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    drop
-; CHECK-NEXT:    local.tee 1
-; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.set 0
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -953,14 +997,16 @@ define void @f299() {
 define void @f300() {
 ; CHECK-LABEL: f300:
 ; CHECK:         .functype f300 () -> ()
-; CHECK-NEXT:    .local i32, i32
+; CHECK-NEXT:    .local i32, i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
 ; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 2
 ; CHECK-NEXT:    local.get 1
-; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -975,14 +1021,16 @@ define void @f300() {
 define void @f301() {
 ; CHECK-LABEL: f301:
 ; CHECK:         .functype f301 () -> ()
-; CHECK-NEXT:    .local i32
+; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
-; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    drop
-; CHECK-NEXT:    call op_1_to_0
-; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.set 0
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_1_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -1473,11 +1521,12 @@ define void @f327() {
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
 ; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    local.tee 1
-; CHECK-NEXT:    local.get 1
-; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -1495,12 +1544,13 @@ define void @f328() {
 ; CHECK-NEXT:    .local i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
-; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    drop
-; CHECK-NEXT:    local.tee 1
-; CHECK-NEXT:    local.get 1
-; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.set 0
+; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
 ; CHECK-NEXT:    call op_1_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -1519,13 +1569,14 @@ define void @f333() {
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
 ; CHECK-NEXT:    drop
+; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    local.tee 1
-; CHECK-NEXT:    local.get 1
-; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    call op_2_to_0
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
   %t1 = extractvalue {i32, i32, i32} %t0, 0
@@ -1543,13 +1594,14 @@ define void @f334() {
 ; CHECK-NEXT:    .local i32, i32, i32
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    call op_0_to_3
+; CHECK-NEXT:    local.set 2
 ; CHECK-NEXT:    local.set 1
 ; CHECK-NEXT:    local.set 0
-; CHECK-NEXT:    local.tee 2
-; CHECK-NEXT:    local.get 2
-; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    local.get 0
+; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    local.get 1
+; CHECK-NEXT:    local.get 2
 ; CHECK-NEXT:    call op_2_to_0
 ; CHECK-NEXT:    # fallthrough-return
   %t0 = call {i32, i32, i32} @op_0_to_3()
@@ -1568,13 +1620,14 @@ define void @f336() {
 ; CHECK-NEXT:    .local i32, i32, i32...
[truncated]

@alexcrichton

Copy link
Copy Markdown
Contributor Author

@dschuff would you be able to merge this for me?

@QuantumSegfault

Copy link
Copy Markdown
Contributor

I got it.

@QuantumSegfault
QuantumSegfault merged commit b472674 into llvm:main Jun 8, 2026
12 checks passed
@alexcrichton

Copy link
Copy Markdown
Contributor Author

Thanks!

@alexcrichton
alexcrichton deleted the wasm-disable-stackify-multi-ret branch June 8, 2026 23:44
@llvm-ci

llvm-ci commented Jun 8, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder release-noassertions-warnings running on google-integrate-b2 while building llvm at step 6 "build-default".

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

Here is the relevant piece of the build log for the reference
Step 6 (build-default) failure: cmake (failure)
...
39.354 [4526/34/4027] Building WasmSSAOps.h.inc...
39.355 [4525/34/4028] Building SymbolInterfaces.cpp.inc...
39.355 [4524/34/4029] Building SymbolInterfaces.h.inc...
39.360 [4523/34/4030] Building RegionKindInterface.cpp.inc...
39.366 [4522/34/4031] Building RegionKindInterface.h.inc...
39.367 [4521/34/4032] Building XeGPUTransformOps.cpp.inc...
39.367 [4520/34/4033] Building QuantStorageTypeInterface.cpp.inc...
39.369 [4519/34/4034] Building SymbolInterfacesAttrInterface.cpp.inc...
39.375 [4518/34/4035] Building XeGPUTransformOps.h.inc...
39.378 [4517/34/4036] Building CXX object lib/Target/WebAssembly/CMakeFiles/LLVMWebAssemblyCodeGen.dir/WebAssemblyRegStackify.cpp.o
FAILED: lib/Target/WebAssembly/CMakeFiles/LLVMWebAssemblyCodeGen.dir/WebAssemblyRegStackify.cpp.o 
CCACHE_CPP2=yes CCACHE_HASHDIR=yes CCACHE_SLOPPINESS=pch_defines,time_macros /usr/bin/ccache /usr/bin/c++ -D_GLIBCXX_USE_CXX11_ABI=1 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/b/release-noassertions-warnings-build/build/lib/Target/WebAssembly -I/b/release-noassertions-warnings-build/llvm-project/llvm/lib/Target/WebAssembly -I/b/release-noassertions-warnings-build/build/include -I/b/release-noassertions-warnings-build/llvm-project/llvm/include -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wno-pass-failed -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Xclang -fno-pch-timestamp -O3 -DNDEBUG -std=c++17 -fvisibility=hidden -fno-exceptions -funwind-tables -fno-rtti -Winvalid-pch -Xclang -include-pch -Xclang /b/release-noassertions-warnings-build/build/lib/CodeGen/CMakeFiles/LLVMCodeGen.dir/cmake_pch.hxx.pch -Xclang -include -Xclang /b/release-noassertions-warnings-build/build/lib/CodeGen/CMakeFiles/LLVMCodeGen.dir/cmake_pch.hxx -MD -MT lib/Target/WebAssembly/CMakeFiles/LLVMWebAssemblyCodeGen.dir/WebAssemblyRegStackify.cpp.o -MF lib/Target/WebAssembly/CMakeFiles/LLVMWebAssemblyCodeGen.dir/WebAssemblyRegStackify.cpp.o.d -o lib/Target/WebAssembly/CMakeFiles/LLVMWebAssemblyCodeGen.dir/WebAssemblyRegStackify.cpp.o -c /b/release-noassertions-warnings-build/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp
/b/release-noassertions-warnings-build/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp:354:23: error: unused variable 'UseI' [-Werror,-Wunused-variable]
  354 |   const MachineInstr *UseI = Use->getParent();
      |                       ^~~~
1 error generated.
39.378 [4517/33/4037] Building SymbolInterfacesAttrInterface.h.inc...
39.380 [4517/32/4038] Building QuantStorageTypeInterface.h.inc...
39.380 [4517/31/4039] Building XeGPUEnums.cpp.inc...
39.381 [4517/30/4040] Building OpAsmAttrInterface.cpp.inc...
39.385 [4517/29/4041] Building X86.h.inc...
39.388 [4517/28/4042] Building X86Types.cpp.inc...
39.388 [4517/27/4043] Building OpAsmAttrInterface.h.inc...
39.390 [4517/26/4044] Building OpAsmOpInterface.cpp.inc...
39.390 [4517/25/4045] Linking CXX static library lib/libLLVMAnalysis.a
39.391 [4517/24/4046] Building XeGPUEnums.h.inc...
39.394 [4517/23/4047] Building AlignmentAttrInterface.cpp.inc...
39.394 [4517/22/4048] Building OpAsmOpInterface.h.inc...
39.396 [4517/21/4049] Building SPIRVAttrUtils.inc...
39.399 [4517/20/4050] Building OpAsmTypeInterface.h.inc...
39.400 [4517/19/4051] Building AlignmentAttrInterface.h.inc...
39.405 [4517/18/4052] Building CallInterfaces.cpp.inc...
39.406 [4517/17/4053] Building OpAsmTypeInterface.cpp.inc...
39.573 [4517/16/4054] Building X86GenFastISel.inc...
40.408 [4517/15/4055] Building X86GenGlobalISel.inc...
41.935 [4517/14/4056] Building X86GenDAGISel.inc...
42.377 [4517/13/4057] Building RISCVGenDAGISel.inc...
42.681 [4517/12/4058] Building RISCVGenInstrInfo.inc...
44.579 [4517/11/4059] Building RISCVGenGlobalISel.inc...
45.318 [4517/10/4060] Building AMDGPUGenCallingConv.inc...
45.498 [4517/9/4061] Building X86GenSubtargetInfo.inc...
46.318 [4517/8/4062] Building AMDGPUGenAsmWriter.inc...
46.489 [4517/7/4063] Building X86GenInstrInfo.inc...
50.823 [4517/6/4064] Building AMDGPUGenDAGISel.inc...
52.553 [4517/5/4065] Building AMDGPUGenGlobalISel.inc...
53.227 [4517/4/4066] Building AMDGPUGenInstrInfo.inc...
57.229 [4517/3/4067] Building AMDGPUGenAsmMatcher.inc...
59.803 [4517/2/4068] Building AMDGPUGenRegisterInfo.inc...
61.729 [4517/1/4069] Building AMDGPUGenRegisterBank.inc...

@QuantumSegfault

Copy link
Copy Markdown
Contributor

@alexcrichton

For future reference, CI treats all warnings as errors. There must be no warnings when compiling.

Can you open a patch PR real quick, or would you prefer this reverted for now?

@alexcrichton

Copy link
Copy Markdown
Contributor Author

Sorry about that! Posted a fix to #202464 and I don't think I saw this in development since it's a release-only unused variable (I was compiling with assertions on)

@QuantumSegfault

Copy link
Copy Markdown
Contributor

Sorry about that! Posted a fix to #202464 and I don't think I saw this in development since it's a release-only unused variable (I was compiling with assertions on)

Ah, yeah. I had the same problem once. It's a shame the normal CI doesn't catch that. I guess the CI is already expensive enough. But a compile dry-run to check for would-be warnings in release could be helpful. But I suppose, pretty rare to come across. Not worth burdening every PR.

alexcrichton added a commit to alexcrichton/llvm-project that referenced this pull request Jun 9, 2026
QuantumSegfault pushed a commit that referenced this pull request Jun 9, 2026
carlobertolli pushed a commit to carlobertolli/llvm-project that referenced this pull request Jun 11, 2026
This commit updates the `WebAssemblyRegStackify.cpp` pass to
specifically exclude attempting to stackify the first def of a multi-def
instruction. As the previous comments indicate this is possible to do in
some situations, but the current logic is incomplete and has led to
miscompilations such as llvm#98323 and llvm#199910. One option would be to make
the logic more robust, but in lieu of that in the meantime the change
here is to completely disable stackification in these situations. This
provides at least a "known working" base to build on later and fixes the
known regressions around this.

Closes llvm#98323
Closes llvm#199910
carlobertolli pushed a commit to carlobertolli/llvm-project that referenced this pull request Jun 11, 2026
Arshia001 added a commit to wasix-org/llvm-project that referenced this pull request Aug 6, 2026
Backport: [WebAssembly] Don't stackify multi-def instructions (llvm#200429)
kilyanni pushed a commit to wasix-org/llvm-project that referenced this pull request Aug 6, 2026
This commit updates the `WebAssemblyRegStackify.cpp` pass to
specifically exclude attempting to stackify the first def of a multi-def
instruction. As the previous comments indicate this is possible to do in
some situations, but the current logic is incomplete and has led to
miscompilations such as llvm#98323 and llvm#199910. One option would be to make
the logic more robust, but in lieu of that in the meantime the change
here is to completely disable stackification in these situations. This
provides at least a "known working" base to build on later and fixes the
known regressions around this.

Closes llvm#98323
Closes llvm#199910

Backport to LLVM 21.1.2. Adapt the multivalue.ll REGS check to the older WebAssembly register-printer syntax.

(cherry picked from commit b472674)
Arshia001 added a commit to wasix-org/llvm-project that referenced this pull request Aug 6, 2026
[WebAssembly] Don't stackify multi-def instructions (llvm#200429)
Arshia001 added a commit to wasix-org/llvm-project that referenced this pull request Aug 6, 2026
[WebAssembly] Don't stackify multi-def instructions (llvm#200429)
Arshia001 added a commit to wasix-org/rust that referenced this pull request Aug 6, 2026
Adds the WebAssemblyRegStackify.cpp change from wasix-org/llvm-project
a3d94d9 (backport of llvm/llvm-project#200429) to wasix-llvm.patch.

The pass could stackify the first def of a multi-def instruction, which
miscompiled overflow checks built on i64.mul_wide_s: the overflow flag
came back set for products that fit, e.g. 3 * 7 reporting overflow while
returning 21. Reachable from Rust via i64::overflowing_mul when built
with -Ctarget-feature=+wide-arithmetic, and from C through
__builtin_mul_overflow.

Only the source hunk is taken; the commit's test-file updates are left
out, matching the rest of this patch.

Refs ECO-421, ECO-426.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

[WebAssembly] signed wide multiply overflow check miscompiled Miscompile with multivalue ABI

4 participants