Index: src/wasm/wasm-objects.cc |
diff --git a/src/wasm/wasm-objects.cc b/src/wasm/wasm-objects.cc |
index c9b552f4a78a0260abe6e8266e132354a8da12c6..9d66a7736c6e4be24ef10e3f30b663e351a00f52 100644 |
--- a/src/wasm/wasm-objects.cc |
+++ b/src/wasm/wasm-objects.cc |
@@ -5,10 +5,13 @@ |
#include "src/wasm/wasm-objects.h" |
#include "src/utils.h" |
+#include "src/assembler-inl.h" |
#include "src/base/iterator.h" |
+#include "src/compiler/wasm-compiler.h" |
#include "src/debug/debug-interface.h" |
#include "src/objects-inl.h" |
#include "src/wasm/module-decoder.h" |
+#include "src/wasm/wasm-code-specialization.h" |
#include "src/wasm/wasm-module.h" |
#include "src/wasm/wasm-text.h" |
@@ -214,6 +217,218 @@ bool IsBreakablePosition(Handle<WasmCompiledModule> compiled_module, |
} |
#endif // DEBUG |
+int AdvanceSourcePositionTableIterator(SourcePositionTableIterator& iterator, |
+ int offset) { |
+ DCHECK(!iterator.done()); |
+ int byte_pos; |
+ do { |
+ byte_pos = iterator.source_position().ScriptOffset(); |
+ iterator.Advance(); |
+ } while (!iterator.done() && iterator.code_offset() <= offset); |
+ return byte_pos; |
+} |
+ |
+int ExtractDirectCallIndex(wasm::Decoder& decoder, const byte* pc) { |
+ DCHECK_EQ(static_cast<int>(kExprCallFunction), static_cast<int>(*pc)); |
+ decoder.Reset(pc + 1, pc + 6); |
+ uint32_t call_idx = decoder.consume_u32v("call index"); |
+ DCHECK(decoder.ok()); |
+ DCHECK_GE(kMaxInt, call_idx); |
+ return static_cast<int>(call_idx); |
+} |
+ |
+static void RecordLazyCodeStats(Isolate* isolate, Code* code) { |
+ isolate->counters()->asm_wasm_lazily_compiled_functions()->Increment(); |
+ isolate->counters()->wasm_generated_code_size()->Increment(code->body_size()); |
+ isolate->counters()->wasm_reloc_size()->Increment( |
+ code->relocation_info()->length()); |
+} |
+ |
+class LazyCompilationOrchestrator { |
+ bool CompileFunction(Isolate* isolate, Handle<WasmInstanceObject> instance, |
+ int func_index) WARN_UNUSED_RESULT { |
+ Handle<WasmCompiledModule> compiled_module(instance->compiled_module(), |
+ isolate); |
+ if (Code::cast(compiled_module->code_table()->get(func_index))->kind() == |
+ Code::WASM_FUNCTION) { |
+ return true; |
+ } |
+ wasm::ModuleEnv module_env(compiled_module->module(), nullptr); |
+ // TODO(clemensh): Remove this; it's not concurrency-safe. |
+ std::vector<Handle<FixedArray>> fun_tables; |
+ std::vector<Handle<FixedArray>> sig_tables; |
+ size_t num_function_tables = |
+ compiled_module->module()->function_tables.size(); |
+ for (size_t i = 0; i < num_function_tables; ++i) { |
+ fun_tables.push_back(isolate->factory()->NewFixedArray(1)); |
+ sig_tables.push_back(isolate->factory()->NewFixedArray(1)); |
+ } |
+ module_env.function_tables = &fun_tables; |
+ module_env.signature_tables = &sig_tables; |
+ uint8_t* module_start = compiled_module->module_bytes()->GetChars(); |
+ const WasmFunction* func = &module_env.module->functions[func_index]; |
+ wasm::FunctionBody body{func->sig, module_start, |
+ module_start + func->code_start_offset, |
+ module_start + func->code_end_offset}; |
+ wasm::WasmName name = Vector<const char>::cast( |
+ compiled_module->GetRawFunctionName(func_index)); |
+ ErrorThrower thrower(isolate, "WasmLazyCompile"); |
+ compiler::WasmCompilationUnit unit(isolate, &module_env, body, name, |
+ func_index); |
+ unit.ExecuteCompilation(); |
+ Handle<Code> code = unit.FinishCompilation(&thrower); |
+ |
+ Handle<FixedArray> deopt_data = |
+ isolate->factory()->NewFixedArray(2, TENURED); |
+ Handle<WeakCell> weak_instance = isolate->factory()->NewWeakCell(instance); |
+ deopt_data->set(0, *weak_instance); |
+ deopt_data->set(1, Smi::FromInt(func_index)); |
+ code->set_deoptimization_data(*deopt_data); |
+ |
+ if (thrower.error()) { |
+ if (!isolate->has_pending_exception()) isolate->Throw(*thrower.Reify()); |
+ return false; |
+ } |
+ |
+ DCHECK_EQ(Builtins::kWasmCompileLazy, |
+ Code::cast(compiled_module->code_table()->get(func_index)) |
+ ->builtin_index()); |
+ compiled_module->code_table()->set(func_index, *code); |
+ |
+ // Now specialize the generated code for this instance. |
+ Zone specialization_zone(isolate->allocator(), ZONE_NAME); |
+ CodeSpecialization code_specialization(isolate, &specialization_zone); |
+ if (module_env.module->globals_size) { |
+ Address globals_start = reinterpret_cast<Address>( |
+ instance->globals_buffer()->backing_store()); |
+ code_specialization.RelocateGlobals(nullptr, globals_start); |
+ } |
+ if (instance->has_memory_buffer()) { |
+ Address mem_start = |
+ reinterpret_cast<Address>(instance->memory_buffer()->backing_store()); |
+ int mem_size = instance->memory_buffer()->byte_length()->Number(); |
+ DCHECK_IMPLIES(mem_size == 0, mem_start == nullptr); |
+ if (mem_size > 0) { |
+ code_specialization.RelocateMemoryReferences(nullptr, 0, mem_start, |
+ mem_size); |
+ } |
+ } |
+ code_specialization.RelocateDirectCalls(instance); |
+ if (module_env.used_indirect_tables) { |
+ for (size_t i = 0; i < num_function_tables; ++i) { |
+ Handle<Object> new_fun_table( |
+ compiled_module->function_tables()->get(static_cast<int>(i)), |
+ isolate); |
+ code_specialization.RelocateObject(fun_tables[i], new_fun_table); |
+ Handle<Object> new_sig_table( |
+ compiled_module->signature_tables()->get(static_cast<int>(i)), |
+ isolate); |
+ code_specialization.RelocateObject(sig_tables[i], new_sig_table); |
+ } |
+ } |
+ code_specialization.ApplyToWasmCode(*code, SKIP_ICACHE_FLUSH); |
+ Assembler::FlushICache(isolate, code->instruction_start(), |
+ code->instruction_size()); |
+ RecordLazyCodeStats(isolate, *code); |
+ return true; |
+ } |
+ |
+ public: |
+ MaybeHandle<Code> CompileLazy(Isolate* isolate, |
+ Handle<WasmInstanceObject> instance, |
+ Handle<Code> caller, int call_offset, |
+ int exported_func_index, bool patch_caller) { |
+ struct NonCompiledFunction { |
+ int offset; |
+ int func_index; |
+ }; |
+ std::vector<NonCompiledFunction> non_compiled_functions; |
+ int func_to_return_idx = exported_func_index; |
+ wasm::Decoder decoder(nullptr, nullptr); |
+ bool is_js_to_wasm = caller->kind() == Code::JS_TO_WASM_FUNCTION; |
+ Handle<WasmCompiledModule> compiled_module(instance->compiled_module(), |
+ isolate); |
+ |
+ if (is_js_to_wasm) { |
+ non_compiled_functions.push_back({0, exported_func_index}); |
+ } else if (patch_caller) { |
+ DisallowHeapAllocation no_gc; |
+ SeqOneByteString* module_bytes = compiled_module->module_bytes(); |
+ SourcePositionTableIterator source_pos_iterator( |
+ caller->source_position_table()); |
+ DCHECK_EQ(2, caller->deoptimization_data()->length()); |
+ int caller_func_index = |
+ Smi::cast(caller->deoptimization_data()->get(1))->value(); |
+ const byte* func_bytes = |
+ module_bytes->GetChars() + compiled_module->module() |
+ ->functions[caller_func_index] |
+ .code_start_offset; |
+ for (RelocIterator it(*caller, RelocInfo::kCodeTargetMask); !it.done(); |
+ it.next()) { |
+ Code* callee = |
+ Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); |
+ if (callee->builtin_index() != Builtins::kWasmCompileLazy) continue; |
+ size_t offset_l = it.rinfo()->pc() - caller->instruction_start(); |
+ DCHECK_GE(kMaxInt, offset_l); |
+ int offset = static_cast<int>(offset_l); |
+ int byte_pos = |
+ AdvanceSourcePositionTableIterator(source_pos_iterator, offset); |
+ int called_func_index = |
+ ExtractDirectCallIndex(decoder, func_bytes + byte_pos); |
+ non_compiled_functions.push_back({offset, called_func_index}); |
+ // Call offset one instruction after the call. Remember the last called |
+ // function before that offset. |
+ if (offset < call_offset) func_to_return_idx = called_func_index; |
+ } |
+ } |
+ |
+ // TODO(clemensh): compile all functions in non_compiled_functions in |
+ // background, wait for func_to_return_idx. |
+ if (!CompileFunction(isolate, instance, func_to_return_idx)) { |
+ return {}; |
+ } |
+ |
+ if (is_js_to_wasm || patch_caller) { |
+ DisallowHeapAllocation no_gc; |
+ // Now patch the code object with all functions which are now compiled. |
+ int idx = 0; |
+ for (RelocIterator it(*caller, RelocInfo::kCodeTargetMask); !it.done(); |
+ it.next()) { |
+ Code* callee = |
+ Code::GetCodeFromTargetAddress(it.rinfo()->target_address()); |
+ if (callee->builtin_index() != Builtins::kWasmCompileLazy) continue; |
+ DCHECK_GT(non_compiled_functions.size(), idx); |
+ int called_func_index = non_compiled_functions[idx].func_index; |
+ if (callee->deoptimization_data()->length() > 0) { |
+ DCHECK_EQ(called_func_index, |
+ Smi::cast(callee->deoptimization_data()->get(1))->value()); |
+ } |
+ if (is_js_to_wasm) { |
+ DCHECK_EQ(func_to_return_idx, called_func_index); |
+ } else { |
+ DCHECK_EQ(non_compiled_functions[idx].offset, |
+ it.rinfo()->pc() - caller->instruction_start()); |
+ } |
+ ++idx; |
+ Handle<Code> callee_compiled( |
+ Code::cast(compiled_module->code_table()->get(called_func_index))); |
+ if (callee_compiled->builtin_index() == Builtins::kWasmCompileLazy) { |
+ DCHECK_NE(func_to_return_idx, called_func_index); |
+ continue; |
+ } |
+ DCHECK_EQ(Code::WASM_FUNCTION, callee_compiled->kind()); |
+ it.rinfo()->set_target_address(callee_compiled->instruction_start()); |
+ } |
+ DCHECK_EQ(non_compiled_functions.size(), idx); |
+ } |
+ |
+ Code* ret = |
+ Code::cast(compiled_module->code_table()->get(func_to_return_idx)); |
+ DCHECK_EQ(Code::WASM_FUNCTION, ret->kind()); |
+ return handle(ret, isolate); |
+ } |
+}; |
+ |
} // namespace |
Handle<WasmModuleObject> WasmModuleObject::New( |
@@ -547,6 +762,8 @@ DEFINE_OPTIONAL_ARR_ACCESSORS(WasmSharedModuleData, asm_js_offset_table, |
kAsmJsOffsetTable, ByteArray); |
DEFINE_OPTIONAL_ARR_GETTER(WasmSharedModuleData, breakpoint_infos, |
kBreakPointInfos, FixedArray); |
+DEFINE_OPTIONAL_ARR_GETTER(WasmSharedModuleData, lazy_compilation_orchestrator, |
+ kLazyCompilationOrchestrator, Foreign); |
Handle<WasmSharedModuleData> WasmSharedModuleData::New( |
Isolate* isolate, Handle<Foreign> module_wrapper, |
@@ -736,6 +953,16 @@ void WasmSharedModuleData::SetBreakpointsOnNewInstance( |
} |
} |
+void WasmSharedModuleData::PrepareForLazyCompilation( |
+ Handle<WasmSharedModuleData> shared) { |
+ if (shared->has_lazy_compilation_orchestrator()) return; |
+ Isolate* isolate = shared->GetIsolate(); |
+ LazyCompilationOrchestrator* orch = new LazyCompilationOrchestrator(); |
+ Handle<Managed<LazyCompilationOrchestrator>> orch_handle = |
+ Managed<LazyCompilationOrchestrator>::New(isolate, orch); |
+ shared->set(WasmSharedModuleData::kLazyCompilationOrchestrator, *orch_handle); |
+} |
+ |
Handle<WasmCompiledModule> WasmCompiledModule::New( |
Isolate* isolate, Handle<WasmSharedModuleData> shared) { |
Handle<FixedArray> ret = |
@@ -1162,6 +1389,18 @@ MaybeHandle<FixedArray> WasmCompiledModule::CheckBreakPoints(int position) { |
return isolate->debug()->GetHitBreakPointObjects(breakpoint_objects); |
} |
+MaybeHandle<Code> WasmCompiledModule::CompileLazy( |
+ Isolate* isolate, Handle<WasmInstanceObject> instance, Handle<Code> caller, |
+ int offset, int func_index, bool patch_caller) { |
+ isolate->set_context(*instance->compiled_module()->native_context()); |
+ Object* orch_obj = |
+ instance->compiled_module()->shared()->lazy_compilation_orchestrator(); |
+ LazyCompilationOrchestrator* orch = |
+ Managed<LazyCompilationOrchestrator>::cast(orch_obj)->get(); |
+ return orch->CompileLazy(isolate, instance, caller, offset, func_index, |
+ patch_caller); |
+} |
+ |
Handle<WasmInstanceWrapper> WasmInstanceWrapper::New( |
Isolate* isolate, Handle<WasmInstanceObject> instance) { |
Handle<FixedArray> array = |