[wasm][anyref] Implement decoding of table.get and table.set

R=titzer@chromium.org

Bug: v8:7581
Change-Id: I857a40a0f955b3506d7958d2128a1b4560cff0bc
Reviewed-on: https://chromium-review.googlesource.com/c/1458236
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Ben Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59439}
This commit is contained in:
Andreas Haas 2019-02-07 14:46:00 +01:00 committed by Commit Bot
parent 5208e1da45
commit dd6f4d4f4c
9 changed files with 176 additions and 8 deletions

View File

@ -3215,6 +3215,14 @@ Node* WasmGraphBuilder::SetGlobal(uint32_t index, Node* val) {
graph()->NewNode(op, base, offset, val, Effect(), Control()));
}
Node* WasmGraphBuilder::GetTable(uint32_t table_index, Node* index) {
UNIMPLEMENTED();
}
Node* WasmGraphBuilder::SetTable(uint32_t table_index, Node* index, Node* val) {
UNIMPLEMENTED();
}
Node* WasmGraphBuilder::CheckBoundsAndAlignment(
uint8_t access_size, Node* index, uint32_t offset,
wasm::WasmCodePosition position) {

View File

@ -272,12 +272,14 @@ class WasmGraphBuilder {
Node* Invert(Node* node);
Node* GetGlobal(uint32_t index);
Node* SetGlobal(uint32_t index, Node* val);
Node* GetTable(uint32_t table_index, Node* index);
Node* SetTable(uint32_t table_index, Node* index, Node* val);
//-----------------------------------------------------------------------
// Operations that concern the linear memory.
//-----------------------------------------------------------------------
Node* CurrentMemoryPages();
Node* GetGlobal(uint32_t index);
Node* SetGlobal(uint32_t index, Node* val);
Node* TraceMemoryOperation(bool is_store, MachineRepresentation, Node* index,
uint32_t offset, wasm::WasmCodePosition);
Node* LoadMem(wasm::ValueType type, MachineType memtype, Node* index,

View File

@ -1242,6 +1242,16 @@ class LiftoffCompiler {
__ Store(addr, no_reg, offset, reg, type, {}, nullptr, true);
}
void GetTable(FullDecoder* decoder, const Value& index, Value* result,
TableIndexImmediate<validate>& imm) {
unsupported(decoder, "table_get");
}
void SetTable(FullDecoder* decoder, const Value& index, const Value& value,
TableIndexImmediate<validate>& imm) {
unsupported(decoder, "table_set");
}
void Unreachable(FullDecoder* decoder) {
Label* unreachable_label = AddOutOfLineTrap(
decoder->position(), WasmCode::kThrowWasmTrapUnreachable);

View File

@ -345,9 +345,6 @@ struct TableIndexImmediate {
inline TableIndexImmediate() = default;
inline TableIndexImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_u8<validate>(pc + 1, "table index");
if (!VALIDATE(index == 0)) {
decoder->errorf(pc + 1, "expected table index 0, found %u", index);
}
}
};
@ -678,6 +675,10 @@ struct ControlBase {
const LocalIndexImmediate<validate>& imm) \
F(GetGlobal, Value* result, const GlobalIndexImmediate<validate>& imm) \
F(SetGlobal, const Value& value, const GlobalIndexImmediate<validate>& imm) \
F(GetTable, const Value& index, Value* result, \
const TableIndexImmediate<validate>& imm) \
F(SetTable, const Value& index, const Value& value, \
const TableIndexImmediate<validate>& imm) \
F(Unreachable) \
F(Select, const Value& cond, const Value& fval, const Value& tval, \
Value* result) \
@ -1153,12 +1154,16 @@ class WasmDecoder : public Decoder {
BranchDepthImmediate<validate> imm(decoder, pc);
return 1 + imm.length;
}
case kExprSetGlobal:
case kExprGetGlobal: {
case kExprGetGlobal:
case kExprSetGlobal: {
GlobalIndexImmediate<validate> imm(decoder, pc);
return 1 + imm.length;
}
case kExprGetTable:
case kExprSetTable: {
TableIndexImmediate<validate> imm(decoder, pc);
return 1 + imm.length;
}
case kExprCallFunction: {
CallFunctionImmediate<validate> imm(decoder, pc);
return 1 + imm.length;
@ -1332,9 +1337,11 @@ class WasmDecoder : public Decoder {
switch (opcode) {
case kExprSelect:
return {3, 1};
case kExprSetTable:
FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
return {2, 0};
FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE)
case kExprGetTable:
case kExprTeeLocal:
case kExprMemoryGrow:
return {1, 1};
@ -2009,6 +2016,28 @@ class WasmFullDecoder : public WasmDecoder<validate> {
CALL_INTERFACE_IF_REACHABLE(SetGlobal, value, imm);
break;
}
case kExprGetTable: {
CHECK_PROTOTYPE_OPCODE(anyref);
TableIndexImmediate<validate> imm(this, this->pc_);
len = 1 + imm.length;
if (!this->Validate(this->pc_, imm)) break;
DCHECK_NOT_NULL(this->module_);
auto index = Pop(0, kWasmI32);
auto* result = Push(this->module_->tables[imm.index].type);
CALL_INTERFACE_IF_REACHABLE(GetTable, index, result, imm);
break;
}
case kExprSetTable: {
CHECK_PROTOTYPE_OPCODE(anyref);
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 index = Pop(0, kWasmI32);
CALL_INTERFACE_IF_REACHABLE(SetTable, index, value, imm);
break;
}
case kExprI32LoadMem8S:
len = 1 + DecodeLoadMem(LoadType::kI32Load8S);
break;

View File

@ -287,6 +287,16 @@ class WasmGraphBuildingInterface {
BUILD(SetGlobal, imm.index, value.node);
}
void GetTable(FullDecoder* decoder, const Value& index, Value* result,
const TableIndexImmediate<validate>& imm) {
result->node = BUILD(GetTable, imm.index, index.node);
}
void SetTable(FullDecoder* decoder, const Value& index, const Value& value,
const TableIndexImmediate<validate>& imm) {
BUILD(SetTable, imm.index, index.node, value.node);
}
void Unreachable(FullDecoder* decoder) {
BUILD(Unreachable, decoder->position());
}

View File

@ -144,6 +144,8 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(TeeLocal, "tee_local")
CASE_OP(GetGlobal, "get_global")
CASE_OP(SetGlobal, "set_global")
CASE_OP(GetTable, "get_table")
CASE_OP(SetTable, "set_table")
CASE_ALL_OP(Const, "const")
CASE_OP(MemorySize, "memory.size")
CASE_OP(MemoryGrow, "memory.grow")

View File

@ -48,6 +48,8 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, bool hasBigIntFeature);
V(TeeLocal, 0x22, _) \
V(GetGlobal, 0x23, _) \
V(SetGlobal, 0x24, _) \
V(GetTable, 0x25, _) \
V(SetTable, 0x26, _) \
V(I32Const, 0x41, _) \
V(I64Const, 0x42, _) \
V(F32Const, 0x43, _) \

View File

@ -354,6 +354,10 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_GET_GLOBAL(index) kExprGetGlobal, static_cast<byte>(index)
#define WASM_SET_GLOBAL(index, val) \
val, kExprSetGlobal, static_cast<byte>(index)
#define WASM_GET_TABLE(table_index, index) \
index, kExprGetTable, static_cast<byte>(table_index)
#define WASM_SET_TABLE(table_index, index, val) \
index, val, kExprSetTable, static_cast<byte>(table_index)
#define WASM_LOAD_MEM(type, index) \
index, \
static_cast<byte>(v8::internal::wasm::LoadStoreOpcodeOf(type, false)), \

View File

@ -256,6 +256,18 @@ class TestModuleBuilder {
return static_cast<byte>(mod.exceptions.size() - 1);
}
byte AddTable(ValueType type, uint32_t initial_size, bool has_maximum_size,
uint32_t maximum_size) {
CHECK(type == kWasmAnyRef || type == kWasmAnyFunc);
mod.tables.emplace_back();
WasmTable& table = mod.tables.back();
table.type = type;
table.initial_size = initial_size;
table.has_maximum_size = has_maximum_size;
table.maximum_size = maximum_size;
return static_cast<byte>(mod.tables.size() - 1);
}
void InitializeMemory() {
mod.has_memory = true;
mod.initial_pages = 1;
@ -1817,6 +1829,95 @@ TEST_F(FunctionBodyDecoderTest, AllSetGlobalCombinations) {
}
}
TEST_F(FunctionBodyDecoderTest, SetTable) {
WASM_FEATURE_SCOPE(anyref);
TestModuleBuilder builder;
module = builder.module();
byte tab_ref1 = builder.AddTable(kWasmAnyRef, 10, true, 20);
byte tab_func1 = builder.AddTable(kWasmAnyFunc, 20, true, 30);
byte tab_func2 = builder.AddTable(kWasmAnyFunc, 10, false, 20);
byte tab_ref2 = builder.AddTable(kWasmAnyRef, 10, false, 20);
ValueType sig_types[]{kWasmAnyRef, kWasmAnyFunc, kWasmI32};
FunctionSig sig(0, 3, sig_types);
byte local_ref = 0;
byte local_func = 1;
byte local_int = 2;
EXPECT_VERIFIES_S(
&sig, WASM_SET_TABLE(tab_ref1, WASM_I32V(6), WASM_GET_LOCAL(local_ref)));
EXPECT_VERIFIES_S(&sig, WASM_SET_TABLE(tab_func1, WASM_I32V(5),
WASM_GET_LOCAL(local_func)));
EXPECT_VERIFIES_S(&sig, WASM_SET_TABLE(tab_func2, WASM_I32V(7),
WASM_GET_LOCAL(local_func)));
EXPECT_VERIFIES_S(
&sig, WASM_SET_TABLE(tab_ref2, WASM_I32V(8), WASM_GET_LOCAL(local_ref)));
// We can store anyfunc values as anyref, but not the other way around.
EXPECT_VERIFIES_S(
&sig, WASM_SET_TABLE(tab_ref1, WASM_I32V(4), WASM_GET_LOCAL(local_func)));
EXPECT_FAILURE_S(
&sig, WASM_SET_TABLE(tab_func1, WASM_I32V(9), WASM_GET_LOCAL(local_ref)));
EXPECT_FAILURE_S(
&sig, WASM_SET_TABLE(tab_func2, WASM_I32V(3), WASM_GET_LOCAL(local_ref)));
EXPECT_VERIFIES_S(
&sig, WASM_SET_TABLE(tab_ref2, WASM_I32V(2), WASM_GET_LOCAL(local_func)));
EXPECT_FAILURE_S(
&sig, WASM_SET_TABLE(tab_ref1, WASM_I32V(9), WASM_GET_LOCAL(local_int)));
EXPECT_FAILURE_S(
&sig, WASM_SET_TABLE(tab_func1, WASM_I32V(3), WASM_GET_LOCAL(local_int)));
// Out-of-bounds table index should fail.
byte oob_tab = 37;
EXPECT_FAILURE_S(
&sig, WASM_SET_TABLE(oob_tab, WASM_I32V(9), WASM_GET_LOCAL(local_ref)));
EXPECT_FAILURE_S(
&sig, WASM_SET_TABLE(oob_tab, WASM_I32V(3), WASM_GET_LOCAL(local_func)));
}
TEST_F(FunctionBodyDecoderTest, GetTable) {
WASM_FEATURE_SCOPE(anyref);
TestModuleBuilder builder;
module = builder.module();
byte tab_ref1 = builder.AddTable(kWasmAnyRef, 10, true, 20);
byte tab_func1 = builder.AddTable(kWasmAnyFunc, 20, true, 30);
byte tab_func2 = builder.AddTable(kWasmAnyFunc, 10, false, 20);
byte tab_ref2 = builder.AddTable(kWasmAnyRef, 10, false, 20);
ValueType sig_types[]{kWasmAnyRef, kWasmAnyFunc, kWasmI32};
FunctionSig sig(0, 3, sig_types);
byte local_ref = 0;
byte local_func = 1;
byte local_int = 2;
EXPECT_VERIFIES_S(
&sig, WASM_SET_LOCAL(local_ref, WASM_GET_TABLE(tab_ref1, WASM_I32V(6))));
EXPECT_VERIFIES_S(
&sig, WASM_SET_LOCAL(local_ref, WASM_GET_TABLE(tab_ref2, WASM_I32V(8))));
EXPECT_VERIFIES_S(
&sig,
WASM_SET_LOCAL(local_func, WASM_GET_TABLE(tab_func1, WASM_I32V(5))));
EXPECT_VERIFIES_S(
&sig,
WASM_SET_LOCAL(local_func, WASM_GET_TABLE(tab_func2, WASM_I32V(7))));
// We can store anyfunc values as anyref, but not the other way around.
EXPECT_FAILURE_S(
&sig, WASM_SET_LOCAL(local_func, WASM_GET_TABLE(tab_ref1, WASM_I32V(4))));
EXPECT_VERIFIES_S(
&sig, WASM_SET_LOCAL(local_ref, WASM_GET_TABLE(tab_func1, WASM_I32V(9))));
EXPECT_VERIFIES_S(
&sig, WASM_SET_LOCAL(local_ref, WASM_GET_TABLE(tab_func2, WASM_I32V(3))));
EXPECT_FAILURE_S(
&sig, WASM_SET_LOCAL(local_func, WASM_GET_TABLE(tab_ref2, WASM_I32V(2))));
EXPECT_FAILURE_S(
&sig, WASM_SET_LOCAL(local_int, WASM_GET_TABLE(tab_ref1, WASM_I32V(9))));
EXPECT_FAILURE_S(
&sig, WASM_SET_LOCAL(local_int, WASM_GET_TABLE(tab_func1, WASM_I32V(3))));
// Out-of-bounds table index should fail.
byte oob_tab = 37;
EXPECT_FAILURE_S(
&sig, WASM_SET_LOCAL(local_ref, WASM_GET_TABLE(oob_tab, WASM_I32V(9))));
EXPECT_FAILURE_S(
&sig, WASM_SET_LOCAL(local_func, WASM_GET_TABLE(oob_tab, WASM_I32V(3))));
}
TEST_F(FunctionBodyDecoderTest, WasmMemoryGrow) {
TestModuleBuilder builder;
module = builder.module();