[wasm] Create WasmCompiledModule together with WasmModuleObject

We need to create one {WasmCompiledModule} before creating the
{WasmModuleObject}. This CL refactors the code such that the
{WasmModuleObject} itself creates the {WasmCompiledModule}. This moves
us closer to removing the {WasmCompiledModule}.

R=titzer@chromium.org

Change-Id: I9f85e47f643c39840036f4f1f92df736732c8f74
Reviewed-on: https://chromium-review.googlesource.com/1105762
Reviewed-by: Ben Titzer <titzer@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53833}
This commit is contained in:
Clemens Hammacher 2018-06-19 10:41:44 +02:00 committed by Commit Bot
parent 0256c6d0cd
commit e69b8f8969
6 changed files with 103 additions and 107 deletions

View File

@ -535,14 +535,6 @@ ModuleEnv CreateDefaultModuleEnv(const WasmModule* module,
return ModuleEnv(module, use_trap_handler, kRuntimeExceptionSupport);
}
Handle<WasmCompiledModule> NewCompiledModule(Isolate* isolate,
WasmModule* module,
ModuleEnv& env) {
Handle<WasmCompiledModule> compiled_module =
WasmCompiledModule::New(isolate, module, env);
return compiled_module;
}
size_t GetMaxUsableMemorySize(Isolate* isolate) {
return isolate->heap()->memory_allocator()->code_range()->valid()
? isolate->heap()->memory_allocator()->code_range()->size()
@ -1032,13 +1024,10 @@ MaybeHandle<WasmModuleObject> CompileToModuleObject(
// and information needed at instantiation time. This object needs to be
// serializable. Instantiation may occur off a deserialized version of this
// object.
Handle<WasmCompiledModule> compiled_module =
NewCompiledModule(isolate, wasm_module, env);
Handle<WasmModuleObject> module_object = WasmModuleObject::New(
isolate, compiled_module, export_wrappers, std::move(module),
Handle<SeqOneByteString>::cast(module_bytes), script,
asm_js_offset_table);
compiled_module->GetNativeModule()->SetModuleObject(module_object);
Handle<WasmModuleObject> module_object =
WasmModuleObject::New(isolate, export_wrappers, std::move(module), env,
Handle<SeqOneByteString>::cast(module_bytes),
script, asm_js_offset_table);
CompileNativeModule(isolate, thrower, module_object, wasm_module, &env);
if (thrower->error()) return {};
@ -1056,7 +1045,9 @@ MaybeHandle<WasmModuleObject> CompileToModuleObject(
}
// Log the code within the generated module for profiling.
compiled_module->GetNativeModule()->LogWasmCodes(isolate);
NativeModule* native_module =
module_object->compiled_module()->GetNativeModule();
native_module->LogWasmCodes(isolate);
return module_object;
}
@ -2333,9 +2324,7 @@ void AsyncCompileJob::Start() {
void AsyncCompileJob::Abort() {
background_task_manager_.CancelAndWait();
if (!compiled_module_.is_null()) {
compiled_module_->GetNativeModule()->compilation_state()->Abort();
}
if (native_module_) native_module_->compilation_state()->Abort();
if (num_pending_foreground_tasks_ == 0) {
// No task is pending, we can just remove the AsyncCompileJob.
isolate_->wasm_engine()->RemoveCompileJob(this);
@ -2397,49 +2386,14 @@ AsyncCompileJob::~AsyncCompileJob() {
}
void AsyncCompileJob::FinishCompile() {
RecordStats(compiled_module_->GetNativeModule(), counters());
// Create heap objects for script and module bytes to be stored in the
// module object. Asm.js is not compiled asynchronously.
Handle<Script> script = CreateWasmScript(isolate_, wire_bytes_);
Handle<ByteArray> asm_js_offset_table;
// TODO(wasm): Improve efficiency of storing module wire bytes.
// 1. Only store relevant sections, not function bodies
// 2. Don't make a second copy of the bytes here; reuse the copy made
// for asynchronous compilation and store it as an external one
// byte string for serialization/deserialization.
Handle<String> module_bytes =
isolate_->factory()
->NewStringFromOneByte({wire_bytes_.start(), wire_bytes_.length()},
TENURED)
.ToHandleChecked();
DCHECK(module_bytes->IsSeqOneByteString());
int export_wrapper_size = static_cast<int>(module_->num_exported_functions);
Handle<FixedArray> export_wrappers =
isolate_->factory()->NewFixedArray(export_wrapper_size, TENURED);
// Create the module object.
// TODO(clemensh): For the same module (same bytes / same hash), we should
// only have one {WasmModuleObject}. Otherwise, we might only set
// breakpoints on a (potentially empty) subset of the instances.
// Create the module object.
module_object_ = WasmModuleObject::New(
isolate_, compiled_module_, export_wrappers, module_,
Handle<SeqOneByteString>::cast(module_bytes), script,
asm_js_offset_table);
compiled_module_->GetNativeModule()->SetModuleObject(module_object_);
{
DeferredHandleScope deferred(isolate_);
module_object_ = handle(*module_object_, isolate_);
deferred_handles_.push_back(deferred.Detach());
}
RecordStats(native_module_, counters());
// Finish the wasm script now and make it public to the debugger.
Handle<Script> script(module_object_->script(), isolate_);
isolate_->debug()->OnAfterCompile(script);
// Log the code within the generated module for profiling.
compiled_module_->GetNativeModule()->LogWasmCodes(isolate_);
native_module_->LogWasmCodes(isolate_);
// TODO(wasm): compiling wrappers should be made async as well.
DoSync<CompileWrappers>();
@ -2612,18 +2566,45 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
// is done.
job_->background_task_manager_.CancelAndWait();
// Create heap objects for script and module bytes to be stored in the
// module object. Asm.js is not compiled asynchronously.
Handle<Script> script = CreateWasmScript(job_->isolate_, job_->wire_bytes_);
Handle<ByteArray> asm_js_offset_table;
WasmModule* module = job_->module_.get();
DCHECK_LE(module->num_imported_functions, module->functions.size());
// 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.
ModuleEnv env = CreateDefaultModuleEnv(module);
job_->compiled_module_ = NewCompiledModule(job_->isolate_, module, env);
int export_wrapper_size = static_cast<int>(module->num_exported_functions);
Handle<FixedArray> export_wrappers =
job_->isolate_->factory()->NewFixedArray(export_wrapper_size, TENURED);
// TODO(wasm): Improve efficiency of storing module wire bytes.
// 1. Only store relevant sections, not function bodies
// 2. Don't make a second copy of the bytes here; reuse the copy made
// for asynchronous compilation and store it as an external one
// byte string for serialization/deserialization.
Handle<String> module_bytes =
job_->isolate_->factory()
->NewStringFromOneByte(
{job_->wire_bytes_.start(), job_->wire_bytes_.length()},
TENURED)
.ToHandleChecked();
DCHECK(module_bytes->IsSeqOneByteString());
// Create the module object and populate with compiled functions and
// information needed at instantiation time.
// TODO(clemensh): For the same module (same bytes / same hash), we should
// only have one {WasmModuleObject}. Otherwise, we might only set
// breakpoints on a (potentially empty) subset of the instances.
// Create the module object.
job_->module_object_ =
WasmModuleObject::New(job_->isolate_, export_wrappers, job_->module_,
env, Handle<SeqOneByteString>::cast(module_bytes),
script, asm_js_offset_table);
job_->native_module_ =
job_->module_object_->compiled_module()->GetNativeModule();
{
DeferredHandleScope deferred(job_->isolate_);
job_->compiled_module_ = handle(*job_->compiled_module_, job_->isolate_);
job_->module_object_ = handle(*job_->module_object_, job_->isolate_);
job_->deferred_handles_.push_back(deferred.Detach());
}
size_t num_functions =
@ -2639,7 +2620,7 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
}
CompilationState* compilation_state =
job_->compiled_module_->GetNativeModule()->compilation_state();
job_->native_module_->compilation_state();
{
// Instance field {job_} cannot be captured by copy, therefore
// we need to add a local helper variable {job}. We want to
@ -2675,8 +2656,7 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
case CompilationEvent::kFailedCompilation: {
// Tier-up compilation should not fail if baseline compilation
// did not fail.
DCHECK(!job->compiled_module_->GetNativeModule()
->compilation_state()
DCHECK(!job->native_module_->compilation_state()
->baseline_compilation_finished());
SaveContext saved_context(job->isolate());
@ -2702,12 +2682,11 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
// InitializeCompilationUnits always returns 0 for streaming compilation,
// then DoAsync would do the same as NextStep already.
size_t functions_count = GetNumFunctionsToCompile(env.module);
size_t functions_count = GetNumFunctionsToCompile(module);
compilation_state->SetNumberOfFunctionsToCompile(functions_count);
// Add compilation units and kick off compilation.
InitializeCompilationUnits(module->functions, job_->wire_bytes_,
env.module,
job_->compiled_module_->GetNativeModule());
InitializeCompilationUnits(module->functions, job_->wire_bytes_, module,
job_->native_module_);
}
}
};
@ -2756,9 +2735,8 @@ class AsyncCompileJob::FinishModule : public CompileStep {
size_t num_functions =
job_->module_->functions.size() - job_->module_->num_imported_functions;
if (job_->compiled_module_->GetNativeModule()
->compilation_state()
->compile_mode() == CompileMode::kRegular ||
if (job_->native_module_->compilation_state()->compile_mode() ==
CompileMode::kRegular ||
num_functions == 0) {
// If we do not tier up, the async compile job is done here and
// can be deleted.
@ -2768,9 +2746,8 @@ class AsyncCompileJob::FinishModule : public CompileStep {
// If background tiering compilation finished before we resolved the
// promise, switch to patching now. Otherwise, patching will be scheduled
// by a callback.
DCHECK_EQ(CompileMode::kTiering, job_->compiled_module_->GetNativeModule()
->compilation_state()
->compile_mode());
DCHECK_EQ(CompileMode::kTiering,
job_->native_module_->compilation_state()->compile_mode());
if (job_->tiering_completed_) {
job_->isolate_->wasm_engine()->RemoveCompileJob(job_);
}
@ -2799,8 +2776,8 @@ void AsyncStreamingProcessor::FinishAsyncCompileJobWithError(ResultBase error) {
// Check if there is already a CompiledModule, in which case we have to clean
// up the CompilationState as well.
if (!job_->compiled_module_.is_null()) {
job_->compiled_module_->GetNativeModule()->compilation_state()->Abort();
if (job_->native_module_) {
job_->native_module_->compilation_state()->Abort();
if (job_->num_pending_foreground_tasks_ == 0) {
job_->DoSync<AsyncCompileJob::DecodeFail>(std::move(result));
@ -2882,14 +2859,14 @@ bool AsyncStreamingProcessor::ProcessCodeSectionHeader(size_t functions_count,
constexpr bool on_foreground = true;
job_->step_->Run(on_foreground);
NativeModule* native_module = job_->compiled_module_->GetNativeModule();
native_module->compilation_state()->SetNumberOfFunctionsToCompile(
job_->native_module_->compilation_state()->SetNumberOfFunctionsToCompile(
functions_count);
// Set outstanding_finishers_ to 2, because both the AsyncCompileJob and the
// AsyncStreamingProcessor have to finish.
job_->outstanding_finishers_.SetValue(2);
compilation_unit_builder_.reset(new CompilationUnitBuilder(native_module));
compilation_unit_builder_.reset(
new CompilationUnitBuilder(job_->native_module_));
return true;
}
@ -2925,14 +2902,25 @@ void AsyncStreamingProcessor::OnFinishedChunk() {
void AsyncStreamingProcessor::OnFinishedStream(std::unique_ptr<uint8_t[]> bytes,
size_t length) {
TRACE_STREAMING("Finish stream...\n");
// TODO(clemensh): Move wire bytes to the NativeModule, remove code below.
job_->bytes_copy_ = std::move(bytes);
job_->wire_bytes_ = ModuleWireBytes(job_->bytes_copy_.get(),
job_->bytes_copy_.get() + length);
Handle<String> module_bytes =
job_->isolate_->factory()
->NewStringFromOneByte(
{job_->wire_bytes_.start(), job_->wire_bytes_.length()}, TENURED)
.ToHandleChecked();
DCHECK(module_bytes->IsSeqOneByteString());
if (!job_->module_object_.is_null()) {
job_->module_object_->set_module_bytes(
SeqOneByteString::cast(*module_bytes));
}
ModuleResult result = decoder_.FinishDecoding(false);
DCHECK(result.ok());
DCHECK_EQ(job_->module_, result.val);
if (job_->DecrementAndCheckFinisherCount()) {
if (job_->compiled_module_.is_null()) {
if (job_->native_module_ == nullptr) {
// We are processing a WebAssembly module without code section. We need to
// prepare compilation first before we can finish it.
// {PrepareAndStartCompile} will call {FinishCompile} by itself if there

View File

@ -146,8 +146,8 @@ class AsyncCompileJob {
std::shared_ptr<WasmModule> module_;
std::vector<DeferredHandles*> deferred_handles_;
Handle<WasmCompiledModule> compiled_module_;
Handle<WasmModuleObject> module_object_;
NativeModule* native_module_ = nullptr;
std::unique_ptr<CompileStep> step_;
CancelableTaskManager background_task_manager_;

View File

@ -272,9 +272,8 @@ enum DispatchTableElements : int {
} // namespace
Handle<WasmModuleObject> WasmModuleObject::New(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
Handle<FixedArray> export_wrappers,
std::shared_ptr<wasm::WasmModule> module,
Isolate* isolate, Handle<FixedArray> export_wrappers,
std::shared_ptr<wasm::WasmModule> module, wasm::ModuleEnv& env,
Handle<SeqOneByteString> module_bytes, Handle<Script> script,
Handle<ByteArray> asm_js_offset_table) {
// The {managed_module} will take shared ownership of the {WasmModule} object,
@ -284,6 +283,12 @@ Handle<WasmModuleObject> WasmModuleObject::New(
Managed<WasmModule>::FromSharedPtr(isolate, module_size,
std::move(module));
// Create the first {WasmCompiledModule} associated with this
// {WasmModuleObject}.
Handle<WasmCompiledModule> compiled_module =
WasmCompiledModule::New(isolate, managed_module->raw(), env);
// Now create the {WasmModuleObject}.
Handle<JSFunction> module_cons(
isolate->native_context()->wasm_module_constructor());
auto module_object = Handle<WasmModuleObject>::cast(
@ -303,6 +308,10 @@ Handle<WasmModuleObject> WasmModuleObject::New(
if (!asm_js_offset_table.is_null()) {
module_object->set_asm_js_offset_table(*asm_js_offset_table);
}
// TODO(clemensh): Move the reference to the native module to the module
// object.
compiled_module->GetNativeModule()->SetModuleObject(module_object);
return module_object;
}

View File

@ -135,12 +135,13 @@ class WasmModuleObject : public JSObject {
WASM_MODULE_OBJECT_FIELDS)
#undef WASM_MODULE_OBJECT_FIELDS
static Handle<WasmModuleObject> New(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
Handle<FixedArray> export_wrappers,
std::shared_ptr<wasm::WasmModule> module,
Handle<SeqOneByteString> module_bytes, Handle<Script> script,
Handle<ByteArray> asm_js_offset_table);
static Handle<WasmModuleObject> New(Isolate* isolate,
Handle<FixedArray> export_wrappers,
std::shared_ptr<wasm::WasmModule> module,
wasm::ModuleEnv& env,
Handle<SeqOneByteString> module_bytes,
Handle<Script> script,
Handle<ByteArray> asm_js_offset_table);
// Set a breakpoint on the given byte position inside the given module.
// This will affect all live and future instances of the module.

View File

@ -602,9 +602,14 @@ MaybeHandle<WasmModuleObject> DeserializeNativeModule(
trap_handler::IsTrapHandlerEnabled() ? kUseTrapHandler : kNoTrapHandler;
wasm::ModuleEnv env(module, use_trap_handler,
wasm::RuntimeExceptionSupport::kRuntimeExceptionSupport);
Handle<WasmCompiledModule> compiled_module =
WasmCompiledModule::New(isolate, module, env);
Handle<WasmModuleObject> module_object = WasmModuleObject::New(
isolate, export_wrappers, std::move(decode_result.val), env,
Handle<SeqOneByteString>::cast(module_bytes), script,
Handle<ByteArray>::null());
Handle<WasmCompiledModule> compiled_module(module_object->compiled_module(),
isolate);
NativeModule* native_module = compiled_module->GetNativeModule();
if (FLAG_wasm_lazy_compilation) {
native_module->SetLazyBuiltin(BUILTIN_CODE(isolate, WasmCompileLazy));
}
@ -613,12 +618,6 @@ MaybeHandle<WasmModuleObject> DeserializeNativeModule(
Reader reader(data + kVersionSize);
if (!deserializer.Read(&reader)) return {};
Handle<WasmModuleObject> module_object = WasmModuleObject::New(
isolate, compiled_module, export_wrappers, std::move(decode_result.val),
Handle<SeqOneByteString>::cast(module_bytes), script,
Handle<ByteArray>::null());
native_module->SetModuleObject(module_object);
// TODO(6792): Wrappers below might be cloned using {Factory::CopyCode}. This
// requires unlocking the code space here. This should eventually be moved
// into the allocator.

View File

@ -217,12 +217,11 @@ Handle<WasmInstanceObject> TestingModuleBuilder::InitInstanceObject() {
script->set_type(Script::TYPE_WASM);
Handle<FixedArray> export_wrappers = isolate_->factory()->NewFixedArray(0);
ModuleEnv env = CreateModuleEnv();
Handle<WasmCompiledModule> compiled_module =
WasmCompiledModule::New(isolate_, test_module_ptr_, env);
Handle<WasmModuleObject> module_object = WasmModuleObject::New(
isolate_, compiled_module, export_wrappers, test_module_, empty_string,
script, Handle<ByteArray>::null());
compiled_module->GetNativeModule()->SetModuleObject(module_object);
Handle<WasmModuleObject> module_object =
WasmModuleObject::New(isolate_, export_wrappers, test_module_, env,
empty_string, script, Handle<ByteArray>::null());
Handle<WasmCompiledModule> compiled_module(module_object->compiled_module(),
isolate_);
// This method is called when we initialize TestEnvironment. We don't
// have a memory yet, so we won't create it here. We'll update the
// interpreter when we get a memory. We do have globals, though.