Commit b4dc310a authored by mtrofin's avatar mtrofin Committed by Commit bot

[wasm] reuse the first compiled module.

This change avoids needing to keep around an unused compiled
module. Instead, the result of compiling the wasm bytes is
given to the first instance. The module object and that instance object
point to the same compiled module. Instances are, then, cloned from
the compiled module the module object points to. When an instance is
collected, we make sure that the module object still has a clone
available, and, if the last instance is GC-ed, we also reset the compiled
module so that it does not reference its heap, so that it (==heap) may
be collected.

This is achieved by linking the clones in a double-linked list and
registering a finalizer for each. When we create an instance, we tie it
in the front of the list, making the module object point to it (O(1)). When
the finalizer is called, we relink the list over the dying object (O(1)). The
costliest operation is finalizing the last instance, since we need to visit
all wasm functions and reset heap references.

BUG=v8:5316

Committed: https://crrev.com/01f5af515728aebe6c5246f4f7dd6c573e8748af
Review-Url: https://codereview.chromium.org/2305903002
Cr-Original-Commit-Position: refs/heads/master@{#39153}
Cr-Commit-Position: refs/heads/master@{#39361}
parent 8e5ac62d
......@@ -773,5 +773,31 @@ RUNTIME_FUNCTION(Runtime_DeserializeWasmModule) {
wasm::ModuleOrigin::kWasmOrigin);
}
RUNTIME_FUNCTION(Runtime_ValidateWasmInstancesChain) {
HandleScope shs(isolate);
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSObject, module_obj, 0);
CONVERT_ARG_HANDLE_CHECKED(Smi, instance_count, 1);
wasm::testing::ValidateInstancesChain(isolate, module_obj,
instance_count->value());
return isolate->heap()->ToBoolean(true);
}
RUNTIME_FUNCTION(Runtime_ValidateWasmModuleState) {
HandleScope shs(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSObject, module_obj, 0);
wasm::testing::ValidateModuleState(isolate, module_obj);
return isolate->heap()->ToBoolean(true);
}
RUNTIME_FUNCTION(Runtime_ValidateWasmOrphanedInstance) {
HandleScope shs(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSObject, instance_obj, 0);
wasm::testing::ValidateOrphanedInstance(isolate, instance_obj);
return isolate->heap()->ToBoolean(true);
}
} // namespace internal
} // namespace v8
......@@ -18,16 +18,12 @@
namespace v8 {
namespace internal {
namespace {
const int kWasmMemArrayBuffer = 2;
}
RUNTIME_FUNCTION(Runtime_WasmGrowMemory) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
uint32_t delta_pages = 0;
CHECK(args[0]->ToUint32(&delta_pages));
Handle<JSObject> module_object;
Handle<JSObject> module_instance;
{
// Get the module JSObject
......@@ -41,17 +37,17 @@ RUNTIME_FUNCTION(Runtime_WasmGrowMemory) {
Object* owning_instance = wasm::GetOwningWasmInstance(undefined, code);
CHECK_NOT_NULL(owning_instance);
CHECK_NE(owning_instance, undefined);
module_object = handle(JSObject::cast(owning_instance), isolate);
module_instance = handle(JSObject::cast(owning_instance), isolate);
}
Address old_mem_start, new_mem_start;
uint32_t old_size, new_size;
// Get mem buffer associated with module object
Handle<Object> obj(module_object->GetInternalField(kWasmMemArrayBuffer),
isolate);
if (obj->IsUndefined(isolate)) {
MaybeHandle<JSArrayBuffer> maybe_mem_buffer =
wasm::GetInstanceMemory(isolate, module_instance);
Handle<JSArrayBuffer> old_buffer;
if (!maybe_mem_buffer.ToHandle(&old_buffer)) {
// If module object does not have linear memory associated with it,
// Allocate new array buffer of given size.
old_mem_start = nullptr;
......@@ -73,7 +69,6 @@ RUNTIME_FUNCTION(Runtime_WasmGrowMemory) {
}
#endif
} else {
Handle<JSArrayBuffer> old_buffer = Handle<JSArrayBuffer>::cast(obj);
old_mem_start = static_cast<Address>(old_buffer->backing_store());
old_size = old_buffer->byte_length()->Number();
// If the old memory was zero-sized, we should have been in the
......@@ -107,8 +102,10 @@ RUNTIME_FUNCTION(Runtime_WasmGrowMemory) {
buffer->set_is_neuterable(false);
// Set new buffer to be wasm memory
module_object->SetInternalField(kWasmMemArrayBuffer, *buffer);
CHECK(wasm::UpdateWasmModuleMemory(module_object, old_mem_start,
wasm::SetInstanceMemory(module_instance, *buffer);
CHECK(wasm::UpdateWasmModuleMemory(module_instance, old_mem_start,
new_mem_start, old_size, new_size));
return *isolate->factory()->NewNumberFromInt(old_size /
......
......@@ -890,7 +890,10 @@ namespace internal {
F(SerializeWasmModule, 1, 1) \
F(DeserializeWasmModule, 1, 1) \
F(IsAsmWasmCode, 1, 1) \
F(IsNotAsmWasmCode, 1, 1)
F(IsNotAsmWasmCode, 1, 1) \
F(ValidateWasmInstancesChain, 2, 1) \
F(ValidateWasmModuleState, 1, 1) \
F(ValidateWasmOrphanedInstance, 1, 1)
#define FOR_EACH_INTRINSIC_TYPEDARRAY(F) \
F(ArrayBufferGetByteLength, 1, 1) \
......
......@@ -98,6 +98,10 @@ void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
UNREACHABLE();
}
if (ElideObject(obj)) {
return SerializeObject(*isolate()->factory()->undefined_value(),
how_to_code, where_to_point, skip);
}
// Past this point we should not see any (context-specific) maps anymore.
CHECK(!obj->IsMap());
// There should be no references to the global object embedded.
......
......@@ -36,6 +36,7 @@ class CodeSerializer : public Serializer {
UNREACHABLE();
}
virtual bool ElideObject(Object* obj) { return false; }
void SerializeGeneric(HeapObject* heap_object, HowToCode how_to_code,
WhereToPoint where_to_point);
......@@ -73,6 +74,8 @@ class WasmCompiledModuleSerializer : public CodeSerializer {
}
}
bool ElideObject(Object* obj) override { return obj->IsWeakCell(); };
private:
WasmCompiledModuleSerializer(Isolate* isolate, uint32_t source_hash)
: CodeSerializer(isolate, source_hash) {}
......
This diff is collapsed.
......@@ -414,6 +414,19 @@ MaybeHandle<JSObject> CreateModuleObjectFromBytes(Isolate* isolate,
// instance
// was collected, or the instance object owning the Code object
Object* GetOwningWasmInstance(Object* undefined, Code* code);
MaybeHandle<JSArrayBuffer> GetInstanceMemory(Isolate* isolate,
Handle<JSObject> instance);
void SetInstanceMemory(Handle<JSObject> instance, JSArrayBuffer* buffer);
namespace testing {
void ValidateInstancesChain(Isolate* isolate, Handle<JSObject> module_obj,
int instance_count);
void ValidateModuleState(Isolate* isolate, Handle<JSObject> module_obj);
void ValidateOrphanedInstance(Isolate* isolate, Handle<JSObject> instance);
} // namespace testing
} // namespace wasm
} // namespace internal
} // namespace v8
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO (mtrofin): re-enable ignition (v8:5345)
// Flags: --no-ignition --no-ignition-staging
// Flags: --expose-wasm --expose-gc --allow-natives-syntax
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
(function CompiledModuleInstancesAreGCed() {
var builder = new WasmModuleBuilder();
builder.addMemory(1,1, true);
builder.addImport("getValue", kSig_i);
builder.addFunction("f", kSig_i)
.addBody([
kExprCallImport, kArity0, 0
]).exportFunc();
var module = new WebAssembly.Module(builder.toBuffer());
%ValidateWasmModuleState(module);
%ValidateWasmInstancesChain(module, 0);
var i1 = new WebAssembly.Instance(module, {getValue: () => 1});
%ValidateWasmInstancesChain(module, 1);
var i2 = new WebAssembly.Instance(module, {getValue: () => 2});
%ValidateWasmInstancesChain(module, 2);
var i3 = new WebAssembly.Instance(module, {getValue: () => 3});
%ValidateWasmInstancesChain(module, 3);
assertEquals(1, i1.exports.f());
i1 = null;
gc();
%ValidateWasmInstancesChain(module, 2);
assertEquals(3, i3.exports.f());
i3 = null;
gc();
%ValidateWasmInstancesChain(module, 1);
assertEquals(2, i2.exports.f());
i2 = null;
gc();
%ValidateWasmModuleState(module);
var i4 = new WebAssembly.Instance(module, {getValue: () => 4});
assertEquals(4, i4.exports.f());
module = null;
gc();
%ValidateWasmOrphanedInstance(i4);
})();
......@@ -155,24 +155,50 @@ promise.then(module => CheckInstance(new WebAssembly.Instance(module)));
})();
(function GlobalsArePrivateToTheInstance() {
var builder = new WasmModuleBuilder();
builder.addGlobal(kAstI32);
builder.addFunction("read", kSig_i_v)
.addBody([
kExprGetGlobal, 0])
.exportFunc();
builder.addFunction("write", kSig_v_i)
.addBody([
kExprGetLocal, 0,
kExprSetGlobal, 0])
.exportFunc();
var module = new WebAssembly.Module(builder.toBuffer());
var i1 = new WebAssembly.Instance(module);
var i2 = new WebAssembly.Instance(module);
i1.exports.write(1);
i2.exports.write(2);
assertEquals(1, i1.exports.read());
assertEquals(2, i2.exports.read());
})();
(function InstanceMemoryIsIsolated() {
var builder = new WasmModuleBuilder();
builder.addGlobal(kAstI32);
builder.addFunction("read", kSig_i_v)
.addBody([
kExprGetGlobal, 0])
.exportFunc();
builder.addMemory(1,1, true);
builder.addFunction("write", kSig_v_i)
builder.addFunction("f", kSig_i)
.addBody([
kExprGetLocal, 0,
kExprSetGlobal, 0])
.exportFunc();
kExprI32Const, 0,
kExprI32LoadMem, 0, 0
]).exportFunc();
var mem_1 = new ArrayBuffer(65536);
var mem_2 = new ArrayBuffer(65536);
var view_1 = new Int32Array(mem_1);
var view_2 = new Int32Array(mem_2);
view_1[0] = 1;
view_2[0] = 1000;
var module = new WebAssembly.Module(builder.toBuffer());
var i1 = new WebAssembly.Instance(module);
var i2 = new WebAssembly.Instance(module);
i1.exports.write(1);
i2.exports.write(2);
assertEquals(1, i1.exports.read());
assertEquals(2, i2.exports.read());
var i1 = new WebAssembly.Instance(module, null, mem_1);
var i2 = new WebAssembly.Instance(module, null, mem_2);
assertEquals(1, i1.exports.f());
assertEquals(1000, i2.exports.f());
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment