Commit 01dc5707 authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm] Generate code for the table.get and table.set instructions

This CL contains the following changes:
(1) Allocate memory for WasmTables in the WasmInstance.
    - We extend the WasmInstance by a FixedArray which stores
      references to the WasmTables.
(2) Rename the name of the backing store of WasmTables from `functions`
    to `elements`.
    - The name `functions` just does not fit anyref tables.
(3) Generate code with TurboFan for table.get and table.set.
(4) Extend wasm-module-builder.js to be able to generate modules with
    multiple tables.
(5) Add  mjsunit tests to test table.get and table.set.

R=mstarzinger@chromium.org

Bug: v8:7581
Change-Id: I44af4838ee7a37b394841a2f673ecae5734a4d1c
Reviewed-on: https://chromium-review.googlesource.com/c/1463519
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59529}
parent f269293a
...@@ -3215,12 +3215,67 @@ Node* WasmGraphBuilder::SetGlobal(uint32_t index, Node* val) { ...@@ -3215,12 +3215,67 @@ Node* WasmGraphBuilder::SetGlobal(uint32_t index, Node* val) {
graph()->NewNode(op, base, offset, val, Effect(), Control())); graph()->NewNode(op, base, offset, val, Effect(), Control()));
} }
Node* WasmGraphBuilder::GetTable(uint32_t table_index, Node* index) { void WasmGraphBuilder::GetTableBaseAndOffset(uint32_t table_index, Node* index,
UNIMPLEMENTED(); wasm::WasmCodePosition position,
Node** base_node,
Node** offset_node) {
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::kElementsOffset),
assert_size(storage_field_size, MachineType::TaggedPointer()));
int length_field_size =
FixedArray::kLengthOffsetEnd - FixedArray::kLengthOffset + 1;
Node* storage_size =
LOAD_RAW(storage, wasm::ObjectAccess::ToTagged(FixedArray::kLengthOffset),
assert_size(length_field_size, MachineType::TaggedSigned()));
storage_size = BuildChangeSmiToInt32(storage_size);
// Bounds check against the table size.
Node* in_bounds = graph()->NewNode(mcgraph()->machine()->Uint32LessThan(),
index, storage_size);
TrapIfFalse(wasm::kTrapTableOutOfBounds, in_bounds, position);
// From the index, calculate the actual offset in the FixeArray. This
// is kHeaderSize + (index * kTaggedSize). kHeaderSize can be acquired with
// wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(0).
Node* index_times_tagged_size =
graph()->NewNode(mcgraph()->machine()->IntMul(), Uint32ToUintptr(index),
mcgraph()->Int32Constant(kTaggedSize));
*offset_node = graph()->NewNode(
mcgraph()->machine()->IntAdd(), index_times_tagged_size,
mcgraph()->IntPtrConstant(
wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(0)));
*base_node = storage;
}
Node* WasmGraphBuilder::GetTable(uint32_t table_index, Node* index,
wasm::WasmCodePosition position) {
Node* base = nullptr;
Node* offset = nullptr;
GetTableBaseAndOffset(table_index, index, position, &base, &offset);
return SetEffect(
graph()->NewNode(mcgraph()->machine()->Load(MachineType::AnyTagged()),
base, offset, Effect(), Control()));
} }
Node* WasmGraphBuilder::SetTable(uint32_t table_index, Node* index, Node* val) { Node* WasmGraphBuilder::SetTable(uint32_t table_index, Node* index, Node* val,
UNIMPLEMENTED(); wasm::WasmCodePosition position) {
Node* base = nullptr;
Node* offset = nullptr;
GetTableBaseAndOffset(table_index, index, position, &base, &offset);
const Operator* op = mcgraph()->machine()->Store(
StoreRepresentation(MachineRepresentation::kTagged, kFullWriteBarrier));
Node* store = graph()->NewNode(op, base, offset, val, Effect(), Control());
return SetEffect(store);
} }
Node* WasmGraphBuilder::CheckBoundsAndAlignment( Node* WasmGraphBuilder::CheckBoundsAndAlignment(
......
...@@ -274,8 +274,10 @@ class WasmGraphBuilder { ...@@ -274,8 +274,10 @@ class WasmGraphBuilder {
Node* GetGlobal(uint32_t index); Node* GetGlobal(uint32_t index);
Node* SetGlobal(uint32_t index, Node* val); Node* SetGlobal(uint32_t index, Node* val);
Node* GetTable(uint32_t table_index, Node* index); Node* GetTable(uint32_t table_index, Node* index,
Node* SetTable(uint32_t table_index, Node* index, Node* val); wasm::WasmCodePosition position);
Node* SetTable(uint32_t table_index, Node* index, Node* val,
wasm::WasmCodePosition position);
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// Operations that concern the linear memory. // Operations that concern the linear memory.
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
...@@ -323,6 +325,10 @@ class WasmGraphBuilder { ...@@ -323,6 +325,10 @@ class WasmGraphBuilder {
void GetBaseAndOffsetForImportedMutableAnyRefGlobal( void GetBaseAndOffsetForImportedMutableAnyRefGlobal(
const wasm::WasmGlobal& global, Node** base, Node** offset); const wasm::WasmGlobal& global, Node** base, Node** offset);
void GetTableBaseAndOffset(uint32_t table_index, Node* index,
wasm::WasmCodePosition position, Node** base_node,
Node** offset_node);
// Utilities to manipulate sets of instance cache nodes. // Utilities to manipulate sets of instance cache nodes.
void InitInstanceCache(WasmInstanceCacheNodes* instance_cache); void InitInstanceCache(WasmInstanceCacheNodes* instance_cache);
void PrepareInstanceCacheForLoop(WasmInstanceCacheNodes* instance_cache, void PrepareInstanceCacheForLoop(WasmInstanceCacheNodes* instance_cache,
......
...@@ -289,12 +289,12 @@ class WasmGraphBuildingInterface { ...@@ -289,12 +289,12 @@ class WasmGraphBuildingInterface {
void GetTable(FullDecoder* decoder, const Value& index, Value* result, void GetTable(FullDecoder* decoder, const Value& index, Value* result,
const TableIndexImmediate<validate>& imm) { const TableIndexImmediate<validate>& imm) {
result->node = BUILD(GetTable, imm.index, index.node); result->node = BUILD(GetTable, imm.index, index.node, decoder->position());
} }
void SetTable(FullDecoder* decoder, const Value& index, const Value& value, void SetTable(FullDecoder* decoder, const Value& index, const Value& value,
const TableIndexImmediate<validate>& imm) { const TableIndexImmediate<validate>& imm) {
BUILD(SetTable, imm.index, index.node, value.node); BUILD(SetTable, imm.index, index.node, value.node, decoder->position());
} }
void Unreachable(FullDecoder* decoder) { void Unreachable(FullDecoder* decoder) {
......
...@@ -524,6 +524,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -524,6 +524,7 @@ class ModuleDecoderImpl : public Decoder {
// ===== Imported table ========================================== // ===== Imported table ==========================================
if (!AddTable(module_.get())) break; if (!AddTable(module_.get())) break;
import->index = static_cast<uint32_t>(module_->tables.size()); import->index = static_cast<uint32_t>(module_->tables.size());
module_->num_imported_tables++;
module_->tables.emplace_back(); module_->tables.emplace_back();
WasmTable* table = &module_->tables.back(); WasmTable* table = &module_->tables.back();
table->imported = true; table->imported = true;
...@@ -1573,13 +1574,10 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1573,13 +1574,10 @@ class ModuleDecoderImpl : public Decoder {
flags = consume_u32v("flags"); flags = consume_u32v("flags");
if (failed()) return; if (failed()) return;
} else { } else {
flags = consume_u32v(name); // Without the bulk memory proposal, we should still read the table index.
if (failed()) return; // This is the same as reading the `ActiveWithIndex` flag with the bulk
// memory proposal.
if (flags != 0) { flags = SegmentFlags::kActiveWithIndex;
errorf(pos, "illegal %s %u != 0", name, flags);
return;
}
} }
bool read_index; bool read_index;
......
...@@ -383,9 +383,18 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() { ...@@ -383,9 +383,18 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// Reserve the metadata for indirect function tables. // Set up table storage space.
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
int table_count = static_cast<int>(module_->tables.size()); int table_count = static_cast<int>(module_->tables.size());
Handle<FixedArray> tables = isolate_->factory()->NewFixedArray(table_count);
for (int i = module_->num_imported_tables; i < table_count; i++) {
const WasmTable& table = module_->tables[i];
Handle<WasmTableObject> table_obj = WasmTableObject::New(
isolate_, table.initial_size, table.maximum_size, nullptr);
tables->set(i, *table_obj);
}
instance->set_tables(*tables);
table_instances_.resize(table_count); table_instances_.resize(table_count);
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
...@@ -821,7 +830,7 @@ bool InstanceBuilder::ProcessImportedTable(Handle<WasmInstanceObject> instance, ...@@ -821,7 +830,7 @@ bool InstanceBuilder::ProcessImportedTable(Handle<WasmInstanceObject> instance,
table_instance.table_object = Handle<WasmTableObject>::cast(value); table_instance.table_object = Handle<WasmTableObject>::cast(value);
instance->set_table_object(*table_instance.table_object); instance->set_table_object(*table_instance.table_object);
table_instance.js_functions = table_instance.js_functions =
Handle<FixedArray>(table_instance.table_object->functions(), isolate_); Handle<FixedArray>(table_instance.table_object->elements(), isolate_);
int imported_table_size = table_instance.js_functions->length(); int imported_table_size = table_instance.js_functions->length();
if (imported_table_size < static_cast<int>(table.initial_size)) { if (imported_table_size < static_cast<int>(table.initial_size)) {
...@@ -1445,13 +1454,12 @@ void InstanceBuilder::InitializeTables(Handle<WasmInstanceObject> instance) { ...@@ -1445,13 +1454,12 @@ void InstanceBuilder::InitializeTables(Handle<WasmInstanceObject> instance) {
size_t table_count = module_->tables.size(); size_t table_count = module_->tables.size();
for (size_t index = 0; index < table_count; ++index) { for (size_t index = 0; index < table_count; ++index) {
const WasmTable& table = module_->tables[index]; const WasmTable& table = module_->tables[index];
TableInstance& table_instance = table_instances_[index];
if (!instance->has_indirect_function_table() && if (!instance->has_indirect_function_table() &&
table.type == kWasmAnyFunc) { table.type == kWasmAnyFunc) {
WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize( WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
instance, table.initial_size); instance, table.initial_size);
table_instance.table_size = table.initial_size; table_instances_[index].table_size = table.initial_size;
} }
} }
} }
...@@ -1567,7 +1575,7 @@ bool LoadElemSegment(Isolate* isolate, Handle<WasmInstanceObject> instance, ...@@ -1567,7 +1575,7 @@ bool LoadElemSegment(Isolate* isolate, Handle<WasmInstanceObject> instance,
Handle<FixedArray> js_functions; Handle<FixedArray> js_functions;
if (instance->has_table_object()) { if (instance->has_table_object()) {
table_object = Handle<WasmTableObject>(instance->table_object(), isolate); table_object = Handle<WasmTableObject>(instance->table_object(), isolate);
js_functions = Handle<FixedArray>(table_object->functions(), isolate); js_functions = Handle<FixedArray>(table_object->elements(), isolate);
} }
TableInstance table_instance = {table_object, js_functions, TableInstance table_instance = {table_object, js_functions,
......
...@@ -1328,7 +1328,7 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1328,7 +1328,7 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
return; return;
} }
i::Handle<i::FixedArray> old_array(receiver->functions(), i_isolate); i::Handle<i::FixedArray> old_array(receiver->elements(), i_isolate);
uint32_t old_size = static_cast<uint32_t>(old_array->length()); uint32_t old_size = static_cast<uint32_t>(old_array->length());
uint64_t max_size64 = receiver->maximum_length()->Number(); uint64_t max_size64 = receiver->maximum_length()->Number();
...@@ -1356,7 +1356,7 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1356,7 +1356,7 @@ void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
} }
i::Object null = i::ReadOnlyRoots(i_isolate).null_value(); i::Object null = i::ReadOnlyRoots(i_isolate).null_value();
for (uint32_t i = old_size; i < new_size; ++i) new_array->set(i, null); for (uint32_t i = old_size; i < new_size; ++i) new_array->set(i, null);
receiver->set_functions(*new_array); receiver->set_elements(*new_array);
} }
// TODO(gdeepti): use weak links for instances // TODO(gdeepti): use weak links for instances
...@@ -1372,7 +1372,7 @@ void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1372,7 +1372,7 @@ void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {
ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.get()"); ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.get()");
Local<Context> context = isolate->GetCurrentContext(); Local<Context> context = isolate->GetCurrentContext();
EXTRACT_THIS(receiver, WasmTableObject); EXTRACT_THIS(receiver, WasmTableObject);
i::Handle<i::FixedArray> array(receiver->functions(), i_isolate); i::Handle<i::FixedArray> array(receiver->elements(), i_isolate);
uint32_t index; uint32_t index;
if (!EnforceUint32("Argument 0", args[0], context, &thrower, &index)) { if (!EnforceUint32("Argument 0", args[0], context, &thrower, &index)) {
...@@ -1412,7 +1412,7 @@ void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1412,7 +1412,7 @@ void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
return; return;
} }
if (index >= static_cast<uint64_t>(receiver->functions()->length())) { if (index >= static_cast<uint64_t>(receiver->elements()->length())) {
thrower.RangeError("index out of bounds"); thrower.RangeError("index out of bounds");
return; return;
} }
......
...@@ -169,6 +169,7 @@ struct V8_EXPORT_PRIVATE WasmModule { ...@@ -169,6 +169,7 @@ struct V8_EXPORT_PRIVATE WasmModule {
uint32_t tagged_globals_buffer_size = 0; uint32_t tagged_globals_buffer_size = 0;
uint32_t num_imported_mutable_globals = 0; uint32_t num_imported_mutable_globals = 0;
uint32_t num_imported_functions = 0; uint32_t num_imported_functions = 0;
uint32_t num_imported_tables = 0;
uint32_t num_declared_functions = 0; // excluding imported uint32_t num_declared_functions = 0; // excluding imported
uint32_t num_exported_functions = 0; uint32_t num_exported_functions = 0;
uint32_t num_declared_data_segments = 0; // From the DataCount section. uint32_t num_declared_data_segments = 0; // From the DataCount section.
......
...@@ -102,7 +102,7 @@ bool WasmModuleObject::is_asm_js() { ...@@ -102,7 +102,7 @@ bool WasmModuleObject::is_asm_js() {
} }
// WasmTableObject // WasmTableObject
ACCESSORS(WasmTableObject, functions, FixedArray, kFunctionsOffset) ACCESSORS(WasmTableObject, elements, FixedArray, kElementsOffset)
ACCESSORS(WasmTableObject, maximum_length, Object, kMaximumLengthOffset) ACCESSORS(WasmTableObject, maximum_length, Object, kMaximumLengthOffset)
ACCESSORS(WasmTableObject, dispatch_tables, FixedArray, kDispatchTablesOffset) ACCESSORS(WasmTableObject, dispatch_tables, FixedArray, kDispatchTablesOffset)
...@@ -222,6 +222,7 @@ OPTIONAL_ACCESSORS(WasmInstanceObject, debug_info, WasmDebugInfo, ...@@ -222,6 +222,7 @@ OPTIONAL_ACCESSORS(WasmInstanceObject, debug_info, WasmDebugInfo,
kDebugInfoOffset) kDebugInfoOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, table_object, WasmTableObject, OPTIONAL_ACCESSORS(WasmInstanceObject, table_object, WasmTableObject,
kTableObjectOffset) kTableObjectOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, tables, FixedArray, kTablesOffset)
ACCESSORS(WasmInstanceObject, imported_function_refs, FixedArray, ACCESSORS(WasmInstanceObject, imported_function_refs, FixedArray,
kImportedFunctionRefsOffset) kImportedFunctionRefsOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, indirect_function_table_refs, FixedArray, OPTIONAL_ACCESSORS(WasmInstanceObject, indirect_function_table_refs, FixedArray,
...@@ -297,7 +298,7 @@ OPTIONAL_ACCESSORS(WasmDebugInfo, c_wasm_entry_map, Managed<wasm::SignatureMap>, ...@@ -297,7 +298,7 @@ OPTIONAL_ACCESSORS(WasmDebugInfo, c_wasm_entry_map, Managed<wasm::SignatureMap>,
#undef WRITE_PRIMITIVE_FIELD #undef WRITE_PRIMITIVE_FIELD
#undef PRIMITIVE_ACCESSORS #undef PRIMITIVE_ACCESSORS
uint32_t WasmTableObject::current_length() { return functions()->length(); } uint32_t WasmTableObject::current_length() { return elements()->length(); }
bool WasmMemoryObject::has_maximum_pages() { return maximum_pages() >= 0; } bool WasmMemoryObject::has_maximum_pages() { return maximum_pages() >= 0; }
......
...@@ -783,16 +783,19 @@ Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial, ...@@ -783,16 +783,19 @@ Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
auto table_obj = Handle<WasmTableObject>::cast( auto table_obj = Handle<WasmTableObject>::cast(
isolate->factory()->NewJSObject(table_ctor)); isolate->factory()->NewJSObject(table_ctor));
*js_functions = isolate->factory()->NewFixedArray(initial); Handle<FixedArray> backing_store = isolate->factory()->NewFixedArray(initial);
Object null = ReadOnlyRoots(isolate).null_value(); Object null = ReadOnlyRoots(isolate).null_value();
for (int i = 0; i < static_cast<int>(initial); ++i) { for (int i = 0; i < static_cast<int>(initial); ++i) {
(*js_functions)->set(i, null); backing_store->set(i, null);
} }
table_obj->set_functions(**js_functions); table_obj->set_elements(*backing_store);
Handle<Object> max = isolate->factory()->NewNumberFromUint(maximum); Handle<Object> max = isolate->factory()->NewNumberFromUint(maximum);
table_obj->set_maximum_length(*max); table_obj->set_maximum_length(*max);
table_obj->set_dispatch_tables(ReadOnlyRoots(isolate).empty_fixed_array()); table_obj->set_dispatch_tables(ReadOnlyRoots(isolate).empty_fixed_array());
if (js_functions != nullptr) {
*js_functions = backing_store;
}
return Handle<WasmTableObject>::cast(table_obj); return Handle<WasmTableObject>::cast(table_obj);
} }
...@@ -825,7 +828,7 @@ void WasmTableObject::Grow(Isolate* isolate, uint32_t count) { ...@@ -825,7 +828,7 @@ void WasmTableObject::Grow(Isolate* isolate, uint32_t count) {
Handle<FixedArray> dispatch_tables(this->dispatch_tables(), isolate); Handle<FixedArray> dispatch_tables(this->dispatch_tables(), isolate);
DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements); DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements);
uint32_t old_size = functions()->length(); uint32_t old_size = elements()->length();
// Tables are stored in the instance object, no code patching is // Tables are stored in the instance object, no code patching is
// necessary. We simply have to grow the raw tables in each instance // necessary. We simply have to grow the raw tables in each instance
...@@ -846,7 +849,7 @@ void WasmTableObject::Grow(Isolate* isolate, uint32_t count) { ...@@ -846,7 +849,7 @@ void WasmTableObject::Grow(Isolate* isolate, uint32_t count) {
void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table, void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
uint32_t table_index, Handle<JSFunction> function) { uint32_t table_index, Handle<JSFunction> function) {
Handle<FixedArray> array(table->functions(), isolate); Handle<FixedArray> array(table->elements(), isolate);
if (function.is_null()) { if (function.is_null()) {
ClearDispatchTables(isolate, table, table_index); // Degenerate case. ClearDispatchTables(isolate, table, table_index); // Degenerate case.
array->set(table_index, ReadOnlyRoots(isolate).null_value()); array->set(table_index, ReadOnlyRoots(isolate).null_value());
...@@ -1447,7 +1450,7 @@ bool WasmInstanceObject::CopyTableEntries(Isolate* isolate, ...@@ -1447,7 +1450,7 @@ bool WasmInstanceObject::CopyTableEntries(Isolate* isolate,
auto max = instance->indirect_function_table_size(); auto max = instance->indirect_function_table_size();
if (!IsInBounds(dst, count, max)) return false; if (!IsInBounds(dst, count, max)) return false;
if (!IsInBounds(src, count, max)) return false; if (!IsInBounds(src, count, max)) return false;
if (dst == src) return true; // no-op if (dst == src) return true; // no-op
if (!instance->has_table_object()) { if (!instance->has_table_object()) {
// No table object, only need to update this instance. // No table object, only need to update this instance.
...@@ -1469,7 +1472,7 @@ bool WasmInstanceObject::CopyTableEntries(Isolate* isolate, ...@@ -1469,7 +1472,7 @@ bool WasmInstanceObject::CopyTableEntries(Isolate* isolate,
} }
// Copy the function entries. // Copy the function entries.
Handle<FixedArray> functions(table->functions(), isolate); Handle<FixedArray> functions(table->elements(), isolate);
if (src < dst) { if (src < dst) {
for (uint32_t i = count; i > 0; i--) { for (uint32_t i = count; i > 0; i--) {
functions->set(dst + i - 1, functions->get(src + i - 1)); functions->set(dst + i - 1, functions->get(src + i - 1));
......
...@@ -254,14 +254,14 @@ class WasmTableObject : public JSObject { ...@@ -254,14 +254,14 @@ class WasmTableObject : public JSObject {
public: public:
DECL_CAST(WasmTableObject) DECL_CAST(WasmTableObject)
DECL_ACCESSORS(functions, FixedArray) DECL_ACCESSORS(elements, FixedArray)
// TODO(titzer): introduce DECL_I64_ACCESSORS macro // TODO(titzer): introduce DECL_I64_ACCESSORS macro
DECL_ACCESSORS(maximum_length, Object) DECL_ACCESSORS(maximum_length, Object)
DECL_ACCESSORS(dispatch_tables, FixedArray) DECL_ACCESSORS(dispatch_tables, FixedArray)
// Layout description. // Layout description.
#define WASM_TABLE_OBJECT_FIELDS(V) \ #define WASM_TABLE_OBJECT_FIELDS(V) \
V(kFunctionsOffset, kTaggedSize) \ V(kElementsOffset, kTaggedSize) \
V(kMaximumLengthOffset, kTaggedSize) \ V(kMaximumLengthOffset, kTaggedSize) \
V(kDispatchTablesOffset, kTaggedSize) \ V(kDispatchTablesOffset, kTaggedSize) \
V(kSize, 0) V(kSize, 0)
...@@ -405,6 +405,7 @@ class WasmInstanceObject : public JSObject { ...@@ -405,6 +405,7 @@ class WasmInstanceObject : public JSObject {
DECL_OPTIONAL_ACCESSORS(imported_mutable_globals_buffers, FixedArray) DECL_OPTIONAL_ACCESSORS(imported_mutable_globals_buffers, FixedArray)
DECL_OPTIONAL_ACCESSORS(debug_info, WasmDebugInfo) DECL_OPTIONAL_ACCESSORS(debug_info, WasmDebugInfo)
DECL_OPTIONAL_ACCESSORS(table_object, WasmTableObject) DECL_OPTIONAL_ACCESSORS(table_object, WasmTableObject)
DECL_OPTIONAL_ACCESSORS(tables, FixedArray)
DECL_ACCESSORS(imported_function_refs, FixedArray) DECL_ACCESSORS(imported_function_refs, FixedArray)
DECL_OPTIONAL_ACCESSORS(indirect_function_table_refs, FixedArray) DECL_OPTIONAL_ACCESSORS(indirect_function_table_refs, FixedArray)
DECL_OPTIONAL_ACCESSORS(managed_native_allocations, Foreign) DECL_OPTIONAL_ACCESSORS(managed_native_allocations, Foreign)
...@@ -449,6 +450,7 @@ class WasmInstanceObject : public JSObject { ...@@ -449,6 +450,7 @@ class WasmInstanceObject : public JSObject {
V(kImportedMutableGlobalsBuffersOffset, kTaggedSize) \ V(kImportedMutableGlobalsBuffersOffset, kTaggedSize) \
V(kDebugInfoOffset, kTaggedSize) \ V(kDebugInfoOffset, kTaggedSize) \
V(kTableObjectOffset, kTaggedSize) \ V(kTableObjectOffset, kTaggedSize) \
V(kTablesOffset, kTaggedSize) \
V(kImportedFunctionRefsOffset, kTaggedSize) \ V(kImportedFunctionRefsOffset, kTaggedSize) \
V(kIndirectFunctionTableRefsOffset, kTaggedSize) \ V(kIndirectFunctionTableRefsOffset, kTaggedSize) \
V(kManagedNativeAllocationsOffset, kTaggedSize) \ V(kManagedNativeAllocationsOffset, kTaggedSize) \
......
...@@ -211,6 +211,8 @@ let kExprSetLocal = 0x21; ...@@ -211,6 +211,8 @@ let kExprSetLocal = 0x21;
let kExprTeeLocal = 0x22; let kExprTeeLocal = 0x22;
let kExprGetGlobal = 0x23; let kExprGetGlobal = 0x23;
let kExprSetGlobal = 0x24; let kExprSetGlobal = 0x24;
let kExprGetTable = 0x25;
let kExprSetTable = 0x26;
let kExprI32Const = 0x41; let kExprI32Const = 0x41;
let kExprI64Const = 0x42; let kExprI64Const = 0x42;
let kExprF32Const = 0x43; let kExprF32Const = 0x43;
...@@ -469,6 +471,7 @@ let kTrapTypeError = 8; ...@@ -469,6 +471,7 @@ let kTrapTypeError = 8;
let kTrapUnalignedAccess = 9; let kTrapUnalignedAccess = 9;
let kTrapDataSegmentDropped = 10; let kTrapDataSegmentDropped = 10;
let kTrapElemSegmentDropped = 11; let kTrapElemSegmentDropped = 11;
let kTrapTableOutOfBounds = 12;
let kTrapMsgs = [ let kTrapMsgs = [
"unreachable", "unreachable",
...@@ -482,7 +485,8 @@ let kTrapMsgs = [ ...@@ -482,7 +485,8 @@ let kTrapMsgs = [
"wasm function signature contains illegal type", "wasm function signature contains illegal type",
"operation does not support unaligned accesses", "operation does not support unaligned accesses",
"data segment has been dropped", "data segment has been dropped",
"element segment has been dropped" "element segment has been dropped",
"table access out of bounds"
]; ];
function assertTraps(trap, code) { function assertTraps(trap, code) {
...@@ -656,21 +660,37 @@ class WasmGlobalBuilder { ...@@ -656,21 +660,37 @@ class WasmGlobalBuilder {
} }
} }
class WasmTableBuilder {
constructor(module, type, initial_size, max_size) {
this.module = module;
this.type = type;
this.initial_size = initial_size;
this.has_max = max_size != undefined;
this.max_size = max_size;
}
exportAs(name) {
this.module.exports.push({name: name, kind: kExternalTable,
index: this.index});
return this;
}
}
class WasmModuleBuilder { class WasmModuleBuilder {
constructor() { constructor() {
this.types = []; this.types = [];
this.imports = []; this.imports = [];
this.exports = []; this.exports = [];
this.globals = []; this.globals = [];
this.tables = [];
this.exceptions = []; this.exceptions = [];
this.functions = []; this.functions = [];
this.table_length_min = 0;
this.table_length_max = undefined;
this.element_segments = []; this.element_segments = [];
this.data_segments = []; this.data_segments = [];
this.explicit = []; this.explicit = [];
this.num_imported_funcs = 0; this.num_imported_funcs = 0;
this.num_imported_globals = 0; this.num_imported_globals = 0;
this.num_imported_tables = 0;
this.num_imported_exceptions = 0; this.num_imported_exceptions = 0;
return this; return this;
} }
...@@ -723,6 +743,16 @@ class WasmModuleBuilder { ...@@ -723,6 +743,16 @@ class WasmModuleBuilder {
return glob; return glob;
} }
addTable(type, initial_size, max_size = undefined) {
if (type != kWasmAnyRef && type != kWasmAnyFunc) {
throw new Error('Tables must be of type kWasmAnyRef or kWasmAnyFunc');
}
let table = new WasmTableBuilder(this, type, initial_size, max_size);
table.index = this.tables.length + this.num_imported_tables;
this.tables.push(table);
return table;
}
addException(type) { addException(type) {
let type_index = (typeof type) == "number" ? type : this.addType(type); let type_index = (typeof type) == "number" ? type : this.addType(type);
let except_index = this.exceptions.length + this.num_imported_exceptions; let except_index = this.exceptions.length + this.num_imported_exceptions;
...@@ -766,9 +796,13 @@ class WasmModuleBuilder { ...@@ -766,9 +796,13 @@ class WasmModuleBuilder {
} }
addImportedTable(module, name, initial, maximum) { addImportedTable(module, name, initial, maximum) {
if (this.tables.length != 0) {
throw new Error('Imported tables must be declared before local ones');
}
let o = {module: module, name: name, kind: kExternalTable, initial: initial, let o = {module: module, name: name, kind: kExternalTable, initial: initial,
maximum: maximum}; maximum: maximum};
this.imports.push(o); this.imports.push(o);
return this.num_imported_tables++;
} }
addImportedException(module, name, type) { addImportedException(module, name, type) {
...@@ -807,15 +841,19 @@ class WasmModuleBuilder { ...@@ -807,15 +841,19 @@ class WasmModuleBuilder {
} }
addElementSegment(base, is_global, array, is_import = false) { addElementSegment(base, is_global, array, is_import = false) {
if (this.tables.length + this.num_imported_tables == 0) {
this.addTable(kWasmAnyFunc, 0);
}
this.element_segments.push({base: base, is_global: is_global, this.element_segments.push({base: base, is_global: is_global,
array: array, is_active: true}); array: array, is_active: true});
if (!is_global) { if (!is_global) {
var length = base + array.length; var length = base + array.length;
if (length > this.table_length_min && !is_import) { if (!is_import && length > this.tables[0].initial_size) {
this.table_length_min = length; this.tables[0].initial_size = length;
} }
if (length > this.table_length_max && !is_import) { if (!is_import && this.tables[0].has_max &&
this.table_length_max = length; length > this.tables[0].max_size) {
this.tables[0].max_size = length;
} }
} }
return this; return this;
...@@ -831,12 +869,17 @@ class WasmModuleBuilder { ...@@ -831,12 +869,17 @@ class WasmModuleBuilder {
if (typeof n != 'number') if (typeof n != 'number')
throw new Error('invalid table (entries have to be numbers): ' + array); throw new Error('invalid table (entries have to be numbers): ' + array);
} }
return this.addElementSegment(this.table_length_min, false, array); if (this.tables.length == 0) {
this.addTable(kWasmAnyFunc, 0);
}
return this.addElementSegment(this.tables[0].initial_size, false, array);
} }
setTableBounds(min, max = undefined) { setTableBounds(min, max = undefined) {
this.table_length_min = min; if (this.tables.length != 0) {
this.table_length_max = max; throw new Error("The table bounds of table '0' have already been set.");
}
this.addTable(kWasmAnyFunc, min, max);
return this; return this;
} }
...@@ -923,16 +966,16 @@ class WasmModuleBuilder { ...@@ -923,16 +966,16 @@ class WasmModuleBuilder {
} }
// Add table section // Add table section
if (wasm.table_length_min > 0) { if (wasm.tables.length > 0) {
if (debug) print("emitting table @ " + binary.length); if (debug) print ("emitting tables @ " + binary.length);
binary.emit_section(kTableSectionCode, section => { binary.emit_section(kTableSectionCode, section => {
section.emit_u8(1); // one table entry section.emit_u32v(wasm.tables.length);
section.emit_u8(kWasmAnyFunctionTypeForm); for (let table of wasm.tables) {
const max = wasm.table_length_max; section.emit_u8(table.type);
const has_max = max !== undefined; section.emit_u8(table.has_max);
section.emit_u8(has_max ? kHasMaximumFlag : 0); section.emit_u32v(table.initial_size);
section.emit_u32v(wasm.table_length_min); if (table.has_max) section.emit_u32v(table.max_size);
if (has_max) section.emit_u32v(max); }
}); });
} }
......
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