Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1018)

Unified Diff: src/wasm/wasm-module.cc

Issue 2454503005: [wasm] Support for restricted table imports. (Closed)
Patch Set: Fix GC stress issue; tables weren't being reset correctly Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/wasm/wasm-module.h ('k') | test/cctest/wasm/wasm-run-utils.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
« no previous file with comments | « src/wasm/wasm-module.h ('k') | test/cctest/wasm/wasm-run-utils.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698