Index: src/wasm/wasm-module.cc |
diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc |
index 4aa5445b49bc537b92e1f0bf7cb574e258e013ad..aa385f824e6917b38815aadf8d047c4fa5964ea4 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(); |
} |
@@ -500,7 +528,7 @@ static void ResetCompiledModule(Isolate* isolate, JSObject* owner, |
// table references. |
Object* fct_obj = compiled_module->ptr_to_code_table(); |
if (fct_obj != nullptr && fct_obj != undefined && |
- (old_mem_size > 0 || globals_start != nullptr)) { |
+ (old_mem_size > 0 || globals_start != nullptr || function_tables)) { |
FixedArray* functions = FixedArray::cast(fct_obj); |
for (int i = 0; i < functions->length(); ++i) { |
Code* code = Code::cast(functions->get(i)); |
@@ -517,9 +545,9 @@ static void ResetCompiledModule(Isolate* isolate, JSObject* owner, |
changed = true; |
} else if (RelocInfo::IsEmbeddedObject(mode) && function_tables) { |
Object* old = it.rinfo()->target_object(); |
- for (int i = 0; i < function_tables->length(); ++i) { |
- if (function_tables->get(i) == old) { |
- it.rinfo()->set_target_object(empty_function_tables->get(i)); |
+ for (int j = 0; j < function_tables->length(); ++j) { |
+ if (function_tables->get(j) == old) { |
+ it.rinfo()->set_target_object(empty_function_tables->get(j)); |
changed = true; |
} |
} |
@@ -843,6 +871,101 @@ 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 Handle<Code> CompileImportWrapper(Isolate* isolate, int index, |
+ FunctionSig* sig, |
+ Handle<JSReceiver> target, |
+ Handle<String> module_name, |
+ MaybeHandle<String> import_name) { |
+ Handle<Code> code; |
+ 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. |
+ return UnwrapImportWrapper(target); |
+ } else { |
+ // Signature mismatch. Compile a new wrapper for the new signature. |
+ return compiler::CompileWasmToJSWrapper(isolate, target, sig, index, |
+ module_name, import_name); |
+ } |
+} |
+ |
+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), |
+ UnwrapImportWrapper(function)); |
+ } |
+} |
+ |
// A helper class to simplify instantiating a module from a compiled module. |
// It closes over the {Isolate}, the {ErrorThrower}, the {WasmCompiledModule}, |
// etc. |
@@ -951,13 +1074,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; |
} |
@@ -972,15 +1094,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. |
@@ -1004,7 +1138,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 = |
@@ -1035,75 +1169,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. |
@@ -1192,11 +1265,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_; |
@@ -1205,7 +1277,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, |
@@ -1264,14 +1339,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(); |
@@ -1280,11 +1354,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)) { |
@@ -1297,55 +1370,7 @@ class WasmInstanceBuilder { |
} |
} |
- Handle<Code> CompileImportWrapper(int index, const WasmImport& import, |
- Handle<JSReceiver> target, |
- Handle<String> module_name, |
- 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 |
- // 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; |
- } else { |
- // Signature mismatch. Compile a new wrapper for the new signature. |
- return compiler::CompileWasmToJSWrapper(isolate_, target, sig, index, |
- module_name, import_name); |
- } |
- } |
- |
- 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(); |
@@ -1358,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(); |
@@ -1378,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]; |
@@ -1412,16 +1437,64 @@ class WasmInstanceBuilder { |
} |
Handle<Code> import_wrapper = CompileImportWrapper( |
- index, import, Handle<JSReceiver>::cast(function), module_name, |
- function_name); |
+ isolate_, index, module_->functions[import.index].sig, |
+ Handle<JSReceiver>::cast(function), module_name, function_name); |
code_table->set(num_imported_functions, *import_wrapper); |
RecordStats(isolate_, *import_wrapper); |
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)) { |
@@ -1435,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()) { |
@@ -1444,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: |
@@ -1456,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. |
@@ -1488,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: |
@@ -1504,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(); |
} |
@@ -1519,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_); |
@@ -1556,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 = |
@@ -1568,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: { |
@@ -1622,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(); |
@@ -1649,47 +1717,120 @@ class WasmInstanceBuilder { |
return; |
} |
} |
+ } |
- // 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. |
+ 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)); |
+ } |
+ } |
+ |
+ 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); |
} |
}; |
@@ -1960,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; |
@@ -1972,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; |