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

Side by Side Diff: src/wasm/wasm-module.cc

Issue 2443353002: [wasm] Add support for exporting WebAssembly.Table instances. (Closed)
Patch Set: [wasm] Add support for exporting WebAssembly.Table instances. Created 4 years, 1 month 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 unified diff | Download patch
OLDNEW
1 // Copyright 2015 the V8 project authors. All rights reserved. 1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <memory> 5 #include <memory>
6 6
7 #include "src/base/atomic-utils.h" 7 #include "src/base/atomic-utils.h"
8 #include "src/code-stubs.h" 8 #include "src/code-stubs.h"
9 9
10 #include "src/macro-assembler.h" 10 #include "src/macro-assembler.h"
(...skipping 1020 matching lines...) Expand 10 before | Expand all | Expand 10 after
1031 deopt_data->set_length(2); 1031 deopt_data->set_length(2);
1032 code->set_deoptimization_data(*deopt_data); 1032 code->set_deoptimization_data(*deopt_data);
1033 } 1033 }
1034 } 1034 }
1035 1035
1036 //-------------------------------------------------------------------------- 1036 //--------------------------------------------------------------------------
1037 // Set up the indirect function tables for the new instance. 1037 // Set up the indirect function tables for the new instance.
1038 //-------------------------------------------------------------------------- 1038 //--------------------------------------------------------------------------
1039 int function_table_count = 1039 int function_table_count =
1040 static_cast<int>(module_->function_tables.size()); 1040 static_cast<int>(module_->function_tables.size());
1041 std::vector<InitializedTable> inited_tables;
1042 inited_tables.reserve(module_->function_tables.size());
1041 if (function_table_count > 0) { 1043 if (function_table_count > 0) {
1042 Handle<FixedArray> old_function_tables = 1044 Handle<FixedArray> old_function_tables =
1043 compiled_module_->function_tables(); 1045 compiled_module_->function_tables();
1044 Handle<FixedArray> new_function_tables = 1046 Handle<FixedArray> new_function_tables =
1045 factory->NewFixedArray(function_table_count); 1047 factory->NewFixedArray(function_table_count);
1046 for (int index = 0; index < function_table_count; ++index) { 1048 for (int index = 0; index < function_table_count; ++index) {
1047 WasmIndirectFunctionTable& table = module_->function_tables[index]; 1049 WasmIndirectFunctionTable& table = module_->function_tables[index];
1048 uint32_t size = table.max_size > 0 ? table.max_size : table.size; 1050 uint32_t table_size = table.size;
1049 Handle<FixedArray> new_table = factory->NewFixedArray(size * 2); 1051 Handle<FixedArray> new_table = factory->NewFixedArray(table_size * 2);
1052
1053 inited_tables.push_back({Handle<JSObject>::null(),
1054 Handle<FixedArray>::null(), new_table,
1055 std::vector<WasmFunction*>()});
1056 InitializedTable& init_table = inited_tables.back();
1057 if (table.exported) {
1058 init_table.new_entries.insert(init_table.new_entries.begin(),
bradnelson 2016/10/25 07:33:39 There will be one by default correct?
titzer 2016/10/25 08:27:37 This code handles any number of tables with any si
1059 table_size, nullptr);
1060 }
1061
1050 for (int i = 0; i < new_table->length(); ++i) { 1062 for (int i = 0; i < new_table->length(); ++i) {
1051 static const int kInvalidSigIndex = -1; 1063 static const int kInvalidSigIndex = -1;
1052 // Fill the table with invalid signature indexes so that uninitialized 1064 // Fill the table with invalid signature indexes so that uninitialized
1053 // entries will always fail the signature check. 1065 // entries will always fail the signature check.
1054 new_table->set(i, Smi::FromInt(kInvalidSigIndex)); 1066 new_table->set(i, Smi::FromInt(kInvalidSigIndex));
1055 } 1067 }
1056 for (auto table_init : module_->table_inits) { 1068 for (auto table_init : module_->table_inits) {
1057 uint32_t base = EvalUint32InitExpr(globals, table_init.offset); 1069 uint32_t base = EvalUint32InitExpr(globals, table_init.offset);
1058 uint32_t table_size = static_cast<uint32_t>(new_table->length());
1059 if (base > table_size || 1070 if (base > table_size ||
1060 (base + table_init.entries.size() > table_size)) { 1071 (base + table_init.entries.size() > table_size)) {
1061 thrower_->CompileError("table initializer is out of bounds"); 1072 thrower_->CompileError("table initializer is out of bounds");
1062 continue; 1073 continue;
1063 } 1074 }
1064 for (size_t i = 0; i < table_init.entries.size(); ++i) { 1075 for (size_t i = 0; i < table_init.entries.size(); ++i) {
1065 FunctionSig* sig = module_->functions[table_init.entries[i]].sig; 1076 WasmFunction* func = &module_->functions[table_init.entries[i]];
1077 if (table.exported) {
1078 init_table.new_entries[i + base] = func;
1079 }
1080 FunctionSig* sig = func->sig;
1066 int32_t sig_index = table.map.Find(sig); 1081 int32_t sig_index = table.map.Find(sig);
1067 new_table->set(static_cast<int>(i + base), Smi::FromInt(sig_index)); 1082 new_table->set(static_cast<int>(i + base), Smi::FromInt(sig_index));
1068 new_table->set(static_cast<int>(i + base + size), 1083 new_table->set(static_cast<int>(i + base + table_size),
1069 code_table->get(table_init.entries[i])); 1084 code_table->get(table_init.entries[i]));
1070 } 1085 }
1071 } 1086 }
1072 new_function_tables->set(static_cast<int>(index), *new_table); 1087 new_function_tables->set(static_cast<int>(index), *new_table);
1073 } 1088 }
1074 // Patch all code that has references to the old indirect table. 1089 // Patch all code that has references to the old indirect table.
1075 for (int i = 0; i < code_table->length(); ++i) { 1090 for (int i = 0; i < code_table->length(); ++i) {
1076 if (!code_table->get(i)->IsCode()) continue; 1091 if (!code_table->get(i)->IsCode()) continue;
1077 Handle<Code> code(Code::cast(code_table->get(i)), isolate_); 1092 Handle<Code> code(Code::cast(code_table->get(i)), isolate_);
1078 for (int j = 0; j < function_table_count; ++j) { 1093 for (int j = 0; j < function_table_count; ++j) {
1079 ReplaceReferenceInCode( 1094 ReplaceReferenceInCode(
1080 code, Handle<Object>(old_function_tables->get(j), isolate_), 1095 code, Handle<Object>(old_function_tables->get(j), isolate_),
1081 Handle<Object>(new_function_tables->get(j), isolate_)); 1096 Handle<Object>(new_function_tables->get(j), isolate_));
1082 } 1097 }
1083 } 1098 }
1084 compiled_module_->set_function_tables(new_function_tables); 1099 compiled_module_->set_function_tables(new_function_tables);
1085 } 1100 }
1086 1101
1087 //-------------------------------------------------------------------------- 1102 //--------------------------------------------------------------------------
1088 // Set up the exports object for the new instance. 1103 // Set up the exports object for the new instance.
1089 //-------------------------------------------------------------------------- 1104 //--------------------------------------------------------------------------
1090 ProcessExports(globals, code_table, instance); 1105 ProcessExports(globals, inited_tables, code_table, instance);
1091 1106
1092 if (num_imported_functions > 0 || !owner.is_null()) { 1107 if (num_imported_functions > 0 || !owner.is_null()) {
1093 // If the code was cloned, or new imports were compiled, patch. 1108 // If the code was cloned, or new imports were compiled, patch.
1094 PatchDirectCalls(old_code_table, code_table, num_imported_functions); 1109 PatchDirectCalls(old_code_table, code_table, num_imported_functions);
1095 } 1110 }
1096 1111
1097 FlushICache(isolate_, code_table); 1112 FlushICache(isolate_, code_table);
1098 1113
1099 //-------------------------------------------------------------------------- 1114 //--------------------------------------------------------------------------
1100 // Set up and link the new instance. 1115 // Set up and link the new instance.
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
1168 } 1183 }
1169 } 1184 }
1170 1185
1171 DCHECK(!isolate_->has_pending_exception()); 1186 DCHECK(!isolate_->has_pending_exception());
1172 TRACE("Finishing instance %d\n", compiled_module_->instance_id()); 1187 TRACE("Finishing instance %d\n", compiled_module_->instance_id());
1173 TRACE_CHAIN(WasmCompiledModule::cast(module_object_->GetInternalField(0))); 1188 TRACE_CHAIN(WasmCompiledModule::cast(module_object_->GetInternalField(0)));
1174 return instance; 1189 return instance;
1175 } 1190 }
1176 1191
1177 private: 1192 private:
1193 // Represents the initialized state of a table.
1194 struct InitializedTable {
1195 Handle<JSObject> table_object; // WebAssembly.Table instance
1196 Handle<FixedArray> js_functions; // JSFunctions exported
1197 Handle<FixedArray> dispatch_table; // internal (code, sig) pairs
1198 std::vector<WasmFunction*> new_entries; // overwriting entries
1199 };
1200
1178 Isolate* isolate_; 1201 Isolate* isolate_;
1179 WasmModule* module_; 1202 WasmModule* module_;
1180 ErrorThrower* thrower_; 1203 ErrorThrower* thrower_;
1181 Handle<JSObject> module_object_; 1204 Handle<JSObject> module_object_;
1182 Handle<JSReceiver> ffi_; 1205 Handle<JSReceiver> ffi_;
1183 Handle<JSArrayBuffer> memory_; 1206 Handle<JSArrayBuffer> memory_;
1184 Handle<WasmCompiledModule> compiled_module_; 1207 Handle<WasmCompiledModule> compiled_module_;
1185 1208
1186 // Helper routine to print out errors with imports (FFI). 1209 // Helper routine to print out errors with imports (FFI).
1187 MaybeHandle<JSFunction> ReportFFIError(const char* error, uint32_t index, 1210 MaybeHandle<JSFunction> ReportFFIError(const char* error, uint32_t index,
(...skipping 301 matching lines...) Expand 10 before | Expand all | Expand 10 after
1489 1512
1490 if (mem_buffer.is_null()) { 1513 if (mem_buffer.is_null()) {
1491 thrower_->RangeError("Out of memory: wasm memory"); 1514 thrower_->RangeError("Out of memory: wasm memory");
1492 } 1515 }
1493 return mem_buffer; 1516 return mem_buffer;
1494 } 1517 }
1495 1518
1496 // Process the exports, creating wrappers for functions, tables, memories, 1519 // Process the exports, creating wrappers for functions, tables, memories,
1497 // and globals. 1520 // and globals.
1498 void ProcessExports(MaybeHandle<JSArrayBuffer> globals, 1521 void ProcessExports(MaybeHandle<JSArrayBuffer> globals,
1522 std::vector<InitializedTable>& inited_tables,
1499 Handle<FixedArray> code_table, 1523 Handle<FixedArray> code_table,
1500 Handle<JSObject> instance) { 1524 Handle<JSObject> instance) {
1501 if (module_->export_table.size() == 0) return; 1525 if (module_->export_table.size() == 0) return;
1502 1526
1527 // If there are any exported tables, cache the JSFunctions created
rossberg 2016/10/25 08:19:20 Don't you need to do that anyway, in case the same
titzer 2016/10/25 08:23:13 I don't think we've spec'd that case yet. As it is
rossberg 2016/10/25 08:28:59 Actually, JS.md explicitly requires it. ;)
1528 // for export to reuse in the exported tables.
1529 Handle<FixedArray> exported_functions;
1530 for (auto table : module_->function_tables) {
1531 if (table.exported) {
1532 exported_functions = isolate_->factory()->NewFixedArray(
1533 static_cast<int>(module_->functions.size()));
1534 break;
1535 }
1536 }
1537
1503 Handle<JSObject> exports_object = instance; 1538 Handle<JSObject> exports_object = instance;
1504 if (module_->origin == kWasmOrigin) { 1539 if (module_->origin == kWasmOrigin) {
1505 // Create the "exports" object. 1540 // Create the "exports" object.
1506 Handle<JSFunction> object_function = Handle<JSFunction>( 1541 Handle<JSFunction> object_function = Handle<JSFunction>(
1507 isolate_->native_context()->object_function(), isolate_); 1542 isolate_->native_context()->object_function(), isolate_);
1508 exports_object = 1543 exports_object =
1509 isolate_->factory()->NewJSObject(object_function, TENURED); 1544 isolate_->factory()->NewJSObject(object_function, TENURED);
1510 Handle<String> exports_name = 1545 Handle<String> exports_name =
1511 isolate_->factory()->InternalizeUtf8String("exports"); 1546 isolate_->factory()->InternalizeUtf8String("exports");
1512 JSObject::AddProperty(instance, exports_name, exports_object, READ_ONLY); 1547 JSObject::AddProperty(instance, exports_name, exports_object, READ_ONLY);
1513 } 1548 }
1514 1549
1515 PropertyDescriptor desc; 1550 PropertyDescriptor desc;
1516 desc.set_writable(false); 1551 desc.set_writable(false);
1517 1552
1518 int func_index = 0; 1553 int func_index = 0;
1519 for (auto exp : module_->export_table) { 1554 for (auto exp : module_->export_table) {
1520 Handle<String> name = 1555 Handle<String> name =
1521 ExtractStringFromModuleBytes(isolate_, compiled_module_, 1556 ExtractStringFromModuleBytes(isolate_, compiled_module_,
1522 exp.name_offset, exp.name_length) 1557 exp.name_offset, exp.name_length)
1523 .ToHandleChecked(); 1558 .ToHandleChecked();
1524 switch (exp.kind) { 1559 switch (exp.kind) {
1525 case kExternalFunction: { 1560 case kExternalFunction: {
1526 // Wrap and export the code as a JSFunction. 1561 // Wrap and export the code as a JSFunction.
1527 WasmFunction& function = module_->functions[exp.index]; 1562 WasmFunction& function = module_->functions[exp.index];
1528 int export_index = 1563 int export_index =
1529 static_cast<int>(module_->functions.size() + func_index); 1564 static_cast<int>(module_->functions.size() + func_index);
1530 Handle<Code> export_code = 1565 Handle<Code> export_code =
rossberg 2016/10/25 08:19:20 Like here, you should first check whether exported
titzer 2016/10/25 08:23:13 That would mean that there would be only one expor
1531 code_table->GetValueChecked<Code>(isolate_, export_index); 1566 code_table->GetValueChecked<Code>(isolate_, export_index);
1532 desc.set_value(WrapExportCodeAsJSFunction( 1567 Handle<JSFunction> js_function = WrapExportCodeAsJSFunction(
1533 isolate_, export_code, name, function.sig, func_index, instance)); 1568 isolate_, export_code, name, function.sig, func_index, instance);
1569 if (!exported_functions.is_null()) {
1570 exported_functions->set(exp.index, *js_function);
1571 }
1572 desc.set_value(js_function);
1534 func_index++; 1573 func_index++;
1535 break; 1574 break;
1536 } 1575 }
1537 case kExternalTable: 1576 case kExternalTable: {
1538 // TODO(titzer): create a WebAssembly.Table instance. 1577 InitializedTable& init_table = inited_tables[exp.index];
1539 // TODO(titzer): should it have the same identity as an import? 1578 WasmIndirectFunctionTable& table =
1579 module_->function_tables[exp.index];
1580 if (init_table.table_object.is_null()) {
1581 init_table.table_object = WasmJs::CreateWasmTableObject(
1582 isolate_, table.size, table.max_size != 0, table.max_size,
1583 &init_table.js_functions);
1584 }
1585 desc.set_value(init_table.table_object);
1540 break; 1586 break;
1587 }
1541 case kExternalMemory: { 1588 case kExternalMemory: {
1542 // Export the memory as a WebAssembly.Memory object. 1589 // Export the memory as a WebAssembly.Memory object.
1543 Handle<Object> memory_object( 1590 Handle<Object> memory_object(
1544 instance->GetInternalField(kWasmMemObject), isolate_); 1591 instance->GetInternalField(kWasmMemObject), isolate_);
1545 if (memory_object->IsUndefined(isolate_)) { 1592 if (memory_object->IsUndefined(isolate_)) {
1546 // If there was no imported WebAssembly.Memory object, create one. 1593 // If there was no imported WebAssembly.Memory object, create one.
1547 Handle<JSArrayBuffer> buffer( 1594 Handle<JSArrayBuffer> buffer(
1548 JSArrayBuffer::cast( 1595 JSArrayBuffer::cast(
1549 instance->GetInternalField(kWasmMemArrayBuffer)), 1596 instance->GetInternalField(kWasmMemArrayBuffer)),
1550 isolate_); 1597 isolate_);
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
1582 } 1629 }
1583 1630
1584 v8::Maybe<bool> status = JSReceiver::DefineOwnProperty( 1631 v8::Maybe<bool> status = JSReceiver::DefineOwnProperty(
1585 isolate_, exports_object, name, &desc, Object::THROW_ON_ERROR); 1632 isolate_, exports_object, name, &desc, Object::THROW_ON_ERROR);
1586 if (!status.IsJust()) { 1633 if (!status.IsJust()) {
1587 thrower_->TypeError("export of %.*s failed.", name->length(), 1634 thrower_->TypeError("export of %.*s failed.", name->length(),
1588 name->ToCString().get()); 1635 name->ToCString().get());
1589 return; 1636 return;
1590 } 1637 }
1591 } 1638 }
1639
1640 // Fill out the contents of WebAssembly.Table instances.
1641 if (!exported_functions.is_null()) {
1642 // TODO(titzer): We compile JS->WASM wrappers for any function that is a
1643 // member of an exported indirect table that is not itself exported. This
1644 // could be done at compile time and cached instead.
1645 WasmInstance temp_instance(module_);
1646 temp_instance.context = isolate_->native_context();
1647 temp_instance.mem_size = 0;
1648 temp_instance.mem_start = nullptr;
1649 temp_instance.globals_start = nullptr;
1650
1651 ModuleEnv module_env;
1652 module_env.module = module_;
1653 module_env.instance = &temp_instance;
1654 module_env.origin = module_->origin;
1655
1656 // Fill the exported tables with JSFunctions.
1657 for (auto inited_table : inited_tables) {
1658 if (inited_table.js_functions.is_null()) continue;
1659 for (int i = 0; i < static_cast<int>(inited_table.new_entries.size());
1660 i++) {
1661 if (inited_table.new_entries[i] == nullptr) continue;
1662 int func_index =
1663 static_cast<int>(inited_table.new_entries[i]->func_index);
bradnelson 2016/10/25 07:33:39 These can only be in bounds correct? DCHECK?
titzer 2016/10/25 08:27:37 The loop iterates over the inited_table.new_entrie
1664 if (!exported_functions->get(func_index)->IsJSFunction()) {
1665 // No JSFunction entry yet exists for this function. Create one.
1666 Handle<Code> wasm_code(Code::cast(code_table->get(func_index)),
1667 isolate_);
1668 Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper(
1669 isolate_, &module_env, wasm_code, func_index);
1670 Handle<JSFunction> js_function = WrapExportCodeAsJSFunction(
1671 isolate_, wrapper_code, isolate_->factory()->empty_string(),
1672 module_->functions[func_index].sig, func_index, instance);
1673 exported_functions->set(func_index, *js_function);
1674 }
1675 inited_table.js_functions->set(i,
1676 exported_functions->get(func_index));
1677 }
1678 }
1679 }
1592 } 1680 }
1593 }; 1681 };
1594 1682
1595 // Instantiates a WASM module, creating a WebAssembly.Instance from a 1683 // Instantiates a WASM module, creating a WebAssembly.Instance from a
1596 // WebAssembly.Module. 1684 // WebAssembly.Module.
1597 MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate, 1685 MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
1598 ErrorThrower* thrower, 1686 ErrorThrower* thrower,
1599 Handle<JSObject> wasm_module, 1687 Handle<JSObject> wasm_module,
1600 Handle<JSReceiver> ffi, 1688 Handle<JSReceiver> ffi,
1601 Handle<JSArrayBuffer> memory) { 1689 Handle<JSArrayBuffer> memory) {
(...skipping 388 matching lines...) Expand 10 before | Expand all | Expand 10 after
1990 CHECK_NOT_NULL(result.val); 2078 CHECK_NOT_NULL(result.val);
1991 module = const_cast<WasmModule*>(result.val); 2079 module = const_cast<WasmModule*>(result.val);
1992 } 2080 }
1993 2081
1994 Handle<WasmModuleWrapper> module_wrapper = 2082 Handle<WasmModuleWrapper> module_wrapper =
1995 WasmModuleWrapper::New(isolate, module); 2083 WasmModuleWrapper::New(isolate, module);
1996 2084
1997 compiled_module->set_module_wrapper(module_wrapper); 2085 compiled_module->set_module_wrapper(module_wrapper);
1998 DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module)); 2086 DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module));
1999 } 2087 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698