Commit d87287bc authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm][anyref] Allow tables of different reference types

Allow the decoding of multiple tables, and allow these tables to have
any reference type. In addition, rename function-tables (in different
occurrences) to tables.

R=titzer@chromium.org

Bug: v8:7581
Change-Id: I191ea8e303b76563f9d91ae7447b373c4760d8b8
Reviewed-on: https://chromium-review.googlesource.com/1019581
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54355}
parent 7184ce39
......@@ -849,7 +849,7 @@ class WasmDecoder : public Decoder {
}
inline bool Validate(const byte* pc, CallIndirectImmediate<validate>& imm) {
if (!VALIDATE(module_ != nullptr && !module_->function_tables.empty())) {
if (!VALIDATE(module_ != nullptr && !module_->tables.empty())) {
error("function table has to exist to execute call_indirect");
return false;
}
......
......@@ -1103,11 +1103,8 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
//--------------------------------------------------------------------------
// Reserve the metadata for indirect function tables.
//--------------------------------------------------------------------------
int function_table_count = static_cast<int>(module_->function_tables.size());
table_instances_.reserve(module_->function_tables.size());
for (int index = 0; index < function_table_count; ++index) {
table_instances_.emplace_back();
}
int table_count = static_cast<int>(module_->tables.size());
table_instances_.resize(table_count);
//--------------------------------------------------------------------------
// Process the imports for the module.
......@@ -1123,7 +1120,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
//--------------------------------------------------------------------------
// Initialize the indirect tables.
//--------------------------------------------------------------------------
if (function_table_count > 0) {
if (table_count > 0) {
InitializeTables(instance);
}
......@@ -1186,7 +1183,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
//--------------------------------------------------------------------------
// Initialize the indirect function tables.
//--------------------------------------------------------------------------
if (function_table_count > 0) {
if (table_count > 0) {
LoadTableSegments(instance);
}
......@@ -1551,8 +1548,7 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
}
uint32_t table_num = import.index;
DCHECK_EQ(table_num, num_imported_tables);
const WasmIndirectFunctionTable& table =
module_->function_tables[table_num];
const WasmTable& table = module_->tables[table_num];
TableInstance& table_instance = table_instances_[table_num];
table_instance.table_object = Handle<WasmTableObject>::cast(value);
instance->set_table_object(*table_instance.table_object);
......@@ -1840,7 +1836,7 @@ bool InstanceBuilder::NeedsWrappers() const {
for (auto& table_instance : table_instances_) {
if (!table_instance.js_wrappers.is_null()) return true;
}
for (auto& table : module_->function_tables) {
for (auto& table : module_->tables) {
if (table.exported) return true;
}
return false;
......@@ -1944,8 +1940,7 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
case kExternalTable: {
// Export a table as a WebAssembly.Table object.
TableInstance& table_instance = table_instances_[exp.index];
const WasmIndirectFunctionTable& table =
module_->function_tables[exp.index];
const WasmTable& table = module_->tables[exp.index];
if (table_instance.table_object.is_null()) {
uint32_t maximum = table.has_maximum_size ? table.maximum_size
: FLAG_wasm_max_table_size;
......@@ -2048,9 +2043,9 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
}
void InstanceBuilder::InitializeTables(Handle<WasmInstanceObject> instance) {
size_t table_count = module_->function_tables.size();
size_t table_count = module_->tables.size();
for (size_t index = 0; index < table_count; ++index) {
const WasmIndirectFunctionTable& table = module_->function_tables[index];
const WasmTable& table = module_->tables[index];
TableInstance& table_instance = table_instances_[index];
if (!instance->has_indirect_function_table()) {
......@@ -2063,8 +2058,8 @@ void InstanceBuilder::InitializeTables(Handle<WasmInstanceObject> instance) {
void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
NativeModule* native_module = module_object_->native_module();
int function_table_count = static_cast<int>(module_->function_tables.size());
for (int index = 0; index < function_table_count; ++index) {
int table_count = static_cast<int>(module_->tables.size());
for (int index = 0; index < table_count; ++index) {
TableInstance& table_instance = table_instances_[index];
// TODO(titzer): this does redundant work if there are multiple tables,
......
......@@ -473,12 +473,11 @@ class ModuleDecoderImpl : public Decoder {
case kExternalTable: {
// ===== Imported table ==========================================
if (!AddTable(module_.get())) break;
import->index =
static_cast<uint32_t>(module_->function_tables.size());
module_->function_tables.emplace_back();
WasmIndirectFunctionTable* table = &module_->function_tables.back();
import->index = static_cast<uint32_t>(module_->tables.size());
module_->tables.emplace_back();
WasmTable* table = &module_->tables.back();
table->imported = true;
expect_u8("element type", kWasmAnyFunctionTypeCode);
expect_u8("element type", kLocalAnyFunc);
uint8_t flags = validate_table_flags("element count");
consume_resizable_limits(
"element count", "elements", FLAG_wasm_max_table_size,
......@@ -547,13 +546,16 @@ class ModuleDecoderImpl : public Decoder {
}
void DecodeTableSection() {
uint32_t table_count = consume_count("table count", kV8MaxWasmTables);
// TODO(ahaas): Set the correct limit to {kV8MaxWasmTables} once the
// implementation of AnyRef landed.
uint32_t max_count = FLAG_experimental_wasm_anyref ? 10 : kV8MaxWasmTables;
uint32_t table_count = consume_count("table count", max_count);
for (uint32_t i = 0; ok() && i < table_count; i++) {
if (!AddTable(module_.get())) break;
module_->function_tables.emplace_back();
WasmIndirectFunctionTable* table = &module_->function_tables.back();
expect_u8("table type", kWasmAnyFunctionTypeCode);
module_->tables.emplace_back();
WasmTable* table = &module_->tables.back();
table->type = consume_reference_type();
uint8_t flags = validate_table_flags("table elements");
consume_resizable_limits(
"table elements", "elements", FLAG_wasm_max_table_size,
......@@ -618,7 +620,7 @@ class ModuleDecoderImpl : public Decoder {
break;
}
case kExternalTable: {
WasmIndirectFunctionTable* table = nullptr;
WasmTable* table = nullptr;
exp->index = consume_table_index(module_.get(), &table);
if (table) table->exported = true;
break;
......@@ -694,7 +696,7 @@ class ModuleDecoderImpl : public Decoder {
uint32_t element_count =
consume_count("element count", FLAG_wasm_max_table_size);
if (element_count > 0 && module_->function_tables.size() == 0) {
if (element_count > 0 && module_->tables.size() == 0) {
error(pc_, "The element section requires a table");
}
for (uint32_t i = 0; ok() && i < element_count; ++i) {
......@@ -703,7 +705,7 @@ class ModuleDecoderImpl : public Decoder {
if (table_index != 0) {
errorf(pos, "illegal table index %u != 0", table_index);
}
if (table_index >= module_->function_tables.size()) {
if (table_index >= module_->tables.size()) {
errorf(pos, "out of bounds table index %u", table_index);
break;
}
......@@ -932,7 +934,8 @@ class ModuleDecoderImpl : public Decoder {
}
bool AddTable(WasmModule* module) {
if (module->function_tables.size() > 0) {
if (FLAG_experimental_wasm_anyref) return true;
if (module->tables.size() > 0) {
error("At most one table is supported");
return false;
} else {
......@@ -1079,9 +1082,8 @@ class ModuleDecoderImpl : public Decoder {
return consume_index("global index", module->globals, global);
}
uint32_t consume_table_index(WasmModule* module,
WasmIndirectFunctionTable** table) {
return consume_index("table index", module->function_tables, table);
uint32_t consume_table_index(WasmModule* module, WasmTable** table) {
return consume_index("table index", module->tables, table);
}
template <typename T>
......@@ -1295,6 +1297,26 @@ class ModuleDecoderImpl : public Decoder {
}
}
// Reads a single 8-bit integer, interpreting it as a reference type.
ValueType consume_reference_type() {
byte val = consume_u8("reference type");
ValueTypeCode t = static_cast<ValueTypeCode>(val);
switch (t) {
case kLocalAnyFunc:
return kWasmAnyFunc;
case kLocalAnyRef:
if (!FLAG_experimental_wasm_anyref) {
error(pc_ - 1,
"Invalid type. Set --experimental-wasm-anyref to use 'AnyRef'");
}
return kWasmAnyRef;
default:
break;
}
error(pc_ - 1, "invalid reference type");
return kWasmStmt;
}
FunctionSig* consume_sig(Zone* zone) {
constexpr bool has_return_values = true;
return consume_sig_internal(zone, has_return_values);
......
......@@ -26,7 +26,6 @@ enum ValueTypeCode : uint8_t {
};
// Binary encoding of other types.
constexpr uint8_t kWasmFunctionTypeCode = 0x60;
constexpr uint8_t kWasmAnyFunctionTypeCode = 0x70;
// Binary encoding of import/export kinds.
enum ImportExportKindCode : uint8_t {
......
......@@ -969,16 +969,14 @@ class CodeMap {
InterpreterCode* GetIndirectCode(uint32_t table_index, uint32_t entry_index) {
uint32_t saved_index;
USE(saved_index);
if (table_index >= module_->function_tables.size()) return nullptr;
if (table_index >= module_->tables.size()) return nullptr;
// Mask table index for SSCA mitigation.
saved_index = table_index;
table_index &=
static_cast<int32_t>((table_index - module_->function_tables.size()) &
~static_cast<int32_t>(table_index)) >>
31;
table_index &= static_cast<int32_t>((table_index - module_->tables.size()) &
~static_cast<int32_t>(table_index)) >>
31;
DCHECK_EQ(table_index, saved_index);
const WasmIndirectFunctionTable* table =
&module_->function_tables[table_index];
const WasmTable* table = &module_->tables[table_index];
if (entry_index >= table->values.size()) return nullptr;
// Mask entry_index for SSCA mitigation.
saved_index = entry_index;
......@@ -2249,7 +2247,7 @@ class ThreadImpl {
code->at(pc));
uint32_t entry_index = Pop().to<uint32_t>();
// Assume only one table for now.
DCHECK_LE(module()->function_tables.size(), 1u);
DCHECK_LE(module()->tables.size(), 1u);
ExternalCallResult result =
CallIndirectFunction(0, entry_index, imm.sig_index);
switch (result.type) {
......
......@@ -392,7 +392,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
if (indirect_functions_.size() > 0) {
size_t start = EmitSection(kTableSectionCode, buffer);
buffer.write_u8(1); // table count
buffer.write_u8(kWasmAnyFunctionTypeCode);
buffer.write_u8(kLocalAnyFunc);
buffer.write_u8(kHasMaximumFlag);
buffer.write_size(indirect_functions_.size());
buffer.write_size(indirect_functions_.size());
......
......@@ -330,7 +330,7 @@ size_t EstimateWasmModuleSize(const WasmModule* module) {
size_t estimate =
sizeof(WasmModule) + VectorSize(module->signatures) +
VectorSize(module->signature_ids) + VectorSize(module->functions) +
VectorSize(module->data_segments) + VectorSize(module->function_tables) +
VectorSize(module->data_segments) + VectorSize(module->tables) +
VectorSize(module->import_table) + VectorSize(module->export_table) +
VectorSize(module->exceptions) + VectorSize(module->table_inits);
// TODO(wasm): include names table and wire bytes in size estimate
......
......@@ -73,9 +73,9 @@ struct WasmDataSegment {
};
// Static representation of a wasm indirect call table.
struct WasmIndirectFunctionTable {
MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmIndirectFunctionTable);
struct WasmTable {
MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmTable);
ValueType type = kWasmStmt; // table type.
uint32_t initial_size = 0; // initial table size.
uint32_t maximum_size = 0; // maximum table size.
bool has_maximum_size = false; // true if there is a maximum size.
......@@ -146,7 +146,7 @@ struct V8_EXPORT_PRIVATE WasmModule {
std::vector<uint32_t> signature_ids; // by signature index
std::vector<WasmFunction> functions;
std::vector<WasmDataSegment> data_segments;
std::vector<WasmIndirectFunctionTable> function_tables;
std::vector<WasmTable> tables;
std::vector<WasmImport> import_table;
std::vector<WasmExport> export_table;
std::vector<WasmException> exceptions;
......
......@@ -131,7 +131,7 @@ size_t EstimateNativeAllocationsSize(const WasmModule* module) {
size_t estimate = sizeof(WasmInstanceNativeAllocations) +
(1 * kPointerSize * module->num_imported_mutable_globals) +
(2 * kPointerSize * module->num_imported_functions);
for (auto& table : module->function_tables) {
for (auto& table : module->tables) {
estimate += 3 * kPointerSize * table.initial_size;
}
return estimate;
......
......@@ -147,8 +147,8 @@ Handle<JSFunction> TestingModuleBuilder::WrapCode(uint32_t index) {
void TestingModuleBuilder::AddIndirectFunctionTable(
const uint16_t* function_indexes, uint32_t table_size) {
test_module_->function_tables.emplace_back();
WasmIndirectFunctionTable& table = test_module_->function_tables.back();
test_module_->tables.emplace_back();
WasmTable& table = test_module_->tables.back();
table.initial_size = table_size;
table.maximum_size = table_size;
table.has_maximum_size = true;
......@@ -164,7 +164,7 @@ void TestingModuleBuilder::PopulateIndirectFunctionTable() {
auto instance = instance_object();
uint32_t num_tables = 1; // TODO(titzer): multiple tables.
for (uint32_t i = 0; i < num_tables; i++) {
WasmIndirectFunctionTable& table = test_module_->function_tables[i];
WasmTable& table = test_module_->tables[i];
int table_size = static_cast<int>(instance->indirect_function_table_size());
for (int j = 0; j < table_size; j++) {
WasmFunction& function = test_module_->functions[table.values[j]];
......
......@@ -245,7 +245,7 @@ class TestModuleBuilder {
mod.maximum_pages = 100;
}
void InitializeFunctionTable() { mod.function_tables.emplace_back(); }
void InitializeTable() { mod.tables.emplace_back(); }
WasmModule* module() { return &mod; }
......@@ -1522,7 +1522,7 @@ TEST_F(FunctionBodyDecoderTest, MultiReturnType) {
TEST_F(FunctionBodyDecoderTest, SimpleIndirectCalls) {
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeFunctionTable();
builder.InitializeTable();
module = builder.module();
byte f0 = builder.AddSignature(sigs.i_v());
......@@ -1538,7 +1538,7 @@ TEST_F(FunctionBodyDecoderTest, SimpleIndirectCalls) {
TEST_F(FunctionBodyDecoderTest, IndirectCallsOutOfBounds) {
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeFunctionTable();
builder.InitializeTable();
module = builder.module();
EXPECT_FAILURE_S(sig, WASM_CALL_INDIRECT0(0, WASM_ZERO));
......@@ -1555,7 +1555,7 @@ TEST_F(FunctionBodyDecoderTest, IndirectCallsOutOfBounds) {
TEST_F(FunctionBodyDecoderTest, IndirectCallsWithMismatchedSigs3) {
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeFunctionTable();
builder.InitializeTable();
module = builder.module();
byte f0 = builder.AddFunction(sigs.i_f());
......@@ -1593,7 +1593,7 @@ TEST_F(FunctionBodyDecoderTest, IndirectCallsWithoutTableCrash) {
TEST_F(FunctionBodyDecoderTest, IncompleteIndirectCall) {
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeFunctionTable();
builder.InitializeTable();
module = builder.module();
static byte code[] = {kExprCallIndirect};
......@@ -1604,7 +1604,7 @@ TEST_F(FunctionBodyDecoderTest, IncompleteStore) {
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeMemory();
builder.InitializeFunctionTable();
builder.InitializeTable();
module = builder.module();
static byte code[] = {kExprI32StoreMem};
......@@ -1616,7 +1616,7 @@ TEST_F(FunctionBodyDecoderTest, IncompleteS8x16Shuffle) {
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeMemory();
builder.InitializeFunctionTable();
builder.InitializeTable();
module = builder.module();
static byte code[] = {kSimdPrefix,
......
......@@ -797,22 +797,22 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction) {
// funcs ---------------------------------------------------------------
ONE_EMPTY_FUNCTION,
// table declaration ---------------------------------------------------
SECTION(Table, 4), ENTRY_COUNT(1), kWasmAnyFunctionTypeCode, 0, 1};
SECTION(Table, 4), ENTRY_COUNT(1), kLocalAnyFunc, 0, 1};
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
if (result.ok()) {
EXPECT_EQ(1u, result.val->signatures.size());
EXPECT_EQ(1u, result.val->functions.size());
EXPECT_EQ(1u, result.val->function_tables.size());
EXPECT_EQ(1u, result.val->function_tables[0].initial_size);
EXPECT_EQ(1u, result.val->tables.size());
EXPECT_EQ(1u, result.val->tables[0].initial_size);
}
}
TEST_F(WasmModuleVerifyTest, ElementSectionWithInternalTable) {
static const byte data[] = {
// table ---------------------------------------------------------------
SECTION(Table, 4), ENTRY_COUNT(1), kWasmAnyFunctionTypeCode, 0, 1,
SECTION(Table, 4), ENTRY_COUNT(1), kLocalAnyFunc, 0, 1,
// elements ------------------------------------------------------------
SECTION(Element, 1),
0 // entry count
......@@ -825,14 +825,14 @@ TEST_F(WasmModuleVerifyTest, ElementSectionWithImportedTable) {
static const byte data[] = {
// imports -------------------------------------------------------------
SECTION(Import, 9), ENTRY_COUNT(1),
NAME_LENGTH(1), // --
'm', // module name
NAME_LENGTH(1), // --
't', // table name
kExternalTable, // import kind
kWasmAnyFunctionTypeCode, // elem_type
0, // no maximum field
1, // initial size
NAME_LENGTH(1), // --
'm', // module name
NAME_LENGTH(1), // --
't', // table name
kExternalTable, // import kind
kLocalAnyFunc, // elem_type
0, // no maximum field
1, // initial size
// elements ------------------------------------------------------------
SECTION(Element, 1),
0 // entry count
......@@ -863,7 +863,7 @@ TEST_F(WasmModuleVerifyTest, Regression_735887) {
// funcs ---------------------------------------------------------------
ONE_EMPTY_FUNCTION,
// table declaration ---------------------------------------------------
SECTION(Table, 4), ENTRY_COUNT(1), kWasmAnyFunctionTypeCode, 0, 1,
SECTION(Table, 4), ENTRY_COUNT(1), kLocalAnyFunc, 0, 1,
// elements ------------------------------------------------------------
SECTION(Element, 7),
1, // entry count
......@@ -882,7 +882,7 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction_one_entry) {
// funcs ---------------------------------------------------------------
ONE_EMPTY_FUNCTION,
// table declaration ---------------------------------------------------
SECTION(Table, 4), ENTRY_COUNT(1), kWasmAnyFunctionTypeCode, 0, 1,
SECTION(Table, 4), ENTRY_COUNT(1), kLocalAnyFunc, 0, 1,
// elements ------------------------------------------------------------
SECTION(Element, 7),
1, // entry count
......@@ -895,8 +895,8 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction_one_entry) {
if (result.ok()) {
EXPECT_EQ(1u, result.val->signatures.size());
EXPECT_EQ(1u, result.val->functions.size());
EXPECT_EQ(1u, result.val->function_tables.size());
EXPECT_EQ(1u, result.val->function_tables[0].initial_size);
EXPECT_EQ(1u, result.val->tables.size());
EXPECT_EQ(1u, result.val->tables[0].initial_size);
}
}
......@@ -910,7 +910,7 @@ TEST_F(WasmModuleVerifyTest, MultipleIndirectFunctions) {
// funcs ------------------------------------------------------
FOUR_EMPTY_FUNCTIONS,
// table declaration -------------------------------------------
SECTION(Table, 4), ENTRY_COUNT(1), kWasmAnyFunctionTypeCode, 0, 8,
SECTION(Table, 4), ENTRY_COUNT(1), kLocalAnyFunc, 0, 8,
// table elements ----------------------------------------------
SECTION(Element, 14),
1, // entry count
......@@ -931,8 +931,8 @@ TEST_F(WasmModuleVerifyTest, MultipleIndirectFunctions) {
if (result.ok()) {
EXPECT_EQ(2u, result.val->signatures.size());
EXPECT_EQ(4u, result.val->functions.size());
EXPECT_EQ(1u, result.val->function_tables.size());
EXPECT_EQ(8u, result.val->function_tables[0].initial_size);
EXPECT_EQ(1u, result.val->tables.size());
EXPECT_EQ(8u, result.val->tables[0].initial_size);
}
}
......@@ -960,6 +960,45 @@ TEST_F(WasmModuleVerifyTest, IndirectFunctionInvalidIndex) {
EXPECT_FAILURE(data);
}
TEST_F(WasmModuleVerifyTest, MultipleTablesWithoutFlag) {
static const byte data[] = {
SECTION(Table, 7), // table section
ENTRY_COUNT(2), // 2 tables
kLocalAnyFunc, // table 1: type
0, // table 1: no maximum
10, // table 1: minimum size
kLocalAnyFunc, // table 2: type
0, // table 2: no maximum
10, // table 2: minimum size
};
EXPECT_FAILURE(data);
}
TEST_F(WasmModuleVerifyTest, MultipleTablesWithFlag) {
FlagScope<bool> flag_scope(&FLAG_experimental_wasm_anyref, true);
static const byte data[] = {
SECTION(Table, 7), // table section
ENTRY_COUNT(2), // 2 tables
kLocalAnyFunc, // table 1: type
0, // table 1: no maximum
10, // table 1: minimum size
kLocalAnyRef, // table 2: type
0, // table 2: no maximum
11, // table 2: minimum size
};
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(2u, result.val->tables.size());
EXPECT_EQ(10u, result.val->tables[0].initial_size);
EXPECT_EQ(kWasmAnyFunc, result.val->tables[0].type);
EXPECT_EQ(11u, result.val->tables[1].initial_size);
EXPECT_EQ(kWasmAnyRef, result.val->tables[1].type);
}
class WasmSignatureDecodeTest : public TestWithZone {
public:
WasmSignatureDecodeTest()
......
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