Revert "Revert "[wasm] Reference indirect tables as addresses of global handles""

This reverts commit af37f6b970.

Reason for revert: Reverted dependency fixed.

Original change's description:
> Revert "[wasm] Reference indirect tables as addresses of global handles"
> 
> This reverts commit 186099d49f.
> 
> Reason for revert: Need to revert:
> https://chromium-review.googlesource.com/c/613880
> 
> Original change's description:
> > [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: Brad Nelson <bradnelson@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#47444}
> 
> TBR=bradnelson@chromium.org,titzer@chromium.org,mtrofin@chromium.org
> 
> Change-Id: Ic3dff87410a51a2072ddc16cfc83a230526d4c56
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://chromium-review.googlesource.com/622568
> Reviewed-by: Michael Achenbach <machenbach@chromium.org>
> Commit-Queue: Michael Achenbach <machenbach@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#47450}

TBR=bradnelson@chromium.org,machenbach@chromium.org,titzer@chromium.org,mtrofin@chromium.org

Change-Id: I3dc5dc8be26b5462703edac954cbedbb8f504c1e
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/622035
Reviewed-by: Mircea Trofin <mtrofin@chromium.org>
Commit-Queue: Mircea Trofin <mtrofin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47455}
This commit is contained in:
Mircea Trofin 2017-08-19 16:35:05 +00:00 committed by Commit Bot
parent 5eb1aa488e
commit b22fb03a49
15 changed files with 361 additions and 121 deletions

View File

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

View File

@ -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());

View File

@ -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);
}
}

View File

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

View File

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

View File

@ -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);
}
};

View File

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

View File

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

View File

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

View File

@ -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)));
}

View File

@ -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);
}

View File

