author | Sebastian Hengst <archaeopteryx@coole-files.de> |
Wed, 30 Mar 2016 14:27:34 +0200 | |
changeset 290887 | be61ede92adbcad7000db7ba128276ced1ce73b2 |
parent 290886 | 69a7e03cf1f8ee309ab4bf38d1761693a30191d2 |
child 290888 | 88df6d5a1f21bd9c8298f3503620a9647ef3d857 |
push id | 74411 |
push user | archaeopteryx@coole-files.de |
push date | Wed, 30 Mar 2016 12:29:16 +0000 |
treeherder | mozilla-inbound@be61ede92adb [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | backout |
bugs | 1254142 |
milestone | 48.0a1 |
backs out | 15a3458b4d114f6494370c5eae2583e77e6c73f5 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -554,22 +554,25 @@ static bool DecodeBranch(FunctionDecoder& f, Expr expr, ExprType* type) { MOZ_ASSERT(expr == Expr::Br || expr == Expr::BrIf); uint32_t relativeDepth; if (!f.d().readVarU32(&relativeDepth)) return f.fail("expected relative depth"); - ExprType brType; - if (!DecodeExpr(f, &brType)) + if (!f.branchWithType(relativeDepth, ExprType::Void)) + return f.fail("branch depth exceeds current nesting level"); + + Expr value; + if (!f.d().readExpr(&value)) return f.fail("expected branch value"); - if (!f.branchWithType(relativeDepth, brType)) - return f.fail("branch depth exceeds current nesting level"); + if (value != Expr::Nop) + return f.fail("NYI: branch values"); if (expr == Expr::BrIf) { ExprType actual; if (!DecodeExpr(f, &actual)) return false; if (!CheckType(f, actual, ValType::I32)) return false;
--- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -12,19 +12,16 @@ * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "asmjs/WasmIonCompile.h" - -#include <functional> - #include "asmjs/WasmGenerator.h" #include "jit/CodeGenerator.h" using namespace js; using namespace js::jit; using namespace js::wasm; @@ -936,46 +933,56 @@ class FunctionCompiler private: static bool hasPushed(MBasicBlock* block) { uint32_t numPushed = block->stackDepth() - block->info().firstStackSlot(); MOZ_ASSERT(numPushed == 0 || numPushed == 1); return numPushed; } + static void push(MBasicBlock* block, MDefinition* def) + { + MOZ_ASSERT(!hasPushed(block)); + block->push(def); + } + + static void popAll(BlockVector* blocks) + { + for (MBasicBlock* block : *blocks) + block->pop(); + } + public: - void pushDef(MDefinition* def) + bool addJoinPredecessor(MDefinition* def, BlockVector* blocks) { if (inDeadCode()) - return; - MOZ_ASSERT(!hasPushed(curBlock_)); - if (def && def->type() != MIRType_None) - curBlock_->push(def); - } - - typedef std::function<MBasicBlock*(size_t)> GetBlockFunction; - - void ensurePushInvariants(GetBlockFunction getBlock, size_t numBlocks) - { - // Preserve the invariant that, for every iterated MBasicBlock, + return true; + + // Preserve the invariant that, for every MBasicBlock in 'blocks', // either: every MBasicBlock has a non-void pushed expression OR no // MBasicBlock has any pushed expression. This is required by // MBasicBlock::addPredecessor. - bool allPushed = true; - - for (size_t i = 0; allPushed && i < numBlocks; i++) - allPushed = hasPushed(getBlock(i)); - - if (!allPushed) { - for (size_t i = 0; i < numBlocks; i++) { - MBasicBlock* block = getBlock(i); - if (hasPushed(block)) - block->pop(); + if (def) { + if (blocks->empty()) { + if (def->type() != MIRType_None) + push(curBlock_, def); + } else { + if (hasPushed((*blocks)[0])) { + if (def->type() == MIRType_None) + popAll(blocks); + else + push(curBlock_, def); + } } + } else { + if (!blocks->empty() && hasPushed((*blocks)[0])) + popAll(blocks); } + + return blocks->append(curBlock_); } bool joinIf(MBasicBlock* joinBlock, BlockVector* blocks, MDefinition** def) { MOZ_ASSERT_IF(curBlock_, blocks->back() == curBlock_); curBlock_ = joinBlock; return joinIfElse(nullptr, blocks, def); } @@ -983,32 +990,21 @@ class FunctionCompiler void switchToElse(MBasicBlock* elseBlock) { if (!elseBlock) return; curBlock_ = elseBlock; mirGraph().moveBlockToEnd(curBlock_); } - bool addJoinPredecessor(MDefinition* def, BlockVector* blocks) - { - if (inDeadCode()) - return true; - pushDef(def); - return blocks->append(curBlock_); - } - bool joinIfElse(MDefinition* elseDef, BlockVector* blocks, MDefinition** def) { if (!addJoinPredecessor(elseDef, blocks)) return false; - auto getBlock = [&](size_t i) -> MBasicBlock* { return (*blocks)[i]; }; - ensurePushInvariants(getBlock, blocks->length()); - if (blocks->empty()) { *def = nullptr; return true; } MBasicBlock* join; if (!goToNewBlock((*blocks)[0], &join)) return false; @@ -1027,21 +1023,21 @@ class FunctionCompiler bool startBlock() { MOZ_ASSERT_IF(blockDepth_ < blockPatches_.length(), blockPatches_[blockDepth_].empty()); blockDepth_++; return true; } - bool finishBlock(MDefinition** def) + bool finishBlock() { MOZ_ASSERT(blockDepth_); uint32_t topLabel = --blockDepth_; - return bindBranches(topLabel, def); + return bindBranches(topLabel); } bool startLoop(MBasicBlock** loopHeader) { *loopHeader = nullptr; blockDepth_ += 2; loopDepth_++; @@ -1072,28 +1068,33 @@ class FunctionCompiler { for (size_t i = 0, depth = b->stackDepth(); i < depth; i++) { MDefinition* def = b->getSlot(i); if (def->isUnused()) b->setSlot(i, def->toPhi()->getOperand(0)); } } - bool setLoopBackedge(MBasicBlock* loopEntry, MBasicBlock* loopBody, MBasicBlock* backedge) + bool setLoopBackedge(MBasicBlock* loopEntry, MBasicBlock* loopBody, MBasicBlock* backedge, + MDefinition** loopResult) { if (!loopEntry->setBackedgeAsmJS(backedge)) return false; // Flag all redundant phis as unused. for (MPhiIterator phi = loopEntry->phisBegin(); phi != loopEntry->phisEnd(); phi++) { MOZ_ASSERT(phi->numOperands() == 2); if (phi->getOperand(0) == phi->getOperand(1)) phi->setUnused(); } + // The loop result may also be referencing a recycled phi. + if (*loopResult && (*loopResult)->isUnused()) + *loopResult = (*loopResult)->toPhi()->getOperand(0); + // Fix up phis stored in the slots Vector of pending blocks. for (ControlFlowPatchVector& patches : blockPatches_) { for (ControlFlowPatch& p : patches) { MBasicBlock* block = p.ins->block(); if (block->loopDepth() >= loopEntry->loopDepth()) fixupRedundantPhis(block); } } @@ -1137,37 +1138,33 @@ class FunctionCompiler // Expr::Loop doesn't have an implicit backedge so temporarily set // aside the end of the loop body to bind backedges. MBasicBlock* loopBody = curBlock_; curBlock_ = nullptr; // TODO (bug 1253544): blocks branching to the top join to a single // backedge block. Could they directly be set as backedges of the loop // instead? - MDefinition* _; - if (!bindBranches(headerLabel, &_)) + if (!bindBranches(headerLabel)) return false; MOZ_ASSERT(loopHeader->loopDepth() == loopDepth_); if (curBlock_) { // We're on the loop backedge block, created by bindBranches. - if (hasPushed(curBlock_)) - curBlock_->pop(); - MOZ_ASSERT(curBlock_->loopDepth() == loopDepth_); curBlock_->end(MGoto::New(alloc(), loopHeader)); - if (!setLoopBackedge(loopHeader, loopBody, curBlock_)) + if (!setLoopBackedge(loopHeader, loopBody, curBlock_, loopResult)) return false; } curBlock_ = loopBody; loopDepth_--; - if (!bindBranches(afterLabel, loopResult)) + if (!bindBranches(afterLabel)) return false; // If we have not created a new block in bindBranches, we're still on // the inner loop body, which loop depth is incorrect. if (curBlock_ && curBlock_->loopDepth() != loopDepth_) { MBasicBlock* out; if (!goToNewBlock(curBlock_, &out)) return false; @@ -1183,47 +1180,43 @@ class FunctionCompiler uint32_t absolute = blockDepth_ - 1 - relative; if (absolute >= blockPatches_.length() && !blockPatches_.resize(absolute + 1)) return false; return blockPatches_[absolute].append(ControlFlowPatch(ins, index)); } - bool br(uint32_t relativeDepth, MDefinition* maybeValue) + bool br(uint32_t relativeDepth) { if (inDeadCode()) return true; MGoto* jump = MGoto::NewAsm(alloc()); if (!addControlFlowPatch(jump, relativeDepth, MGoto::TargetIndex)) return false; - pushDef(maybeValue); - curBlock_->end(jump); curBlock_ = nullptr; return true; } - bool brIf(uint32_t relativeDepth, MDefinition* maybeValue, MDefinition* condition) + bool brIf(uint32_t relativeDepth, MDefinition* condition) { if (inDeadCode()) return true; MBasicBlock* joinBlock = nullptr; if (!newBlock(curBlock_, &joinBlock)) return false; MTest* test = MTest::NewAsm(alloc(), condition, joinBlock); if (!addControlFlowPatch(test, relativeDepth, MTest::TrueBranchIndex)) return false; - pushDef(maybeValue); - curBlock_->end(test); curBlock_ = joinBlock; return true; } bool brTable(MDefinition* expr, uint32_t defaultDepth, const Uint32Vector& depths) { if (inDeadCode()) @@ -1322,31 +1315,24 @@ class FunctionCompiler bool goToExistingBlock(MBasicBlock* prev, MBasicBlock* next) { MOZ_ASSERT(prev); MOZ_ASSERT(next); prev->end(MGoto::New(alloc(), next)); return next->addPredecessor(alloc(), prev); } - bool bindBranches(uint32_t absolute, MDefinition** def) + bool bindBranches(uint32_t absolute) { - if (absolute >= blockPatches_.length() || blockPatches_[absolute].empty()) { - *def = !inDeadCode() && hasPushed(curBlock_) ? curBlock_->pop() : nullptr; + if (absolute >= blockPatches_.length()) return true; - } ControlFlowPatchVector& patches = blockPatches_[absolute]; - - auto getBlock = [&](size_t i) -> MBasicBlock* { - if (i < patches.length()) - return patches[i].ins->block(); - return curBlock_; - }; - ensurePushInvariants(getBlock, patches.length() + !!curBlock_); + if (patches.empty()) + return true; MBasicBlock* join = nullptr; MControlInstruction* ins = patches[0].ins; if (!newBlock(ins->block(), &join)) return false; ins->replaceSuccessor(patches[0].index, join); @@ -1356,18 +1342,16 @@ class FunctionCompiler return false; ins->replaceSuccessor(patches[i].index, join); } if (curBlock_ && !goToExistingBlock(curBlock_, join)) return false; curBlock_ = join; - *def = hasPushed(curBlock_) ? curBlock_->pop() : nullptr; - patches.clear(); return true; } }; static bool EmitLiteral(FunctionCompiler& f, ValType type, MDefinition** def) { @@ -2541,20 +2525,20 @@ EmitLoop(FunctionCompiler& f, MDefinitio f.addInterruptCheck(); if (uint32_t numStmts = f.readVarU32()) { for (uint32_t i = 0; i < numStmts - 1; i++) { MDefinition* _; if (!EmitExpr(f, &_)) return false; } - MDefinition* last = nullptr; - if (!EmitExpr(f, &last)) + if (!EmitExpr(f, def)) return false; - f.pushDef(last); + } else { + *def = nullptr; } return f.closeLoop(loopHeader, def); } static bool EmitIfElse(FunctionCompiler& f, Expr op, MDefinition** def) { @@ -2619,17 +2603,17 @@ EmitBrTable(FunctionCompiler& f, MDefini MDefinition* index; if (!EmitExpr(f, &index)) return false; *def = nullptr; // Empty table if (!numCases) - return f.br(defaultDepth, nullptr); + return f.br(defaultDepth); return f.brTable(index, defaultDepth, depths); } static bool EmitReturn(FunctionCompiler& f, MDefinition** def) { ExprType ret = f.sig().ret(); @@ -2659,48 +2643,46 @@ EmitUnreachable(FunctionCompiler& f, MDe static bool EmitBlock(FunctionCompiler& f, MDefinition** def) { if (!f.startBlock()) return false; if (uint32_t numStmts = f.readVarU32()) { for (uint32_t i = 0; i < numStmts - 1; i++) { - MDefinition* _ = nullptr; + MDefinition* _; if (!EmitExpr(f, &_)) return false; } - MDefinition* last = nullptr; - if (!EmitExpr(f, &last)) + if (!EmitExpr(f, def)) return false; - f.pushDef(last); + } else { + *def = nullptr; } - return f.finishBlock(def); + return f.finishBlock(); } static bool EmitBranch(FunctionCompiler& f, Expr op, MDefinition** def) { MOZ_ASSERT(op == Expr::Br || op == Expr::BrIf); uint32_t relativeDepth = f.readVarU32(); - MDefinition* maybeValue = nullptr; - if (!EmitExpr(f, &maybeValue)) - return false; + MOZ_ALWAYS_TRUE(f.readExpr() == Expr::Nop); if (op == Expr::Br) { - if (!f.br(relativeDepth, maybeValue)) + if (!f.br(relativeDepth)) return false; } else { MDefinition* condition; if (!EmitExpr(f, &condition)) return false; - if (!f.brIf(relativeDepth, maybeValue, condition)) + if (!f.brIf(relativeDepth, condition)) return false; } *def = nullptr; return true; } static bool
--- a/js/src/asmjs/WasmTextToBinary.cpp +++ b/js/src/asmjs/WasmTextToBinary.cpp @@ -320,32 +320,28 @@ class WasmAstBlock : public WasmAstExpr const WasmAstExprVector& exprs() const { return exprs_; } }; class WasmAstBranch : public WasmAstExpr { Expr expr_; WasmAstExpr* cond_; WasmRef target_; - WasmAstExpr* value_; public: static const WasmAstExprKind Kind = WasmAstExprKind::Branch; - explicit WasmAstBranch(Expr expr, WasmAstExpr* cond, WasmRef target, WasmAstExpr* value) + explicit WasmAstBranch(Expr expr, WasmAstExpr* cond, WasmRef target) : WasmAstExpr(Kind), expr_(expr), cond_(cond), - target_(target), - value_(value) + target_(target) {} - Expr expr() const { return expr_; } WasmRef& target() { return target_; } WasmAstExpr& cond() const { MOZ_ASSERT(cond_); return *cond_; } - WasmAstExpr* maybeValue() const { return value_; } }; class WasmAstCall : public WasmAstExpr { Expr expr_; WasmRef func_; WasmAstExprVector args_; @@ -2077,40 +2073,24 @@ static WasmAstBranch* ParseBranch(WasmParseContext& c, Expr expr) { MOZ_ASSERT(expr == Expr::Br || expr == Expr::BrIf); WasmRef target; if (!c.ts.matchRef(&target, c.error)) return nullptr; - WasmAstExpr* value = nullptr; - if (c.ts.getIf(WasmToken::OpenParen)) { - value = ParseExprInsideParens(c); - if (!value) - return nullptr; - if (!c.ts.match(WasmToken::CloseParen, c.error)) - return nullptr; - } - WasmAstExpr* cond = nullptr; if (expr == Expr::BrIf) { - if (c.ts.getIf(WasmToken::OpenParen)) { - cond = ParseExprInsideParens(c); - if (!cond) - return nullptr; - if (!c.ts.match(WasmToken::CloseParen, c.error)) - return nullptr; - } else { - cond = value; - value = nullptr; - } + cond = ParseExpr(c); + if (!cond) + return nullptr; } - return new(c.lifo) WasmAstBranch(expr, cond, target, value); + return new(c.lifo) WasmAstBranch(expr, cond, target); } static bool ParseArgs(WasmParseContext& c, WasmAstExprVector* args) { while (c.ts.getIf(WasmToken::OpenParen)) { WasmAstExpr* arg = ParseExprInsideParens(c); if (!arg || !args->append(arg)) @@ -3624,17 +3604,17 @@ EncodeBranch(Encoder& e, WasmAstBranch& MOZ_ASSERT(br.expr() == Expr::Br || br.expr() == Expr::BrIf); if (!e.writeExpr(br.expr())) return false; if (!e.writeVarU32(br.target().index())) return false; - if (br.maybeValue() ? !EncodeExpr(e, *br.maybeValue()) : !e.writeExpr(Expr::Nop)) + if (!e.writeExpr(Expr::Nop)) return false; if (br.expr() == Expr::BrIf) { if (!EncodeExpr(e, br.cond())) return false; } return true;
--- a/js/src/jit-test/tests/wasm/basic-control-flow.js +++ b/js/src/jit-test/tests/wasm/basic-control-flow.js @@ -139,16 +139,18 @@ assertEq(wasmEvalText('(module (func (re assertEq(wasmEvalText('(module (func (result i32) (return (i32.const 1))) (export "" 0))')(), 1); assertEq(wasmEvalText('(module (func (if (return) (i32.const 0))) (export "" 0))')(), undefined); assertErrorMessage(() => wasmEvalText('(module (func (result f32) (return (i32.const 1))) (export "" 0))'), TypeError, mismatchError("i32", "f32")); assertThrowsInstanceOf(() => wasmEvalText('(module (func (result i32) (return)) (export "" 0))'), TypeError); // TODO: Reenable when syntactic arities are added for returns //assertThrowsInstanceOf(() => wasmEvalText('(module (func (return (i32.const 1))) (export "" 0))'), TypeError); +// TODO: convert these to wasmEval and assert some results once they are implemented + // ---------------------------------------------------------------------------- // br / br_if assertErrorMessage(() => wasmEvalText('(module (func (result i32) (block (br 0))) (export "" 0))'), TypeError, mismatchError("void", "i32")); assertErrorMessage(() => wasmEvalText('(module (func (result i32) (block (br_if 0 (i32.const 0)))) (export "" 0))'), TypeError, mismatchError("void", "i32")); const DEPTH_OUT_OF_BOUNDS = /branch depth exceeds current nesting level/; @@ -262,126 +264,16 @@ var isNonZero = wasmEvalText(`(module (f ) (return (i32.const 1)) ) (export "" 0))`); assertEq(isNonZero(0), 0); assertEq(isNonZero(1), 1); assertEq(isNonZero(-1), 1); -// branches with values -// br/br_if and block -assertErrorMessage(() => wasmEvalText('(module (func (result i32) (block (br 0))))'), TypeError, mismatchError("void", "i32")); -assertErrorMessage(() => wasmEvalText('(module (func (result i32) (block (br 0 (f32.const 42)))))'), TypeError, mismatchError("f32", "i32")); - -assertErrorMessage(() => wasmEvalText(`(module (func (result i32) (param i32) (block (if (get_local 0) (br 0 (i32.const 42))))) (export "" 0))`), TypeError, mismatchError("void", "i32")); -assertErrorMessage(() => wasmEvalText(`(module (func (result i32) (param i32) (block (if (get_local 0) (br 0 (i32.const 42))) (br 0 (f32.const 42)))) (export "" 0))`), TypeError, mismatchError("void", "i32")); - -assertEq(wasmEvalText('(module (func (result i32) (block (br 0 (i32.const 42)) (i32.const 13))) (export "" 0))')(), 42); - -assertEq(wasmEvalText('(module (func) (func (block (br 0 (call 0)) (i32.const 13))) (export "" 0))')(), undefined); -assertEq(wasmEvalText('(module (func) (func (block (br_if 0 (call 0) (i32.const 1)) (i32.const 13))) (export "" 0))')(), undefined); - -var f = wasmEvalText(`(module (func (result i32) (param i32) (block (if (get_local 0) (br 0 (i32.const 42))) (i32.const 43))) (export "" 0))`); -assertEq(f(0), 43); -assertEq(f(1), 42); - -var f = wasmEvalText(`(module (func (result i32) (param i32) (block (br_if 0 (i32.const 42) (get_local 0)) (i32.const 43))) (export "" 0))`); -assertEq(f(0), 43); -assertEq(f(1), 42); - -var f = wasmEvalText(`(module (func (result i32) (param i32) (block (if (get_local 0) (br 0 (i32.const 42))) (br 0 (i32.const 43)))) (export "" 0))`); -assertEq(f(0), 43); -assertEq(f(1), 42); - -var f = wasmEvalText(`(module (func (result i32) (param i32) (block (br_if 0 (i32.const 42) (get_local 0)) (br 0 (i32.const 43)))) (export "" 0))`); -assertEq(f(0), 43); -assertEq(f(1), 42); - -var f = wasmEvalText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block (if (get_local 0) (br 0 (i32.const 99))) (i32.const -1)))) (export "" 0))`); -assertEq(f(0), 0); -assertEq(f(1), 100); - -var f = wasmEvalText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block (br_if 0 (i32.const 99) (get_local 0)) (i32.const -1)))) (export "" 0))`); -assertEq(f(0), 0); -assertEq(f(1), 100); - -assertEq(wasmEvalText(`(module (func (result i32) (block (br 0 (return (i32.const 42))) (i32.const 0))) (export "" 0))`)(), 42); -assertEq(wasmEvalText(`(module (func (result i32) (block (return (br 0 (i32.const 42))))) (export "" 0))`)(), 42); -assertEq(wasmEvalText(`(module (func (result i32) (block (return (br 0 (i32.const 42))) (i32.const 0))) (export "" 0))`)(), 42); - -assertEq(wasmEvalText(`(module (func (result f32) (block (br 0 (i32.const 0))) (block (br 0 (f32.const 42)))) (export "" 0))`)(), 42); - -var called = 0; -var imports = { - sideEffects: { - ifTrue(x) {assertEq(x, 13); called++;}, - ifFalse(x) {assertEq(x, 37); called--;} - } -} -var f = wasmEvalText(`(module - (import "sideEffects" "ifTrue" (param i32)) - (import "sideEffects" "ifFalse" (param i32)) - (func - (param i32) (result i32) - (block $outer - (if - (get_local 0) - (br $outer (call_import 0 (i32.const 13))) - ) - (if - (i32.eqz (get_local 0)) - (br $outer (call_import 1 (i32.const 37))) - ) - ) - (i32.const 42) - ) -(export "" 0))`, imports); -assertEq(f(0), 42); -assertEq(called, -1); -assertEq(f(1), 42); -assertEq(called, 0); - -// br/br_if and loop -assertEq(wasmEvalText(`(module (func (param i32) (result i32) (loop $out $in (br $out (get_local 0)))) (export "" 0))`)(1), 1); - -assertEq(wasmEvalText(`(module (func (param i32) (result i32) - (loop $out $in - (if (get_local 0) (br $in (i32.const 1))) - (if (get_local 0) (br $in (f32.const 2))) - (if (get_local 0) (br $in (f64.const 3))) - (if (get_local 0) (br $in)) - (i32.const 7) - ) - ) (export "" 0))`)(0), 7); - -assertEq(wasmEvalText(`(module - (func - (result i32) - (local i32) - (loop $out $in - (set_local 0 (i32.add (get_local 0) (i32.const 1))) - (if (i32.ge_s (get_local 0) (i32.const 7)) (br $out (get_local 0))) - (br $in) - ) - ) -(export "" 0))`)(), 7); - -assertEq(wasmEvalText(`(module - (func - (result i32) - (local i32) - (loop $out $in - (set_local 0 (i32.add (get_local 0) (i32.const 1))) - (br_if $out (get_local 0) (i32.ge_s (get_local 0) (i32.const 7))) - (br $in) - ) - ) -(export "" 0))`)(), 7); - // ---------------------------------------------------------------------------- // loop assertErrorMessage(() => wasmEvalText('(module (func (loop (br 2))))'), TypeError, DEPTH_OUT_OF_BOUNDS); assertEq(wasmEvalText('(module (func (loop)) (export "" 0))')(), undefined); assertEq(wasmEvalText('(module (func (result i32) (loop (i32.const 2)) (i32.const 1)) (export "" 0))')(), 1);