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

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

Issue 2056633002: [wasm] Separate compilation from instantiation (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: feedback Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/wasm/wasm-module.h ('k') | src/x64/assembler-x64.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/wasm/wasm-module.cc
diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc
index 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;
« no previous file with comments | « src/wasm/wasm-module.h ('k') | src/x64/assembler-x64.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698