Index: src/wasm/wasm-module.cc |
diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc |
index b2a954074ad47327e2b7c63c25e87e363aebccb5..582e5eb07bad1ae6f6ee667c3981dc6e2c3d7121 100644 |
--- a/src/wasm/wasm-module.cc |
+++ b/src/wasm/wasm-module.cc |
@@ -18,7 +18,6 @@ |
#include "src/wasm/module-decoder.h" |
#include "src/wasm/wasm-debug.h" |
#include "src/wasm/wasm-function-name-table.h" |
-#include "src/wasm/wasm-js.h" |
#include "src/wasm/wasm-module.h" |
#include "src/wasm/wasm-result.h" |
@@ -27,39 +26,6 @@ |
namespace v8 { |
namespace internal { |
namespace wasm { |
- |
-const char* SectionName(WasmSectionCode code) { |
- switch (code) { |
- case kUnknownSectionCode: |
- return "Unknown"; |
- case kTypeSectionCode: |
- return "Type"; |
- case kImportSectionCode: |
- return "Import"; |
- case kFunctionSectionCode: |
- return "Function"; |
- case kTableSectionCode: |
- return "Table"; |
- case kMemorySectionCode: |
- return "Memory"; |
- case kGlobalSectionCode: |
- return "Global"; |
- case kExportSectionCode: |
- return "Export"; |
- case kStartSectionCode: |
- return "Start"; |
- case kCodeSectionCode: |
- return "Code"; |
- case kElementSectionCode: |
- return "Element"; |
- case kDataSectionCode: |
- return "Data"; |
- case kNameSectionCode: |
- return "Name"; |
- default: |
- return "<unknown>"; |
- } |
-} |
enum JSFunctionExportInternalField { |
kInternalModuleInstance, |
@@ -68,6 +34,59 @@ |
}; |
static const int kPlaceholderMarker = 1000000000; |
+ |
+static const char* wasmSections[] = { |
+#define F(enumerator, order, string) string, |
+ FOR_EACH_WASM_SECTION_TYPE(F) |
+#undef F |
+ "<unknown>" // entry for "Max" |
+}; |
+ |
+static uint8_t wasmSectionsLengths[]{ |
+#define F(enumerator, order, string) sizeof(string) - 1, |
+ FOR_EACH_WASM_SECTION_TYPE(F) |
+#undef F |
+ 9 // entry for "Max" |
+}; |
+ |
+static uint8_t wasmSectionsOrders[]{ |
+#define F(enumerator, order, string) order, |
+ FOR_EACH_WASM_SECTION_TYPE(F) |
+#undef F |
+ 0 // entry for "Max" |
+}; |
+ |
+static_assert(sizeof(wasmSections) / sizeof(wasmSections[0]) == |
+ (size_t)WasmSection::Code::Max + 1, |
+ "expected enum WasmSection::Code to be monotonic from 0"); |
+ |
+WasmSection::Code WasmSection::begin() { return (WasmSection::Code)0; } |
+WasmSection::Code WasmSection::end() { return WasmSection::Code::Max; } |
+WasmSection::Code WasmSection::next(WasmSection::Code code) { |
+ return (WasmSection::Code)(1 + (uint32_t)code); |
+} |
+ |
+const char* WasmSection::getName(WasmSection::Code code) { |
+ return wasmSections[(size_t)code]; |
+} |
+ |
+size_t WasmSection::getNameLength(WasmSection::Code code) { |
+ return wasmSectionsLengths[(size_t)code]; |
+} |
+ |
+int WasmSection::getOrder(WasmSection::Code code) { |
+ return wasmSectionsOrders[(size_t)code]; |
+} |
+ |
+WasmSection::Code WasmSection::lookup(const byte* string, uint32_t length) { |
+ // TODO(jfb) Linear search, it may be better to do a common-prefix search. |
+ for (Code i = begin(); i != end(); i = next(i)) { |
+ if (getNameLength(i) == length && 0 == memcmp(getName(i), string, length)) { |
+ return i; |
+ } |
+ } |
+ return Code::Max; |
+} |
std::ostream& operator<<(std::ostream& os, const WasmModule& module) { |
os << "WASM module with "; |
@@ -139,7 +158,7 @@ |
namespace { |
// Internal constants for the layout of the module object. |
-enum WasmInstanceObjectFields { |
+enum WasmInstanceFields { |
kWasmCompiledModule = 0, |
kWasmModuleFunctionTable, |
kWasmModuleCodeTable, |
@@ -150,7 +169,6 @@ |
kWasmFunctionNamesArray, |
kWasmModuleBytesString, |
kWasmDebugInfo, |
- kWasmNumImportedFunctions, |
kWasmModuleInternalFieldCount |
}; |
@@ -160,15 +178,17 @@ |
// the compiled module is either obtained from the current v8 instance, or from |
// a snapshot produced by a compatible (==identical) v8 instance, we simply |
// fail at instantiation time, in the face of invalid data. |
-enum WasmCompiledModule { |
- kCodeTable, // FixedArray of Code |
- kImportData, // maybe FixedArray of FixedArray respecting the |
- // WasmImportData structure. |
- kExportData, // maybe FixedArray of FixedArray of WasmExportData |
- // structure |
- kStartupData, // maybe FixedArray of WasmExportData structure |
+enum CompiledWasmObjectFields { |
+ kFunctions, // FixedArray of Code |
+ kImportData, // maybe FixedArray of FixedArray respecting the |
+ // WasmImportMetadata structure. |
+ kImportMap, // FixedArray. The i-th element is the Code object used for |
+ // import i |
+ kExports, // maybe FixedArray of FixedArray of WasmExportMetadata |
+ // structure |
+ kStartupFunction, // maybe FixedArray of WasmExportMetadata structure |
kTableOfIndirectFunctionTables, // maybe FixedArray of FixedArray of |
- // WasmIndirectFunctionTableData |
+ // WasmIndirectFunctionTableMetadata |
kModuleBytes, // maybe String |
kFunctionNameTable, // maybe ByteArray |
kMinRequiredMemory, // Smi. an uint32_t |
@@ -177,32 +197,33 @@ |
// WasmSegmentInfo structure |
kDataSegments, // maybe ByteArray. |
- kGlobalsSize, // Smi. an uint32_t |
- kMemSize, // Smi.an uint32_t |
- kMemStart, // MaybeHandle<ArrayBuffer> |
- kExportMem, // Smi. bool |
- kOrigin, // Smi. ModuleOrigin |
- kNextInstance, // WeakCell. See compiled code cloning. |
- kPrevInstance, // WeakCell. See compiled code cloning. |
- kOwningInstance, // WeakCell, pointing to the owning instance. |
- kModuleObject, // WeakCell, pointing to the module object. |
- kWasmCompiledModuleSize // Sentinel value. |
+ kGlobalsSize, // Smi. an uint32_t |
+ kMemSize, // Smi.an uint32_t |
+ kMemStart, // MaybeHandle<ArrayBuffer> |
+ kExportMem, // Smi. bool |
+ kOrigin, // Smi. ModuleOrigin |
+ kNextInstance, // WeakCell. See compiled code cloning. |
+ kPrevInstance, // WeakCell. See compiled code cloning. |
+ kOwningInstance, // WeakCell, pointing to the owning instance. |
+ kModuleObject, // WeakCell, pointing to the module object. |
+ kCompiledWasmObjectTableSize // Sentinel value. |
}; |
-enum WasmImportData { |
- kModuleName, // String |
- kFunctionName, // maybe String |
- kOutputCount, // Smi. an uint32_t |
- kSignature, // ByteArray. A copy of the data in FunctionSig |
- kWasmImportDataSize // Sentinel value. |
+enum WasmImportMetadata { |
+ kModuleName, // String |
+ kFunctionName, // maybe String |
+ kOutputCount, // Smi. an uint32_t |
+ kSignature, // ByteArray. A copy of the data in FunctionSig |
+ kWasmImportDataTableSize // Sentinel value. |
}; |
-enum WasmExportData { |
- kExportName, // String |
- kExportArity, // Smi, an int |
- kExportedFunctionIndex, // Smi, an uint32_t |
- kExportedSignature, // ByteArray. A copy of the data in FunctionSig |
- kWasmExportDataSize // Sentinel value. |
+enum WasmExportMetadata { |
+ kExportCode, // Code |
+ kExportName, // String |
+ kExportArity, // Smi, an int |
+ kExportedFunctionIndex, // Smi, an uint32_t |
+ kExportedSignature, // ByteArray. A copy of the data in FunctionSig |
+ kWasmExportMetadataTableSize // Sentinel value. |
}; |
enum WasmSegmentInfo { |
@@ -211,10 +232,10 @@ |
kWasmSegmentInfoSize // Sentinel value. |
}; |
-enum WasmIndirectFunctionTableData { |
- kSize, // Smi. an uint32_t |
- kTable, // FixedArray of indirect function table |
- kWasmIndirectFunctionTableDataSize // Sentinel value. |
+enum WasmIndirectFunctionTableMetadata { |
+ kSize, // Smi. an uint32_t |
+ kTable, // FixedArray of indirect function table |
+ kWasmIndirectFunctionTableMetadataSize // Sentinel value. |
}; |
uint32_t GetMinModuleMemSize(const WasmModule* module) { |
@@ -258,6 +279,7 @@ |
static_cast<int>(module->data_segments.size()), TENURED); |
uint32_t data_size = 0; |
for (const WasmDataSegment& segment : module->data_segments) { |
+ if (!segment.init) continue; |
if (segment.source_size == 0) continue; |
data_size += segment.source_size; |
} |
@@ -266,12 +288,11 @@ |
uint32_t last_insertion_pos = 0; |
for (uint32_t i = 0; i < module->data_segments.size(); ++i) { |
const WasmDataSegment& segment = module->data_segments[i]; |
+ if (!segment.init) continue; |
if (segment.source_size == 0) continue; |
Handle<ByteArray> js_segment = |
factory->NewByteArray(kWasmSegmentInfoSize * sizeof(uint32_t), TENURED); |
- // TODO(titzer): add support for global offsets for dest_addr |
- CHECK_EQ(WasmInitExpr::kI32Const, segment.dest_addr.kind); |
- js_segment->set_int(kDestAddr, segment.dest_addr.val.i32_const); |
+ js_segment->set_int(kDestAddr, segment.dest_addr); |
js_segment->set_int(kSourceSize, segment.source_size); |
segments->set(i, *js_segment); |
data->copy_in(last_insertion_pos, |
@@ -380,54 +401,75 @@ |
return code; |
} |
-bool LinkFunction(Handle<Code> unlinked, |
- std::vector<Handle<Code>>& code_table) { |
+// TODO(mtrofin): remove when we stop relying on placeholders. |
+void InitializePlaceholders(Factory* factory, |
+ std::vector<Handle<Code>>* placeholders, |
+ size_t size) { |
+ DCHECK(placeholders->empty()); |
+ placeholders->reserve(size); |
+ |
+ for (uint32_t i = 0; i < size; ++i) { |
+ placeholders->push_back(CreatePlaceholder(factory, i, Code::WASM_FUNCTION)); |
+ } |
+} |
+ |
+bool LinkFunction(Isolate* isolate, Handle<Code> unlinked, |
+ Handle<FixedArray> code_targets, |
+ Code::Kind kind = Code::WASM_FUNCTION) { |
bool modified = false; |
int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); |
AllowDeferredHandleDereference embedding_raw_address; |
for (RelocIterator it(*unlinked, mode_mask); !it.done(); it.next()) { |
- RelocInfo::Mode mode = it.rinfo()->rmode(); |
- if (RelocInfo::IsCodeTarget(mode)) { |
- Code* target = |
- Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); |
- if (target->constant_pool_offset() < kPlaceholderMarker) continue; |
- switch (target->kind()) { |
- case Code::WASM_FUNCTION: // fall through |
- case Code::WASM_TO_JS_FUNCTION: // fall through |
- case Code::JS_TO_WASM_FUNCTION: { |
- // Patch direct calls to placeholder code objects. |
- uint32_t index = target->constant_pool_offset() - kPlaceholderMarker; |
- Handle<Code> new_target = code_table[index]; |
- if (target != *new_target) { |
- it.rinfo()->set_target_address(new_target->instruction_start(), |
- UPDATE_WRITE_BARRIER, |
- SKIP_ICACHE_FLUSH); |
- modified = true; |
- } |
- break; |
- } |
- default: |
- break; |
+ Code* target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); |
+ if (target->kind() == kind && |
+ target->constant_pool_offset() >= kPlaceholderMarker) { |
+ // Patch direct calls to placeholder code objects. |
+ uint32_t index = target->constant_pool_offset() - kPlaceholderMarker; |
+ CHECK(index < static_cast<uint32_t>(code_targets->length())); |
+ Handle<Code> new_target = |
+ code_targets->GetValueChecked<Code>(isolate, index); |
+ if (target != *new_target) { |
+ it.rinfo()->set_target_address(new_target->instruction_start(), |
+ UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH); |
+ modified = true; |
} |
} |
} |
return modified; |
} |
-void FlushICache(Isolate* isolate, Handle<FixedArray> functions) { |
+void LinkModuleFunctions(Isolate* isolate, Handle<FixedArray> functions) { |
+ for (int i = 0; i < functions->length(); ++i) { |
+ Handle<Code> code = functions->GetValueChecked<Code>(isolate, i); |
+ LinkFunction(isolate, code, functions); |
+ } |
+} |
+ |
+void FlushAssemblyCache(Isolate* isolate, Handle<FixedArray> functions) { |
for (int i = 0; i < functions->length(); ++i) { |
Handle<Code> code = functions->GetValueChecked<Code>(isolate, i); |
Assembler::FlushICache(isolate, code->instruction_start(), |
code->instruction_size()); |
} |
} |
+ |
+void SetRuntimeSupport(Isolate* isolate, Handle<JSObject> js_object) { |
+ Handle<FixedArray> functions = Handle<FixedArray>( |
+ FixedArray::cast(js_object->GetInternalField(kWasmModuleCodeTable))); |
+ Handle<WeakCell> weak_link = isolate->factory()->NewWeakCell(js_object); |
+ |
+ for (int i = FLAG_skip_compiling_wasm_funcs; i < functions->length(); ++i) { |
+ Handle<Code> code = functions->GetValueChecked<Code>(isolate, i); |
+ Handle<FixedArray> deopt_data = |
+ isolate->factory()->NewFixedArray(2, TENURED); |
+ deopt_data->set(0, *weak_link); |
+ deopt_data->set(1, Smi::FromInt(static_cast<int>(i))); |
+ deopt_data->set_length(2); |
+ code->set_deoptimization_data(*deopt_data); |
+ } |
+} |
+ |
} // namespace |
- |
-uint32_t GetNumImportedFunctions(Handle<JSObject> wasm_object) { |
- return static_cast<uint32_t>( |
- Smi::cast(wasm_object->GetInternalField(kWasmNumImportedFunctions)) |
- ->value()); |
-} |
WasmModule::WasmModule(byte* module_start) |
: module_start(module_start), |
@@ -435,12 +477,10 @@ |
min_mem_pages(0), |
max_mem_pages(0), |
mem_export(false), |
+ mem_external(false), |
start_function_index(-1), |
origin(kWasmOrigin), |
globals_size(0), |
- num_imported_functions(0), |
- num_declared_functions(0), |
- num_exported_functions(0), |
pending_tasks(new base::Semaphore(0)) {} |
static MaybeHandle<JSFunction> ReportFFIError( |
@@ -520,7 +560,7 @@ |
DisallowHandleDereference no_deref; |
DisallowCodeDependencyChange no_dependency_change; |
- // - 1 because AtomicIncrement returns the value after the atomic increment. |
+ // - 1 because AtomicIntrement returns the value after the atomic increment. |
size_t index = next_unit->Increment(1) - 1; |
if (index >= compilation_units->size()) { |
return false; |
@@ -529,8 +569,10 @@ |
compiler::WasmCompilationUnit* unit = compilation_units->at(index); |
if (unit != nullptr) { |
unit->ExecuteCompilation(); |
- base::LockGuard<base::Mutex> guard(result_mutex); |
- executed_units->push(unit); |
+ { |
+ base::LockGuard<base::Mutex> guard(result_mutex); |
+ executed_units->push(unit); |
+ } |
} |
return true; |
} |
@@ -573,6 +615,11 @@ |
code->relocation_info()->length()); |
} |
+static void RecordStats(Isolate* isolate, |
+ const std::vector<Handle<Code>>& functions) { |
+ for (Handle<Code> c : functions) RecordStats(isolate, *c); |
+} |
+ |
static void RecordStats(Isolate* isolate, Handle<FixedArray> functions) { |
DisallowHeapAllocation no_gc; |
for (int i = 0; i < functions->length(); ++i) { |
@@ -591,16 +638,16 @@ |
return old_address; |
} |
-Handle<FixedArray> GetImportsData(Factory* factory, const WasmModule* module) { |
+Handle<FixedArray> GetImportsMetadata(Factory* factory, |
+ const WasmModule* module) { |
Handle<FixedArray> ret = factory->NewFixedArray( |
static_cast<int>(module->import_table.size()), TENURED); |
for (size_t i = 0; i < module->import_table.size(); ++i) { |
const WasmImport& import = module->import_table[i]; |
- if (import.kind != kExternalFunction) continue; |
WasmName module_name = module->GetNameOrNull(import.module_name_offset, |
import.module_name_length); |
- WasmName function_name = module->GetNameOrNull(import.field_name_offset, |
- import.field_name_length); |
+ WasmName function_name = module->GetNameOrNull(import.function_name_offset, |
+ import.function_name_length); |
Handle<String> module_name_string = |
factory->InternalizeUtf8String(module_name); |
@@ -608,97 +655,107 @@ |
function_name.is_empty() |
? Handle<String>::null() |
: factory->InternalizeUtf8String(function_name); |
- FunctionSig* fsig = module->functions[import.index].sig; |
- Handle<ByteArray> sig = factory->NewByteArray( |
- static_cast<int>(fsig->parameter_count() + fsig->return_count()), |
- TENURED); |
- sig->copy_in(0, reinterpret_cast<const byte*>(fsig->raw_data()), |
+ Handle<ByteArray> sig = |
+ factory->NewByteArray(static_cast<int>(import.sig->parameter_count() + |
+ import.sig->return_count()), |
+ TENURED); |
+ sig->copy_in(0, reinterpret_cast<const byte*>(import.sig->raw_data()), |
sig->length()); |
Handle<FixedArray> encoded_import = |
- factory->NewFixedArray(kWasmImportDataSize, TENURED); |
+ factory->NewFixedArray(kWasmImportDataTableSize, TENURED); |
encoded_import->set(kModuleName, *module_name_string); |
if (!function_name_string.is_null()) { |
encoded_import->set(kFunctionName, *function_name_string); |
} |
- encoded_import->set(kOutputCount, |
- Smi::FromInt(static_cast<int>(fsig->return_count()))); |
+ encoded_import->set( |
+ kOutputCount, |
+ Smi::FromInt(static_cast<int>(import.sig->return_count()))); |
encoded_import->set(kSignature, *sig); |
ret->set(static_cast<int>(i), *encoded_import); |
} |
return ret; |
} |
-Handle<Code> CompileImportWrapper(Isolate* isolate, |
- const Handle<JSReceiver> ffi, int index, |
- Handle<FixedArray> import_data, |
- ErrorThrower* thrower) { |
- Handle<FixedArray> data = |
- import_data->GetValueChecked<FixedArray>(isolate, index); |
- Handle<String> module_name = |
- data->GetValueChecked<String>(isolate, kModuleName); |
- MaybeHandle<String> function_name = |
- data->GetValue<String>(isolate, kFunctionName); |
- |
- // TODO(mtrofin): this is an uint32_t, actually. We should rationalize |
- // it when we rationalize signed/unsigned stuff. |
- int ret_count = Smi::cast(data->get(kOutputCount))->value(); |
- CHECK_GE(ret_count, 0); |
- Handle<ByteArray> sig_data = |
- data->GetValueChecked<ByteArray>(isolate, kSignature); |
- int sig_data_size = sig_data->length(); |
- int param_count = sig_data_size - ret_count; |
- CHECK(param_count >= 0); |
- |
- MaybeHandle<JSReceiver> function = LookupFunction( |
- thrower, isolate->factory(), ffi, index, module_name, function_name); |
- if (function.is_null()) return Handle<Code>::null(); |
- Handle<Code> code; |
- Handle<JSReceiver> target = function.ToHandleChecked(); |
- bool isMatch = 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) { |
- int exported_param_count = |
- Smi::cast(func->GetInternalField(kInternalArity))->value(); |
- Handle<ByteArray> exportedSig = Handle<ByteArray>( |
- ByteArray::cast(func->GetInternalField(kInternalSignature))); |
- if (exported_param_count == param_count && |
- exportedSig->length() == sig_data->length() && |
- memcmp(exportedSig->data(), sig_data->data(), |
- exportedSig->length()) == 0) { |
- isMatch = true; |
+bool CompileWrappersToImportedFunctions(Isolate* isolate, |
+ const Handle<JSReceiver> ffi, |
+ std::vector<Handle<Code>>& imports, |
+ Handle<FixedArray> import_data, |
+ ErrorThrower* thrower) { |
+ uint32_t import_count = static_cast<uint32_t>(import_data->length()); |
+ if (import_count > 0) { |
+ imports.reserve(import_count); |
+ for (uint32_t index = 0; index < import_count; ++index) { |
+ Handle<FixedArray> data = |
+ import_data->GetValueChecked<FixedArray>(isolate, index); |
+ Handle<String> module_name = |
+ data->GetValueChecked<String>(isolate, kModuleName); |
+ MaybeHandle<String> function_name = |
+ data->GetValue<String>(isolate, kFunctionName); |
+ |
+ // TODO(mtrofin): this is an uint32_t, actually. We should rationalize |
+ // it when we rationalize signed/unsigned stuff. |
+ int ret_count = Smi::cast(data->get(kOutputCount))->value(); |
+ CHECK(ret_count >= 0); |
+ Handle<ByteArray> sig_data = |
+ data->GetValueChecked<ByteArray>(isolate, kSignature); |
+ int sig_data_size = sig_data->length(); |
+ int param_count = sig_data_size - ret_count; |
+ CHECK(param_count >= 0); |
+ |
+ MaybeHandle<JSReceiver> function = LookupFunction( |
+ thrower, isolate->factory(), ffi, index, module_name, function_name); |
+ if (function.is_null()) return false; |
+ Handle<Code> code; |
+ Handle<JSReceiver> target = function.ToHandleChecked(); |
+ bool isMatch = 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) { |
+ int exported_param_count = |
+ Smi::cast(func->GetInternalField(kInternalArity))->value(); |
+ Handle<ByteArray> exportedSig = Handle<ByteArray>( |
+ ByteArray::cast(func->GetInternalField(kInternalSignature))); |
+ if (exported_param_count == param_count && |
+ exportedSig->length() == sig_data->length() && |
+ memcmp(exportedSig->data(), sig_data->data(), |
+ exportedSig->length()) == 0) { |
+ isMatch = true; |
+ } |
+ } |
} |
- } |
- } |
- if (isMatch) { |
- 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); |
+ if (isMatch) { |
+ 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); |
+ } else { |
+ // Copy the signature to avoid a raw pointer into a heap object when |
+ // GC can happen. |
+ Zone zone(isolate->allocator()); |
+ MachineRepresentation* reps = |
+ zone.NewArray<MachineRepresentation>(sig_data_size); |
+ memcpy(reps, sig_data->data(), |
+ sizeof(MachineRepresentation) * sig_data_size); |
+ FunctionSig sig(ret_count, param_count, reps); |
+ |
+ code = compiler::CompileWasmToJSWrapper(isolate, target, &sig, index, |
+ module_name, function_name); |
} |
- } |
- DCHECK(wasm_count == 1); |
- return code; |
- } else { |
- // Copy the signature to avoid a raw pointer into a heap object when |
- // GC can happen. |
- Zone zone(isolate->allocator()); |
- MachineRepresentation* reps = |
- zone.NewArray<MachineRepresentation>(sig_data_size); |
- memcpy(reps, sig_data->data(), |
- sizeof(MachineRepresentation) * sig_data_size); |
- FunctionSig sig(ret_count, param_count, reps); |
- |
- return compiler::CompileWasmToJSWrapper(isolate, target, &sig, index, |
- module_name, function_name); |
- } |
+ imports.push_back(code); |
+ } |
+ } |
+ return true; |
} |
void InitializeParallelCompilation( |
@@ -706,10 +763,8 @@ |
std::vector<compiler::WasmCompilationUnit*>& compilation_units, |
ModuleEnv& module_env, ErrorThrower* thrower) { |
for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); ++i) { |
- const WasmFunction* func = &functions[i]; |
- compilation_units[i] = |
- func->imported ? nullptr : new compiler::WasmCompilationUnit( |
- thrower, isolate, &module_env, func, i); |
+ compilation_units[i] = new compiler::WasmCompilationUnit( |
+ thrower, isolate, &module_env, &functions[i], i); |
} |
} |
@@ -839,8 +894,8 @@ |
for (uint32_t i = FLAG_skip_compiling_wasm_funcs; |
i < module->functions.size(); ++i) { |
const WasmFunction& func = module->functions[i]; |
- if (func.imported) continue; // Imports are compiled at instantiation time. |
- |
+ |
+ DCHECK_EQ(i, func.func_index); |
WasmName str = module->GetName(func.name_offset, func.name_length); |
Handle<Code> code = Handle<Code>::null(); |
// Compile the function. |
@@ -856,8 +911,91 @@ |
} |
} |
-void PatchDirectCalls(Handle<FixedArray> old_functions, |
- Handle<FixedArray> new_functions, int start) { |
+void SetDebugSupport(Factory* factory, Handle<FixedArray> compiled_module, |
+ Handle<JSObject> js_object) { |
+ Isolate* isolate = compiled_module->GetIsolate(); |
+ MaybeHandle<String> module_bytes_string = |
+ compiled_module->GetValue<String>(isolate, kModuleBytes); |
+ if (!module_bytes_string.is_null()) { |
+ js_object->SetInternalField(kWasmModuleBytesString, |
+ *module_bytes_string.ToHandleChecked()); |
+ } |
+ |
+ MaybeHandle<ByteArray> function_name_table = |
+ compiled_module->GetValue<ByteArray>(isolate, kFunctionNameTable); |
+ if (!function_name_table.is_null()) { |
+ js_object->SetInternalField(kWasmFunctionNamesArray, |
+ *function_name_table.ToHandleChecked()); |
+ } |
+} |
+ |
+bool SetupGlobals(Isolate* isolate, MaybeHandle<JSObject> template_owner, |
+ Handle<FixedArray> compiled_module, Handle<JSObject> instance, |
+ ErrorThrower* thrower) { |
+ uint32_t globals_size = static_cast<uint32_t>( |
+ Smi::cast(compiled_module->get(kGlobalsSize))->value()); |
+ if (globals_size > 0) { |
+ Handle<JSArrayBuffer> globals_buffer = |
+ NewArrayBuffer(isolate, globals_size); |
+ if (globals_buffer.is_null()) { |
+ thrower->Error("Out of memory: wasm globals"); |
+ return false; |
+ } |
+ Address old_address = |
+ template_owner.is_null() |
+ ? nullptr |
+ : GetGlobalStartAddressFromCodeTemplate( |
+ *isolate->factory()->undefined_value(), |
+ JSObject::cast(*template_owner.ToHandleChecked())); |
+ RelocateGlobals(instance, old_address, |
+ static_cast<Address>(globals_buffer->backing_store())); |
+ instance->SetInternalField(kWasmGlobalsArrayBuffer, *globals_buffer); |
+ } |
+ return true; |
+} |
+ |
+bool SetupInstanceHeap(Isolate* isolate, Handle<FixedArray> compiled_module, |
+ Handle<JSObject> instance, Handle<JSArrayBuffer> memory, |
+ ErrorThrower* thrower) { |
+ uint32_t min_mem_pages = static_cast<uint32_t>( |
+ Smi::cast(compiled_module->get(kMinRequiredMemory))->value()); |
+ isolate->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages); |
+ // TODO(wasm): re-enable counter for max_mem_pages when we use that field. |
+ |
+ if (memory.is_null() && min_mem_pages > 0) { |
+ memory = AllocateMemory(thrower, isolate, min_mem_pages); |
+ if (memory.is_null()) { |
+ return false; |
+ } |
+ } |
+ |
+ if (!memory.is_null()) { |
+ instance->SetInternalField(kWasmMemArrayBuffer, *memory); |
+ Address mem_start = static_cast<Address>(memory->backing_store()); |
+ uint32_t mem_size = static_cast<uint32_t>(memory->byte_length()->Number()); |
+ uint32_t old_mem_size = static_cast<uint32_t>( |
+ compiled_module->GetValueChecked<HeapNumber>(isolate, kMemSize) |
+ ->value()); |
+ MaybeHandle<JSArrayBuffer> old_mem = |
+ compiled_module->GetValue<JSArrayBuffer>(isolate, kMemStart); |
+ Address old_mem_start = |
+ old_mem.is_null() |
+ ? nullptr |
+ : static_cast<Address>(old_mem.ToHandleChecked()->backing_store()); |
+ RelocateInstanceCode(instance, old_mem_start, mem_start, old_mem_size, |
+ mem_size); |
+ LoadDataSegments(compiled_module, mem_start, mem_size); |
+ compiled_module->GetValueChecked<HeapNumber>(isolate, kMemSize) |
+ ->set_value(static_cast<double>(mem_size)); |
+ compiled_module->set(kMemStart, *memory); |
+ } |
+ return true; |
+} |
+ |
+void FixupFunctionsAndImports(Handle<FixedArray> old_functions, |
+ Handle<FixedArray> new_functions, |
+ MaybeHandle<FixedArray> maybe_old_imports, |
+ MaybeHandle<FixedArray> maybe_new_imports) { |
DCHECK_EQ(new_functions->length(), old_functions->length()); |
DisallowHeapAllocation no_gc; |
@@ -866,9 +1004,19 @@ |
old_to_new_code.insert(std::make_pair(Code::cast(old_functions->get(i)), |
Code::cast(new_functions->get(i)))); |
} |
+ DCHECK_EQ(maybe_old_imports.is_null(), maybe_new_imports.is_null()); |
+ if (!maybe_old_imports.is_null()) { |
+ Handle<FixedArray> old_imports = maybe_old_imports.ToHandleChecked(); |
+ Handle<FixedArray> new_imports = maybe_new_imports.ToHandleChecked(); |
+ DCHECK_EQ(new_imports->length(), old_imports->length()); |
+ for (int i = 0; i < new_imports->length(); ++i) { |
+ old_to_new_code.insert(std::make_pair(Code::cast(old_imports->get(i)), |
+ Code::cast(new_imports->get(i)))); |
+ } |
+ } |
int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); |
AllowDeferredHandleDereference embedding_raw_address; |
- for (int i = start; i < new_functions->length(); ++i) { |
+ for (int i = 0; i < new_functions->length(); ++i) { |
Code* wasm_function = Code::cast(new_functions->get(i)); |
for (RelocIterator it(wasm_function, mode_mask); !it.done(); it.next()) { |
Code* old_code = |
@@ -878,14 +1026,107 @@ |
auto found = old_to_new_code.find(old_code); |
DCHECK(found != old_to_new_code.end()); |
Code* new_code = found->second; |
- if (new_code != old_code) { |
- it.rinfo()->set_target_address(new_code->instruction_start(), |
- UPDATE_WRITE_BARRIER, |
- SKIP_ICACHE_FLUSH); |
+ // Avoid redundant updates, expected for wasm functions, if we're at the |
+ // first instance. |
+ if (new_code == old_code) { |
+ DCHECK(new_code->kind() == Code::WASM_FUNCTION); |
+ continue; |
+ } |
+ it.rinfo()->set_target_address(new_code->instruction_start(), |
+ UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH); |
+ } |
+ } |
+ } |
+} |
+ |
+bool SetupImports(Isolate* isolate, Handle<FixedArray> compiled_module, |
+ Handle<JSObject> instance, ErrorThrower* thrower, |
+ Handle<JSReceiver> ffi) { |
+ //------------------------------------------------------------------------- |
+ // Compile wrappers to imported functions. |
+ //------------------------------------------------------------------------- |
+ std::vector<Handle<Code>> import_code; |
+ MaybeHandle<FixedArray> maybe_import_data = |
+ compiled_module->GetValue<FixedArray>(isolate, kImportData); |
+ Handle<FixedArray> import_data; |
+ if (maybe_import_data.ToHandle(&import_data)) { |
+ if (!CompileWrappersToImportedFunctions(isolate, ffi, import_code, |
+ import_data, thrower)) { |
+ return false; |
+ } |
+ } |
+ |
+ RecordStats(isolate, import_code); |
+ if (import_code.empty()) return true; |
+ |
+ Handle<FixedArray> new_imports = |
+ isolate->factory()->NewFixedArray(static_cast<int>(import_code.size())); |
+ for (int i = 0; i < new_imports->length(); ++i) { |
+ new_imports->set(i, *import_code[i]); |
+ } |
+ compiled_module->set(kImportMap, *new_imports); |
+ return true; |
+} |
+ |
+bool SetupExportsObject(Handle<FixedArray> compiled_module, Isolate* isolate, |
+ Handle<JSObject> instance, ErrorThrower* thrower) { |
+ Factory* factory = isolate->factory(); |
+ bool mem_export = |
+ static_cast<bool>(Smi::cast(compiled_module->get(kExportMem))->value()); |
+ ModuleOrigin origin = static_cast<ModuleOrigin>( |
+ Smi::cast(compiled_module->get(kOrigin))->value()); |
+ |
+ MaybeHandle<FixedArray> maybe_exports = |
+ compiled_module->GetValue<FixedArray>(isolate, kExports); |
+ if (!maybe_exports.is_null() || mem_export) { |
+ PropertyDescriptor desc; |
+ desc.set_writable(false); |
+ |
+ Handle<JSObject> exports_object = instance; |
+ if (origin == kWasmOrigin) { |
+ // Create the "exports" object. |
+ Handle<JSFunction> object_function = Handle<JSFunction>( |
+ isolate->native_context()->object_function(), isolate); |
+ exports_object = factory->NewJSObject(object_function, TENURED); |
+ Handle<String> exports_name = factory->InternalizeUtf8String("exports"); |
+ JSObject::AddProperty(instance, exports_name, exports_object, READ_ONLY); |
+ } |
+ Handle<FixedArray> exports; |
+ if (maybe_exports.ToHandle(&exports)) { |
+ int exports_size = exports->length(); |
+ for (int i = 0; i < exports_size; ++i) { |
+ if (thrower->error()) return false; |
+ Handle<FixedArray> export_metadata = |
+ exports->GetValueChecked<FixedArray>(isolate, i); |
+ Handle<Code> export_code = |
+ export_metadata->GetValueChecked<Code>(isolate, kExportCode); |
+ RecordStats(isolate, *export_code); |
+ Handle<String> name = |
+ export_metadata->GetValueChecked<String>(isolate, kExportName); |
+ int arity = Smi::cast(export_metadata->get(kExportArity))->value(); |
+ MaybeHandle<ByteArray> signature = |
+ export_metadata->GetValue<ByteArray>(isolate, kExportedSignature); |
+ Handle<JSFunction> function = WrapExportCodeAsJSFunction( |
+ isolate, export_code, name, arity, signature, instance); |
+ desc.set_value(function); |
+ Maybe<bool> status = JSReceiver::DefineOwnProperty( |
+ isolate, exports_object, name, &desc, Object::THROW_ON_ERROR); |
+ if (!status.IsJust()) { |
+ thrower->Error("export of %.*s failed.", name->length(), |
+ name->ToCString().get()); |
+ return false; |
} |
} |
} |
- } |
+ if (mem_export) { |
+ // Export the memory as a named property. |
+ Handle<String> name = factory->InternalizeUtf8String("memory"); |
+ Handle<JSArrayBuffer> memory = Handle<JSArrayBuffer>( |
+ JSArrayBuffer::cast(instance->GetInternalField(kWasmMemArrayBuffer))); |
+ JSObject::AddProperty(exports_object, name, memory, READ_ONLY); |
+ } |
+ } |
+ return true; |
} |
#define GET_COMPILED_MODULE_WEAK_RELATION_OR_NULL(Field) \ |
@@ -925,7 +1166,7 @@ |
RelocInfo::ModeMask(RelocInfo::WASM_MEMORY_SIZE_REFERENCE) | |
RelocInfo::ModeMask(RelocInfo::WASM_GLOBAL_REFERENCE); |
- Object* fct_obj = compiled_module->get(kCodeTable); |
+ Object* fct_obj = compiled_module->get(kFunctions); |
if (fct_obj != nullptr && fct_obj != undefined && |
(old_mem_size > 0 || globals_start != nullptr)) { |
FixedArray* functions = FixedArray::cast(fct_obj); |
@@ -1028,7 +1269,7 @@ |
temp_instance_for_compilation.function_tables[i] = values; |
Handle<FixedArray> metadata = isolate->factory()->NewFixedArray( |
- kWasmIndirectFunctionTableDataSize, TENURED); |
+ kWasmIndirectFunctionTableMetadataSize, TENURED); |
metadata->set(kSize, Smi::FromInt(function_tables[i].size)); |
metadata->set(kTable, *values); |
indirect_table.ToHandleChecked()->set(i, *metadata); |
@@ -1041,26 +1282,27 @@ |
module_env.module = this; |
module_env.instance = &temp_instance_for_compilation; |
module_env.origin = origin; |
- |
- // The {code_table} array contains import wrappers and functions (which |
- // are both included in {functions.size()}, and export wrappers. |
- int code_table_size = |
- static_cast<int>(functions.size() + num_exported_functions); |
- Handle<FixedArray> code_table = |
- factory->NewFixedArray(static_cast<int>(code_table_size), TENURED); |
- |
- // Initialize the code table with placeholders. |
- for (uint32_t i = 0; i < functions.size(); i++) { |
- Code::Kind kind = Code::WASM_FUNCTION; |
- if (i < num_imported_functions) kind = Code::WASM_TO_JS_FUNCTION; |
- Handle<Code> placeholder = CreatePlaceholder(factory, i, kind); |
- code_table->set(static_cast<int>(i), *placeholder); |
- temp_instance_for_compilation.function_code[i] = placeholder; |
- } |
- |
+ InitializePlaceholders(factory, &module_env.placeholders, functions.size()); |
+ |
+ Handle<FixedArray> compiled_functions = |
+ factory->NewFixedArray(static_cast<int>(functions.size()), TENURED); |
+ |
+ MaybeHandle<FixedArray> maybe_imports; |
+ if (import_table.size() > 0) { |
+ temp_instance_for_compilation.import_code.resize(import_table.size()); |
+ Handle<FixedArray> imports = |
+ factory->NewFixedArray(static_cast<int>(import_table.size())); |
+ for (uint32_t i = 0; i < import_table.size(); ++i) { |
+ Handle<Code> placeholder = |
+ CreatePlaceholder(factory, i, Code::WASM_TO_JS_FUNCTION); |
+ temp_instance_for_compilation.import_code[i] = placeholder; |
+ imports->set(i, *placeholder); |
+ } |
+ maybe_imports = imports; |
+ } |
isolate->counters()->wasm_functions_per_module()->AddSample( |
static_cast<int>(functions.size())); |
- if (!FLAG_trace_wasm_decoder && FLAG_wasm_num_compilation_tasks != 0) { |
+ if (FLAG_wasm_num_compilation_tasks != 0) { |
CompileInParallel(isolate, this, |
temp_instance_for_compilation.function_code, thrower, |
&module_env); |
@@ -1075,48 +1317,40 @@ |
for (size_t i = FLAG_skip_compiling_wasm_funcs; |
i < temp_instance_for_compilation.function_code.size(); ++i) { |
Code* code = *temp_instance_for_compilation.function_code[i]; |
- code_table->set(static_cast<int>(i), code); |
- } |
- |
- // Link the functions in the module. |
- for (size_t i = FLAG_skip_compiling_wasm_funcs; |
- i < temp_instance_for_compilation.function_code.size(); ++i) { |
- Handle<Code> code = temp_instance_for_compilation.function_code[i]; |
- bool modified = |
- LinkFunction(code, temp_instance_for_compilation.function_code); |
- if (modified) { |
- // TODO(mtrofin): do we need to flush the cache here? |
- Assembler::FlushICache(isolate, code->instruction_start(), |
- code->instruction_size()); |
- } |
- } |
+ compiled_functions->set(static_cast<int>(i), code); |
+ } |
+ |
+ LinkModuleFunctions(isolate, compiled_functions); |
+ |
+ // TODO(mtrofin): do we need to flush the cache here? |
+ FlushAssemblyCache(isolate, compiled_functions); |
// Create the compiled module object, and populate with compiled functions |
// and information needed at instantiation time. This object needs to be |
// serializable. Instantiation may occur off a deserialized version of this |
// object. |
Handle<FixedArray> ret = |
- factory->NewFixedArray(kWasmCompiledModuleSize, TENURED); |
- ret->set(kCodeTable, *code_table); |
+ factory->NewFixedArray(kCompiledWasmObjectTableSize, TENURED); |
+ ret->set(kFunctions, *compiled_functions); |
if (!indirect_table.is_null()) { |
ret->set(kTableOfIndirectFunctionTables, *indirect_table.ToHandleChecked()); |
} |
- Handle<FixedArray> import_data = GetImportsData(factory, this); |
+ if (!maybe_imports.is_null()) { |
+ ret->set(kImportMap, *maybe_imports.ToHandleChecked()); |
+ } |
+ Handle<FixedArray> import_data = GetImportsMetadata(factory, this); |
ret->set(kImportData, *import_data); |
- // Compile exported function wrappers. |
- int export_size = static_cast<int>(num_exported_functions); |
+ // Compile export functions. |
+ int export_size = static_cast<int>(export_table.size()); |
+ Handle<Code> startup_fct; |
if (export_size > 0) { |
Handle<FixedArray> exports = factory->NewFixedArray(export_size, TENURED); |
- int index = -1; |
- |
- for (const WasmExport& exp : export_table) { |
- if (exp.kind != kExternalFunction) |
- continue; // skip non-function exports. |
- index++; |
- Handle<FixedArray> export_data = |
- factory->NewFixedArray(kWasmExportDataSize, TENURED); |
- FunctionSig* funcSig = functions[exp.index].sig; |
+ for (int i = 0; i < export_size; ++i) { |
+ Handle<FixedArray> export_metadata = |
+ factory->NewFixedArray(kWasmExportMetadataTableSize, TENURED); |
+ const WasmExport& exp = export_table[i]; |
+ FunctionSig* funcSig = functions[exp.func_index].sig; |
Handle<ByteArray> exportedSig = |
factory->NewByteArray(static_cast<int>(funcSig->parameter_count() + |
funcSig->return_count()), |
@@ -1124,34 +1358,45 @@ |
exportedSig->copy_in(0, |
reinterpret_cast<const byte*>(funcSig->raw_data()), |
exportedSig->length()); |
- export_data->set(kExportedSignature, *exportedSig); |
+ export_metadata->set(kExportedSignature, *exportedSig); |
WasmName str = GetName(exp.name_offset, exp.name_length); |
Handle<String> name = factory->InternalizeUtf8String(str); |
- Handle<Code> code = code_table->GetValueChecked<Code>(isolate, exp.index); |
+ Handle<Code> code = |
+ temp_instance_for_compilation.function_code[exp.func_index]; |
Handle<Code> export_code = compiler::CompileJSToWasmWrapper( |
- isolate, &module_env, code, exp.index); |
+ isolate, &module_env, code, exp.func_index); |
if (thrower->error()) return nothing; |
- export_data->set(kExportName, *name); |
- export_data->set(kExportArity, |
- Smi::FromInt(static_cast<int>( |
- functions[exp.index].sig->parameter_count()))); |
- export_data->set(kExportedFunctionIndex, |
- Smi::FromInt(static_cast<int>(exp.index))); |
- exports->set(index, *export_data); |
- code_table->set(static_cast<int>(functions.size() + index), *export_code); |
- } |
- ret->set(kExportData, *exports); |
- } |
- |
- // Record data for startup function. |
+ export_metadata->set(kExportCode, *export_code); |
+ export_metadata->set(kExportName, *name); |
+ export_metadata->set( |
+ kExportArity, Smi::FromInt(static_cast<int>( |
+ functions[exp.func_index].sig->parameter_count()))); |
+ export_metadata->set(kExportedFunctionIndex, |
+ Smi::FromInt(static_cast<int>(exp.func_index))); |
+ exports->set(i, *export_metadata); |
+ if (exp.func_index == start_function_index) { |
+ startup_fct = export_code; |
+ } |
+ } |
+ ret->set(kExports, *exports); |
+ } |
+ |
+ // Compile startup function, if we haven't already. |
if (start_function_index >= 0) { |
+ uint32_t index = static_cast<uint32_t>(start_function_index); |
HandleScope scope(isolate); |
- Handle<FixedArray> startup_data = |
- factory->NewFixedArray(kWasmExportDataSize, TENURED); |
- startup_data->set(kExportArity, Smi::FromInt(0)); |
- startup_data->set(kExportedFunctionIndex, |
- Smi::FromInt(start_function_index)); |
- ret->set(kStartupData, *startup_data); |
+ if (startup_fct.is_null()) { |
+ Handle<Code> code = temp_instance_for_compilation.function_code[index]; |
+ DCHECK_EQ(0, functions[index].sig->parameter_count()); |
+ startup_fct = |
+ compiler::CompileJSToWasmWrapper(isolate, &module_env, code, index); |
+ } |
+ Handle<FixedArray> metadata = |
+ factory->NewFixedArray(kWasmExportMetadataTableSize, TENURED); |
+ metadata->set(kExportCode, *startup_fct); |
+ metadata->set(kExportArity, Smi::FromInt(0)); |
+ metadata->set(kExportedFunctionIndex, Smi::FromInt(start_function_index)); |
+ ret->set(kStartupFunction, *metadata); |
} |
// TODO(wasm): saving the module bytes for debugging is wasteful. We should |
@@ -1231,211 +1476,173 @@ |
return cloned_indirect_tables; |
} |
-// Instantiates a WASM module, creating a WebAssembly.Instance from a |
-// WebAssembly.Module. |
+Handle<FixedArray> CloneModuleForInstance(Isolate* isolate, |
+ Handle<JSObject> module_object, |
+ bool* template_is_owned, |
+ Handle<FixedArray>* module_template) { |
+ Factory* factory = isolate->factory(); |
+ |
+ Handle<FixedArray> original; |
+ for (int i = 0; i < 2; ++i) { |
+ original = handle(FixedArray::cast(module_object->GetInternalField(0))); |
+ if (GetOwningInstance(*original) == nullptr) { |
+ *template_is_owned = false; |
+ *module_template = original; |
+ return original; |
+ } |
+ if (i < 1) { |
+ isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags, |
+ GarbageCollectionReason::kRuntime); |
+ } |
+ } |
+ *template_is_owned = true; |
+ *module_template = original; |
+ |
+ // We insert the latest clone in front. |
+ Handle<FixedArray> clone = factory->CopyFixedArray(original); |
+ Handle<WeakCell> weak_link_to_wasm_obj = |
+ original->GetValueChecked<WeakCell>(isolate, kModuleObject); |
+ |
+ clone->set(kModuleObject, *weak_link_to_wasm_obj); |
+ Handle<WeakCell> link_to_original = factory->NewWeakCell(original); |
+ clone->set(kNextInstance, *link_to_original); |
+ Handle<WeakCell> link_to_clone = factory->NewWeakCell(clone); |
+ original->set(kPrevInstance, *link_to_clone); |
+ JSObject::cast(weak_link_to_wasm_obj->value())->SetInternalField(0, *clone); |
+ |
+ // Clone each wasm code object. |
+ Handle<FixedArray> orig_wasm_functions = |
+ original->GetValueChecked<FixedArray>(isolate, kFunctions); |
+ Handle<FixedArray> clone_wasm_functions = |
+ factory->CopyFixedArray(orig_wasm_functions); |
+ clone->set(kFunctions, *clone_wasm_functions); |
+ for (int i = 0; i < clone_wasm_functions->length(); ++i) { |
+ Handle<Code> orig_code = |
+ clone_wasm_functions->GetValueChecked<Code>(isolate, i); |
+ Handle<Code> cloned_code = factory->CopyCode(orig_code); |
+ clone_wasm_functions->set(i, *cloned_code); |
+ } |
+ |
+ MaybeHandle<FixedArray> maybe_orig_exports = |
+ original->GetValue<FixedArray>(isolate, kExports); |
+ Handle<FixedArray> orig_exports; |
+ if (maybe_orig_exports.ToHandle(&orig_exports)) { |
+ Handle<FixedArray> cloned_exports = factory->CopyFixedArray(orig_exports); |
+ clone->set(kExports, *cloned_exports); |
+ for (int i = 0; i < orig_exports->length(); ++i) { |
+ Handle<FixedArray> export_metadata = |
+ orig_exports->GetValueChecked<FixedArray>(isolate, i); |
+ Handle<FixedArray> clone_metadata = |
+ factory->CopyFixedArray(export_metadata); |
+ cloned_exports->set(i, *clone_metadata); |
+ Handle<Code> orig_code = |
+ export_metadata->GetValueChecked<Code>(isolate, kExportCode); |
+ Handle<Code> cloned_code = factory->CopyCode(orig_code); |
+ clone_metadata->set(kExportCode, *cloned_code); |
+ // TODO(wasm): This is actually a uint32_t, but since FixedArray indexes |
+ // in int, we are taking the risk of invalid values. |
+ int exported_fct_index = |
+ Smi::cast(export_metadata->get(kExportedFunctionIndex))->value(); |
+ CHECK_GE(exported_fct_index, 0); |
+ CHECK_LT(exported_fct_index, clone_wasm_functions->length()); |
+ Handle<Code> new_target = clone_wasm_functions->GetValueChecked<Code>( |
+ isolate, exported_fct_index); |
+ PatchJSWrapper(isolate, cloned_code, new_target); |
+ } |
+ } |
+ |
+ MaybeHandle<FixedArray> maybe_startup = |
+ original->GetValue<FixedArray>(isolate, kStartupFunction); |
+ if (!maybe_startup.is_null()) { |
+ Handle<FixedArray> startup_metadata = |
+ factory->CopyFixedArray(maybe_startup.ToHandleChecked()); |
+ Handle<Code> startup_fct_clone = factory->CopyCode( |
+ startup_metadata->GetValueChecked<Code>(isolate, kExportCode)); |
+ startup_metadata->set(kExportCode, *startup_fct_clone); |
+ clone->set(kStartupFunction, *startup_metadata); |
+ // TODO(wasm): see todo above about int vs size_t indexing in FixedArray. |
+ int startup_fct_index = |
+ Smi::cast(startup_metadata->get(kExportedFunctionIndex))->value(); |
+ CHECK_GE(startup_fct_index, 0); |
+ CHECK_LT(startup_fct_index, clone_wasm_functions->length()); |
+ Handle<Code> new_target = |
+ clone_wasm_functions->GetValueChecked<Code>(isolate, startup_fct_index); |
+ PatchJSWrapper(isolate, startup_fct_clone, new_target); |
+ } |
+ clone->set(kImportMap, *isolate->factory()->undefined_value()); |
+ return clone; |
+} |
+ |
+// Instantiates a wasm module as a JSObject. |
+// * allocates a backing store of {mem_size} bytes. |
+// * installs a named property "memory" for that buffer if exported |
+// * installs named properties on the object for exported functions |
+// * compiles wasm code to machine code |
MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate, |
- ErrorThrower* thrower, |
Handle<JSObject> module_object, |
Handle<JSReceiver> ffi, |
Handle<JSArrayBuffer> memory) { |
- MaybeHandle<JSObject> nothing; |
HistogramTimerScope wasm_instantiate_module_time_scope( |
isolate->counters()->wasm_instantiate_module_time()); |
+ ErrorThrower thrower(isolate, "WasmModule::Instantiate()"); |
Factory* factory = isolate->factory(); |
- //-------------------------------------------------------------------------- |
- // Reuse the compiled module (if no owner), otherwise clone. |
- //-------------------------------------------------------------------------- |
- Handle<FixedArray> compiled_module; |
- Handle<FixedArray> code_table; |
- Handle<FixedArray> old_code_table; |
- Handle<JSObject> owner; |
- { |
- Handle<FixedArray> original( |
- FixedArray::cast(module_object->GetInternalField(0)), isolate); |
- // Always make a new copy of the code_table, since the old_code_table |
- // may still have placeholders for imports. |
- old_code_table = original->GetValueChecked<FixedArray>(isolate, kCodeTable); |
- code_table = factory->CopyFixedArray(old_code_table); |
- |
- WeakCell* tmp = GetOwningInstance(*original); |
- if (tmp != nullptr) { |
- // There is already an owner, clone everything. |
- owner = Handle<JSObject>(JSObject::cast(tmp->value()), isolate); |
- // Insert the latest clone in front. |
- compiled_module = factory->CopyFixedArray(original); |
- Handle<WeakCell> weak_link_to_wasm_obj = |
- original->GetValueChecked<WeakCell>(isolate, kModuleObject); |
- |
- compiled_module->set(kModuleObject, *weak_link_to_wasm_obj); |
- Handle<WeakCell> link_to_original = factory->NewWeakCell(original); |
- compiled_module->set(kNextInstance, *link_to_original); |
- Handle<WeakCell> link_to_clone = factory->NewWeakCell(compiled_module); |
- original->set(kPrevInstance, *link_to_clone); |
- JSObject::cast(weak_link_to_wasm_obj->value()) |
- ->SetInternalField(0, *compiled_module); |
- |
- // Clone the code for WASM functions and exports. |
- for (int i = 0; i < code_table->length(); ++i) { |
- Handle<Code> orig_code = code_table->GetValueChecked<Code>(isolate, i); |
- switch (orig_code->kind()) { |
- case Code::WASM_TO_JS_FUNCTION: |
- // Imports will be overwritten with newly compiled wrappers. |
- break; |
- case Code::JS_TO_WASM_FUNCTION: |
- case Code::WASM_FUNCTION: { |
- Handle<Code> code = factory->CopyCode(orig_code); |
- code_table->set(i, *code); |
- break; |
- } |
- default: |
- UNREACHABLE(); |
- } |
- } |
- RecordStats(isolate, code_table); |
- } else { |
- // There was no owner, so we can reuse the original. |
- compiled_module = original; |
- } |
- compiled_module->set(kCodeTable, *code_table); |
- } |
- |
- //-------------------------------------------------------------------------- |
- // Allocate the instance object. |
- //-------------------------------------------------------------------------- |
+ bool template_is_owned = false; |
+ Handle<FixedArray> compiled_module_template; |
+ Handle<FixedArray> compiled_module = CloneModuleForInstance( |
+ isolate, module_object, &template_is_owned, &compiled_module_template); |
+ |
+ MaybeHandle<JSObject> template_owner; |
+ if (template_is_owned) { |
+ Handle<WeakCell> weak_owner = |
+ compiled_module_template->GetValueChecked<WeakCell>(isolate, |
+ kOwningInstance); |
+ template_owner = handle(JSObject::cast(weak_owner->value())); |
+ } |
+ // These fields are compulsory. |
+ Handle<FixedArray> code_table = |
+ compiled_module->GetValueChecked<FixedArray>(isolate, kFunctions); |
+ |
+ RecordStats(isolate, code_table); |
+ |
+ MaybeHandle<JSObject> nothing; |
+ |
Handle<Map> map = factory->NewMap( |
JS_OBJECT_TYPE, |
JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize); |
- Handle<JSObject> instance = factory->NewJSObjectFromMap(map, TENURED); |
- instance->SetInternalField(kWasmModuleCodeTable, *code_table); |
- |
- //-------------------------------------------------------------------------- |
- // Set up the memory for the new instance. |
- //-------------------------------------------------------------------------- |
- MaybeHandle<JSArrayBuffer> old_memory; |
- // TODO(titzer): handle imported memory properly. |
- |
- uint32_t min_mem_pages = static_cast<uint32_t>( |
- Smi::cast(compiled_module->get(kMinRequiredMemory))->value()); |
- isolate->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages); |
- // TODO(wasm): re-enable counter for max_mem_pages when we use that field. |
- |
- if (memory.is_null() && min_mem_pages > 0) { |
- memory = AllocateMemory(thrower, isolate, min_mem_pages); |
- if (memory.is_null()) return nothing; // failed to allocate memory |
- } |
- |
- if (!memory.is_null()) { |
- instance->SetInternalField(kWasmMemArrayBuffer, *memory); |
- Address mem_start = static_cast<Address>(memory->backing_store()); |
- uint32_t mem_size = static_cast<uint32_t>(memory->byte_length()->Number()); |
- LoadDataSegments(compiled_module, mem_start, mem_size); |
- |
- uint32_t old_mem_size = static_cast<uint32_t>( |
- compiled_module->GetValueChecked<HeapNumber>(isolate, kMemSize) |
- ->value()); |
- MaybeHandle<JSArrayBuffer> old_mem = |
- compiled_module->GetValue<JSArrayBuffer>(isolate, kMemStart); |
- Address old_mem_start = |
- old_mem.is_null() |
- ? nullptr |
- : static_cast<Address>(old_mem.ToHandleChecked()->backing_store()); |
- RelocateInstanceCode(instance, old_mem_start, mem_start, old_mem_size, |
- mem_size); |
- compiled_module->GetValueChecked<HeapNumber>(isolate, kMemSize) |
- ->set_value(static_cast<double>(mem_size)); |
- compiled_module->set(kMemStart, *memory); |
- } |
- |
- //-------------------------------------------------------------------------- |
- // Set up the globals for the new instance. |
- //-------------------------------------------------------------------------- |
- MaybeHandle<JSArrayBuffer> old_globals; |
- MaybeHandle<JSArrayBuffer> globals; |
- uint32_t globals_size = static_cast<uint32_t>( |
- Smi::cast(compiled_module->get(kGlobalsSize))->value()); |
- if (globals_size > 0) { |
- Handle<JSArrayBuffer> global_buffer = NewArrayBuffer(isolate, globals_size); |
- globals = global_buffer; |
- if (globals.is_null()) { |
- thrower->Error("Out of memory: wasm globals"); |
- return nothing; |
- } |
- Address old_address = |
- owner.is_null() ? nullptr : GetGlobalStartAddressFromCodeTemplate( |
- *isolate->factory()->undefined_value(), |
- JSObject::cast(*owner)); |
- RelocateGlobals(instance, old_address, |
- static_cast<Address>(global_buffer->backing_store())); |
- instance->SetInternalField(kWasmGlobalsArrayBuffer, *global_buffer); |
- } |
- |
- //-------------------------------------------------------------------------- |
- // Compile the import wrappers for the new instance. |
- //-------------------------------------------------------------------------- |
- // TODO(titzer): handle imported globals and function tables. |
- Handle<FixedArray> import_data; |
- int num_imported_functions = 0; |
- if (compiled_module->GetValue<FixedArray>(isolate, kImportData) |
- .ToHandle(&import_data)) { |
- num_imported_functions = import_data->length(); |
- for (int index = 0; index < num_imported_functions; index++) { |
- Handle<Code> import_wrapper = |
- CompileImportWrapper(isolate, ffi, index, import_data, thrower); |
- if (thrower->error()) return nothing; |
- code_table->set(index, *import_wrapper); |
- RecordStats(isolate, *import_wrapper); |
- } |
- } |
- |
- //-------------------------------------------------------------------------- |
- // Set up the debug support for the new instance. |
- //-------------------------------------------------------------------------- |
- MaybeHandle<String> module_bytes_string = |
- compiled_module->GetValue<String>(isolate, kModuleBytes); |
- if (!module_bytes_string.is_null()) { |
- instance->SetInternalField(kWasmModuleBytesString, |
- *module_bytes_string.ToHandleChecked()); |
- } |
- |
- MaybeHandle<ByteArray> function_name_table = |
- compiled_module->GetValue<ByteArray>(isolate, kFunctionNameTable); |
- if (!function_name_table.is_null()) { |
- Handle<ByteArray> handle = function_name_table.ToHandleChecked(); |
- instance->SetInternalField(kWasmFunctionNamesArray, *handle); |
- } |
- |
- { |
- Handle<Object> handle = factory->NewNumber(num_imported_functions); |
- instance->SetInternalField(kWasmNumImportedFunctions, *handle); |
- } |
- |
- //-------------------------------------------------------------------------- |
- // Set up the runtime support for the new instance. |
- //-------------------------------------------------------------------------- |
- Handle<WeakCell> weak_link = isolate->factory()->NewWeakCell(instance); |
- |
- for (int i = num_imported_functions + FLAG_skip_compiling_wasm_funcs; |
- i < code_table->length(); ++i) { |
- Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i); |
- if (code->kind() == Code::WASM_FUNCTION) { |
- Handle<FixedArray> deopt_data = |
- isolate->factory()->NewFixedArray(2, TENURED); |
- deopt_data->set(0, *weak_link); |
- deopt_data->set(1, Smi::FromInt(static_cast<int>(i))); |
- deopt_data->set_length(2); |
- code->set_deoptimization_data(*deopt_data); |
- } |
- } |
- |
- //-------------------------------------------------------------------------- |
- // Set up the indirect function tables for the new instance. |
- //-------------------------------------------------------------------------- |
+ Handle<JSObject> js_object = factory->NewJSObjectFromMap(map, TENURED); |
+ js_object->SetInternalField(kWasmModuleCodeTable, *code_table); |
+ |
+ // Remember the old imports, for the case when we are at the first instance - |
+ // they will be replaced with the instance's actual imports in SetupImports. |
+ MaybeHandle<FixedArray> old_imports = |
+ compiled_module_template->GetValue<FixedArray>(isolate, kImportMap); |
+ if (!(SetupInstanceHeap(isolate, compiled_module, js_object, memory, |
+ &thrower) && |
+ SetupGlobals(isolate, template_owner, compiled_module, js_object, |
+ &thrower) && |
+ SetupImports(isolate, compiled_module, js_object, &thrower, ffi) && |
+ SetupExportsObject(compiled_module, isolate, js_object, &thrower))) { |
+ return nothing; |
+ } |
+ |
+ FixupFunctionsAndImports( |
+ compiled_module_template->GetValueChecked<FixedArray>(isolate, |
+ kFunctions), |
+ code_table, old_imports, |
+ compiled_module->GetValue<FixedArray>(isolate, kImportMap)); |
+ |
+ SetDebugSupport(factory, compiled_module, js_object); |
+ SetRuntimeSupport(isolate, js_object); |
+ |
+ FlushAssemblyCache(isolate, code_table); |
+ |
{ |
std::vector<Handle<Code>> functions( |
static_cast<size_t>(code_table->length())); |
for (int i = 0; i < code_table->length(); ++i) { |
- functions[i] = code_table->GetValueChecked<Code>(isolate, i); |
+ functions[static_cast<size_t>(i)] = |
+ code_table->GetValueChecked<Code>(isolate, i); |
} |
MaybeHandle<FixedArray> maybe_indirect_tables = |
@@ -1444,9 +1651,11 @@ |
Handle<FixedArray> indirect_tables_template; |
if (maybe_indirect_tables.ToHandle(&indirect_tables_template)) { |
Handle<FixedArray> to_replace = |
- owner.is_null() ? indirect_tables_template |
- : handle(FixedArray::cast(owner->GetInternalField( |
- kWasmModuleFunctionTable))); |
+ template_owner.is_null() |
+ ? indirect_tables_template |
+ : handle(FixedArray::cast( |
+ template_owner.ToHandleChecked()->GetInternalField( |
+ kWasmModuleFunctionTable))); |
Handle<FixedArray> indirect_tables = SetupIndirectFunctionTable( |
isolate, code_table, indirect_tables_template, to_replace); |
for (int i = 0; i < indirect_tables->length(); ++i) { |
@@ -1457,104 +1666,24 @@ |
metadata->GetValueChecked<FixedArray>(isolate, kTable); |
wasm::PopulateFunctionTable(table, size, &functions); |
} |
- instance->SetInternalField(kWasmModuleFunctionTable, *indirect_tables); |
- } |
- } |
- |
- //-------------------------------------------------------------------------- |
- // Set up the exports object for the new instance. |
- //-------------------------------------------------------------------------- |
- bool mem_export = |
- static_cast<bool>(Smi::cast(compiled_module->get(kExportMem))->value()); |
- ModuleOrigin origin = static_cast<ModuleOrigin>( |
- Smi::cast(compiled_module->get(kOrigin))->value()); |
- |
- MaybeHandle<FixedArray> maybe_exports = |
- compiled_module->GetValue<FixedArray>(isolate, kExportData); |
- if (!maybe_exports.is_null() || mem_export) { |
- PropertyDescriptor desc; |
- desc.set_writable(false); |
- |
- Handle<JSObject> exports_object = instance; |
- if (origin == kWasmOrigin) { |
- // Create the "exports" object. |
- Handle<JSFunction> object_function = Handle<JSFunction>( |
- isolate->native_context()->object_function(), isolate); |
- exports_object = factory->NewJSObject(object_function, TENURED); |
- Handle<String> exports_name = factory->InternalizeUtf8String("exports"); |
- JSObject::AddProperty(instance, exports_name, exports_object, READ_ONLY); |
- } |
- Handle<FixedArray> exports; |
- int first_export = -1; |
- // TODO(wasm): another iteration over the code objects. |
- for (int i = 0; i < code_table->length(); i++) { |
- Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i); |
- if (code->kind() == Code::JS_TO_WASM_FUNCTION) { |
- first_export = i; |
- break; |
- } |
- } |
- if (maybe_exports.ToHandle(&exports)) { |
- int export_size = exports->length(); |
- for (int i = 0; i < export_size; ++i) { |
- Handle<FixedArray> export_data = |
- exports->GetValueChecked<FixedArray>(isolate, i); |
- Handle<String> name = |
- export_data->GetValueChecked<String>(isolate, kExportName); |
- int arity = Smi::cast(export_data->get(kExportArity))->value(); |
- MaybeHandle<ByteArray> signature = |
- export_data->GetValue<ByteArray>(isolate, kExportedSignature); |
- Handle<Code> export_code = |
- code_table->GetValueChecked<Code>(isolate, first_export + i); |
- Handle<JSFunction> function = WrapExportCodeAsJSFunction( |
- isolate, export_code, name, arity, signature, instance); |
- desc.set_value(function); |
- Maybe<bool> status = JSReceiver::DefineOwnProperty( |
- isolate, exports_object, name, &desc, Object::THROW_ON_ERROR); |
- if (!status.IsJust()) { |
- thrower->Error("export of %.*s failed.", name->length(), |
- name->ToCString().get()); |
- return nothing; |
- } |
- } |
- } |
- if (mem_export) { |
- // Export the memory as a named property. |
- Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>( |
- JSArrayBuffer::cast(instance->GetInternalField(kWasmMemArrayBuffer))); |
- Handle<Object> memory_object = |
- WasmJs::CreateWasmMemoryObject(isolate, buffer, false, 0); |
- // TODO(titzer): export the memory with the correct name. |
- Handle<String> name = factory->InternalizeUtf8String("memory"); |
- JSObject::AddProperty(exports_object, name, memory_object, READ_ONLY); |
- } |
- } |
- |
- if (num_imported_functions > 0 || !owner.is_null()) { |
- // If the code was cloned, or new imports were compiled, patch. |
- PatchDirectCalls(old_code_table, code_table, num_imported_functions); |
- } |
- |
- FlushICache(isolate, code_table); |
- |
- //-------------------------------------------------------------------------- |
+ js_object->SetInternalField(kWasmModuleFunctionTable, *indirect_tables); |
+ } |
+ } |
+ |
// Run the start function if one was specified. |
- //-------------------------------------------------------------------------- |
- Handle<FixedArray> startup_data; |
- if (compiled_module->GetValue<FixedArray>(isolate, kStartupData) |
- .ToHandle(&startup_data)) { |
+ MaybeHandle<FixedArray> maybe_startup_fct = |
+ compiled_module->GetValue<FixedArray>(isolate, kStartupFunction); |
+ Handle<FixedArray> metadata; |
+ if (maybe_startup_fct.ToHandle(&metadata)) { |
HandleScope scope(isolate); |
- int32_t start_index = |
- startup_data->GetValueChecked<Smi>(isolate, kExportedFunctionIndex) |
- ->value(); |
Handle<Code> startup_code = |
- code_table->GetValueChecked<Code>(isolate, start_index); |
- int arity = Smi::cast(startup_data->get(kExportArity))->value(); |
+ metadata->GetValueChecked<Code>(isolate, kExportCode); |
+ int arity = Smi::cast(metadata->get(kExportArity))->value(); |
MaybeHandle<ByteArray> startup_signature = |
- startup_data->GetValue<ByteArray>(isolate, kExportedSignature); |
+ metadata->GetValue<ByteArray>(isolate, kExportedSignature); |
Handle<JSFunction> startup_fct = WrapExportCodeAsJSFunction( |
isolate, startup_code, factory->InternalizeUtf8String("start"), arity, |
- startup_signature, instance); |
+ startup_signature, js_object); |
RecordStats(isolate, *startup_code); |
// Call the JS function. |
Handle<Object> undefined = isolate->factory()->undefined_value(); |
@@ -1562,25 +1691,39 @@ |
Execution::Call(isolate, startup_fct, undefined, 0, nullptr); |
if (retval.is_null()) { |
- thrower->Error("WASM.instantiateModule(): start function failed"); |
+ thrower.Error("WASM.instantiateModule(): start function failed"); |
return nothing; |
} |
} |
- DCHECK(wasm::IsWasmObject(*instance)); |
+ DCHECK(wasm::IsWasmObject(*js_object)); |
if (!compiled_module->GetValue<WeakCell>(isolate, kModuleObject).is_null()) { |
- instance->SetInternalField(kWasmCompiledModule, *compiled_module); |
- Handle<WeakCell> link_to_owner = factory->NewWeakCell(instance); |
+ js_object->SetInternalField(kWasmCompiledModule, *compiled_module); |
+ Handle<WeakCell> link_to_owner = factory->NewWeakCell(js_object); |
compiled_module->set(kOwningInstance, *link_to_owner); |
- Handle<Object> global_handle = isolate->global_handles()->Create(*instance); |
+ Handle<Object> global_handle = |
+ isolate->global_handles()->Create(*js_object); |
GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(), |
&InstanceFinalizer, |
v8::WeakCallbackType::kFinalizer); |
} |
- return instance; |
+ return js_object; |
+} |
+ |
+// TODO(mtrofin): remove this once we move to WASM_DIRECT_CALL |
+Handle<Code> ModuleEnv::GetCodeOrPlaceholder(uint32_t index) const { |
+ DCHECK(IsValidFunction(index)); |
+ if (!placeholders.empty()) return placeholders[index]; |
+ DCHECK_NOT_NULL(instance); |
+ return instance->function_code[index]; |
+} |
+ |
+Handle<Code> ModuleEnv::GetImportCode(uint32_t index) { |
+ DCHECK(IsValidImport(index)); |
+ return instance ? instance->import_code[index] : Handle<Code>::null(); |
} |
compiler::CallDescriptor* ModuleEnv::GetCallDescriptor(Zone* zone, |