From d6ec3a9e18072b2ae9e444d5281bb6234d208e60 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Jun 2026 18:44:11 -0500 Subject: [PATCH] [WebAssembly] Don't stackify multi-def instructions (#200429) 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 Backport to LLVM 21.1.2. Adapt the multivalue.ll REGS check to the older WebAssembly register-printer syntax. (cherry picked from commit b47267441e513f5d65169933cba48c26eb40b803) --- .../WebAssembly/WebAssemblyRegStackify.cpp | 38 +-- .../WebAssembly/multivalue-do-not-stackify.ll | 33 ++ .../WebAssembly/multivalue-stackify.ll | 322 +++++++++++------- llvm/test/CodeGen/WebAssembly/multivalue.ll | 38 ++- .../CodeGen/WebAssembly/multivalue_libcall.ll | 12 + .../CodeGen/WebAssembly/wide-arithmetic.ll | 47 ++- 6 files changed, 327 insertions(+), 163 deletions(-) create mode 100644 llvm/test/CodeGen/WebAssembly/multivalue-do-not-stackify.ll diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp index bc91c6424b63e..7f2eb7d9b7fd8 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp @@ -339,38 +339,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 ; 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: local.get 0 +; CHECK-NEXT: local.get 0 ; CHECK-NEXT: call op_2_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() @@ -1593,14 +1646,15 @@ define void @f337() { ; 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: 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 @@ -1619,11 +1673,12 @@ define void @f338() { ; 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_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, i32} @op_0_to_3() @@ -1638,14 +1693,16 @@ define void @f338() { define void @f339() { ; CHECK-LABEL: f339: ; CHECK: .functype f339 () -> () -; 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: 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, i32} @op_0_to_3() @@ -1660,12 +1717,16 @@ define void @f339() { define void @f340() { ; CHECK-LABEL: f340: ; CHECK: .functype f340 () -> () -; CHECK-NEXT: .local 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_2_to_0 ; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: call op_2_to_0 +; CHECK-NEXT: local.get 2 ; CHECK-NEXT: call op_1_to_0 ; CHECK-NEXT: # fallthrough-return %t0 = call {i32, i32, i32} @op_0_to_3() @@ -1683,13 +1744,14 @@ define void @f343() { ; 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 0 +; CHECK-NEXT: local.get 1 ; CHECK-NEXT: call op_2_to_0 +; CHECK-NEXT: local.get 0 ; CHECK-NEXT: local.get 2 -; 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() @@ -1709,12 +1771,13 @@ define void @f344() { ; 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: 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, i32} @op_0_to_3() @@ -1730,15 +1793,17 @@ define void @f344() { define void @f346() { ; CHECK-LABEL: f346: ; CHECK: .functype f346 () -> () -; 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: local.get 1 ; CHECK-NEXT: call op_2_to_0 -; CHECK-NEXT: local.get 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() @@ -1757,13 +1822,14 @@ define void @f347() { ; 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 0 -; CHECK-NEXT: call op_2_to_0 ; CHECK-NEXT: local.get 1 +; CHECK-NEXT: call op_2_to_0 ; CHECK-NEXT: local.get 2 +; 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() @@ -1779,15 +1845,17 @@ define void @f347() { define void @f348() { ; CHECK-LABEL: f348: ; CHECK: .functype f348 () -> () -; 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: local.get 1 ; CHECK-NEXT: call op_2_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() @@ -1803,13 +1871,17 @@ define void @f348() { define void @f349() { ; CHECK-LABEL: f349: ; CHECK: .functype f349 () -> () -; CHECK-NEXT: .local 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_2_to_0 -; CHECK-NEXT: local.get 0 ; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: call op_2_to_0 +; CHECK-NEXT: local.get 2 +; 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() @@ -1828,12 +1900,13 @@ define void @f350() { ; 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_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, i32} @op_0_to_3() @@ -1848,14 +1921,16 @@ define void @f350() { define void @f351() { ; CHECK-LABEL: f351: ; CHECK: .functype f351 () -> () -; 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 1 -; CHECK-NEXT: call op_2_to_0 ; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 2 +; 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() @@ -1870,14 +1945,16 @@ define void @f351() { define void @f352() { ; CHECK-LABEL: f352: ; CHECK: .functype f352 () -> () -; 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: local.get 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() @@ -1895,13 +1972,14 @@ define void @f354() { ; 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 1 -; CHECK-NEXT: call op_2_to_0 +; CHECK-NEXT: local.get 0 ; CHECK-NEXT: local.get 2 +; CHECK-NEXT: call op_2_to_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() @@ -1920,14 +1998,15 @@ define void @f356() { ; 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 1 -; CHECK-NEXT: call op_2_to_0 ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: local.get 2 ; CHECK-NEXT: call op_2_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() %t1 = extractvalue {i32, i32, i32} %t0, 0 @@ -1942,15 +2021,17 @@ define void @f356() { define void @f357() { ; CHECK-LABEL: f357: ; CHECK: .functype f357 () -> () -; 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 1 -; CHECK-NEXT: call op_2_to_0 -; CHECK-NEXT: local.get 0 ; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 2 +; 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() @@ -1966,15 +2047,17 @@ define void @f357() { define void @f358() { ; CHECK-LABEL: f358: ; CHECK: .functype f358 () -> () -; 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 1 -; CHECK-NEXT: call op_2_to_0 ; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 2 +; 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() @@ -1993,13 +2076,14 @@ define void @f359() { ; 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: 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, i32} @op_0_to_3() @@ -2015,15 +2099,17 @@ define void @f359() { define void @f360() { ; CHECK-LABEL: f360: ; CHECK: .functype f360 () -> () -; 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 1 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 2 ; CHECK-NEXT: call op_2_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() diff --git a/llvm/test/CodeGen/WebAssembly/multivalue.ll b/llvm/test/CodeGen/WebAssembly/multivalue.ll index 5001db7e57a1e..46c75f5506ce7 100644 --- a/llvm/test/CodeGen/WebAssembly/multivalue.ll +++ b/llvm/test/CodeGen/WebAssembly/multivalue.ll @@ -48,9 +48,14 @@ define void @pair_call() { ; CHECK-LABEL: pair_call_return: ; CHECK-NEXT: .functype pair_call_return () -> (i32, i64) +; CHECK-NEXT: .local i32, i64 ; CHECK-NEXT: call pair_const{{$}} +; CHECK-NEXT: local.set 1 +; CHECK-NEXT: local.set 0 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 ; CHECK-NEXT: end_function{{$}} -; REGS: call $push{{[0-9]+}}=, $push{{[0-9]+}}=, pair_const{{$}} +; REGS: call $0=, $1=, pair_const{{$}} define %pair @pair_call_return() { %p = call %pair @pair_const() ret %pair %p @@ -58,11 +63,16 @@ define %pair @pair_call_return() { ; CHECK-LABEL: pair_call_indirect: ; CHECK-NEXT: .functype pair_call_indirect (i32) -> (i32, i64) +; CHECK-NEXT: .local i64 ; CHECK-NEXT: local.get 0{{$}} ; CHECK-NEXT: call_indirect () -> (i32, i64){{$}} +; CHECK-NEXT: local.set 1 +; CHECK-NEXT: local.set 0 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 ; REF: call_indirect __indirect_function_table, () -> (i32, i64){{$}} ; CHECK-NEXT: end_function{{$}} -; REGS: call_indirect $push{{[0-9]+}}=, $push{{[0-9]+}}=, $0{{$}} +; REGS: call_indirect ${{[0-9]+}}=, ${{[0-9]+}}=, $0{{$}} define %pair @pair_call_indirect(ptr %f) { %p = call %pair %f() ret %pair %p @@ -80,10 +90,13 @@ define %pair @pair_tail_call() { ; CHECK-LABEL: pair_call_return_first: ; CHECK-NEXT: .functype pair_call_return_first () -> (i32) +; CHECK-NEXT: .local i32 ; CHECK-NEXT: call pair_const{{$}} ; CHECK-NEXT: drop{{$}} +; CHECK-NEXT: local.set 0 +; CHECK-NEXT: local.get 0 ; CHECK-NEXT: end_function{{$}} -; REGS: call $push{{[0-9]+}}=, $drop=, pair_const{{$}} +; REGS: call $0=, $drop=, pair_const{{$}} define i32 @pair_call_return_first() { %p = call %pair @pair_const() %v = extractvalue %pair %p, 0 @@ -107,11 +120,14 @@ define i64 @pair_call_return_second() { ; CHECK-LABEL: pair_call_use_first: ; CHECK-NEXT: .functype pair_call_use_first () -> () +; CHECK-NEXT: .local i32 ; CHECK-NEXT: call pair_const{{$}} ; CHECK-NEXT: drop{{$}} +; CHECK-NEXT: local.set 0 +; CHECK-NEXT: local.get 0 ; CHECK-NEXT: call use_i32{{$}} ; CHECK-NEXT: end_function{{$}} -; REGS: call $push{{[0-9]+}}=, $drop=, pair_const{{$}} +; REGS: call $0=, $drop=, pair_const{{$}} define void @pair_call_use_first() { %p = call %pair @pair_const() %v = extractvalue %pair %p, 0 @@ -138,13 +154,15 @@ define void @pair_call_use_second() { ; CHECK-LABEL: pair_call_use_first_return_second: ; CHECK-NEXT: .functype pair_call_use_first_return_second () -> (i64) -; CHECK-NEXT: .local i64{{$}} +; CHECK-NEXT: .local i32, i64{{$}} ; CHECK-NEXT: call pair_const{{$}} +; CHECK-NEXT: local.set 1{{$}} ; CHECK-NEXT: local.set 0{{$}} -; CHECK-NEXT: call use_i32{{$}} ; CHECK-NEXT: local.get 0{{$}} +; CHECK-NEXT: call use_i32{{$}} +; CHECK-NEXT: local.get 1{{$}} ; CHECK-NEXT: end_function{{$}} -; REGS: call $push{{[0-9]+}}=, $0=, pair_const{{$}} +; REGS: call $0=, $1=, pair_const{{$}} define i64 @pair_call_use_first_return_second() { %p = call %pair @pair_const() %v = extractvalue %pair %p, 0 @@ -177,8 +195,12 @@ define i32 @pair_call_use_second_return_first() { ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: local.get 1 ; CHECK-NEXT: call pair_ident{{$}} +; CHECK-NEXT: local.set 1 +; CHECK-NEXT: local.set 0 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 ; CHECK-NEXT: end_function{{$}} -; REGS: call $push{{[0-9]+}}=, $push{{[0-9]+}}=, pair_ident, $0, $1{{$}} +; REGS: call $0=, $1=, pair_ident, $0, $1{{$}} define %pair @pair_pass_through(%pair %p) { %r = call %pair @pair_ident(%pair %p) ret %pair %r diff --git a/llvm/test/CodeGen/WebAssembly/multivalue_libcall.ll b/llvm/test/CodeGen/WebAssembly/multivalue_libcall.ll index c1343d32f80e5..5f2ba7ee52a12 100644 --- a/llvm/test/CodeGen/WebAssembly/multivalue_libcall.ll +++ b/llvm/test/CodeGen/WebAssembly/multivalue_libcall.ll @@ -15,6 +15,10 @@ define i128 @multivalue_sdiv(i128 %a, i128 %b) { ; MULTIVALUE-NEXT: local.get 2 ; MULTIVALUE-NEXT: local.get 3 ; MULTIVALUE-NEXT: call __divti3 +; MULTIVALUE-NEXT: local.set 2 +; MULTIVALUE-NEXT: local.set 3 +; MULTIVALUE-NEXT: local.get 3 +; MULTIVALUE-NEXT: local.get 2 ; MULTIVALUE-NEXT: # fallthrough-return ; ; NO_MULTIVALUE-LABEL: multivalue_sdiv: @@ -59,6 +63,10 @@ define fp128 @multivalue_fsub(fp128 %a, fp128 %b) { ; MULTIVALUE-NEXT: local.get 2 ; MULTIVALUE-NEXT: local.get 3 ; MULTIVALUE-NEXT: call __subtf3 +; MULTIVALUE-NEXT: local.set 2 +; MULTIVALUE-NEXT: local.set 3 +; MULTIVALUE-NEXT: local.get 3 +; MULTIVALUE-NEXT: local.get 2 ; MULTIVALUE-NEXT: # fallthrough-return ; ; NO_MULTIVALUE-LABEL: multivalue_fsub: @@ -102,6 +110,10 @@ define i128 @multivalue_lshr(i128 %a, i128 %b) { ; MULTIVALUE-NEXT: local.get 0 ; MULTIVALUE-NEXT: i32.wrap_i64 ; MULTIVALUE-NEXT: call __ashlti3 +; MULTIVALUE-NEXT: local.set 3 +; MULTIVALUE-NEXT: local.set 0 +; MULTIVALUE-NEXT: local.get 0 +; MULTIVALUE-NEXT: local.get 3 ; MULTIVALUE-NEXT: # fallthrough-return ; ; NO_MULTIVALUE-LABEL: multivalue_lshr: diff --git a/llvm/test/CodeGen/WebAssembly/wide-arithmetic.ll b/llvm/test/CodeGen/WebAssembly/wide-arithmetic.ll index 71974b012a2b6..724ae46ebca10 100644 --- a/llvm/test/CodeGen/WebAssembly/wide-arithmetic.ll +++ b/llvm/test/CodeGen/WebAssembly/wide-arithmetic.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 -; RUN: llc -mattr=+wide-arithmetic < %s | FileCheck %s +; RUN: llc -mattr=+wide-arithmetic < %s -O2 | FileCheck %s target triple = "wasm32-unknown-unknown" @@ -50,16 +50,18 @@ define i128 @sub_i128(i128 %a, i128 %b) { define i128 @mul_i128(i128 %a, i128 %b) { ; CHECK-LABEL: mul_i128: ; CHECK: .functype mul_i128 (i32, i64, i64, i64, i64) -> () -; CHECK-NEXT: .local i64 +; CHECK-NEXT: .local i64, i64 ; CHECK-NEXT: # %bb.0: -; CHECK-NEXT: local.get 0 ; CHECK-NEXT: local.get 1 ; CHECK-NEXT: local.get 3 ; CHECK-NEXT: i64.mul_wide_u +; CHECK-NEXT: local.set 6 ; CHECK-NEXT: local.set 5 -; CHECK-NEXT: i64.store 0 ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: local.get 5 +; CHECK-NEXT: i64.store 0 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 6 ; CHECK-NEXT: local.get 1 ; CHECK-NEXT: local.get 4 ; CHECK-NEXT: i64.mul @@ -192,22 +194,26 @@ define { i64, i64 } @add_wide3_u_via_intrinsics(i64 %a, i64 %b, i64 %c) { ; CHECK-LABEL: add_wide3_u_via_intrinsics: ; CHECK: .functype add_wide3_u_via_intrinsics (i32, i64, i64, i64) -> () ; CHECK-NEXT: # %bb.0: -; CHECK-NEXT: local.get 0 ; CHECK-NEXT: local.get 1 ; CHECK-NEXT: i64.const 0 ; CHECK-NEXT: local.get 2 ; CHECK-NEXT: i64.const 0 ; CHECK-NEXT: i64.add128 +; CHECK-NEXT: local.set 1 ; CHECK-NEXT: local.set 2 +; CHECK-NEXT: local.get 2 ; CHECK-NEXT: i64.const 0 ; CHECK-NEXT: local.get 3 ; CHECK-NEXT: i64.const 0 ; CHECK-NEXT: i64.add128 -; CHECK-NEXT: local.set 1 -; CHECK-NEXT: i64.store 0 +; CHECK-NEXT: local.set 3 +; CHECK-NEXT: local.set 2 ; CHECK-NEXT: local.get 0 ; CHECK-NEXT: local.get 2 +; CHECK-NEXT: i64.store 0 +; CHECK-NEXT: local.get 0 ; CHECK-NEXT: local.get 1 +; CHECK-NEXT: local.get 3 ; CHECK-NEXT: i64.add ; CHECK-NEXT: i64.store 8 ; CHECK-NEXT: # fallthrough-return @@ -239,6 +245,10 @@ define { i64, i64 } @add_wide3_u_via_i128(i64 %a, i64 %b, i64 %c) { ; CHECK-NEXT: local.get 2 ; CHECK-NEXT: i64.const 0 ; CHECK-NEXT: i64.add128 +; CHECK-NEXT: local.set 1 +; CHECK-NEXT: local.set 2 +; CHECK-NEXT: local.get 2 +; CHECK-NEXT: local.get 1 ; CHECK-NEXT: local.get 3 ; CHECK-NEXT: i64.const 0 ; CHECK-NEXT: i64.add128 @@ -264,3 +274,26 @@ define { i64, i64 } @add_wide3_u_via_i128(i64 %a, i64 %b, i64 %c) { %ret1 = insertvalue { i64, i64 } %ret0, i64 %carry, 1 ret { i64, i64 } %ret1 } + +define i1 @smul64_with_overflow(i64 %a, i64 %b) { +; CHECK-LABEL: smul64_with_overflow: +; CHECK: .functype smul64_with_overflow (i64, i64) -> (i32) +; CHECK-NEXT: # %bb.0: # %entry +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: i64.mul_wide_s +; CHECK-NEXT: local.set 0 +; CHECK-NEXT: local.set 1 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: local.get 1 +; CHECK-NEXT: i64.const 63 +; CHECK-NEXT: i64.shr_s +; CHECK-NEXT: i64.ne +; CHECK-NEXT: # fallthrough-return +entry: + %res = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 %a, i64 %b) + %ov = extractvalue { i64, i1 } %res, 1 + ret i1 %ov +} + +declare { i64, i1 } @llvm.smul.with.overflow.i64(i64, i64)