diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index c33b647953ec..dfc0a0173eae 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -6500,9 +6500,7 @@ CheckSwitch(FunctionValidator& f, ParseNode* switchStmt) if (!f.encoder().writeExpr(Expr::BrTable)) return false; - // Write default depth and number of cases (tableLength - 1 + 1 (default)). - if (!f.encoder().writeVarU32(defaultDepth)) - return false; + // Write the number of cases (tableLength - 1 + 1 (default)). if (!f.encoder().writeVarU32(tableLength)) return false; @@ -6514,6 +6512,10 @@ CheckSwitch(FunctionValidator& f, ParseNode* switchStmt) return false; } + // Write the default depth. + if (!f.encoder().writeVarU32(defaultDepth)) + return false; + // Subtract lowest case value, so that all the cases start from 0. if (low) { if (!f.encoder().writeExpr(Expr::I32Sub)) diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index 083adcec1eaa..0987db301f81 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -416,7 +416,7 @@ DecodeBr(FunctionDecoder& f, ExprType expected) { uint32_t relativeDepth; if (!f.d().readVarU32(&relativeDepth)) - return false; + return f.fail("expected relative depth"); if (!f.isLabelInBounds(relativeDepth)) return f.fail("branch depth exceeds current nesting level"); @@ -429,7 +429,7 @@ DecodeBrIf(FunctionDecoder& f, ExprType expected) { uint32_t relativeDepth; if (!f.d().readVarU32(&relativeDepth)) - return false; + return f.fail("expected relative depth"); if (!f.isLabelInBounds(relativeDepth)) return f.fail("branch depth exceeds current nesting level"); @@ -438,6 +438,32 @@ DecodeBrIf(FunctionDecoder& f, ExprType expected) DecodeExpr(f, ExprType::I32); } +static bool +DecodeBrTable(FunctionDecoder& f) +{ + uint32_t tableLength; + if (!f.d().readVarU32(&tableLength)) + return false; + if (tableLength > MaxBrTableElems) + return f.fail("too many br_table entries"); + + for (uint32_t i = 0; i < tableLength; i++) { + uint32_t depth; + if (!f.d().readVarU32(&depth)) + return f.fail("missing br_table entry"); + if (!f.isLabelInBounds(depth)) + return f.fail("branch depth exceeds current nesting level"); + } + + uint32_t defaultDepth; + if (!f.d().readVarU32(&defaultDepth)) + return f.fail("expected default relative depth"); + if (!f.isLabelInBounds(defaultDepth)) + return f.fail("branch depth exceeds current nesting level"); + + return DecodeExpr(f, ExprType::I32); +} + static bool DecodeReturn(FunctionDecoder& f) { @@ -671,6 +697,8 @@ DecodeExpr(FunctionDecoder& f, ExprType expected) return DecodeBr(f, expected); case Expr::BrIf: return DecodeBrIf(f, expected); + case Expr::BrTable: + return DecodeBrTable(f); case Expr::Return: return DecodeReturn(f); default: diff --git a/js/src/asmjs/WasmBinary.h b/js/src/asmjs/WasmBinary.h index 389d8cd99797..d85157419339 100644 --- a/js/src/asmjs/WasmBinary.h +++ b/js/src/asmjs/WasmBinary.h @@ -64,7 +64,7 @@ enum class Expr : uint16_t // Calls Call = 0x12, CallIndirect = 0x13, - CallImport = 0x09, + CallImport = 0x1f, // Constants and calls I32Const = 0x0a, @@ -827,6 +827,7 @@ static const unsigned MaxImports = 4 * 1024; static const unsigned MaxExports = 4 * 1024; static const unsigned MaxTableElems = 128 * 1024; static const unsigned MaxArgsPerFunc = 4 * 1024; +static const unsigned MaxBrTableElems = 4 * 1024; } // namespace wasm } // namespace js diff --git a/js/src/asmjs/WasmIonCompile.cpp b/js/src/asmjs/WasmIonCompile.cpp index f2f015c89ec3..41855a983b15 100644 --- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -2524,16 +2524,8 @@ EmitIfElse(FunctionCompiler& f, bool hasElse, ExprType expected, MDefinition** d static bool EmitBrTable(FunctionCompiler& f) { - uint32_t defaultDepth = f.readVarU32(); uint32_t numCases = f.readVarU32(); - // Empty table - if (!numCases) { - MDefinition* _; - return EmitExpr(f, ExprType::I32, &_) && - f.br(defaultDepth); - } - BlockVector cases; if (!cases.resize(numCases)) return false; @@ -2545,10 +2537,16 @@ EmitBrTable(FunctionCompiler& f) for (size_t i = 0; i < numCases; i++) depths[i] = f.readVarU32(); + uint32_t defaultDepth = f.readVarU32(); + MDefinition* index; if (!EmitExpr(f, ExprType::I32, &index)) return false; + // Empty table + if (!numCases) + return f.br(defaultDepth); + MBasicBlock* switchBlock; if (!f.startSwitch(index, numCases, &switchBlock)) return false; diff --git a/js/src/asmjs/WasmText.cpp b/js/src/asmjs/WasmText.cpp index 59c1e5555e44..378894d9f8b9 100644 --- a/js/src/asmjs/WasmText.cpp +++ b/js/src/asmjs/WasmText.cpp @@ -3154,10 +3154,9 @@ class Resolver return ref.name().empty() || resolveRef(varMap_, ref); } bool resolveTarget(WasmRef& ref) { - for (size_t i = targetStack_.length(); i > 0; i--) { - uint32_t targetIndex = i - 1; - if (targetStack_[targetIndex] == ref.name()) { - ref.setIndex(targetIndex); + for (size_t i = 0, e = targetStack_.length(); i < e; i++) { + if (targetStack_[e - i - 1] == ref.name()) { + ref.setIndex(i); return true; } } @@ -3659,9 +3658,6 @@ EncodeBranchTable(Encoder& e, WasmAstBranchTable& bt) if (!e.writeExpr(Expr::BrTable)) return false; - if (!e.writeVarU32(bt.def().index())) - return false; - if (!e.writeVarU32(bt.table().length())) return false; @@ -3670,6 +3666,9 @@ EncodeBranchTable(Encoder& e, WasmAstBranchTable& bt) return false; } + if (!e.writeVarU32(bt.def().index())) + return false; + return EncodeExpr(e, bt.index()); } diff --git a/js/src/jit-test/tests/wasm/basic-control-flow.js b/js/src/jit-test/tests/wasm/basic-control-flow.js index 14774b9cab6d..a9247fef8abd 100644 --- a/js/src/jit-test/tests/wasm/basic-control-flow.js +++ b/js/src/jit-test/tests/wasm/basic-control-flow.js @@ -147,33 +147,266 @@ assertThrowsInstanceOf(() => wasmEvalText('(module (func (return (i32.const 1))) // ---------------------------------------------------------------------------- // br / br_if -wasmTextToBinary('(module (func (br 0)))'); -wasmTextToBinary('(module (func (block (br 0))))'); -wasmTextToBinary('(module (func (block $l (br $l))))'); -wasmTextToBinary('(module (func (br_if 0 (i32.const 0))))'); -wasmTextToBinary('(module (func (result i32) (block (br_if 0 (i32.const 1)))))'); -wasmTextToBinary('(module (func (result i32) (block $l (br_if $l (i32.const 1)))))'); - -wasmTextToBinary('(module (func (block $l (block $m (br $l)))))'); -wasmTextToBinary('(module (func (block $l (block $m (br $m)))))'); - -assertErrorMessage(() => wasmEvalText('(module (func (br 1)))'), TypeError, /branch depth exceeds current nesting level/); -assertErrorMessage(() => wasmEvalText('(module (func (br_if 1 (i32.const 0))))'), TypeError, /branch depth exceeds current nesting level/); 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/; + +assertErrorMessage(() => wasmEvalText('(module (func (br 0)))'), TypeError, DEPTH_OUT_OF_BOUNDS); +assertErrorMessage(() => wasmEvalText('(module (func (block (br 1))))'), TypeError, DEPTH_OUT_OF_BOUNDS); +assertErrorMessage(() => wasmEvalText('(module (func (loop (br 2))))'), TypeError, DEPTH_OUT_OF_BOUNDS); + +assertErrorMessage(() => wasmEvalText('(module (func (br_if 0 (i32.const 0))))'), TypeError, DEPTH_OUT_OF_BOUNDS); +assertErrorMessage(() => wasmEvalText('(module (func (block (br_if 1 (i32.const 0)))))'), TypeError, DEPTH_OUT_OF_BOUNDS); +assertErrorMessage(() => wasmEvalText('(module (func (loop (br_if 2 (i32.const 0)))))'), TypeError, DEPTH_OUT_OF_BOUNDS); + +assertErrorMessage(() => wasmEvalText(`(module (func (result i32) + (block + (if_else + (br 0) + (i32.const 0) + (i32.const 2) + ) + ) +) (export "" 0))`), TypeError, mismatchError("void", "i32")); + +assertErrorMessage(() => wasmEvalText(`(module (func (block $out (br_if $out (br 0)))) (export "" 0))`), TypeError, mismatchError("void", "i32")); + +assertEq(wasmEvalText('(module (func (block (br 0))) (export "" 0))')(), undefined); +assertEq(wasmEvalText('(module (func (block $l (br $l))) (export "" 0))')(), undefined); + +assertEq(wasmEvalText('(module (func (block (block (br 1)))) (export "" 0))')(), undefined); +assertEq(wasmEvalText('(module (func (block $l (block (br $l)))) (export "" 0))')(), undefined); + +assertEq(wasmEvalText('(module (func (block $l (block $m (br $l)))) (export "" 0))')(), undefined); +assertEq(wasmEvalText('(module (func (block $l (block $m (br $m)))) (export "" 0))')(), undefined); + +assertEq(wasmEvalText(`(module (func (result i32) + (block + (br 0) + (return (i32.const 0)) + ) + (return (i32.const 1)) +) (export "" 0))`)(), 1); + +assertEq(wasmEvalText(`(module (func (result i32) + (block + (block + (br 0) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) + (return (i32.const 2)) +) (export "" 0))`)(), 1); + +assertEq(wasmEvalText(`(module (func (result i32) + (block $outer + (block $inner + (br $inner) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) + (return (i32.const 2)) +) (export "" 0))`)(), 1); + +// TODO enable once bug 1253572 is fixed. +/* +var notcalled = false; +var called = false; +var imports = { + notcalled() {notcalled = true}, + called() {called = true} +}; +assertEq(wasmEvalText(`(module (import "inc" "") (func + (block + (return (br 0)) + (call_import 0) + ) + (call_import 1) +) (export "" 0))`)(imports), undefined); +assertEq(notcalled, false); +assertEq(called, true); + +assertEq(wasmEvalText(`(module (func + (block + (i32.add + (i32.const 0) + (return (br 0)) + ) + ) + (return) +) (export "" 0))`)(), 1); +*/ + +assertEq(wasmEvalText(`(module (func (result i32) + (block + (if_else + (i32.const 1) + (br 0) + (return (i32.const 0)) + ) + ) + (return (i32.const 1)) +) (export "" 0))`)(), 1); + +assertEq(wasmEvalText('(module (func (block (br_if 0 (i32.const 1)))) (export "" 0))')(), undefined); +assertEq(wasmEvalText('(module (func (block (br_if 0 (i32.const 0)))) (export "" 0))')(), undefined); +assertEq(wasmEvalText('(module (func (block $l (br_if $l (i32.const 1)))) (export "" 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))`); + +assertEq(isNonZero(0), 0); +assertEq(isNonZero(1), 1); +assertEq(isNonZero(-1), 1); + // ---------------------------------------------------------------------------- // loop -wasmTextToBinary('(module (func (loop (br 0))))'); -wasmTextToBinary('(module (func (loop (br 1))))'); -wasmTextToBinary('(module (func (loop $a (br $a))))'); -wasmTextToBinary('(module (func (loop $a $b (br $a))))'); -wasmTextToBinary('(module (func (loop $a $b (br $b))))'); -wasmTextToBinary('(module (func (loop $a $a (br $a))))'); +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); + +assertEq(wasmEvalText('(module (func (loop (br 1))) (export "" 0))')(), undefined); +assertEq(wasmEvalText('(module (func (loop $a (br $a))) (export "" 0))')(), undefined); +assertEq(wasmEvalText('(module (func (loop $a $b (br $a))) (export "" 0))')(), undefined); + +assertEq(wasmEvalText(`(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 "" 0))`)(), 6); + +assertEq(wasmEvalText(`(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 "" 0))`)(), 6); + +assertEq(wasmEvalText(`(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 "" 0))`)(), 6); + +assertEq(wasmEvalText(`(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 "" 0))`)(), 6); + +assertEq(wasmEvalText(`(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 "" 0))`)(), 6); // ---------------------------------------------------------------------------- -// tableswitch +// br_table + +assertErrorMessage(() => wasmEvalText('(module (func (br_table (i32.const 0) (table) (br 0))))'), TypeError, DEPTH_OUT_OF_BOUNDS); + +assertErrorMessage(() => wasmEvalText('(module (func (block (br_table (i32.const 0) (table (br 2)) (br 0)))))'), TypeError, DEPTH_OUT_OF_BOUNDS); + +assertErrorMessage(() => wasmEvalText('(module (func (block (br_table (f32.const 0) (table) (br 0)))))'), TypeError, mismatchError("f32", "i32")); + +assertEq(wasmEvalText(`(module (func (result i32) (param i32) + (block $default + (br_table (get_local 0) (table) (br $default)) + (return (i32.const 0)) + ) + (return (i32.const 1)) +) (export "" 0))`)(), 1); + +assertEq(wasmEvalText(`(module (func (result i32) (param i32) + (block $default + (br_table (return (i32.const 1)) (table) (br $default)) + (return (i32.const 0)) + ) + (return (i32.const 2)) +) (export "" 0))`)(), 1); + +assertEq(wasmEvalText(`(module (func (result i32) (param i32) + (block $outer + (block $inner + (br_table (get_local 0) (table) (br $inner)) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) + (return (i32.const 2)) +) (export "" 0))`)(), 1); + +var f = wasmEvalText(`(module (func (result i32) (param i32) + (block $0 + (block $1 + (block $2 + (block $default + (br_table + (get_local 0) + (table (br $0) (br $1) (br $2)) + (br $default) + ) + ) + (return (i32.const -1)) + ) + (return (i32.const 2)) + ) + ) + (return (i32.const 0)) +) (export "" 0))`); + +assertEq(f(-2), -1); +assertEq(f(-1), -1); +assertEq(f(0), 0); +assertEq(f(1), 0); +assertEq(f(2), 2); +assertEq(f(3), -1); -wasmTextToBinary('(module (func (param i32) (block $b1 (block $b2 (br_table (get_local 0) (table (br $b1) (br $b2)) (br $b2))))))'); -wasmTextToBinary('(module (func (param i32) (block $b1 (br_table (get_local 0) (table) (br $b1)))))'); diff --git a/js/src/jit-test/tests/wasm/timeout-no-signals.js b/js/src/jit-test/tests/wasm/timeout-no-signals.js new file mode 100644 index 000000000000..08107a04d5ee --- /dev/null +++ b/js/src/jit-test/tests/wasm/timeout-no-signals.js @@ -0,0 +1,7 @@ +// |jit-test| exitstatus: 6; +load(libdir + "wasm.js"); + +setJitCompilerOption("signals.enable", 0); +timeout(1); +wasmEvalText('(module (func (loop (br 0))) (export "" 0))')(); +assertEq(true, false); diff --git a/js/src/jit-test/tests/wasm/timeout.js b/js/src/jit-test/tests/wasm/timeout.js new file mode 100644 index 000000000000..b18bdb4e5350 --- /dev/null +++ b/js/src/jit-test/tests/wasm/timeout.js @@ -0,0 +1,6 @@ +// |jit-test| exitstatus: 6; +load(libdir + "wasm.js"); + +timeout(1); +wasmEvalText('(module (func (loop (br 0))) (export "" 0))')(); +assertEq(true, false);