From 6e00321acbce793fd36eb383372085c31ba6e943 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 10:06:15 -0700 Subject: [PATCH 01/17] start --- src/wasm/wasm-stack-opts.cpp | 60 ++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/wasm/wasm-stack-opts.cpp b/src/wasm/wasm-stack-opts.cpp index eae9b6c681f..d5051582bc9 100644 --- a/src/wasm/wasm-stack-opts.cpp +++ b/src/wasm/wasm-stack-opts.cpp @@ -272,6 +272,66 @@ void StackIROptimizer::local2Stack() { values.push_back(instIndex); } } + + // Optimize the simple case of a multivalue tee and extract. If an expression + // returns a tuple, and that tuple is immediately consumed, we end up with + // something like this: + // + // local.tee $1 + // tuple.extract 4 0 + // local.get $1 + // tuple.extract 4 1 + // local.get $1 + // tuple.extract 4 2 + // local.get $1 + // tuple.extract 4 3 + // + // The tuple is teed, then we extract the components one by one. If no other + // uses of the tee exist, we can just remove all of this. + for (Index instIndex = 0; instIndex < insts.size(); instIndex++) { + auto* inst = insts[instIndex]; + if (!inst) { + continue; + } + auto* tee = inst->origin->dynCast(); + if (!tee || !tee->type.isTuple()) { + continue; + } + + // This is a tee of a tuple. Look for the expected extracts/gets. Each + // tuple index has 2 items. + auto size = tee->type.size(); + if (instIndex + size * 2 >= insts.size()) { + // Not enough items. + continue; + } + bool ok = true; + for (Index i = 0; i < size; i++) { + // Each tuple index has a pair of items. + auto tupleIndexStart = instIndex + i * 2; + // The first tuple index has the tee (already validated). Others have a + // get. + if (i != 0) { + auto* get = insts[tupleIndexStart]->origin->dynCast(); + if (!get || get->index != tee->index) { + ok = false; + break; + } + } + // The second item of the pair is an extract. + auto* extract = insts[tupleIndexStart + 1]->origin->dynCast(); + if (!extract || extract->index != i) { + ok = false; + break; + } + } + if (ok) { + // Optimize. + for (Index i = 0; i < size * 2; i++) { + insts[instIndex + i] = nullptr; + } + } + } } // There may be unnecessary blocks we can remove: blocks without arriving From 87697e91db7c73766cee54fb2ebb6e6048bd02a6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 10:10:44 -0700 Subject: [PATCH 02/17] test --- .../passes/optimize-stack-ir-multivalue.wast | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 test/lit/passes/optimize-stack-ir-multivalue.wast diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast new file mode 100644 index 00000000000..15b119a610c --- /dev/null +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -0,0 +1,29 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: wasm-opt %s -all --optimize-level=3 --generate-stack-ir --optimize-stack-ir --print-stack-ir | filecheck %s + +(module + ;; CHECK: (type $0 (func (result i32 f64 anyref))) + + ;; CHECK: (func $multivalue-return (type $0) (result i32 f64 anyref) + ;; CHECK-NEXT: (local $temp (tuple i32 f64 anyref)) + ;; CHECK-NEXT: call $multivalue-return + ;; CHECK-NEXT: tuple.make 3 + ;; CHECK-NEXT: ) + (func $multivalue-return (result i32 f64 anyref) + (local $temp (tuple i32 f64 anyref)) + (tuple.make 3 + (tuple.extract 3 0 + (local.tee $temp + (call $multivalue-return) + ) + ) + (tuple.extract 3 1 + (local.get $temp) + ) + (tuple.extract 3 2 + (local.get $temp) + ) + ) + ) +) From 89b294a831a1f258162dc866b62ddf3ddf5e6e24 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 10:12:36 -0700 Subject: [PATCH 03/17] test --- test/lit/passes/optimize-stack-ir-multivalue.wast | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index 15b119a610c..225bbb28c91 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -1,17 +1,20 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: wasm-opt %s -all --optimize-level=3 --generate-stack-ir --optimize-stack-ir --print-stack-ir | filecheck %s +;; RUN: wasm-opt %s -all --optimize-level=3 --generate-stack-ir --optimize-stack-ir --roundtrip --print-stack-ir | filecheck %s (module ;; CHECK: (type $0 (func (result i32 f64 anyref))) ;; CHECK: (func $multivalue-return (type $0) (result i32 f64 anyref) - ;; CHECK-NEXT: (local $temp (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $temp i32) + ;; CHECK-NEXT: (local $1 f64) + ;; CHECK-NEXT: (local $2 anyref) ;; CHECK-NEXT: call $multivalue-return - ;; CHECK-NEXT: tuple.make 3 ;; CHECK-NEXT: ) (func $multivalue-return (result i32 f64 anyref) (local $temp (tuple i32 f64 anyref)) + ;; We can remove all these tuple operations after optiming and + ;; roundtripping (though a few locals will be added FIXME). (tuple.make 3 (tuple.extract 3 0 (local.tee $temp From 812bb63ca398762d57a5a6cc79dc498a3cec6719 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 10:44:43 -0700 Subject: [PATCH 04/17] test --- src/wasm/wasm-stack-opts.cpp | 10 ++++- .../passes/optimize-stack-ir-multivalue.wast | 38 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/wasm/wasm-stack-opts.cpp b/src/wasm/wasm-stack-opts.cpp index d5051582bc9..cea053d0015 100644 --- a/src/wasm/wasm-stack-opts.cpp +++ b/src/wasm/wasm-stack-opts.cpp @@ -309,17 +309,23 @@ void StackIROptimizer::local2Stack() { for (Index i = 0; i < size; i++) { // Each tuple index has a pair of items. auto tupleIndexStart = instIndex + i * 2; + auto* first = insts[tupleIndexStart]; + auto* second = insts[tupleIndexStart + 1]; + if (!first || !second) { + ok = false; + break; + } // The first tuple index has the tee (already validated). Others have a // get. if (i != 0) { - auto* get = insts[tupleIndexStart]->origin->dynCast(); + auto* get = first->origin->dynCast(); if (!get || get->index != tee->index) { ok = false; break; } } // The second item of the pair is an extract. - auto* extract = insts[tupleIndexStart + 1]->origin->dynCast(); + auto* extract = second->origin->dynCast(); if (!extract || extract->index != i) { ok = false; break; diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index 225bbb28c91..0dd8932a00c 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -5,6 +5,8 @@ (module ;; CHECK: (type $0 (func (result i32 f64 anyref))) + ;; CHECK: (type $1 (func (result i32 f64))) + ;; CHECK: (func $multivalue-return (type $0) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) @@ -29,4 +31,40 @@ ) ) ) + + ;; CHECK: (func $multivalue-return-too-short (type $1) (result i32 f64) + ;; CHECK-NEXT: (local $temp i32) + ;; CHECK-NEXT: (local $1 f64) + ;; CHECK-NEXT: (local $2 anyref) + ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $scratch_4 f64) + ;; CHECK-NEXT: (local $scratch_5 i32) + ;; CHECK-NEXT: call $multivalue-return + ;; CHECK-NEXT: local.tee $scratch + ;; CHECK-NEXT: tuple.extract 3 0 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 1 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 2 + ;; CHECK-NEXT: local.set $2 + ;; CHECK-NEXT: local.set $1 + ;; CHECK-NEXT: local.tee $temp + ;; CHECK-NEXT: local.get $1 + ;; CHECK-NEXT: tuple.make 2 + ;; CHECK-NEXT: ) + (func $multivalue-return-too-short (result i32 f64) + (local $temp (tuple i32 f64 anyref)) + ;; As above, but we only return 2 of the tuple's 3 items (i.e., we are too + ;; short to fit the pattern), so we do not optimize here. + (tuple.make 2 + (tuple.extract 3 0 + (local.tee $temp + (call $multivalue-return) + ) + ) + (tuple.extract 3 1 + (local.get $temp) + ) + ) + ) ) From c6a42adb18ad4157adfe6d1fc4088e41d94c3548 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 10:46:02 -0700 Subject: [PATCH 05/17] test --- .../passes/optimize-stack-ir-multivalue.wast | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index 0dd8932a00c..b7171b4f847 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -7,6 +7,8 @@ ;; CHECK: (type $1 (func (result i32 f64))) + ;; CHECK: (type $2 (func (result i32 f64 anyref eqref))) + ;; CHECK: (func $multivalue-return (type $0) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) @@ -67,4 +69,32 @@ ) ) ) + + ;; CHECK: (func $multivalue-return-extra (type $2) (result i32 f64 anyref eqref) + ;; CHECK-NEXT: (local $temp i32) + ;; CHECK-NEXT: (local $1 f64) + ;; CHECK-NEXT: (local $2 anyref) + ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; CHECK-NEXT: call $multivalue-return + ;; CHECK-NEXT: ref.null none + ;; CHECK-NEXT: tuple.make 4 + ;; CHECK-NEXT: ) + (func $multivalue-return-extra (result i32 f64 anyref eqref) + (local $temp (tuple i32 f64 anyref)) + ;; As above, but we add an item to the tuple. We can optimize here. + (tuple.make 4 + (tuple.extract 3 0 + (local.tee $temp + (call $multivalue-return) + ) + ) + (tuple.extract 3 1 + (local.get $temp) + ) + (tuple.extract 3 2 + (local.get $temp) + ) + (ref.null eq) + ) + ) ) From 8a81c92f3b7998ef6857f7f9cf3125773648b32d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 10:47:02 -0700 Subject: [PATCH 06/17] test --- .../passes/optimize-stack-ir-multivalue.wast | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index b7171b4f847..28178030ae0 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -9,6 +9,8 @@ ;; CHECK: (type $2 (func (result i32 f64 anyref eqref))) + ;; CHECK: (type $3 (func (result i32 f64 anyref anyref))) + ;; CHECK: (func $multivalue-return (type $0) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) @@ -97,4 +99,46 @@ (ref.null eq) ) ) + + ;; CHECK: (func $multivalue-return-extra-middle (type $3) (result i32 f64 anyref anyref) + ;; CHECK-NEXT: (local $temp i32) + ;; CHECK-NEXT: (local $1 f64) + ;; CHECK-NEXT: (local $2 anyref) + ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $scratch_4 f64) + ;; CHECK-NEXT: (local $scratch_5 i32) + ;; CHECK-NEXT: call $multivalue-return + ;; CHECK-NEXT: local.tee $scratch + ;; CHECK-NEXT: tuple.extract 3 0 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 1 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 2 + ;; CHECK-NEXT: local.set $2 + ;; CHECK-NEXT: local.set $1 + ;; CHECK-NEXT: local.tee $temp + ;; CHECK-NEXT: local.get $1 + ;; CHECK-NEXT: ref.null none + ;; CHECK-NEXT: local.get $2 + ;; CHECK-NEXT: tuple.make 4 + ;; CHECK-NEXT: ) + (func $multivalue-return-extra-middle (result i32 f64 anyref anyref) + (local $temp (tuple i32 f64 anyref)) + ;; As the last case, but the extra item is in the middle. We cannot + ;; optimize. + (tuple.make 4 + (tuple.extract 3 0 + (local.tee $temp + (call $multivalue-return) + ) + ) + (tuple.extract 3 1 + (local.get $temp) + ) + (ref.null eq) + (tuple.extract 3 2 + (local.get $temp) + ) + ) + ) ) From 8ea3f826344e800b133d580ac01079401f5cb5da Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 10:51:00 -0700 Subject: [PATCH 07/17] test --- src/wasm/wasm-stack-opts.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wasm/wasm-stack-opts.cpp b/src/wasm/wasm-stack-opts.cpp index cea053d0015..fad72cfaec5 100644 --- a/src/wasm/wasm-stack-opts.cpp +++ b/src/wasm/wasm-stack-opts.cpp @@ -301,14 +301,14 @@ void StackIROptimizer::local2Stack() { // This is a tee of a tuple. Look for the expected extracts/gets. Each // tuple index has 2 items. auto size = tee->type.size(); - if (instIndex + size * 2 >= insts.size()) { - // Not enough items. - continue; - } bool ok = true; for (Index i = 0; i < size; i++) { // Each tuple index has a pair of items. auto tupleIndexStart = instIndex + i * 2; + if (tupleIndexStart + 1 >= insts.size()) { + ok = false; + break; + } auto* first = insts[tupleIndexStart]; auto* second = insts[tupleIndexStart + 1]; if (!first || !second) { From a75b2e606df544898f27d5bc0b78448efa2eb5a4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 10:52:31 -0700 Subject: [PATCH 08/17] test --- .../passes/optimize-stack-ir-multivalue.wast | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index 28178030ae0..d538c05f40f 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -11,6 +11,8 @@ ;; CHECK: (type $3 (func (result i32 f64 anyref anyref))) + ;; CHECK: (type $4 (func (param f64) (result i32 f64 anyref))) + ;; CHECK: (func $multivalue-return (type $0) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) @@ -141,4 +143,42 @@ ) ) ) + + ;; CHECK: (func $multivalue-return-non-get (type $4) (param $other f64) (result i32 f64 anyref) + ;; CHECK-NEXT: (local $temp i32) + ;; CHECK-NEXT: (local $2 f64) + ;; CHECK-NEXT: (local $3 anyref) + ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $scratch_5 f64) + ;; CHECK-NEXT: (local $scratch_6 i32) + ;; CHECK-NEXT: call $multivalue-return + ;; CHECK-NEXT: local.tee $scratch + ;; CHECK-NEXT: tuple.extract 3 0 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 1 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 2 + ;; CHECK-NEXT: local.set $3 + ;; CHECK-NEXT: local.set $2 + ;; CHECK-NEXT: local.tee $temp + ;; CHECK-NEXT: local.get $other + ;; CHECK-NEXT: local.get $3 + ;; CHECK-NEXT: tuple.make 3 + ;; CHECK-NEXT: ) + (func $multivalue-return-non-get (param $other f64) (result i32 f64 anyref) + (local $temp (tuple i32 f64 anyref)) + ;; As the first case, but one get is replaced with something else, so we do + ;; not optimize. + (tuple.make 3 + (tuple.extract 3 0 + (local.tee $temp + (call $multivalue-return) + ) + ) + (local.get $other) ;; this changed + (tuple.extract 3 2 + (local.get $temp) + ) + ) + ) ) From c9ffcb26f8c7e70f46a01fb3d7c862b40cfab032 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 10:54:19 -0700 Subject: [PATCH 09/17] test --- .../passes/optimize-stack-ir-multivalue.wast | 80 ++++++++++++++++--- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index d538c05f40f..b78bd76413c 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -3,17 +3,17 @@ ;; RUN: wasm-opt %s -all --optimize-level=3 --generate-stack-ir --optimize-stack-ir --roundtrip --print-stack-ir | filecheck %s (module - ;; CHECK: (type $0 (func (result i32 f64 anyref))) + ;; CHECK: (type $0 (func (param f64) (result i32 f64 anyref))) - ;; CHECK: (type $1 (func (result i32 f64))) + ;; CHECK: (type $1 (func (result i32 f64 anyref))) - ;; CHECK: (type $2 (func (result i32 f64 anyref eqref))) + ;; CHECK: (type $2 (func (result i32 f64))) - ;; CHECK: (type $3 (func (result i32 f64 anyref anyref))) + ;; CHECK: (type $3 (func (result i32 f64 anyref eqref))) - ;; CHECK: (type $4 (func (param f64) (result i32 f64 anyref))) + ;; CHECK: (type $4 (func (result i32 f64 anyref anyref))) - ;; CHECK: (func $multivalue-return (type $0) (result i32 f64 anyref) + ;; CHECK: (func $multivalue-return (type $1) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) ;; CHECK-NEXT: (local $2 anyref) @@ -38,7 +38,7 @@ ) ) - ;; CHECK: (func $multivalue-return-too-short (type $1) (result i32 f64) + ;; CHECK: (func $multivalue-return-too-short (type $2) (result i32 f64) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) ;; CHECK-NEXT: (local $2 anyref) @@ -74,7 +74,7 @@ ) ) - ;; CHECK: (func $multivalue-return-extra (type $2) (result i32 f64 anyref eqref) + ;; CHECK: (func $multivalue-return-extra (type $3) (result i32 f64 anyref eqref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) ;; CHECK-NEXT: (local $2 anyref) @@ -102,7 +102,7 @@ ) ) - ;; CHECK: (func $multivalue-return-extra-middle (type $3) (result i32 f64 anyref anyref) + ;; CHECK: (func $multivalue-return-extra-middle (type $4) (result i32 f64 anyref anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) ;; CHECK-NEXT: (local $2 anyref) @@ -144,7 +144,7 @@ ) ) - ;; CHECK: (func $multivalue-return-non-get (type $4) (param $other f64) (result i32 f64 anyref) + ;; CHECK: (func $multivalue-return-non-get (type $0) (param $other f64) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (local $3 anyref) @@ -181,4 +181,64 @@ ) ) ) + + ;; CHECK: (func $multivalue-return-non-extract (type $0) (param $other f64) (result i32 f64 anyref) + ;; CHECK-NEXT: (local $temp i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local $3 f64) + ;; CHECK-NEXT: (local $4 f64) + ;; CHECK-NEXT: (local $5 f64) + ;; CHECK-NEXT: (local $6 anyref) + ;; CHECK-NEXT: (local $7 anyref) + ;; CHECK-NEXT: (local $scratch_8 (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $scratch_9 f64) + ;; CHECK-NEXT: (local $scratch_10 i32) + ;; CHECK-NEXT: (local $scratch_11 f64) + ;; CHECK-NEXT: (local $scratch_12 i32) + ;; CHECK-NEXT: (local $scratch_13 f64) + ;; CHECK-NEXT: (local $scratch_14 i32) + ;; CHECK-NEXT: call $multivalue-return + ;; CHECK-NEXT: local.tee $scratch_8 + ;; CHECK-NEXT: tuple.extract 3 0 + ;; CHECK-NEXT: local.get $scratch_8 + ;; CHECK-NEXT: tuple.extract 3 1 + ;; CHECK-NEXT: local.get $scratch_8 + ;; CHECK-NEXT: tuple.extract 3 2 + ;; CHECK-NEXT: local.set $6 + ;; CHECK-NEXT: local.set $3 + ;; CHECK-NEXT: local.tee $temp + ;; CHECK-NEXT: local.get $temp + ;; CHECK-NEXT: local.get $3 + ;; CHECK-NEXT: local.get $6 + ;; CHECK-NEXT: local.set $7 + ;; CHECK-NEXT: local.set $4 + ;; CHECK-NEXT: local.get $4 + ;; CHECK-NEXT: local.get $7 + ;; CHECK-NEXT: drop + ;; CHECK-NEXT: local.set $5 + ;; CHECK-NEXT: drop + ;; CHECK-NEXT: local.get $5 + ;; CHECK-NEXT: local.get $6 + ;; CHECK-NEXT: tuple.make 3 + ;; CHECK-NEXT: ) + (func $multivalue-return-non-extract (param $other f64) (result i32 f64 anyref) + (local $temp (tuple i32 f64 anyref)) + ;; As the first case, but one extract is replaced with something else, so we + ;; do not optimize. + (tuple.make 3 + (tuple.extract 3 0 + (local.tee $temp + (call $multivalue-return) + ) + ) + (tuple.extract 3 1 + (local.get $temp) + (nop) ;; this breaks the pattern, appearing where the tuple.extract + ;; should be + ) + (tuple.extract 3 2 + (local.get $temp) + ) + ) + ) ) From aff3a1c1a635e39e5f72611b9adfd418abee6c64 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 10:56:36 -0700 Subject: [PATCH 10/17] test --- .../passes/optimize-stack-ir-multivalue.wast | 56 ++++++++++++++++--- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index b78bd76413c..2c539b68eb2 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -3,9 +3,9 @@ ;; RUN: wasm-opt %s -all --optimize-level=3 --generate-stack-ir --optimize-stack-ir --roundtrip --print-stack-ir | filecheck %s (module - ;; CHECK: (type $0 (func (param f64) (result i32 f64 anyref))) + ;; CHECK: (type $0 (func (result i32 f64 anyref))) - ;; CHECK: (type $1 (func (result i32 f64 anyref))) + ;; CHECK: (type $1 (func (param f64) (result i32 f64 anyref))) ;; CHECK: (type $2 (func (result i32 f64))) @@ -13,7 +13,7 @@ ;; CHECK: (type $4 (func (result i32 f64 anyref anyref))) - ;; CHECK: (func $multivalue-return (type $1) (result i32 f64 anyref) + ;; CHECK: (func $multivalue-return (type $0) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) ;; CHECK-NEXT: (local $2 anyref) @@ -144,7 +144,7 @@ ) ) - ;; CHECK: (func $multivalue-return-non-get (type $0) (param $other f64) (result i32 f64 anyref) + ;; CHECK: (func $multivalue-return-bad-get (type $1) (param $other f64) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (local $3 anyref) @@ -165,9 +165,9 @@ ;; CHECK-NEXT: local.get $3 ;; CHECK-NEXT: tuple.make 3 ;; CHECK-NEXT: ) - (func $multivalue-return-non-get (param $other f64) (result i32 f64 anyref) + (func $multivalue-return-bad-get (param $other f64) (result i32 f64 anyref) (local $temp (tuple i32 f64 anyref)) - ;; As the first case, but one get is replaced with something else, so we do + ;; As the first case, but one get has the wrong index, so we do ;; not optimize. (tuple.make 3 (tuple.extract 3 0 @@ -182,7 +182,49 @@ ) ) - ;; CHECK: (func $multivalue-return-non-extract (type $0) (param $other f64) (result i32 f64 anyref) + ;; CHECK: (func $multivalue-return-non-get (type $0) (result i32 f64 anyref) + ;; CHECK-NEXT: (local $temp i32) + ;; CHECK-NEXT: (local $1 f64) + ;; CHECK-NEXT: (local $2 anyref) + ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $scratch_4 f64) + ;; CHECK-NEXT: (local $scratch_5 i32) + ;; CHECK-NEXT: call $multivalue-return + ;; CHECK-NEXT: local.tee $scratch + ;; CHECK-NEXT: tuple.extract 3 0 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 1 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 2 + ;; CHECK-NEXT: local.set $2 + ;; CHECK-NEXT: local.set $1 + ;; CHECK-NEXT: local.tee $temp + ;; CHECK-NEXT: local.get $1 + ;; CHECK-NEXT: local.get $2 + ;; CHECK-NEXT: tuple.make 3 + ;; CHECK-NEXT: ) + (func $multivalue-return-non-get (result i32 f64 anyref) + (local $temp (tuple i32 f64 anyref)) + ;; As the first case, but one get is replaced by a non-get, so we do + ;; not optimize. + (tuple.make 3 + (tuple.extract 3 0 + (local.tee $temp + (call $multivalue-return) + ) + ) + (tuple.extract 3 1 + (nop) ;; this breaks the pattern, appearing where the get + ;; should be + (local.get $temp) + ) + (tuple.extract 3 2 + (local.get $temp) + ) + ) + ) + + ;; CHECK: (func $multivalue-return-non-extract (type $1) (param $other f64) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local $3 f64) From e3fffa1a9b0b24c8d20199dd0863e4a0bcb103d3 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 10:57:39 -0700 Subject: [PATCH 11/17] test --- .../passes/optimize-stack-ir-multivalue.wast | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index 2c539b68eb2..678cb7355ea 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -13,6 +13,8 @@ ;; CHECK: (type $4 (func (result i32 f64 anyref anyref))) + ;; CHECK: (type $5 (func (param f64) (result i32 anyref anyref))) + ;; CHECK: (func $multivalue-return (type $0) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) @@ -224,6 +226,45 @@ ) ) + ;; CHECK: (func $multivalue-return-bad-extract (type $5) (param $other f64) (result i32 anyref anyref) + ;; CHECK-NEXT: (local $temp i32) + ;; CHECK-NEXT: (local $2 f64) + ;; CHECK-NEXT: (local $3 anyref) + ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $scratch_5 f64) + ;; CHECK-NEXT: (local $scratch_6 i32) + ;; CHECK-NEXT: call $multivalue-return + ;; CHECK-NEXT: local.tee $scratch + ;; CHECK-NEXT: tuple.extract 3 0 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 1 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 2 + ;; CHECK-NEXT: local.set $3 + ;; CHECK-NEXT: local.set $2 + ;; CHECK-NEXT: local.tee $temp + ;; CHECK-NEXT: local.get $3 + ;; CHECK-NEXT: local.get $3 + ;; CHECK-NEXT: tuple.make 3 + ;; CHECK-NEXT: ) + (func $multivalue-return-bad-extract (param $other f64) (result i32 anyref anyref) + (local $temp (tuple i32 f64 anyref)) + ;; As the first case, but one extract has the wrong index, so we + ;; do not optimize. + (tuple.make 3 + (tuple.extract 3 0 + (local.tee $temp + (call $multivalue-return) + ) + ) + (tuple.extract 3 2 ;; this changed from 1 to 2 + (local.get $temp) + ) + (tuple.extract 3 2 + (local.get $temp) + ) + ) + ) ;; CHECK: (func $multivalue-return-non-extract (type $1) (param $other f64) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $scratch i32) From 80eeeef80a53cb42a75e475a5a8526b801bc1f96 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 11:00:38 -0700 Subject: [PATCH 12/17] test --- .../passes/optimize-stack-ir-multivalue.wast | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index 678cb7355ea..f46f1571fbc 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -15,6 +15,8 @@ ;; CHECK: (type $5 (func (param f64) (result i32 anyref anyref))) + ;; CHECK: (type $6 (func)) + ;; CHECK: (func $multivalue-return (type $0) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) @@ -324,4 +326,66 @@ ) ) ) + + ;; CHECK: (func $multiple-multivalue-return (type $6) + ;; CHECK-NEXT: (local $temp3 i32) + ;; CHECK-NEXT: (local $temp2 i32) + ;; CHECK-NEXT: (local $2 f64) + ;; CHECK-NEXT: (local $3 f64) + ;; CHECK-NEXT: (local $4 anyref) + ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $scratch_6 f64) + ;; CHECK-NEXT: (local $scratch_7 i32) + ;; CHECK-NEXT: (local $scratch_8 (tuple i32 f64)) + ;; CHECK-NEXT: (local $scratch_9 i32) + ;; CHECK-NEXT: call $multivalue-return + ;; CHECK-NEXT: local.tee $scratch + ;; CHECK-NEXT: tuple.extract 3 0 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 1 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 2 + ;; CHECK-NEXT: drop + ;; CHECK-NEXT: drop + ;; CHECK-NEXT: drop + ;; CHECK-NEXT: call $multivalue-return-too-short + ;; CHECK-NEXT: local.tee $scratch_8 + ;; CHECK-NEXT: tuple.extract 2 0 + ;; CHECK-NEXT: local.get $scratch_8 + ;; CHECK-NEXT: tuple.extract 2 1 + ;; CHECK-NEXT: drop + ;; CHECK-NEXT: drop + ;; CHECK-NEXT: ) + (func $multiple-multivalue-return + (local $temp3 (tuple i32 f64 anyref)) + (local $temp2 (tuple i32 f64)) + ;; Multiple optimizations in one function. + (tuple.drop 3 + (tuple.make 3 + (tuple.extract 3 0 + (local.tee $temp3 + (call $multivalue-return) + ) + ) + (tuple.extract 3 1 + (local.get $temp3) + ) + (tuple.extract 3 2 + (local.get $temp3) + ) + ) + ) + (tuple.drop 2 + (tuple.make 2 + (tuple.extract 2 0 + (local.tee $temp2 + (call $multivalue-return-too-short) + ) + ) + (tuple.extract 2 1 + (local.get $temp2) + ) + ) + ) + ) ) From fbf13a604eb0812aeb146b1998c41657f12b71ef Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 11:03:42 -0700 Subject: [PATCH 13/17] test --- .../passes/optimize-stack-ir-multivalue.wast | 74 +++++++------------ 1 file changed, 28 insertions(+), 46 deletions(-) diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index f46f1571fbc..67e51e4c6d3 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -15,7 +15,7 @@ ;; CHECK: (type $5 (func (param f64) (result i32 anyref anyref))) - ;; CHECK: (type $6 (func)) + ;; CHECK: (type $6 (func (param i32 f64 anyref))) ;; CHECK: (func $multivalue-return (type $0) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) @@ -327,65 +327,47 @@ ) ) - ;; CHECK: (func $multiple-multivalue-return (type $6) + ;; CHECK: (func $multiple-multivalue-return (type $6) (param $0 i32) (param $1 f64) (param $2 anyref) ;; CHECK-NEXT: (local $temp3 i32) ;; CHECK-NEXT: (local $temp2 i32) - ;; CHECK-NEXT: (local $2 f64) - ;; CHECK-NEXT: (local $3 f64) - ;; CHECK-NEXT: (local $4 anyref) + ;; CHECK-NEXT: (local $5 f64) + ;; CHECK-NEXT: (local $6 f64) + ;; CHECK-NEXT: (local $7 anyref) ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) - ;; CHECK-NEXT: (local $scratch_6 f64) - ;; CHECK-NEXT: (local $scratch_7 i32) - ;; CHECK-NEXT: (local $scratch_8 (tuple i32 f64)) - ;; CHECK-NEXT: (local $scratch_9 i32) + ;; CHECK-NEXT: (local $scratch_9 (tuple i32 f64)) ;; CHECK-NEXT: call $multivalue-return - ;; CHECK-NEXT: local.tee $scratch - ;; CHECK-NEXT: tuple.extract 3 0 - ;; CHECK-NEXT: local.get $scratch - ;; CHECK-NEXT: tuple.extract 3 1 - ;; CHECK-NEXT: local.get $scratch - ;; CHECK-NEXT: tuple.extract 3 2 - ;; CHECK-NEXT: drop - ;; CHECK-NEXT: drop - ;; CHECK-NEXT: drop + ;; CHECK-NEXT: call $multiple-multivalue-return ;; CHECK-NEXT: call $multivalue-return-too-short - ;; CHECK-NEXT: local.tee $scratch_8 - ;; CHECK-NEXT: tuple.extract 2 0 - ;; CHECK-NEXT: local.get $scratch_8 - ;; CHECK-NEXT: tuple.extract 2 1 - ;; CHECK-NEXT: drop - ;; CHECK-NEXT: drop + ;; CHECK-NEXT: ref.null none + ;; CHECK-NEXT: call $multiple-multivalue-return ;; CHECK-NEXT: ) - (func $multiple-multivalue-return + (func $multiple-multivalue-return (param i32 f64 anyref) (local $temp3 (tuple i32 f64 anyref)) (local $temp2 (tuple i32 f64)) ;; Multiple optimizations in one function. - (tuple.drop 3 - (tuple.make 3 - (tuple.extract 3 0 - (local.tee $temp3 - (call $multivalue-return) - ) - ) - (tuple.extract 3 1 - (local.get $temp3) - ) - (tuple.extract 3 2 - (local.get $temp3) + (call $multiple-multivalue-return + (tuple.extract 3 0 + (local.tee $temp3 + (call $multivalue-return) ) ) + (tuple.extract 3 1 + (local.get $temp3) + ) + (tuple.extract 3 2 + (local.get $temp3) + ) ) - (tuple.drop 2 - (tuple.make 2 - (tuple.extract 2 0 - (local.tee $temp2 - (call $multivalue-return-too-short) - ) - ) - (tuple.extract 2 1 - (local.get $temp2) + (call $multiple-multivalue-return + (tuple.extract 2 0 + (local.tee $temp2 + (call $multivalue-return-too-short) ) ) + (tuple.extract 2 1 + (local.get $temp2) + ) + (ref.null any) ) ) ) From 486c6bd5a3e2167189eabd578267e5937bf4dbdb Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 11:04:25 -0700 Subject: [PATCH 14/17] test --- .../passes/optimize-stack-ir-multivalue.wast | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index 67e51e4c6d3..c3dbedc22df 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -335,16 +335,22 @@ ;; CHECK-NEXT: (local $7 anyref) ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) ;; CHECK-NEXT: (local $scratch_9 (tuple i32 f64)) + ;; CHECK-NEXT: (local $scratch_10 (tuple i32 f64 anyref)) ;; CHECK-NEXT: call $multivalue-return ;; CHECK-NEXT: call $multiple-multivalue-return ;; CHECK-NEXT: call $multivalue-return-too-short ;; CHECK-NEXT: ref.null none ;; CHECK-NEXT: call $multiple-multivalue-return + ;; CHECK-NEXT: call $multivalue-return + ;; CHECK-NEXT: call $multiple-multivalue-return ;; CHECK-NEXT: ) (func $multiple-multivalue-return (param i32 f64 anyref) (local $temp3 (tuple i32 f64 anyref)) (local $temp2 (tuple i32 f64)) - ;; Multiple optimizations in one function. + + ;; Multiple optimizations in one function, including a case where we reuse + ;; the local index. + (call $multiple-multivalue-return (tuple.extract 3 0 (local.tee $temp3 @@ -358,6 +364,7 @@ (local.get $temp3) ) ) + (call $multiple-multivalue-return (tuple.extract 2 0 (local.tee $temp2 @@ -369,5 +376,19 @@ ) (ref.null any) ) + + (call $multiple-multivalue-return + (tuple.extract 3 0 + (local.tee $temp3 + (call $multivalue-return) + ) + ) + (tuple.extract 3 1 + (local.get $temp3) + ) + (tuple.extract 3 2 + (local.get $temp3) + ) + ) ) ) From 5664d3c20c252668ea16e6a725d98cae83796644 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Apr 2026 11:08:53 -0700 Subject: [PATCH 15/17] test --- src/wasm/wasm-stack-opts.cpp | 9 +- .../passes/optimize-stack-ir-multivalue.wast | 82 ++++++++++++++++--- 2 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/wasm/wasm-stack-opts.cpp b/src/wasm/wasm-stack-opts.cpp index fad72cfaec5..0050bd95fe5 100644 --- a/src/wasm/wasm-stack-opts.cpp +++ b/src/wasm/wasm-stack-opts.cpp @@ -298,9 +298,16 @@ void StackIROptimizer::local2Stack() { continue; } + // The tee must be read by exactly the proper number of gets, and no more, + // which is one less than the tuple size (the tee provides one get). + auto size = tee->type.size(); + auto& setInfluences = localGraph.getSetInfluences(tee); + if (setInfluences.size() != size - 1) { + continue; + } + // This is a tee of a tuple. Look for the expected extracts/gets. Each // tuple index has 2 items. - auto size = tee->type.size(); bool ok = true; for (Index i = 0; i < size; i++) { // Each tuple index has a pair of items. diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index c3dbedc22df..b52abcf1b0c 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -7,15 +7,15 @@ ;; CHECK: (type $1 (func (param f64) (result i32 f64 anyref))) - ;; CHECK: (type $2 (func (result i32 f64))) + ;; CHECK: (type $2 (func (param i32 f64 anyref))) - ;; CHECK: (type $3 (func (result i32 f64 anyref eqref))) + ;; CHECK: (type $3 (func (result i32 f64))) - ;; CHECK: (type $4 (func (result i32 f64 anyref anyref))) + ;; CHECK: (type $4 (func (result i32 f64 anyref eqref))) - ;; CHECK: (type $5 (func (param f64) (result i32 anyref anyref))) + ;; CHECK: (type $5 (func (result i32 f64 anyref anyref))) - ;; CHECK: (type $6 (func (param i32 f64 anyref))) + ;; CHECK: (type $6 (func (param f64) (result i32 anyref anyref))) ;; CHECK: (func $multivalue-return (type $0) (result i32 f64 anyref) ;; CHECK-NEXT: (local $temp i32) @@ -42,7 +42,7 @@ ) ) - ;; CHECK: (func $multivalue-return-too-short (type $2) (result i32 f64) + ;; CHECK: (func $multivalue-return-too-short (type $3) (result i32 f64) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) ;; CHECK-NEXT: (local $2 anyref) @@ -78,7 +78,7 @@ ) ) - ;; CHECK: (func $multivalue-return-extra (type $3) (result i32 f64 anyref eqref) + ;; CHECK: (func $multivalue-return-extra (type $4) (result i32 f64 anyref eqref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) ;; CHECK-NEXT: (local $2 anyref) @@ -106,7 +106,7 @@ ) ) - ;; CHECK: (func $multivalue-return-extra-middle (type $4) (result i32 f64 anyref anyref) + ;; CHECK: (func $multivalue-return-extra-middle (type $5) (result i32 f64 anyref anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $1 f64) ;; CHECK-NEXT: (local $2 anyref) @@ -228,7 +228,7 @@ ) ) - ;; CHECK: (func $multivalue-return-bad-extract (type $5) (param $other f64) (result i32 anyref anyref) + ;; CHECK: (func $multivalue-return-bad-extract (type $6) (param $other f64) (result i32 anyref anyref) ;; CHECK-NEXT: (local $temp i32) ;; CHECK-NEXT: (local $2 f64) ;; CHECK-NEXT: (local $3 anyref) @@ -327,7 +327,7 @@ ) ) - ;; CHECK: (func $multiple-multivalue-return (type $6) (param $0 i32) (param $1 f64) (param $2 anyref) + ;; CHECK: (func $multiple-multivalue-return (type $2) (param $0 i32) (param $1 f64) (param $2 anyref) ;; CHECK-NEXT: (local $temp3 i32) ;; CHECK-NEXT: (local $temp2 i32) ;; CHECK-NEXT: (local $5 f64) @@ -391,4 +391,66 @@ ) ) ) + + ;; CHECK: (func $multiple-multivalue-return-local-reuse (type $2) (param $0 i32) (param $1 f64) (param $2 anyref) + ;; CHECK-NEXT: (local $temp3 i32) + ;; CHECK-NEXT: (local $temp2 i32) + ;; CHECK-NEXT: (local $5 f64) + ;; CHECK-NEXT: (local $6 f64) + ;; CHECK-NEXT: (local $7 anyref) + ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $scratch_9 f64) + ;; CHECK-NEXT: (local $scratch_10 i32) + ;; CHECK-NEXT: call $multivalue-return + ;; CHECK-NEXT: local.tee $scratch + ;; CHECK-NEXT: tuple.extract 3 0 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 1 + ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: tuple.extract 3 2 + ;; CHECK-NEXT: local.set $7 + ;; CHECK-NEXT: local.set $5 + ;; CHECK-NEXT: local.tee $temp3 + ;; CHECK-NEXT: local.get $5 + ;; CHECK-NEXT: local.get $7 + ;; CHECK-NEXT: call $multiple-multivalue-return + ;; CHECK-NEXT: local.get $temp3 + ;; CHECK-NEXT: local.get $5 + ;; CHECK-NEXT: local.get $7 + ;; CHECK-NEXT: call $multiple-multivalue-return + ;; CHECK-NEXT: ) + (func $multiple-multivalue-return-local-reuse (param i32 f64 anyref) + (local $temp3 (tuple i32 f64 anyref)) + (local $temp2 (tuple i32 f64)) + + ;; As the last case, we have two things to possibly optimize. Here we reuse + ;; the tee'd value after the first one, which prevents any optimizations of + ;; this pattern. + + (call $multiple-multivalue-return + (tuple.extract 3 0 + (local.tee $temp3 + (call $multivalue-return) + ) + ) + (tuple.extract 3 1 + (local.get $temp3) + ) + (tuple.extract 3 2 + (local.get $temp3) + ) + ) + + (call $multiple-multivalue-return + (tuple.extract 3 0 + (local.get $temp3) ;; this changed + ) + (tuple.extract 3 1 + (local.get $temp3) + ) + (tuple.extract 3 2 + (local.get $temp3) + ) + ) + ) ) From 304ae140a90d919036be34d0a747aaaa79c51559 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 20 Apr 2026 09:54:49 -0700 Subject: [PATCH 16/17] test with and without roundtrip --- .../passes/optimize-stack-ir-multivalue.wast | 404 ++++++++++++------ 1 file changed, 265 insertions(+), 139 deletions(-) diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index b52abcf1b0c..79bfb77a311 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -1,6 +1,10 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: wasm-opt %s -all --optimize-level=3 --generate-stack-ir --optimize-stack-ir --roundtrip --print-stack-ir | filecheck %s +;; RUN: wasm-opt %s -all --optimize-level=3 --generate-stack-ir --optimize-stack-ir --print-stack-ir | filecheck %s + +;; Also test with roundtrip to verify that parsing does not undo this +;; optimization. +;; RUN: wasm-opt %s -all --optimize-level=3 --generate-stack-ir --optimize-stack-ir --roundtrip --print-stack-ir | filecheck %s --check-prefix=ROUNDTRIP (module ;; CHECK: (type $0 (func (result i32 f64 anyref))) @@ -18,11 +22,30 @@ ;; CHECK: (type $6 (func (param f64) (result i32 anyref anyref))) ;; CHECK: (func $multivalue-return (type $0) (result i32 f64 anyref) - ;; CHECK-NEXT: (local $temp i32) - ;; CHECK-NEXT: (local $1 f64) - ;; CHECK-NEXT: (local $2 anyref) + ;; CHECK-NEXT: (local $temp (tuple i32 f64 anyref)) ;; CHECK-NEXT: call $multivalue-return + ;; CHECK-NEXT: tuple.make 3 ;; CHECK-NEXT: ) + ;; ROUNDTRIP: (type $0 (func (result i32 f64 anyref))) + + ;; ROUNDTRIP: (type $1 (func (param f64) (result i32 f64 anyref))) + + ;; ROUNDTRIP: (type $2 (func (param i32 f64 anyref))) + + ;; ROUNDTRIP: (type $3 (func (result i32 f64))) + + ;; ROUNDTRIP: (type $4 (func (result i32 f64 anyref eqref))) + + ;; ROUNDTRIP: (type $5 (func (result i32 f64 anyref anyref))) + + ;; ROUNDTRIP: (type $6 (func (param f64) (result i32 anyref anyref))) + + ;; ROUNDTRIP: (func $multivalue-return (type $0) (result i32 f64 anyref) + ;; ROUNDTRIP-NEXT: (local $temp i32) + ;; ROUNDTRIP-NEXT: (local $1 f64) + ;; ROUNDTRIP-NEXT: (local $2 anyref) + ;; ROUNDTRIP-NEXT: call $multivalue-return + ;; ROUNDTRIP-NEXT: ) (func $multivalue-return (result i32 f64 anyref) (local $temp (tuple i32 f64 anyref)) ;; We can remove all these tuple operations after optiming and @@ -43,25 +66,34 @@ ) ;; CHECK: (func $multivalue-return-too-short (type $3) (result i32 f64) - ;; CHECK-NEXT: (local $temp i32) - ;; CHECK-NEXT: (local $1 f64) - ;; CHECK-NEXT: (local $2 anyref) - ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) - ;; CHECK-NEXT: (local $scratch_4 f64) - ;; CHECK-NEXT: (local $scratch_5 i32) + ;; CHECK-NEXT: (local $temp (tuple i32 f64 anyref)) ;; CHECK-NEXT: call $multivalue-return - ;; CHECK-NEXT: local.tee $scratch + ;; CHECK-NEXT: local.tee $temp ;; CHECK-NEXT: tuple.extract 3 0 - ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: local.get $temp ;; CHECK-NEXT: tuple.extract 3 1 - ;; CHECK-NEXT: local.get $scratch - ;; CHECK-NEXT: tuple.extract 3 2 - ;; CHECK-NEXT: local.set $2 - ;; CHECK-NEXT: local.set $1 - ;; CHECK-NEXT: local.tee $temp - ;; CHECK-NEXT: local.get $1 ;; CHECK-NEXT: tuple.make 2 ;; CHECK-NEXT: ) + ;; ROUNDTRIP: (func $multivalue-return-too-short (type $3) (result i32 f64) + ;; ROUNDTRIP-NEXT: (local $temp i32) + ;; ROUNDTRIP-NEXT: (local $1 f64) + ;; ROUNDTRIP-NEXT: (local $2 anyref) + ;; ROUNDTRIP-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; ROUNDTRIP-NEXT: (local $scratch_4 f64) + ;; ROUNDTRIP-NEXT: (local $scratch_5 i32) + ;; ROUNDTRIP-NEXT: call $multivalue-return + ;; ROUNDTRIP-NEXT: local.tee $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 0 + ;; ROUNDTRIP-NEXT: local.get $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 1 + ;; ROUNDTRIP-NEXT: local.get $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 2 + ;; ROUNDTRIP-NEXT: local.set $2 + ;; ROUNDTRIP-NEXT: local.set $1 + ;; ROUNDTRIP-NEXT: local.tee $temp + ;; ROUNDTRIP-NEXT: local.get $1 + ;; ROUNDTRIP-NEXT: tuple.make 2 + ;; ROUNDTRIP-NEXT: ) (func $multivalue-return-too-short (result i32 f64) (local $temp (tuple i32 f64 anyref)) ;; As above, but we only return 2 of the tuple's 3 items (i.e., we are too @@ -79,14 +111,20 @@ ) ;; CHECK: (func $multivalue-return-extra (type $4) (result i32 f64 anyref eqref) - ;; CHECK-NEXT: (local $temp i32) - ;; CHECK-NEXT: (local $1 f64) - ;; CHECK-NEXT: (local $2 anyref) - ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $temp (tuple i32 f64 anyref)) ;; CHECK-NEXT: call $multivalue-return ;; CHECK-NEXT: ref.null none ;; CHECK-NEXT: tuple.make 4 ;; CHECK-NEXT: ) + ;; ROUNDTRIP: (func $multivalue-return-extra (type $4) (result i32 f64 anyref eqref) + ;; ROUNDTRIP-NEXT: (local $temp i32) + ;; ROUNDTRIP-NEXT: (local $1 f64) + ;; ROUNDTRIP-NEXT: (local $2 anyref) + ;; ROUNDTRIP-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; ROUNDTRIP-NEXT: call $multivalue-return + ;; ROUNDTRIP-NEXT: ref.null none + ;; ROUNDTRIP-NEXT: tuple.make 4 + ;; ROUNDTRIP-NEXT: ) (func $multivalue-return-extra (result i32 f64 anyref eqref) (local $temp (tuple i32 f64 anyref)) ;; As above, but we add an item to the tuple. We can optimize here. @@ -107,27 +145,39 @@ ) ;; CHECK: (func $multivalue-return-extra-middle (type $5) (result i32 f64 anyref anyref) - ;; CHECK-NEXT: (local $temp i32) - ;; CHECK-NEXT: (local $1 f64) - ;; CHECK-NEXT: (local $2 anyref) - ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) - ;; CHECK-NEXT: (local $scratch_4 f64) - ;; CHECK-NEXT: (local $scratch_5 i32) + ;; CHECK-NEXT: (local $temp (tuple i32 f64 anyref)) ;; CHECK-NEXT: call $multivalue-return - ;; CHECK-NEXT: local.tee $scratch + ;; CHECK-NEXT: local.tee $temp ;; CHECK-NEXT: tuple.extract 3 0 - ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: local.get $temp ;; CHECK-NEXT: tuple.extract 3 1 - ;; CHECK-NEXT: local.get $scratch - ;; CHECK-NEXT: tuple.extract 3 2 - ;; CHECK-NEXT: local.set $2 - ;; CHECK-NEXT: local.set $1 - ;; CHECK-NEXT: local.tee $temp - ;; CHECK-NEXT: local.get $1 ;; CHECK-NEXT: ref.null none - ;; CHECK-NEXT: local.get $2 + ;; CHECK-NEXT: local.get $temp + ;; CHECK-NEXT: tuple.extract 3 2 ;; CHECK-NEXT: tuple.make 4 ;; CHECK-NEXT: ) + ;; ROUNDTRIP: (func $multivalue-return-extra-middle (type $5) (result i32 f64 anyref anyref) + ;; ROUNDTRIP-NEXT: (local $temp i32) + ;; ROUNDTRIP-NEXT: (local $1 f64) + ;; ROUNDTRIP-NEXT: (local $2 anyref) + ;; ROUNDTRIP-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; ROUNDTRIP-NEXT: (local $scratch_4 f64) + ;; ROUNDTRIP-NEXT: (local $scratch_5 i32) + ;; ROUNDTRIP-NEXT: call $multivalue-return + ;; ROUNDTRIP-NEXT: local.tee $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 0 + ;; ROUNDTRIP-NEXT: local.get $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 1 + ;; ROUNDTRIP-NEXT: local.get $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 2 + ;; ROUNDTRIP-NEXT: local.set $2 + ;; ROUNDTRIP-NEXT: local.set $1 + ;; ROUNDTRIP-NEXT: local.tee $temp + ;; ROUNDTRIP-NEXT: local.get $1 + ;; ROUNDTRIP-NEXT: ref.null none + ;; ROUNDTRIP-NEXT: local.get $2 + ;; ROUNDTRIP-NEXT: tuple.make 4 + ;; ROUNDTRIP-NEXT: ) (func $multivalue-return-extra-middle (result i32 f64 anyref anyref) (local $temp (tuple i32 f64 anyref)) ;; As the last case, but the extra item is in the middle. We cannot @@ -149,26 +199,36 @@ ) ;; CHECK: (func $multivalue-return-bad-get (type $1) (param $other f64) (result i32 f64 anyref) - ;; CHECK-NEXT: (local $temp i32) - ;; CHECK-NEXT: (local $2 f64) - ;; CHECK-NEXT: (local $3 anyref) - ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) - ;; CHECK-NEXT: (local $scratch_5 f64) - ;; CHECK-NEXT: (local $scratch_6 i32) + ;; CHECK-NEXT: (local $temp (tuple i32 f64 anyref)) ;; CHECK-NEXT: call $multivalue-return - ;; CHECK-NEXT: local.tee $scratch - ;; CHECK-NEXT: tuple.extract 3 0 - ;; CHECK-NEXT: local.get $scratch - ;; CHECK-NEXT: tuple.extract 3 1 - ;; CHECK-NEXT: local.get $scratch - ;; CHECK-NEXT: tuple.extract 3 2 - ;; CHECK-NEXT: local.set $3 - ;; CHECK-NEXT: local.set $2 ;; CHECK-NEXT: local.tee $temp + ;; CHECK-NEXT: tuple.extract 3 0 ;; CHECK-NEXT: local.get $other - ;; CHECK-NEXT: local.get $3 + ;; CHECK-NEXT: local.get $temp + ;; CHECK-NEXT: tuple.extract 3 2 ;; CHECK-NEXT: tuple.make 3 ;; CHECK-NEXT: ) + ;; ROUNDTRIP: (func $multivalue-return-bad-get (type $1) (param $other f64) (result i32 f64 anyref) + ;; ROUNDTRIP-NEXT: (local $temp i32) + ;; ROUNDTRIP-NEXT: (local $2 f64) + ;; ROUNDTRIP-NEXT: (local $3 anyref) + ;; ROUNDTRIP-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; ROUNDTRIP-NEXT: (local $scratch_5 f64) + ;; ROUNDTRIP-NEXT: (local $scratch_6 i32) + ;; ROUNDTRIP-NEXT: call $multivalue-return + ;; ROUNDTRIP-NEXT: local.tee $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 0 + ;; ROUNDTRIP-NEXT: local.get $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 1 + ;; ROUNDTRIP-NEXT: local.get $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 2 + ;; ROUNDTRIP-NEXT: local.set $3 + ;; ROUNDTRIP-NEXT: local.set $2 + ;; ROUNDTRIP-NEXT: local.tee $temp + ;; ROUNDTRIP-NEXT: local.get $other + ;; ROUNDTRIP-NEXT: local.get $3 + ;; ROUNDTRIP-NEXT: tuple.make 3 + ;; ROUNDTRIP-NEXT: ) (func $multivalue-return-bad-get (param $other f64) (result i32 f64 anyref) (local $temp (tuple i32 f64 anyref)) ;; As the first case, but one get has the wrong index, so we do @@ -187,26 +247,37 @@ ) ;; CHECK: (func $multivalue-return-non-get (type $0) (result i32 f64 anyref) - ;; CHECK-NEXT: (local $temp i32) - ;; CHECK-NEXT: (local $1 f64) - ;; CHECK-NEXT: (local $2 anyref) - ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) - ;; CHECK-NEXT: (local $scratch_4 f64) - ;; CHECK-NEXT: (local $scratch_5 i32) + ;; CHECK-NEXT: (local $temp (tuple i32 f64 anyref)) ;; CHECK-NEXT: call $multivalue-return - ;; CHECK-NEXT: local.tee $scratch + ;; CHECK-NEXT: local.tee $temp ;; CHECK-NEXT: tuple.extract 3 0 - ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: local.get $temp ;; CHECK-NEXT: tuple.extract 3 1 - ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: local.get $temp ;; CHECK-NEXT: tuple.extract 3 2 - ;; CHECK-NEXT: local.set $2 - ;; CHECK-NEXT: local.set $1 - ;; CHECK-NEXT: local.tee $temp - ;; CHECK-NEXT: local.get $1 - ;; CHECK-NEXT: local.get $2 ;; CHECK-NEXT: tuple.make 3 ;; CHECK-NEXT: ) + ;; ROUNDTRIP: (func $multivalue-return-non-get (type $0) (result i32 f64 anyref) + ;; ROUNDTRIP-NEXT: (local $temp i32) + ;; ROUNDTRIP-NEXT: (local $1 f64) + ;; ROUNDTRIP-NEXT: (local $2 anyref) + ;; ROUNDTRIP-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; ROUNDTRIP-NEXT: (local $scratch_4 f64) + ;; ROUNDTRIP-NEXT: (local $scratch_5 i32) + ;; ROUNDTRIP-NEXT: call $multivalue-return + ;; ROUNDTRIP-NEXT: local.tee $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 0 + ;; ROUNDTRIP-NEXT: local.get $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 1 + ;; ROUNDTRIP-NEXT: local.get $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 2 + ;; ROUNDTRIP-NEXT: local.set $2 + ;; ROUNDTRIP-NEXT: local.set $1 + ;; ROUNDTRIP-NEXT: local.tee $temp + ;; ROUNDTRIP-NEXT: local.get $1 + ;; ROUNDTRIP-NEXT: local.get $2 + ;; ROUNDTRIP-NEXT: tuple.make 3 + ;; ROUNDTRIP-NEXT: ) (func $multivalue-return-non-get (result i32 f64 anyref) (local $temp (tuple i32 f64 anyref)) ;; As the first case, but one get is replaced by a non-get, so we do @@ -229,26 +300,37 @@ ) ;; CHECK: (func $multivalue-return-bad-extract (type $6) (param $other f64) (result i32 anyref anyref) - ;; CHECK-NEXT: (local $temp i32) - ;; CHECK-NEXT: (local $2 f64) - ;; CHECK-NEXT: (local $3 anyref) - ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) - ;; CHECK-NEXT: (local $scratch_5 f64) - ;; CHECK-NEXT: (local $scratch_6 i32) + ;; CHECK-NEXT: (local $temp (tuple i32 f64 anyref)) ;; CHECK-NEXT: call $multivalue-return - ;; CHECK-NEXT: local.tee $scratch + ;; CHECK-NEXT: local.tee $temp ;; CHECK-NEXT: tuple.extract 3 0 - ;; CHECK-NEXT: local.get $scratch - ;; CHECK-NEXT: tuple.extract 3 1 - ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: local.get $temp + ;; CHECK-NEXT: tuple.extract 3 2 + ;; CHECK-NEXT: local.get $temp ;; CHECK-NEXT: tuple.extract 3 2 - ;; CHECK-NEXT: local.set $3 - ;; CHECK-NEXT: local.set $2 - ;; CHECK-NEXT: local.tee $temp - ;; CHECK-NEXT: local.get $3 - ;; CHECK-NEXT: local.get $3 ;; CHECK-NEXT: tuple.make 3 ;; CHECK-NEXT: ) + ;; ROUNDTRIP: (func $multivalue-return-bad-extract (type $6) (param $other f64) (result i32 anyref anyref) + ;; ROUNDTRIP-NEXT: (local $temp i32) + ;; ROUNDTRIP-NEXT: (local $2 f64) + ;; ROUNDTRIP-NEXT: (local $3 anyref) + ;; ROUNDTRIP-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; ROUNDTRIP-NEXT: (local $scratch_5 f64) + ;; ROUNDTRIP-NEXT: (local $scratch_6 i32) + ;; ROUNDTRIP-NEXT: call $multivalue-return + ;; ROUNDTRIP-NEXT: local.tee $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 0 + ;; ROUNDTRIP-NEXT: local.get $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 1 + ;; ROUNDTRIP-NEXT: local.get $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 2 + ;; ROUNDTRIP-NEXT: local.set $3 + ;; ROUNDTRIP-NEXT: local.set $2 + ;; ROUNDTRIP-NEXT: local.tee $temp + ;; ROUNDTRIP-NEXT: local.get $3 + ;; ROUNDTRIP-NEXT: local.get $3 + ;; ROUNDTRIP-NEXT: tuple.make 3 + ;; ROUNDTRIP-NEXT: ) (func $multivalue-return-bad-extract (param $other f64) (result i32 anyref anyref) (local $temp (tuple i32 f64 anyref)) ;; As the first case, but one extract has the wrong index, so we @@ -268,44 +350,58 @@ ) ) ;; CHECK: (func $multivalue-return-non-extract (type $1) (param $other f64) (result i32 f64 anyref) - ;; CHECK-NEXT: (local $temp i32) - ;; CHECK-NEXT: (local $scratch i32) - ;; CHECK-NEXT: (local $3 f64) - ;; CHECK-NEXT: (local $4 f64) - ;; CHECK-NEXT: (local $5 f64) - ;; CHECK-NEXT: (local $6 anyref) - ;; CHECK-NEXT: (local $7 anyref) - ;; CHECK-NEXT: (local $scratch_8 (tuple i32 f64 anyref)) - ;; CHECK-NEXT: (local $scratch_9 f64) - ;; CHECK-NEXT: (local $scratch_10 i32) - ;; CHECK-NEXT: (local $scratch_11 f64) - ;; CHECK-NEXT: (local $scratch_12 i32) - ;; CHECK-NEXT: (local $scratch_13 f64) - ;; CHECK-NEXT: (local $scratch_14 i32) + ;; CHECK-NEXT: (local $temp (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) ;; CHECK-NEXT: call $multivalue-return - ;; CHECK-NEXT: local.tee $scratch_8 + ;; CHECK-NEXT: local.tee $temp ;; CHECK-NEXT: tuple.extract 3 0 - ;; CHECK-NEXT: local.get $scratch_8 + ;; CHECK-NEXT: local.get $temp + ;; CHECK-NEXT: local.set $scratch + ;; CHECK-NEXT: local.get $scratch ;; CHECK-NEXT: tuple.extract 3 1 - ;; CHECK-NEXT: local.get $scratch_8 - ;; CHECK-NEXT: tuple.extract 3 2 - ;; CHECK-NEXT: local.set $6 - ;; CHECK-NEXT: local.set $3 - ;; CHECK-NEXT: local.tee $temp ;; CHECK-NEXT: local.get $temp - ;; CHECK-NEXT: local.get $3 - ;; CHECK-NEXT: local.get $6 - ;; CHECK-NEXT: local.set $7 - ;; CHECK-NEXT: local.set $4 - ;; CHECK-NEXT: local.get $4 - ;; CHECK-NEXT: local.get $7 - ;; CHECK-NEXT: drop - ;; CHECK-NEXT: local.set $5 - ;; CHECK-NEXT: drop - ;; CHECK-NEXT: local.get $5 - ;; CHECK-NEXT: local.get $6 + ;; CHECK-NEXT: tuple.extract 3 2 ;; CHECK-NEXT: tuple.make 3 ;; CHECK-NEXT: ) + ;; ROUNDTRIP: (func $multivalue-return-non-extract (type $1) (param $other f64) (result i32 f64 anyref) + ;; ROUNDTRIP-NEXT: (local $temp i32) + ;; ROUNDTRIP-NEXT: (local $scratch i32) + ;; ROUNDTRIP-NEXT: (local $3 f64) + ;; ROUNDTRIP-NEXT: (local $4 f64) + ;; ROUNDTRIP-NEXT: (local $5 f64) + ;; ROUNDTRIP-NEXT: (local $6 anyref) + ;; ROUNDTRIP-NEXT: (local $7 anyref) + ;; ROUNDTRIP-NEXT: (local $scratch_8 (tuple i32 f64 anyref)) + ;; ROUNDTRIP-NEXT: (local $scratch_9 f64) + ;; ROUNDTRIP-NEXT: (local $scratch_10 i32) + ;; ROUNDTRIP-NEXT: (local $scratch_11 f64) + ;; ROUNDTRIP-NEXT: (local $scratch_12 i32) + ;; ROUNDTRIP-NEXT: (local $scratch_13 f64) + ;; ROUNDTRIP-NEXT: (local $scratch_14 i32) + ;; ROUNDTRIP-NEXT: call $multivalue-return + ;; ROUNDTRIP-NEXT: local.tee $scratch_8 + ;; ROUNDTRIP-NEXT: tuple.extract 3 0 + ;; ROUNDTRIP-NEXT: local.get $scratch_8 + ;; ROUNDTRIP-NEXT: tuple.extract 3 1 + ;; ROUNDTRIP-NEXT: local.get $scratch_8 + ;; ROUNDTRIP-NEXT: tuple.extract 3 2 + ;; ROUNDTRIP-NEXT: local.set $6 + ;; ROUNDTRIP-NEXT: local.set $3 + ;; ROUNDTRIP-NEXT: local.tee $temp + ;; ROUNDTRIP-NEXT: local.get $temp + ;; ROUNDTRIP-NEXT: local.get $3 + ;; ROUNDTRIP-NEXT: local.get $6 + ;; ROUNDTRIP-NEXT: local.set $7 + ;; ROUNDTRIP-NEXT: local.set $4 + ;; ROUNDTRIP-NEXT: local.get $4 + ;; ROUNDTRIP-NEXT: local.get $7 + ;; ROUNDTRIP-NEXT: drop + ;; ROUNDTRIP-NEXT: local.set $5 + ;; ROUNDTRIP-NEXT: drop + ;; ROUNDTRIP-NEXT: local.get $5 + ;; ROUNDTRIP-NEXT: local.get $6 + ;; ROUNDTRIP-NEXT: tuple.make 3 + ;; ROUNDTRIP-NEXT: ) (func $multivalue-return-non-extract (param $other f64) (result i32 f64 anyref) (local $temp (tuple i32 f64 anyref)) ;; As the first case, but one extract is replaced with something else, so we @@ -328,14 +424,8 @@ ) ;; CHECK: (func $multiple-multivalue-return (type $2) (param $0 i32) (param $1 f64) (param $2 anyref) - ;; CHECK-NEXT: (local $temp3 i32) - ;; CHECK-NEXT: (local $temp2 i32) - ;; CHECK-NEXT: (local $5 f64) - ;; CHECK-NEXT: (local $6 f64) - ;; CHECK-NEXT: (local $7 anyref) - ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) - ;; CHECK-NEXT: (local $scratch_9 (tuple i32 f64)) - ;; CHECK-NEXT: (local $scratch_10 (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $temp3 (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $temp2 (tuple i32 f64)) ;; CHECK-NEXT: call $multivalue-return ;; CHECK-NEXT: call $multiple-multivalue-return ;; CHECK-NEXT: call $multivalue-return-too-short @@ -344,6 +434,23 @@ ;; CHECK-NEXT: call $multivalue-return ;; CHECK-NEXT: call $multiple-multivalue-return ;; CHECK-NEXT: ) + ;; ROUNDTRIP: (func $multiple-multivalue-return (type $2) (param $0 i32) (param $1 f64) (param $2 anyref) + ;; ROUNDTRIP-NEXT: (local $temp3 i32) + ;; ROUNDTRIP-NEXT: (local $temp2 i32) + ;; ROUNDTRIP-NEXT: (local $5 f64) + ;; ROUNDTRIP-NEXT: (local $6 f64) + ;; ROUNDTRIP-NEXT: (local $7 anyref) + ;; ROUNDTRIP-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; ROUNDTRIP-NEXT: (local $scratch_9 (tuple i32 f64)) + ;; ROUNDTRIP-NEXT: (local $scratch_10 (tuple i32 f64 anyref)) + ;; ROUNDTRIP-NEXT: call $multivalue-return + ;; ROUNDTRIP-NEXT: call $multiple-multivalue-return + ;; ROUNDTRIP-NEXT: call $multivalue-return-too-short + ;; ROUNDTRIP-NEXT: ref.null none + ;; ROUNDTRIP-NEXT: call $multiple-multivalue-return + ;; ROUNDTRIP-NEXT: call $multivalue-return + ;; ROUNDTRIP-NEXT: call $multiple-multivalue-return + ;; ROUNDTRIP-NEXT: ) (func $multiple-multivalue-return (param i32 f64 anyref) (local $temp3 (tuple i32 f64 anyref)) (local $temp2 (tuple i32 f64)) @@ -393,32 +500,51 @@ ) ;; CHECK: (func $multiple-multivalue-return-local-reuse (type $2) (param $0 i32) (param $1 f64) (param $2 anyref) - ;; CHECK-NEXT: (local $temp3 i32) - ;; CHECK-NEXT: (local $temp2 i32) - ;; CHECK-NEXT: (local $5 f64) - ;; CHECK-NEXT: (local $6 f64) - ;; CHECK-NEXT: (local $7 anyref) - ;; CHECK-NEXT: (local $scratch (tuple i32 f64 anyref)) - ;; CHECK-NEXT: (local $scratch_9 f64) - ;; CHECK-NEXT: (local $scratch_10 i32) + ;; CHECK-NEXT: (local $temp3 (tuple i32 f64 anyref)) + ;; CHECK-NEXT: (local $temp2 (tuple i32 f64)) ;; CHECK-NEXT: call $multivalue-return - ;; CHECK-NEXT: local.tee $scratch + ;; CHECK-NEXT: local.tee $temp3 ;; CHECK-NEXT: tuple.extract 3 0 - ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: local.get $temp3 ;; CHECK-NEXT: tuple.extract 3 1 - ;; CHECK-NEXT: local.get $scratch + ;; CHECK-NEXT: local.get $temp3 ;; CHECK-NEXT: tuple.extract 3 2 - ;; CHECK-NEXT: local.set $7 - ;; CHECK-NEXT: local.set $5 - ;; CHECK-NEXT: local.tee $temp3 - ;; CHECK-NEXT: local.get $5 - ;; CHECK-NEXT: local.get $7 ;; CHECK-NEXT: call $multiple-multivalue-return ;; CHECK-NEXT: local.get $temp3 - ;; CHECK-NEXT: local.get $5 - ;; CHECK-NEXT: local.get $7 + ;; CHECK-NEXT: tuple.extract 3 0 + ;; CHECK-NEXT: local.get $temp3 + ;; CHECK-NEXT: tuple.extract 3 1 + ;; CHECK-NEXT: local.get $temp3 + ;; CHECK-NEXT: tuple.extract 3 2 ;; CHECK-NEXT: call $multiple-multivalue-return ;; CHECK-NEXT: ) + ;; ROUNDTRIP: (func $multiple-multivalue-return-local-reuse (type $2) (param $0 i32) (param $1 f64) (param $2 anyref) + ;; ROUNDTRIP-NEXT: (local $temp3 i32) + ;; ROUNDTRIP-NEXT: (local $temp2 i32) + ;; ROUNDTRIP-NEXT: (local $5 f64) + ;; ROUNDTRIP-NEXT: (local $6 f64) + ;; ROUNDTRIP-NEXT: (local $7 anyref) + ;; ROUNDTRIP-NEXT: (local $scratch (tuple i32 f64 anyref)) + ;; ROUNDTRIP-NEXT: (local $scratch_9 f64) + ;; ROUNDTRIP-NEXT: (local $scratch_10 i32) + ;; ROUNDTRIP-NEXT: call $multivalue-return + ;; ROUNDTRIP-NEXT: local.tee $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 0 + ;; ROUNDTRIP-NEXT: local.get $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 1 + ;; ROUNDTRIP-NEXT: local.get $scratch + ;; ROUNDTRIP-NEXT: tuple.extract 3 2 + ;; ROUNDTRIP-NEXT: local.set $7 + ;; ROUNDTRIP-NEXT: local.set $5 + ;; ROUNDTRIP-NEXT: local.tee $temp3 + ;; ROUNDTRIP-NEXT: local.get $5 + ;; ROUNDTRIP-NEXT: local.get $7 + ;; ROUNDTRIP-NEXT: call $multiple-multivalue-return + ;; ROUNDTRIP-NEXT: local.get $temp3 + ;; ROUNDTRIP-NEXT: local.get $5 + ;; ROUNDTRIP-NEXT: local.get $7 + ;; ROUNDTRIP-NEXT: call $multiple-multivalue-return + ;; ROUNDTRIP-NEXT: ) (func $multiple-multivalue-return-local-reuse (param i32 f64 anyref) (local $temp3 (tuple i32 f64 anyref)) (local $temp2 (tuple i32 f64)) From 6629df9f76b19f1e015e571994dcf7cdfbbc6be6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 20 Apr 2026 09:56:44 -0700 Subject: [PATCH 17/17] update a comment --- test/lit/passes/optimize-stack-ir-multivalue.wast | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lit/passes/optimize-stack-ir-multivalue.wast b/test/lit/passes/optimize-stack-ir-multivalue.wast index 79bfb77a311..0eeb4dee870 100644 --- a/test/lit/passes/optimize-stack-ir-multivalue.wast +++ b/test/lit/passes/optimize-stack-ir-multivalue.wast @@ -49,7 +49,7 @@ (func $multivalue-return (result i32 f64 anyref) (local $temp (tuple i32 f64 anyref)) ;; We can remove all these tuple operations after optiming and - ;; roundtripping (though a few locals will be added FIXME). + ;; roundtripping (though a few locals will be added in roundtripping FIXME). (tuple.make 3 (tuple.extract 3 0 (local.tee $temp