diff --git a/CHANGELOG.md b/CHANGELOG.md index 324c29526f3..52e453ee48f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Current Trunk - BinaryenSelect no longer takes a type parameter. - AutoDrop APIs have been removed. + - Binaryen now supports parsing control flow structures with parameter types. v120 ---- diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 807b6c0030e..3e0bc7c4094 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -1447,11 +1447,7 @@ struct ParseDefsCtx : TypeParserCtx { Result getBlockTypeFromTypeUse(Index pos, HeapType type) { assert(type.isSignature()); - if (type.getSignature().params != Type::none) { - return in.err(pos, "block parameters not yet supported"); - } - // TODO: Once we support block parameters, return an error here if any of - // them are named. + // TODO: Error if block parameters are named return type; } @@ -1822,9 +1818,11 @@ struct ParseDefsCtx : TypeParserCtx { HeapType type) { // TODO: validate labels? // TODO: Move error on input types to here? - return withLoc(pos, - irBuilder.makeBlock(label ? *label : Name{}, - type.getSignature().results)); + if (!type.isSignature()) { + return in.err(pos, "expected function type"); + } + return withLoc( + pos, irBuilder.makeBlock(label ? *label : Name{}, type.getSignature())); } Result<> makeIf(Index pos, @@ -1832,10 +1830,11 @@ struct ParseDefsCtx : TypeParserCtx { std::optional label, HeapType type) { // TODO: validate labels? - // TODO: Move error on input types to here? + if (!type.isSignature()) { + return in.err(pos, "expected function type"); + } return withLoc( - pos, - irBuilder.makeIf(label ? *label : Name{}, type.getSignature().results)); + pos, irBuilder.makeIf(label ? *label : Name{}, type.getSignature())); } Result<> visitElse() { return withLoc(irBuilder.visitElse()); } @@ -1845,10 +1844,11 @@ struct ParseDefsCtx : TypeParserCtx { std::optional label, HeapType type) { // TODO: validate labels? - // TODO: Move error on input types to here? + if (!type.isSignature()) { + return in.err(pos, "expected function type"); + } return withLoc( - pos, - irBuilder.makeLoop(label ? *label : Name{}, type.getSignature().results)); + pos, irBuilder.makeLoop(label ? *label : Name{}, type.getSignature())); } Result<> makeTry(Index pos, @@ -1856,10 +1856,11 @@ struct ParseDefsCtx : TypeParserCtx { std::optional label, HeapType type) { // TODO: validate labels? - // TODO: Move error on input types to here? + if (!type.isSignature()) { + return in.err(pos, "expected function type"); + } return withLoc( - pos, - irBuilder.makeTry(label ? *label : Name{}, type.getSignature().results)); + pos, irBuilder.makeTry(label ? *label : Name{}, type.getSignature())); } Result<> makeTryTable(Index pos, @@ -1875,12 +1876,10 @@ struct ParseDefsCtx : TypeParserCtx { labels.push_back(info.label); isRefs.push_back(info.isRef); } - return withLoc(pos, - irBuilder.makeTryTable(label ? *label : Name{}, - type.getSignature().results, - tags, - labels, - isRefs)); + return withLoc( + pos, + irBuilder.makeTryTable( + label ? *label : Name{}, type.getSignature(), tags, labels, isRefs)); } Result<> visitCatch(Index pos, Name tag) { diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 163a62b1e41..61f4faf69e2 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1467,10 +1467,12 @@ class WasmBinaryReader { bool getBasicType(int32_t code, Type& out); bool getBasicHeapType(int64_t code, HeapType& out); + // Get the signature of control flow structure. + Signature getBlockType(); // Read a value and get a type for it. Type getType(); // Get a type given the initial S32LEB has already been read, and is provided. - Type getType(int initial); + Type getType(int code); HeapType getHeapType(); HeapType getIndexedHeapType(); diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index 84ac2697930..250d5d17c18 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -80,15 +80,18 @@ class IRBuilder : public UnifiedExpressionVisitor> { // the corresponding `makeXYZ` function below instead of `visitXYZStart`, but // either way must call `visitEnd` and friends at the appropriate times. Result<> visitFunctionStart(Function* func); - Result<> visitBlockStart(Block* block); - Result<> visitIfStart(If* iff, Name label = {}); + Result<> visitBlockStart(Block* block, Type inputType = Type::none); + Result<> visitIfStart(If* iff, Name label = {}, Type inputType = Type::none); Result<> visitElse(); - Result<> visitLoopStart(Loop* iff); - Result<> visitTryStart(Try* tryy, Name label = {}); + Result<> visitLoopStart(Loop* iff, Type inputType = Type::none); + Result<> + visitTryStart(Try* tryy, Name label = {}, Type inputType = Type::none); Result<> visitCatch(Name tag); Result<> visitCatchAll(); Result<> visitDelegate(Index label); - Result<> visitTryTableStart(TryTable* trytable, Name label = {}); + Result<> visitTryTableStart(TryTable* trytable, + Name label = {}, + Type inputType = Type::none); Result<> visitEnd(); // Used to visit break nodes when traversing a single block without its @@ -113,9 +116,9 @@ class IRBuilder : public UnifiedExpressionVisitor> { // nodes. This is generally safer than calling `visit` because the function // signatures ensure that there are no missing fields. Result<> makeNop(); - Result<> makeBlock(Name label, Type type); - Result<> makeIf(Name label, Type type); - Result<> makeLoop(Name label, Type type); + Result<> makeBlock(Name label, Signature sig); + Result<> makeIf(Name label, Signature sig); + Result<> makeLoop(Name label, Signature sig); Result<> makeBreak(Index label, bool isConditional); Result<> makeSwitch(const std::vector& labels, Index defaultLabel); // Unlike Builder::makeCall, this assumes the function already exists. @@ -180,9 +183,9 @@ class IRBuilder : public UnifiedExpressionVisitor> { Result<> makeTableFill(Name table); Result<> makeTableCopy(Name destTable, Name srcTable); Result<> makeTableInit(Name elem, Name table); - Result<> makeTry(Name label, Type type); + Result<> makeTry(Name label, Signature sig); Result<> makeTryTable(Name label, - Type type, + Signature sig, const std::vector& tags, const std::vector& labels, const std::vector& isRefs); @@ -323,13 +326,21 @@ class IRBuilder : public UnifiedExpressionVisitor> { // The branch label name for this scope. Always fresh, never shadowed. Name label; + // For Try/Catch/CatchAll scopes, we need to separately track a label used // for branches, since the normal label is only used for delegates. Name branchLabel; bool labelUsed = 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. + Type inputType; + Index inputLocal = -1; + + // The stack of instructions being built in this scope. std::vector exprStack; + // Whether we have seen an unreachable instruction and are in // stack-polymorphic unreachable mode. bool unreachable = false; @@ -338,29 +349,39 @@ class IRBuilder : public UnifiedExpressionVisitor> { size_t startPos = 0; ScopeCtx() : scope(NoScope{}) {} - ScopeCtx(Scope scope) : scope(scope) {} - ScopeCtx(Scope scope, Name label, bool labelUsed) - : scope(scope), label(label), labelUsed(labelUsed) {} + 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}); + return ScopeCtx(FuncScope{func}, Type::none); } - static ScopeCtx makeBlock(Block* block) { - return ScopeCtx(BlockScope{block}); + static ScopeCtx makeBlock(Block* block, Type inputType) { + return ScopeCtx(BlockScope{block}, inputType); } - static ScopeCtx makeIf(If* iff, Name originalLabel = {}) { - return ScopeCtx(IfScope{iff, originalLabel}); + static ScopeCtx makeIf(If* iff, Name originalLabel, Type inputType) { + return ScopeCtx(IfScope{iff, originalLabel}, inputType); } - static ScopeCtx - makeElse(If* iff, Name originalLabel, Name label, bool labelUsed) { - return ScopeCtx(ElseScope{iff, originalLabel}, label, labelUsed); + static ScopeCtx makeElse(If* iff, + Name originalLabel, + Name label, + bool labelUsed, + Type inputType, + Index inputLocal) { + return ScopeCtx( + ElseScope{iff, originalLabel}, label, labelUsed, inputType, inputLocal); } - static ScopeCtx makeLoop(Loop* loop) { return ScopeCtx(LoopScope{loop}); } - static ScopeCtx makeTry(Try* tryy, Name originalLabel = {}) { - return ScopeCtx(TryScope{tryy, originalLabel}); + static ScopeCtx makeLoop(Loop* loop, Type inputType) { + return ScopeCtx(LoopScope{loop}, inputType); + } + static ScopeCtx makeTry(Try* tryy, Name originalLabel, Type inputType) { + return ScopeCtx(TryScope{tryy, originalLabel}, inputType); } static ScopeCtx makeCatch(Try* tryy, Name originalLabel, @@ -378,8 +399,9 @@ class IRBuilder : public UnifiedExpressionVisitor> { return ScopeCtx( CatchAllScope{tryy, originalLabel}, label, labelUsed, branchLabel); } - static ScopeCtx makeTryTable(TryTable* trytable, Name originalLabel = {}) { - return ScopeCtx(TryTableScope{trytable, originalLabel}); + static ScopeCtx + makeTryTable(TryTable* trytable, Name originalLabel, Type inputType) { + return ScopeCtx(TryTableScope{trytable, originalLabel}, inputType); } bool isNone() { return std::get_if(&scope); } @@ -518,6 +540,7 @@ class IRBuilder : public UnifiedExpressionVisitor> { } WASM_UNREACHABLE("unexpected scope kind"); } + bool isDelimiter() { return getElse() || getCatch() || getCatchAll(); } }; // The stack of block contexts currently being parsed. @@ -541,7 +564,7 @@ class IRBuilder : public UnifiedExpressionVisitor> { Index blockHint = 0; Index labelHint = 0; - void pushScope(ScopeCtx scope) { + Result<> pushScope(ScopeCtx&& scope) { if (auto label = scope.getOriginalLabel()) { // Assign a fresh label to the scope, if necessary. if (!scope.label) { @@ -554,7 +577,21 @@ class IRBuilder : public UnifiedExpressionVisitor> { scope.startPos = lastBinaryPos; lastBinaryPos = *binaryPos; } - scopeStack.push_back(scope); + 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{}; } ScopeCtx& getScope() { @@ -610,6 +647,8 @@ class IRBuilder : public UnifiedExpressionVisitor> { Result getLabelType(Index label); Result getLabelType(Name labelName); + void fixLoopWithInput(Loop* loop, Type inputType, Index scratch); + void dump(); }; diff --git a/src/wasm-type.h b/src/wasm-type.h index b48a1071328..1e7c0a0ba15 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -870,6 +870,33 @@ std::ostream& operator<<(std::ostream&, Struct); std::ostream& operator<<(std::ostream&, Array); std::ostream& operator<<(std::ostream&, TypeBuilder::ErrorReason); +// Inline some nontrivial methods here for performance reasons. + +inline bool HeapType::isBottom() const { + if (isBasic()) { + switch (getBasic(Unshared)) { + case ext: + case func: + case cont: + case any: + case eq: + case i31: + case struct_: + case array: + case exn: + case string: + return false; + case none: + case noext: + case nofunc: + case nocont: + case noexn: + return true; + } + } + return false; +} + } // namespace wasm namespace std { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 0c931f51864..791dc53d777 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2121,30 +2121,30 @@ bool WasmBinaryReader::getBasicHeapType(int64_t code, HeapType& out) { } } -Type WasmBinaryReader::getType(int initial) { - // Single value types are negative; signature indices are non-negative - if (initial >= 0) { - // TODO: Handle block input types properly. - auto sig = getSignatureByTypeIndex(initial); - if (sig.params != Type::none) { - throwError("control flow inputs are not supported yet"); - } - return sig.results; +Signature WasmBinaryReader::getBlockType() { + // Single value types are negative; signature indices are non-negative. + auto code = getS32LEB(); + if (code >= 0) { + return getSignatureByTypeIndex(code); + } + if (code == BinaryConsts::EncodedType::Empty) { + return Signature(); } + return Signature(Type::none, getType(code)); +} + +Type WasmBinaryReader::getType(int code) { Type type; - if (getBasicType(initial, type)) { + if (getBasicType(code, type)) { return type; } - switch (initial) { - // None only used for block signatures. TODO: Separate out? - case BinaryConsts::EncodedType::Empty: - return Type::none; + switch (code) { case BinaryConsts::EncodedType::nullable: return Type(getHeapType(), Nullable); case BinaryConsts::EncodedType::nonnullable: return Type(getHeapType(), NonNullable); default: - throwError("invalid wasm type: " + std::to_string(initial)); + throwError("invalid wasm type: " + std::to_string(code)); } WASM_UNREACHABLE("unexpected type"); } @@ -2885,11 +2885,11 @@ Result<> WasmBinaryReader::readInst() { uint8_t code = getInt8(); switch (code) { case BinaryConsts::Block: - return builder.makeBlock(Name(), getType()); + return builder.makeBlock(Name(), getBlockType()); case BinaryConsts::If: - return builder.makeIf(Name(), getType()); + return builder.makeIf(Name(), getBlockType()); case BinaryConsts::Loop: - return builder.makeLoop(Name(), getType()); + return builder.makeLoop(Name(), getBlockType()); case BinaryConsts::Br: return builder.makeBreak(getU32LEB(), false); case BinaryConsts::BrIf: @@ -2974,9 +2974,9 @@ Result<> WasmBinaryReader::readInst() { case BinaryConsts::TableSet: return builder.makeTableSet(getTableName(getU32LEB())); case BinaryConsts::Try: - return builder.makeTry(Name(), getType()); + return builder.makeTry(Name(), getBlockType()); case BinaryConsts::TryTable: { - auto type = getType(); + auto type = getBlockType(); std::vector tags; std::vector labels; std::vector isRefs; diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 96212ccd7fc..6cd62e43991 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -679,9 +679,8 @@ Result<> IRBuilder::visitExpression(Expression* curr) { Result IRBuilder::getLabelType(Index label) { auto scope = getScope(label); CHECK_ERR(scope); - // Loops would receive their input type rather than their output type, if we - // supported that. - return (*scope)->getLoop() ? Type::none : (*scope)->getResultType(); + // Loops receive their input type rather than their output type. + return (*scope)->getLoop() ? (*scope)->inputType : (*scope)->getResultType(); } Result IRBuilder::getLabelType(Name labelName) { @@ -722,35 +721,31 @@ Result<> IRBuilder::visitFunctionStart(Function* func) { return Ok{}; } -Result<> IRBuilder::visitBlockStart(Block* curr) { +Result<> IRBuilder::visitBlockStart(Block* curr, Type inputType) { applyDebugLoc(curr); - pushScope(ScopeCtx::makeBlock(curr)); - return Ok{}; + return pushScope(ScopeCtx::makeBlock(curr, inputType)); } -Result<> IRBuilder::visitIfStart(If* iff, Name label) { +Result<> IRBuilder::visitIfStart(If* iff, Name label, Type inputType) { applyDebugLoc(iff); CHECK_ERR(visitIf(iff)); - pushScope(ScopeCtx::makeIf(iff, label)); - return Ok{}; + return pushScope(ScopeCtx::makeIf(iff, label, inputType)); } -Result<> IRBuilder::visitLoopStart(Loop* loop) { +Result<> IRBuilder::visitLoopStart(Loop* loop, Type inputType) { applyDebugLoc(loop); - pushScope(ScopeCtx::makeLoop(loop)); - return Ok{}; + return pushScope(ScopeCtx::makeLoop(loop, inputType)); } -Result<> IRBuilder::visitTryStart(Try* tryy, Name label) { +Result<> IRBuilder::visitTryStart(Try* tryy, Name label, Type inputType) { applyDebugLoc(tryy); - pushScope(ScopeCtx::makeTry(tryy, label)); - return Ok{}; + return pushScope(ScopeCtx::makeTry(tryy, label, inputType)); } -Result<> IRBuilder::visitTryTableStart(TryTable* trytable, Name label) { +Result<> +IRBuilder::visitTryTableStart(TryTable* trytable, Name label, Type inputType) { applyDebugLoc(trytable); - pushScope(ScopeCtx::makeTryTable(trytable, label)); - return Ok{}; + return pushScope(ScopeCtx::makeTryTable(trytable, label, inputType)); } Result IRBuilder::finishScope(Block* block) { @@ -849,6 +844,8 @@ Result<> IRBuilder::visitElse() { auto originalLabel = scope.getOriginalLabel(); auto label = scope.label; auto labelUsed = scope.labelUsed; + auto inputType = scope.inputType; + auto inputLocal = scope.inputLocal; auto expr = finishScope(); CHECK_ERR(expr); iff->ifTrue = *expr; @@ -858,8 +855,8 @@ Result<> IRBuilder::visitElse() { lastBinaryPos - codeSectionOffset; } - pushScope(ScopeCtx::makeElse(iff, originalLabel, label, labelUsed)); - return Ok{}; + return pushScope(ScopeCtx::makeElse( + iff, originalLabel, label, labelUsed, inputType, inputLocal)); } Result<> IRBuilder::visitCatch(Name tag) { @@ -891,8 +888,8 @@ Result<> IRBuilder::visitCatch(Name tag) { delimiterLocs[delimiterLocs.size()] = lastBinaryPos - codeSectionOffset; } - pushScope( - ScopeCtx::makeCatch(tryy, originalLabel, label, labelUsed, branchLabel)); + CHECK_ERR(pushScope( + ScopeCtx::makeCatch(tryy, originalLabel, label, labelUsed, branchLabel))); // Push a pop for the exception payload if necessary. auto params = wasm.getTag(tag)->sig.params; if (params != Type::none) { @@ -933,9 +930,8 @@ Result<> IRBuilder::visitCatchAll() { delimiterLocs[delimiterLocs.size()] = lastBinaryPos - codeSectionOffset; } - pushScope( + return pushScope( ScopeCtx::makeCatchAll(tryy, originalLabel, label, labelUsed, branchLabel)); - return Ok{}; } Result<> IRBuilder::visitDelegate(Index label) { @@ -1035,11 +1031,24 @@ Result<> IRBuilder::visitEnd() { } else if (auto* loop = scope.getLoop()) { loop->body = *expr; loop->name = scope.label; + 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); + } loop->finalize(loop->type); push(loop); } else if (auto* iff = scope.getIf()) { iff->ifTrue = *expr; - iff->ifFalse = nullptr; + 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); + } else { + iff->ifFalse = nullptr; + } iff->finalize(iff->type); push(maybeWrapForLabel(iff)); } else if (auto* iff = scope.getElse()) { @@ -1067,6 +1076,46 @@ Result<> IRBuilder::visitEnd() { return Ok{}; } +// 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: +// +// (loop $l (param t1) (result t2) ...) +// +// to this: +// +// (loop $l0 (result t2) +// (block $l1 (result t2) +// (local.set $scratch ;; set the branch values to the scratch local +// (block $l (result t1) +// (br $l1 ;; exit the loop with the fallthrough value, if any. +// ... ;; contains branches to $l +// ) +// ) +// ) +// (br $l0) ;; continue the loop +// ) +// ) +void IRBuilder::fixLoopWithInput(Loop* loop, Type inputType, Index scratch) { + auto l = loop->name; + auto l0 = makeFresh(l, 0); + auto l1 = makeFresh(l, 1); + + Block* inner = + loop->type == Type::none + ? builder.blockifyWithName( + 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); + + loop->body = outer; + loop->name = l0; +} + Result IRBuilder::getLabelIndex(Name label, bool inDelegate) { auto it = labelDepths.find(label); if (it == labelDepths.end() || it->second.empty()) { @@ -1128,24 +1177,24 @@ Result<> IRBuilder::makeNop() { return Ok{}; } -Result<> IRBuilder::makeBlock(Name label, Type type) { +Result<> IRBuilder::makeBlock(Name label, Signature sig) { auto* block = wasm.allocator.alloc(); block->name = label; - block->type = type; - return visitBlockStart(block); + block->type = sig.results; + return visitBlockStart(block, sig.params); } -Result<> IRBuilder::makeIf(Name label, Type type) { +Result<> IRBuilder::makeIf(Name label, Signature sig) { auto* iff = wasm.allocator.alloc(); - iff->type = type; - return visitIfStart(iff, label); + iff->type = sig.results; + return visitIfStart(iff, label, sig.params); } -Result<> IRBuilder::makeLoop(Name label, Type type) { +Result<> IRBuilder::makeLoop(Name label, Signature sig) { auto* loop = wasm.allocator.alloc(); loop->name = label; - loop->type = type; - return visitLoopStart(loop); + loop->type = sig.results; + return visitLoopStart(loop, sig.params); } Result<> IRBuilder::makeBreak(Index label, bool isConditional) { @@ -1584,19 +1633,19 @@ Result<> IRBuilder::makeTableInit(Name elem, Name table) { return Ok{}; } -Result<> IRBuilder::makeTry(Name label, Type type) { +Result<> IRBuilder::makeTry(Name label, Signature sig) { auto* tryy = wasm.allocator.alloc(); - tryy->type = type; - return visitTryStart(tryy, label); + tryy->type = sig.results; + return visitTryStart(tryy, label, sig.params); } Result<> IRBuilder::makeTryTable(Name label, - Type type, + Signature sig, const std::vector& tags, const std::vector& labels, const std::vector& isRefs) { auto* trytable = wasm.allocator.alloc(); - trytable->type = type; + trytable->type = sig.results; trytable->catchTags.set(tags); trytable->catchRefs.set(isRefs); trytable->catchDests.reserve(labels.size()); @@ -1605,7 +1654,7 @@ Result<> IRBuilder::makeTryTable(Name label, CHECK_ERR(name); trytable->catchDests.push_back(*name); } - return visitTryTableStart(trytable, label); + return visitTryTableStart(trytable, label, sig.params); } Result<> IRBuilder::makeThrow(Name tag) { diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index a6ed6877097..4f341588b6a 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -866,31 +866,6 @@ HeapTypeKind HeapType::getKind() const { return getHeapTypeInfo(*this)->kind; } -bool HeapType::isBottom() const { - if (isBasic()) { - switch (getBasic(Unshared)) { - case ext: - case func: - case cont: - case any: - case eq: - case i31: - case struct_: - case array: - case exn: - case string: - return false; - case none: - case noext: - case nofunc: - case nocont: - case noexn: - return true; - } - } - return false; -} - bool HeapType::isOpen() const { if (isBasic()) { return false; diff --git a/test/lit/binary/bad-multivalue-block.test b/test/lit/binary/bad-multivalue-block.test deleted file mode 100644 index 8b100fe89ed..00000000000 --- a/test/lit/binary/bad-multivalue-block.test +++ /dev/null @@ -1,16 +0,0 @@ -;; Test that we error properly on a block with a bad multivalue (inputs). - -;; File contents: -;; -;; (module -;; (func $test -;; i32.const 0 -;; (block (param i32) -;; drop -;; ) -;; ) -;; ) - -;; RUN: not wasm-opt -all %s.wasm 2>&1 | filecheck %s - -;; CHECK: control flow inputs are not supported yet diff --git a/test/lit/binary/bad-multivalue-block.test.wasm b/test/lit/binary/bad-multivalue-block.test.wasm deleted file mode 100644 index e44b9033f20..00000000000 Binary files a/test/lit/binary/bad-multivalue-block.test.wasm and /dev/null differ diff --git a/test/lit/binary/bad-multivalue-if.test b/test/lit/binary/bad-multivalue-if.test deleted file mode 100644 index 8fe20601285..00000000000 --- a/test/lit/binary/bad-multivalue-if.test +++ /dev/null @@ -1,22 +0,0 @@ -;; Test that we error properly on an if with a bad multivalue (inputs). - -;; File contents: -;; -;; (module -;; (func $test -;; i32.const 0 -;; i32.const 1 -;; (if (param i32) -;; (then -;; drop -;; ) -;; (else -;; drop -;; ) -;; ) -;; ) -;; ) - -;; RUN: not wasm-opt -all %s.wasm 2>&1 | filecheck %s - -;; CHECK: control flow inputs are not supported yet diff --git a/test/lit/binary/bad-multivalue-if.test.wasm b/test/lit/binary/bad-multivalue-if.test.wasm deleted file mode 100644 index baddfec4ed7..00000000000 Binary files a/test/lit/binary/bad-multivalue-if.test.wasm and /dev/null differ diff --git a/test/lit/control-flow-input.wast b/test/lit/control-flow-input.wast new file mode 100644 index 00000000000..7baae35f0ef --- /dev/null +++ b/test/lit/control-flow-input.wast @@ -0,0 +1,623 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py 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 +;; --enable-all --debug-names and should be regenerated when new tests are added +;; here. + +;; RUN: wasm-opt -all %s -S -o - | filecheck %s +;; RUN: wasm-opt -all %s.wasm -S -o - | filecheck %s + +(module + (type $id (func (param i32) (result i32))) + + ;; CHECK: (tag $e (param i32)) + (tag $e (param i32)) + + ;; CHECK: (func $block (type $0) (result i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $block (result i32) + i32.const 0 + block (param i32) (result i32) + end + ) + + ;; CHECK: (func $block-multivalue (type $1) (result i32 i64) + ;; CHECK-NEXT: (local $scratch (tuple 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: ) + ;; 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 + i64.const 1 + block (param i32 i64) (result i32 i64) + end + ) + + ;; CHECK: (func $block-drop (type $2) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $block-drop + i32.const 0 + block (param i32) + drop + end + ) + + ;; 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_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: ) + ;; 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: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (tuple.extract 2 1 + ;; CHECK-NEXT: (local.get $scratch_1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $block-multivalue-drop + i32.const 0 + i64.const 1 + block (param i32 i64) + drop + drop + end + ) + + ;; CHECK: (func $block-passthrough-nop (type $0) (result i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local $scratch_1 i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $scratch_1 + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (local.get $scratch_1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $block-passthrough-nop (result i32) + i32.const 0 + block (param i32) (result i32) + nop + end + ) + + ;; CHECK: (func $block-passthrough-type (type $0) (result i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $block-passthrough-type (result i32) + i32.const 0 + block (type $id) + end + ) + + ;; CHECK: (func $loop (type $0) (result i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop (result i32) + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $loop (result i32) + i32.const 0 + loop (param i32) (result i32) + end + ) + + ;; CHECK: (func $loop-branch (type $2) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $label0 + ;; CHECK-NEXT: (block $label1 + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (block $label (result i32) + ;; CHECK-NEXT: (br $label + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $loop-branch + i32.const 0 + loop (param i32) + br 0 + end + ) + + ;; CHECK: (func $loop-branch-cond (type $0) (result i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $label0 (result i32) + ;; CHECK-NEXT: (block $label1 (result i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (block $label (result i32) + ;; CHECK-NEXT: (br $label1 + ;; CHECK-NEXT: (br_if $label + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $loop-branch-cond (result i32) + i32.const 0 + loop (param i32) (result i32) + i32.const 1 + br_if 0 + end + ) + + ;; CHECK: (func $loop-branch-cond-drop (type $2) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $label0 + ;; CHECK-NEXT: (block $label1 + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (block $label (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (br_if $label + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $loop-branch-cond-drop + i32.const 0 + loop (param i32) + i32.const 1 + br_if 0 + drop + end + ) + + ;; CHECK: (func $loop-branch-cond-new-val (type $4) (result i64) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $label0 (result i64) + ;; CHECK-NEXT: (block $label1 (result i64) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (block $label (result i32) + ;; CHECK-NEXT: (br $label1 + ;; CHECK-NEXT: (block (result i64) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (br_if $label + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i64.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $loop-branch-cond-new-val (result i64) + i32.const 0 + loop (param i32) (result i64) + i32.const 1 + br_if 0 + drop + i64.const 2 + 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-NEXT: (local.set $scratch + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: ) + ;; 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.get $scratch) + ;; 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: (br_table $label $label1 $label2 + ;; CHECK-NEXT: (local.get $scratch_1) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label11) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $nested-loops-multivalue (result i32 i64) + i32.const 0 + i64.const 1 + loop (param i32 i64) + loop (param i32 i64) + i32.const 2 + br_table 0 1 2 + end + end + unreachable + ) + + ;; CHECK: (func $if (type $0) (result i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if (result i32) + i32.const 0 + i32.const 1 + if (param i32) (result i32) + end + ) + + ;; CHECK: (func $if-new-val (type $0) (result i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if-new-val (result i32) + i32.const 0 + i32.const 1 + if (param i32) (result i32) + drop + i32.const 2 + end + ) + + ;; CHECK: (func $if-multivalue (type $1) (result i32 i64) + ;; CHECK-NEXT: (local $scratch (tuple 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: ) + ;; CHECK-NEXT: (if (type $1) (result i32 i64) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if-multivalue (result i32 i64) + i32.const 0 + i64.const 1 + i32.const 2 + if (param i32 i64) (result i32 i64) + end + ) + + ;; CHECK: (func $if-else (type $0) (result i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if-else (result i32) + i32.const 0 + i32.const 1 + if (param i32) (result i32) + else + end + ) + + ;; CHECK: (func $if-else-drop (type $2) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if-else-drop + i32.const 0 + i32.const 1 + if (param i32) + drop + else + drop + end + ) + + ;; 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_2 i32) + ;; CHECK-NEXT: (local $scratch_3 (tuple i32 i64)) + ;; CHECK-NEXT: (local $scratch_4 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: ) + ;; CHECK-NEXT: (if (result f32) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: (then + ;; 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: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (tuple.extract 2 1 + ;; CHECK-NEXT: (local.get $scratch_1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (f32.const 3) + ;; CHECK-NEXT: ) + ;; 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: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (tuple.extract 2 1 + ;; CHECK-NEXT: (local.get $scratch_3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $scratch_4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (f32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $if-else-multivalue (result f32) + i32.const 0 + i64.const 1 + i32.const 2 + if (param i32 i64) (result f32) + drop + drop + f32.const 3 + else + drop + drop + f32.const 4 + end + ) + + ;; CHECK: (func $try (type $0) (result i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try (result i32) + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $try (result i32) + i32.const 0 + try (param i32) (result i32) + end + ) + + ;; CHECK: (func $try-catch (type $0) (result i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try (result i32) + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $e + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $try-catch (result i32) + i32.const 0 + try (param i32) (result i32) + catch $e + end + ) + + ;; CHECK: (func $try-catch-all (type $0) (result i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try (result i32) + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $try-catch-all (result i32) + i32.const 0 + try (param i32) (result i32) + catch_all + i32.const 1 + end + ) + + ;; CHECK: (func $try-catch-delegate (type $0) (result i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try (result i32) + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (delegate 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $try-catch-delegate (result i32) + i32.const 0 + try (param i32) (result i32) + delegate 0 + ) + + ;; CHECK: (func $try-table (type $0) (result i32) + ;; CHECK-NEXT: (local $scratch i32) + ;; CHECK-NEXT: (local.set $scratch + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try_table (result i32) + ;; CHECK-NEXT: (local.get $scratch) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $try-table (result i32) + i32.const 0 + try_table (param i32) (result i32) + end + ) +) diff --git a/test/lit/control-flow-input.wast.wasm b/test/lit/control-flow-input.wast.wasm new file mode 100644 index 00000000000..664d1be843a Binary files /dev/null and b/test/lit/control-flow-input.wast.wasm differ diff --git a/test/lit/parse-bad-block-params.wast b/test/lit/parse-bad-block-params.wast deleted file mode 100644 index 67e05989ce6..00000000000 --- a/test/lit/parse-bad-block-params.wast +++ /dev/null @@ -1,12 +0,0 @@ -;; RUN: not wasm-opt %s -S -o - 2>&1 | filecheck %s - -;; CHECK: 8:11: error: block parameters not yet supported - -(module - (func - (i32.const 0) - (block (param i32) - (drop) - ) - ) -)