[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

Review-Url: https://codereview.chromium.org/2305903002
Cr-Commit-Position: refs/heads/master@{#39153}
This commit is contained in:
mtrofin 2016-09-05 03:08:02 -07:00 committed by Commit bot
parent 7e95e2064a
commit 01f5af5157
9 changed files with 565 additions and 145 deletions

View File

@ -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

View File

@ -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
@ -101,9 +96,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 /

View File

@ -887,7 +887,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) \

View File

@ -98,6 +98,10 @@ void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
UNREACHABLE();
}
if (SkipOver(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.

View File

@ -36,6 +36,7 @@ class CodeSerializer : public Serializer {
UNREACHABLE();
}
virtual bool SkipOver(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 SkipOver(Object* obj) override { return obj->IsWeakCell(); };
private:
WasmCompiledModuleSerializer(Isolate* isolate, uint32_t source_hash)
: CodeSerializer(isolate, source_hash) {}

View File

@ -158,15 +158,19 @@ Object* GetOwningWasmInstance(Object* undefined, Code* code) {
namespace {
// Internal constants for the layout of the module object.
const int kWasmModuleFunctionTable = 0;
const int kWasmModuleCodeTable = 1;
const int kWasmMemArrayBuffer = 2;
const int kWasmGlobalsArrayBuffer = 3;
// TODO(clemensh): Remove function name array, extract names from module bytes.
const int kWasmFunctionNamesArray = 4;
const int kWasmModuleBytesString = 5;
const int kWasmDebugInfo = 6;
const int kWasmModuleInternalFieldCount = 7;
enum WasmInstanceFields {
kWasmCompiledModule = 0,
kWasmModuleFunctionTable,
kWasmModuleCodeTable,
kWasmMemArrayBuffer,
kWasmGlobalsArrayBuffer,
// TODO(clemensh): Remove function name array, extract names from module
// bytes.
kWasmFunctionNamesArray,
kWasmModuleBytesString,
kWasmDebugInfo,
kWasmModuleInternalFieldCount
};
// TODO(mtrofin): Unnecessary once we stop using JS Heap for wasm code.
// For now, each field is expected to have the type commented by its side.
@ -194,8 +198,14 @@ enum CompiledWasmObjectFields {
kDataSegments, // maybe ByteArray.
kGlobalsSize, // Smi. an uint32_t
kMemSize, // Smi.an uint32_t
kMemStart, // MaybeHandle<ArrayBuffer>
kExportMem, // Smi. bool
kOrigin, // Smi. ModuleOrigin
kNextInstance, // WeakCell. See compiled code cloning.
kPrevInstance, // WeakCell. See compiled code cloning.
kOwningInstance, // WeakCell, pointing to the owning instance.
kModuleObject, // WeakCell, pointing to the module object.
kCompiledWasmObjectTableSize // Sentinel value.
};
@ -329,8 +339,9 @@ Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size) {
return buffer;
}
void RelocateInstanceCode(Handle<JSObject> instance, Address start,
uint32_t prev_size, uint32_t new_size) {
void RelocateInstanceCode(Handle<JSObject> instance, Address old_start,
Address start, uint32_t prev_size,
uint32_t new_size) {
Handle<FixedArray> functions = Handle<FixedArray>(
FixedArray::cast(instance->GetInternalField(kWasmModuleCodeTable)));
for (int i = 0; i < functions->length(); ++i) {
@ -339,7 +350,7 @@ void RelocateInstanceCode(Handle<JSObject> instance, Address start,
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, start, prev_size,
it.rinfo()->update_wasm_memory_reference(old_start, start, prev_size,
new_size);
}
}
@ -361,7 +372,8 @@ Handle<JSArrayBuffer> AllocateMemory(ErrorThrower* thrower, Isolate* isolate,
return mem_buffer;
}
void RelocateGlobals(Handle<JSObject> instance, Address globals_start) {
void RelocateGlobals(Handle<JSObject> instance, Address old_start,
Address globals_start) {
Handle<FixedArray> functions = Handle<FixedArray>(
FixedArray::cast(instance->GetInternalField(kWasmModuleCodeTable)));
uint32_t function_count = static_cast<uint32_t>(functions->length());
@ -370,7 +382,7 @@ void RelocateGlobals(Handle<JSObject> instance, Address globals_start) {
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, globals_start);
it.rinfo()->update_wasm_global_reference(old_start, globals_start);
}
}
}
@ -401,40 +413,35 @@ void InitializePlaceholders(Factory* factory,
}
}
bool LinkFunction(Handle<Code> unlinked,
const std::vector<Handle<Code>>& code_targets,
Code::Kind kind) {
bool LinkFunction(Isolate* isolate, Handle<Code> unlinked,
Handle<FixedArray> code_targets,
Code::Kind kind = Code::WASM_FUNCTION) {
bool modified = false;
int mode_mask = RelocInfo::kCodeTargetMask;
int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
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(),
UPDATE_WRITE_BARRIER,
SKIP_ICACHE_FLUSH);
modified = true;
}
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 < static_cast<uint32_t>(code_targets->length()));
Handle<Code> new_target =
code_targets->GetValueChecked<Code>(isolate, index);
if (target != *new_target) {
it.rinfo()->set_target_address(new_target->instruction_start(),
UPDATE_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];
LinkFunction(code, functions, Code::WASM_FUNCTION);
void LinkModuleFunctions(Isolate* isolate, Handle<FixedArray> functions) {
for (int i = 0; i < functions->length(); ++i) {
Handle<Code> code = functions->GetValueChecked<Code>(isolate, i);
LinkFunction(isolate, code, functions);
}
}
@ -453,8 +460,6 @@ void SetRuntimeSupport(Isolate* isolate, Handle<JSObject> js_object) {
for (int i = FLAG_skip_compiling_wasm_funcs; i < functions->length(); ++i) {
Handle<Code> code = functions->GetValueChecked<Code>(isolate, i);
DCHECK(code->deoptimization_data() == nullptr ||
code->deoptimization_data()->length() == 0);
Handle<FixedArray> deopt_data =
isolate->factory()->NewFixedArray(2, TENURED);
deopt_data->set(0, *weak_link);
@ -621,6 +626,17 @@ static void RecordStats(Isolate* isolate, Handle<FixedArray> functions) {
}
}
Address GetGlobalStartAddressFromCodeTemplate(Object* undefined,
JSObject* owner) {
Address old_address = nullptr;
Object* stored_value = owner->GetInternalField(kWasmGlobalsArrayBuffer);
if (stored_value != undefined) {
old_address = static_cast<Address>(
JSArrayBuffer::cast(stored_value)->backing_store());
}
return old_address;
}
Handle<FixedArray> GetImportsMetadata(Factory* factory,
const WasmModule* module) {
Handle<FixedArray> ret = factory->NewFixedArray(
@ -912,8 +928,9 @@ void SetDebugSupport(Factory* factory, Handle<FixedArray> compiled_module,
}
}
bool SetupGlobals(Isolate* isolate, Handle<FixedArray> compiled_module,
Handle<JSObject> instance, ErrorThrower* thrower) {
bool SetupGlobals(Isolate* isolate, MaybeHandle<JSObject> template_owner,
Handle<FixedArray> compiled_module, Handle<JSObject> instance,
ErrorThrower* thrower) {
uint32_t globals_size = static_cast<uint32_t>(
Smi::cast(compiled_module->get(kGlobalsSize))->value());
if (globals_size > 0) {
@ -923,7 +940,13 @@ bool SetupGlobals(Isolate* isolate, Handle<FixedArray> compiled_module,
thrower->Error("Out of memory: wasm globals");
return false;
}
RelocateGlobals(instance,
Address old_address =
template_owner.is_null()
? nullptr
: GetGlobalStartAddressFromCodeTemplate(
*isolate->factory()->undefined_value(),
JSObject::cast(*template_owner.ToHandleChecked()));
RelocateGlobals(instance, old_address,
static_cast<Address>(globals_buffer->backing_store()));
instance->SetInternalField(kWasmGlobalsArrayBuffer, *globals_buffer);
}
@ -949,13 +972,72 @@ bool SetupInstanceHeap(Isolate* isolate, Handle<FixedArray> compiled_module,
instance->SetInternalField(kWasmMemArrayBuffer, *memory);
Address mem_start = static_cast<Address>(memory->backing_store());
uint32_t mem_size = static_cast<uint32_t>(memory->byte_length()->Number());
RelocateInstanceCode(instance, mem_start,
WasmModule::kPageSize * min_mem_pages, mem_size);
uint32_t old_mem_size = static_cast<uint32_t>(
compiled_module->GetValueChecked<HeapNumber>(isolate, kMemSize)
->value());
MaybeHandle<JSArrayBuffer> old_mem =
compiled_module->GetValue<JSArrayBuffer>(isolate, kMemStart);
Address old_mem_start =
old_mem.is_null()
? nullptr
: static_cast<Address>(old_mem.ToHandleChecked()->backing_store());
RelocateInstanceCode(instance, old_mem_start, mem_start, old_mem_size,
mem_size);
LoadDataSegments(compiled_module, mem_start, mem_size);
compiled_module->GetValueChecked<HeapNumber>(isolate, kMemSize)
->set_value(static_cast<double>(mem_size));
compiled_module->set(kMemStart, *memory);
}
return true;
}
void FixupFunctionsAndImports(Handle<FixedArray> old_functions,
Handle<FixedArray> new_functions,
MaybeHandle<FixedArray> maybe_old_imports,
MaybeHandle<FixedArray> maybe_new_imports) {
DCHECK_EQ(new_functions->length(), old_functions->length());
DisallowHeapAllocation no_gc;
std::map<Code*, Code*> old_to_new_code;
for (int i = 0; i < new_functions->length(); ++i) {
old_to_new_code.insert(std::make_pair(Code::cast(old_functions->get(i)),
Code::cast(new_functions->get(i))));
}
DCHECK_EQ(maybe_old_imports.is_null(), maybe_new_imports.is_null());
if (!maybe_old_imports.is_null()) {
Handle<FixedArray> old_imports = maybe_old_imports.ToHandleChecked();
Handle<FixedArray> new_imports = maybe_new_imports.ToHandleChecked();
DCHECK_EQ(new_imports->length(), old_imports->length());
for (int i = 0; i < new_imports->length(); ++i) {
old_to_new_code.insert(std::make_pair(Code::cast(old_imports->get(i)),
Code::cast(new_imports->get(i))));
}
}
int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
AllowDeferredHandleDereference embedding_raw_address;
for (int i = 0; i < new_functions->length(); ++i) {
Code* wasm_function = Code::cast(new_functions->get(i));
for (RelocIterator it(wasm_function, mode_mask); !it.done(); it.next()) {
Code* old_code =
Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
if (old_code->kind() == Code::WASM_TO_JS_FUNCTION ||
old_code->kind() == Code::WASM_FUNCTION) {
auto found = old_to_new_code.find(old_code);
DCHECK(found != old_to_new_code.end());
Code* new_code = found->second;
// Avoid redundant updates, expected for wasm functions, if we're at the
// first instance.
if (new_code == old_code) {
DCHECK(new_code->kind() == Code::WASM_FUNCTION);
continue;
}
it.rinfo()->set_target_address(new_code->instruction_start(),
UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
}
}
}
}
bool SetupImports(Isolate* isolate, Handle<FixedArray> compiled_module,
Handle<JSObject> instance, ErrorThrower* thrower,
Handle<JSReceiver> ffi) {
@ -974,49 +1056,14 @@ bool SetupImports(Isolate* isolate, Handle<FixedArray> compiled_module,
}
RecordStats(isolate, import_code);
if (import_code.empty()) return true;
{
DisallowHeapAllocation no_gc;
std::map<Code*, int> import_to_index;
Handle<FixedArray> mapping =
compiled_module->GetValueChecked<FixedArray>(isolate, kImportMap);
for (int i = 0; i < mapping->length(); ++i) {
import_to_index.insert(std::make_pair(Code::cast(mapping->get(i)), i));
}
Handle<FixedArray> code_table = Handle<FixedArray>(
FixedArray::cast(instance->GetInternalField(kWasmModuleCodeTable)));
int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
AllowDeferredHandleDereference embedding_raw_address;
for (int i = 0; i < code_table->length(); ++i) {
Handle<Code> wasm_function =
code_table->GetValueChecked<Code>(isolate, i);
for (RelocIterator it(*wasm_function, mode_mask); !it.done(); it.next()) {
Code* target =
Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
if (target->kind() == Code::WASM_TO_JS_FUNCTION) {
auto found_index = import_to_index.find(target);
CHECK(found_index != import_to_index.end());
int index = found_index->second;
CHECK(index < static_cast<int>(import_code.size()));
Handle<Code> new_target = import_code[index];
it.rinfo()->set_target_address(new_target->instruction_start(),
UPDATE_WRITE_BARRIER,
SKIP_ICACHE_FLUSH);
}
}
}
}
Handle<FixedArray> new_mapping =
Handle<FixedArray> new_imports =
isolate->factory()->NewFixedArray(static_cast<int>(import_code.size()));
for (int i = 0; i < new_mapping->length(); ++i) {
new_mapping->set(i, *import_code[i]);
for (int i = 0; i < new_imports->length(); ++i) {
new_imports->set(i, *import_code[i]);
}
compiled_module->set(kImportMap, *new_mapping);
compiled_module->set(kImportMap, *new_imports);
return true;
}
@ -1081,6 +1128,122 @@ bool SetupExportsObject(Handle<FixedArray> compiled_module, Isolate* isolate,
return true;
}
#define GET_COMPILED_MODULE_WEAK_RELATION_OR_NULL(Field) \
WeakCell* Get##Field(const FixedArray* compiled_module) { \
Object* obj = compiled_module->get(k##Field); \
DCHECK_NOT_NULL(obj); \
if (obj->IsWeakCell()) { \
return WeakCell::cast(obj); \
} else { \
return nullptr; \
} \
}
GET_COMPILED_MODULE_WEAK_RELATION_OR_NULL(NextInstance)
GET_COMPILED_MODULE_WEAK_RELATION_OR_NULL(PrevInstance)
GET_COMPILED_MODULE_WEAK_RELATION_OR_NULL(OwningInstance)
GET_COMPILED_MODULE_WEAK_RELATION_OR_NULL(ModuleObject)
static void ResetCompiledModule(Isolate* isolate, JSObject* owner,
FixedArray* compiled_module) {
Object* undefined = *isolate->factory()->undefined_value();
Object* mem_size_obj = compiled_module->get(kMemSize);
DCHECK(mem_size_obj->IsMutableHeapNumber());
uint32_t old_mem_size =
static_cast<uint32_t>(HeapNumber::cast(mem_size_obj)->value());
Object* mem_start = compiled_module->get(kMemStart);
Address old_mem_address = nullptr;
Address globals_start =
GetGlobalStartAddressFromCodeTemplate(undefined, owner);
if (old_mem_size > 0) {
CHECK_NE(mem_start, undefined);
old_mem_address =
static_cast<Address>(JSArrayBuffer::cast(mem_start)->backing_store());
}
int mode_mask = RelocInfo::ModeMask(RelocInfo::WASM_MEMORY_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::WASM_MEMORY_SIZE_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::WASM_GLOBAL_REFERENCE);
Object* fct_obj = compiled_module->get(kFunctions);
if (fct_obj != nullptr && fct_obj != undefined &&
(old_mem_size > 0 || globals_start != nullptr)) {
FixedArray* functions = FixedArray::cast(fct_obj);
for (int i = 0; i < functions->length(); ++i) {
Code* code = Code::cast(functions->get(i));
bool changed = false;
for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
RelocInfo::Mode mode = it.rinfo()->rmode();
if (RelocInfo::IsWasmMemoryReference(mode) ||
RelocInfo::IsWasmMemorySizeReference(mode)) {
it.rinfo()->update_wasm_memory_reference(old_mem_address, nullptr,
old_mem_size, old_mem_size);
changed = true;
} else {
CHECK(RelocInfo::IsWasmGlobalReference(mode));
it.rinfo()->update_wasm_global_reference(globals_start, nullptr);
changed = true;
}
}
if (changed) {
Assembler::FlushICache(isolate, code->instruction_start(),
code->instruction_size());
}
}
}
compiled_module->set(kOwningInstance, undefined);
compiled_module->set(kMemStart, undefined);
}
static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
JSObject* owner = *p;
FixedArray* compiled_module =
FixedArray::cast(owner->GetInternalField(kWasmCompiledModule));
Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
Object* undefined = *isolate->factory()->undefined_value();
WeakCell* weak_module_obj = GetModuleObject(compiled_module);
DCHECK_NOT_NULL(weak_module_obj);
// weak_module_obj may have been cleared, meaning the module object
// was GC-ed. In that case, there won't be any new instances created,
// and we don't need to maintain the links between instances.
if (!weak_module_obj->cleared()) {
JSObject* module_obj = JSObject::cast(weak_module_obj->value());
FixedArray* current_template =
FixedArray::cast(module_obj->GetInternalField(0));
DCHECK_NULL(GetPrevInstance(current_template));
WeakCell* next = GetNextInstance(compiled_module);
WeakCell* prev = GetPrevInstance(compiled_module);
if (current_template == compiled_module) {
if (next == nullptr) {
ResetCompiledModule(isolate, owner, compiled_module);
} else {
DCHECK(next->value()->IsFixedArray());
module_obj->SetInternalField(0, next->value());
DCHECK_NULL(prev);
FixedArray::cast(next->value())->set(kPrevInstance, undefined);
}
} else {
DCHECK(!(prev == nullptr && next == nullptr));
// the only reason prev or next would be cleared is if the
// respective objects got collected, but if that happened,
// we would have relinked the list.
if (prev != nullptr) {
DCHECK(!prev->cleared());
FixedArray::cast(prev->value())
->set(kNextInstance, next == nullptr ? undefined : next);
}
if (next != nullptr) {
DCHECK(!next->cleared());
FixedArray::cast(next->value())
->set(kPrevInstance, prev == nullptr ? undefined : prev);
}
}
}
GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
}
} // namespace
MaybeHandle<FixedArray> WasmModule::CompileFunctions(
@ -1156,6 +1319,11 @@ MaybeHandle<FixedArray> WasmModule::CompileFunctions(
compiled_functions->set(static_cast<int>(i), code);
}
LinkModuleFunctions(isolate, compiled_functions);
// TODO(mtrofin): do we need to flush the cache here?
FlushAssemblyCache(isolate, compiled_functions);
// 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
@ -1251,6 +1419,10 @@ MaybeHandle<FixedArray> WasmModule::CompileFunctions(
ret->set(kGlobalsSize, Smi::FromInt(globals_size));
ret->set(kExportMem, Smi::FromInt(mem_export));
ret->set(kOrigin, Smi::FromInt(origin));
Handle<HeapNumber> size_as_object = factory->NewHeapNumber(
static_cast<double>(temp_instance_for_compilation.mem_size), MUTABLE,
TENURED);
ret->set(kMemSize, *size_as_object);
return ret;
}
@ -1275,7 +1447,8 @@ void PatchJSWrapper(Isolate* isolate, Handle<Code> wrapper,
Handle<FixedArray> SetupIndirectFunctionTable(
Isolate* isolate, Handle<FixedArray> wasm_functions,
Handle<FixedArray> indirect_table_template) {
Handle<FixedArray> indirect_table_template,
Handle<FixedArray> tables_to_replace) {
Factory* factory = isolate->factory();
Handle<FixedArray> cloned_indirect_tables =
factory->CopyFixedArray(indirect_table_template);
@ -1290,10 +1463,13 @@ Handle<FixedArray> SetupIndirectFunctionTable(
Handle<FixedArray> cloned_table = factory->CopyFixedArray(orig_table);
cloned_metadata->set(kTable, *cloned_table);
// Patch the cloned code to refer to the cloned kTable.
for (int i = 0; i < wasm_functions->length(); ++i) {
Handle<FixedArray> table_to_replace =
tables_to_replace->GetValueChecked<FixedArray>(isolate, i)
->GetValueChecked<FixedArray>(isolate, kTable);
for (int fct_index = 0; fct_index < wasm_functions->length(); ++fct_index) {
Handle<Code> wasm_function =
wasm_functions->GetValueChecked<Code>(isolate, i);
PatchFunctionTable(wasm_function, orig_table, cloned_table);
wasm_functions->GetValueChecked<Code>(isolate, fct_index);
PatchFunctionTable(wasm_function, table_to_replace, cloned_table);
}
}
return cloned_indirect_tables;
@ -1302,7 +1478,24 @@ Handle<FixedArray> SetupIndirectFunctionTable(
Handle<FixedArray> CloneModuleForInstance(Isolate* isolate,
Handle<FixedArray> original) {
Factory* factory = isolate->factory();
bool is_first =
original->GetValue<WeakCell>(isolate, kOwningInstance).is_null();
if (is_first) {
return original;
}
// We insert the latest clone in front.
Handle<FixedArray> clone = factory->CopyFixedArray(original);
Handle<WeakCell> weak_link_to_wasm_obj =
original->GetValueChecked<WeakCell>(isolate, kModuleObject);
clone->set(kModuleObject, *weak_link_to_wasm_obj);
Handle<WeakCell> link_to_original = factory->NewWeakCell(original);
clone->set(kNextInstance, *link_to_original);
Handle<WeakCell> link_to_clone = factory->NewWeakCell(clone);
original->set(kPrevInstance, *link_to_clone);
JSObject::cast(weak_link_to_wasm_obj->value())->SetInternalField(0, *clone);
// Clone each wasm code object.
Handle<FixedArray> orig_wasm_functions =
@ -1363,6 +1556,7 @@ Handle<FixedArray> CloneModuleForInstance(Isolate* isolate,
clone_wasm_functions->GetValueChecked<Code>(isolate, startup_fct_index);
PatchJSWrapper(isolate, startup_fct_clone, new_target);
}
clone->set(kImportMap, *isolate->factory()->undefined_value());
return clone;
}
@ -1372,27 +1566,30 @@ Handle<FixedArray> CloneModuleForInstance(Isolate* isolate,
// * installs named properties on the object for exported functions
// * compiles wasm code to machine code
MaybeHandle<JSObject> WasmModule::Instantiate(
Isolate* isolate, Handle<FixedArray> compiled_module,
Isolate* isolate, Handle<FixedArray> compiled_module_template,
Handle<JSReceiver> ffi, Handle<JSArrayBuffer> memory) {
HistogramTimerScope wasm_instantiate_module_time_scope(
isolate->counters()->wasm_instantiate_module_time());
ErrorThrower thrower(isolate, "WasmModule::Instantiate()");
Factory* factory = isolate->factory();
compiled_module = CloneModuleForInstance(isolate, compiled_module);
Handle<FixedArray> compiled_module =
CloneModuleForInstance(isolate, compiled_module_template);
bool template_is_owned =
GetOwningInstance(*compiled_module_template) != nullptr;
MaybeHandle<JSObject> template_owner;
if (template_is_owned) {
Handle<WeakCell> weak_owner =
compiled_module_template->GetValueChecked<WeakCell>(isolate,
kOwningInstance);
template_owner = handle(JSObject::cast(weak_owner->value()));
}
// These fields are compulsory.
Handle<FixedArray> code_table =
compiled_module->GetValueChecked<FixedArray>(isolate, kFunctions);
std::vector<Handle<Code>> functions(
static_cast<size_t>(code_table->length()));
for (int i = 0; i < code_table->length(); ++i) {
functions[static_cast<size_t>(i)] =
code_table->GetValueChecked<Code>(isolate, i);
}
LinkModuleFunctions(isolate, functions);
RecordStats(isolate, code_table);
MaybeHandle<JSObject> nothing;
@ -1403,35 +1600,61 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
Handle<JSObject> js_object = factory->NewJSObjectFromMap(map, TENURED);
js_object->SetInternalField(kWasmModuleCodeTable, *code_table);
// Remember the old imports, for the case when we are at the first instance -
// they will be replaced with the instance's actual imports in SetupImports.
MaybeHandle<FixedArray> old_imports =
compiled_module_template->GetValue<FixedArray>(isolate, kImportMap);
if (!(SetupInstanceHeap(isolate, compiled_module, js_object, memory,
&thrower) &&
SetupGlobals(isolate, compiled_module, js_object, &thrower) &&
SetupGlobals(isolate, template_owner, compiled_module, js_object,
&thrower) &&
SetupImports(isolate, compiled_module, js_object, &thrower, ffi) &&
SetupExportsObject(compiled_module, isolate, js_object, &thrower))) {
return nothing;
}
FixupFunctionsAndImports(
compiled_module_template->GetValueChecked<FixedArray>(isolate,
kFunctions),
code_table, old_imports,
compiled_module->GetValue<FixedArray>(isolate, kImportMap));
SetDebugSupport(factory, compiled_module, js_object);
SetRuntimeSupport(isolate, js_object);
FlushAssemblyCache(isolate, code_table);
MaybeHandle<FixedArray> maybe_indirect_tables =
compiled_module->GetValue<FixedArray>(isolate,
kTableOfIndirectFunctionTables);
Handle<FixedArray> indirect_tables_template;
if (maybe_indirect_tables.ToHandle(&indirect_tables_template)) {
Handle<FixedArray> indirect_tables = SetupIndirectFunctionTable(
isolate, code_table, indirect_tables_template);
for (int i = 0; i < indirect_tables->length(); ++i) {
Handle<FixedArray> metadata =
indirect_tables->GetValueChecked<FixedArray>(isolate, i);
uint32_t size = Smi::cast(metadata->get(kSize))->value();
Handle<FixedArray> table =
metadata->GetValueChecked<FixedArray>(isolate, kTable);
wasm::PopulateFunctionTable(table, size, &functions);
{
std::vector<Handle<Code>> functions(
static_cast<size_t>(code_table->length()));
for (int i = 0; i < code_table->length(); ++i) {
functions[static_cast<size_t>(i)] =
code_table->GetValueChecked<Code>(isolate, i);
}
MaybeHandle<FixedArray> maybe_indirect_tables =
compiled_module->GetValue<FixedArray>(isolate,
kTableOfIndirectFunctionTables);
Handle<FixedArray> indirect_tables_template;
if (maybe_indirect_tables.ToHandle(&indirect_tables_template)) {
Handle<FixedArray> to_replace =
template_owner.is_null()
? indirect_tables_template
: handle(FixedArray::cast(
template_owner.ToHandleChecked()->GetInternalField(
kWasmModuleFunctionTable)));
Handle<FixedArray> indirect_tables = SetupIndirectFunctionTable(
isolate, code_table, indirect_tables_template, to_replace);
for (int i = 0; i < indirect_tables->length(); ++i) {
Handle<FixedArray> metadata =
indirect_tables->GetValueChecked<FixedArray>(isolate, i);
uint32_t size = Smi::cast(metadata->get(kSize))->value();
Handle<FixedArray> table =
metadata->GetValueChecked<FixedArray>(isolate, kTable);
wasm::PopulateFunctionTable(table, size, &functions);
}
js_object->SetInternalField(kWasmModuleFunctionTable, *indirect_tables);
}
js_object->SetInternalField(kWasmModuleFunctionTable, *indirect_tables);
}
// Run the start function if one was specified.
@ -1461,6 +1684,19 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
}
DCHECK(wasm::IsWasmObject(*js_object));
if (!compiled_module->GetValue<WeakCell>(isolate, kModuleObject).is_null()) {
js_object->SetInternalField(kWasmCompiledModule, *compiled_module);
Handle<WeakCell> link_to_owner = factory->NewWeakCell(js_object);
compiled_module->set(kOwningInstance, *link_to_owner);
Handle<Object> global_handle =
isolate->global_handles()->Create(*js_object);
GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(),
&InstanceFinalizer,
v8::WeakCallbackType::kFinalizer);
}
return js_object;
}
@ -1648,6 +1884,8 @@ Handle<JSObject> CreateCompiledModuleObject(Isolate* isolate,
Handle<Symbol> module_sym(isolate->native_context()->wasm_module_sym());
Object::SetProperty(module_obj, module_sym, module_obj, STRICT).Check();
}
Handle<WeakCell> link_to_module = isolate->factory()->NewWeakCell(module_obj);
compiled_module->set(kModuleObject, *link_to_module);
return module_obj;
}
@ -1673,6 +1911,25 @@ MaybeHandle<JSObject> CreateModuleObjectFromBytes(Isolate* isolate,
origin);
}
MaybeHandle<JSArrayBuffer> GetInstanceMemory(Isolate* isolate,
Handle<JSObject> instance) {
Object* mem = instance->GetInternalField(kWasmMemArrayBuffer);
DCHECK(IsWasmObject(*instance));
if (mem->IsUndefined(isolate)) return MaybeHandle<JSArrayBuffer>();
return Handle<JSArrayBuffer>(JSArrayBuffer::cast(mem));
}
void SetInstanceMemory(Handle<JSObject> instance, JSArrayBuffer* buffer) {
DisallowHeapAllocation no_gc;
DCHECK(IsWasmObject(*instance));
instance->SetInternalField(kWasmMemArrayBuffer, buffer);
Object* module = instance->GetInternalField(kWasmCompiledModule);
if (module->IsFixedArray()) {
HeapNumber::cast(FixedArray::cast(module)->get(kMemSize))
->set_value(buffer->byte_length()->Number());
}
}
namespace testing {
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
@ -1757,6 +2014,51 @@ int32_t CallFunction(Isolate* isolate, Handle<JSObject> instance,
return -1;
}
void ValidateInstancesChain(Isolate* isolate, Handle<JSObject> module_obj,
int instance_count) {
CHECK_GE(instance_count, 0);
DisallowHeapAllocation no_gc;
FixedArray* compiled_module =
FixedArray::cast(module_obj->GetInternalField(0));
CHECK_EQ(JSObject::cast(GetModuleObject(compiled_module)->value()),
*module_obj);
Object* prev = nullptr;
int found_instances = GetOwningInstance(compiled_module) == nullptr ? 0 : 1;
FixedArray* current_instance = compiled_module;
while (GetNextInstance(current_instance) != nullptr) {
CHECK((prev == nullptr && GetPrevInstance(current_instance) == nullptr) ||
GetPrevInstance(current_instance)->value() == prev);
CHECK_EQ(GetModuleObject(current_instance)->value(), *module_obj);
CHECK(IsWasmObject(GetOwningInstance(current_instance)->value()));
prev = current_instance;
current_instance =
FixedArray::cast(GetNextInstance(current_instance)->value());
++found_instances;
CHECK_LE(found_instances, instance_count);
}
CHECK_EQ(found_instances, instance_count);
}
void ValidateModuleState(Isolate* isolate, Handle<JSObject> module_obj) {
DisallowHeapAllocation no_gc;
FixedArray* compiled_module =
FixedArray::cast(module_obj->GetInternalField(0));
CHECK_NOT_NULL(GetModuleObject(compiled_module));
CHECK_EQ(GetModuleObject(compiled_module)->value(), *module_obj);
CHECK_NULL(GetPrevInstance(compiled_module));
CHECK_NULL(GetNextInstance(compiled_module));
CHECK_NULL(GetOwningInstance(compiled_module));
}
void ValidateOrphanedInstance(Isolate* isolate, Handle<JSObject> instance) {
DisallowHeapAllocation no_gc;
CHECK(IsWasmObject(*instance));
FixedArray* compiled_module =
FixedArray::cast(instance->GetInternalField(kWasmCompiledModule));
CHECK_NOT_NULL(GetModuleObject(compiled_module));
CHECK(GetModuleObject(compiled_module)->cleared());
}
} // namespace testing
} // namespace wasm
} // namespace internal

View File

@ -415,6 +415,10 @@ MaybeHandle<JSObject> CreateModuleObjectFromBytes(Isolate* isolate,
// 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 {
// Decode, verify, and run the function labeled "main" in the
@ -425,6 +429,12 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
int32_t CallFunction(Isolate* isolate, Handle<JSObject> instance,
ErrorThrower* thrower, const char* name, int argc,
Handle<Object> argv[], bool asm_js = false);
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

View File

@ -0,0 +1,50 @@
// 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);
})();

View File

@ -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();
var builder = new WasmModuleBuilder();
builder.addGlobal(kAstI32);
builder.addFunction("read", kSig_i_v)
.addBody([
kExprGetGlobal, 0])
.exportFunc();
builder.addFunction("write", kSig_v_i)
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.addMemory(1,1, true);
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());
})();