Commit 186099d4 authored by Mircea Trofin's avatar Mircea Trofin Committed by Commit Bot

[wasm] Reference indirect tables as addresses of global handles

This sets us up for getting the wasm code generation off the GC heap.
We reference tables as global handles, which have a stable address. This
requires an extra instruction when attempting to make an indirect call,
per table (i.e. one for the signature table and one for the function
table).

Bug: 
Change-Id: I83743ba0f1dfdeba9aee5d27232f8823981288f8
Reviewed-on: https://chromium-review.googlesource.com/612322
Commit-Queue: Mircea Trofin <mtrofin@chromium.org>
Reviewed-by: 's avatarBrad Nelson <bradnelson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47444}
parent 96cd1cfb
......@@ -313,6 +313,17 @@ void RelocInfo::update_wasm_memory_reference(
set_embedded_address(isolate, updated_reference, icache_flush_mode);
}
void RelocInfo::set_global_handle(Isolate* isolate, Address address,
ICacheFlushMode icache_flush_mode) {
DCHECK_EQ(rmode_, WASM_GLOBAL_HANDLE);
set_embedded_address(isolate, address, icache_flush_mode);
}
Address RelocInfo::global_handle() const {
DCHECK_EQ(rmode_, WASM_GLOBAL_HANDLE);
return embedded_address();
}
void RelocInfo::update_wasm_memory_size(Isolate* isolate, uint32_t old_size,
uint32_t new_size,
ICacheFlushMode icache_flush_mode) {
......@@ -698,6 +709,8 @@ const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) {
return "wasm function table size reference";
case WASM_PROTECTED_INSTRUCTION_LANDING:
return "wasm protected instruction landing";
case WASM_GLOBAL_HANDLE:
return "global handle";
case NUMBER_OF_MODES:
case PC_JUMP:
UNREACHABLE();
......@@ -783,6 +796,7 @@ void RelocInfo::Verify(Isolate* isolate) {
case WASM_MEMORY_SIZE_REFERENCE:
case WASM_GLOBAL_REFERENCE:
case WASM_FUNCTION_TABLE_SIZE_REFERENCE:
case WASM_GLOBAL_HANDLE:
case WASM_PROTECTED_INSTRUCTION_LANDING:
// TODO(eholk): make sure the protected instruction is in range.
case NONE32:
......
......@@ -339,6 +339,7 @@ class RelocInfo {
WASM_MEMORY_SIZE_REFERENCE,
WASM_FUNCTION_TABLE_SIZE_REFERENCE,
WASM_PROTECTED_INSTRUCTION_LANDING,
WASM_GLOBAL_HANDLE,
RUNTIME_ENTRY,
COMMENT,
......@@ -446,16 +447,15 @@ class RelocInfo {
return mode == WASM_FUNCTION_TABLE_SIZE_REFERENCE;
}
static inline bool IsWasmReference(Mode mode) {
return mode == WASM_MEMORY_REFERENCE || mode == WASM_GLOBAL_REFERENCE ||
mode == WASM_MEMORY_SIZE_REFERENCE ||
mode == WASM_FUNCTION_TABLE_SIZE_REFERENCE;
return IsWasmPtrReference(mode) || IsWasmSizeReference(mode);
}
static inline bool IsWasmSizeReference(Mode mode) {
return mode == WASM_MEMORY_SIZE_REFERENCE ||
mode == WASM_FUNCTION_TABLE_SIZE_REFERENCE;
}
static inline bool IsWasmPtrReference(Mode mode) {
return mode == WASM_MEMORY_REFERENCE || mode == WASM_GLOBAL_REFERENCE;
return mode == WASM_MEMORY_REFERENCE || mode == WASM_GLOBAL_REFERENCE ||
mode == WASM_GLOBAL_HANDLE;
}
static inline bool IsWasmProtectedLanding(Mode mode) {
return mode == WASM_PROTECTED_INSTRUCTION_LANDING;
......@@ -490,6 +490,8 @@ class RelocInfo {
Address wasm_global_reference() const;
uint32_t wasm_function_table_size_reference() const;
uint32_t wasm_memory_size_reference() const;
Address global_handle() const;
void update_wasm_memory_reference(
Isolate* isolate, Address old_base, Address new_base,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
......@@ -507,6 +509,10 @@ class RelocInfo {
WriteBarrierMode write_barrier_mode = UPDATE_WRITE_BARRIER,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
void set_global_handle(
Isolate* isolate, Address address,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
// this relocation applies to;
// can only be called if IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
INLINE(Address target_address());
......
......@@ -849,6 +849,8 @@ void PipelineWasmCompilationJob::ValidateImmovableEmbeddedObjects() const {
Object* target = nullptr;
switch (mode) {
case RelocInfo::CODE_TARGET:
// this would be either one of the stubs or builtins, because
// we didn't link yet.
target = reinterpret_cast<Object*>(it.rinfo()->target_address());
break;
case RelocInfo::EMBEDDED_OBJECT:
......@@ -860,9 +862,7 @@ void PipelineWasmCompilationJob::ValidateImmovableEmbeddedObjects() const {
CHECK_NOT_NULL(target);
bool is_immovable =
target->IsSmi() || Heap::IsImmovable(HeapObject::cast(target));
// TODO(mtrofin): remove the fixed array part when WebAssembly.Table
// is backed by native object, rather than a FixedArray
CHECK(is_immovable || target->IsFixedArray());
CHECK(is_immovable);
}
}
......
......@@ -2254,9 +2254,14 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t sig_index, Node** args,
Node* size = function_table_sizes_[table_index];
Node* in_bounds = graph()->NewNode(machine->Uint32LessThan(), key, size);
TrapIfFalse(wasm::kTrapFuncInvalid, in_bounds, position);
Node* table = function_tables_[table_index];
Node* signatures = signature_tables_[table_index];
Node* table_address = function_tables_[table_index];
Node* table = graph()->NewNode(
jsgraph()->machine()->Load(MachineType::AnyTagged()), table_address,
jsgraph()->IntPtrConstant(0), *effect_, *control_);
Node* signatures_address = signature_tables_[table_index];
Node* signatures = graph()->NewNode(
jsgraph()->machine()->Load(MachineType::AnyTagged()), signatures_address,
jsgraph()->IntPtrConstant(0), *effect_, *control_);
// Load signature from the table and check.
// The table is a FixedArray; signatures are encoded as SMIs.
// [sig1, sig2, sig3, ...., code1, code2, code3 ...]
......@@ -2988,10 +2993,16 @@ void WasmGraphBuilder::EnsureFunctionTableNodes() {
if (function_tables_.size() > 0) return;
size_t tables_size = env_->function_tables.size();
for (size_t i = 0; i < tables_size; ++i) {
auto function_handle = env_->function_tables[i];
auto signature_handle = env_->signature_tables[i];
function_tables_.push_back(HeapConstant(function_handle));
signature_tables_.push_back(HeapConstant(signature_handle));
wasm::GlobalHandleAddress function_handle_address =
env_->function_tables[i];
wasm::GlobalHandleAddress signature_handle_address =
env_->signature_tables[i];
function_tables_.push_back(jsgraph()->RelocatableIntPtrConstant(
reinterpret_cast<intptr_t>(function_handle_address),
RelocInfo::WASM_GLOBAL_HANDLE));
signature_tables_.push_back(jsgraph()->RelocatableIntPtrConstant(
reinterpret_cast<intptr_t>(signature_handle_address),
RelocInfo::WASM_GLOBAL_HANDLE));
uint32_t table_size = env_->module->function_tables[i].initial_size;
function_table_sizes_.push_back(jsgraph()->RelocatableInt32Constant(
static_cast<uint32_t>(table_size),
......@@ -3819,8 +3830,8 @@ Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::WasmModule* module,
// TODO(titzer): compile JS to WASM wrappers without a {ModuleEnv}.
ModuleEnv env = {module,
std::vector<Handle<FixedArray>>(), // function_tables
std::vector<Handle<FixedArray>>(), // signature_tables
std::vector<Address>(), // function_tables
std::vector<Address>(), // signature_tables
std::vector<wasm::SignatureMap*>(), // signature_maps
std::vector<Handle<Code>>(), // function_code
BUILTIN_CODE(isolate, Illegal), // default_function_code
......
......@@ -52,12 +52,14 @@ struct ModuleEnv {
// A pointer to the decoded module's static representation.
const wasm::WasmModule* module;
// The function tables are FixedArrays of code used to dispatch indirect
// calls. (the same length as module.function_tables)
const std::vector<Handle<FixedArray>> function_tables;
// calls. (the same length as module.function_tables). We use the address
// to a global handle to the FixedArray.
const std::vector<Address> function_tables;
// The signatures tables are FixedArrays of SMIs used to check signatures
// match at runtime.
// (the same length as module.function_tables)
const std::vector<Handle<FixedArray>> signature_tables;
// We use the address to a global handle to the FixedArray.
const std::vector<Address> signature_tables;
// Signature maps canonicalize {FunctionSig*} to indexes. New entries can be
// added to a signature map during graph building.
// Normally, these signature maps correspond to the signature maps in the
......
......@@ -327,13 +327,12 @@ MaybeHandle<WasmModuleObject> ModuleCompiler::CompileToModuleObject(
ErrorThrower* thrower, const ModuleWireBytes& wire_bytes,
Handle<Script> asm_js_script,
Vector<const byte> asm_js_offset_table_bytes) {
Factory* factory = isolate_->factory();
TimedHistogramScope wasm_compile_module_time_scope(
module_->is_wasm() ? counters()->wasm_compile_wasm_module_time()
: counters()->wasm_compile_asm_module_time());
return CompileToModuleObjectInternal(thrower, wire_bytes, asm_js_script,
asm_js_offset_table_bytes, factory);
return CompileToModuleObjectInternal(
isolate_, thrower, wire_bytes, asm_js_script, asm_js_offset_table_bytes);
}
namespace {
......@@ -519,16 +518,25 @@ double MonotonicallyIncreasingTimeInMs() {
}
std::unique_ptr<compiler::ModuleEnv> CreateDefaultModuleEnv(
Factory* factory, WasmModule* module, Handle<Code> illegal_builtin) {
std::vector<Handle<FixedArray>> function_tables;
std::vector<Handle<FixedArray>> signature_tables;
Isolate* isolate, WasmModule* module, Handle<Code> illegal_builtin,
GlobalHandleLifetimeManager* lifetime_manager) {
std::vector<GlobalHandleAddress> function_tables;
std::vector<GlobalHandleAddress> signature_tables;
std::vector<SignatureMap*> signature_maps;
for (size_t i = 0; i < module->function_tables.size(); i++) {
auto& function_table = module->function_tables[i];
function_tables.push_back(factory->NewFixedArray(1, TENURED));
signature_tables.push_back(factory->NewFixedArray(1, TENURED));
signature_maps.push_back(&function_table.map);
// We need *some* value for each table. We'll reuse this value when
// we want to reset a {WasmCompiledModule}. We could just insert
// bogus values (e.g. 0, 1, etc), but to keep things consistent, we'll
// create a valid global handle for the undefined value.
// These global handles are deleted when finalizing the module object.
Handle<Object> func_table =
isolate->global_handles()->Create(isolate->heap()->undefined_value());
Handle<Object> sig_table =
isolate->global_handles()->Create(isolate->heap()->undefined_value());
function_tables.push_back(func_table.address());
signature_tables.push_back(sig_table.address());
signature_maps.push_back(&module->function_tables[i].map);
}
std::vector<Handle<Code>> empty_code;
......@@ -566,9 +574,10 @@ void ReopenHandles(Isolate* isolate, const std::vector<Handle<T>>& vec) {
} // namespace
MaybeHandle<WasmModuleObject> ModuleCompiler::CompileToModuleObjectInternal(
ErrorThrower* thrower, const ModuleWireBytes& wire_bytes,
Handle<Script> asm_js_script, Vector<const byte> asm_js_offset_table_bytes,
Factory* factory) {
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& wire_bytes,
Handle<Script> asm_js_script,
Vector<const byte> asm_js_offset_table_bytes) {
Factory* factory = isolate->factory();
// Check whether lazy compilation is enabled for this module.
bool lazy_compile = compile_lazy(module_.get());
......@@ -579,7 +588,9 @@ MaybeHandle<WasmModuleObject> ModuleCompiler::CompileToModuleObjectInternal(
? BUILTIN_CODE(isolate_, WasmCompileLazy)
: BUILTIN_CODE(isolate_, Illegal);
auto env = CreateDefaultModuleEnv(factory, module_.get(), init_builtin);
GlobalHandleLifetimeManager globals_manager;
auto env = CreateDefaultModuleEnv(isolate, module_.get(), init_builtin,
&globals_manager);
// The {code_table} array contains import wrappers and functions (which
// are both included in {functions.size()}, and export wrappers).
......@@ -695,6 +706,10 @@ MaybeHandle<WasmModuleObject> ModuleCompiler::CompileToModuleObjectInternal(
func_index++;
}
// Now we can relinquish control to the global handles, because the
// {WasmModuleObject} will take care of them in its finalizer, which it'll
// setup in {New}.
globals_manager.ReleaseWithoutDestroying();
return WasmModuleObject::New(isolate_, compiled_module);
}
......@@ -1134,6 +1149,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
DCHECK(!isolate_->has_pending_exception());
TRACE("Finishing instance %d\n", compiled_module_->instance_id());
TRACE_CHAIN(module_object_->compiled_module());
globals_manager_.ReleaseWithoutDestroying();
return instance;
}
......@@ -1725,9 +1741,16 @@ void InstanceBuilder::InitializeTables(
CodeSpecialization* code_specialization) {
int function_table_count = static_cast<int>(module_->function_tables.size());
Handle<FixedArray> new_function_tables =
isolate_->factory()->NewFixedArray(function_table_count);
isolate_->factory()->NewFixedArray(function_table_count, TENURED);
Handle<FixedArray> new_signature_tables =
isolate_->factory()->NewFixedArray(function_table_count);
isolate_->factory()->NewFixedArray(function_table_count, TENURED);
Handle<FixedArray> old_function_tables = compiled_module_->function_tables();
Handle<FixedArray> old_signature_tables =
compiled_module_->signature_tables();
DCHECK_EQ(old_function_tables->length(), new_function_tables->length());
DCHECK_EQ(old_signature_tables->length(), new_signature_tables->length());
for (int index = 0; index < function_table_count; ++index) {
WasmIndirectFunctionTable& table = module_->function_tables[index];
TableInstance& table_instance = table_instances_[index];
......@@ -1752,27 +1775,36 @@ void InstanceBuilder::InitializeTables(
table_size, table_instance.function_table->length());
}
}
int int_index = static_cast<int>(index);
new_function_tables->set(static_cast<int>(index),
*table_instance.function_table);
new_signature_tables->set(static_cast<int>(index),
*table_instance.signature_table);
}
// We create a global handle here and delete it when finalizing the
// instance. Even if the same table is shared accross many instances, each
// will have its own private global handle to it. Meanwhile, we the global
// handles root the respective objects (the tables).
Handle<FixedArray> global_func_table =
isolate_->global_handles()->Create(*table_instance.function_table);
Handle<FixedArray> global_sig_table =
isolate_->global_handles()->Create(*table_instance.signature_table);
FixedArray* old_function_tables = compiled_module_->ptr_to_function_tables();
DCHECK_EQ(old_function_tables->length(), new_function_tables->length());
for (int i = 0, e = new_function_tables->length(); i < e; ++i) {
code_specialization->RelocateObject(
handle(old_function_tables->get(i), isolate_),
handle(new_function_tables->get(i), isolate_));
}
FixedArray* old_signature_tables =
compiled_module_->ptr_to_signature_tables();
DCHECK_EQ(old_signature_tables->length(), new_signature_tables->length());
for (int i = 0, e = new_signature_tables->length(); i < e; ++i) {
code_specialization->RelocateObject(
handle(old_signature_tables->get(i), isolate_),
handle(new_signature_tables->get(i), isolate_));
GlobalHandleAddress new_func_table_addr = global_func_table.address();
GlobalHandleAddress new_sig_table_addr = global_sig_table.address();
globals_manager_.Add(new_func_table_addr);
globals_manager_.Add(new_sig_table_addr);
WasmCompiledModule::SetTableValue(isolate_, new_function_tables, int_index,
new_func_table_addr);
WasmCompiledModule::SetTableValue(isolate_, new_signature_tables, int_index,
new_sig_table_addr);
GlobalHandleAddress old_func_table_addr =
WasmCompiledModule::GetTableValue(*old_function_tables, int_index);
GlobalHandleAddress old_sig_table_addr =
WasmCompiledModule::GetTableValue(*old_signature_tables, int_index);
code_specialization->RelocatePointer(old_func_table_addr,
new_func_table_addr);
code_specialization->RelocatePointer(old_sig_table_addr,
new_sig_table_addr);
}
compiled_module_->set_function_tables(new_function_tables);
......@@ -2058,8 +2090,8 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
Factory* factory = isolate->factory();
Handle<Code> illegal_builtin = BUILTIN_CODE(isolate, Illegal);
job_->module_env_ =
CreateDefaultModuleEnv(factory, module_.get(), illegal_builtin);
job_->module_env_ = CreateDefaultModuleEnv(
isolate, module_.get(), illegal_builtin, &job_->globals_manager_);
// The {code_table} array contains import wrappers and functions (which
// are both included in {functions.size()}, and export wrappers.
......@@ -2083,8 +2115,6 @@ class AsyncCompileJob::PrepareAndStartCompile : public CompileStep {
centry_stub = Handle<Code>(*centry_stub, isolate);
job_->code_table_ = Handle<FixedArray>(*job_->code_table_, isolate);
compiler::ModuleEnv* env = job_->module_env_.get();
ReopenHandles(isolate, env->function_tables);
ReopenHandles(isolate, env->signature_tables);
ReopenHandles(isolate, env->function_code);
Handle<Code>* mut =
const_cast<Handle<Code>*>(&env->default_function_code);
......@@ -2328,6 +2358,7 @@ class AsyncCompileJob::FinishModule : public CompileStep {
Handle<WasmModuleObject> result =
WasmModuleObject::New(job_->isolate_, job_->compiled_module_);
// {job_} is deleted in AsyncCompileSucceeded, therefore the {return}.
job_->globals_manager_.ReleaseWithoutDestroying();
return job_->AsyncCompileSucceeded(result);
}
};
......
......@@ -164,9 +164,9 @@ class ModuleCompiler {
private:
MaybeHandle<WasmModuleObject> CompileToModuleObjectInternal(
ErrorThrower* thrower, const ModuleWireBytes& wire_bytes,
Handle<Script> asm_js_script,
Vector<const byte> asm_js_offset_table_bytes, Factory* factory);
Isolate* isolate, ErrorThrower* thrower,
const ModuleWireBytes& wire_bytes, Handle<Script> asm_js_script,
Vector<const byte> asm_js_offset_table_bytes);
Isolate* isolate_;
std::unique_ptr<WasmModule> module_;
......@@ -234,6 +234,7 @@ class InstanceBuilder {
std::vector<Handle<JSFunction>> js_wrappers_;
JSToWasmWrapperCache js_to_wasm_cache_;
WeakCallbackInfo<void>::Callback instance_finalizer_callback_;
GlobalHandleLifetimeManager globals_manager_;
const std::shared_ptr<Counters>& async_counters() const {
return async_counters_;
......@@ -347,6 +348,7 @@ class AsyncCompileJob {
Handle<JSPromise> module_promise_;
std::unique_ptr<ModuleCompiler> compiler_;
std::unique_ptr<compiler::ModuleEnv> module_env_;
GlobalHandleLifetimeManager globals_manager_;
std::vector<DeferredHandles*> deferred_handles_;
Handle<WasmModuleObject> module_object_;
......
......@@ -84,8 +84,7 @@ bool IsAtWasmDirectCallTarget(RelocIterator& it) {
} // namespace
CodeSpecialization::CodeSpecialization(Isolate* isolate, Zone* zone)
: objects_to_relocate(isolate->heap(), ZoneAllocationPolicy(zone)) {}
CodeSpecialization::CodeSpecialization(Isolate* isolate, Zone* zone) {}
CodeSpecialization::~CodeSpecialization() {}
......@@ -120,11 +119,8 @@ void CodeSpecialization::RelocateDirectCalls(
relocate_direct_calls_instance = instance;
}
void CodeSpecialization::RelocateObject(Handle<Object> old_obj,
Handle<Object> new_obj) {
DCHECK(!old_obj.is_null() && !new_obj.is_null());
has_objects_to_relocate = true;
objects_to_relocate.Set(*old_obj, new_obj);
void CodeSpecialization::RelocatePointer(Address old_ptr, Address new_ptr) {
pointers_to_relocate.insert(std::make_pair(old_ptr, new_ptr));
}
bool CodeSpecialization::ApplyToWholeInstance(
......@@ -191,7 +187,7 @@ bool CodeSpecialization::ApplyToWasmCode(Code* code,
bool reloc_globals = old_globals_start || new_globals_start;
bool patch_table_size = old_function_table_size || new_function_table_size;
bool reloc_direct_calls = !relocate_direct_calls_instance.is_null();
bool reloc_objects = has_objects_to_relocate;
bool reloc_pointers = pointers_to_relocate.size() > 0;
int reloc_mode = 0;
auto add_mode = [&reloc_mode](bool cond, RelocInfo::Mode mode) {
......@@ -202,7 +198,7 @@ bool CodeSpecialization::ApplyToWasmCode(Code* code,
add_mode(reloc_globals, RelocInfo::WASM_GLOBAL_REFERENCE);
add_mode(patch_table_size, RelocInfo::WASM_FUNCTION_TABLE_SIZE_REFERENCE);
add_mode(reloc_direct_calls, RelocInfo::CODE_TARGET);
add_mode(reloc_objects, RelocInfo::EMBEDDED_OBJECT);
add_mode(reloc_pointers, RelocInfo::WASM_GLOBAL_HANDLE);
std::unique_ptr<PatchDirectCallsHelper> patch_direct_calls_helper;
bool changed = false;
......@@ -258,13 +254,12 @@ bool CodeSpecialization::ApplyToWasmCode(Code* code,
UPDATE_WRITE_BARRIER, icache_flush_mode);
changed = true;
} break;
case RelocInfo::EMBEDDED_OBJECT: {
DCHECK(reloc_objects);
Object* old = it.rinfo()->target_object();
Handle<Object>* new_obj = objects_to_relocate.Find(old);
if (new_obj) {
it.rinfo()->set_target_object(HeapObject::cast(**new_obj),
UPDATE_WRITE_BARRIER,
case RelocInfo::WASM_GLOBAL_HANDLE: {
DCHECK(reloc_pointers);
Address old_ptr = it.rinfo()->global_handle();
if (pointers_to_relocate.count(old_ptr) == 1) {
Address new_ptr = pointers_to_relocate[old_ptr];
it.rinfo()->set_global_handle(code->GetIsolate(), new_ptr,
icache_flush_mode);
changed = true;
}
......
......@@ -39,7 +39,7 @@ class CodeSpecialization {
// Update all direct call sites based on the code table in the given instance.
void RelocateDirectCalls(Handle<WasmInstanceObject> instance);
// Relocate an arbitrary object (e.g. function table).
void RelocateObject(Handle<Object> old_obj, Handle<Object> new_obj);
void RelocatePointer(Address old_obj, Address new_obj);
// Apply all relocations and patching to all code in the instance (wasm code
// and exported functions).
......@@ -62,8 +62,7 @@ class CodeSpecialization {
Handle<WasmInstanceObject> relocate_direct_calls_instance;
bool has_objects_to_relocate = false;
IdentityMap<Handle<Object>, ZoneAllocationPolicy> objects_to_relocate;
std::map<Address, Address> pointers_to_relocate;
};
} // namespace wasm
......
......@@ -2328,8 +2328,12 @@ class ThreadImpl {
if (table_index >= static_cast<uint32_t>(sig_tables->length())) {
return {ExternalCallResult::INVALID_FUNC};
}
FixedArray* sig_table =
FixedArray::cast(sig_tables->get(static_cast<int>(table_index)));
// Reconstitute the global handle to sig_table, and, further below,
// to the function table, from the address stored in the
// respective table of tables.
int table_index_as_int = static_cast<int>(table_index);
Handle<FixedArray> sig_table(reinterpret_cast<FixedArray**>(
WasmCompiledModule::GetTableValue(sig_tables, table_index_as_int)));
if (entry_index >= static_cast<uint32_t>(sig_table->length())) {
return {ExternalCallResult::INVALID_FUNC};
}
......@@ -2341,8 +2345,8 @@ class ThreadImpl {
// Get code object.
FixedArray* fun_tables = compiled_module->ptr_to_function_tables();
DCHECK_EQ(sig_tables->length(), fun_tables->length());
FixedArray* fun_table =
FixedArray::cast(fun_tables->get(static_cast<int>(table_index)));
Handle<FixedArray> fun_table(reinterpret_cast<FixedArray**>(
WasmCompiledModule::GetTableValue(fun_tables, table_index_as_int)));
DCHECK_EQ(sig_table->length(), fun_table->length());
target = Code::cast(fun_table->get(static_cast<int>(entry_index)));
}
......
......@@ -217,18 +217,18 @@ compiler::ModuleEnv CreateModuleEnvFromCompiledModule(
DisallowHeapAllocation no_gc;
WasmModule* module = compiled_module->module();
std::vector<Handle<FixedArray>> function_tables;
std::vector<Handle<FixedArray>> signature_tables;
std::vector<GlobalHandleAddress> function_tables;
std::vector<GlobalHandleAddress> signature_tables;
std::vector<SignatureMap*> signature_maps;
int num_function_tables = static_cast<int>(module->function_tables.size());
for (int i = 0; i < num_function_tables; i++) {
for (int i = 0; i < num_function_tables; ++i) {
FixedArray* ft = compiled_module->ptr_to_function_tables();
FixedArray* st = compiled_module->ptr_to_signature_tables();
// TODO(clemensh): defer these handles for concurrent compilation.
function_tables.push_back(handle(FixedArray::cast(ft->get(i))));
signature_tables.push_back(handle(FixedArray::cast(st->get(i))));
function_tables.push_back(WasmCompiledModule::GetTableValue(ft, i));
signature_tables.push_back(WasmCompiledModule::GetTableValue(st, i));
signature_maps.push_back(&module->function_tables[i].map);
}
......
This diff is collapsed.
......@@ -24,7 +24,34 @@ namespace internal {
namespace wasm {
class InterpretedFrame;
class WasmInterpreter;
}
// When we compile or instantiate, we need to create global handles
// for function tables. Normally, these handles get destroyed when the
// respective objects get GCed. If we fail to construct those objects,
// we can leak global hanles. The exit path in these cases isn't unique,
// and may grow.
//
// This type addresses that.
typedef Address GlobalHandleAddress;
class GlobalHandleLifetimeManager final {
public:
void Add(GlobalHandleAddress addr) { handles_.push_back(addr); }
// Call this when compilation or instantiation has succeeded, and we've
// passed the control to a JS object with a finalizer that'll destroy
// the handles.
void ReleaseWithoutDestroying() { handles_.clear(); }
~GlobalHandleLifetimeManager() {
for (auto& addr : handles_) {
GlobalHandles::Destroy(reinterpret_cast<Object**>(addr));
}
}
private:
std::vector<Address> handles_;
};
} // namespace wasm
class WasmCompiledModule;
class WasmDebugInfo;
......@@ -65,6 +92,9 @@ class WasmModuleObject : public JSObject {
static Handle<WasmModuleObject> New(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module);
private:
static void Finalizer(const v8::WeakCallbackInfo<void>& data);
};
// Representation of a WebAssembly.Table JavaScript-level object.
......@@ -386,7 +416,8 @@ class WasmCompiledModule : public FixedArray {
MACRO(OBJECT, FixedArray, weak_exported_functions) \
MACRO(OBJECT, FixedArray, function_tables) \
MACRO(OBJECT, FixedArray, signature_tables) \
MACRO(OBJECT, FixedArray, empty_function_tables) \
MACRO(CONST_OBJECT, FixedArray, empty_function_tables) \
MACRO(CONST_OBJECT, FixedArray, empty_signature_tables) \
MACRO(LARGE_NUMBER, size_t, embedded_mem_start) \
MACRO(LARGE_NUMBER, size_t, globals_start) \
MACRO(LARGE_NUMBER, uint32_t, embedded_mem_size) \
......@@ -420,12 +451,13 @@ class WasmCompiledModule : public FixedArray {
static Handle<WasmCompiledModule> New(
Isolate* isolate, Handle<WasmSharedModuleData> shared,
Handle<FixedArray> code_table,
const std::vector<Handle<FixedArray>>& function_tables,
const std::vector<Handle<FixedArray>>& signature_tables);
const std::vector<wasm::GlobalHandleAddress>& function_tables,
const std::vector<wasm::GlobalHandleAddress>& signature_tables);
static Handle<WasmCompiledModule> Clone(Isolate* isolate,
Handle<WasmCompiledModule> module);
static void Reset(Isolate* isolate, WasmCompiledModule* module);
static void Reset(Isolate* isolate, WasmCompiledModule* module,
bool clear_global_handles = true);
Address GetEmbeddedMemStartOrNull() const {
return has_embedded_mem_start()
......@@ -565,6 +597,11 @@ class WasmCompiledModule : public FixedArray {
set_code_table(testing_table);
}
static void SetTableValue(Isolate* isolate, Handle<FixedArray> table,
int index, Address value);
static void UpdateTableValue(FixedArray* table, int index, Address value);
static Address GetTableValue(FixedArray* table, int index);
private:
void InitId();
......
......@@ -276,8 +276,14 @@ class TestingModuleBuilder {
table.map.FindOrInsert(test_module_.functions[function_indexes[i]].sig);
}
function_tables_.push_back(isolate_->factory()->NewFixedArray(table_size));
signature_tables_.push_back(isolate_->factory()->NewFixedArray(table_size));
function_tables_.push_back(
isolate_->global_handles()
->Create(*isolate_->factory()->NewFixedArray(table_size))
.address());
signature_tables_.push_back(
isolate_->global_handles()
->Create(*isolate_->factory()->NewFixedArray(table_size))
.address());
}
void PopulateIndirectFunctionTable() {
......@@ -285,8 +291,10 @@ class TestingModuleBuilder {
// Initialize the fixed arrays in instance->function_tables.
for (uint32_t i = 0; i < function_tables_.size(); i++) {
WasmIndirectFunctionTable& table = test_module_.function_tables[i];
Handle<FixedArray> function_table = function_tables_[i];
Handle<FixedArray> signature_table = signature_tables_[i];
Handle<FixedArray> function_table(
reinterpret_cast<FixedArray**>(function_tables_[i]));
Handle<FixedArray> signature_table(
reinterpret_cast<FixedArray**>(signature_tables_[i]));
int table_size = static_cast<int>(table.values.size());
for (int j = 0; j < table_size; j++) {
WasmFunction& function = test_module_.functions[table.values[j]];
......@@ -354,8 +362,8 @@ class TestingModuleBuilder {
byte* mem_start_;
uint32_t mem_size_;
std::vector<Handle<Code>> function_code_;
std::vector<Handle<FixedArray>> function_tables_;
std::vector<Handle<FixedArray>> signature_tables_;
std::vector<GlobalHandleAddress> function_tables_;
std::vector<GlobalHandleAddress> signature_tables_;
V8_ALIGNED(8) byte globals_data_[kMaxGlobalsSize];
WasmInterpreter* interpreter_;
Handle<WasmInstanceObject> instance_object_;
......
......@@ -67,5 +67,7 @@ gc();
module = null;
})();
gc();
// the first GC will clear the module, the second the instance.
gc();
%ValidateWasmOrphanedInstance(instance4);
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