@ -177,9 +177,33 @@ Handle<WasmModuleObject> WasmModuleObject::New(
Handle<WeakCell> link_to_module =
isolate->factory()->NewWeakCell(module_object);
compiled_module->set_weak_wasm_module(link_to_module);
Handle<Object> global_handle =
isolate->global_handles()->Create(*module_object);
GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(),
&Finalizer, v8::WeakCallbackType::kFinalizer);
return module_object;
}
void WasmModuleObject::Finalizer(const v8::WeakCallbackInfo<void>& data) {
DisallowHeapAllocation no_gc;
JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
WasmModuleObject* module = reinterpret_cast<WasmModuleObject*>(*p);
WasmCompiledModule* compiled_module = module->compiled_module();
if (compiled_module->has_empty_function_tables()) {
DCHECK(compiled_module->has_empty_signature_tables());
for (int i = 0, e = compiled_module->empty_function_tables()->length();
i < e; ++i) {
GlobalHandles::Destroy(
reinterpret_cast<Object**>(WasmCompiledModule::GetTableValue(
compiled_module->ptr_to_empty_function_tables(), i)));
GlobalHandles::Destroy(
reinterpret_cast<Object**>(WasmCompiledModule::GetTableValue(
compiled_module->ptr_to_empty_signature_tables(), i)));
}
}
GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
}
Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
int64_t maximum,
Handle<FixedArray>* js_functions) {
@ -239,23 +263,52 @@ void WasmTableObject::grow(Isolate* isolate, uint32_t count) {
FixedArray::cast(dispatch_tables->get(i + 2)));
Handle<FixedArray> old_signature_table(
FixedArray::cast(dispatch_tables->get(i + 3)));
Handle<FixedArray> new_function_table =
isolate->factory()->CopyFixedArrayAndGrow(old_function_table, count);
Handle<FixedArray> new_signature_table =
isolate->factory()->CopyFixedArrayAndGrow(old_signature_table, count);
Handle<FixedArray> new_function_table = isolate->global_handles()->Create(
*isolate->factory()->CopyFixedArrayAndGrow(old_function_table, count));
Handle<FixedArray> new_signature_table = isolate->global_handles()->Create(
*isolate->factory()->CopyFixedArrayAndGrow(old_signature_table, count));
GlobalHandleAddress new_function_table_addr = new_function_table.address();
GlobalHandleAddress new_signature_table_addr =
new_signature_table.address();
int table_index = Smi::cast(dispatch_tables->get(i + 1))->value();
// Update dispatch tables with new function/signature tables
dispatch_tables->set(i + 2, *new_function_table);
dispatch_tables->set(i + 3, *new_signature_table);
// Patch the code of the respective instance.
CodeSpecialization code_specialization(isolate, &specialization_zone);
code_specialization.PatchTableSize(old_size, old_size + count);
code_specialization.RelocateObject(old_function_table, new_function_table);
code_specialization.RelocateObject(old_signature_table,
new_signature_table);
code_specialization.ApplyToWholeInstance(
WasmInstanceObject::cast(dispatch_tables->get(i)));
{
DisallowHeapAllocation no_gc;
CodeSpecialization code_specialization(isolate, &specialization_zone);
WasmInstanceObject* instance =
WasmInstanceObject::cast(dispatch_tables->get(i));
WasmCompiledModule* compiled_module = instance->compiled_module();
GlobalHandleAddress old_function_table_addr =
WasmCompiledModule::GetTableValue(
compiled_module->ptr_to_function_tables(), table_index);
GlobalHandleAddress old_signature_table_addr =
WasmCompiledModule::GetTableValue(
compiled_module->ptr_to_signature_tables(), table_index);
code_specialization.PatchTableSize(old_size, old_size + count);
code_specialization.RelocatePointer(old_function_table_addr,
new_function_table_addr);
code_specialization.RelocatePointer(old_signature_table_addr,
new_signature_table_addr);
code_specialization.ApplyToWholeInstance(instance);
WasmCompiledModule::UpdateTableValue(
compiled_module->ptr_to_function_tables(), table_index,
new_function_table_addr);
WasmCompiledModule::UpdateTableValue(
compiled_module->ptr_to_signature_tables(), table_index,
new_signature_table_addr);
// We need to destroy the global handles this instance held to the
// old tables now, otherwise we'd leak global handles.
GlobalHandles::Destroy(
reinterpret_cast<Object**>(old_function_table_addr));
GlobalHandles::Destroy(
reinterpret_cast<Object**>(old_signature_table_addr));
}
}
}
@ -797,8 +850,8 @@ void WasmSharedModuleData::PrepareForLazyCompilation(
Handle<WasmCompiledModule> 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) {
DCHECK_EQ(function_tables.size(), signature_tables.size());
Handle<FixedArray> ret =
isolate->factory()->NewFixedArray(PropertyIndices::Count, TENURED);
@ -824,12 +877,18 @@ Handle<WasmCompiledModule> WasmCompiledModule::New(
Handle<FixedArray> ft =
isolate->factory()->NewFixedArray(num_function_tables, TENURED);
for (int i = 0; i < num_function_tables; ++i) {
st->set(i, *(signature_tables[i]));
ft->set(i, *(function_tables[i]));
size_t index = static_cast<size_t>(i);
SetTableValue(isolate, ft, i, function_tables[index]);
SetTableValue(isolate, st, i, signature_tables[index]);
}
// TODO(wasm): setting the empty tables here this way is OK under the
// assumption that we compile and then instantiate. It needs rework if we do
// direct instantiation. The empty tables are used as a default when
// resetting the compiled module.
compiled_module->set_signature_tables(st);
compiled_module->set_empty_function_tables(ft);
compiled_module->set_empty_signature_tables(st);
compiled_module->set_function_tables(ft);
compiled_module->set_empty_function_tables(ft);
}
// TODO(mtrofin): copy the rest of the specialization parameters over.
@ -864,8 +923,30 @@ Handle<WasmCompiledModule> WasmCompiledModule::Clone(
return ret;
}
void WasmCompiledModule::SetTableValue(Isolate* isolate,
Handle<FixedArray> table, int index,
Address value) {
Handle<HeapNumber> number = isolate->factory()->NewHeapNumber(
static_cast<double>(reinterpret_cast<size_t>(value)), MUTABLE, TENURED);
table->set(index, *number);
}
void WasmCompiledModule::UpdateTableValue(FixedArray* table, int index,
Address value) {
DisallowHeapAllocation no_gc;
HeapNumber::cast(table->get(index))
->set_value(static_cast<double>(reinterpret_cast<size_t>(value)));
}
Address WasmCompiledModule::GetTableValue(FixedArray* table, int index) {
DisallowHeapAllocation no_gc;
double value = HeapNumber::cast(table->get(index))->value();
return reinterpret_cast<Address>(static_cast<size_t>(value));
}
void WasmCompiledModule::Reset(Isolate* isolate,
WasmCompiledModule* compiled_module) {
WasmCompiledModule* compiled_module,
bool clear_global_handles) {
DisallowHeapAllocation no_gc;
TRACE("Resetting %d\n", compiled_module->instance_id());
Object* undefined = *isolate->factory()->undefined_value();
@ -898,16 +979,36 @@ void WasmCompiledModule::Reset(Isolate* isolate,
// Reset function tables.
if (compiled_module->has_function_tables()) {
FixedArray* function_tables = compiled_module->ptr_to_function_tables();
FixedArray* signature_tables = compiled_module->ptr_to_signature_tables();
FixedArray* empty_function_tables =
compiled_module->ptr_to_empty_function_tables();
FixedArray* empty_signature_tables =
compiled_module->ptr_to_empty_signature_tables();
if (function_tables != empty_function_tables) {
DCHECK_EQ(function_tables->length(), empty_function_tables->length());
for (int i = 0, e = function_tables->length(); i < e; ++i) {
code_specialization.RelocateObject(
handle(function_tables->get(i), isolate),
handle(empty_function_tables->get(i), isolate));
GlobalHandleAddress func_addr =
WasmCompiledModule::GetTableValue(function_tables, i);
GlobalHandleAddress sig_addr =
WasmCompiledModule::GetTableValue(signature_tables, i);
code_specialization.RelocatePointer(
func_addr,
WasmCompiledModule::GetTableValue(empty_function_tables, i));
code_specialization.RelocatePointer(
sig_addr,
WasmCompiledModule::GetTableValue(empty_signature_tables, i));
// We create a global handle per table per instance. When sharing
// tables, to avoid accummulating global handles until the last
// instance sharing the table is GC-ed, destroy the handles here.
// Except if we call this post-deserialize - because maybe the
// instance that originated the {WasmCompiledModule} is still alive.
if (clear_global_handles) {
GlobalHandles::Destroy(reinterpret_cast<Object**>(func_addr));
GlobalHandles::Destroy(reinterpret_cast<Object**>(sig_addr));
}
}
compiled_module->set_ptr_to_function_tables(empty_function_tables);
compiled_module->set_ptr_to_signature_tables(empty_signature_tables);
}
}
@ -1065,7 +1166,35 @@ void WasmCompiledModule::ReinitializeAfterDeserialization(
isolate);
DCHECK(!WasmSharedModuleData::IsWasmSharedModuleData(*shared));
WasmSharedModuleData::ReinitializeAfterDeserialization(isolate, shared);
WasmCompiledModule::Reset(isolate, *compiled_module);
int function_table_count =
static_cast<int>(compiled_module->module()->function_tables.size());
if (function_table_count > 0) {
// The tables are of the right size, but contain bogus global handle
// addresses. Produce new global handles for the empty tables, then reset,
// which will relocate the code. We end up with a WasmCompiledModule as-if
// it were just compiled.
DCHECK(compiled_module->has_function_tables());
DCHECK(compiled_module->has_signature_tables());
DCHECK(compiled_module->has_empty_signature_tables());
DCHECK(compiled_module->has_empty_function_tables());
for (int i = 0; i < function_table_count; ++i) {
Handle<Object> global_func_table_handle =
isolate->global_handles()->Create(isolate->heap()->undefined_value());
Handle<Object> global_sig_table_handle =
isolate->global_handles()->Create(isolate->heap()->undefined_value());
GlobalHandleAddress new_func_table = global_func_table_handle.address();
GlobalHandleAddress new_sig_table = global_sig_table_handle.address();
SetTableValue(isolate, compiled_module->empty_function_tables(), i,
new_func_table);
SetTableValue(isolate, compiled_module->empty_signature_tables(), i,
new_sig_table);
}
}
// Reset, but don't delete any global handles, because their owning instance
// may still be active.
WasmCompiledModule::Reset(isolate, *compiled_module, false);
DCHECK(WasmSharedModuleData::IsWasmSharedModuleData(*shared));
}

View File

@ -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();

View File

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

View File

@ -67,5 +67,7 @@ gc();
module = null;
})();
gc();
// the first GC will clear the module, the second the instance.
gc();
%ValidateWasmOrphanedInstance(instance4);