diff --git a/src/wasm/wasm-debug.cc b/src/wasm/wasm-debug.cc index 59ad5aaeab..5ff89e1c79 100644 --- a/src/wasm/wasm-debug.cc +++ b/src/wasm/wasm-debug.cc @@ -94,10 +94,8 @@ class InterpreterHandle { } // Set pointer to globals storage. - if (instance->has_globals_buffer()) { - instance_.globals_start = - reinterpret_cast(instance->globals_buffer()->backing_store()); - } + instance_.globals_start = + debug_info->wasm_instance()->compiled_module()->GetGlobalsStartOrNull(); } ~InterpreterHandle() { diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc index cfffcec07e..5935a6e107 100644 --- a/src/wasm/wasm-module.cc +++ b/src/wasm/wasm-module.cc @@ -649,77 +649,6 @@ class CompilationHelper { } }; -static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner, - WasmCompiledModule* compiled_module) { - TRACE("Resetting %d\n", compiled_module->instance_id()); - Object* undefined = *isolate->factory()->undefined_value(); - Object* fct_obj = compiled_module->ptr_to_code_table(); - if (fct_obj != nullptr && fct_obj != undefined) { - uint32_t old_mem_size = compiled_module->mem_size(); - uint32_t default_mem_size = compiled_module->default_mem_size(); - Object* mem_start = compiled_module->maybe_ptr_to_memory(); - - // Patch code to update memory references, global references, and function - // table references. - Zone specialization_zone(isolate->allocator(), ZONE_NAME); - CodeSpecialization code_specialization(isolate, &specialization_zone); - - if (old_mem_size > 0) { - CHECK_NE(mem_start, undefined); - Address old_mem_address = - static_cast
(JSArrayBuffer::cast(mem_start)->backing_store()); - code_specialization.RelocateMemoryReferences( - old_mem_address, old_mem_size, nullptr, default_mem_size); - } - - if (owner->has_globals_buffer()) { - Address globals_start = - static_cast
(owner->globals_buffer()->backing_store()); - code_specialization.RelocateGlobals(globals_start, nullptr); - } - - // Reset function tables. - if (compiled_module->has_function_tables()) { - FixedArray* function_tables = compiled_module->ptr_to_function_tables(); - FixedArray* empty_function_tables = - compiled_module->ptr_to_empty_function_tables(); - DCHECK_EQ(function_tables->length(), empty_function_tables->length()); - for (int i = 0, e = function_tables->length(); i < e; ++i) { - code_specialization.RelocateObject( - handle(function_tables->get(i), isolate), - handle(empty_function_tables->get(i), isolate)); - } - compiled_module->set_ptr_to_function_tables(empty_function_tables); - } - - FixedArray* functions = FixedArray::cast(fct_obj); - for (int i = compiled_module->num_imported_functions(), - end = functions->length(); - i < end; ++i) { - Code* code = Code::cast(functions->get(i)); - // Skip lazy compile stubs. - if (code->builtin_index() == Builtins::kWasmCompileLazy) continue; - if (code->kind() != Code::WASM_FUNCTION) { - // From here on, there should only be wrappers for exported functions. - for (; i < end; ++i) { - DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, - Code::cast(functions->get(i))->kind()); - } - break; - } - bool changed = - code_specialization.ApplyToWasmCode(code, SKIP_ICACHE_FLUSH); - // TODO(wasm): Check if this is faster than passing FLUSH_ICACHE_IF_NEEDED - // above. - if (changed) { - Assembler::FlushICache(isolate, code->instruction_start(), - code->instruction_size()); - } - } - } - compiled_module->reset_memory(); -} - static void MemoryInstanceFinalizer(Isolate* isolate, WasmInstanceObject* instance) { DisallowHeapAllocation no_gc; @@ -805,7 +734,7 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo& data) { if (current_template == compiled_module) { if (next == nullptr) { - ResetCompiledModule(isolate, owner, compiled_module); + WasmCompiledModule::Reset(isolate, compiled_module); } else { DCHECK(next->value()->IsFixedArray()); wasm_module->SetEmbedderField(0, next->value()); @@ -1268,15 +1197,17 @@ class InstantiationHelper { thrower_->RangeError("Out of memory: wasm globals"); return {}; } - Address old_globals_start = nullptr; - if (!owner.is_null()) { - DCHECK(owner.ToHandleChecked()->has_globals_buffer()); - old_globals_start = static_cast
( - owner.ToHandleChecked()->globals_buffer()->backing_store()); - } + Address old_globals_start = compiled_module_->GetGlobalsStartOrNull(); Address new_globals_start = static_cast
(global_buffer->backing_store()); code_specialization.RelocateGlobals(old_globals_start, new_globals_start); + // The address of the backing buffer for the golbals is in native memory + // and, thus, not moving. We need it saved for + // serialization/deserialization purposes - so that the other end + // understands how to relocate the references. We still need to save the + // JSArrayBuffer on the instance, to keep it all alive. + WasmCompiledModule::SetGlobalsStartAddressFrom(factory, compiled_module_, + global_buffer); instance->set_globals_buffer(*global_buffer); } @@ -1312,8 +1243,6 @@ class InstantiationHelper { //-------------------------------------------------------------------------- // Set up the memory for the new instance. //-------------------------------------------------------------------------- - MaybeHandle old_memory; - uint32_t min_mem_pages = module_->min_mem_pages; (module_->is_wasm() ? isolate_->counters()->wasm_wasm_min_mem_pages_count() : isolate_->counters()->wasm_asm_min_mem_pages_count()) @@ -1362,25 +1291,24 @@ class InstantiationHelper { // Initialize memory. //-------------------------------------------------------------------------- if (!memory_.is_null()) { - instance->set_memory_buffer(*memory_); Address mem_start = static_cast
(memory_->backing_store()); uint32_t mem_size = static_cast(memory_->byte_length()->Number()); LoadDataSegments(mem_start, mem_size); uint32_t old_mem_size = compiled_module_->mem_size(); - Address old_mem_start = - compiled_module_->has_memory() - ? static_cast
( - compiled_module_->memory()->backing_store()) - : nullptr; + Address old_mem_start = compiled_module_->GetEmbeddedMemStartOrNull(); // We might get instantiated again with the same memory. No patching // needed in this case. if (old_mem_start != mem_start || old_mem_size != mem_size) { code_specialization.RelocateMemoryReferences( old_mem_start, old_mem_size, mem_start, mem_size); } - compiled_module_->set_memory(memory_); + // Just like with globals, we need to keep both the JSArrayBuffer + // and save the start pointer. + instance->set_memory_buffer(*memory_); + WasmCompiledModule::SetSpecializationMemInfoFrom( + factory, compiled_module_, memory_); } //-------------------------------------------------------------------------- @@ -2318,11 +2246,12 @@ MaybeHandle wasm::GetInstanceMemory( return MaybeHandle(); } -void SetInstanceMemory(Handle instance, - JSArrayBuffer* buffer) { - DisallowHeapAllocation no_gc; - instance->set_memory_buffer(buffer); - instance->compiled_module()->set_ptr_to_memory(buffer); +// May GC, because SetSpecializationMemInfoFrom may GC +void SetInstanceMemory(Isolate* isolate, Handle instance, + Handle buffer) { + instance->set_memory_buffer(*buffer); + WasmCompiledModule::SetSpecializationMemInfoFrom( + isolate->factory(), handle(instance->compiled_module()), buffer); } int32_t wasm::GetInstanceMemorySize(Isolate* isolate, @@ -2495,14 +2424,14 @@ int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate, new_buffer = GrowMemoryBuffer(isolate, memory_buffer, pages, max_pages); if (new_buffer.is_null()) return -1; DCHECK(!instance_wrapper->has_previous()); - SetInstanceMemory(instance, *new_buffer); + SetInstanceMemory(isolate, instance, new_buffer); UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size); while (instance_wrapper->has_next()) { instance_wrapper = instance_wrapper->next_wrapper(); DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper)); Handle instance = instance_wrapper->instance_object(); DCHECK(IsWasmInstance(*instance)); - SetInstanceMemory(instance, *new_buffer); + SetInstanceMemory(isolate, instance, new_buffer); UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size); } } @@ -2532,7 +2461,7 @@ int32_t wasm::GrowMemory(Isolate* isolate, Handle instance, Handle buffer = GrowMemoryBuffer(isolate, instance_buffer, pages, max_pages); if (buffer.is_null()) return -1; - SetInstanceMemory(instance, *buffer); + SetInstanceMemory(isolate, instance, buffer); UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size); DCHECK(old_size % WasmModule::kPageSize == 0); return (old_size / WasmModule::kPageSize); @@ -3110,7 +3039,7 @@ bool LazyCompilationOrchestrator::CompileFunction( CodeSpecialization code_specialization(isolate, &specialization_zone); if (module_env.module->globals_size) { Address globals_start = - reinterpret_cast
(instance->globals_buffer()->backing_store()); + reinterpret_cast
(compiled_module->globals_start()); code_specialization.RelocateGlobals(nullptr, globals_start); } if (instance->has_memory_buffer()) { diff --git a/src/wasm/wasm-objects.cc b/src/wasm/wasm-objects.cc index 8e67d10290..33502704de 100644 --- a/src/wasm/wasm-objects.cc +++ b/src/wasm/wasm-objects.cc @@ -772,6 +772,102 @@ Handle WasmCompiledModule::New( return compiled_module; } +Handle WasmCompiledModule::Clone( + Isolate* isolate, Handle module) { + Handle ret = Handle::cast( + isolate->factory()->CopyFixedArray(module)); + ret->InitId(); + ret->reset_weak_owning_instance(); + ret->reset_weak_next_instance(); + ret->reset_weak_prev_instance(); + ret->reset_weak_exported_functions(); + if (ret->has_embedded_mem_start()) { + WasmCompiledModule::recreate_embedded_mem_start(ret, isolate->factory(), + ret->embedded_mem_start()); + } + if (ret->has_globals_start()) { + WasmCompiledModule::recreate_globals_start(ret, isolate->factory(), + ret->globals_start()); + } + if (ret->has_embedded_mem_size()) { + WasmCompiledModule::recreate_embedded_mem_size(ret, isolate->factory(), + ret->embedded_mem_size()); + } + return ret; +} + +void WasmCompiledModule::Reset(Isolate* isolate, + WasmCompiledModule* compiled_module) { + DisallowHeapAllocation no_gc; + TRACE("Resetting %d\n", compiled_module->instance_id()); + Object* undefined = *isolate->factory()->undefined_value(); + Object* fct_obj = compiled_module->ptr_to_code_table(); + if (fct_obj != nullptr && fct_obj != undefined) { + uint32_t old_mem_size = compiled_module->mem_size(); + uint32_t default_mem_size = compiled_module->default_mem_size(); + Address old_mem_start = compiled_module->GetEmbeddedMemStartOrNull(); + + // Patch code to update memory references, global references, and function + // table references. + Zone specialization_zone(isolate->allocator(), ZONE_NAME); + CodeSpecialization code_specialization(isolate, &specialization_zone); + + if (old_mem_size > 0 && old_mem_start != nullptr) { + code_specialization.RelocateMemoryReferences(old_mem_start, old_mem_size, + nullptr, default_mem_size); + } + + if (compiled_module->has_globals_start()) { + Address globals_start = + reinterpret_cast
(compiled_module->globals_start()); + code_specialization.RelocateGlobals(globals_start, nullptr); + compiled_module->set_globals_start(0); + } + + // Reset function tables. + if (compiled_module->has_function_tables()) { + FixedArray* function_tables = compiled_module->ptr_to_function_tables(); + FixedArray* empty_function_tables = + compiled_module->ptr_to_empty_function_tables(); + if (function_tables != empty_function_tables) { + DCHECK_EQ(function_tables->length(), empty_function_tables->length()); + for (int i = 0, e = function_tables->length(); i < e; ++i) { + code_specialization.RelocateObject( + handle(function_tables->get(i), isolate), + handle(empty_function_tables->get(i), isolate)); + } + compiled_module->set_ptr_to_function_tables(empty_function_tables); + } + } + + FixedArray* functions = FixedArray::cast(fct_obj); + for (int i = compiled_module->num_imported_functions(), + end = functions->length(); + i < end; ++i) { + Code* code = Code::cast(functions->get(i)); + // Skip lazy compile stubs. + if (code->builtin_index() == Builtins::kWasmCompileLazy) continue; + if (code->kind() != Code::WASM_FUNCTION) { + // From here on, there should only be wrappers for exported functions. + for (; i < end; ++i) { + DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, + Code::cast(functions->get(i))->kind()); + } + break; + } + bool changed = + code_specialization.ApplyToWasmCode(code, SKIP_ICACHE_FLUSH); + // TODO(wasm): Check if this is faster than passing FLUSH_ICACHE_IF_NEEDED + // above. + if (changed) { + Assembler::FlushICache(isolate, code->instruction_start(), + code->instruction_size()); + } + } + } + compiled_module->ResetSpecializationMemInfoIfNeeded(); +} + void WasmCompiledModule::InitId() { #if DEBUG static uint32_t instance_id_counter = 0; @@ -780,6 +876,45 @@ void WasmCompiledModule::InitId() { #endif } +void WasmCompiledModule::ResetSpecializationMemInfoIfNeeded() { + DisallowHeapAllocation no_gc; + if (has_embedded_mem_start()) { + set_embedded_mem_size(0); + set_embedded_mem_start(0); + } +} + +void WasmCompiledModule::SetSpecializationMemInfoFrom( + Factory* factory, Handle compiled_module, + Handle buffer) { + DCHECK(!buffer.is_null()); + size_t start_address = reinterpret_cast(buffer->backing_store()); + uint32_t size = static_cast(buffer->byte_length()->Number()); + if (!compiled_module->has_embedded_mem_start()) { + DCHECK(!compiled_module->has_embedded_mem_size()); + WasmCompiledModule::recreate_embedded_mem_start(compiled_module, factory, + start_address); + WasmCompiledModule::recreate_embedded_mem_size(compiled_module, factory, + size); + } else { + compiled_module->set_embedded_mem_start(start_address); + compiled_module->set_embedded_mem_size(size); + } +} + +void WasmCompiledModule::SetGlobalsStartAddressFrom( + Factory* factory, Handle compiled_module, + Handle buffer) { + DCHECK(!buffer.is_null()); + size_t start_address = reinterpret_cast(buffer->backing_store()); + if (!compiled_module->has_globals_start()) { + WasmCompiledModule::recreate_globals_start(compiled_module, factory, + start_address); + } else { + compiled_module->set_globals_start(start_address); + } +} + MaybeHandle WasmCompiledModule::ExtractUtf8StringFromModuleBytes( Isolate* isolate, Handle compiled_module, uint32_t offset, uint32_t size) { @@ -807,13 +942,23 @@ bool WasmCompiledModule::IsWasmCompiledModule(Object* obj) { Object* obj = arr->get(kID_##NAME); \ if (!(TYPE_CHECK)) return false; \ } while (false); +// We're OK with undefined, generally, because maybe we don't +// have a value for that item. For example, we may not have a +// memory, or globals. +// We're not OK with the fixed numbers being undefined. We want +// to set once all of them. #define WCM_CHECK_OBJECT(TYPE, NAME) \ WCM_CHECK_TYPE(NAME, obj->IsUndefined(isolate) || obj->Is##TYPE()) #define WCM_CHECK_WASM_OBJECT(TYPE, NAME) \ WCM_CHECK_TYPE(NAME, TYPE::Is##TYPE(obj)) #define WCM_CHECK_WEAK_LINK(TYPE, NAME) WCM_CHECK_OBJECT(WeakCell, NAME) -#define WCM_CHECK_SMALL_NUMBER(TYPE, NAME) WCM_CHECK_TYPE(NAME, obj->IsSmi()) +#define WCM_CHECK_SMALL_NUMBER(TYPE, NAME) \ + WCM_CHECK_TYPE(NAME, obj->IsUndefined(isolate) || obj->IsSmi()) #define WCM_CHECK(KIND, TYPE, NAME) WCM_CHECK_##KIND(TYPE, NAME) +#define WCM_CHECK_SMALL_FIXED_NUMBER(TYPE, NAME) \ + WCM_CHECK_TYPE(NAME, obj->IsSmi()) +#define WCM_CHECK_LARGE_NUMBER(TYPE, NAME) \ + WCM_CHECK_TYPE(NAME, obj->IsUndefined(isolate) || obj->IsMutableHeapNumber()) WCM_PROPERTY_TABLE(WCM_CHECK) #undef WCM_CHECK @@ -845,11 +990,13 @@ void WasmCompiledModule::ReinitializeAfterDeserialization( isolate); DCHECK(!WasmSharedModuleData::IsWasmSharedModuleData(*shared)); WasmSharedModuleData::ReinitializeAfterDeserialization(isolate, shared); + WasmCompiledModule::Reset(isolate, *compiled_module); DCHECK(WasmSharedModuleData::IsWasmSharedModuleData(*shared)); } uint32_t WasmCompiledModule::mem_size() const { - return has_memory() ? memory()->byte_length()->Number() : default_mem_size(); + DCHECK(has_embedded_mem_size() == has_embedded_mem_start()); + return has_embedded_mem_start() ? embedded_mem_size() : default_mem_size(); } uint32_t WasmCompiledModule::default_mem_size() const { diff --git a/src/wasm/wasm-objects.h b/src/wasm/wasm-objects.h index b35f754d4a..09eef82a26 100644 --- a/src/wasm/wasm-objects.h +++ b/src/wasm/wasm-objects.h @@ -221,6 +221,30 @@ class WasmSharedModuleData : public FixedArray { friend class WasmCompiledModule; }; +// This represents the set of wasm compiled functions, together +// with all the information necessary for re-specializing them. +// +// We specialize wasm functions to their instance by embedding: +// - raw interior pointers into the backing store of the array buffer +// used as memory of a particular WebAssembly.Instance object. +// - bounds check limits, computed at compile time, relative to the +// size of the memory. +// - the objects representing the function tables and signature tables +// - raw pointer to the globals buffer. +// +// Even without instantiating, we need values for all of these parameters. +// We need to track these values to be able to create new instances and +// to be able to serialize/deserialize. +// The design decisions for how we track these values is not too immediate, +// and it deserves a summary. The "tricky" ones are: memory, globals, and +// the tables (signature and functions). +// The first 2 (memory & globals) are embedded as raw pointers to native +// buffers. All we need to track them is the start addresses and, in the +// case of memory, the size. We model all of them as HeapNumbers, because +// we need to store size_t values (for addresses), and potentially full +// 32 bit unsigned values for the size. Smis are 31 bits. +// For tables, we need to hold a reference to the JS Heap object, because +// we embed them as objects, and they may move. class WasmCompiledModule : public FixedArray { public: enum Fields { kFieldCount }; @@ -267,12 +291,19 @@ class WasmCompiledModule : public FixedArray { #define WCM_WASM_OBJECT(TYPE, NAME) \ WCM_OBJECT_OR_WEAK(TYPE, NAME, kID_##NAME, TYPE::Is##TYPE(obj)) -#define WCM_SMALL_NUMBER(TYPE, NAME) \ +#define WCM_SMALL_FIXED_NUMBER(TYPE, NAME) \ TYPE NAME() const { \ return static_cast(Smi::cast(get(kID_##NAME))->value()); \ } \ void set_##NAME(TYPE value) { set(kID_##NAME, Smi::FromInt(value)); } +#define WCM_SMALL_NUMBER(TYPE, NAME) \ + TYPE NAME() const { \ + return static_cast(Smi::cast(get(kID_##NAME))->value()); \ + } \ + void set_##NAME(TYPE value) { set(kID_##NAME, Smi::FromInt(value)); } \ + bool has_##NAME() const { return get(kID_##NAME)->IsSmi(); } + #define WCM_WEAK_LINK(TYPE, NAME) \ WCM_OBJECT_OR_WEAK(WeakCell, weak_##NAME, kID_##NAME, obj->IsWeakCell()); \ \ @@ -280,21 +311,44 @@ class WasmCompiledModule : public FixedArray { return handle(TYPE::cast(weak_##NAME()->value())); \ } -#define CORE_WCM_PROPERTY_TABLE(MACRO) \ - MACRO(WASM_OBJECT, WasmSharedModuleData, shared) \ - MACRO(OBJECT, Context, native_context) \ - MACRO(SMALL_NUMBER, uint32_t, num_imported_functions) \ - MACRO(OBJECT, FixedArray, code_table) \ - MACRO(OBJECT, FixedArray, weak_exported_functions) \ - MACRO(OBJECT, FixedArray, function_tables) \ - MACRO(OBJECT, FixedArray, signature_tables) \ - MACRO(OBJECT, FixedArray, empty_function_tables) \ - MACRO(OBJECT, JSArrayBuffer, memory) \ - MACRO(SMALL_NUMBER, uint32_t, min_mem_pages) \ - MACRO(SMALL_NUMBER, uint32_t, max_mem_pages) \ - MACRO(WEAK_LINK, WasmCompiledModule, next_instance) \ - MACRO(WEAK_LINK, WasmCompiledModule, prev_instance) \ - MACRO(WEAK_LINK, JSObject, owning_instance) \ +#define WCM_LARGE_NUMBER(TYPE, NAME) \ + TYPE NAME() const { \ + Object* value = get(kID_##NAME); \ + DCHECK(value->IsMutableHeapNumber()); \ + return static_cast(HeapNumber::cast(value)->value()); \ + } \ + \ + void set_##NAME(TYPE value) { \ + Object* number = get(kID_##NAME); \ + DCHECK(number->IsMutableHeapNumber()); \ + HeapNumber::cast(number)->set_value(static_cast(value)); \ + } \ + \ + static void recreate_##NAME(Handle obj, \ + Factory* factory, TYPE init_val) { \ + Handle number = factory->NewHeapNumber( \ + static_cast(init_val), MutableMode::MUTABLE, TENURED); \ + obj->set(kID_##NAME, *number); \ + } \ + bool has_##NAME() const { return get(kID_##NAME)->IsMutableHeapNumber(); } + +#define CORE_WCM_PROPERTY_TABLE(MACRO) \ + MACRO(WASM_OBJECT, WasmSharedModuleData, shared) \ + MACRO(OBJECT, Context, native_context) \ + MACRO(SMALL_FIXED_NUMBER, uint32_t, num_imported_functions) \ + MACRO(OBJECT, FixedArray, code_table) \ + MACRO(OBJECT, FixedArray, weak_exported_functions) \ + MACRO(OBJECT, FixedArray, function_tables) \ + MACRO(OBJECT, FixedArray, signature_tables) \ + MACRO(OBJECT, FixedArray, empty_function_tables) \ + MACRO(LARGE_NUMBER, size_t, embedded_mem_start) \ + MACRO(LARGE_NUMBER, size_t, globals_start) \ + MACRO(LARGE_NUMBER, uint32_t, embedded_mem_size) \ + MACRO(SMALL_FIXED_NUMBER, uint32_t, min_mem_pages) \ + MACRO(SMALL_FIXED_NUMBER, uint32_t, max_mem_pages) \ + MACRO(WEAK_LINK, WasmCompiledModule, next_instance) \ + MACRO(WEAK_LINK, WasmCompiledModule, prev_instance) \ + MACRO(WEAK_LINK, JSObject, owning_instance) \ MACRO(WEAK_LINK, WasmModuleObject, wasm_module) #if DEBUG @@ -320,20 +374,36 @@ class WasmCompiledModule : public FixedArray { Handle shared); static Handle Clone(Isolate* isolate, - Handle module) { - Handle ret = Handle::cast( - isolate->factory()->CopyFixedArray(module)); - ret->InitId(); - ret->reset_weak_owning_instance(); - ret->reset_weak_next_instance(); - ret->reset_weak_prev_instance(); - ret->reset_weak_exported_functions(); - return ret; + Handle module); + static void Reset(Isolate* isolate, WasmCompiledModule* module); + + Address GetEmbeddedMemStartOrNull() const { + DisallowHeapAllocation no_gc; + if (has_embedded_mem_start()) { + return reinterpret_cast
(embedded_mem_start()); + } + return nullptr; + } + + Address GetGlobalsStartOrNull() const { + DisallowHeapAllocation no_gc; + if (has_globals_start()) { + return reinterpret_cast
(globals_start()); + } + return nullptr; } uint32_t mem_size() const; uint32_t default_mem_size() const; + void ResetSpecializationMemInfoIfNeeded(); + static void SetSpecializationMemInfoFrom( + Factory* factory, Handle compiled_module, + Handle buffer); + static void SetGlobalsStartAddressFrom( + Factory* factory, Handle compiled_module, + Handle buffer); + #define DECLARATION(KIND, TYPE, NAME) WCM_##KIND(TYPE, NAME) WCM_PROPERTY_TABLE(DECLARATION) #undef DECLARATION diff --git a/test/mjsunit/wasm/compiled-module-serialization.js b/test/mjsunit/wasm/compiled-module-serialization.js index 54858a78e3..80840e3535 100644 --- a/test/mjsunit/wasm/compiled-module-serialization.js +++ b/test/mjsunit/wasm/compiled-module-serialization.js @@ -37,16 +37,9 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); var wire_bytes = builder.toBuffer(); var module = new WebAssembly.Module(wire_bytes); - var buff = %SerializeWasmModule(module); - module = null; - gc(); - module = %DeserializeWasmModule(buff, wire_bytes); - var mem_1 = new WebAssembly.Memory({initial: 1}); var view_1 = new Int32Array(mem_1.buffer); - view_1[0] = 42; - var outval_1; var i1 = new WebAssembly.Instance(module, {"": {some_value: () => 1, @@ -57,6 +50,28 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); assertEquals(43, i1.exports.main(0)); assertEquals(42, outval_1); + var buff = %SerializeWasmModule(module); + module = null; + gc(); + module = %DeserializeWasmModule(buff, wire_bytes); + + var mem_2 = new WebAssembly.Memory({initial: 2}); + var view_2 = new Int32Array(mem_2.buffer); + + view_2[0] = 50; + + var outval_2; + var i2 = new WebAssembly.Instance(module, {"": + {some_value: () => 1, + writer: (x) => outval_2 = x , + memory: mem_2} + }); + + assertEquals(51, i2.exports.main(0)); + + assertEquals(50, outval_2); + // The instances don't share memory through deserialization. + assertEquals(43, i1.exports.main(0)); })(); (function DeserializeInvalidObject() { @@ -103,3 +118,128 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); var instance3 = new WebAssembly.Instance(clone); assertFalse(instance3 == undefined); })(); + + +(function SerializeAfterInstantiationWithMemory() { + let builder = new WasmModuleBuilder(); + builder.addImportedMemory("", "memory", 1); + builder.addFunction("main", kSig_i_v) + .addBody([kExprI32Const, 42]) + .exportFunc(); + + var wire_bytes = builder.toBuffer() + var compiled_module = new WebAssembly.Module(wire_bytes); + var mem_1 = new WebAssembly.Memory({initial: 1}); + var ffi = {"":{memory:mem_1}}; + var instance1 = new WebAssembly.Instance(compiled_module, ffi); + var serialized = %SerializeWasmModule(compiled_module); + var clone = %DeserializeWasmModule(serialized, wire_bytes); + + assertNotNull(clone); + assertFalse(clone == undefined); + assertFalse(clone == compiled_module); + assertEquals(clone.constructor, compiled_module.constructor); + var instance2 = new WebAssembly.Instance(clone, ffi); + assertFalse(instance2 == undefined); +})(); + +(function GlobalsArePrivateBetweenClones() { + var builder = new WasmModuleBuilder(); + builder.addGlobal(kWasmI32, true); + builder.addFunction("read", kSig_i_v) + .addBody([ + kExprGetGlobal, 0]) + .exportFunc(); + + builder.addFunction("write", kSig_v_i) + .addBody([ + kExprGetLocal, 0, + kExprSetGlobal, 0]) + .exportFunc(); + + var wire_bytes = builder.toBuffer(); + var module = new WebAssembly.Module(wire_bytes); + var i1 = new WebAssembly.Instance(module); + // serialize and replace module + var buff = %SerializeWasmModule(module); + var module_clone = %DeserializeWasmModule(buff, wire_bytes); + var i2 = new WebAssembly.Instance(module_clone); + i1.exports.write(1); + i2.exports.write(2); + assertEquals(1, i1.exports.read()); + assertEquals(2, i2.exports.read()); +})(); + +(function SharedTableTest() { + let kTableSize = 3; + var sig_index1; + + function MakeTableExportingModule(constant) { + // A module that defines a table and exports it. + var builder = new WasmModuleBuilder(); + builder.addType(kSig_i_i); + builder.addType(kSig_i_ii); + sig_index1 = builder.addType(kSig_i_v); + var f1 = builder.addFunction("f1", sig_index1) + .addBody([kExprI32Const, constant]); + + builder.addFunction("main", kSig_i_ii) + .addBody([ + kExprGetLocal, 0, // -- + kExprCallIndirect, sig_index1, kTableZero]) // -- + .exportAs("main"); + + builder.setFunctionTableLength(kTableSize); + builder.addFunctionTableInit(0, false, [f1.index]); + builder.addExportOfKind("table", kExternalTable, 0); + + return new WebAssembly.Module(builder.toBuffer()); + } + var m1 = MakeTableExportingModule(11); + + // Module {m2} imports the table and adds {f2}. + var builder = new WasmModuleBuilder(); + builder.addType(kSig_i_ii); + var sig_index2 = builder.addType(kSig_i_v); + var f2 = builder.addFunction("f2", sig_index2) + .addBody([kExprI32Const, 22]); + + builder.addFunction("main", kSig_i_ii) + .addBody([ + kExprGetLocal, 0, // -- + kExprCallIndirect, sig_index2, kTableZero]) // -- + .exportAs("main"); + + builder.addImportedTable("z", "table", kTableSize, kTableSize); + builder.addFunctionTableInit(1, false, [f2.index], true); + var m2_bytes = builder.toBuffer(); + var m2 = new WebAssembly.Module(m2_bytes); + + assertFalse(sig_index1 == sig_index2); + + var i1 = new WebAssembly.Instance(m1); + var i2 = new WebAssembly.Instance(m2, {z: {table: i1.exports.table}}); + + var serialized_m2 = %SerializeWasmModule(m2); + var m2_clone = %DeserializeWasmModule(serialized_m2, m2_bytes); + + var m3 = MakeTableExportingModule(33); + var i3 = new WebAssembly.Instance(m3); + var i2_prime = new WebAssembly.Instance(m2_clone, + {z: {table: i3.exports.table}}); + + assertEquals(11, i1.exports.main(0)); + assertEquals(11, i2.exports.main(0)); + + assertEquals(22, i1.exports.main(1)); + assertEquals(22, i2.exports.main(1)); + + assertEquals(33, i3.exports.main(0)); + assertEquals(33, i2_prime.exports.main(0)); + + assertThrows(() => i1.exports.main(2)); + assertThrows(() => i2.exports.main(2)); + assertThrows(() => i1.exports.main(3)); + assertThrows(() => i2.exports.main(3)); + +})();