[wasm] Implement the table.fill instruction
The implementation is done with a runtime function. R=mstarzinger@chromium.org Bug: v8:7581 Change-Id: I5f27b1fdc7cc2baf6919b4db3bf053a350b91a74 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1596738 Commit-Queue: Andreas Haas <ahaas@chromium.org> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Cr-Commit-Position: refs/heads/master@{#61274}
This commit is contained in:
parent
4bb788182b
commit
8168c76976
@ -4767,6 +4767,17 @@ Node* WasmGraphBuilder::TableSize(uint32_t table_index) {
|
||||
return BuildChangeSmiToInt32(table_size);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::TableFill(uint32_t table_index, Node* start,
|
||||
Node* value, Node* count) {
|
||||
Node* args[] = {
|
||||
graph()->NewNode(mcgraph()->common()->NumberConstant(table_index)),
|
||||
BuildConvertUint32ToSmiWithSaturation(start, FLAG_wasm_max_table_size),
|
||||
value,
|
||||
BuildConvertUint32ToSmiWithSaturation(count, FLAG_wasm_max_table_size)};
|
||||
|
||||
return BuildCallToRuntime(Runtime::kWasmTableFill, args, arraysize(args));
|
||||
}
|
||||
|
||||
class WasmDecorator final : public GraphDecorator {
|
||||
public:
|
||||
explicit WasmDecorator(NodeOriginTable* origins, wasm::Decoder* decoder)
|
||||
|
@ -387,6 +387,7 @@ class WasmGraphBuilder {
|
||||
Node* src, Node* size, wasm::WasmCodePosition position);
|
||||
Node* TableGrow(uint32_t table_index, Node* value, Node* delta);
|
||||
Node* TableSize(uint32_t table_index);
|
||||
Node* TableFill(uint32_t table_index, Node* start, Node* value, Node* count);
|
||||
|
||||
bool has_simd() const { return has_simd_; }
|
||||
|
||||
|
@ -622,5 +622,37 @@ RUNTIME_FUNCTION(Runtime_WasmTableGrow) {
|
||||
|
||||
return Smi::FromInt(result);
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WasmTableFill) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(4, args.length());
|
||||
auto instance =
|
||||
Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
|
||||
CONVERT_UINT32_ARG_CHECKED(table_index, 0);
|
||||
CONVERT_UINT32_ARG_CHECKED(start, 1);
|
||||
CONVERT_ARG_CHECKED(Object, value_raw, 2);
|
||||
// TODO(mstarzinger): Manually box because parameters are not visited yet.
|
||||
Handle<Object> value(value_raw, isolate);
|
||||
CONVERT_UINT32_ARG_CHECKED(count, 3);
|
||||
|
||||
Handle<WasmTableObject> table(
|
||||
WasmTableObject::cast(instance->tables()->get(table_index)), isolate);
|
||||
|
||||
uint32_t table_size = static_cast<uint32_t>(table->entries()->length());
|
||||
|
||||
if (start >= table_size) {
|
||||
return ThrowTableOutOfBounds(isolate, instance);
|
||||
}
|
||||
|
||||
// Even when table.fill goes out-of-bounds, as many entries as possible are
|
||||
// put into the table. Only afterwards we trap.
|
||||
uint32_t fill_count = std::min(count, table_size - start);
|
||||
WasmTableObject::Fill(isolate, table, start, value, fill_count);
|
||||
|
||||
if (fill_count < count) {
|
||||
return ThrowTableOutOfBounds(isolate, instance);
|
||||
}
|
||||
return ReadOnlyRoots(isolate).undefined_value();
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -548,6 +548,7 @@ namespace internal {
|
||||
F(WasmTableInit, 5, 1) \
|
||||
F(WasmTableCopy, 5, 1) \
|
||||
F(WasmTableGrow, 3, 1) \
|
||||
F(WasmTableFill, 4, 1) \
|
||||
F(WasmIndirectCallCheckSignatureAndGetTargetInstance, 3, 1) \
|
||||
F(WasmIndirectCallGetTargetAddress, 2, 1) \
|
||||
F(WasmIsValidAnyFuncValue, 1, 1) \
|
||||
|
@ -2000,6 +2000,10 @@ class LiftoffCompiler {
|
||||
Value* result) {
|
||||
unsupported(decoder, "table.size");
|
||||
}
|
||||
void TableFill(FullDecoder* decoder, const TableIndexImmediate<validate>& imm,
|
||||
Value& start, Value& value, Value& count) {
|
||||
unsupported(decoder, "table.fill");
|
||||
}
|
||||
|
||||
private:
|
||||
LiftoffAssembler asm_;
|
||||
|
@ -735,7 +735,9 @@ struct ControlBase {
|
||||
F(TableCopy, const TableCopyImmediate<validate>& imm, Vector<Value> args) \
|
||||
F(TableGrow, const TableIndexImmediate<validate>& imm, const Value& value, \
|
||||
const Value& delta, Value* result) \
|
||||
F(TableSize, const TableIndexImmediate<validate>& imm, Value* result)
|
||||
F(TableSize, const TableIndexImmediate<validate>& imm, Value* result) \
|
||||
F(TableFill, const TableIndexImmediate<validate>& imm, const Value& start, \
|
||||
const Value& value, const Value& count)
|
||||
|
||||
// Generic Wasm bytecode decoder with utilities for decoding immediates,
|
||||
// lengths, etc.
|
||||
@ -1300,7 +1302,8 @@ class WasmDecoder : public Decoder {
|
||||
return 2 + imm.length;
|
||||
}
|
||||
case kExprTableGrow:
|
||||
case kExprTableSize: {
|
||||
case kExprTableSize:
|
||||
case kExprTableFill: {
|
||||
TableIndexImmediate<validate> imm(decoder, pc);
|
||||
return 2 + imm.length;
|
||||
}
|
||||
@ -2233,7 +2236,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
|
||||
opcode = static_cast<WasmOpcode>(opcode << 8 | numeric_index);
|
||||
if (opcode < kExprMemoryInit) {
|
||||
CHECK_PROTOTYPE_OPCODE(sat_f2i_conversions);
|
||||
} else if (opcode == kExprTableGrow || opcode == kExprTableSize) {
|
||||
} else if (opcode == kExprTableGrow || opcode == kExprTableSize ||
|
||||
opcode == kExprTableFill) {
|
||||
CHECK_PROTOTYPE_OPCODE(anyref);
|
||||
} else {
|
||||
CHECK_PROTOTYPE_OPCODE(bulk_memory);
|
||||
@ -2688,6 +2692,16 @@ class WasmFullDecoder : public WasmDecoder<validate> {
|
||||
CALL_INTERFACE_IF_REACHABLE(TableSize, imm, result);
|
||||
break;
|
||||
}
|
||||
case kExprTableFill: {
|
||||
TableIndexImmediate<validate> imm(this, this->pc_ + 1);
|
||||
if (!this->Validate(this->pc_, imm)) break;
|
||||
len += imm.length;
|
||||
auto count = Pop(2, sig->GetParam(2));
|
||||
auto value = Pop(1, this->module_->tables[imm.index].type);
|
||||
auto start = Pop(0, sig->GetParam(0));
|
||||
CALL_INTERFACE_IF_REACHABLE(TableFill, imm, start, value, count);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
this->error("invalid numeric opcode");
|
||||
break;
|
||||
|
@ -577,6 +577,11 @@ class WasmGraphBuildingInterface {
|
||||
result->node = BUILD(TableSize, imm.index);
|
||||
}
|
||||
|
||||
void TableFill(FullDecoder* decoder, const TableIndexImmediate<validate>& imm,
|
||||
Value& start, Value& value, Value& count) {
|
||||
BUILD(TableFill, imm.index, start.node, value.node, count.node);
|
||||
}
|
||||
|
||||
private:
|
||||
SsaEnv* ssa_env_;
|
||||
compiler::WasmGraphBuilder* builder_;
|
||||
|
@ -994,6 +994,19 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate,
|
||||
return result;
|
||||
}
|
||||
|
||||
void WasmTableObject::Fill(Isolate* isolate, Handle<WasmTableObject> table,
|
||||
uint32_t start, Handle<Object> entry,
|
||||
uint32_t count) {
|
||||
// Bounds checks must be done by the caller.
|
||||
DCHECK_LT(start, table->entries()->length());
|
||||
DCHECK_LE(count, table->entries()->length());
|
||||
DCHECK_LE(start + count, table->entries()->length());
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
WasmTableObject::Set(isolate, table, start + i, entry);
|
||||
}
|
||||
}
|
||||
|
||||
void WasmTableObject::UpdateDispatchTables(
|
||||
Isolate* isolate, Handle<WasmTableObject> table, int entry_index,
|
||||
wasm::FunctionSig* sig, Handle<WasmInstanceObject> target_instance,
|
||||
|
@ -287,6 +287,9 @@ class V8_EXPORT_PRIVATE WasmTableObject : public JSObject {
|
||||
static Handle<Object> Get(Isolate* isolate, Handle<WasmTableObject> table,
|
||||
uint32_t index);
|
||||
|
||||
static void Fill(Isolate* isolate, Handle<WasmTableObject> table,
|
||||
uint32_t start, Handle<Object> entry, uint32_t count);
|
||||
|
||||
static void UpdateDispatchTables(Isolate* isolate,
|
||||
Handle<WasmTableObject> table,
|
||||
int entry_index, wasm::FunctionSig* sig,
|
||||
|
@ -211,6 +211,7 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
|
||||
CASE_OP(TableCopy, "table.copy")
|
||||
CASE_OP(TableGrow, "table.grow")
|
||||
CASE_OP(TableSize, "table.size")
|
||||
CASE_OP(TableFill, "table.fill")
|
||||
|
||||
// SIMD opcodes.
|
||||
CASE_SIMD_OP(Splat, "splat")
|
||||
|
@ -405,24 +405,26 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature);
|
||||
FOREACH_SIMD_1_OPERAND_1_PARAM_OPCODE(V) \
|
||||
FOREACH_SIMD_1_OPERAND_2_PARAM_OPCODE(V)
|
||||
|
||||
#define FOREACH_NUMERIC_OPCODE(V) \
|
||||
V(I32SConvertSatF32, 0xfc00, i_f) \
|
||||
V(I32UConvertSatF32, 0xfc01, i_f) \
|
||||
V(I32SConvertSatF64, 0xfc02, i_d) \
|
||||
V(I32UConvertSatF64, 0xfc03, i_d) \
|
||||
V(I64SConvertSatF32, 0xfc04, l_f) \
|
||||
V(I64UConvertSatF32, 0xfc05, l_f) \
|
||||
V(I64SConvertSatF64, 0xfc06, l_d) \
|
||||
V(I64UConvertSatF64, 0xfc07, l_d) \
|
||||
V(MemoryInit, 0xfc08, v_iii) \
|
||||
V(DataDrop, 0xfc09, v_v) \
|
||||
V(MemoryCopy, 0xfc0a, v_iii) \
|
||||
V(MemoryFill, 0xfc0b, v_iii) \
|
||||
V(TableInit, 0xfc0c, v_iii) \
|
||||
V(ElemDrop, 0xfc0d, v_v) \
|
||||
V(TableCopy, 0xfc0e, v_iii) \
|
||||
V(TableGrow, 0xfc0f, i_ai) \
|
||||
V(TableSize, 0xfc10, i_v)
|
||||
#define FOREACH_NUMERIC_OPCODE(V) \
|
||||
V(I32SConvertSatF32, 0xfc00, i_f) \
|
||||
V(I32UConvertSatF32, 0xfc01, i_f) \
|
||||
V(I32SConvertSatF64, 0xfc02, i_d) \
|
||||
V(I32UConvertSatF64, 0xfc03, i_d) \
|
||||
V(I64SConvertSatF32, 0xfc04, l_f) \
|
||||
V(I64UConvertSatF32, 0xfc05, l_f) \
|
||||
V(I64SConvertSatF64, 0xfc06, l_d) \
|
||||
V(I64UConvertSatF64, 0xfc07, l_d) \
|
||||
V(MemoryInit, 0xfc08, v_iii) \
|
||||
V(DataDrop, 0xfc09, v_v) \
|
||||
V(MemoryCopy, 0xfc0a, v_iii) \
|
||||
V(MemoryFill, 0xfc0b, v_iii) \
|
||||
V(TableInit, 0xfc0c, v_iii) \
|
||||
V(ElemDrop, 0xfc0d, v_v) \
|
||||
V(TableCopy, 0xfc0e, v_iii) \
|
||||
V(TableGrow, 0xfc0f, i_ai) \
|
||||
V(TableSize, 0xfc10, i_v) \
|
||||
/*TableFill is polymorph in the second parameter. It's anyref or anyfunc.*/ \
|
||||
V(TableFill, 0xfc11, v_iii)
|
||||
|
||||
#define FOREACH_ATOMIC_OPCODE(V) \
|
||||
V(AtomicNotify, 0xfe00, i_ii) \
|
||||
|
@ -41,6 +41,8 @@ class TestSignatures {
|
||||
sig_v_i(0, 1, kIntTypes4),
|
||||
sig_v_ii(0, 2, kIntTypes4),
|
||||
sig_v_iii(0, 3, kIntTypes4),
|
||||
sig_v_r(0, 1, kRefTypes4),
|
||||
sig_v_a(0, 1, kFuncTypes4),
|
||||
sig_s_i(1, 1, kSimd128IntTypes4),
|
||||
sig_ii_v(2, 0, kIntTypes4),
|
||||
sig_iii_v(3, 0, kIntTypes4) {
|
||||
@ -50,6 +52,7 @@ class TestSignatures {
|
||||
for (int i = 0; i < 4; i++) kFloatTypes4[i] = kWasmF32;
|
||||
for (int i = 0; i < 4; i++) kDoubleTypes4[i] = kWasmF64;
|
||||
for (int i = 0; i < 4; i++) kRefTypes4[i] = kWasmAnyRef;
|
||||
for (int i = 0; i < 4; i++) kFuncTypes4[i] = kWasmAnyFunc;
|
||||
for (int i = 1; i < 4; i++) kIntLongTypes4[i] = kWasmI64;
|
||||
for (int i = 1; i < 4; i++) kIntFloatTypes4[i] = kWasmF32;
|
||||
for (int i = 1; i < 4; i++) kIntDoubleTypes4[i] = kWasmF64;
|
||||
@ -93,6 +96,8 @@ class TestSignatures {
|
||||
FunctionSig* v_i() { return &sig_v_i; }
|
||||
FunctionSig* v_ii() { return &sig_v_ii; }
|
||||
FunctionSig* v_iii() { return &sig_v_iii; }
|
||||
FunctionSig* v_r() { return &sig_v_r; }
|
||||
FunctionSig* v_a() { return &sig_v_a; }
|
||||
FunctionSig* s_i() { return &sig_s_i; }
|
||||
|
||||
FunctionSig* ii_v() { return &sig_ii_v; }
|
||||
@ -113,6 +118,7 @@ class TestSignatures {
|
||||
ValueType kFloatTypes4[4];
|
||||
ValueType kDoubleTypes4[4];
|
||||
ValueType kRefTypes4[4];
|
||||
ValueType kFuncTypes4[4];
|
||||
ValueType kIntLongTypes4[4];
|
||||
ValueType kIntFloatTypes4[4];
|
||||
ValueType kIntDoubleTypes4[4];
|
||||
@ -149,6 +155,8 @@ class TestSignatures {
|
||||
FunctionSig sig_v_i;
|
||||
FunctionSig sig_v_ii;
|
||||
FunctionSig sig_v_iii;
|
||||
FunctionSig sig_v_r;
|
||||
FunctionSig sig_v_a;
|
||||
FunctionSig sig_s_i;
|
||||
|
||||
FunctionSig sig_ii_v;
|
||||
|
@ -623,6 +623,8 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
|
||||
static_cast<byte>(table)
|
||||
#define WASM_TABLE_SIZE(table) \
|
||||
WASM_NUMERIC_OP(kExprTableSize), static_cast<byte>(table)
|
||||
#define WASM_TABLE_FILL(table, times, value, start) \
|
||||
times, value, start, WASM_NUMERIC_OP(kExprTableFill), static_cast<byte>(table)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Memory Operations.
|
||||
|
196
test/mjsunit/wasm/table-fill.js
Normal file
196
test/mjsunit/wasm/table-fill.js
Normal file
@ -0,0 +1,196 @@
|
||||
// Copyright 2019 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --expose-wasm --experimental-wasm-anyref
|
||||
|
||||
load('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
|
||||
function dummy_func(val) {
|
||||
let builder = new WasmModuleBuilder();
|
||||
builder.addFunction('dummy', kSig_i_v)
|
||||
.addBody([kExprI32Const, val])
|
||||
.exportAs('dummy');
|
||||
return builder.instantiate().exports.dummy;
|
||||
}
|
||||
|
||||
let kSig_v_iri = makeSig([kWasmI32, kWasmAnyRef, kWasmI32], []);
|
||||
let kSig_v_iai = makeSig([kWasmI32, kWasmAnyFunc, kWasmI32], []);
|
||||
let kSig_r_i = makeSig([kWasmI32], [kWasmAnyRef]);
|
||||
|
||||
const builder = new WasmModuleBuilder();
|
||||
const size = 10;
|
||||
const maximum = size;
|
||||
const import_ref =
|
||||
builder.addImportedTable('imp', 'table_ref', size, maximum, kWasmAnyRef);
|
||||
const import_func =
|
||||
builder.addImportedTable('imp', 'table_func', size, maximum, kWasmAnyFunc);
|
||||
const internal_ref = builder.addTable(kWasmAnyRef, size, maximum).index;
|
||||
const internal_func = builder.addTable(kWasmAnyFunc, size, maximum).index;
|
||||
|
||||
// Add fill and get functions for the anyref tables.
|
||||
for (index of [import_ref, internal_ref]) {
|
||||
builder.addFunction(`fill${index}`, kSig_v_iri)
|
||||
.addBody([
|
||||
kExprGetLocal, 0, kExprGetLocal, 1, kExprGetLocal, 2, kNumericPrefix,
|
||||
kExprTableFill, index
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
builder.addFunction(`get${index}`, kSig_r_i)
|
||||
.addBody([kExprGetLocal, 0, kExprGetTable, index])
|
||||
.exportFunc();
|
||||
}
|
||||
|
||||
// Add fill and call functions for the anyfunc tables.
|
||||
const sig_index = builder.addType(kSig_i_v);
|
||||
for (index of [import_func, internal_func]) {
|
||||
builder.addFunction(`fill${index}`, kSig_v_iai)
|
||||
.addBody([
|
||||
kExprGetLocal, 0, kExprGetLocal, 1, kExprGetLocal, 2, kNumericPrefix,
|
||||
kExprTableFill, index
|
||||
])
|
||||
.exportFunc();
|
||||
|
||||
builder.addFunction(`call${index}`, kSig_i_i)
|
||||
.addBody([kExprGetLocal, 0, kExprCallIndirect, sig_index, index])
|
||||
.exportFunc();
|
||||
}
|
||||
|
||||
const table_ref =
|
||||
new WebAssembly.Table({element: 'anyref', initial: size, maximum: maximum});
|
||||
const table_func = new WebAssembly.Table(
|
||||
{element: 'anyfunc', initial: size, maximum: maximum});
|
||||
|
||||
const instance =
|
||||
builder.instantiate({imp: {table_ref: table_ref, table_func: table_func}});
|
||||
|
||||
function checkAnyRefTable(getter, start, count, value) {
|
||||
for (i = 0; i < count; ++i) {
|
||||
assertEquals(value, getter(start + i));
|
||||
}
|
||||
}
|
||||
|
||||
(function testAnyRefTableIsUninitialized() {
|
||||
print(arguments.callee.name);
|
||||
|
||||
checkAnyRefTable(instance.exports[`get${import_ref}`], 0, size, null);
|
||||
checkAnyRefTable(instance.exports[`get${internal_ref}`], 0, size, null);
|
||||
})();
|
||||
|
||||
(function testAnyRefTableFill() {
|
||||
print(arguments.callee.name);
|
||||
// Fill table and check the content.
|
||||
let start = 1;
|
||||
let value = {foo: 23};
|
||||
let count = 3;
|
||||
instance.exports[`fill${import_ref}`](start, value, count);
|
||||
checkAnyRefTable(instance.exports[`get${import_ref}`], start, count, value);
|
||||
value = 'foo';
|
||||
instance.exports[`fill${internal_ref}`](start, value, count);
|
||||
checkAnyRefTable(instance.exports[`get${internal_ref}`], start, count, value);
|
||||
})();
|
||||
|
||||
(function testAnyRefTableFillOOB() {
|
||||
print(arguments.callee.name);
|
||||
// Fill table out-of-bounds, check if the table got filled as much as
|
||||
// possible.
|
||||
let start = 7;
|
||||
let value = {foo: 27};
|
||||
// {maximum + 4} elements definitely don't fit into the table.
|
||||
let count = maximum + 4;
|
||||
assertTraps(
|
||||
kTrapTableOutOfBounds,
|
||||
() => instance.exports[`fill${import_ref}`](start, value, count));
|
||||
checkAnyRefTable(
|
||||
instance.exports[`get${import_ref}`], start, size - start, value);
|
||||
|
||||
value = 45;
|
||||
assertTraps(
|
||||
kTrapTableOutOfBounds,
|
||||
() => instance.exports[`fill${internal_ref}`](start, value, count));
|
||||
checkAnyRefTable(
|
||||
instance.exports[`get${internal_ref}`], start, size - start, value);
|
||||
})();
|
||||
|
||||
(function testAnyRefTableFillOOBCountZero() {
|
||||
print(arguments.callee.name);
|
||||
// Fill 0 elements at an oob position. This should trap.
|
||||
let start = size + 32;
|
||||
let value = 'bar';
|
||||
assertTraps(
|
||||
kTrapTableOutOfBounds,
|
||||
() => instance.exports[`fill${import_ref}`](start, value, 0));
|
||||
assertTraps(
|
||||
kTrapTableOutOfBounds,
|
||||
() => instance.exports[`fill${internal_ref}`](start, value, 0));
|
||||
})();
|
||||
|
||||
function checkAnyFuncTable(call, start, count, value) {
|
||||
for (i = 0; i < count; ++i) {
|
||||
if (value) {
|
||||
assertEquals(value, call(start + i));
|
||||
} else {
|
||||
assertTraps(kTrapFuncSigMismatch, () => call(start + i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(function testAnyRefTableIsUninitialized() {
|
||||
print(arguments.callee.name);
|
||||
// Check that the table is uninitialized.
|
||||
checkAnyFuncTable(instance.exports[`call${import_func}`], 0, size);
|
||||
checkAnyFuncTable(instance.exports[`call${internal_func}`], 0, size);
|
||||
})();
|
||||
|
||||
(function testAnyFuncTableFill() {
|
||||
print(arguments.callee.name);
|
||||
// Fill and check the result.
|
||||
let start = 1;
|
||||
let value = 44;
|
||||
let count = 3;
|
||||
instance.exports[`fill${import_func}`](start, dummy_func(value), count);
|
||||
checkAnyFuncTable(
|
||||
instance.exports[`call${import_func}`], start, count, value);
|
||||
value = 21;
|
||||
instance.exports[`fill${internal_func}`](start, dummy_func(value), count);
|
||||
checkAnyFuncTable(
|
||||
instance.exports[`call${internal_func}`], start, count, value);
|
||||
})();
|
||||
|
||||
(function testAnyFuncTableFillOOB() {
|
||||
print(arguments.callee.name);
|
||||
// Fill table out-of-bounds, check if the table got filled as much as
|
||||
// possible.
|
||||
let start = 7;
|
||||
let value = 38;
|
||||
// {maximum + 4} elements definitely don't fit into the table.
|
||||
let count = maximum + 4;
|
||||
assertTraps(
|
||||
kTrapTableOutOfBounds,
|
||||
() => instance.exports[`fill${import_func}`](
|
||||
start, dummy_func(value), count));
|
||||
checkAnyFuncTable(
|
||||
instance.exports[`call${import_func}`], start, size - start, value);
|
||||
|
||||
value = 46;
|
||||
assertTraps(
|
||||
kTrapTableOutOfBounds,
|
||||
() => instance.exports[`fill${internal_func}`](
|
||||
start, dummy_func(value), count));
|
||||
checkAnyFuncTable(
|
||||
instance.exports[`call${internal_func}`], start, size - start, value);
|
||||
})();
|
||||
|
||||
(function testAnyFuncTableFillOOBCountZero() {
|
||||
print(arguments.callee.name);
|
||||
// Fill 0 elements at an oob position. This should trap.
|
||||
let start = size + 32;
|
||||
let value = dummy_func(33);
|
||||
assertTraps(
|
||||
kTrapTableOutOfBounds,
|
||||
() => instance.exports[`fill${import_func}`](start, null, 0));
|
||||
assertTraps(
|
||||
kTrapTableOutOfBounds,
|
||||
() => instance.exports[`fill${internal_func}`](start, null, 0));
|
||||
})();
|
@ -392,6 +392,7 @@ let kExprElemDrop = 0x0d;
|
||||
let kExprTableCopy = 0x0e;
|
||||
let kExprTableGrow = 0x0f;
|
||||
let kExprTableSize = 0x10;
|
||||
let kExprTableFill = 0x11;
|
||||
|
||||
// Atomic opcodes.
|
||||
let kExprAtomicNotify = 0x00;
|
||||
|
@ -3206,6 +3206,31 @@ TEST_F(FunctionBodyDecoderTest, TableSize) {
|
||||
ExpectFailure(sigs.i_v(), {WASM_TABLE_SIZE(tab + 2)});
|
||||
}
|
||||
|
||||
TEST_F(FunctionBodyDecoderTest, TableFill) {
|
||||
TestModuleBuilder builder;
|
||||
byte tab_func = builder.AddTable(kWasmAnyFunc, 10, true, 20);
|
||||
byte tab_ref = builder.AddTable(kWasmAnyRef, 10, true, 20);
|
||||
|
||||
module = builder.module();
|
||||
|
||||
ExpectFailure(sigs.v_a(),
|
||||
{WASM_TABLE_FILL(tab_func, WASM_ONE, WASM_REF_NULL, WASM_ONE)});
|
||||
WASM_FEATURE_SCOPE(anyref);
|
||||
ExpectValidates(sigs.v_a(), {WASM_TABLE_FILL(tab_func, WASM_ONE,
|
||||
WASM_REF_NULL, WASM_ONE)});
|
||||
ExpectValidates(sigs.v_r(), {WASM_TABLE_FILL(tab_ref, WASM_ONE, WASM_REF_NULL,
|
||||
WASM_ONE)});
|
||||
// Anyfunc table cannot be initialized with an anyref value.
|
||||
ExpectFailure(sigs.v_r(), {WASM_TABLE_FILL(tab_func, WASM_ONE,
|
||||
WASM_GET_LOCAL(0), WASM_ONE)});
|
||||
// Anyref table can be initialized with an anyfunc value.
|
||||
ExpectValidates(sigs.v_a(), {WASM_TABLE_FILL(tab_ref, WASM_ONE,
|
||||
WASM_GET_LOCAL(0), WASM_ONE)});
|
||||
// Check that the table index gets verified.
|
||||
ExpectFailure(sigs.v_r(), {WASM_TABLE_FILL(tab_ref + 2, WASM_ONE,
|
||||
WASM_REF_NULL, WASM_ONE)});
|
||||
}
|
||||
|
||||
TEST_F(FunctionBodyDecoderTest, TableOpsWithoutTable) {
|
||||
TestModuleBuilder builder;
|
||||
builder.AddTable(kWasmAnyRef, 10, true, 20);
|
||||
@ -3213,8 +3238,9 @@ TEST_F(FunctionBodyDecoderTest, TableOpsWithoutTable) {
|
||||
WASM_FEATURE_SCOPE(anyref);
|
||||
ExpectFailure(sigs.i_v(), {WASM_TABLE_GROW(0, WASM_REF_NULL, WASM_ONE)});
|
||||
ExpectFailure(sigs.i_v(), {WASM_TABLE_SIZE(0)});
|
||||
ExpectFailure(sigs.i_r(),
|
||||
{WASM_TABLE_FILL(0, WASM_ONE, WASM_REF_NULL, WASM_ONE)});
|
||||
}
|
||||
|
||||
{
|
||||
WASM_FEATURE_SCOPE(bulk_memory);
|
||||
builder.AddPassiveElementSegment();
|
||||
|
Loading…
Reference in New Issue
Block a user