Backed out changeset 15a3458b4d11 (bug 1254142) for bustage. r=backout on a CLOSED TREE
authorSebastian Hengst <archaeopteryx@coole-files.de>
Wed, 30 Mar 2016 14:27:34 +0200
changeset 290887 be61ede92adbcad7000db7ba128276ced1ce73b2
parent 290886 69a7e03cf1f8ee309ab4bf38d1761693a30191d2
child 290888 88df6d5a1f21bd9c8298f3503620a9647ef3d857
push id74411
push userarchaeopteryx@coole-files.de
push dateWed, 30 Mar 2016 12:29:16 +0000
treeherdermozilla-inbound@be61ede92adb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1254142
milestone48.0a1
backs out15a3458b4d114f6494370c5eae2583e77e6c73f5
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
Backed out changeset 15a3458b4d11 (bug 1254142) for bustage. r=backout on a CLOSED TREE
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmIonCompile.cpp
js/src/asmjs/WasmTextToBinary.cpp
js/src/jit-test/tests/wasm/basic-control-flow.js
--- 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);