From ad26e65ab9c2c19e5dcdab5a87133fbd94050c61 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Sat, 31 Oct 2026 22:49:31 -0700 Subject: [PATCH 1/3] [IRBuilder] Use multiple scratch locals for inputs Instead of using a single tuple-typed scratch local for multivalue control flow inputs, allocate one scalar scratch local per input value. This avoids eagerly packaging scalar inputs into a tuple on scope entry. It also avoids allocating extra tuple scratch locals to extract elements when inputs are consumed inside the scope. Scalar input locals do have some downsides. Scopes that pass all of their inputs through untouched must now repackage them with tuple.make. Multivalue loop trampolines in fixLoopWithInput also still need a tuple scratch local to unpack branch values into the scalar input locals. However, real parameterized scopes usually consume their inputs as individual values. Scalar input locals can also be coalesced directly by standard local passes without tuple optimization, and they will allow individual parameters to be tunneled across nested scopes with different arities in a future commit. --- src/wasm-ir-builder.h | 49 +--- src/wasm/wasm-ir-builder.cpp | 99 ++++++-- test/lit/basic/extra-branch-values.wast | 119 +++++----- test/lit/control-flow-input.wast | 298 +++++++++++++++++------- test/lit/control-flow-input.wast.wasm | Bin 793 -> 933 bytes 5 files changed, 368 insertions(+), 197 deletions(-) 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..3535561db2f 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -778,6 +778,16 @@ struct IRBuilder::ChildPopper ConstraintCollector{builder, children}.visitStackSwitch(curr, ct); return popConstrainedChildren(children); } + + Result<> popInputs(Type inputType, std::vector& inputs) { + inputs.resize(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]}}); + } + return popConstrainedChildren(children); + } }; Result<> IRBuilder::visit(Expression* curr) { @@ -877,6 +887,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()) { + std::vector inputs; + CHECK_ERR(ChildPopper{*this}.popInputs(scope.inputType, 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 +1258,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 +1268,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 +1425,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 +1446,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 +1459,21 @@ 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); + 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 664d1be843a4e81a76bd4dc15f445160e49a08c4..9c8408ca0a76dce8aec39dc79dd8cb9f930d3d11 100644 GIT binary patch literal 933 zcmZWo+mf0<5bd4;R0dqv^*Tvas`5UM`!3{1M4Vl%AW#BH?ZdA71NoSILjEk>sL`xc zF@4VI(>)gkNU9wI06wZE9`HZ|EP*13!`qTx4gic;e`6B~_{s{t%1>HIdR$is;R?4S zg^fU1NmKsg#k9D8W5r zjD$n$;%)qALG4m^eYW^o(C6VN3;>%kq0wq!55$;{BoQ; z*;)J}HMpy+NWbHt4QAJ5_4Y@SHCBB7sF=DbGi$ba+0;_sWb>jt=A5rLxhCJ+B;6#d zbG_>yJ?ni@dgB{E%bk;4CRJ5$_C@o&F*HYaa&%wI^-F1cPF579uXr@8eUhgeHMk-s zE%J3R{-_w9DxBTOx&3MA9QxDTR<&IlHdI%~b0@Y>1AqHuPR9F?@a6P|9ild?>_cz@ z?L?#DnO&@7S?^yaJsM1!)ahm#{(~mTvf1!L*EX}yNp0t?yXNB*kDC52y8aRzaNDon mg&z2zAb=41a0dgphao(`2*xmhDP4s*I4;#}*UF(^$o>V?j>!`M delta 397 zcmZ9IK~jS-6o%o26d_P6N@4U2QIDYBBtTjiQ^G*d>8clSp~I#wyKvz#Jd5{mOj{}) zH!uJ9T!%fs>=-Rii MX+V(?OP`tl0%ZAh@&Et; From 26fcd22e29723211caf9af7ede1a1f687a0b6b4a Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 2 Nov 2026 15:31:45 -0800 Subject: [PATCH 2/3] simplify popInputs, add assertion --- src/support/result.h | 4 ++++ src/wasm/wasm-ir-builder.cpp | 24 +++++++++++++----------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/support/result.h b/src/support/result.h index f34f6300759..615e88ea3df 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,9 @@ struct Err { return (typename decltype(_val)::ErrorType)(*err); \ } +// Assert that the result is not an error. +#define ASSERT_OK(val) assert(!(val).getErr()); + // Represent a result of type T or an error message. template struct [[nodiscard]] Result { using ErrorType = E; diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 3535561db2f..2ca99b9fca9 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -779,14 +779,15 @@ struct IRBuilder::ChildPopper return popConstrainedChildren(children); } - Result<> popInputs(Type inputType, std::vector& inputs) { - inputs.resize(inputType.size()); + 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]}}); } - return popConstrainedChildren(children); + CHECK_ERR(popConstrainedChildren(children)); + return inputs; } }; @@ -902,14 +903,14 @@ Result<> IRBuilder::pushScope(ScopeCtx&& scope) { } bool hasInput = scope.inputType != Type::none; if (hasInput && !scope.isDelimiter()) { - std::vector inputs; - CHECK_ERR(ChildPopper{*this}.popInputs(scope.inputType, inputs)); - scope.inputLocals.reserve(inputs.size()); - for (size_t i = 0; i < inputs.size(); ++i) { + 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])); + pushSynthetic(builder.makeLocalSet(*scratch, (*inputs)[i])); } } scopeStack.emplace_back(std::move(scope)); @@ -1463,12 +1464,13 @@ void IRBuilder::fixLoopWithInput(Loop* loop, if (inputType.isSingle()) { list.push_back(builder.makeLocalSet(scratch[0], inner)); } else { - auto tupleScratch = *addScratchLocal(inputType); - list.push_back(builder.makeLocalSet(tupleScratch, inner)); + 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), + builder.makeTupleExtract(builder.makeLocalGet(*tupleScratch, inputType), i))); } } From d79a69e77a9968a7bfb09233257038176663bb76 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 2 Nov 2026 16:33:14 -0800 Subject: [PATCH 3/3] Fix ASSERT_OK redefinition and NDEBUG evaluation --- src/passes/Outlining.cpp | 6 ------ src/support/result.h | 8 ++++++-- 2 files changed, 6 insertions(+), 8 deletions(-) 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 615e88ea3df..490facfd28c 100644 --- a/src/support/result.h +++ b/src/support/result.h @@ -40,8 +40,12 @@ struct Err { return (typename decltype(_val)::ErrorType)(*err); \ } -// Assert that the result is not an error. -#define ASSERT_OK(val) assert(!(val).getErr()); +// 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 {