Bug 1246116: Wire BrTable in wasm and add a bunch of tests; r=sunfish

MozReview-Commit-ID: 7sw9zUD44OW
This commit is contained in:
Benjamin Bouvier 2016-03-04 12:35:34 +01:00
Родитель d95ecfa483
Коммит 577d0af387
8 изменённых файлов: 316 добавлений и 42 удалений

Просмотреть файл

@ -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))

Просмотреть файл

@ -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:

Просмотреть файл

@ -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

Просмотреть файл

@ -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;

Просмотреть файл

@ -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());
}

Просмотреть файл

@ -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)))))');

Просмотреть файл

@ -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);

Просмотреть файл

@ -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);