зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
ebb7081cb2
|
@ -0,0 +1,226 @@
|
|||
// |jit-test| skip-if: !wasmReftypesEnabled()
|
||||
|
||||
let ins
|
||||
= wasmEvalText(
|
||||
`(module
|
||||
(table 8 anyref) ;; table 0
|
||||
(table $t 10 anyref) ;; table 1
|
||||
|
||||
;; fill/get for table 0, referenced implicitly
|
||||
(func (export "fill0") (param $i i32) (param $r anyref) (param $n i32)
|
||||
(table.fill (local.get $i) (local.get $r) (local.get $n))
|
||||
)
|
||||
(func (export "get0") (param $i i32) (result anyref)
|
||||
(table.get (local.get $i))
|
||||
)
|
||||
|
||||
;; fill/get for table 1, referenced explicitly
|
||||
(func (export "fill1") (param $i i32) (param $r anyref) (param $n i32)
|
||||
(table.fill $t (local.get $i) (local.get $r) (local.get $n))
|
||||
)
|
||||
(func (export "get1") (param $i i32) (result anyref)
|
||||
(table.get $t (local.get $i))
|
||||
)
|
||||
)`);
|
||||
|
||||
function Obj(n) {
|
||||
this.n = n;
|
||||
}
|
||||
|
||||
function mkObj(n) {
|
||||
return new Obj(n);
|
||||
}
|
||||
|
||||
const obj1 = mkObj(1);
|
||||
const obj2 = mkObj(2);
|
||||
const obj3 = mkObj(3);
|
||||
const obj4 = mkObj(4);
|
||||
const obj5 = mkObj(5);
|
||||
const obj6 = mkObj(6);
|
||||
const obj7 = mkObj(7);
|
||||
const obj8 = mkObj(8);
|
||||
|
||||
// An initial test to ascertain that tables 0 and 1 are independent
|
||||
|
||||
// Fill in table 0, then check it.
|
||||
assertEq(ins.exports.fill0(2, obj7, 5), undefined)
|
||||
assertEq(ins.exports.fill0(1, obj8, 3), undefined);
|
||||
|
||||
function check_table0() {
|
||||
assertEq(ins.exports.get0(0), null);
|
||||
assertEq(ins.exports.get0(1), obj8);
|
||||
assertEq(ins.exports.get0(2), obj8);
|
||||
assertEq(ins.exports.get0(3), obj8);
|
||||
assertEq(ins.exports.get0(4), obj7);
|
||||
assertEq(ins.exports.get0(5), obj7);
|
||||
assertEq(ins.exports.get0(6), obj7);
|
||||
assertEq(ins.exports.get0(7), null);
|
||||
}
|
||||
|
||||
// Check that table 0 has the expected content.
|
||||
check_table0();
|
||||
|
||||
// Check that messing with table 0 above hasn't changed table 1.
|
||||
for (let i = 0; i < 10; i++) {
|
||||
assertEq(ins.exports.get1(i), null);
|
||||
}
|
||||
|
||||
|
||||
// Now a bunch of tests involving only table 1.
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(2, obj1, 3), undefined);
|
||||
assertEq(ins.exports.get1(1), null);
|
||||
assertEq(ins.exports.get1(2), obj1);
|
||||
assertEq(ins.exports.get1(3), obj1);
|
||||
assertEq(ins.exports.get1(4), obj1);
|
||||
assertEq(ins.exports.get1(5), null);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(4, obj2, 2), undefined);
|
||||
assertEq(ins.exports.get1(3), obj1);
|
||||
assertEq(ins.exports.get1(4), obj2);
|
||||
assertEq(ins.exports.get1(5), obj2);
|
||||
assertEq(ins.exports.get1(6), null);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(4, obj3, 0), undefined);
|
||||
assertEq(ins.exports.get1(3), obj1);
|
||||
assertEq(ins.exports.get1(4), obj2);
|
||||
assertEq(ins.exports.get1(5), obj2);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(8, obj4, 2), undefined);
|
||||
assertEq(ins.exports.get1(7), null);
|
||||
assertEq(ins.exports.get1(8), obj4);
|
||||
assertEq(ins.exports.get1(9), obj4);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(9, null, 1), undefined);
|
||||
assertEq(ins.exports.get1(8), obj4);
|
||||
assertEq(ins.exports.get1(9), null);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(10, obj5, 0), undefined);
|
||||
assertEq(ins.exports.get1(9), null);
|
||||
|
||||
// Partly outside the table
|
||||
assertErrorMessage(() => ins.exports.fill1(8, obj6, 3),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
assertEq(ins.exports.get1(7), null);
|
||||
assertEq(ins.exports.get1(8), obj6);
|
||||
assertEq(ins.exports.get1(9), obj6);
|
||||
|
||||
|
||||
// Boundary tests on table 1: at the edge of the table.
|
||||
|
||||
// Length-zero fill1 at the edge of the table must succeed
|
||||
assertEq(ins.exports.fill1(10, null, 0), undefined);
|
||||
|
||||
// Length-one fill1 at the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(10, null, 1),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
// Length-more-than-one fill1 at the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(10, null, 2),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
|
||||
// Boundary tests on table 1: beyond the edge of the table:
|
||||
|
||||
// Length-zero fill1 beyond the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(11, null, 0),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
// Length-one fill1 beyond the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(11, null, 1),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
// Length-more-than-one fill1 beyond the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(11, null, 2),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
|
||||
// Type errors. Required sig is: (i32, anyref, i32) -> void
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t 10 anyref)
|
||||
(func $expected-3-args-got-0
|
||||
(table.fill $t)
|
||||
))`),
|
||||
WebAssembly.CompileError, /popping value from empty stack/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t 10 anyref)
|
||||
(func $expected-3-args-got-1
|
||||
(table.fill $t (i32.const 0))
|
||||
))`),
|
||||
WebAssembly.CompileError, /popping value from empty stack/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t 10 anyref)
|
||||
(func $expected-3-args-got-2
|
||||
(table.fill $t (ref.null) (i32.const 0))
|
||||
))`),
|
||||
WebAssembly.CompileError, /popping value from empty stack/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t 10 anyref)
|
||||
(func $argty-1-wrong
|
||||
(table.fill $t (i32.const 0) (ref.null) (f64.const 0))
|
||||
))`),
|
||||
WebAssembly.CompileError,
|
||||
/type mismatch: expression has type f64 but expected i32/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t 10 anyref)
|
||||
(func $argty-2-wrong
|
||||
(table.fill $t (i32.const 0) (f32.const 0) (i32.const 0))
|
||||
))`),
|
||||
WebAssembly.CompileError,
|
||||
/type mismatch: expression has type f32 but expected anyref/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t 10 anyref)
|
||||
(func $argty-3-wrong
|
||||
(table.fill $t (i64.const 0) (ref.null) (i32.const 0))
|
||||
))`),
|
||||
WebAssembly.CompileError,
|
||||
/type mismatch: expression has type i64 but expected i32/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t 10 anyref)
|
||||
(func $retty-wrong (result i32)
|
||||
(table.fill $t (i32.const 0) (ref.null) (i32.const 0))
|
||||
))`),
|
||||
WebAssembly.CompileError,
|
||||
/popping value from empty stack/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t 0 funcref)
|
||||
(func $tables-of-funcref-not-allowed (param $r anyref)
|
||||
(table.fill $t (i32.const 1) (local.get $r) (i32.const 1))
|
||||
))`),
|
||||
WebAssembly.CompileError, /table.fill only on tables of anyref/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t1 1 anyref)
|
||||
(table $t2 1 funcref)
|
||||
(func $tables-of-funcref-not-allowed-2 (param $r anyref)
|
||||
(table.fill $t2 (i32.const 0) (local.get $r) (i32.const 1))
|
||||
))`),
|
||||
WebAssembly.CompileError, /table.fill only on tables of anyref/);
|
||||
|
||||
|
||||
// Following all the above tests on table 1, check table 0 hasn't changed.
|
||||
check_table0();
|
|
@ -430,6 +430,7 @@ enum class AstExprKind {
|
|||
StructNarrow,
|
||||
#endif
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
TableFill,
|
||||
TableGet,
|
||||
TableGrow,
|
||||
TableSet,
|
||||
|
@ -919,6 +920,25 @@ class AstMemOrTableInit : public AstExpr {
|
|||
#endif
|
||||
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
class AstTableFill : public AstExpr {
|
||||
AstRef targetTable_;
|
||||
AstExpr* start_;
|
||||
AstExpr* val_;
|
||||
AstExpr* len_;
|
||||
|
||||
public:
|
||||
static const AstExprKind Kind = AstExprKind::TableFill;
|
||||
explicit AstTableFill(AstRef targetTable,
|
||||
AstExpr* start, AstExpr* val, AstExpr* len)
|
||||
: AstExpr(Kind, ExprType::Void), targetTable_(targetTable),
|
||||
start_(start), val_(val), len_(len) {}
|
||||
|
||||
AstRef& targetTable() { return targetTable_; }
|
||||
AstExpr& start() const { return *start_; }
|
||||
AstExpr& val() const { return *val_; }
|
||||
AstExpr& len() const { return *len_; }
|
||||
};
|
||||
|
||||
class AstTableGet : public AstExpr {
|
||||
AstRef targetTable_;
|
||||
AstExpr* index_;
|
||||
|
|
|
@ -6850,6 +6850,7 @@ class BaseCompiler final : public BaseCompilerInterface {
|
|||
MOZ_MUST_USE bool emitMemFill();
|
||||
MOZ_MUST_USE bool emitMemOrTableInit(bool isMem);
|
||||
#endif
|
||||
MOZ_MUST_USE bool emitTableFill();
|
||||
MOZ_MUST_USE bool emitTableGet();
|
||||
MOZ_MUST_USE bool emitTableGrow();
|
||||
MOZ_MUST_USE bool emitTableSet();
|
||||
|
@ -10332,6 +10333,37 @@ bool BaseCompiler::emitMemOrTableInit(bool isMem) {
|
|||
}
|
||||
#endif
|
||||
|
||||
MOZ_MUST_USE
|
||||
bool BaseCompiler::emitTableFill() {
|
||||
uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
|
||||
|
||||
Nothing nothing;
|
||||
uint32_t tableIndex;
|
||||
if (!iter_.readTableFill(&tableIndex, ¬hing, ¬hing, ¬hing)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (deadCode_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// fill(start:u32, val:ref, len:u32, table:u32) -> u32
|
||||
//
|
||||
// Returns -1 on trap, otherwise 0.
|
||||
pushI32(tableIndex);
|
||||
if (!emitInstanceCall(lineOrBytecode, SASigTableFill,
|
||||
/*pushReturnedValue=*/false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Label ok;
|
||||
masm.branchTest32(Assembler::NotSigned, ReturnReg, ReturnReg, &ok);
|
||||
trap(Trap::ThrowReported);
|
||||
masm.bind(&ok);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE
|
||||
bool BaseCompiler::emitTableGet() {
|
||||
uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
|
||||
|
@ -11565,6 +11597,8 @@ bool BaseCompiler::emitBody() {
|
|||
CHECK_NEXT(emitMemOrTableInit(/*isMem=*/false));
|
||||
#endif // ENABLE_WASM_BULKMEM_OPS
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
case uint32_t(MiscOp::TableFill):
|
||||
CHECK_NEXT(emitTableFill());
|
||||
case uint32_t(MiscOp::TableGrow):
|
||||
CHECK_NEXT(emitTableGrow());
|
||||
case uint32_t(MiscOp::TableSize):
|
||||
|
|
|
@ -121,6 +121,8 @@ const SymbolicAddressSignature SASigTableCopy = {
|
|||
{_PTR, _I32, _I32, _I32, _I32, _I32, _END}};
|
||||
const SymbolicAddressSignature SASigElemDrop = {
|
||||
SymbolicAddress::ElemDrop, _I32, 2, {_PTR, _I32, _END}};
|
||||
const SymbolicAddressSignature SASigTableFill = {
|
||||
SymbolicAddress::TableFill, _I32, 5, {_PTR, _I32, _RoN, _I32, _I32, _END}};
|
||||
const SymbolicAddressSignature SASigTableGet = {
|
||||
SymbolicAddress::TableGet, _PTR, 3, {_PTR, _I32, _I32, _END}};
|
||||
const SymbolicAddressSignature SASigTableGrow = {
|
||||
|
@ -766,6 +768,9 @@ void* wasm::AddressOf(SymbolicAddress imm, ABIFunctionType* abiType) {
|
|||
case SymbolicAddress::ElemDrop:
|
||||
*abiType = Args_General2;
|
||||
return FuncCast(Instance::elemDrop, *abiType);
|
||||
case SymbolicAddress::TableFill:
|
||||
*abiType = Args_General5;
|
||||
return FuncCast(Instance::tableFill, *abiType);
|
||||
case SymbolicAddress::TableInit:
|
||||
*abiType = Args_General6;
|
||||
return FuncCast(Instance::tableInit, *abiType);
|
||||
|
@ -895,6 +900,7 @@ bool wasm::NeedsBuiltinThunk(SymbolicAddress sym) {
|
|||
case SymbolicAddress::MemInit:
|
||||
case SymbolicAddress::TableCopy:
|
||||
case SymbolicAddress::ElemDrop:
|
||||
case SymbolicAddress::TableFill:
|
||||
case SymbolicAddress::TableGet:
|
||||
case SymbolicAddress::TableGrow:
|
||||
case SymbolicAddress::TableInit:
|
||||
|
|
|
@ -58,6 +58,7 @@ extern const SymbolicAddressSignature SASigMemFill;
|
|||
extern const SymbolicAddressSignature SASigMemInit;
|
||||
extern const SymbolicAddressSignature SASigTableCopy;
|
||||
extern const SymbolicAddressSignature SASigElemDrop;
|
||||
extern const SymbolicAddressSignature SASigTableFill;
|
||||
extern const SymbolicAddressSignature SASigTableGet;
|
||||
extern const SymbolicAddressSignature SASigTableGrow;
|
||||
extern const SymbolicAddressSignature SASigTableInit;
|
||||
|
|
|
@ -385,7 +385,7 @@ enum class MiscOp {
|
|||
// Reftypes, per proposal as of February 2019.
|
||||
TableGrow = 0x0f,
|
||||
TableSize = 0x10,
|
||||
// TableFill = 0x11, // reserved
|
||||
TableFill = 0x11,
|
||||
|
||||
// Structure operations. Note, these are unofficial.
|
||||
StructNew = 0x50,
|
||||
|
|
|
@ -1361,6 +1361,8 @@ static const char* ThunkedNativeToDescription(SymbolicAddress func) {
|
|||
return "call to native memory.init function";
|
||||
case SymbolicAddress::TableCopy:
|
||||
return "call to native table.copy function";
|
||||
case SymbolicAddress::TableFill:
|
||||
return "call to native table.fill function";
|
||||
case SymbolicAddress::ElemDrop:
|
||||
return "call to native elem.drop function";
|
||||
case SymbolicAddress::TableGet:
|
||||
|
|
|
@ -851,6 +851,53 @@ Instance::tableInit(Instance* instance, uint32_t dstOffset, uint32_t srcOffset,
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* static */ int32_t /* -1 to signal trap; 0 for ok */
|
||||
Instance::tableFill(Instance* instance, uint32_t start, void* value,
|
||||
uint32_t len, uint32_t tableIndex)
|
||||
{
|
||||
Table& table = *instance->tables()[tableIndex];
|
||||
MOZ_RELEASE_ASSERT(table.kind() == TableKind::AnyRef);
|
||||
|
||||
if (len == 0) {
|
||||
// Even though the length is zero, we must check for a valid offset. But
|
||||
// zero-length operations at the edge of the table are allowed.
|
||||
if (start <= table.length()) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
// Here, we know that |len - 1| cannot underflow.
|
||||
|
||||
bool mustTrap = false;
|
||||
|
||||
// We must write the table until we trap, so we have to deal with
|
||||
// arithmetic overflow in the limit calculation.
|
||||
uint64_t highestOffset = uint64_t(start) + uint64_t(len - 1);
|
||||
if (highestOffset >= table.length()) {
|
||||
// We would write past the end. Compute what we have space for in the
|
||||
// target and make that the new len.
|
||||
uint64_t avail = table.length() < start ? 0 : table.length() - start;
|
||||
MOZ_ASSERT(len > avail);
|
||||
len = uint32_t(avail);
|
||||
mustTrap = true;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
uint32_t index = start + i;
|
||||
MOZ_ASSERT(index < table.length());
|
||||
table.setAnyRef(index, AnyRef::fromCompiledCode(value));
|
||||
}
|
||||
|
||||
if (!mustTrap) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
JSContext* cx = TlsContext.get();
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_TABLE_OUT_OF_BOUNDS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The return convention for tableGet() is awkward but avoids a situation where
|
||||
// Ion code has to hold a value that may or may not be a pointer to GC'd
|
||||
// storage, or where Ion has to pass in a pointer to storage where a return
|
||||
|
|
|
@ -200,6 +200,8 @@ class Instance {
|
|||
uint32_t srcOffset, uint32_t len,
|
||||
uint32_t dstTableIndex, uint32_t srcTableIndex);
|
||||
static int32_t elemDrop(Instance* instance, uint32_t segIndex);
|
||||
static int32_t tableFill(Instance* instance, uint32_t start, void* value,
|
||||
uint32_t len, uint32_t tableIndex);
|
||||
static void* tableGet(Instance* instance, uint32_t index,
|
||||
uint32_t tableIndex);
|
||||
static uint32_t tableGrow(Instance* instance, void* initValue, uint32_t delta,
|
||||
|
|
|
@ -3137,6 +3137,60 @@ static bool EmitMemOrTableInit(FunctionCompiler& f, bool isMem) {
|
|||
// Note, table.{get,grow,set} on table(funcref) are currently rejected by the
|
||||
// verifier.
|
||||
|
||||
static bool EmitTableFill(FunctionCompiler& f) {
|
||||
uint32_t tableIndex;
|
||||
MDefinition *start, *val, *len;
|
||||
if (!f.iter().readTableFill(&tableIndex, &start, &val, &len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (f.inDeadCode()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
|
||||
|
||||
const SymbolicAddressSignature& callee = SASigTableFill;
|
||||
CallCompileState args;
|
||||
if (!f.passInstance(callee.argTypes[0], &args)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f.passArg(start, callee.argTypes[1], &args)) {
|
||||
return false;
|
||||
}
|
||||
if (!f.passArg(val, callee.argTypes[2], &args)) {
|
||||
return false;
|
||||
}
|
||||
if (!f.passArg(len, callee.argTypes[3], &args)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MDefinition* tableIndexArg =
|
||||
f.constant(Int32Value(tableIndex), MIRType::Int32);
|
||||
if (!tableIndexArg) {
|
||||
return false;
|
||||
}
|
||||
if (!f.passArg(tableIndexArg, callee.argTypes[4], &args)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f.finishCall(&args)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MDefinition* ret;
|
||||
if (!f.builtinInstanceMethodCall(callee, lineOrBytecode, args, &ret)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f.checkI32NegativeMeansFailedResult(ret)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool EmitTableGet(FunctionCompiler& f) {
|
||||
uint32_t tableIndex;
|
||||
MDefinition* index;
|
||||
|
@ -3865,6 +3919,8 @@ static bool EmitBodyExprs(FunctionCompiler& f) {
|
|||
CHECK(EmitMemOrTableInit(f, /*isMem=*/false));
|
||||
#endif
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
case uint32_t(MiscOp::TableFill):
|
||||
CHECK(EmitTableFill(f));
|
||||
case uint32_t(MiscOp::TableGrow):
|
||||
CHECK(EmitTableGrow(f));
|
||||
case uint32_t(MiscOp::TableSize):
|
||||
|
|
|
@ -296,6 +296,8 @@ OpKind wasm::Classify(OpBytes op) {
|
|||
case MiscOp::MemInit:
|
||||
case MiscOp::TableInit:
|
||||
WASM_BULK_OP(OpKind::MemOrTableInit);
|
||||
case MiscOp::TableFill:
|
||||
WASM_REF_OP(OpKind::TableFill);
|
||||
case MiscOp::TableGrow:
|
||||
WASM_REF_OP(OpKind::TableGrow);
|
||||
case MiscOp::TableSize:
|
||||
|
|
|
@ -168,6 +168,7 @@ enum class OpKind {
|
|||
DataOrElemDrop,
|
||||
MemFill,
|
||||
MemOrTableInit,
|
||||
TableFill,
|
||||
TableGet,
|
||||
TableGrow,
|
||||
TableSet,
|
||||
|
@ -466,6 +467,8 @@ class MOZ_STACK_CLASS OpIter : private Policy {
|
|||
MOZ_MUST_USE bool readMemOrTableInit(bool isMem, uint32_t* segIndex,
|
||||
uint32_t* dstTableIndex, Value* dst,
|
||||
Value* src, Value* len);
|
||||
MOZ_MUST_USE bool readTableFill(uint32_t* tableIndex, Value* start,
|
||||
Value* val, Value* len);
|
||||
MOZ_MUST_USE bool readTableGet(uint32_t* tableIndex, Value* index);
|
||||
MOZ_MUST_USE bool readTableGrow(uint32_t* tableIndex, Value* initValue,
|
||||
Value* delta);
|
||||
|
@ -1973,6 +1976,38 @@ inline bool OpIter<Policy>::readMemOrTableInit(bool isMem, uint32_t* segIndex,
|
|||
return true;
|
||||
}
|
||||
|
||||
template <typename Policy>
|
||||
inline bool OpIter<Policy>::readTableFill(uint32_t* tableIndex, Value* start,
|
||||
Value* val, Value* len) {
|
||||
MOZ_ASSERT(Classify(op_) == OpKind::TableFill);
|
||||
|
||||
if (!popWithType(ValType::I32, len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!popWithType(ValType::AnyRef, val)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!popWithType(ValType::I32, start)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!readVarU32(tableIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*tableIndex >= env_.tables.length()) {
|
||||
return fail("table index out of range for table.fill");
|
||||
}
|
||||
|
||||
if (env_.tables[*tableIndex].kind != TableKind::AnyRef) {
|
||||
return fail("table.fill only on tables of anyref");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Policy>
|
||||
inline bool OpIter<Policy>::readTableGet(uint32_t* tableIndex, Value* index) {
|
||||
MOZ_ASSERT(Classify(op_) == OpKind::TableGet);
|
||||
|
|
|
@ -146,6 +146,7 @@ class WasmToken {
|
|||
TableInit,
|
||||
#endif
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
TableFill,
|
||||
TableGet,
|
||||
TableGrow,
|
||||
TableSet,
|
||||
|
@ -346,6 +347,7 @@ class WasmToken {
|
|||
case TableInit:
|
||||
#endif
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
case TableFill:
|
||||
case TableGet:
|
||||
case TableGrow:
|
||||
case TableSet:
|
||||
|
@ -2260,6 +2262,9 @@ WasmToken WasmTokenStream::next() {
|
|||
}
|
||||
#endif
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
if (consume(u"fill")) {
|
||||
return WasmToken(WasmToken::TableFill, begin, cur_);
|
||||
}
|
||||
if (consume(u"get")) {
|
||||
return WasmToken(WasmToken::TableGet, begin, cur_);
|
||||
}
|
||||
|
@ -3791,6 +3796,31 @@ static AstMemOrTableInit* ParseMemOrTableInit(WasmParseContext& c,
|
|||
#endif
|
||||
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
static AstTableFill* ParseTableFill(WasmParseContext& c, bool inParens) {
|
||||
// (table.fill table start val len)
|
||||
// (table.fill start val len)
|
||||
|
||||
AstRef targetTable = AstRef(0);
|
||||
c.ts.getIfRef(&targetTable);
|
||||
|
||||
AstExpr* start = ParseExpr(c, inParens);
|
||||
if (!start) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstExpr* val = ParseExpr(c, inParens);
|
||||
if (!val) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstExpr* len = ParseExpr(c, inParens);
|
||||
if (!len) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new (c.lifo) AstTableFill(targetTable, start, val, len);
|
||||
}
|
||||
|
||||
static AstTableGet* ParseTableGet(WasmParseContext& c, bool inParens) {
|
||||
// (table.get table index)
|
||||
// (table.get index)
|
||||
|
@ -4046,6 +4076,8 @@ static AstExpr* ParseExprBody(WasmParseContext& c, WasmToken token,
|
|||
return ParseMemOrTableInit(c, inParens, /*isMem=*/false);
|
||||
#endif
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
case WasmToken::TableFill:
|
||||
return ParseTableFill(c, inParens);
|
||||
case WasmToken::TableGet:
|
||||
return ParseTableGet(c, inParens);
|
||||
case WasmToken::TableGrow:
|
||||
|
@ -5659,6 +5691,11 @@ static bool ResolveMemOrTableInit(Resolver& r, AstMemOrTableInit& s) {
|
|||
#endif
|
||||
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
static bool ResolveTableFill(Resolver& r, AstTableFill& s) {
|
||||
return ResolveExpr(r, s.start()) && ResolveExpr(r, s.val()) &&
|
||||
ResolveExpr(r, s.len()) && r.resolveTable(s.targetTable());
|
||||
}
|
||||
|
||||
static bool ResolveTableGet(Resolver& r, AstTableGet& s) {
|
||||
return ResolveExpr(r, s.index()) && r.resolveTable(s.targetTable());
|
||||
}
|
||||
|
@ -5811,6 +5848,8 @@ static bool ResolveExpr(Resolver& r, AstExpr& expr) {
|
|||
return ResolveMemOrTableInit(r, expr.as<AstMemOrTableInit>());
|
||||
#endif
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
case AstExprKind::TableFill:
|
||||
return ResolveTableFill(r, expr.as<AstTableFill>());
|
||||
case AstExprKind::TableGet:
|
||||
return ResolveTableGet(r, expr.as<AstTableGet>());
|
||||
case AstExprKind::TableGrow:
|
||||
|
@ -6433,6 +6472,12 @@ static bool EncodeMemOrTableInit(Encoder& e, AstMemOrTableInit& s) {
|
|||
#endif
|
||||
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
static bool EncodeTableFill(Encoder& e, AstTableFill& s) {
|
||||
return EncodeExpr(e, s.start()) && EncodeExpr(e, s.val()) &&
|
||||
EncodeExpr(e, s.len()) && e.writeOp(MiscOp::TableFill) &&
|
||||
e.writeVarU32(s.targetTable().index());
|
||||
}
|
||||
|
||||
static bool EncodeTableGet(Encoder& e, AstTableGet& s) {
|
||||
return EncodeExpr(e, s.index()) && e.writeOp(Op::TableGet) &&
|
||||
e.writeVarU32(s.targetTable().index());
|
||||
|
@ -6610,6 +6655,8 @@ static bool EncodeExpr(Encoder& e, AstExpr& expr) {
|
|||
return EncodeMemOrTableInit(e, expr.as<AstMemOrTableInit>());
|
||||
#endif
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
case AstExprKind::TableFill:
|
||||
return EncodeTableFill(e, expr.as<AstTableFill>());
|
||||
case AstExprKind::TableGet:
|
||||
return EncodeTableGet(e, expr.as<AstTableGet>());
|
||||
case AstExprKind::TableGrow:
|
||||
|
|
|
@ -1890,6 +1890,7 @@ enum class SymbolicAddress {
|
|||
MemInit,
|
||||
TableCopy,
|
||||
ElemDrop,
|
||||
TableFill,
|
||||
TableGet,
|
||||
TableGrow,
|
||||
TableInit,
|
||||
|
|
|
@ -870,6 +870,11 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env,
|
|||
}
|
||||
#endif
|
||||
#ifdef ENABLE_WASM_REFTYPES
|
||||
case uint32_t(MiscOp::TableFill): {
|
||||
uint32_t unusedTableIndex;
|
||||
CHECK(iter.readTableFill(&unusedTableIndex,
|
||||
¬hing, ¬hing, ¬hing));
|
||||
}
|
||||
case uint32_t(MiscOp::TableGrow): {
|
||||
uint32_t unusedTableIndex;
|
||||
CHECK(iter.readTableGrow(&unusedTableIndex, ¬hing, ¬hing));
|
||||
|
|
Загрузка…
Ссылка в новой задаче