Index: src/wasm/wasm-module.cc |
diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc |
index 81ae133b687a987c12554664494f82f98f07e22a..386a9ce5af8a2994d11bd5182ed246e9950cd30d 100644 |
--- a/src/wasm/wasm-module.cc |
+++ b/src/wasm/wasm-module.cc |
@@ -112,98 +112,6 @@ std::ostream& operator<<(std::ostream& os, const WasmFunctionName& pair) { |
return os; |
} |
-// A helper class for compiling multiple wasm functions that offers |
-// placeholder code objects for calling functions that are not yet compiled. |
-class WasmLinker { |
- public: |
- WasmLinker(Isolate* isolate, std::vector<Handle<Code>>* functions) |
- : isolate_(isolate), |
- placeholder_code_(functions->size()), |
- function_code_(functions) { |
- for (uint32_t i = 0; i < placeholder_code_.size(); ++i) { |
- CreatePlaceholder(i); |
- } |
- } |
- |
- Handle<Code> GetPlaceholderCode(uint32_t index) const { |
- return placeholder_code_[index]; |
- } |
- |
- void Finish(uint32_t index, Handle<Code> code) { |
- DCHECK(index < function_code().size()); |
- function_code()[index] = code; |
- } |
- |
- void Link(Handle<FixedArray> function_table, |
- const std::vector<uint16_t>& functions) { |
- for (size_t i = 0; i < function_code().size(); i++) { |
- LinkFunction(function_code()[i]); |
- } |
- if (!function_table.is_null()) { |
- int table_size = static_cast<int>(functions.size()); |
- DCHECK_EQ(function_table->length(), table_size * 2); |
- for (int i = 0; i < table_size; i++) { |
- function_table->set(i + table_size, *function_code()[functions[i]]); |
- } |
- } |
- } |
- |
- private: |
- std::vector<Handle<Code>>& function_code() { return *function_code_; } |
- |
- void CreatePlaceholder(uint32_t index) { |
- DCHECK(index < function_code().size()); |
- DCHECK(function_code()[index].is_null()); |
- // Create a placeholder code object and encode the corresponding index in |
- // the {constant_pool_offset} field of the code object. |
- // TODO(titzer): placeholder code objects are somewhat dangerous. |
- byte buffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; // fake instructions. |
- CodeDesc desc = {buffer, 8, 8, 0, 0, nullptr}; |
- Handle<Code> code = isolate_->factory()->NewCode( |
- desc, Code::KindField::encode(Code::WASM_FUNCTION), |
- Handle<Object>::null()); |
- code->set_constant_pool_offset(static_cast<int>(index) + |
- kPlaceholderMarker); |
- placeholder_code_[index] = code; |
- function_code()[index] = code; |
- } |
- |
- Isolate* isolate_; |
- std::vector<Handle<Code>> placeholder_code_; |
- std::vector<Handle<Code>>* function_code_; |
- |
- void LinkFunction(Handle<Code> code) { |
- bool modified = false; |
- int mode_mask = RelocInfo::kCodeTargetMask; |
- AllowDeferredHandleDereference embedding_raw_address; |
- for (RelocIterator it(*code, 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->kind() == Code::WASM_FUNCTION && |
- target->constant_pool_offset() >= kPlaceholderMarker) { |
- // Patch direct calls to placeholder code objects. |
- uint32_t index = target->constant_pool_offset() - kPlaceholderMarker; |
- CHECK(index < function_code().size()); |
- Handle<Code> new_target = function_code()[index]; |
- if (target != *new_target) { |
- CHECK_EQ(*placeholder_code_[index], target); |
- it.rinfo()->set_target_address(new_target->instruction_start(), |
- SKIP_WRITE_BARRIER, |
- SKIP_ICACHE_FLUSH); |
- modified = true; |
- } |
- } |
- } |
- } |
- if (modified) { |
- Assembler::FlushICache(isolate_, code->instruction_start(), |
- code->instruction_size()); |
- } |
- } |
-}; |
- |
namespace { |
// Internal constants for the layout of the module object. |
const int kWasmModuleFunctionTable = 0; |
@@ -216,6 +124,10 @@ const int kWasmModuleBytesString = 5; |
const int kWasmDebugInfo = 6; |
const int kWasmModuleInternalFieldCount = 7; |
+uint32_t GetMinModuleMemSize(const WasmModule* module) { |
+ return WasmModule::kPageSize * module->min_mem_pages; |
+} |
+ |
void LoadDataSegments(const WasmModule* module, byte* mem_addr, |
size_t mem_size) { |
for (const WasmDataSegment& segment : module->data_segments) { |
@@ -247,15 +159,13 @@ Handle<FixedArray> BuildFunctionTable(Isolate* isolate, |
Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size, |
byte** backing_store) { |
+ *backing_store = nullptr; |
if (size > (WasmModule::kMaxMemPages * WasmModule::kPageSize)) { |
// TODO(titzer): lift restriction on maximum memory allocated here. |
- *backing_store = nullptr; |
return Handle<JSArrayBuffer>::null(); |
} |
- void* memory = |
- isolate->array_buffer_allocator()->Allocate(static_cast<int>(size)); |
- if (!memory) { |
- *backing_store = nullptr; |
+ void* memory = isolate->array_buffer_allocator()->Allocate(size); |
+ if (memory == nullptr) { |
return Handle<JSArrayBuffer>::null(); |
} |
@@ -275,12 +185,27 @@ Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size, |
return buffer; |
} |
+void RelocateInstanceCode(WasmModuleInstance* instance) { |
+ for (uint32_t i = 0; i < instance->function_code.size(); ++i) { |
+ Handle<Code> function = instance->function_code[i]; |
+ AllowDeferredHandleDereference embedding_raw_address; |
+ int mask = (1 << RelocInfo::WASM_MEMORY_REFERENCE) | |
+ (1 << RelocInfo::WASM_MEMORY_SIZE_REFERENCE); |
+ for (RelocIterator it(*function, mask); !it.done(); it.next()) { |
+ it.rinfo()->update_wasm_memory_reference( |
+ nullptr, instance->mem_start, GetMinModuleMemSize(instance->module), |
+ static_cast<uint32_t>(instance->mem_size)); |
+ } |
+ } |
+} |
+ |
// Set the memory for a module instance to be the {memory} array buffer. |
void SetMemory(WasmModuleInstance* instance, Handle<JSArrayBuffer> memory) { |
memory->set_is_neuterable(false); |
instance->mem_start = reinterpret_cast<byte*>(memory->backing_store()); |
instance->mem_size = memory->byte_length()->Number(); |
instance->mem_buffer = memory; |
+ RelocateInstanceCode(instance); |
} |
// Allocate memory for a module instance as a new JSArrayBuffer. |
@@ -293,14 +218,15 @@ bool AllocateMemory(ErrorThrower* thrower, Isolate* isolate, |
thrower->Error("Out of memory: wasm memory too large"); |
return false; |
} |
- instance->mem_size = WasmModule::kPageSize * instance->module->min_mem_pages; |
+ instance->mem_size = GetMinModuleMemSize(instance->module); |
instance->mem_buffer = |
NewArrayBuffer(isolate, instance->mem_size, &instance->mem_start); |
- if (!instance->mem_start) { |
+ if (instance->mem_start == nullptr) { |
thrower->Error("Out of memory: wasm memory"); |
instance->mem_size = 0; |
return false; |
} |
+ RelocateInstanceCode(instance); |
return true; |
} |
@@ -315,9 +241,97 @@ bool AllocateGlobals(ErrorThrower* thrower, Isolate* isolate, |
thrower->Error("Out of memory: wasm globals"); |
return false; |
} |
+ |
+ for (uint32_t i = 0; i < instance->function_code.size(); ++i) { |
+ Handle<Code> function = instance->function_code[i]; |
+ AllowDeferredHandleDereference embedding_raw_address; |
+ int mask = 1 << RelocInfo::WASM_GLOBAL_REFERENCE; |
+ for (RelocIterator it(*function, mask); !it.done(); it.next()) { |
+ it.rinfo()->update_wasm_global_reference(nullptr, |
+ instance->globals_start); |
+ } |
+ } |
} |
return true; |
} |
+ |
+Handle<Code> CreatePlaceholder(Factory* factory, uint32_t index, |
+ Code::Kind kind) { |
+ // Create a placeholder code object and encode the corresponding index in |
+ // the {constant_pool_offset} field of the code object. |
+ // TODO(titzer): placeholder code objects are somewhat dangerous. |
+ static byte buffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; // fake instructions. |
+ static CodeDesc desc = {buffer, 8, 8, 0, 0, nullptr}; |
+ Handle<Code> code = factory->NewCode(desc, Code::KindField::encode(kind), |
+ Handle<Object>::null()); |
+ code->set_constant_pool_offset(static_cast<int>(index) + kPlaceholderMarker); |
+ return code; |
+} |
+ |
+// 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(Handle<Code> unlinked, |
+ const std::vector<Handle<Code>>& code_targets, |
+ Code::Kind kind) { |
+ bool modified = false; |
+ int mode_mask = RelocInfo::kCodeTargetMask; |
+ 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->kind() == kind && |
+ target->constant_pool_offset() >= kPlaceholderMarker) { |
+ // Patch direct calls to placeholder code objects. |
+ uint32_t index = target->constant_pool_offset() - kPlaceholderMarker; |
+ CHECK(index < code_targets.size()); |
+ Handle<Code> new_target = code_targets[index]; |
+ if (target != *new_target) { |
+ it.rinfo()->set_target_address(new_target->instruction_start(), |
+ SKIP_WRITE_BARRIER, SKIP_ICACHE_FLUSH); |
+ modified = true; |
+ } |
+ } |
+ } |
+ } |
+ return modified; |
+} |
+ |
+void LinkModuleFunctions(Isolate* isolate, |
+ std::vector<Handle<Code>>& functions) { |
+ for (size_t i = 0; i < functions.size(); i++) { |
+ Handle<Code> code = functions[i]; |
+ bool modified = LinkFunction(code, functions, Code::WASM_FUNCTION); |
+ if (modified) { |
+ Assembler::FlushICache(isolate, code->instruction_start(), |
+ code->instruction_size()); |
+ } |
+ } |
+} |
+ |
+void LinkImports(Isolate* isolate, std::vector<Handle<Code>>& functions, |
+ const std::vector<Handle<Code>>& imports) { |
+ for (uint32_t i = 0; i < functions.size(); ++i) { |
+ Handle<Code> code = functions[i]; |
+ bool modified = LinkFunction(code, imports, Code::WASM_TO_JS_FUNCTION); |
+ if (modified) { |
+ Assembler::FlushICache(isolate, code->instruction_start(), |
+ code->instruction_size()); |
+ } |
+ } |
+} |
+ |
} // namespace |
WasmModule::WasmModule() |
@@ -480,10 +494,10 @@ bool CompileWrappersToImportedFunctions( |
Isolate* isolate, const WasmModule* module, const Handle<JSReceiver> ffi, |
WasmModuleInstance* instance, ErrorThrower* thrower, Factory* factory, |
ModuleEnv* module_env, CodeStats& code_stats) { |
- uint32_t index = 0; |
if (module->import_table.size() > 0) { |
instance->import_code.reserve(module->import_table.size()); |
- for (const WasmImport& import : module->import_table) { |
+ for (uint32_t index = 0; index < module->import_table.size(); ++index) { |
+ const WasmImport& import = module->import_table[index]; |
WasmName module_name = module->GetNameOrNull(import.module_name_offset, |
import.module_name_length); |
WasmName function_name = module->GetNameOrNull( |
@@ -495,9 +509,8 @@ bool CompileWrappersToImportedFunctions( |
Handle<Code> code = compiler::CompileWasmToJSWrapper( |
isolate, module_env, function.ToHandleChecked(), import.sig, |
module_name, function_name); |
- instance->import_code.push_back(code); |
+ instance->import_code[index] = code; |
code_stats.Record(*code); |
- index++; |
} |
} |
return true; |
@@ -657,6 +670,18 @@ void CompileSequentially(Isolate* isolate, const WasmModule* module, |
functions[i] = code; |
} |
} |
+ |
+void PopulateFunctionTable(WasmModuleInstance* instance) { |
+ if (!instance->function_table.is_null()) { |
+ int table_size = static_cast<int>(instance->module->function_table.size()); |
+ DCHECK_EQ(instance->function_table->length(), table_size * 2); |
+ for (int i = 0; i < table_size; i++) { |
+ instance->function_table->set( |
+ i + table_size, |
+ *instance->function_code[instance->module->function_table[i]]); |
+ } |
+ } |
+} |
} // namespace |
void SetDeoptimizationData(Factory* factory, Handle<JSObject> js_object, |
@@ -675,6 +700,64 @@ void SetDeoptimizationData(Factory* factory, Handle<JSObject> js_object, |
} |
} |
+Handle<FixedArray> WasmModule::CompileFunctions(Isolate* isolate) const { |
+ Factory* factory = isolate->factory(); |
+ ErrorThrower thrower(isolate, "WasmModule::CompileFunctions()"); |
+ CodeStats code_stats; |
+ |
+ WasmModuleInstance temp_instance_for_compilation(this); |
+ temp_instance_for_compilation.function_table = |
+ BuildFunctionTable(isolate, this); |
+ temp_instance_for_compilation.context = isolate->native_context(); |
+ temp_instance_for_compilation.mem_size = GetMinModuleMemSize(this); |
+ temp_instance_for_compilation.mem_start = nullptr; |
+ temp_instance_for_compilation.globals_start = nullptr; |
+ |
+ ModuleEnv module_env; |
+ module_env.module = this; |
+ module_env.instance = &temp_instance_for_compilation; |
+ module_env.origin = origin; |
+ InitializePlaceholders(factory, &module_env.placeholders, functions.size()); |
+ |
+ Handle<FixedArray> ret = |
+ factory->NewFixedArray(static_cast<int>(functions.size()), TENURED); |
+ |
+ temp_instance_for_compilation.import_code.resize(import_table.size()); |
+ for (uint32_t i = 0; i < import_table.size(); ++i) { |
+ temp_instance_for_compilation.import_code[i] = |
+ CreatePlaceholder(factory, i, Code::WASM_TO_JS_FUNCTION); |
+ } |
+ isolate->counters()->wasm_functions_per_module()->AddSample( |
+ static_cast<int>(functions.size())); |
+ if (FLAG_wasm_num_compilation_tasks != 0) { |
+ CompileInParallel(isolate, this, |
+ temp_instance_for_compilation.function_code, &thrower, |
+ &module_env); |
+ } else { |
+ CompileSequentially(isolate, this, |
+ temp_instance_for_compilation.function_code, &thrower, |
+ &module_env); |
+ } |
+ if (thrower.error()) { |
+ return Handle<FixedArray>::null(); |
+ } |
+ |
+ LinkModuleFunctions(isolate, temp_instance_for_compilation.function_code); |
+ |
+ // At this point, compilation has completed. Update the code table |
+ // and record sizes. |
+ 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]; |
+ ret->set(static_cast<int>(i), code); |
+ code_stats.Record(code); |
+ } |
+ |
+ PopulateFunctionTable(&temp_instance_for_compilation); |
+ |
+ return ret; |
+} |
+ |
// 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 |
@@ -702,8 +785,10 @@ MaybeHandle<JSObject> WasmModule::Instantiate( |
WasmModuleInstance instance(this); |
instance.context = isolate->native_context(); |
instance.js_object = factory->NewJSObjectFromMap(map, TENURED); |
- Handle<FixedArray> code_table = |
- factory->NewFixedArray(static_cast<int>(functions.size()), TENURED); |
+ |
+ Handle<FixedArray> code_table = CompileFunctions(isolate); |
+ if (code_table.is_null()) return Handle<JSObject>::null(); |
+ |
instance.js_object->SetInternalField(kWasmModuleCodeTable, *code_table); |
size_t module_bytes_len = |
instance.module->module_end - instance.module->module_start; |
@@ -716,6 +801,11 @@ MaybeHandle<JSObject> WasmModule::Instantiate( |
instance.js_object->SetInternalField(kWasmModuleBytesString, |
*module_bytes_string); |
+ for (uint32_t i = 0; i < functions.size(); ++i) { |
+ Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i))); |
+ instance.function_code[i] = code; |
+ } |
+ |
//------------------------------------------------------------------------- |
// Allocate and initialize the linear memory. |
//------------------------------------------------------------------------- |
@@ -748,12 +838,9 @@ MaybeHandle<JSObject> WasmModule::Instantiate( |
HistogramTimerScope wasm_compile_module_time_scope( |
isolate->counters()->wasm_compile_module_time()); |
- instance.function_table = BuildFunctionTable(isolate, this); |
- WasmLinker linker(isolate, &instance.function_code); |
ModuleEnv module_env; |
module_env.module = this; |
module_env.instance = &instance; |
- module_env.linker = &linker; |
module_env.origin = origin; |
//------------------------------------------------------------------------- |
@@ -764,37 +851,10 @@ MaybeHandle<JSObject> WasmModule::Instantiate( |
code_stats)) { |
return MaybeHandle<JSObject>(); |
} |
- //------------------------------------------------------------------------- |
- // Compile all functions in the module. |
- //------------------------------------------------------------------------- |
{ |
- isolate->counters()->wasm_functions_per_module()->AddSample( |
- static_cast<int>(functions.size())); |
- if (FLAG_wasm_num_compilation_tasks != 0) { |
- CompileInParallel(isolate, this, instance.function_code, &thrower, |
- &module_env); |
- } else { |
- // 5) The main thread finishes the compilation. |
- CompileSequentially(isolate, this, instance.function_code, &thrower, |
- &module_env); |
- } |
- if (thrower.error()) { |
- return Handle<JSObject>::null(); |
- } |
- |
- // At this point, compilation has completed. Update the code table |
- // and record sizes. |
- for (size_t i = FLAG_skip_compiling_wasm_funcs; |
- i < instance.function_code.size(); ++i) { |
- Code* code = *instance.function_code[i]; |
- code_table->set(static_cast<int>(i), code); |
- code_stats.Record(code); |
- } |
- |
- // Patch all direct call sites. |
- linker.Link(instance.function_table, this->function_table); |
instance.js_object->SetInternalField(kWasmModuleFunctionTable, |
Smi::FromInt(0)); |
+ LinkImports(isolate, instance.function_code, instance.import_code); |
SetDeoptimizationData(factory, instance.js_object, instance.function_code); |
@@ -878,9 +938,10 @@ MaybeHandle<JSObject> WasmModule::Instantiate( |
return instance.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 (linker != nullptr) return linker->GetPlaceholderCode(index); |
+ if (!placeholders.empty()) return placeholders[index]; |
DCHECK_NOT_NULL(instance); |
return instance->function_code[index]; |
} |
@@ -928,6 +989,14 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start, |
int32_t CompileAndRunWasmModule(Isolate* isolate, const WasmModule* module) { |
ErrorThrower thrower(isolate, "CompileAndRunWasmModule"); |
WasmModuleInstance instance(module); |
+ Handle<FixedArray> code_table = module->CompileFunctions(isolate); |
+ |
+ if (code_table.is_null()) return -1; |
+ |
+ for (uint32_t i = 0; i < module->functions.size(); ++i) { |
+ Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i))); |
+ instance.function_code[i] = code; |
+ } |
// Allocate and initialize the linear memory. |
if (!AllocateMemory(&thrower, isolate, &instance)) { |
@@ -940,17 +1009,12 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const WasmModule* module) { |
return -1; |
} |
- // Build the function table. |
- instance.function_table = BuildFunctionTable(isolate, module); |
- |
- // Create module environment. |
- WasmLinker linker(isolate, &instance.function_code); |
ModuleEnv module_env; |
module_env.module = module; |
module_env.instance = &instance; |
- module_env.linker = &linker; |
module_env.origin = module->origin; |
- |
+ InitializePlaceholders(isolate->factory(), &module_env.placeholders, |
+ module->functions.size()); |
if (module->export_table.size() == 0) { |
thrower.Error("WASM.compileRun() failed: no exported functions"); |
return -2; |
@@ -961,11 +1025,11 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const WasmModule* module) { |
// Compile the function and install it in the linker. |
Handle<Code> code = compiler::WasmCompilationUnit::CompileWasmFunction( |
&thrower, isolate, &module_env, &func); |
- if (!code.is_null()) linker.Finish(func.func_index, code); |
+ if (!code.is_null()) instance.function_code[func.func_index] = code; |
if (thrower.error()) return -1; |
} |
- linker.Link(instance.function_table, instance.module->function_table); |
+ LinkModuleFunctions(isolate, instance.function_code); |
// Wrap the main code so it can be called as a JS function. |
uint32_t main_index = module->export_table.back().func_index; |