[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:
parent
5208e1da45
commit
dd6f4d4f4c
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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, _) \
|
||||
|
@ -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)), \
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user