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