Chromium Code Reviews| Index: src/wasm/wasm-module.cc |
| diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc |
| index 7bc382600051d64fd7ecb84818174cf2edca4ba6..a1a4fc8bdabb9843f5c173a116f61a9ed6ddd080 100644 |
| --- a/src/wasm/wasm-module.cc |
| +++ b/src/wasm/wasm-module.cc |
| @@ -37,6 +37,34 @@ namespace base = v8::base; |
| instance->PrintInstancesChain(); \ |
| } while (false) |
| +static const int kInvalidSigIndex = -1; |
| + |
| +// Collects all the data values to which a given WASM code object may be |
| +// specialized. |
| +struct Specialization { |
| + // The native context, which is used in JS->WASM and WASM->JS wrappers |
| + // and calls to the runtime. |
| + Handle<Context> context; |
| + |
| + // Specialization to the memory. |
| + byte* memory_base; |
| + uint32_t memory_size; |
| + |
| + // Specialization to the globals. |
| + byte* globals_base; |
| + |
| + // Specialization to the function table. |
| + uint32_t function_table_size; |
| + Handle<FixedArray> function_table_sigs; |
| + Handle<FixedArray> function_table_code; |
| + |
| + Specialization() |
| + : memory_base(nullptr), |
| + memory_size(0), |
| + globals_base(nullptr), |
| + function_table_size(0) {} |
| +}; |
| + |
| namespace { |
| static const int kPlaceholderMarker = 1000000000; |
| @@ -87,7 +115,7 @@ void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref, |
| } |
| Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size) { |
| - if (size > (WasmModule::kMaxMemPages * WasmModule::kPageSize)) { |
| + if (size > (WasmModule::kV8MaxPages * WasmModule::kPageSize)) { |
| // TODO(titzer): lift restriction on maximum memory allocated here. |
| return Handle<JSArrayBuffer>::null(); |
| } |
| @@ -842,6 +870,83 @@ MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions( |
| return ret; |
| } |
| +static WasmFunction* GetWasmFunctionForImportWrapper(Isolate* isolate, |
| + Handle<Object> target) { |
| + if (target->IsJSFunction()) { |
| + Handle<JSFunction> func = Handle<JSFunction>::cast(target); |
| + Handle<Code> export_wrapper_code = handle(func->code()); |
| + if (export_wrapper_code->kind() == Code::JS_TO_WASM_FUNCTION) { |
| + Handle<JSObject> other_instance( |
| + JSObject::cast(func->GetInternalField(kInternalModuleInstance)), |
| + isolate); |
| + int func_index = |
| + Smi::cast(func->GetInternalField(kInternalFunctionIndex))->value(); |
| + return &GetCppModule(other_instance)->functions[func_index]; |
| + } |
| + } |
| + return nullptr; |
| +} |
| + |
| +static Handle<Code> UnwrapImportWrapper(Handle<Object> target) { |
| + Handle<JSFunction> func = Handle<JSFunction>::cast(target); |
| + Handle<Code> export_wrapper_code = handle(func->code()); |
| + int found = 0; |
| + int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); |
| + Handle<Code> code; |
| + for (RelocIterator it(*export_wrapper_code, mask); !it.done(); it.next()) { |
| + RelocInfo* rinfo = it.rinfo(); |
| + Address target_address = rinfo->target_address(); |
| + Code* target = Code::GetCodeFromTargetAddress(target_address); |
| + if (target->kind() == Code::WASM_FUNCTION || |
| + target->kind() == Code::WASM_TO_JS_FUNCTION) { |
| + ++found; |
| + code = handle(target); |
| + } |
| + } |
| + DCHECK(found == 1); |
| + return code; |
| +} |
| + |
| +static void UpdateDispatchTablesInternal(Isolate* isolate, |
| + Handle<FixedArray> dispatch_tables, |
| + int index, WasmFunction* function, |
| + Handle<Code> code) { |
| + DCHECK_EQ(0, dispatch_tables->length() % 3); |
| + for (int i = 0; i < dispatch_tables->length(); i += 3) { |
| + Handle<Object> instance(dispatch_tables->get(i), isolate); |
| + WasmModule* module = GetCppModule(Handle<JSObject>::cast(instance)); |
| + int table_index = Smi::cast(dispatch_tables->get(i + 1))->value(); |
| + Handle<FixedArray> dispatch_table( |
| + FixedArray::cast(dispatch_tables->get(i + 2)), isolate); |
| + if (function) { |
| + // TODO(titzer): the signature might need to be copied to avoid |
| + // a dangling pointer in the signature map. |
| + int sig_index = static_cast<int>( |
| + module->function_tables[table_index].map.FindOrInsert(function->sig)); |
| + dispatch_table->set(index, Smi::FromInt(sig_index)); |
| + dispatch_table->set(index + (dispatch_table->length() / 2), *code); |
| + } else { |
| + Code* code = nullptr; |
| + dispatch_table->set(index, Smi::FromInt(-1)); |
| + dispatch_table->set(index + (dispatch_table->length() / 2), code); |
| + } |
| + } |
| +} |
| + |
| +void wasm::UpdateDispatchTables(Isolate* isolate, |
| + Handle<FixedArray> dispatch_tables, int index, |
| + Handle<JSFunction> function) { |
| + if (function.is_null()) { |
| + UpdateDispatchTablesInternal(isolate, dispatch_tables, index, nullptr, |
| + Handle<Code>::null()); |
| + } else { |
| + UpdateDispatchTablesInternal( |
| + isolate, dispatch_tables, index, |
| + GetWasmFunctionForImportWrapper(isolate, function), |
|
bradnelson
2016/10/28 16:59:01
Seems like CompileImportWrapper below could get us
titzer
2016/10/28 17:29:08
Done.
|
| + UnwrapImportWrapper(function)); |
| + } |
| +} |
| + |
| // A helper class to simplify instantiating a module from a compiled module. |
| // It closes over the {Isolate}, the {ErrorThrower}, the {WasmCompiledModule}, |
| // etc. |
| @@ -950,13 +1055,12 @@ class WasmInstanceBuilder { |
| // Set up the globals for the new instance. |
| //-------------------------------------------------------------------------- |
| MaybeHandle<JSArrayBuffer> old_globals; |
| - MaybeHandle<JSArrayBuffer> globals; |
| uint32_t globals_size = module_->globals_size; |
| if (globals_size > 0) { |
| Handle<JSArrayBuffer> global_buffer = |
| NewArrayBuffer(isolate_, globals_size); |
| - globals = global_buffer; |
| - if (globals.is_null()) { |
| + globals_ = global_buffer; |
| + if (globals_.is_null()) { |
| thrower_->RangeError("Out of memory: wasm globals"); |
| return nothing; |
| } |
| @@ -971,15 +1075,27 @@ class WasmInstanceBuilder { |
| } |
| //-------------------------------------------------------------------------- |
| + // Prepare for initialization of 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_.push_back({Handle<JSObject>::null(), |
| + Handle<FixedArray>::null(), |
| + Handle<FixedArray>::null()}); |
| + } |
| + |
| + //-------------------------------------------------------------------------- |
| // Process the imports for the module. |
| //-------------------------------------------------------------------------- |
| - int num_imported_functions = ProcessImports(globals, code_table, instance); |
| + int num_imported_functions = ProcessImports(code_table, instance); |
| if (num_imported_functions < 0) return nothing; |
| //-------------------------------------------------------------------------- |
| // Process the initialization for the module's globals. |
| //-------------------------------------------------------------------------- |
| - InitGlobals(globals); |
| + InitGlobals(); |
| //-------------------------------------------------------------------------- |
| // Set up the memory for the new instance. |
| @@ -1003,7 +1119,7 @@ class WasmInstanceBuilder { |
| Address mem_start = static_cast<Address>(memory_->backing_store()); |
| uint32_t mem_size = |
| static_cast<uint32_t>(memory_->byte_length()->Number()); |
| - LoadDataSegments(globals, mem_start, mem_size); |
| + LoadDataSegments(mem_start, mem_size); |
| uint32_t old_mem_size = compiled_module_->mem_size(); |
| Address old_mem_start = |
| @@ -1034,75 +1150,14 @@ class WasmInstanceBuilder { |
| } |
| //-------------------------------------------------------------------------- |
| - // Set up the indirect function tables for the new instance. |
| + // Set up the exports object for the new instance. |
| //-------------------------------------------------------------------------- |
| - int function_table_count = |
| - static_cast<int>(module_->function_tables.size()); |
| - std::vector<InitializedTable> inited_tables; |
| - inited_tables.reserve(module_->function_tables.size()); |
| - if (function_table_count > 0) { |
| - Handle<FixedArray> old_function_tables = |
| - compiled_module_->function_tables(); |
| - Handle<FixedArray> new_function_tables = |
| - factory->NewFixedArray(function_table_count); |
| - for (int index = 0; index < function_table_count; ++index) { |
| - WasmIndirectFunctionTable& table = module_->function_tables[index]; |
| - uint32_t table_size = table.size; |
| - Handle<FixedArray> new_table = factory->NewFixedArray(table_size * 2); |
| - |
| - inited_tables.push_back({Handle<JSObject>::null(), |
| - Handle<FixedArray>::null(), new_table, |
| - std::vector<WasmFunction*>()}); |
| - InitializedTable& init_table = inited_tables.back(); |
| - if (table.exported) { |
| - init_table.new_entries.insert(init_table.new_entries.begin(), |
| - table_size, nullptr); |
| - } |
| - |
| - for (int i = 0; i < new_table->length(); ++i) { |
| - static const int kInvalidSigIndex = -1; |
| - // Fill the table with invalid signature indexes so that uninitialized |
| - // entries will always fail the signature check. |
| - new_table->set(i, Smi::FromInt(kInvalidSigIndex)); |
| - } |
| - for (auto table_init : module_->table_inits) { |
| - uint32_t base = EvalUint32InitExpr(globals, table_init.offset); |
| - if (base > table_size || |
| - (base + table_init.entries.size() > table_size)) { |
| - thrower_->CompileError("table initializer is out of bounds"); |
| - continue; |
| - } |
| - for (size_t i = 0; i < table_init.entries.size(); ++i) { |
| - WasmFunction* func = &module_->functions[table_init.entries[i]]; |
| - if (table.exported) { |
| - init_table.new_entries[i + base] = func; |
| - } |
| - FunctionSig* sig = func->sig; |
| - int32_t sig_index = table.map.Find(sig); |
| - new_table->set(static_cast<int>(i + base), Smi::FromInt(sig_index)); |
| - new_table->set(static_cast<int>(i + base + table_size), |
| - code_table->get(table_init.entries[i])); |
| - } |
| - } |
| - new_function_tables->set(static_cast<int>(index), *new_table); |
| - } |
| - // Patch all code that has references to the old indirect table. |
| - for (int i = 0; i < code_table->length(); ++i) { |
| - if (!code_table->get(i)->IsCode()) continue; |
| - Handle<Code> code(Code::cast(code_table->get(i)), isolate_); |
| - for (int j = 0; j < function_table_count; ++j) { |
| - ReplaceReferenceInCode( |
| - code, Handle<Object>(old_function_tables->get(j), isolate_), |
| - Handle<Object>(new_function_tables->get(j), isolate_)); |
| - } |
| - } |
| - compiled_module_->set_function_tables(new_function_tables); |
| - } |
| + ProcessExports(code_table, instance); |
| //-------------------------------------------------------------------------- |
| - // Set up the exports object for the new instance. |
| + // Set up the indirect function tables for the new instance. |
| //-------------------------------------------------------------------------- |
| - ProcessExports(globals, inited_tables, code_table, instance); |
| + if (function_table_count > 0) InitializeTables(code_table, instance); |
| if (num_imported_functions > 0 || !owner.is_null()) { |
| // If the code was cloned, or new imports were compiled, patch. |
| @@ -1191,11 +1246,10 @@ class WasmInstanceBuilder { |
| private: |
| // Represents the initialized state of a table. |
| - struct InitializedTable { |
| + struct TableInstance { |
| Handle<JSObject> table_object; // WebAssembly.Table instance |
| - Handle<FixedArray> js_functions; // JSFunctions exported |
| + Handle<FixedArray> js_wrappers; // JSFunctions exported |
| Handle<FixedArray> dispatch_table; // internal (code, sig) pairs |
| - std::vector<WasmFunction*> new_entries; // overwriting entries |
| }; |
| Isolate* isolate_; |
| @@ -1204,7 +1258,10 @@ class WasmInstanceBuilder { |
| Handle<JSObject> module_object_; |
| Handle<JSReceiver> ffi_; |
| Handle<JSArrayBuffer> memory_; |
| + Handle<JSArrayBuffer> globals_; |
| Handle<WasmCompiledModule> compiled_module_; |
| + std::vector<TableInstance> table_instances_; |
| + std::vector<Handle<JSFunction>> js_wrappers_; |
| // Helper routine to print out errors with imports (FFI). |
| MaybeHandle<JSFunction> ReportFFIError(const char* error, uint32_t index, |
| @@ -1263,14 +1320,13 @@ class WasmInstanceBuilder { |
| return result; |
| } |
| - uint32_t EvalUint32InitExpr(MaybeHandle<JSArrayBuffer> globals, |
| - WasmInitExpr& expr) { |
| + uint32_t EvalUint32InitExpr(WasmInitExpr& expr) { |
| switch (expr.kind) { |
| case WasmInitExpr::kI32Const: |
| return expr.val.i32_const; |
| case WasmInitExpr::kGlobalIndex: { |
| uint32_t offset = module_->globals[expr.val.global_index].offset; |
| - return *reinterpret_cast<uint32_t*>(raw_buffer_ptr(globals, offset)); |
| + return *reinterpret_cast<uint32_t*>(raw_buffer_ptr(globals_, offset)); |
| } |
| default: |
| UNREACHABLE(); |
| @@ -1279,11 +1335,10 @@ class WasmInstanceBuilder { |
| } |
| // Load data segments into the memory. |
| - void LoadDataSegments(MaybeHandle<JSArrayBuffer> globals, Address mem_addr, |
| - size_t mem_size) { |
| + void LoadDataSegments(Address mem_addr, size_t mem_size) { |
| Handle<SeqOneByteString> module_bytes = compiled_module_->module_bytes(); |
| for (auto segment : module_->data_segments) { |
| - uint32_t dest_offset = EvalUint32InitExpr(globals, segment.dest_addr); |
| + uint32_t dest_offset = EvalUint32InitExpr(segment.dest_addr); |
| uint32_t source_size = segment.source_size; |
| if (dest_offset >= mem_size || source_size >= mem_size || |
| dest_offset >= (mem_size - source_size)) { |
| @@ -1302,40 +1357,12 @@ class WasmInstanceBuilder { |
| MaybeHandle<String> import_name) { |
| FunctionSig* sig = module_->functions[import.index].sig; |
| Handle<Code> code; |
| - bool is_match = false; |
| - Handle<Code> export_wrapper_code; |
| - if (target->IsJSFunction()) { |
| - Handle<JSFunction> func = Handle<JSFunction>::cast(target); |
| - export_wrapper_code = handle(func->code()); |
| - if (export_wrapper_code->kind() == Code::JS_TO_WASM_FUNCTION) { |
| - // Compare signature of other exported wasm function. |
| - Handle<JSObject> other_instance( |
| - JSObject::cast(func->GetInternalField(kInternalModuleInstance)), |
| - isolate_); |
| - int func_index = |
| - Smi::cast(func->GetInternalField(kInternalFunctionIndex))->value(); |
| - FunctionSig* other_sig = |
| - GetCppModule(other_instance)->functions[func_index].sig; |
| - is_match = sig->Equals(other_sig); |
| - } |
| - } |
| - if (is_match) { |
| - // Signature matched. Unwrap the JS->WASM wrapper and return the naked |
| + WasmFunction* other_func = |
| + GetWasmFunctionForImportWrapper(isolate_, target); |
| + if (other_func && sig->Equals(other_func->sig)) { |
| + // Signature matched. Unwrap the JS->WASM wrapper and return the raw |
| // WASM function code. |
| - int wasm_count = 0; |
| - int const mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); |
| - for (RelocIterator it(*export_wrapper_code, mask); !it.done(); |
| - it.next()) { |
| - RelocInfo* rinfo = it.rinfo(); |
| - Address target_address = rinfo->target_address(); |
| - Code* target = Code::GetCodeFromTargetAddress(target_address); |
| - if (target->kind() == Code::WASM_FUNCTION) { |
| - ++wasm_count; |
| - code = handle(target); |
| - } |
| - } |
| - DCHECK(wasm_count == 1); |
| - return code; |
| + return UnwrapImportWrapper(target); |
| } else { |
| // Signature mismatch. Compile a new wrapper for the new signature. |
| return compiler::CompileWasmToJSWrapper(isolate_, target, sig, index, |
| @@ -1343,8 +1370,7 @@ class WasmInstanceBuilder { |
| } |
| } |
| - void WriteGlobalValue(WasmGlobal& global, MaybeHandle<JSArrayBuffer> globals, |
| - Handle<Object> value) { |
| + void WriteGlobalValue(WasmGlobal& global, Handle<Object> value) { |
| double num = 0; |
| if (value->IsSmi()) { |
| num = Smi::cast(*value)->value(); |
| @@ -1357,17 +1383,17 @@ class WasmInstanceBuilder { |
| WasmOpcodes::TypeName(global.type)); |
| switch (global.type) { |
| case kAstI32: |
| - *GetRawGlobalPtr<int32_t>(global, globals) = static_cast<int32_t>(num); |
| + *GetRawGlobalPtr<int32_t>(global) = static_cast<int32_t>(num); |
| break; |
| case kAstI64: |
| // TODO(titzer): initialization of imported i64 globals. |
| UNREACHABLE(); |
| break; |
| case kAstF32: |
| - *GetRawGlobalPtr<float>(global, globals) = static_cast<float>(num); |
| + *GetRawGlobalPtr<float>(global) = static_cast<float>(num); |
| break; |
| case kAstF64: |
| - *GetRawGlobalPtr<double>(global, globals) = static_cast<double>(num); |
| + *GetRawGlobalPtr<double>(global) = static_cast<double>(num); |
| break; |
| default: |
| UNREACHABLE(); |
| @@ -1377,9 +1403,9 @@ class WasmInstanceBuilder { |
| // Process the imports, including functions, tables, globals, and memory, in |
| // order, loading them from the {ffi_} object. Returns the number of imported |
| // functions. |
| - int ProcessImports(MaybeHandle<JSArrayBuffer> globals, |
| - Handle<FixedArray> code_table, Handle<JSObject> instance) { |
| + int ProcessImports(Handle<FixedArray> code_table, Handle<JSObject> instance) { |
| int num_imported_functions = 0; |
| + int num_imported_tables = 0; |
| for (int index = 0; index < static_cast<int>(module_->import_table.size()); |
| ++index) { |
| WasmImport& import = module_->import_table[index]; |
| @@ -1418,9 +1444,57 @@ class WasmInstanceBuilder { |
| num_imported_functions++; |
| break; |
| } |
| - case kExternalTable: |
| - // TODO(titzer): Table imports must be a WebAssembly.Table. |
| + case kExternalTable: { |
| + Handle<Object> value = result.ToHandleChecked(); |
| + if (!WasmJs::IsWasmTableObject(isolate_, value)) { |
| + ReportFFIError("table import requires a WebAssembly.Table", index, |
| + module_name, function_name); |
| + return -1; |
| + } |
| + WasmIndirectFunctionTable& table = |
| + module_->function_tables[num_imported_tables]; |
| + TableInstance& table_instance = table_instances_[num_imported_tables]; |
| + table_instance.table_object = Handle<JSObject>::cast(value); |
| + table_instance.js_wrappers = WasmJs::GetWasmTableFunctions( |
| + isolate_, table_instance.table_object); |
| + |
| + // TODO(titzer): import table size must match exactly for now. |
| + int table_size = table_instance.js_wrappers->length(); |
| + if (table_size != table.min_size) { |
| + thrower_->TypeError( |
| + "table import %d is wrong size (%d), expected %u", index, |
| + table_size, table.min_size); |
| + return -1; |
| + } |
| + |
| + // Allocate a new dispatch table. |
| + table_instance.dispatch_table = |
| + isolate_->factory()->NewFixedArray(table_size * 2); |
| + for (int i = 0; i < table_size * 2; i++) { |
| + table_instance.dispatch_table->set(i, |
| + Smi::FromInt(kInvalidSigIndex)); |
| + } |
| + // Initialize the dispatch table with the (foreign) JS functions |
| + // that are already in the table. |
| + for (int i = 0; i < table_size; i++) { |
| + Handle<Object> val(table_instance.js_wrappers->get(i), isolate_); |
| + if (!val->IsJSFunction()) continue; |
| + WasmFunction* function = |
| + GetWasmFunctionForImportWrapper(isolate_, val); |
| + if (function == nullptr) { |
| + thrower_->TypeError("table import %d[%d] is not a WASM function", |
| + index, i); |
| + return -1; |
| + } |
| + int sig_index = table.map.FindOrInsert(function->sig); |
| + table_instance.dispatch_table->set(i, Smi::FromInt(sig_index)); |
| + table_instance.dispatch_table->set(i + table_size, |
| + *UnwrapImportWrapper(val)); |
| + } |
| + |
| + num_imported_tables++; |
| break; |
| + } |
| case kExternalMemory: { |
| Handle<Object> object = result.ToHandleChecked(); |
| if (!WasmJs::IsWasmMemoryObject(isolate_, object)) { |
| @@ -1434,7 +1508,7 @@ class WasmInstanceBuilder { |
| } |
| case kExternalGlobal: { |
| // Global imports are converted to numbers and written into the |
| - // {globals} array buffer. |
| + // {globals_} array buffer. |
| Handle<Object> object = result.ToHandleChecked(); |
| MaybeHandle<Object> number = Object::ToNumber(object); |
| if (number.is_null()) { |
| @@ -1443,7 +1517,7 @@ class WasmInstanceBuilder { |
| return -1; |
| } |
| Handle<Object> val = number.ToHandleChecked(); |
| - WriteGlobalValue(module_->globals[import.index], globals, val); |
| + WriteGlobalValue(module_->globals[import.index], val); |
| break; |
| } |
| default: |
| @@ -1455,27 +1529,25 @@ class WasmInstanceBuilder { |
| } |
| template <typename T> |
| - T* GetRawGlobalPtr(WasmGlobal& global, MaybeHandle<JSArrayBuffer> globals) { |
| - return reinterpret_cast<T*>(raw_buffer_ptr(globals, global.offset)); |
| + T* GetRawGlobalPtr(WasmGlobal& global) { |
| + return reinterpret_cast<T*>(raw_buffer_ptr(globals_, global.offset)); |
| } |
| // Process initialization of globals. |
| - void InitGlobals(MaybeHandle<JSArrayBuffer> globals) { |
| + void InitGlobals() { |
| for (auto global : module_->globals) { |
| switch (global.init.kind) { |
| case WasmInitExpr::kI32Const: |
| - *GetRawGlobalPtr<int32_t>(global, globals) = |
| - global.init.val.i32_const; |
| + *GetRawGlobalPtr<int32_t>(global) = global.init.val.i32_const; |
| break; |
| case WasmInitExpr::kI64Const: |
| - *GetRawGlobalPtr<int64_t>(global, globals) = |
| - global.init.val.i64_const; |
| + *GetRawGlobalPtr<int64_t>(global) = global.init.val.i64_const; |
| break; |
| case WasmInitExpr::kF32Const: |
| - *GetRawGlobalPtr<float>(global, globals) = global.init.val.f32_const; |
| + *GetRawGlobalPtr<float>(global) = global.init.val.f32_const; |
| break; |
| case WasmInitExpr::kF64Const: |
| - *GetRawGlobalPtr<double>(global, globals) = global.init.val.f64_const; |
| + *GetRawGlobalPtr<double>(global) = global.init.val.f64_const; |
| break; |
| case WasmInitExpr::kGlobalIndex: { |
| // Initialize with another global. |
| @@ -1487,8 +1559,8 @@ class WasmInstanceBuilder { |
| size_t size = (global.type == kAstI64 || global.type == kAstF64) |
| ? sizeof(double) |
| : sizeof(int32_t); |
| - memcpy(raw_buffer_ptr(globals, new_offset), |
| - raw_buffer_ptr(globals, old_offset), size); |
| + memcpy(raw_buffer_ptr(globals_, new_offset), |
| + raw_buffer_ptr(globals_, old_offset), size); |
| break; |
| } |
| case WasmInitExpr::kNone: |
| @@ -1503,7 +1575,7 @@ class WasmInstanceBuilder { |
| // Allocate memory for a module instance as a new JSArrayBuffer. |
| Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages) { |
| - if (min_mem_pages > WasmModule::kMaxMemPages) { |
| + if (min_mem_pages > WasmModule::kV8MaxPages) { |
| thrower_->RangeError("Out of memory: wasm memory too large"); |
| return Handle<JSArrayBuffer>::null(); |
| } |
| @@ -1518,30 +1590,29 @@ class WasmInstanceBuilder { |
| // Process the exports, creating wrappers for functions, tables, memories, |
| // and globals. |
| - void ProcessExports(MaybeHandle<JSArrayBuffer> globals, |
| - std::vector<InitializedTable>& inited_tables, |
| - Handle<FixedArray> code_table, |
| + void ProcessExports(Handle<FixedArray> code_table, |
| Handle<JSObject> instance) { |
| - if (module_->export_table.size() == 0) return; |
| - |
| - // Allocate a table to cache the exported JSFunctions if needed. |
| - bool has_exported_functions = module_->num_exported_functions > 0; |
| - if (!has_exported_functions) { |
| - for (auto table : module_->function_tables) { |
| - if (table.exported) { |
| - has_exported_functions = true; |
| - break; |
| - } |
| + bool needs_wrappers = module_->num_exported_functions > 0; |
| + for (auto table_instance : table_instances_) { |
| + if (!table_instance.js_wrappers.is_null()) { |
| + needs_wrappers = true; |
| + break; |
| + } |
| + } |
| + for (auto table : module_->function_tables) { |
| + if (table.exported) { |
| + needs_wrappers = true; |
| + break; |
| } |
| } |
| - Handle<FixedArray> exported_functions = |
| - has_exported_functions |
| - ? isolate_->factory()->NewFixedArray( |
| - static_cast<int>(module_->functions.size())) |
| - : Handle<FixedArray>::null(); |
| + if (needs_wrappers) { |
| + // Fill the table to cache the exported JSFunction wrappers. |
| + js_wrappers_.insert(js_wrappers_.begin(), module_->functions.size(), |
| + Handle<JSFunction>::null()); |
| + } |
| Handle<JSObject> exports_object = instance; |
| - if (module_->origin == kWasmOrigin) { |
| + if (module_->export_table.size() > 0 && module_->origin == kWasmOrigin) { |
| // Create the "exports" object. |
| Handle<JSFunction> object_function = Handle<JSFunction>( |
| isolate_->native_context()->object_function(), isolate_); |
| @@ -1555,6 +1626,7 @@ class WasmInstanceBuilder { |
| PropertyDescriptor desc; |
| desc.set_writable(false); |
| + // Process each export in the export table. |
| int func_index = 0; |
| for (auto exp : module_->export_table) { |
| Handle<String> name = |
| @@ -1567,34 +1639,31 @@ class WasmInstanceBuilder { |
| WasmFunction& function = module_->functions[exp.index]; |
| int export_index = |
| static_cast<int>(module_->functions.size() + func_index); |
| - Handle<Object> value(exported_functions->get(exp.index), isolate_); |
| - Handle<JSFunction> js_function; |
| - if (value->IsJSFunction()) { |
| - // There already is a JSFunction for this WASM function. |
| - js_function = Handle<JSFunction>::cast(value); |
| - } else { |
| + Handle<JSFunction> js_function = js_wrappers_[exp.index]; |
| + if (js_function.is_null()) { |
| // Wrap the exported code as a JSFunction. |
| Handle<Code> export_code = |
| code_table->GetValueChecked<Code>(isolate_, export_index); |
| js_function = |
| WrapExportCodeAsJSFunction(isolate_, export_code, name, |
| function.sig, func_index, instance); |
| - exported_functions->set(exp.index, *js_function); |
| + js_wrappers_[exp.index] = js_function; |
| } |
| desc.set_value(js_function); |
| func_index++; |
| break; |
| } |
| case kExternalTable: { |
| - InitializedTable& init_table = inited_tables[exp.index]; |
| + // Export a table as a WebAssembly.Table object. |
| + TableInstance& table_instance = table_instances_[exp.index]; |
| WasmIndirectFunctionTable& table = |
| module_->function_tables[exp.index]; |
| - if (init_table.table_object.is_null()) { |
| - init_table.table_object = WasmJs::CreateWasmTableObject( |
| - isolate_, table.size, table.max_size != 0, table.max_size, |
| - &init_table.js_functions); |
| + if (table_instance.table_object.is_null()) { |
| + table_instance.table_object = WasmJs::CreateWasmTableObject( |
| + isolate_, table.min_size, table.has_max, table.max_size, |
| + &table_instance.js_wrappers); |
| } |
| - desc.set_value(init_table.table_object); |
| + desc.set_value(table_instance.table_object); |
| break; |
| } |
| case kExternalMemory: { |
| @@ -1621,13 +1690,13 @@ class WasmInstanceBuilder { |
| double num = 0; |
| switch (global.type) { |
| case kAstI32: |
| - num = *GetRawGlobalPtr<int32_t>(global, globals); |
| + num = *GetRawGlobalPtr<int32_t>(global); |
| break; |
| case kAstF32: |
| - num = *GetRawGlobalPtr<float>(global, globals); |
| + num = *GetRawGlobalPtr<float>(global); |
| break; |
| case kAstF64: |
| - num = *GetRawGlobalPtr<double>(global, globals); |
| + num = *GetRawGlobalPtr<double>(global); |
| break; |
| default: |
| UNREACHABLE(); |
| @@ -1648,47 +1717,120 @@ class WasmInstanceBuilder { |
| return; |
| } |
| } |
| + } |
| + |
| + void InitializeTables(Handle<FixedArray> code_table, |
| + Handle<JSObject> instance) { |
| + Handle<FixedArray> old_function_tables = |
| + compiled_module_->function_tables(); |
| + int function_table_count = |
| + static_cast<int>(module_->function_tables.size()); |
| + Handle<FixedArray> new_function_tables = |
| + isolate_->factory()->NewFixedArray(function_table_count); |
| + for (int index = 0; index < function_table_count; ++index) { |
| + WasmIndirectFunctionTable& table = module_->function_tables[index]; |
| + TableInstance& table_instance = table_instances_[index]; |
| + int table_size = static_cast<int>(table.min_size); |
| + |
| + if (table_instance.dispatch_table.is_null()) { |
| + // Create a new dispatch table if necessary. |
| + table_instance.dispatch_table = |
| + isolate_->factory()->NewFixedArray(table_size * 2); |
| + for (int i = 0; i < table_size; ++i) { |
| + // Fill the table with invalid signature indexes so that |
| + // uninitialized entries will always fail the signature check. |
| + table_instance.dispatch_table->set(i, Smi::FromInt(kInvalidSigIndex)); |
| + } |
| + } |
| - // Fill out the contents of WebAssembly.Table instances. |
| - if (!exported_functions.is_null()) { |
| - // TODO(titzer): We compile JS->WASM wrappers for any function that is a |
| - // member of an exported indirect table that is not itself exported. This |
| - // should be done at compile time and cached instead. |
| - WasmInstance temp_instance(module_); |
| - temp_instance.context = isolate_->native_context(); |
| - temp_instance.mem_size = 0; |
| - temp_instance.mem_start = nullptr; |
| - temp_instance.globals_start = nullptr; |
| - |
| - ModuleEnv module_env; |
| - module_env.module = module_; |
| - module_env.instance = &temp_instance; |
| - module_env.origin = module_->origin; |
| - |
| - // Fill the exported tables with JSFunctions. |
| - for (auto inited_table : inited_tables) { |
| - if (inited_table.js_functions.is_null()) continue; |
| - for (int i = 0; i < static_cast<int>(inited_table.new_entries.size()); |
| - i++) { |
| - if (inited_table.new_entries[i] == nullptr) continue; |
| - int func_index = |
| - static_cast<int>(inited_table.new_entries[i]->func_index); |
| - if (!exported_functions->get(func_index)->IsJSFunction()) { |
| - // No JSFunction entry yet exists for this function. Create one. |
| + new_function_tables->set(static_cast<int>(index), |
| + *table_instance.dispatch_table); |
| + |
| + Handle<FixedArray> all_dispatch_tables; |
| + if (!table_instance.table_object.is_null()) { |
| + // Get the existing dispatch table(s) with the WebAssembly.Table object. |
| + all_dispatch_tables = WasmJs::AddWasmTableDispatchTable( |
| + isolate_, table_instance.table_object, Handle<JSObject>::null(), |
| + index, Handle<FixedArray>::null()); |
| + } |
| + |
| + // TODO(titzer): this does redundant work if there are multiple tables, |
| + // since initializations are not sorted by table index. |
| + for (auto table_init : module_->table_inits) { |
| + uint32_t base = EvalUint32InitExpr(table_init.offset); |
| + if (base > static_cast<uint32_t>(table_size) || |
| + (base + table_init.entries.size() > |
| + static_cast<uint32_t>(table_size))) { |
| + thrower_->CompileError("table initializer is out of bounds"); |
| + continue; |
| + } |
| + for (int i = 0; i < static_cast<int>(table_init.entries.size()); ++i) { |
| + uint32_t func_index = table_init.entries[i]; |
| + WasmFunction* function = &module_->functions[func_index]; |
| + int table_index = static_cast<int>(i + base); |
| + int32_t sig_index = table.map.Find(function->sig); |
| + DCHECK_GE(sig_index, 0); |
| + table_instance.dispatch_table->set(table_index, |
| + Smi::FromInt(sig_index)); |
| + table_instance.dispatch_table->set(table_index + table_size, |
| + code_table->get(func_index)); |
| + |
| + if (!all_dispatch_tables.is_null()) { |
| Handle<Code> wasm_code(Code::cast(code_table->get(func_index)), |
| isolate_); |
| - Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper( |
| - isolate_, &module_env, wasm_code, func_index); |
| - Handle<JSFunction> js_function = WrapExportCodeAsJSFunction( |
| - isolate_, wrapper_code, isolate_->factory()->empty_string(), |
| - module_->functions[func_index].sig, func_index, instance); |
| - exported_functions->set(func_index, *js_function); |
| + if (js_wrappers_[func_index].is_null()) { |
| + // No JSFunction entry yet exists for this function. Create one. |
| + // TODO(titzer): We compile JS->WASM wrappers for functions are |
| + // not exported but are in an exported table. This should be done |
| + // at module compile time and cached instead. |
| + WasmInstance temp_instance(module_); |
| + temp_instance.context = isolate_->native_context(); |
| + temp_instance.mem_size = 0; |
| + temp_instance.mem_start = nullptr; |
| + temp_instance.globals_start = nullptr; |
| + |
| + ModuleEnv module_env; |
| + module_env.module = module_; |
| + module_env.instance = &temp_instance; |
| + module_env.origin = module_->origin; |
| + |
| + Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper( |
| + isolate_, &module_env, wasm_code, func_index); |
| + Handle<JSFunction> js_function = WrapExportCodeAsJSFunction( |
| + isolate_, wrapper_code, isolate_->factory()->empty_string(), |
| + function->sig, func_index, instance); |
| + js_wrappers_[func_index] = js_function; |
| + } |
| + table_instance.js_wrappers->set(table_index, |
| + *js_wrappers_[func_index]); |
| + |
| + UpdateDispatchTablesInternal(isolate_, all_dispatch_tables, |
| + table_index, function, wasm_code); |
| } |
| - inited_table.js_functions->set(i, |
| - exported_functions->get(func_index)); |
| } |
| } |
| + |
| + // TODO(titzer): we add the new dispatch table at the end to avoid |
| + // redundant work and also because the new instance is not yet fully |
| + // initialized. |
| + if (!table_instance.table_object.is_null()) { |
| + // Add the new dispatch table to the WebAssembly.Table object. |
| + all_dispatch_tables = WasmJs::AddWasmTableDispatchTable( |
| + isolate_, table_instance.table_object, instance, index, |
| + table_instance.dispatch_table); |
| + } |
| + } |
| + // Patch all code that has references to the old indirect tables. |
| + for (int i = 0; i < code_table->length(); ++i) { |
| + if (!code_table->get(i)->IsCode()) continue; |
| + Handle<Code> code(Code::cast(code_table->get(i)), isolate_); |
| + for (int j = 0; j < function_table_count; ++j) { |
| + ReplaceReferenceInCode( |
| + code, Handle<Object>(old_function_tables->get(j), isolate_), |
| + Handle<Object>(new_function_tables->get(j), isolate_)); |
| + } |
| } |
| + compiled_module_->set_function_tables(new_function_tables); |
| } |
| }; |
| @@ -1959,7 +2101,7 @@ int32_t wasm::GetInstanceMemorySize(Isolate* isolate, |
| } |
| uint32_t GetMaxInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance) { |
| - uint32_t max_pages = WasmModule::kMaxMemPages; |
| + uint32_t max_pages = WasmModule::kV8MaxPages; |
| Handle<Object> memory_object(instance->GetInternalField(kWasmMemObject), |
| isolate); |
| if (memory_object->IsUndefined(isolate)) return max_pages; |
| @@ -1971,7 +2113,7 @@ int32_t wasm::GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance, |
| if (!IsWasmInstance(*instance)) return -1; |
| if (pages == 0) return GetInstanceMemorySize(isolate, instance); |
| uint32_t max_pages = GetMaxInstanceMemorySize(isolate, instance); |
| - if (WasmModule::kMaxMemPages < max_pages) return -1; |
| + if (WasmModule::kV8MaxPages < max_pages) return -1; |
| Address old_mem_start = nullptr; |
| uint32_t old_size = 0, new_size = 0; |