[wasm] Implement the table.grow instruction
This CL add decoding and code generation for the table.grow instruction. For code generation we just generate a runtime call. The implementation is quite straight-forward. However, I did several small cleanups along the way. I hope it's still acceptable. I could also split out some cleanups into separate CLs. R=mstarzinger@chromium.org Bug: v8:7581 Change-Id: Id885b7e70eb4f5bccfe779eb216f7cc9302ea3a5 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1593078 Commit-Queue: Andreas Haas <ahaas@chromium.org> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Cr-Commit-Position: refs/heads/master@{#61192}
This commit is contained in:
parent
9ebd7612da
commit
ef3c733810
@ -4733,6 +4733,16 @@ Node* WasmGraphBuilder::TableCopy(uint32_t table_src_index,
|
||||
return result;
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::TableGrow(uint32_t table_index, Node* value,
|
||||
Node* delta) {
|
||||
Node* args[] = {
|
||||
graph()->NewNode(mcgraph()->common()->NumberConstant(table_index)), value,
|
||||
BuildConvertUint32ToSmiWithSaturation(delta, FLAG_wasm_max_table_size)};
|
||||
Node* result =
|
||||
BuildCallToRuntime(Runtime::kWasmTableGrow, args, arraysize(args));
|
||||
return BuildChangeSmiToInt32(result);
|
||||
}
|
||||
|
||||
class WasmDecorator final : public GraphDecorator {
|
||||
public:
|
||||
explicit WasmDecorator(NodeOriginTable* origins, wasm::Decoder* decoder)
|
||||
|
@ -385,6 +385,7 @@ class WasmGraphBuilder {
|
||||
Node* ElemDrop(uint32_t elem_segment_index, wasm::WasmCodePosition position);
|
||||
Node* TableCopy(uint32_t table_src_index, uint32_t table_dst_index, Node* dst,
|
||||
Node* src, Node* size, wasm::WasmCodePosition position);
|
||||
Node* TableGrow(uint32_t table_index, Node* value, Node* delta);
|
||||
|
||||
bool has_simd() const { return has_simd_; }
|
||||
|
||||
|
@ -604,5 +604,23 @@ RUNTIME_FUNCTION(Runtime_WasmTableCopy) {
|
||||
if (oob) return ThrowTableOutOfBounds(isolate, instance);
|
||||
return ReadOnlyRoots(isolate).undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WasmTableGrow) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
auto instance =
|
||||
Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
|
||||
CONVERT_UINT32_ARG_CHECKED(table_index, 0);
|
||||
CONVERT_ARG_CHECKED(Object, value_raw, 1);
|
||||
// TODO(mstarzinger): Manually box because parameters are not visited yet.
|
||||
Handle<Object> value(value_raw, isolate);
|
||||
CONVERT_UINT32_ARG_CHECKED(delta, 2);
|
||||
|
||||
Handle<WasmTableObject> table(
|
||||
WasmTableObject::cast(instance->tables()->get(table_index)), isolate);
|
||||
int result = WasmTableObject::Grow(isolate, table, delta, value);
|
||||
|
||||
return Smi::FromInt(result);
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -545,6 +545,7 @@ namespace internal {
|
||||
F(WasmFunctionTableSet, 4, 1) \
|
||||
F(WasmTableInit, 5, 1) \
|
||||
F(WasmTableCopy, 5, 1) \
|
||||
F(WasmTableGrow, 3, 1) \
|
||||
F(WasmIndirectCallCheckSignatureAndGetTargetInstance, 3, 1) \
|
||||
F(WasmIndirectCallGetTargetAddress, 2, 1) \
|
||||
F(WasmIsValidAnyFuncValue, 1, 1) \
|
||||
|
@ -1992,6 +1992,10 @@ class LiftoffCompiler {
|
||||
Vector<Value> args) {
|
||||
unsupported(decoder, "table.copy");
|
||||
}
|
||||
void TableGrow(FullDecoder* decoder, const TableIndexImmediate<validate>& imm,
|
||||
Value& value, Value& delta, Value* result) {
|
||||
unsupported(decoder, "table.grow");
|
||||
}
|
||||
|
||||
private:
|
||||
LiftoffAssembler asm_;
|
||||
|
@ -732,7 +732,9 @@ struct ControlBase {
|
||||
const Value& value, const Value& size) \
|
||||
F(TableInit, const TableInitImmediate<validate>& imm, Vector<Value> args) \
|
||||
F(ElemDrop, const ElemDropImmediate<validate>& imm) \
|
||||
F(TableCopy, const TableCopyImmediate<validate>& imm, Vector<Value> args)
|
||||
F(TableCopy, const TableCopyImmediate<validate>& imm, Vector<Value> args) \
|
||||
F(TableGrow, const TableIndexImmediate<validate>& imm, const Value& value, \
|
||||
const Value& delta, Value* result)
|
||||
|
||||
// Generic Wasm bytecode decoder with utilities for decoding immediates,
|
||||
// lengths, etc.
|
||||
@ -1296,6 +1298,10 @@ class WasmDecoder : public Decoder {
|
||||
TableCopyImmediate<validate> imm(decoder, pc);
|
||||
return 2 + imm.length;
|
||||
}
|
||||
case kExprTableGrow: {
|
||||
TableIndexImmediate<validate> imm(decoder, pc);
|
||||
return 2 + imm.length;
|
||||
}
|
||||
default:
|
||||
decoder->error(pc, "invalid numeric opcode");
|
||||
return 2;
|
||||
@ -1545,7 +1551,16 @@ class WasmFullDecoder : public WasmDecoder<validate> {
|
||||
|
||||
const char* SafeOpcodeNameAt(const byte* pc) {
|
||||
if (pc >= this->end_) return "<end>";
|
||||
return WasmOpcodes::OpcodeName(static_cast<WasmOpcode>(*pc));
|
||||
WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
|
||||
if (!WasmOpcodes::IsPrefixOpcode(opcode)) {
|
||||
return WasmOpcodes::OpcodeName(static_cast<WasmOpcode>(opcode));
|
||||
}
|
||||
// We need one more byte.
|
||||
++pc;
|
||||
if (pc >= this->end_) return "<end>";
|
||||
byte sub_opcode = *pc;
|
||||
opcode = static_cast<WasmOpcode>(opcode << 8 | sub_opcode);
|
||||
return WasmOpcodes::OpcodeName(static_cast<WasmOpcode>(opcode));
|
||||
}
|
||||
|
||||
inline Zone* zone() const { return zone_; }
|
||||
@ -2058,7 +2073,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
|
||||
TableIndexImmediate<validate> imm(this, this->pc_);
|
||||
len = 1 + imm.length;
|
||||
if (!this->Validate(this->pc_, imm)) break;
|
||||
auto value = Pop(0, this->module_->tables[imm.index].type);
|
||||
auto value = Pop(1, this->module_->tables[imm.index].type);
|
||||
auto index = Pop(0, kWasmI32);
|
||||
CALL_INTERFACE_IF_REACHABLE(SetTable, index, value, imm);
|
||||
break;
|
||||
@ -2216,6 +2231,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) {
|
||||
CHECK_PROTOTYPE_OPCODE(anyref);
|
||||
} else {
|
||||
CHECK_PROTOTYPE_OPCODE(bulk_memory);
|
||||
}
|
||||
@ -2651,6 +2668,16 @@ class WasmFullDecoder : public WasmDecoder<validate> {
|
||||
CALL_INTERFACE_IF_REACHABLE(TableCopy, imm, VectorOf(args_));
|
||||
break;
|
||||
}
|
||||
case kExprTableGrow: {
|
||||
TableIndexImmediate<validate> imm(this, this->pc_ + 1);
|
||||
if (!this->Validate(this->pc_, imm)) break;
|
||||
len += imm.length;
|
||||
auto delta = Pop(1, sig->GetParam(1));
|
||||
auto value = Pop(0, this->module_->tables[imm.index].type);
|
||||
auto* result = Push(kWasmI32);
|
||||
CALL_INTERFACE_IF_REACHABLE(TableGrow, imm, value, delta, result);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
this->error("invalid numeric opcode");
|
||||
break;
|
||||
|
@ -534,33 +534,44 @@ class WasmGraphBuildingInterface {
|
||||
BUILD(MemoryInit, imm.data_segment_index, dst.node, src.node, size.node,
|
||||
decoder->position());
|
||||
}
|
||||
|
||||
void DataDrop(FullDecoder* decoder, const DataDropImmediate<validate>& imm) {
|
||||
BUILD(DataDrop, imm.index, decoder->position());
|
||||
}
|
||||
|
||||
void MemoryCopy(FullDecoder* decoder,
|
||||
const MemoryCopyImmediate<validate>& imm, const Value& dst,
|
||||
const Value& src, const Value& size) {
|
||||
BUILD(MemoryCopy, dst.node, src.node, size.node, decoder->position());
|
||||
}
|
||||
|
||||
void MemoryFill(FullDecoder* decoder,
|
||||
const MemoryIndexImmediate<validate>& imm, const Value& dst,
|
||||
const Value& value, const Value& size) {
|
||||
BUILD(MemoryFill, dst.node, value.node, size.node, decoder->position());
|
||||
}
|
||||
|
||||
void TableInit(FullDecoder* decoder, const TableInitImmediate<validate>& imm,
|
||||
Vector<Value> args) {
|
||||
BUILD(TableInit, imm.table.index, imm.elem_segment_index, args[0].node,
|
||||
args[1].node, args[2].node, decoder->position());
|
||||
}
|
||||
|
||||
void ElemDrop(FullDecoder* decoder, const ElemDropImmediate<validate>& imm) {
|
||||
BUILD(ElemDrop, imm.index, decoder->position());
|
||||
}
|
||||
|
||||
void TableCopy(FullDecoder* decoder, const TableCopyImmediate<validate>& imm,
|
||||
Vector<Value> args) {
|
||||
BUILD(TableCopy, imm.table_src.index, imm.table_dst.index, args[0].node,
|
||||
args[1].node, args[2].node, decoder->position());
|
||||
}
|
||||
|
||||
void TableGrow(FullDecoder* decoder, const TableIndexImmediate<validate>& imm,
|
||||
Value& value, Value& delta, Value* result) {
|
||||
result->node = BUILD(TableGrow, imm.index, value.node, delta.node);
|
||||
}
|
||||
|
||||
private:
|
||||
SsaEnv* ssa_env_;
|
||||
compiler::WasmGraphBuilder* builder_;
|
||||
|
@ -851,7 +851,6 @@ int WasmTableObject::Grow(Isolate* isolate, Handle<WasmTableObject> table,
|
||||
uint32_t new_size = old_size + count;
|
||||
auto new_store = isolate->factory()->CopyFixedArrayAndGrow(
|
||||
handle(table->entries(), isolate), count);
|
||||
|
||||
table->set_entries(*new_store, WriteBarrierMode::UPDATE_WRITE_BARRIER);
|
||||
|
||||
Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate);
|
||||
@ -864,6 +863,12 @@ int WasmTableObject::Grow(Isolate* isolate, Handle<WasmTableObject> table,
|
||||
// the instances that import a given table.
|
||||
for (int i = 0; i < dispatch_tables->length();
|
||||
i += kDispatchTableNumElements) {
|
||||
int table_index =
|
||||
Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset))->value();
|
||||
if (table_index > 0) {
|
||||
continue;
|
||||
}
|
||||
// For Table 0 we have to update the indirect function table.
|
||||
Handle<WasmInstanceObject> instance(
|
||||
WasmInstanceObject::cast(dispatch_tables->get(i)), isolate);
|
||||
DCHECK_EQ(old_size, instance->indirect_function_table_size());
|
||||
@ -1026,6 +1031,12 @@ void WasmTableObject::ClearDispatchTables(Isolate* isolate,
|
||||
DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements);
|
||||
for (int i = 0; i < dispatch_tables->length();
|
||||
i += kDispatchTableNumElements) {
|
||||
int table_index =
|
||||
Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset))->value();
|
||||
if (table_index > 0) {
|
||||
// Only table 0 has a dispatch table in the instance at the moment.
|
||||
continue;
|
||||
}
|
||||
Handle<WasmInstanceObject> target_instance(
|
||||
WasmInstanceObject::cast(
|
||||
dispatch_tables->get(i + kDispatchTableInstanceOffset)),
|
||||
|
@ -209,6 +209,7 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
|
||||
CASE_OP(TableInit, "table.init")
|
||||
CASE_OP(ElemDrop, "elem.drop")
|
||||
CASE_OP(TableCopy, "table.copy")
|
||||
CASE_OP(TableGrow, "table.grow")
|
||||
|
||||
// SIMD opcodes.
|
||||
CASE_SIMD_OP(Splat, "splat")
|
||||
|
@ -420,7 +420,8 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature);
|
||||
V(MemoryFill, 0xfc0b, v_iii) \
|
||||
V(TableInit, 0xfc0c, v_iii) \
|
||||
V(ElemDrop, 0xfc0d, v_v) \
|
||||
V(TableCopy, 0xfc0e, v_iii)
|
||||
V(TableCopy, 0xfc0e, v_iii) \
|
||||
V(TableGrow, 0xfc0f, i_ai)
|
||||
|
||||
#define FOREACH_ATOMIC_OPCODE(V) \
|
||||
V(AtomicNotify, 0xfe00, i_ii) \
|
||||
@ -547,7 +548,8 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature);
|
||||
V(l_ill, kWasmI64, kWasmI32, kWasmI64, kWasmI64) \
|
||||
V(i_iil, kWasmI32, kWasmI32, kWasmI32, kWasmI64) \
|
||||
V(i_ill, kWasmI32, kWasmI32, kWasmI64, kWasmI64) \
|
||||
V(i_r, kWasmI32, kWasmAnyRef)
|
||||
V(i_r, kWasmI32, kWasmAnyRef) \
|
||||
V(i_ai, kWasmI32, kWasmAnyFunc, kWasmI32)
|
||||
|
||||
#define FOREACH_SIMD_SIGNATURE(V) \
|
||||
V(s_s, kWasmS128, kWasmS128) \
|
||||
|
@ -25,8 +25,9 @@ class TestSignatures {
|
||||
sig_i_ff(1, 2, kIntFloatTypes4),
|
||||
sig_i_d(1, 1, kIntDoubleTypes4),
|
||||
sig_i_dd(1, 2, kIntDoubleTypes4),
|
||||
sig_i_r(1, 1, kIntRefTypes4),
|
||||
sig_i_rr(1, 2, kIntRefTypes4),
|
||||
sig_i_r(1, 1, kIntAnyRefTypes4),
|
||||
sig_i_rr(1, 2, kIntAnyRefTypes4),
|
||||
sig_i_a(1, 1, kIntAnyFuncTypes4),
|
||||
sig_l_v(1, 0, kLongTypes4),
|
||||
sig_l_l(1, 1, kLongTypes4),
|
||||
sig_l_ll(1, 2, kLongTypes4),
|
||||
@ -49,15 +50,17 @@ 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++) kIntLongTypes4[i] = kWasmI64;
|
||||
for (int i = 0; i < 4; i++) kIntFloatTypes4[i] = kWasmF32;
|
||||
for (int i = 0; i < 4; i++) kIntDoubleTypes4[i] = kWasmF64;
|
||||
for (int i = 0; i < 4; i++) kIntRefTypes4[i] = kWasmAnyRef;
|
||||
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;
|
||||
for (int i = 1; i < 4; i++) kIntAnyRefTypes4[i] = kWasmAnyRef;
|
||||
for (int i = 1; i < 4; i++) kIntAnyFuncTypes4[i] = kWasmAnyFunc;
|
||||
for (int i = 0; i < 4; i++) kSimd128IntTypes4[i] = kWasmS128;
|
||||
kIntLongTypes4[0] = kWasmI32;
|
||||
kIntFloatTypes4[0] = kWasmI32;
|
||||
kIntDoubleTypes4[0] = kWasmI32;
|
||||
kIntRefTypes4[0] = kWasmI32;
|
||||
kIntAnyRefTypes4[0] = kWasmI32;
|
||||
kIntAnyFuncTypes4[0] = kWasmI32;
|
||||
kSimd128IntTypes4[1] = kWasmI32;
|
||||
}
|
||||
|
||||
@ -77,6 +80,7 @@ class TestSignatures {
|
||||
FunctionSig* i_ll() { return &sig_i_ll; }
|
||||
FunctionSig* i_r() { return &sig_i_r; }
|
||||
FunctionSig* i_rr() { return &sig_i_rr; }
|
||||
FunctionSig* i_a() { return &sig_i_a; }
|
||||
|
||||
FunctionSig* f_f() { return &sig_f_f; }
|
||||
FunctionSig* f_ff() { return &sig_f_ff; }
|
||||
@ -112,7 +116,8 @@ class TestSignatures {
|
||||
ValueType kIntLongTypes4[4];
|
||||
ValueType kIntFloatTypes4[4];
|
||||
ValueType kIntDoubleTypes4[4];
|
||||
ValueType kIntRefTypes4[4];
|
||||
ValueType kIntAnyRefTypes4[4];
|
||||
ValueType kIntAnyFuncTypes4[4];
|
||||
ValueType kSimd128IntTypes4[4];
|
||||
|
||||
FunctionSig sig_i_v;
|
||||
@ -126,6 +131,7 @@ class TestSignatures {
|
||||
FunctionSig sig_i_dd;
|
||||
FunctionSig sig_i_r;
|
||||
FunctionSig sig_i_rr;
|
||||
FunctionSig sig_i_a;
|
||||
|
||||
FunctionSig sig_l_v;
|
||||
FunctionSig sig_l_l;
|
||||
|
@ -618,6 +618,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
|
||||
#define WASM_ELEM_DROP(seg) WASM_NUMERIC_OP(kExprElemDrop), U32V_1(seg)
|
||||
#define WASM_TABLE_COPY(dst, src, size) \
|
||||
dst, src, size, WASM_NUMERIC_OP(kExprTableCopy), TABLE_ZERO, TABLE_ZERO
|
||||
#define WASM_TABLE_GROW(table, initial_value, delta) \
|
||||
initial_value, delta, WASM_NUMERIC_OP(kExprTableGrow), \
|
||||
static_cast<byte>(table)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Memory Operations.
|
||||
|
201
test/mjsunit/wasm/table-grow-from-wasm.js
Normal file
201
test/mjsunit/wasm/table-grow-from-wasm.js
Normal file
@ -0,0 +1,201 @@
|
||||
// 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_i_ri = makeSig([kWasmAnyRef, kWasmI32], [kWasmI32]);
|
||||
let kSig_r_i = makeSig([kWasmI32], [kWasmAnyRef]);
|
||||
let kSig_i_ai = makeSig([kWasmAnyFunc, kWasmI32], [kWasmI32]);
|
||||
|
||||
function testGrowInternalAnyRefTable(table_index) {
|
||||
print(arguments.callee.name, table_index);
|
||||
|
||||
const builder = new WasmModuleBuilder();
|
||||
const initial_size = 5;
|
||||
// Add 10 tables, we only test one.
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
builder.addTable(kWasmAnyRef, initial_size).index;
|
||||
}
|
||||
builder.addFunction('grow', kSig_i_ri)
|
||||
.addBody([kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kNumericPrefix, kExprTableGrow, table_index])
|
||||
.exportFunc();
|
||||
|
||||
builder.addFunction('get', kSig_r_i)
|
||||
.addBody([kExprGetLocal, 0, kExprGetTable, table_index])
|
||||
.exportFunc();
|
||||
|
||||
const instance = builder.instantiate();
|
||||
|
||||
let size = initial_size;
|
||||
assertEquals(null, instance.exports.get(size - 2));
|
||||
|
||||
function growAndCheck(element, grow_by) {
|
||||
assertTraps(kTrapTableOutOfBounds, () => instance.exports.get(size));
|
||||
assertEquals(size, instance.exports.grow(element, grow_by));
|
||||
for (let i = 0; i < grow_by; ++i) {
|
||||
assertEquals(element, instance.exports.get(size + i));
|
||||
}
|
||||
size += grow_by;
|
||||
}
|
||||
growAndCheck("Hello", 3);
|
||||
growAndCheck(undefined, 4);
|
||||
growAndCheck(4, 2);
|
||||
growAndCheck({Hello: "World"}, 3);
|
||||
growAndCheck(null, 2);
|
||||
}
|
||||
|
||||
testGrowInternalAnyRefTable(0);
|
||||
testGrowInternalAnyRefTable(7);
|
||||
testGrowInternalAnyRefTable(9);
|
||||
|
||||
function testGrowInternalAnyFuncTable(table_index) {
|
||||
print(arguments.callee.name, table_index);
|
||||
|
||||
const builder = new WasmModuleBuilder();
|
||||
let size = 5;
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
builder.addTable(kWasmAnyFunc, size).index;
|
||||
}
|
||||
builder.addFunction('grow', kSig_i_ai)
|
||||
.addBody([kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kNumericPrefix, kExprTableGrow, table_index])
|
||||
.exportFunc();
|
||||
|
||||
const sig_index = builder.addType(kSig_i_v);
|
||||
builder.addFunction('call', kSig_i_i)
|
||||
.addBody([kExprGetLocal, 0, kExprCallIndirect, sig_index, table_index])
|
||||
.exportFunc();
|
||||
|
||||
const instance = builder.instantiate();
|
||||
assertTraps(kTrapFuncSigMismatch, () => instance.exports.call(size - 2));
|
||||
function growAndCheck(element, grow_by) {
|
||||
assertTraps(kTrapFuncInvalid, () => instance.exports.call(size));
|
||||
assertEquals(size, instance.exports.grow(dummy_func(element), grow_by));
|
||||
for (let i = 0; i < grow_by; ++i) {
|
||||
assertEquals(element, instance.exports.call(size + i));
|
||||
}
|
||||
size += grow_by;
|
||||
}
|
||||
growAndCheck(56, 3);
|
||||
growAndCheck(12, 4);
|
||||
|
||||
assertEquals(size, instance.exports.grow(null, 1));
|
||||
assertTraps(kTrapFuncSigMismatch, () => instance.exports.call(size));
|
||||
}
|
||||
|
||||
testGrowInternalAnyFuncTable(0);
|
||||
testGrowInternalAnyFuncTable(7);
|
||||
testGrowInternalAnyFuncTable(9);
|
||||
|
||||
(function testGrowImportedTable() {
|
||||
print(arguments.callee.name);
|
||||
|
||||
let size = 3;
|
||||
const builder = new WasmModuleBuilder();
|
||||
const table_index = builder.addImportedTable("imp", "table", size, undefined, kWasmAnyRef);
|
||||
builder.addFunction('grow', kSig_i_ri)
|
||||
.addBody([kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kNumericPrefix, kExprTableGrow, table_index])
|
||||
.exportFunc();
|
||||
|
||||
const table = new WebAssembly.Table({element: "anyref", initial: size});
|
||||
|
||||
const instance = builder.instantiate({imp: {table: table}});
|
||||
assertEquals(null, table.get(size - 2));
|
||||
|
||||
function growAndCheck(element, grow_by) {
|
||||
assertEquals(size, instance.exports.grow(element, grow_by));
|
||||
for (let i = 0; i < grow_by; ++i) {
|
||||
assertEquals(element, table.get(size + i));
|
||||
}
|
||||
size += grow_by;
|
||||
}
|
||||
growAndCheck("Hello", 3);
|
||||
growAndCheck(undefined, 4);
|
||||
growAndCheck(4, 2);
|
||||
growAndCheck({ Hello: "World" }, 3);
|
||||
growAndCheck(null, 2);
|
||||
})();
|
||||
|
||||
(function testGrowTableOutOfBounds() {
|
||||
print(arguments.callee.name);
|
||||
|
||||
const initial = 3;
|
||||
const maximum = 10;
|
||||
const max_delta = maximum - initial;
|
||||
const invalid_delta = max_delta + 1;
|
||||
|
||||
const builder = new WasmModuleBuilder();
|
||||
const import_ref = builder.addImportedTable(
|
||||
"imp", "table_ref", initial, maximum, kWasmAnyRef);
|
||||
const import_func = builder.addImportedTable(
|
||||
"imp", "table_func", initial, maximum, kWasmAnyFunc);
|
||||
const internal_ref = builder.addTable(kWasmAnyRef, initial, maximum).index;
|
||||
const internal_func = builder.addTable(kWasmAnyFunc, initial, maximum).index;
|
||||
|
||||
builder.addFunction('grow_imported_ref', kSig_i_ri)
|
||||
.addBody([kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kNumericPrefix, kExprTableGrow, import_ref])
|
||||
.exportFunc();
|
||||
|
||||
builder.addFunction('grow_imported_func', kSig_i_ai)
|
||||
.addBody([kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kNumericPrefix, kExprTableGrow, import_func])
|
||||
.exportFunc();
|
||||
|
||||
builder.addFunction('grow_internal_ref', kSig_i_ri)
|
||||
.addBody([kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kNumericPrefix, kExprTableGrow, internal_ref])
|
||||
.exportFunc();
|
||||
|
||||
builder.addFunction('grow_internal_func', kSig_i_ai)
|
||||
.addBody([kExprGetLocal, 0,
|
||||
kExprGetLocal, 1,
|
||||
kNumericPrefix, kExprTableGrow, internal_func])
|
||||
.exportFunc();
|
||||
|
||||
const table_ref = new WebAssembly.Table(
|
||||
{ element: "anyref", initial: initial, maximum: maximum });
|
||||
const table_func = new WebAssembly.Table(
|
||||
{element: "anyfunc", initial: initial, maximum: maximum});
|
||||
|
||||
const instance = builder.instantiate(
|
||||
{imp: {table_ref: table_ref, table_func: table_func}});
|
||||
|
||||
const ref = { foo: "bar" };
|
||||
const func = dummy_func(17);
|
||||
|
||||
// First check that growing out-of-bounds is not possible.
|
||||
assertEquals(-1, instance.exports.grow_imported_ref(ref, invalid_delta));
|
||||
assertEquals(initial, table_ref.length);
|
||||
assertEquals(-1, instance.exports.grow_imported_func(func, invalid_delta));
|
||||
assertEquals(initial, table_func.length);
|
||||
assertEquals(-1, instance.exports.grow_internal_ref(ref, invalid_delta));
|
||||
assertEquals(-1, instance.exports.grow_internal_func(func, invalid_delta));
|
||||
|
||||
// Check that we can grow to the maximum size.
|
||||
assertEquals(initial, instance.exports.grow_imported_ref(ref, max_delta));
|
||||
assertEquals(maximum, table_ref.length);
|
||||
assertEquals(initial, instance.exports.grow_imported_func(func, max_delta));
|
||||
assertEquals(maximum, table_func.length);
|
||||
assertEquals(initial, instance.exports.grow_internal_ref(ref, max_delta));
|
||||
assertEquals(initial, instance.exports.grow_internal_func(func, max_delta));
|
||||
})();
|
@ -390,6 +390,7 @@ let kExprMemoryFill = 0x0b;
|
||||
let kExprTableInit = 0x0c;
|
||||
let kExprElemDrop = 0x0d;
|
||||
let kExprTableCopy = 0x0e;
|
||||
let kExprTableGrow = 0x0f;
|
||||
|
||||
// Atomic opcodes.
|
||||
let kExprAtomicNotify = 0x00;
|
||||
|
@ -3169,16 +3169,48 @@ TEST_F(FunctionBodyDecoderTest, TableCopy) {
|
||||
{WASM_TABLE_COPY(WASM_ZERO, WASM_ZERO, WASM_ZERO)});
|
||||
}
|
||||
|
||||
TEST_F(FunctionBodyDecoderTest, BulkTableOpsWithoutTable) {
|
||||
TEST_F(FunctionBodyDecoderTest, TableGrow) {
|
||||
TestModuleBuilder builder;
|
||||
builder.InitializeTable();
|
||||
builder.AddPassiveElementSegment();
|
||||
byte tab_func = builder.AddTable(kWasmAnyFunc, 10, true, 20);
|
||||
byte tab_ref = builder.AddTable(kWasmAnyRef, 10, true, 20);
|
||||
|
||||
WASM_FEATURE_SCOPE(bulk_memory);
|
||||
ExpectFailure(sigs.v_v(),
|
||||
{WASM_TABLE_INIT(0, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
|
||||
ExpectFailure(sigs.v_v(), {WASM_ELEM_DROP(0)});
|
||||
ExpectFailure(sigs.v_v(), {WASM_TABLE_COPY(WASM_ZERO, WASM_ZERO, WASM_ZERO)});
|
||||
module = builder.module();
|
||||
|
||||
ExpectFailure(sigs.i_a(),
|
||||
{WASM_TABLE_GROW(tab_func, WASM_REF_NULL, WASM_ONE)});
|
||||
WASM_FEATURE_SCOPE(anyref);
|
||||
ExpectValidates(sigs.i_a(),
|
||||
{WASM_TABLE_GROW(tab_func, WASM_REF_NULL, WASM_ONE)});
|
||||
ExpectValidates(sigs.i_r(),
|
||||
{WASM_TABLE_GROW(tab_ref, WASM_REF_NULL, WASM_ONE)});
|
||||
// Anyfunc table cannot be initialized with an anyref value.
|
||||
ExpectFailure(sigs.i_r(),
|
||||
{WASM_TABLE_GROW(tab_func, WASM_GET_LOCAL(0), WASM_ONE)});
|
||||
// Anyref table can be initialized with an anyfunc value.
|
||||
ExpectValidates(sigs.i_a(),
|
||||
{WASM_TABLE_GROW(tab_ref, WASM_GET_LOCAL(0), WASM_ONE)});
|
||||
// Check that the table index gets verified.
|
||||
ExpectFailure(sigs.i_r(),
|
||||
{WASM_TABLE_GROW(tab_ref + 2, WASM_REF_NULL, WASM_ONE)});
|
||||
}
|
||||
|
||||
TEST_F(FunctionBodyDecoderTest, TableOpsWithoutTable) {
|
||||
TestModuleBuilder builder;
|
||||
builder.AddTable(kWasmAnyRef, 10, true, 20);
|
||||
{
|
||||
WASM_FEATURE_SCOPE(anyref);
|
||||
ExpectFailure(sigs.i_v(), {WASM_TABLE_GROW(0, WASM_REF_NULL, WASM_ONE)});
|
||||
}
|
||||
|
||||
{
|
||||
WASM_FEATURE_SCOPE(bulk_memory);
|
||||
builder.AddPassiveElementSegment();
|
||||
ExpectFailure(sigs.v_v(),
|
||||
{WASM_TABLE_INIT(0, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
|
||||
ExpectFailure(sigs.v_v(), {WASM_ELEM_DROP(0)});
|
||||
ExpectFailure(sigs.v_v(),
|
||||
{WASM_TABLE_COPY(WASM_ZERO, WASM_ZERO, WASM_ZERO)});
|
||||
}
|
||||
}
|
||||
|
||||
class BranchTableIteratorTest : public TestWithZone {
|
||||
|
Loading…
Reference in New Issue
Block a user