diff --git a/src/passes/Outlining.cpp b/src/passes/Outlining.cpp index 840a84bad37..6a48a42b0d9 100644 --- a/src/passes/Outlining.cpp +++ b/src/passes/Outlining.cpp @@ -34,12 +34,6 @@ #define ODBG(statement) #endif -// Check a Result or MaybeResult for error and call Fatal() if the error exists. -#define ASSERT_OK(val) \ - if (auto _val = (val); auto err = _val.getErr()) { \ - Fatal() << err->msg; \ - } - namespace wasm { // This custom hasher conforms to std::hash. Its purpose is to provide diff --git a/src/support/result.h b/src/support/result.h index f34f6300759..490facfd28c 100644 --- a/src/support/result.h +++ b/src/support/result.h @@ -17,6 +17,7 @@ #ifndef wasm_support_result_h #define wasm_support_result_h +#include #include #include @@ -39,6 +40,13 @@ struct Err { return (typename decltype(_val)::ErrorType)(*err); \ } +// Assert that a Result or MaybeResult is not an error. +#define ASSERT_OK(val) \ + do { \ + [[maybe_unused]] auto _val = (val); \ + assert(!_val.getErr()); \ + } while (false) + // Represent a result of type T or an error message. template struct [[nodiscard]] Result { using ErrorType = E; diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index 0cc4e3cb727..3561a40bad6 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -433,10 +433,10 @@ class IRBuilder : public UnifiedExpressionVisitor> { // function's `explicitLabelNames`. bool labelExplicit = false; - // If the control flow scope has an input type, we need to lower it using a - // scratch local because we cannot represent control flow input in the IR. + // If the control flow scope has an input type, we need to lower it using + // scratch locals because we cannot represent control flow input in the IR. Type inputType; - Index inputLocal = -1; + std::vector inputLocals; // If there are br_on_*, try_table, or resume branches that target this // scope and carry additional values, we need to use a scratch local to @@ -461,13 +461,6 @@ class IRBuilder : public UnifiedExpressionVisitor> { ScopeCtx() : scope(NoScope{}) {} ScopeCtx(Scope scope, Type inputType) : scope(scope), inputType(inputType) {} - ScopeCtx( - Scope scope, Name label, bool labelUsed, Type inputType, Index inputLocal) - : scope(scope), label(label), labelUsed(labelUsed), inputType(inputType), - inputLocal(inputLocal) {} - ScopeCtx(Scope scope, Name label, bool labelUsed, Name branchLabel) - : scope(scope), label(label), branchLabel(branchLabel), - labelUsed(labelUsed) {} static ScopeCtx makeFunc(Function* func) { return ScopeCtx(FuncScope{func}, Type::none); @@ -514,7 +507,7 @@ class IRBuilder : public UnifiedExpressionVisitor> { unreachable = false; if (!keepInput) { inputType = Type::none; - inputLocal = -1; + inputLocals.clear(); } } bool isNone() { return std::get_if(&scope); } @@ -715,35 +708,7 @@ class IRBuilder : public UnifiedExpressionVisitor> { Index blockHint = 0; Index labelHint = 0; - Result<> pushScope(ScopeCtx&& scope) { - if (auto label = scope.getOriginalLabel()) { - // Assign a fresh label to the scope, if necessary. - if (!scope.label) { - scope.label = makeFresh(label); - } - // Record the original label to handle references to it correctly. - labelDepths[label].push_back(scopeStack.size() + 1); - } - if (binaryPos) { - scope.startPos = lastBinaryPos; - lastBinaryPos = *binaryPos; - } - bool hasInput = scope.inputType != Type::none; - Index inputLocal = scope.inputLocal; - if (hasInput && !scope.isDelimiter()) { - if (inputLocal == Index(-1)) { - auto scratch = addScratchLocal(scope.inputType); - CHECK_ERR(scratch); - inputLocal = scope.inputLocal = *scratch; - } - CHECK_ERR(makeLocalSet(inputLocal)); - } - scopeStack.emplace_back(std::move(scope)); - if (hasInput) { - CHECK_ERR(makeLocalGet(inputLocal)); - } - return Ok{}; - } + Result<> pushScope(ScopeCtx&& scope); ScopeCtx& getScope() { if (scopeStack.empty()) { @@ -806,7 +771,9 @@ class IRBuilder : public UnifiedExpressionVisitor> { Result> getExtraOutputLocalAndLabel(Index label, size_t extraArity); Expression* fixExtraOutput(ScopeCtx& scope, Name label, Expression* expr); - void fixLoopWithInput(Loop* loop, Type inputType, Index scratch); + void fixLoopWithInput(Loop* loop, + Type inputType, + const std::vector& scratch); void applyAnnotations(Expression* expr, const CodeAnnotation& annotation); diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 78151f238dd..2ca99b9fca9 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -778,6 +778,17 @@ struct IRBuilder::ChildPopper ConstraintCollector{builder, children}.visitStackSwitch(curr, ct); return popConstrainedChildren(children); } + + Result> popInputs(Type inputType) { + std::vector inputs(inputType.size()); + std::vector children; + children.reserve(inputType.size()); + for (size_t i = 0; i < inputType.size(); ++i) { + children.push_back({&inputs[i], {inputType[i]}}); + } + CHECK_ERR(popConstrainedChildren(children)); + return inputs; + } }; Result<> IRBuilder::visit(Expression* curr) { @@ -877,6 +888,42 @@ IRBuilder::visitTryTableStart(TryTable* trytable, Name label, Type inputType) { return pushScope(ScopeCtx::makeTryTable(trytable, label, inputType)); } +Result<> IRBuilder::pushScope(ScopeCtx&& scope) { + if (auto label = scope.getOriginalLabel()) { + // Assign a fresh label to the scope, if necessary. + if (!scope.label) { + scope.label = makeFresh(label); + } + // Record the original label to handle references to it correctly. + labelDepths[label].push_back(scopeStack.size() + 1); + } + if (binaryPos) { + scope.startPos = lastBinaryPos; + lastBinaryPos = *binaryPos; + } + bool hasInput = scope.inputType != Type::none; + if (hasInput && !scope.isDelimiter()) { + auto inputs = ChildPopper{*this}.popInputs(scope.inputType); + CHECK_ERR(inputs); + scope.inputLocals.reserve(inputs->size()); + for (size_t i = 0; i < inputs->size(); ++i) { + auto scratch = addScratchLocal(scope.inputType[i]); + CHECK_ERR(scratch); + scope.inputLocals.push_back(*scratch); + pushSynthetic(builder.makeLocalSet(*scratch, (*inputs)[i])); + } + } + scopeStack.emplace_back(std::move(scope)); + if (hasInput) { + auto& newScope = getScope(); + for (size_t i = 0; i < newScope.inputLocals.size(); ++i) { + pushSynthetic( + builder.makeLocalGet(newScope.inputLocals[i], newScope.inputType[i])); + } + } + return Ok{}; +} + Result IRBuilder::finishScope(Block* block) { #if IR_BUILDER_DEBUG if (auto* loc = std::get_if(&debugLoc)) { @@ -1212,8 +1259,8 @@ Result<> IRBuilder::visitEnd() { if (scope.inputType != Type::none && scope.labelUsed) { // Branches to this loop carry values, but Binaryen IR does not support // that. Fix this by trampolining the branches through new code that sets - // the branch value to the appropriate scratch local. - fixLoopWithInput(loop, scope.inputType, scope.inputLocal); + // the branch value to the appropriate scratch locals. + fixLoopWithInput(loop, scope.inputType, scope.inputLocals); } loop->finalize(loop->type); push(loop); @@ -1222,8 +1269,19 @@ Result<> IRBuilder::visitEnd() { if (scope.inputType != Type::none) { // Normally an if without an else must have type none, but if there is an // input parameter, the empty else arm must propagate its value. - // Synthesize an else arm that loads the value from the scratch local. - iff->ifFalse = builder.makeLocalGet(scope.inputLocal, scope.inputType); + // Synthesize an else arm that loads the value from the scratch locals. + if (scope.inputType.isSingle()) { + iff->ifFalse = + builder.makeLocalGet(scope.inputLocals[0], scope.inputType); + } else { + std::vector gets; + gets.reserve(scope.inputLocals.size()); + for (size_t i = 0; i < scope.inputLocals.size(); ++i) { + gets.push_back( + builder.makeLocalGet(scope.inputLocals[i], scope.inputType[i])); + } + iff->ifFalse = builder.makeTupleMake(gets); + } } else { iff->ifFalse = nullptr; } @@ -1368,17 +1426,18 @@ IRBuilder::fixExtraOutput(ScopeCtx& scope, Name label, Expression* curr) { return curr; } -// Branches to this loop need to be trampolined through code that sets the value -// carried by the branch to the appropriate scratch local before branching to -// the loop. Transform this: +// Branches to this loop need to be trampolined through code that sets the +// values carried by the branch to the appropriate scratch locals before +// branching to the loop. Transform this: // // (loop $l (param t1) (result t2) ...) // -// to this: +// to this (or, when t1 is a tuple, using a tuple scratch local to extract each +// element into its corresponding input scratch local): // // (loop $l0 (result t2) // (block $l1 (result t2) -// (local.set $scratch ;; set the branch values to the scratch local +// (local.set $scratch ;; set the branch value to the scratch local // (block $l (result t1) // (br $l1 ;; exit the loop with the fallthrough value, if any. // ... ;; contains branches to $l @@ -1388,7 +1447,9 @@ IRBuilder::fixExtraOutput(ScopeCtx& scope, Name label, Expression* curr) { // (br $l0) ;; continue the loop // ) // ) -void IRBuilder::fixLoopWithInput(Loop* loop, Type inputType, Index scratch) { +void IRBuilder::fixLoopWithInput(Loop* loop, + Type inputType, + const std::vector& scratch) { auto l = loop->name; auto l0 = makeFresh(l, 0); auto l1 = makeFresh(l, 1); @@ -1399,10 +1460,22 @@ void IRBuilder::fixLoopWithInput(Loop* loop, Type inputType, Index scratch) { loop->body, l, builder.makeBreak(l1), inputType) : builder.makeBlock(l, {builder.makeBreak(l1, loop->body)}, inputType); - Block* outer = builder.makeBlock( - l1, - {builder.makeLocalSet(scratch, inner), builder.makeBreak(l0)}, - loop->type); + std::vector list; + if (inputType.isSingle()) { + list.push_back(builder.makeLocalSet(scratch[0], inner)); + } else { + auto tupleScratch = addScratchLocal(inputType); + ASSERT_OK(tupleScratch); + list.push_back(builder.makeLocalSet(*tupleScratch, inner)); + for (Index i = 0; i < scratch.size(); ++i) { + list.push_back(builder.makeLocalSet( + scratch[i], + builder.makeTupleExtract(builder.makeLocalGet(*tupleScratch, inputType), + i))); + } + } + list.push_back(builder.makeBreak(l0)); + Block* outer = builder.makeBlock(l1, list, loop->type); loop->body = outer; loop->name = l0; diff --git a/test/lit/basic/extra-branch-values.wast b/test/lit/basic/extra-branch-values.wast index 953ca7d634f..8f0980c6b9a 100644 --- a/test/lit/basic/extra-branch-values.wast +++ b/test/lit/basic/extra-branch-values.wast @@ -1931,23 +1931,24 @@ ) ;; CHECK: (func $loop (type $24) (param $0 i32) (param $1 anyref) - ;; CHECK-NEXT: (local $scratch (tuple i32 anyref)) - ;; CHECK-NEXT: (local $scratch_3 (tuple i32 anyref)) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local $scratch_3 anyref) ;; CHECK-NEXT: (local $scratch_4 anyref) ;; CHECK-NEXT: (local $scratch_5 i32) ;; CHECK-NEXT: (local $scratch_6 i32) ;; CHECK-NEXT: (local $scratch_7 (ref any)) ;; CHECK-NEXT: (local $scratch_8 i32) ;; CHECK-NEXT: (local $scratch_9 anyref) + ;; CHECK-NEXT: (local $scratch_10 (tuple i32 anyref)) ;; CHECK-NEXT: (local.set $scratch - ;; CHECK-NEXT: (tuple.make 2 - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_3 + ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (loop $label2 ;; CHECK-NEXT: (block $label3 - ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (local.set $scratch_10 ;; CHECK-NEXT: (block $label (type $2) (result i32 anyref) ;; CHECK-NEXT: (block $label1 ;; CHECK-NEXT: (local.set $scratch_9 @@ -1955,16 +1956,10 @@ ;; CHECK-NEXT: (local.set $scratch_5 ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (local.set $scratch_6 - ;; CHECK-NEXT: (tuple.extract 2 0 - ;; CHECK-NEXT: (local.tee $scratch_3 - ;; CHECK-NEXT: (local.get $scratch) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $scratch_4 - ;; CHECK-NEXT: (tuple.extract 2 1 - ;; CHECK-NEXT: (local.get $scratch_3) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_3) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.get $scratch_6) ;; CHECK-NEXT: ) @@ -1998,6 +1993,16 @@ ;; CHECK-NEXT: (br $label3) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (tuple.extract 2 0 + ;; CHECK-NEXT: (local.get $scratch_10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_3 + ;; CHECK-NEXT: (tuple.extract 2 1 + ;; CHECK-NEXT: (local.get $scratch_10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br $label2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -2033,22 +2038,23 @@ ) ;; CHECK: (func $loop-results (type $8) (param $0 i32) (param $1 anyref) (result i32 anyref) - ;; CHECK-NEXT: (local $scratch (tuple i32 anyref)) - ;; CHECK-NEXT: (local $scratch_3 (tuple i32 anyref)) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local $scratch_3 anyref) ;; CHECK-NEXT: (local $scratch_4 anyref) ;; CHECK-NEXT: (local $scratch_5 i32) ;; CHECK-NEXT: (local $scratch_6 i32) ;; CHECK-NEXT: (local $scratch_7 (ref any)) ;; CHECK-NEXT: (local $scratch_8 anyref) + ;; CHECK-NEXT: (local $scratch_9 (tuple i32 anyref)) ;; CHECK-NEXT: (local.set $scratch - ;; CHECK-NEXT: (tuple.make 2 - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_3 + ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (loop $label2 (type $2) (result i32 anyref) ;; CHECK-NEXT: (block $label3 (type $2) (result i32 anyref) - ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (local.set $scratch_9 ;; CHECK-NEXT: (block $label (type $2) (result i32 anyref) ;; CHECK-NEXT: (br $label3 ;; CHECK-NEXT: (block $label1 (type $2) (result i32 anyref) @@ -2057,16 +2063,10 @@ ;; CHECK-NEXT: (local.set $scratch_5 ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (local.set $scratch_6 - ;; CHECK-NEXT: (tuple.extract 2 0 - ;; CHECK-NEXT: (local.tee $scratch_3 - ;; CHECK-NEXT: (local.get $scratch) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $scratch_4 - ;; CHECK-NEXT: (tuple.extract 2 1 - ;; CHECK-NEXT: (local.get $scratch_3) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_3) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.get $scratch_6) ;; CHECK-NEXT: ) @@ -2094,53 +2094,56 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (tuple.extract 2 0 + ;; CHECK-NEXT: (local.get $scratch_9) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_3 + ;; CHECK-NEXT: (tuple.extract 2 1 + ;; CHECK-NEXT: (local.get $scratch_9) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br $label2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; OPT_O: (func $loop-results (type $8) (param $0 i32) (param $1 anyref) (result i32 anyref) ;; OPT_O-NEXT: (local $2 (tuple i32 anyref)) - ;; OPT_O-NEXT: (local $3 eqref) - ;; OPT_O-NEXT: (local.set $2 - ;; OPT_O-NEXT: (tuple.make 2 - ;; OPT_O-NEXT: (local.get $0) - ;; OPT_O-NEXT: (local.get $1) - ;; OPT_O-NEXT: ) - ;; OPT_O-NEXT: ) ;; OPT_O-NEXT: (loop $label2 (type $2) (result i32 (ref any)) ;; OPT_O-NEXT: (block $label3 (type $2) (result i32 (ref any)) - ;; OPT_O-NEXT: (local.set $2 - ;; OPT_O-NEXT: (block $label (type $0) (result i32 eqref) - ;; OPT_O-NEXT: (br $label3 - ;; OPT_O-NEXT: (block $label1 (type $2) (result i32 (ref any)) - ;; OPT_O-NEXT: (local.set $3 - ;; OPT_O-NEXT: (block $label0 (result eqref) - ;; OPT_O-NEXT: (br $label1 + ;; OPT_O-NEXT: (local.set $0 + ;; OPT_O-NEXT: (tuple.extract 2 0 + ;; OPT_O-NEXT: (local.tee $2 + ;; OPT_O-NEXT: (block $label (type $0) (result i32 eqref) + ;; OPT_O-NEXT: (br $label3 + ;; OPT_O-NEXT: (block $label1 (type $2) (result i32 (ref any)) + ;; OPT_O-NEXT: (br $label ;; OPT_O-NEXT: (tuple.make 2 - ;; OPT_O-NEXT: (local.tee $0 - ;; OPT_O-NEXT: (tuple.extract 2 0 - ;; OPT_O-NEXT: (local.get $2) - ;; OPT_O-NEXT: ) - ;; OPT_O-NEXT: ) - ;; OPT_O-NEXT: (br_on_cast $label0 anyref eqref - ;; OPT_O-NEXT: (tuple.extract 2 1 - ;; OPT_O-NEXT: (local.get $2) + ;; OPT_O-NEXT: (local.get $0) + ;; OPT_O-NEXT: (block $label0 (result eqref) + ;; OPT_O-NEXT: (br $label1 + ;; OPT_O-NEXT: (tuple.make 2 + ;; OPT_O-NEXT: (local.get $0) + ;; OPT_O-NEXT: (br_on_cast $label0 anyref eqref + ;; OPT_O-NEXT: (local.get $1) + ;; OPT_O-NEXT: ) + ;; OPT_O-NEXT: ) ;; OPT_O-NEXT: ) ;; OPT_O-NEXT: ) ;; OPT_O-NEXT: ) ;; OPT_O-NEXT: ) ;; OPT_O-NEXT: ) ;; OPT_O-NEXT: ) - ;; OPT_O-NEXT: (br $label - ;; OPT_O-NEXT: (tuple.make 2 - ;; OPT_O-NEXT: (local.get $0) - ;; OPT_O-NEXT: (local.get $3) - ;; OPT_O-NEXT: ) - ;; OPT_O-NEXT: ) ;; OPT_O-NEXT: ) ;; OPT_O-NEXT: ) ;; OPT_O-NEXT: ) ;; OPT_O-NEXT: ) + ;; OPT_O-NEXT: (local.set $1 + ;; OPT_O-NEXT: (tuple.extract 2 1 + ;; OPT_O-NEXT: (local.get $2) + ;; OPT_O-NEXT: ) + ;; OPT_O-NEXT: ) ;; OPT_O-NEXT: (br $label2) ;; OPT_O-NEXT: ) ;; OPT_O-NEXT: ) diff --git a/test/lit/control-flow-input.wast b/test/lit/control-flow-input.wast index 39cb28d9fa1..babf5293d47 100644 --- a/test/lit/control-flow-input.wast +++ b/test/lit/control-flow-input.wast @@ -1,4 +1,4 @@ -;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. ;; Check that control flow input is correctly parsed using scratch locals. The ;; binary input file is generated from this file using WABT's wat2wasm @@ -11,10 +11,22 @@ (module (type $id (func (param i32) (result i32))) + ;; CHECK: (type $0 (func (result i32 i64))) + + ;; CHECK: (type $1 (func (result i32))) + + ;; CHECK: (type $2 (func)) + + ;; CHECK: (type $3 (func (param i32))) + + ;; CHECK: (type $4 (func (result i64))) + + ;; CHECK: (type $5 (func (result f32))) + ;; CHECK: (tag $e (type $3) (param i32)) (tag $e (param i32)) - ;; CHECK: (func $block (type $0) (result i32) + ;; CHECK: (func $block (type $1) (result i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local.set $scratch ;; CHECK-NEXT: (i32.const 0) @@ -29,17 +41,21 @@ end ) - ;; CHECK: (func $block-multivalue (type $1) (result i32 i64) - ;; CHECK-NEXT: (local $scratch (tuple i32 i64)) + ;; CHECK: (func $block-multivalue (type $0) (result i32 i64) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local $scratch_1 i64) ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_1 + ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (type $0) (result i32 i64) ;; CHECK-NEXT: (tuple.make 2 - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: (local.get $scratch_1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block (type $1) (result i32 i64) - ;; CHECK-NEXT: (local.get $scratch) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $block-multivalue (result i32 i64) i32.const 0 @@ -48,6 +64,102 @@ end ) + ;; CHECK: (func $block-multivalue-tuple-input (type $0) (result i32 i64) + ;; CHECK-NEXT: (local $scratch (tuple i32 i64)) + ;; CHECK-NEXT: (local $scratch_1 i32) + ;; CHECK-NEXT: (local $scratch_2 i64) + ;; CHECK-NEXT: (local.set $scratch_1 + ;; CHECK-NEXT: (tuple.extract 2 0 + ;; CHECK-NEXT: (local.tee $scratch + ;; CHECK-NEXT: (block (type $0) (result i32 i64) + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_2 + ;; CHECK-NEXT: (tuple.extract 2 1 + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (type $0) (result i32 i64) + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (local.get $scratch_1) + ;; CHECK-NEXT: (local.get $scratch_2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $block-multivalue-tuple-input (result i32 i64) + block (result i32 i64) + i32.const 0 + i64.const 1 + end + block (param i32 i64) (result i32 i64) + end + ) + + ;; CHECK: (func $block-multivalue-nop-input (type $0) (result i32 i64) + ;; CHECK-NEXT: (local $scratch i64) + ;; CHECK-NEXT: (local $scratch_1 i32) + ;; CHECK-NEXT: (local $scratch_2 i64) + ;; CHECK-NEXT: (local.set $scratch_1 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_2 + ;; CHECK-NEXT: (block (result i64) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (type $0) (result i32 i64) + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (local.get $scratch_1) + ;; CHECK-NEXT: (local.get $scratch_2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $block-multivalue-nop-input (result i32 i64) + i32.const 0 + nop + i64.const 1 + nop + block (param i32 i64) (result i32 i64) + end + ) + + ;; CHECK: (func $block-multivalue-unreachable-input (type $0) (result i32 i64) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local $scratch_1 i64) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (f32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_1 + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (type $0) (result i32 i64) + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: (local.get $scratch_1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $block-multivalue-unreachable-input (result i32 i64) + f32.const 0 + unreachable + block (param i32 i64) (result i32 i64) + end + ) + ;; CHECK: (func $block-drop (type $2) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local.set $scratch @@ -67,29 +179,23 @@ ) ;; CHECK: (func $block-multivalue-drop (type $2) - ;; CHECK-NEXT: (local $scratch (tuple i32 i64)) - ;; CHECK-NEXT: (local $scratch_1 (tuple i32 i64)) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local $scratch_1 i64) ;; CHECK-NEXT: (local $scratch_2 i32) ;; CHECK-NEXT: (local.set $scratch - ;; CHECK-NEXT: (tuple.make 2 - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i64.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_1 + ;; CHECK-NEXT: (i64.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (local.set $scratch_2 - ;; CHECK-NEXT: (tuple.extract 2 0 - ;; CHECK-NEXT: (local.tee $scratch_1 - ;; CHECK-NEXT: (local.get $scratch) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (tuple.extract 2 1 - ;; CHECK-NEXT: (local.get $scratch_1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.get $scratch_2) ;; CHECK-NEXT: ) @@ -105,7 +211,7 @@ end ) - ;; CHECK: (func $block-passthrough-nop (type $0) (result i32) + ;; CHECK: (func $block-passthrough-nop (type $1) (result i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local $scratch_1 i32) ;; CHECK-NEXT: (local.set $scratch @@ -126,7 +232,7 @@ end ) - ;; CHECK: (func $block-passthrough-type (type $0) (result i32) + ;; CHECK: (func $block-passthrough-type (type $1) (result i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local.set $scratch ;; CHECK-NEXT: (i32.const 0) @@ -141,7 +247,7 @@ end ) - ;; CHECK: (func $loop (type $0) (result i32) + ;; CHECK: (func $loop (type $1) (result i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local.set $scratch ;; CHECK-NEXT: (i32.const 0) @@ -182,7 +288,7 @@ end ) - ;; CHECK: (func $loop-branch-cond (type $0) (result i32) + ;; CHECK: (func $loop-branch-cond (type $1) (result i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local.set $scratch ;; CHECK-NEXT: (i32.const 0) @@ -278,40 +384,70 @@ end ) - ;; CHECK: (func $nested-loops-multivalue (type $1) (result i32 i64) - ;; CHECK-NEXT: (local $scratch (tuple i32 i64)) - ;; CHECK-NEXT: (local $scratch_1 (tuple i32 i64)) - ;; CHECK-NEXT: (block $label2 (type $1) (result i32 i64) + ;; CHECK: (func $nested-loops-multivalue (type $0) (result i32 i64) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local $scratch_1 i64) + ;; CHECK-NEXT: (local $scratch_2 i32) + ;; CHECK-NEXT: (local $scratch_3 i64) + ;; CHECK-NEXT: (local $scratch_4 (tuple i32 i64)) + ;; CHECK-NEXT: (local $scratch_5 (tuple i32 i64)) + ;; CHECK-NEXT: (block $label2 (type $0) (result i32 i64) ;; CHECK-NEXT: (local.set $scratch - ;; CHECK-NEXT: (tuple.make 2 - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i64.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_1 + ;; CHECK-NEXT: (i64.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (loop $label10 ;; CHECK-NEXT: (block $label11 - ;; CHECK-NEXT: (local.set $scratch - ;; CHECK-NEXT: (block $label1 (type $1) (result i32 i64) - ;; CHECK-NEXT: (local.set $scratch_1 + ;; CHECK-NEXT: (local.set $scratch_5 + ;; CHECK-NEXT: (block $label1 (type $0) (result i32 i64) + ;; CHECK-NEXT: (local.set $scratch_2 ;; CHECK-NEXT: (local.get $scratch) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_3 + ;; CHECK-NEXT: (local.get $scratch_1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (loop $label0 ;; CHECK-NEXT: (block $label3 - ;; CHECK-NEXT: (local.set $scratch_1 - ;; CHECK-NEXT: (block $label (type $1) (result i32 i64) + ;; CHECK-NEXT: (local.set $scratch_4 + ;; CHECK-NEXT: (block $label (type $0) (result i32 i64) ;; CHECK-NEXT: (br_table $label $label1 $label2 - ;; CHECK-NEXT: (local.get $scratch_1) + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (local.get $scratch_2) + ;; CHECK-NEXT: (local.get $scratch_3) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br $label3) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_2 + ;; CHECK-NEXT: (tuple.extract 2 0 + ;; CHECK-NEXT: (local.get $scratch_4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_3 + ;; CHECK-NEXT: (tuple.extract 2 1 + ;; CHECK-NEXT: (local.get $scratch_4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br $label0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br $label11) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (tuple.extract 2 0 + ;; CHECK-NEXT: (local.get $scratch_5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_1 + ;; CHECK-NEXT: (tuple.extract 2 1 + ;; CHECK-NEXT: (local.get $scratch_5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br $label10) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -330,7 +466,7 @@ unreachable ) - ;; CHECK: (func $if (type $0) (result i32) + ;; CHECK: (func $if (type $1) (result i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local.set $scratch ;; CHECK-NEXT: (i32.const 0) @@ -352,7 +488,7 @@ end ) - ;; CHECK: (func $if-new-val (type $0) (result i32) + ;; CHECK: (func $if-new-val (type $1) (result i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local.set $scratch ;; CHECK-NEXT: (i32.const 0) @@ -379,21 +515,28 @@ end ) - ;; CHECK: (func $if-multivalue (type $1) (result i32 i64) - ;; CHECK-NEXT: (local $scratch (tuple i32 i64)) + ;; CHECK: (func $if-multivalue (type $0) (result i32 i64) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local $scratch_1 i64) ;; CHECK-NEXT: (local.set $scratch - ;; CHECK-NEXT: (tuple.make 2 - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i64.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_1 + ;; CHECK-NEXT: (i64.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if (type $1) (result i32 i64) + ;; CHECK-NEXT: (if (type $0) (result i32 i64) ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: (local.get $scratch_1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: (local.get $scratch_1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -405,7 +548,7 @@ end ) - ;; CHECK: (func $if-else (type $0) (result i32) + ;; CHECK: (func $if-else (type $1) (result i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local.set $scratch ;; CHECK-NEXT: (i32.const 0) @@ -458,16 +601,15 @@ ) ;; CHECK: (func $if-else-multivalue (type $5) (result f32) - ;; CHECK-NEXT: (local $scratch (tuple i32 i64)) - ;; CHECK-NEXT: (local $scratch_1 (tuple i32 i64)) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local $scratch_1 i64) ;; CHECK-NEXT: (local $scratch_2 i32) - ;; CHECK-NEXT: (local $scratch_3 (tuple i32 i64)) - ;; CHECK-NEXT: (local $scratch_4 i32) + ;; CHECK-NEXT: (local $scratch_3 i32) ;; CHECK-NEXT: (local.set $scratch - ;; CHECK-NEXT: (tuple.make 2 - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i64.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_1 + ;; CHECK-NEXT: (i64.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if (result f32) ;; CHECK-NEXT: (i32.const 2) @@ -475,16 +617,10 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (local.set $scratch_2 - ;; CHECK-NEXT: (tuple.extract 2 0 - ;; CHECK-NEXT: (local.tee $scratch_1 - ;; CHECK-NEXT: (local.get $scratch) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (tuple.extract 2 1 - ;; CHECK-NEXT: (local.get $scratch_1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.get $scratch_2) ;; CHECK-NEXT: ) @@ -494,19 +630,13 @@ ;; CHECK-NEXT: (else ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (local.set $scratch_4 - ;; CHECK-NEXT: (tuple.extract 2 0 - ;; CHECK-NEXT: (local.tee $scratch_3 - ;; CHECK-NEXT: (local.get $scratch) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $scratch_3 + ;; CHECK-NEXT: (local.get $scratch) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (tuple.extract 2 1 - ;; CHECK-NEXT: (local.get $scratch_3) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.get $scratch_4) + ;; CHECK-NEXT: (local.get $scratch_3) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (f32.const 4) @@ -528,7 +658,7 @@ end ) - ;; CHECK: (func $try (type $0) (result i32) + ;; CHECK: (func $try (type $1) (result i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local.set $scratch ;; CHECK-NEXT: (i32.const 0) @@ -545,7 +675,7 @@ end ) - ;; CHECK: (func $try-catch (type $0) (result i32) + ;; CHECK: (func $try-catch (type $1) (result i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local.set $scratch ;; CHECK-NEXT: (i32.const 0) @@ -566,7 +696,7 @@ end ) - ;; CHECK: (func $try-catch-all (type $0) (result i32) + ;; CHECK: (func $try-catch-all (type $1) (result i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local.set $scratch ;; CHECK-NEXT: (i32.const 0) @@ -588,7 +718,7 @@ end ) - ;; CHECK: (func $try-catch-delegate (type $0) (result i32) + ;; CHECK: (func $try-catch-delegate (type $1) (result i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local.set $scratch ;; CHECK-NEXT: (i32.const 0) @@ -606,7 +736,7 @@ delegate 0 ) - ;; CHECK: (func $try-table (type $0) (result i32) + ;; CHECK: (func $try-table (type $1) (result i32) ;; CHECK-NEXT: (local $scratch i32) ;; CHECK-NEXT: (local.set $scratch ;; CHECK-NEXT: (i32.const 0) diff --git a/test/lit/control-flow-input.wast.wasm b/test/lit/control-flow-input.wast.wasm index 664d1be843a..9c8408ca0a7 100644 Binary files a/test/lit/control-flow-input.wast.wasm and b/test/lit/control-flow-input.wast.wasm differ