summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/control-flow.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/wasm/control-flow.js')
-rw-r--r--js/src/jit-test/tests/wasm/control-flow.js736
1 files changed, 736 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/control-flow.js b/js/src/jit-test/tests/wasm/control-flow.js
new file mode 100644
index 000000000..fd4f8e4be
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/control-flow.js
@@ -0,0 +1,736 @@
+load(libdir + "wasm.js");
+
+const RuntimeError = WebAssembly.RuntimeError;
+
+// ----------------------------------------------------------------------------
+// if
+
+// Condition is an int32
+wasmFailValidateText('(module (func (local f32) (if (get_local 0) (i32.const 1))))', mismatchError("f32", "i32"));
+wasmFailValidateText('(module (func (local f32) (if (get_local 0) (i32.const 1) (i32.const 0))))', mismatchError("f32", "i32"));
+wasmFailValidateText('(module (func (local f64) (if (get_local 0) (i32.const 1) (i32.const 0))))', mismatchError("f64", "i32"));
+wasmEvalText('(module (func (local i32) (if (get_local 0) (nop))) (export "" 0))');
+wasmEvalText('(module (func (local i32) (if (get_local 0) (nop) (nop))) (export "" 0))');
+
+// Expression values types are consistent
+wasmFailValidateText('(module (func (result i32) (local f32) (if f32 (i32.const 42) (get_local 0) (i32.const 0))))', mismatchError("i32", "f32"));
+wasmFailValidateText('(module (func (result i32) (local f64) (if i32 (i32.const 42) (i32.const 0) (get_local 0))))', mismatchError("f64", "i32"));
+assertEq(wasmEvalText('(module (func (result i32) (if i32 (i32.const 42) (i32.const 1) (i32.const 2))) (export "" 0))').exports[""](), 1);
+assertEq(wasmEvalText('(module (func (result i32) (if i32 (i32.const 0) (i32.const 1) (i32.const 2))) (export "" 0))').exports[""](), 2);
+
+// Even if we don't yield, sub expressions types still have to match.
+wasmFailValidateText('(module (func (param f32) (if i32 (i32.const 42) (i32.const 1) (get_local 0))) (export "" 0))', mismatchError('f32', 'i32'));
+wasmFailValidateText('(module (func (if i32 (i32.const 42) (i32.const 1) (i32.const 0))) (export "" 0))', /unused values not explicitly dropped by end of block/);
+wasmFullPass('(module (func (drop (if i32 (i32.const 42) (i32.const 1) (i32.const 0)))) (export "run" 0))', undefined);
+wasmFullPass('(module (func (param f32) (if (i32.const 42) (drop (i32.const 1)) (drop (get_local 0)))) (export "run" 0))', undefined, {}, 13.37);
+
+// Sub-expression values are returned
+wasmFullPass(`(module
+ (func
+ (result i32)
+ (if i32
+ (i32.const 42)
+ (block i32
+ (
+ if i32
+ (block i32
+ (drop (i32.const 3))
+ (drop (i32.const 5))
+ (i32.const 0)
+ )
+ (i32.const 1)
+ (i32.const 2)
+ )
+ )
+ (i32.const 0)
+ )
+ )
+ (export "run" 0)
+)`, 2);
+
+// The if (resp. else) branch is taken iff the condition is true (resp. false)
+counter = 0;
+var imports = { "":{inc() { counter++ }} };
+wasmFullPass(`(module
+ (import "" "inc" (result i32))
+ (func
+ (result i32)
+ (if i32
+ (i32.const 42)
+ (i32.const 1)
+ (call 0)
+ )
+ )
+ (export "run" 1)
+)`, 1, imports);
+assertEq(counter, 0);
+
+wasmFullPass(`(module
+ (import "" "inc" (result i32))
+ (func
+ (result i32)
+ (if i32
+ (i32.const 0)
+ (call 0)
+ (i32.const 1)
+ )
+ )
+ (export "run" 1)
+)`, 1, imports);
+assertEq(counter, 0);
+
+wasmFullPass(`(module
+ (import "" "inc" (result i32))
+ (func
+ (if
+ (i32.const 0)
+ (drop (call 0))
+ )
+ )
+ (export "run" 1)
+)`, undefined, imports);
+assertEq(counter, 0);
+
+assertEq(wasmEvalText(`(module
+ (import "" "inc" (result i32))
+ (func
+ (if
+ (i32.const 1)
+ (drop (call 0))
+ )
+ )
+ (export "" 1)
+)`, imports).exports[""](), undefined);
+assertEq(counter, 1);
+
+wasmFullPass(`(module
+ (func
+ (result i32)
+ (if i32
+ (i32.const 0)
+ (br 0 (i32.const 0))
+ (br 0 (i32.const 1))
+ )
+ )
+ (export "run" 0)
+)`, 1);
+assertEq(counter, 1);
+
+wasmFullPass(`(module
+ (func
+ (if
+ (i32.const 1)
+ (br 0)
+ )
+ )
+ (export "run" 0)
+)`, undefined);
+assertEq(counter, 1);
+
+// One can chain if with if/if
+counter = 0;
+wasmFullPass(`(module
+ (import "" "inc" (result i32))
+ (func
+ (result i32)
+ (if i32
+ (i32.const 1)
+ (if i32
+ (i32.const 2)
+ (if i32
+ (i32.const 3)
+ (if i32
+ (i32.const 0)
+ (call 0)
+ (i32.const 42)
+ )
+ (call 0)
+ )
+ (call 0)
+ )
+ (call 0)
+ )
+ )
+ (export "run" 1)
+)`, 42, imports);
+assertEq(counter, 0);
+
+// "if" doesn't return an expression value
+wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 42) (i32.const 0))))', /if without else with a result value/);
+wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 42) (drop (i32.const 0)))))', mismatchError("void", "i32"));
+wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 1) (i32.const 0) (if i32 (i32.const 1) (i32.const 1)))))', /if without else with a result value/);
+wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 1) (drop (i32.const 0)) (if (i32.const 1) (drop (i32.const 1))))))', mismatchError("void", "i32"));
+wasmFailValidateText('(module (func (if i32 (i32.const 1) (i32.const 0) (if i32 (i32.const 1) (i32.const 1)))))', /if without else with a result value/);
+wasmFailValidateText('(module (func (if i32 (i32.const 1) (i32.const 0) (if (i32.const 1) (drop (i32.const 1))))))', mismatchError("void", "i32"));
+wasmFailValidateText('(module (func (if (i32.const 1) (drop (i32.const 0)) (if i32 (i32.const 1) (i32.const 1)))))', /if without else with a result value/);
+wasmEvalText('(module (func (if (i32.const 1) (drop (i32.const 0)) (if (i32.const 1) (drop (i32.const 1))))))');
+
+// ----------------------------------------------------------------------------
+// return
+
+wasmFullPass('(module (func (return)) (export "run" 0))', undefined);
+wasmFullPass('(module (func (result i32) (return (i32.const 1))) (export "run" 0))', 1);
+wasmFullPass('(module (func (if (return) (i32.const 0))) (export "run" 0))', undefined);
+wasmFailValidateText('(module (func (result i32) (return)) (export "" 0))', /popping value from empty stack/);
+wasmFullPass('(module (func (return (i32.const 1))) (export "run" 0))', undefined);
+wasmFailValidateText('(module (func (result f32) (return (i32.const 1))) (export "" 0))', mismatchError("i32", "f32"));
+wasmFailValidateText('(module (func (result i32) (return)) (export "" 0))', /popping value from empty stack/);
+
+// ----------------------------------------------------------------------------
+// br / br_if
+
+wasmFailValidateText('(module (func (result i32) (block (br 0))) (export "" 0))', mismatchError("void", "i32"));
+wasmFailValidateText('(module (func (result i32) (br 0)) (export "" 0))', /popping value from empty stack/);
+wasmFailValidateText('(module (func (result i32) (block (br_if 0 (i32.const 0)))) (export "" 0))', mismatchError("void", "i32"));
+
+const DEPTH_OUT_OF_BOUNDS = /branch depth exceeds current nesting level/;
+
+wasmFailValidateText('(module (func (br 1)))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (block (br 2))))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (loop (br 2))))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (if (i32.const 0) (br 2))))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (if (i32.const 0) (br 1) (br 2))))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (if (i32.const 0) (br 2) (br 1))))', DEPTH_OUT_OF_BOUNDS);
+
+wasmFailValidateText('(module (func (br_if 1 (i32.const 0))))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (block (br_if 2 (i32.const 0)))))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (loop (br_if 2 (i32.const 0)))))', DEPTH_OUT_OF_BOUNDS);
+
+wasmFailValidateText(`(module (func (result i32)
+ (block
+ (if
+ (br 0)
+ (i32.const 0)
+ (i32.const 2)
+ )
+ )
+) (export "" 0))`, mismatchError("void", "i32"));
+
+wasmFullPass(`(module (func (block $out (br_if $out (br 0)))) (export "run" 0))`, undefined);
+
+wasmFullPass('(module (func (br 0)) (export "run" 0))', undefined);
+wasmFullPass('(module (func (block (br 0))) (export "run" 0))', undefined);
+wasmFullPass('(module (func (block $l (br $l))) (export "run" 0))', undefined);
+
+wasmFullPass('(module (func (block (block (br 1)))) (export "run" 0))', undefined);
+wasmFullPass('(module (func (block $l (block (br $l)))) (export "run" 0))', undefined);
+
+wasmFullPass('(module (func (block $l (block $m (br $l)))) (export "run" 0))', undefined);
+wasmFullPass('(module (func (block $l (block $m (br $m)))) (export "run" 0))', undefined);
+
+wasmFullPass(`(module (func (result i32)
+ (block
+ (br 0)
+ (return (i32.const 0))
+ )
+ (return (i32.const 1))
+) (export "run" 0))`, 1);
+
+wasmFullPass(`(module (func (result i32)
+ (block
+ (block
+ (br 0)
+ (return (i32.const 0))
+ )
+ (return (i32.const 1))
+ )
+ (return (i32.const 2))
+) (export "run" 0))`, 1);
+
+wasmFullPass(`(module (func (result i32)
+ (block $outer
+ (block $inner
+ (br $inner)
+ (return (i32.const 0))
+ )
+ (return (i32.const 1))
+ )
+ (return (i32.const 2))
+) (export "run" 0))`, 1);
+
+var notcalled = false;
+var called = false;
+var imports = {"": {
+ notcalled() {notcalled = true},
+ called() {called = true}
+}};
+wasmFullPass(`(module
+(import "" "notcalled")
+(import "" "called")
+(func
+ (block
+ (return (br 0))
+ (call 0)
+ )
+ (call 1)
+) (export "run" 2))`, undefined, imports);
+assertEq(notcalled, false);
+assertEq(called, true);
+
+wasmFullPass(`(module (func
+ (block
+ (i32.add
+ (i32.const 0)
+ (return (br 0))
+ )
+ )
+ (return)
+) (export "run" 0))`, undefined);
+
+wasmFullPass(`(module (func (result i32)
+ (block
+ (if
+ (i32.const 1)
+ (br 0)
+ (return (i32.const 0))
+ )
+ )
+ (return (i32.const 1))
+) (export "run" 0))`, 1);
+
+wasmFullPass('(module (func (br_if 0 (i32.const 1))) (export "run" 0))', undefined);
+wasmFullPass('(module (func (br_if 0 (i32.const 0))) (export "run" 0))', undefined);
+wasmFullPass('(module (func (block (br_if 0 (i32.const 1)))) (export "run" 0))', undefined);
+wasmFullPass('(module (func (block (br_if 0 (i32.const 0)))) (export "run" 0))', undefined);
+wasmFullPass('(module (func (block $l (br_if $l (i32.const 1)))) (export "run" 0))', undefined);
+
+var isNonZero = wasmEvalText(`(module (func (result i32) (param i32)
+ (block
+ (br_if 0 (get_local 0))
+ (return (i32.const 0))
+ )
+ (return (i32.const 1))
+) (export "" 0))`).exports[""];
+
+assertEq(isNonZero(0), 0);
+assertEq(isNonZero(1), 1);
+assertEq(isNonZero(-1), 1);
+
+// branches with values
+// br/br_if and block
+wasmFailValidateText('(module (func (result i32) (br 0)))', /popping value from empty stack/);
+wasmFailValidateText('(module (func (result i32) (br 0 (f32.const 42))))', mismatchError("f32", "i32"));
+wasmFailValidateText('(module (func (result i32) (block (br 0))))', mismatchError("void", "i32"));
+wasmFailValidateText('(module (func (result i32) (block f32 (br 0 (f32.const 42)))))', mismatchError("f32", "i32"));
+
+wasmFailValidateText(`(module (func (result i32) (param i32) (block (if i32 (get_local 0) (br 0 (i32.const 42))))) (export "" 0))`, /if without else with a result value/);
+wasmFailValidateText(`(module (func (result i32) (param i32) (block i32 (if (get_local 0) (drop (i32.const 42))) (br 0 (f32.const 42)))) (export "" 0))`, mismatchError("f32", "i32"));
+
+wasmFullPass('(module (func (result i32) (br 0 (i32.const 42)) (i32.const 13)) (export "run" 0))', 42);
+wasmFullPass('(module (func (result i32) (block i32 (br 0 (i32.const 42)) (i32.const 13))) (export "run" 0))', 42);
+
+wasmFailValidateText('(module (func) (func (block i32 (br 0 (call 0)) (i32.const 13))) (export "" 0))', /popping value from empty stack/);
+wasmFailValidateText('(module (func) (func (block i32 (br_if 0 (call 0) (i32.const 1)) (i32.const 13))) (export "" 0))', /popping value from empty stack/);
+
+var f = wasmEvalText(`(module (func (result i32) (param i32) (block i32 (if (get_local 0) (drop (i32.const 42))) (i32.const 43))) (export "" 0))`).exports[""];
+assertEq(f(0), 43);
+assertEq(f(1), 43);
+
+wasmFailValidateText(`(module (func (result i32) (param i32) (block i32 (if i32 (get_local 0) (br 0 (i32.const 42))) (i32.const 43))) (export "" 0))`, /if without else with a result value/);
+
+var f = wasmEvalText(`(module (func (result i32) (param i32) (block i32 (if (get_local 0) (br 1 (i32.const 42))) (i32.const 43))) (export "" 0))`).exports[""];
+assertEq(f(0), 43);
+assertEq(f(1), 42);
+
+wasmFailValidateText(`(module (func (result i32) (param i32) (block (br_if 0 (i32.const 42) (get_local 0)) (i32.const 43))) (export "" 0))`, /unused values not explicitly dropped by end of block/);
+
+var f = wasmEvalText(`(module (func (result i32) (param i32) (block i32 (drop (br_if 0 (i32.const 42) (get_local 0))) (i32.const 43))) (export "" 0))`).exports[""];
+assertEq(f(0), 43);
+assertEq(f(1), 42);
+
+var f = wasmEvalText(`(module (func (result i32) (param i32) (block i32 (if (get_local 0) (drop (i32.const 42))) (br 0 (i32.const 43)))) (export "" 0))`).exports[""];
+assertEq(f(0), 43);
+assertEq(f(1), 43);
+
+wasmFailValidateText(`(module (func (result i32) (param i32) (block i32 (if i32 (get_local 0) (br 0 (i32.const 42))) (br 0 (i32.const 43)))) (export "" 0))`, /if without else with a result value/);
+
+var f = wasmEvalText(`(module (func (result i32) (param i32) (if (get_local 0) (br 1 (i32.const 42))) (br 0 (i32.const 43))) (export "" 0))`).exports[""];
+assertEq(f(0), 43);
+assertEq(f(1), 42);
+
+var f = wasmEvalText(`(module (func (result i32) (param i32) (block i32 (if (get_local 0) (br 1 (i32.const 42))) (br 0 (i32.const 43)))) (export "" 0))`).exports[""];
+assertEq(f(0), 43);
+assertEq(f(1), 42);
+
+var f = wasmEvalText(`(module (func (result i32) (param i32) (br_if 0 (i32.const 42) (get_local 0)) (br 0 (i32.const 43))) (export "" 0))`).exports[""];
+assertEq(f(0), 43);
+assertEq(f(1), 42);
+
+var f = wasmEvalText(`(module (func (result i32) (param i32) (block i32 (br_if 0 (i32.const 42) (get_local 0)) (br 0 (i32.const 43)))) (export "" 0))`).exports[""];
+assertEq(f(0), 43);
+assertEq(f(1), 42);
+
+var f = wasmEvalText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block i32 (if (get_local 0) (drop (i32.const 99))) (i32.const -1)))) (export "" 0))`).exports[""];
+assertEq(f(0), 0);
+assertEq(f(1), 0);
+
+wasmFailValidateText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block i32 (if i32 (get_local 0) (br 0 (i32.const 99))) (i32.const -1)))) (export "" 0))`, /if without else with a result value/);
+
+var f = wasmEvalText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block i32 (if (get_local 0) (br 1 (i32.const 99))) (i32.const -1)))) (export "" 0))`).exports[""];
+assertEq(f(0), 0);
+assertEq(f(1), 100);
+
+wasmFailValidateText(`(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))`, /unused values not explicitly dropped by end of block/);
+
+var f = wasmEvalText(`(module (func (param i32) (result i32) (i32.add (i32.const 1) (block i32 (drop (br_if 0 (i32.const 99) (get_local 0))) (i32.const -1)))) (export "" 0))`).exports[""];
+assertEq(f(0), 0);
+assertEq(f(1), 100);
+
+wasmFullPass(`(module (func (result i32) (block i32 (br 0 (return (i32.const 42))) (i32.const 0))) (export "run" 0))`, 42);
+wasmFullPass(`(module (func (result i32) (block i32 (return (br 0 (i32.const 42))))) (export "run" 0))`, 42);
+wasmFullPass(`(module (func (result i32) (block i32 (return (br 0 (i32.const 42))) (i32.const 0))) (export "run" 0))`, 42);
+
+wasmFullPass(`(module (func (result f32) (drop (block i32 (br 0 (i32.const 0)))) (block f32 (br 0 (f32.const 42)))) (export "run" 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)
+ (block (call 0 (i32.const 13)) (br $outer))
+ )
+ (if
+ (i32.eqz (get_local 0))
+ (block (call 1 (i32.const 37)) (br $outer))
+ )
+ )
+ (i32.const 42)
+ )
+(export "" 2))`, imports).exports[""];
+assertEq(f(0), 42);
+assertEq(called, -1);
+assertEq(f(1), 42);
+assertEq(called, 0);
+
+// br/br_if and loop
+wasmFullPass(`(module (func (param i32) (result i32) (loop $out $in i32 (br $out (get_local 0)))) (export "run" 0))`, 1, {}, 1);
+wasmFullPass(`(module (func (param i32) (result i32) (loop $in (br 1 (get_local 0)))) (export "run" 0))`, 1, {}, 1);
+wasmFullPass(`(module (func (param i32) (result i32) (block $out i32 (loop $in i32 (br $out (get_local 0))))) (export "run" 0))`, 1, {}, 1);
+
+wasmFailValidateText(`(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))`, /unused values not explicitly dropped by end of block/);
+
+wasmFullPass(`(module
+ (func
+ (result i32)
+ (local i32)
+ (block $out i32
+ (loop $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 "run" 0))`, 7);
+
+wasmFullPass(`(module
+ (func
+ (result i32)
+ (local i32)
+ (block $out i32
+ (loop $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 "run" 0))`, 7);
+
+// ----------------------------------------------------------------------------
+// loop
+
+wasmFailValidateText('(module (func (loop (br 2))))', DEPTH_OUT_OF_BOUNDS);
+
+wasmFailValidateText('(module (func (result i32) (drop (loop (i32.const 2))) (i32.const 1)) (export "" 0))', /unused values not explicitly dropped by end of block/);
+wasmFullPass('(module (func (loop)) (export "run" 0))', undefined);
+wasmFullPass('(module (func (result i32) (loop (drop (i32.const 2))) (i32.const 1)) (export "run" 0))', 1);
+
+wasmFullPass('(module (func (loop (br 1))) (export "run" 0))', undefined);
+wasmFullPass('(module (func (loop $a (br 1))) (export "run" 0))', undefined);
+wasmFullPass('(module (func (loop $a (br_if $a (i32.const 0)))) (export "run" 0))', undefined);
+wasmFullPass('(module (func (loop $a $b (br $a))) (export "run" 0))', undefined);
+wasmFullPass('(module (func (block $a (loop $b (br $a)))) (export "run" 0))', undefined);
+wasmFullPass('(module (func (result i32) (loop i32 (i32.const 1))) (export "run" 0))', 1);
+
+wasmFullPass(`(module (func (result i32) (local i32)
+ (loop
+ $break $continue
+ (if
+ (i32.gt_u (get_local 0) (i32.const 5))
+ (br $break)
+ )
+ (set_local 0 (i32.add (get_local 0) (i32.const 1)))
+ (br $continue)
+ )
+ (return (get_local 0))
+) (export "run" 0))`, 6);
+
+wasmFullPass(`(module (func (result i32) (local i32)
+ (block
+ $break
+ (loop
+ $continue
+ (if
+ (i32.gt_u (get_local 0) (i32.const 5))
+ (br $break)
+ )
+ (set_local 0 (i32.add (get_local 0) (i32.const 1)))
+ (br $continue)
+ )
+ )
+ (return (get_local 0))
+) (export "run" 0))`, 6);
+
+wasmFullPass(`(module (func (result i32) (local i32)
+ (loop
+ $break $continue
+ (br_if
+ $break
+ (i32.gt_u (get_local 0) (i32.const 5))
+ )
+ (set_local 0 (i32.add (get_local 0) (i32.const 1)))
+ (br $continue)
+ )
+ (return (get_local 0))
+) (export "run" 0))`, 6);
+
+wasmFullPass(`(module (func (result i32) (local i32)
+ (block
+ $break
+ (loop
+ $continue
+ (br_if
+ $break
+ (i32.gt_u (get_local 0) (i32.const 5))
+ )
+ (set_local 0 (i32.add (get_local 0) (i32.const 1)))
+ (br $continue)
+ )
+ )
+ (return (get_local 0))
+) (export "run" 0))`, 6);
+
+wasmFullPass(`(module (func (result i32) (local i32)
+ (loop
+ $break $continue
+ (set_local 0 (i32.add (get_local 0) (i32.const 1)))
+ (br_if
+ $continue
+ (i32.le_u (get_local 0) (i32.const 5))
+ )
+ )
+ (return (get_local 0))
+) (export "run" 0))`, 6);
+
+wasmFullPass(`(module (func (result i32) (local i32)
+ (block
+ $break
+ (loop
+ $continue
+ (set_local 0 (i32.add (get_local 0) (i32.const 1)))
+ (br_if
+ $continue
+ (i32.le_u (get_local 0) (i32.const 5))
+ )
+ )
+ )
+ (return (get_local 0))
+) (export "run" 0))`, 6);
+
+wasmFullPass(`(module (func (result i32) (local i32)
+ (loop
+ $break $continue
+ (br_if
+ $break
+ (i32.gt_u (get_local 0) (i32.const 5))
+ )
+ (set_local 0 (i32.add (get_local 0) (i32.const 1)))
+ (loop
+ (br $continue)
+ )
+ (return (i32.const 42))
+ )
+ (return (get_local 0))
+) (export "run" 0))`, 6);
+
+wasmFullPass(`(module (func (result i32) (local i32)
+ (block
+ $break
+ (loop
+ $continue
+ (br_if
+ $break
+ (i32.gt_u (get_local 0) (i32.const 5))
+ )
+ (set_local 0 (i32.add (get_local 0) (i32.const 1)))
+ (loop
+ (br $continue)
+ )
+ (return (i32.const 42))
+ )
+ )
+ (return (get_local 0))
+) (export "run" 0))`, 6);
+
+wasmFullPass(`(module (func (result i32) (local i32)
+ (loop
+ $break $continue
+ (set_local 0 (i32.add (get_local 0) (i32.const 1)))
+ (loop
+ (br_if
+ $continue
+ (i32.le_u (get_local 0) (i32.const 5))
+ )
+ )
+ (br $break)
+ )
+ (return (get_local 0))
+) (export "run" 0))`, 6);
+
+wasmFullPass(`(module (func (result i32) (local i32)
+ (block
+ $break
+ (loop
+ $continue
+ (set_local 0 (i32.add (get_local 0) (i32.const 1)))
+ (loop
+ (br_if
+ $continue
+ (i32.le_u (get_local 0) (i32.const 5))
+ )
+ )
+ (br $break)
+ )
+ )
+ (return (get_local 0))
+) (export "run" 0))`, 6);
+
+// ----------------------------------------------------------------------------
+// br_table
+
+wasmFailValidateText('(module (func (br_table 1 (i32.const 0))))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (br_table 1 0 (i32.const 0))))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (br_table 0 1 (i32.const 0))))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (block (br_table 2 0 (i32.const 0)))))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (block (br_table 0 2 (i32.const 0)))))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (block (br_table 0 (f32.const 0)))))', mismatchError("f32", "i32"));
+wasmFailValidateText('(module (func (loop (br_table 2 0 (i32.const 0)))))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (loop (br_table 0 2 (i32.const 0)))))', DEPTH_OUT_OF_BOUNDS);
+wasmFailValidateText('(module (func (loop (br_table 0 (f32.const 0)))))', mismatchError("f32", "i32"));
+
+wasmFullPass(`(module (func (result i32) (param i32)
+ (block $default
+ (br_table $default (get_local 0))
+ (return (i32.const 0))
+ )
+ (return (i32.const 1))
+) (export "run" 0))`, 1);
+
+wasmFullPass(`(module (func (result i32) (param i32)
+ (block $default
+ (br_table $default (return (i32.const 1)))
+ (return (i32.const 0))
+ )
+ (return (i32.const 2))
+) (export "run" 0))`, 1);
+
+wasmFullPass(`(module (func (result i32) (param i32)
+ (block $outer
+ (block $inner
+ (br_table $inner (get_local 0))
+ (return (i32.const 0))
+ )
+ (return (i32.const 1))
+ )
+ (return (i32.const 2))
+) (export "run" 0))`, 1);
+
+var f = wasmEvalText(`(module (func (result i32) (param i32)
+ (block $0
+ (block $1
+ (block $2
+ (block $default
+ (br_table $0 $1 $2 $default (get_local 0))
+ )
+ (return (i32.const -1))
+ )
+ (return (i32.const 2))
+ )
+ )
+ (return (i32.const 0))
+) (export "" 0))`).exports[""];
+
+assertEq(f(-2), -1);
+assertEq(f(-1), -1);
+assertEq(f(0), 0);
+assertEq(f(1), 0);
+assertEq(f(2), 2);
+assertEq(f(3), -1);
+
+// br_table with values
+wasmFailValidateText('(module (func (result i32) (block (br_table 0 (i32.const 0)))))', mismatchError("void", "i32"));
+wasmFailValidateText('(module (func (result i32) (block i32 (br_table 0 (f32.const 0) (i32.const 0)))))', mismatchError("f32", "i32"));
+
+wasmFailValidateText(`(module
+ (func
+ (result i32)
+ (block $outer f32
+ (block $inner f32
+ (br_table $outer $inner (f32.const 13.37) (i32.const 1))
+ )
+ (br $outer (i32.const 42))
+ )
+ )
+(export "" 0))`, mismatchError("i32", "f32"));
+
+wasmFullPass(`(module (func (result i32) (block $default i32 (br_table $default (i32.const 42) (i32.const 1)))) (export "run" 0))`, 42);
+
+var f = wasmEvalText(`(module (func (param i32) (result i32)
+ (i32.add
+ (block $1 i32
+ (drop (block $0 i32
+ (drop (block $default i32
+ (br_table $0 $1 $default (get_local 0) (get_local 0))
+ ))
+ (tee_local 0 (i32.mul (i32.const 2) (get_local 0)))
+ ))
+ (tee_local 0 (i32.add (i32.const 4) (get_local 0)))
+ )
+ (i32.const 1)
+ )
+ ) (export "" 0))`).exports[""];
+
+assertEq(f(0), 5);
+assertEq(f(1), 2);
+assertEq(f(2), 9);
+assertEq(f(3), 11);
+assertEq(f(4), 13);
+
+// ----------------------------------------------------------------------------
+// unreachable
+
+const UNREACHABLE = /unreachable/;
+assertErrorMessage(wasmEvalText(`(module (func (unreachable)) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
+assertErrorMessage(wasmEvalText(`(module (func (if (unreachable) (i32.const 0))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
+assertErrorMessage(wasmEvalText(`(module (func (block (br_if 0 (unreachable)))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
+assertErrorMessage(wasmEvalText(`(module (func (block (br_table 0 (unreachable)))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
+assertErrorMessage(wasmEvalText(`(module (func (result i32) (i32.add (i32.const 0) (unreachable))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);