Commit 8fab64cf authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm] Implement the table.size instruction

This CL adds decoding and code generation for the table.size
instruction.

R=mstarzinger@chromium.org

Bug: v8:7581
Change-Id: I0e689a993d25db72281ebba0854454be12f4d350
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1593302
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61195}
parent 47fccbfd
......@@ -4743,6 +4743,25 @@ Node* WasmGraphBuilder::TableGrow(uint32_t table_index, Node* value,
return BuildChangeSmiToInt32(result);
}
Node* WasmGraphBuilder::TableSize(uint32_t table_index) {
Node* tables = LOAD_INSTANCE_FIELD(Tables, MachineType::TaggedPointer());
Node* table = LOAD_FIXED_ARRAY_SLOT_ANY(tables, table_index);
int storage_field_size = WasmTableObject::kElementsOffsetEnd -
WasmTableObject::kElementsOffset + 1;
Node* storage = LOAD_RAW(
table, wasm::ObjectAccess::ToTagged(WasmTableObject::kEntriesOffset),
assert_size(storage_field_size, MachineType::TaggedPointer()));
int length_field_size =
FixedArray::kLengthOffsetEnd - FixedArray::kLengthOffset + 1;
Node* table_size =
LOAD_RAW(storage, wasm::ObjectAccess::ToTagged(FixedArray::kLengthOffset),
assert_size(length_field_size, MachineType::TaggedSigned()));
return BuildChangeSmiToInt32(table_size);
}
class WasmDecorator final : public GraphDecorator {
public:
explicit WasmDecorator(NodeOriginTable* origins, wasm::Decoder* decoder)
......
......@@ -386,6 +386,7 @@ class WasmGraphBuilder {
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);
Node* TableSize(uint32_t table_index);
bool has_simd() const { return has_simd_; }
......
......@@ -1996,6 +1996,10 @@ class LiftoffCompiler {
Value& value, Value& delta, Value* result) {
unsupported(decoder, "table.grow");
}
void TableSize(FullDecoder* decoder, const TableIndexImmediate<validate>& imm,
Value* result) {
unsupported(decoder, "table.size");
}
private:
LiftoffAssembler asm_;
......
......@@ -734,7 +734,8 @@ struct ControlBase {
F(ElemDrop, const ElemDropImmediate<validate>& imm) \
F(TableCopy, const TableCopyImmediate<validate>& imm, Vector<Value> args) \
F(TableGrow, const TableIndexImmediate<validate>& imm, const Value& value, \
const Value& delta, Value* result)
const Value& delta, Value* result) \
F(TableSize, const TableIndexImmediate<validate>& imm, Value* result)
// Generic Wasm bytecode decoder with utilities for decoding immediates,
// lengths, etc.
......@@ -1298,7 +1299,8 @@ class WasmDecoder : public Decoder {
TableCopyImmediate<validate> imm(decoder, pc);
return 2 + imm.length;
}
case kExprTableGrow: {
case kExprTableGrow:
case kExprTableSize: {
TableIndexImmediate<validate> imm(decoder, pc);
return 2 + imm.length;
}
......@@ -2231,7 +2233,7 @@ 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) {
} else if (opcode == kExprTableGrow || opcode == kExprTableSize) {
CHECK_PROTOTYPE_OPCODE(anyref);
} else {
CHECK_PROTOTYPE_OPCODE(bulk_memory);
......@@ -2678,6 +2680,14 @@ class WasmFullDecoder : public WasmDecoder<validate> {
CALL_INTERFACE_IF_REACHABLE(TableGrow, imm, value, delta, result);
break;
}
case kExprTableSize: {
TableIndexImmediate<validate> imm(this, this->pc_ + 1);
if (!this->Validate(this->pc_, imm)) break;
len += imm.length;
auto* result = Push(kWasmI32);
CALL_INTERFACE_IF_REACHABLE(TableSize, imm, result);
break;
}
default:
this->error("invalid numeric opcode");
break;
......
......@@ -572,6 +572,11 @@ class WasmGraphBuildingInterface {
result->node = BUILD(TableGrow, imm.index, value.node, delta.node);
}
void TableSize(FullDecoder* decoder, const TableIndexImmediate<validate>& imm,
Value* result) {
result->node = BUILD(TableSize, imm.index);
}
private:
SsaEnv* ssa_env_;
compiler::WasmGraphBuilder* builder_;
......
......@@ -210,6 +210,7 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(ElemDrop, "elem.drop")
CASE_OP(TableCopy, "table.copy")
CASE_OP(TableGrow, "table.grow")
CASE_OP(TableSize, "table.size")
// SIMD opcodes.
CASE_SIMD_OP(Splat, "splat")
......
......@@ -421,7 +421,8 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature);
V(TableInit, 0xfc0c, v_iii) \
V(ElemDrop, 0xfc0d, v_v) \
V(TableCopy, 0xfc0e, v_iii) \
V(TableGrow, 0xfc0f, i_ai)
V(TableGrow, 0xfc0f, i_ai) \
V(TableSize, 0xfc10, i_v)
#define FOREACH_ATOMIC_OPCODE(V) \
V(AtomicNotify, 0xfe00, i_ii) \
......
......@@ -621,6 +621,8 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_TABLE_GROW(table, initial_value, delta) \
initial_value, delta, WASM_NUMERIC_OP(kExprTableGrow), \
static_cast<byte>(table)
#define WASM_TABLE_SIZE(table) \
WASM_NUMERIC_OP(kExprTableSize), static_cast<byte>(table)
//------------------------------------------------------------------------------
// Memory Operations.
......
......@@ -33,6 +33,10 @@ function testGrowInternalAnyRefTable(table_index) {
kNumericPrefix, kExprTableGrow, table_index])
.exportFunc();
builder.addFunction('size', kSig_i_v)
.addBody([kNumericPrefix, kExprTableSize, table_index])
.exportFunc();
builder.addFunction('get', kSig_r_i)
.addBody([kExprGetLocal, 0, kExprGetTable, table_index])
.exportFunc();
......@@ -43,6 +47,7 @@ function testGrowInternalAnyRefTable(table_index) {
assertEquals(null, instance.exports.get(size - 2));
function growAndCheck(element, grow_by) {
assertEquals(size, instance.exports.size());
assertTraps(kTrapTableOutOfBounds, () => instance.exports.get(size));
assertEquals(size, instance.exports.grow(element, grow_by));
for (let i = 0; i < grow_by; ++i) {
......@@ -75,6 +80,10 @@ function testGrowInternalAnyFuncTable(table_index) {
kNumericPrefix, kExprTableGrow, table_index])
.exportFunc();
builder.addFunction('size', kSig_i_v)
.addBody([kNumericPrefix, kExprTableSize, 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])
......@@ -83,6 +92,7 @@ function testGrowInternalAnyFuncTable(table_index) {
const instance = builder.instantiate();
assertTraps(kTrapFuncSigMismatch, () => instance.exports.call(size - 2));
function growAndCheck(element, grow_by) {
assertEquals(size, instance.exports.size());
assertTraps(kTrapFuncInvalid, () => instance.exports.call(size));
assertEquals(size, instance.exports.grow(dummy_func(element), grow_by));
for (let i = 0; i < grow_by; ++i) {
......@@ -113,12 +123,17 @@ testGrowInternalAnyFuncTable(9);
kNumericPrefix, kExprTableGrow, table_index])
.exportFunc();
builder.addFunction('size', kSig_i_v)
.addBody([kNumericPrefix, kExprTableSize, 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.size());
assertEquals(size, instance.exports.grow(element, grow_by));
for (let i = 0; i < grow_by; ++i) {
assertEquals(element, table.get(size + i));
......@@ -172,6 +187,22 @@ testGrowInternalAnyFuncTable(9);
kNumericPrefix, kExprTableGrow, internal_func])
.exportFunc();
builder.addFunction('size_imported_ref', kSig_i_v)
.addBody([kNumericPrefix, kExprTableSize, import_ref])
.exportFunc();
builder.addFunction('size_imported_func', kSig_i_v)
.addBody([kNumericPrefix, kExprTableSize, import_func])
.exportFunc();
builder.addFunction('size_internal_ref', kSig_i_v)
.addBody([kNumericPrefix, kExprTableSize, internal_ref])
.exportFunc();
builder.addFunction('size_internal_func', kSig_i_v)
.addBody([kNumericPrefix, kExprTableSize, internal_func])
.exportFunc();
const table_ref = new WebAssembly.Table(
{ element: "anyref", initial: initial, maximum: maximum });
const table_func = new WebAssembly.Table(
......@@ -186,16 +217,24 @@ testGrowInternalAnyFuncTable(9);
// 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(initial, instance.exports.size_imported_ref());
assertEquals(-1, instance.exports.grow_imported_func(func, invalid_delta));
assertEquals(initial, table_func.length);
assertEquals(initial, instance.exports.size_imported_func());
assertEquals(-1, instance.exports.grow_internal_ref(ref, invalid_delta));
assertEquals(initial, instance.exports.size_internal_ref());
assertEquals(-1, instance.exports.grow_internal_func(func, invalid_delta));
assertEquals(initial, instance.exports.size_internal_func());
// 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(maximum, instance.exports.size_imported_ref());
assertEquals(initial, instance.exports.grow_imported_func(func, max_delta));
assertEquals(maximum, table_func.length);
assertEquals(maximum, instance.exports.size_imported_func());
assertEquals(initial, instance.exports.grow_internal_ref(ref, max_delta));
assertEquals(maximum, instance.exports.size_internal_ref());
assertEquals(initial, instance.exports.grow_internal_func(func, max_delta));
assertEquals(maximum, instance.exports.size_internal_func());
})();
......@@ -391,6 +391,7 @@ let kExprTableInit = 0x0c;
let kExprElemDrop = 0x0d;
let kExprTableCopy = 0x0e;
let kExprTableGrow = 0x0f;
let kExprTableSize = 0x10;
// Atomic opcodes.
let kExprAtomicNotify = 0x00;
......
......@@ -3194,12 +3194,25 @@ TEST_F(FunctionBodyDecoderTest, TableGrow) {
{WASM_TABLE_GROW(tab_ref + 2, WASM_REF_NULL, WASM_ONE)});
}
TEST_F(FunctionBodyDecoderTest, TableSize) {
TestModuleBuilder builder;
int tab = builder.AddTable(kWasmAnyFunc, 10, true, 20);
module = builder.module();
ExpectFailure(sigs.i_v(), {WASM_TABLE_SIZE(tab)});
WASM_FEATURE_SCOPE(anyref);
ExpectValidates(sigs.i_v(), {WASM_TABLE_SIZE(tab)});
ExpectFailure(sigs.i_v(), {WASM_TABLE_SIZE(tab + 2)});
}
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)});
ExpectFailure(sigs.i_v(), {WASM_TABLE_SIZE(0)});
}
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment