[wasm] Refactor indirect function calls

This is the combined second and third step of refactoring indirect
function calls through tables with index > 0 to work without runtime
calls.

The first CL introduces the WasmIndirectFunctionTable heap object. For
a table of type anyfunc within a WebAssembly instance,
WasmIndirectFunctionTable stores the size, the signature id's, the
call targets, and the reference parameters for that table. I used the
names that are already used for the matching fields of the
WasmInstanceObject.

The second CL expands the IndirectFunctionTableEntry to work also on
WasmIndirectFunctionTable objects. All changes to a function table go
through this class.

The third CL introduces uses of the WasmIndirectFunctionTable. In this
CL I change the code generation in TurboFan to replace runime calls with
direct accesses to the new WasmIndirectFunctionTable. Additionally I
extended the initialization of WasmIndirectFunctionTable, and also
implement Table.grow.

R=mstarzinger@chromium.org

Bug: v8:7581
Change-Id: Ic7615c0138562d27897683358ddc0943add1acfe
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1684186
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62515}
This commit is contained in:
Andreas Haas 2019-07-03 17:11:12 +02:00 committed by Commit Bot
parent d3472765d8
commit 02f18b2df1
10 changed files with 262 additions and 300 deletions

View File

@ -2854,25 +2854,69 @@ Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args, Node*** rets,
Node* WasmGraphBuilder::CallIndirect(uint32_t table_index, uint32_t sig_index,
Node** args, Node*** rets,
wasm::WasmCodePosition position) {
if (table_index == 0) {
return BuildIndirectCall(sig_index, args, rets, position, kCallContinues);
}
return BuildIndirectCall(table_index, sig_index, args, rets, position,
kCallContinues);
}
Node* WasmGraphBuilder::BuildIndirectCall(uint32_t sig_index, Node** args,
void WasmGraphBuilder::LoadIndirectFunctionTable(uint32_t table_index,
Node** ift_size,
Node** ift_sig_ids,
Node** ift_targets,
Node** ift_instances) {
if (table_index == 0) {
*ift_size =
LOAD_INSTANCE_FIELD(IndirectFunctionTableSize, MachineType::Uint32());
*ift_sig_ids = LOAD_INSTANCE_FIELD(IndirectFunctionTableSigIds,
MachineType::Pointer());
*ift_targets = LOAD_INSTANCE_FIELD(IndirectFunctionTableTargets,
MachineType::Pointer());
*ift_instances = LOAD_INSTANCE_FIELD(
IndirectFunctionTableRefs, MachineType::TypeCompressedTaggedPointer());
return;
}
Node* ift_tables = LOAD_INSTANCE_FIELD(
IndirectFunctionTables, MachineType::TypeCompressedTaggedPointer());
Node* ift_table = LOAD_FIXED_ARRAY_SLOT_ANY(ift_tables, table_index);
*ift_size = LOAD_RAW(
ift_table,
wasm::ObjectAccess::ToTagged(WasmIndirectFunctionTable::kSizeOffset),
MachineType::Int32());
*ift_sig_ids = LOAD_RAW(
ift_table,
wasm::ObjectAccess::ToTagged(WasmIndirectFunctionTable::kSigIdsOffset),
MachineType::Pointer());
*ift_targets = LOAD_RAW(
ift_table,
wasm::ObjectAccess::ToTagged(WasmIndirectFunctionTable::kTargetsOffset),
MachineType::Pointer());
*ift_instances = LOAD_RAW(
ift_table,
wasm::ObjectAccess::ToTagged(WasmIndirectFunctionTable::kRefsOffset),
MachineType::TypeCompressedTaggedPointer());
}
Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index,
uint32_t sig_index, Node** args,
Node*** rets,
wasm::WasmCodePosition position,
IsReturnCall continuation) {
DCHECK_NOT_NULL(args[0]);
DCHECK_NOT_NULL(env_);
// Assume only one table for now.
wasm::FunctionSig* sig = env_->module->signatures[sig_index];
// First we have to load the table.
Node* ift_size;
Node* ift_sig_ids;
Node* ift_targets;
Node* ift_instances;
LoadIndirectFunctionTable(table_index, &ift_size, &ift_sig_ids, &ift_targets,
&ift_instances);
Node* ift_size =
LOAD_INSTANCE_FIELD(IndirectFunctionTableSize, MachineType::Uint32());
wasm::FunctionSig* sig = env_->module->signatures[sig_index];
MachineOperatorBuilder* machine = mcgraph()->machine();
Node* key = args[0];
@ -2895,9 +2939,6 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t sig_index, Node** args,
}
// Load signature from the table and check.
Node* ift_sig_ids =
LOAD_INSTANCE_FIELD(IndirectFunctionTableSigIds, MachineType::Pointer());
int32_t expected_sig_id = env_->module->signature_ids[sig_index];
Node* int32_scaled_key = Uint32ToUintptr(
graph()->NewNode(machine->Word32Shl(), key, Int32Constant(2)));
@ -2910,11 +2951,6 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t sig_index, Node** args,
TrapIfFalse(wasm::kTrapFuncSigMismatch, sig_match, position);
Node* ift_targets =
LOAD_INSTANCE_FIELD(IndirectFunctionTableTargets, MachineType::Pointer());
Node* ift_instances = LOAD_INSTANCE_FIELD(
IndirectFunctionTableRefs, MachineType::TypeCompressedTaggedPointer());
Node* tagged_scaled_key;
if (kTaggedSize == kInt32Size) {
tagged_scaled_key = int32_scaled_key;
@ -2956,48 +2992,6 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t sig_index, Node** args,
}
}
Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index,
uint32_t sig_index, Node** args,
Node*** rets,
wasm::WasmCodePosition position,
IsReturnCall continuation) {
DCHECK_NOT_NULL(args[0]);
Node* entry_index = args[0];
DCHECK_NOT_NULL(env_);
BoundsCheckTable(table_index, entry_index, position, wasm::kTrapFuncInvalid,
nullptr);
DCHECK(Smi::IsValid(table_index));
DCHECK(Smi::IsValid(sig_index));
Node* runtime_args[]{
graph()->NewNode(mcgraph()->common()->NumberConstant(table_index)),
BuildChangeUint31ToSmi(entry_index),
graph()->NewNode(mcgraph()->common()->NumberConstant(sig_index))};
Node* target_instance = BuildCallToRuntime(
Runtime::kWasmIndirectCallCheckSignatureAndGetTargetInstance,
runtime_args, arraysize(runtime_args));
// We reuse the runtime_args array here, even though we only need the first
// two arguments.
Node* call_target = BuildCallToRuntime(
Runtime::kWasmIndirectCallGetTargetAddress, runtime_args, 2);
wasm::FunctionSig* sig = env_->module->signatures[sig_index];
args[0] = call_target;
const UseRetpoline use_retpoline =
untrusted_code_mitigations_ ? kRetpoline : kNoRetpoline;
switch (continuation) {
case kCallContinues:
return BuildWasmCall(sig, args, rets, position, target_instance,
use_retpoline);
case kReturnCall:
return BuildWasmReturnCall(sig, args, position, target_instance,
use_retpoline);
}
}
Node* WasmGraphBuilder::ReturnCall(uint32_t index, Node** args,
wasm::WasmCodePosition position) {
DCHECK_NULL(args[0]);
@ -3020,9 +3014,6 @@ Node* WasmGraphBuilder::ReturnCall(uint32_t index, Node** args,
Node* WasmGraphBuilder::ReturnCallIndirect(uint32_t table_index,
uint32_t sig_index, Node** args,
wasm::WasmCodePosition position) {
if (table_index == 0) {
return BuildIndirectCall(sig_index, args, nullptr, position, kReturnCall);
}
return BuildIndirectCall(table_index, sig_index, args, nullptr, position,
kReturnCall);
}

View File

@ -490,10 +490,10 @@ class WasmGraphBuilder {
Node* BuildCallNode(wasm::FunctionSig* sig, Node** args,
wasm::WasmCodePosition position, Node* instance_node,
const Operator* op);
// Special implementation for CallIndirect for table 0.
Node* BuildIndirectCall(uint32_t sig_index, Node** args, Node*** rets,
wasm::WasmCodePosition position,
IsReturnCall continuation);
// Helper function for {BuildIndirectCall}.
void LoadIndirectFunctionTable(uint32_t table_index, Node** ift_size,
Node** ift_sig_ids, Node** ift_targets,
Node** ift_instances);
Node* BuildIndirectCall(uint32_t table_index, uint32_t sig_index, Node** args,
Node*** rets, wasm::WasmCodePosition position,
IsReturnCall continuation);

View File

@ -479,122 +479,6 @@ RUNTIME_FUNCTION(Runtime_WasmFunctionTableSet) {
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_WasmIndirectCallCheckSignatureAndGetTargetInstance) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
auto instance =
Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
CONVERT_UINT32_ARG_CHECKED(table_index, 0);
CONVERT_UINT32_ARG_CHECKED(entry_index, 1);
CONVERT_UINT32_ARG_CHECKED(sig_index, 2);
DCHECK(isolate->context().is_null());
isolate->set_context(instance->native_context());
DCHECK_LT(table_index, instance->tables().length());
auto table_obj = handle(
WasmTableObject::cast(instance->tables().get(table_index)), isolate);
// This check is already done in generated code.
DCHECK(WasmTableObject::IsInBounds(isolate, table_obj, entry_index));
bool is_valid;
bool is_null;
MaybeHandle<WasmInstanceObject> maybe_target_instance;
int function_index;
MaybeHandle<WasmJSFunction> maybe_js_function;
WasmTableObject::GetFunctionTableEntry(
isolate, table_obj, entry_index, &is_valid, &is_null,
&maybe_target_instance, &function_index, &maybe_js_function);
CHECK(is_valid);
if (is_null) {
// We throw a signature mismatch trap to be in sync with the generated
// code. There we do a signature check instead of a null-check. Trap
// reasons are not defined in the spec. Otherwise, a null-check is
// performed before a signature, according to the spec.
return ThrowWasmError(isolate, MessageTemplate::kWasmTrapFuncSigMismatch);
}
// TODO(7742): Test and implement indirect calls for non-zero tables.
CHECK(maybe_js_function.is_null());
// Now we do the signature check.
Handle<WasmInstanceObject> target_instance =
maybe_target_instance.ToHandleChecked();
const wasm::WasmModule* target_module =
target_instance->module_object().native_module()->module();
wasm::FunctionSig* target_sig = target_module->functions[function_index].sig;
auto target_sig_id = instance->module()->signature_map.Find(*target_sig);
uint32_t expected_sig_id = instance->module()->signature_ids[sig_index];
if (expected_sig_id != static_cast<uint32_t>(target_sig_id)) {
return ThrowWasmError(isolate, MessageTemplate::kWasmTrapFuncSigMismatch);
}
if (function_index <
static_cast<int>(target_instance->module()->num_imported_functions)) {
// The function in the target instance was imported. Use its imports table,
// which contains a tuple needed by the import wrapper.
ImportedFunctionEntry entry(target_instance, function_index);
return entry.object_ref();
}
return *target_instance;
}
RUNTIME_FUNCTION(Runtime_WasmIndirectCallGetTargetAddress) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
auto instance =
Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
CONVERT_UINT32_ARG_CHECKED(table_index, 0);
CONVERT_UINT32_ARG_CHECKED(entry_index, 1);
DCHECK_LT(table_index, instance->tables().length());
auto table_obj = handle(
WasmTableObject::cast(instance->tables().get(table_index)), isolate);
DCHECK(WasmTableObject::IsInBounds(isolate, table_obj, entry_index));
bool is_valid;
bool is_null;
MaybeHandle<WasmInstanceObject> maybe_target_instance;
int function_index;
MaybeHandle<WasmJSFunction> maybe_js_function;
WasmTableObject::GetFunctionTableEntry(
isolate, table_obj, entry_index, &is_valid, &is_null,
&maybe_target_instance, &function_index, &maybe_js_function);
CHECK(is_valid);
// The null-check should already have been done in
// Runtime_WasmIndirectCallCheckSignatureAndGetTargetInstance. That runtime
// function should always be called first.
CHECK(!is_null);
// TODO(7742): Test and implement indirect calls for non-zero tables.
CHECK(maybe_js_function.is_null());
Handle<WasmInstanceObject> target_instance =
maybe_target_instance.ToHandleChecked();
Address call_target = 0;
if (function_index <
static_cast<int>(target_instance->module()->num_imported_functions)) {
// The function in the target instance was imported. Use its imports table,
// which contains a tuple needed by the import wrapper.
ImportedFunctionEntry entry(target_instance, function_index);
call_target = entry.target();
} else {
// The function in the target instance was not imported.
call_target = target_instance->GetCallTarget(function_index);
}
// The return value is an address and not a SMI. However, the address is
// always aligned, and a SMI uses the same space as {Address}.
CHECK(HAS_SMI_TAG(call_target));
return Smi(call_target);
}
RUNTIME_FUNCTION(Runtime_WasmTableInit) {
HandleScope scope(isolate);
DCHECK_EQ(5, args.length());

View File

@ -528,29 +528,27 @@ namespace internal {
F(TypedArraySet, 2, 1) \
F(TypedArraySortFast, 1, 1)
#define FOR_EACH_INTRINSIC_WASM(F, I) \
F(ThrowWasmError, 1, 1) \
F(ThrowWasmStackOverflow, 0, 1) \
F(WasmI32AtomicWait, 4, 1) \
F(WasmI64AtomicWait, 5, 1) \
F(WasmAtomicNotify, 3, 1) \
F(WasmExceptionGetValues, 1, 1) \
F(WasmExceptionGetTag, 1, 1) \
F(WasmMemoryGrow, 2, 1) \
F(WasmRunInterpreter, 2, 1) \
F(WasmStackGuard, 0, 1) \
F(WasmThrowCreate, 2, 1) \
F(WasmThrowTypeError, 0, 1) \
F(WasmRefFunc, 1, 1) \
F(WasmFunctionTableGet, 3, 1) \
F(WasmFunctionTableSet, 4, 1) \
F(WasmTableInit, 5, 1) \
F(WasmTableCopy, 5, 1) \
F(WasmTableGrow, 3, 1) \
F(WasmTableFill, 4, 1) \
F(WasmIndirectCallCheckSignatureAndGetTargetInstance, 3, 1) \
F(WasmIndirectCallGetTargetAddress, 2, 1) \
F(WasmIsValidAnyFuncValue, 1, 1) \
#define FOR_EACH_INTRINSIC_WASM(F, I) \
F(ThrowWasmError, 1, 1) \
F(ThrowWasmStackOverflow, 0, 1) \
F(WasmI32AtomicWait, 4, 1) \
F(WasmI64AtomicWait, 5, 1) \
F(WasmAtomicNotify, 3, 1) \
F(WasmExceptionGetValues, 1, 1) \
F(WasmExceptionGetTag, 1, 1) \
F(WasmMemoryGrow, 2, 1) \
F(WasmRunInterpreter, 2, 1) \
F(WasmStackGuard, 0, 1) \
F(WasmThrowCreate, 2, 1) \
F(WasmThrowTypeError, 0, 1) \
F(WasmRefFunc, 1, 1) \
F(WasmFunctionTableGet, 3, 1) \
F(WasmFunctionTableSet, 4, 1) \
F(WasmTableInit, 5, 1) \
F(WasmTableCopy, 5, 1) \
F(WasmTableGrow, 3, 1) \
F(WasmTableFill, 4, 1) \
F(WasmIsValidAnyFuncValue, 1, 1) \
F(WasmCompileLazy, 2, 1)
#define FOR_EACH_INTRINSIC_RETURN_PAIR_IMPL(F, I) \

View File

@ -178,7 +178,7 @@ class InstanceBuilder {
// Initialize imported tables of type anyfunc.
bool InitializeImportedIndirectFunctionTable(
Handle<WasmInstanceObject> instance, int import_index,
Handle<WasmInstanceObject> instance, int table_index, int import_index,
Handle<WasmTableObject> table_object);
// Process a single imported table.
@ -231,7 +231,7 @@ class InstanceBuilder {
// and globals.
void ProcessExports(Handle<WasmInstanceObject> instance);
void InitializeIndirectFunctionTable(Handle<WasmInstanceObject> instance);
void InitializeIndirectFunctionTables(Handle<WasmInstanceObject> instance);
void LoadTableSegments(Handle<WasmInstanceObject> instance);
@ -437,7 +437,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
// Initialize the indirect tables.
//--------------------------------------------------------------------------
if (table_count > 0) {
InitializeIndirectFunctionTable(instance);
InitializeIndirectFunctionTables(instance);
}
//--------------------------------------------------------------------------
@ -895,14 +895,12 @@ bool InstanceBuilder::ProcessImportedFunction(
}
bool InstanceBuilder::InitializeImportedIndirectFunctionTable(
Handle<WasmInstanceObject> instance, int import_index,
Handle<WasmInstanceObject> instance, int table_index, int import_index,
Handle<WasmTableObject> table_object) {
int imported_table_size = table_object->entries().length();
// Allocate a new dispatch table.
if (!instance->has_indirect_function_table()) {
WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
instance, imported_table_size);
}
WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
instance, table_index, imported_table_size);
// Initialize the dispatch table with the (foreign) JS functions
// that are already in the table.
for (int i = 0; i < imported_table_size; ++i) {
@ -922,8 +920,8 @@ bool InstanceBuilder::InitializeImportedIndirectFunctionTable(
if (is_null) continue;
Handle<WasmJSFunction> js_function;
if (maybe_js_function.ToHandle(&js_function)) {
WasmInstanceObject::ImportWasmJSFunctionIntoTable(isolate_, instance, i,
js_function);
WasmInstanceObject::ImportWasmJSFunctionIntoTable(
isolate_, instance, table_index, i, js_function);
continue;
}
@ -937,7 +935,7 @@ bool InstanceBuilder::InitializeImportedIndirectFunctionTable(
// Look up the signature's canonical id. If there is no canonical
// id, then the signature does not appear at all in this module,
// so putting {-1} in the table will cause checks to always fail.
IndirectFunctionTableEntry(instance, i)
IndirectFunctionTableEntry(instance, table_index, i)
.Set(module_->signature_map.Find(*sig), target_instance,
function_index);
}
@ -992,10 +990,9 @@ bool InstanceBuilder::ProcessImportedTable(Handle<WasmInstanceObject> instance,
return false;
}
// The indirect function table only exists for table 0.
if (table.type == kWasmAnyFunc && table_index == 0 &&
!InitializeImportedIndirectFunctionTable(instance, import_index,
table_object)) {
if (table.type == kWasmAnyFunc &&
!InitializeImportedIndirectFunctionTable(instance, table_index,
import_index, table_object)) {
return false;
}
@ -1609,15 +1606,15 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
}
}
void InstanceBuilder::InitializeIndirectFunctionTable(
void InstanceBuilder::InitializeIndirectFunctionTables(
Handle<WasmInstanceObject> instance) {
DCHECK_GT(module_->tables.size(), 0);
for (int i = 0; i < static_cast<int>(module_->tables.size()); ++i) {
const WasmTable& table = module_->tables[i];
const WasmTable& table = module_->tables[0];
if (!instance->has_indirect_function_table() && table.type == kWasmAnyFunc) {
WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
instance, table.initial_size);
if (table.type == kWasmAnyFunc) {
WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
instance, i, table.initial_size);
}
}
}
@ -1640,7 +1637,9 @@ bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance,
if (func_index == WasmElemSegment::kNullIndex) {
if (table_object->type() == kWasmAnyFunc) {
IndirectFunctionTableEntry(instance, entry_index).clear();
IndirectFunctionTableEntry(instance, elem_segment.table_index,
entry_index)
.clear();
}
WasmTableObject::Set(isolate, table_object, entry_index,
isolate->factory()->null_value());
@ -1649,13 +1648,11 @@ bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance,
const WasmFunction* function = &module->functions[func_index];
// Update the local dispatch table first if necessary. We only have to
// update the dispatch table if the first table of the instance is changed.
// For all other tables, function calls do not use a dispatch table at
// the moment.
if (elem_segment.table_index == 0 && table_object->type() == kWasmAnyFunc) {
// Update the local dispatch table first if necessary.
if (table_object->type() == kWasmAnyFunc) {
uint32_t sig_id = module->signature_ids[function->sig_index];
IndirectFunctionTableEntry(instance, entry_index)
IndirectFunctionTableEntry(instance, elem_segment.table_index,
entry_index)
.Set(sig_id, instance, func_index);
}

View File

@ -3687,7 +3687,7 @@ class ThreadImpl {
return {ExternalCallResult::INVALID_FUNC};
}
IndirectFunctionTableEntry entry(instance_object_, entry_index);
IndirectFunctionTableEntry entry(instance_object_, 0, entry_index);
// Signature check.
if (entry.sig_id() != static_cast<int32_t>(expected_sig_id)) {
return {ExternalCallResult::SIGNATURE_MISMATCH};

View File

@ -267,10 +267,6 @@ ACCESSORS(WasmInstanceObject, centry_stub, Code, kCEntryStubOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, wasm_exported_functions, FixedArray,
kWasmExportedFunctionsOffset)
inline bool WasmInstanceObject::has_indirect_function_table() {
return indirect_function_table_sig_ids() != nullptr;
}
void WasmInstanceObject::clear_padding() {
if (FIELD_SIZE(kOptionalPaddingOffset) != 0) {
DCHECK_EQ(4, FIELD_SIZE(kOptionalPaddingOffset));
@ -280,10 +276,29 @@ void WasmInstanceObject::clear_padding() {
}
IndirectFunctionTableEntry::IndirectFunctionTableEntry(
Handle<WasmInstanceObject> instance, int index)
: instance_(instance), index_(index) {
DCHECK_GE(index, 0);
DCHECK_LT(index, instance->indirect_function_table_size());
Handle<WasmInstanceObject> instance, int table_index, int entry_index)
: instance_(table_index == 0 ? instance
: Handle<WasmInstanceObject>::null()),
table_(table_index != 0
? handle(WasmIndirectFunctionTable::cast(
instance->indirect_function_tables().get(
table_index)),
instance->GetIsolate())
: Handle<WasmIndirectFunctionTable>::null()),
index_(entry_index) {
DCHECK_GE(entry_index, 0);
DCHECK_LT(entry_index, table_index == 0
? instance->indirect_function_table_size()
: table_->size());
}
IndirectFunctionTableEntry::IndirectFunctionTableEntry(
Handle<WasmIndirectFunctionTable> table, int entry_index)
: instance_(Handle<WasmInstanceObject>::null()),
table_(table),
index_(entry_index) {
DCHECK_GE(entry_index, 0);
DCHECK_LT(entry_index, table_->size());
}
ImportedFunctionEntry::ImportedFunctionEntry(

View File

@ -139,7 +139,9 @@ class WasmInstanceNativeAllocations {
instance->set_indirect_function_table_refs(*new_refs);
for (uint32_t j = old_size; j < new_size; j++) {
IndirectFunctionTableEntry(instance, static_cast<int>(j)).clear();
// {WasmInstanceNativeAllocations} only manages the memory of table 0.
// Therefore we pass the {table_index} as a constant here.
IndirectFunctionTableEntry(instance, 0, static_cast<int>(j)).clear();
}
}
uint32_t* indirect_function_table_sig_ids_ = nullptr;
@ -867,15 +869,14 @@ int WasmTableObject::Grow(Isolate* isolate, Handle<WasmTableObject> table,
i += kDispatchTableNumElements) {
int table_index =
Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value();
if (table_index > 0) {
continue;
}
// For Table 0 we have to update the indirect function table.
Handle<WasmInstanceObject> instance(
WasmInstanceObject::cast(dispatch_tables->get(i)), isolate);
DCHECK_EQ(old_size, instance->indirect_function_table_size());
WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(instance,
new_size);
DCHECK_EQ(old_size, WasmInstanceObject::IndirectFunctionTableSize(
isolate, instance, table_index));
WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
instance, table_index, new_size);
}
for (uint32_t entry = old_size; entry < new_size; ++entry) {
@ -1011,11 +1012,6 @@ void WasmTableObject::UpdateDispatchTables(
i += kDispatchTableNumElements) {
int table_index =
Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value();
if (table_index > 0) {
// Only table 0 has a dispatch table in the instance at the moment.
// TODO(ahaas): Introduce dispatch tables for the other tables as well.
continue;
}
Handle<WasmInstanceObject> instance(
WasmInstanceObject::cast(
dispatch_tables->get(i + kDispatchTableInstanceOffset)),
@ -1023,7 +1019,7 @@ void WasmTableObject::UpdateDispatchTables(
// Note that {SignatureMap::Find} may return {-1} if the signature is
// not found; it will simply never match any check.
auto sig_id = instance->module()->signature_map.Find(*sig);
IndirectFunctionTableEntry(instance, entry_index)
IndirectFunctionTableEntry(instance, table_index, entry_index)
.Set(sig_id, target_instance, target_func_index);
}
}
@ -1041,17 +1037,12 @@ void WasmTableObject::UpdateDispatchTables(Isolate* isolate,
i += kDispatchTableNumElements) {
int table_index =
Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value();
if (table_index > 0) {
// Only table 0 has a dispatch table in the instance at the moment.
// TODO(ahaas): Introduce dispatch tables for the other tables as well.
continue;
}
Handle<WasmInstanceObject> instance(
WasmInstanceObject::cast(
dispatch_tables->get(i + kDispatchTableInstanceOffset)),
isolate);
WasmInstanceObject::ImportWasmJSFunctionIntoTable(isolate, instance,
entry_index, function);
WasmInstanceObject::ImportWasmJSFunctionIntoTable(
isolate, instance, table_index, entry_index, function);
}
}
@ -1085,11 +1076,6 @@ void WasmTableObject::UpdateDispatchTables(
i += kDispatchTableNumElements) {
int table_index =
Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value();
if (table_index > 0) {
// Only table 0 has a dispatch table in the instance at the moment.
// TODO(ahaas): Introduce dispatch tables for the other tables as well.
continue;
}
Handle<WasmInstanceObject> instance(
WasmInstanceObject::cast(
dispatch_tables->get(i + kDispatchTableInstanceOffset)),
@ -1110,7 +1096,7 @@ void WasmTableObject::UpdateDispatchTables(
// Note that {SignatureMap::Find} may return {-1} if the signature is
// not found; it will simply never match any check.
auto sig_id = instance->module()->signature_map.Find(sig);
IndirectFunctionTableEntry(instance, entry_index)
IndirectFunctionTableEntry(instance, table_index, entry_index)
.Set(sig_id, wasm_code->instruction_start(), *tuple);
}
}
@ -1124,16 +1110,13 @@ void WasmTableObject::ClearDispatchTables(Isolate* isolate,
i += kDispatchTableNumElements) {
int table_index =
Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value();
if (table_index > 0) {
// Only table 0 has a dispatch table in the instance at the moment.
continue;
}
Handle<WasmInstanceObject> target_instance(
WasmInstanceObject::cast(
dispatch_tables->get(i + kDispatchTableInstanceOffset)),
isolate);
DCHECK_LT(index, target_instance->indirect_function_table_size());
IndirectFunctionTableEntry(target_instance, index).clear();
DCHECK_LT(index, WasmInstanceObject::IndirectFunctionTableSize(
isolate, target_instance, table_index));
IndirectFunctionTableEntry(target_instance, table_index, index).clear();
}
}
@ -1196,6 +1179,17 @@ class IftNativeAllocations {
return size * (sizeof(Address) + sizeof(uint32_t));
}
void resize(Handle<WasmIndirectFunctionTable> table, uint32_t new_size) {
DCHECK_GE(new_size, sig_ids_.size());
DCHECK_EQ(this, Managed<IftNativeAllocations>::cast(
table->managed_native_allocations())
.raw());
sig_ids_.resize(new_size);
targets_.resize(new_size);
table->set_sig_ids(sig_ids_.data());
table->set_targets(targets_.data());
}
private:
std::vector<uint32_t> sig_ids_;
std::vector<Address> targets_;
@ -1212,9 +1206,32 @@ Handle<WasmIndirectFunctionTable> WasmIndirectFunctionTable::New(
auto native_allocations = Managed<IftNativeAllocations>::Allocate(
isolate, IftNativeAllocations::SizeInMemory(size), table, size);
table->set_managed_native_allocations(*native_allocations);
for (uint32_t i = 0; i < size; ++i) {
IndirectFunctionTableEntry(table, static_cast<int>(i)).clear();
}
return table;
}
void WasmIndirectFunctionTable::Resize(Isolate* isolate,
Handle<WasmIndirectFunctionTable> table,
uint32_t new_size) {
uint32_t old_size = table->size();
if (old_size >= new_size) return; // Nothing to do.
Managed<IftNativeAllocations>::cast(table->managed_native_allocations())
.raw()
->resize(table, new_size);
Handle<FixedArray> old_refs(table->refs(), isolate);
Handle<FixedArray> new_refs = isolate->factory()->CopyFixedArrayAndGrow(
old_refs, static_cast<int>(new_size - old_size));
table->set_refs(*new_refs);
table->set_size(new_size);
for (uint32_t i = old_size; i < new_size; ++i) {
IndirectFunctionTableEntry(table, static_cast<int>(i)).clear();
}
}
namespace {
bool AdjustBufferPermissions(Isolate* isolate, Handle<JSArrayBuffer> old_buffer,
size_t new_size) {
@ -1499,10 +1516,19 @@ MaybeHandle<WasmGlobalObject> WasmGlobalObject::New(
}
void IndirectFunctionTableEntry::clear() {
instance_->indirect_function_table_sig_ids()[index_] = -1;
instance_->indirect_function_table_targets()[index_] = 0;
instance_->indirect_function_table_refs().set(
index_, ReadOnlyRoots(instance_->GetIsolate()).undefined_value());
if (!instance_.is_null()) {
instance_->indirect_function_table_sig_ids()[index_] = -1;
instance_->indirect_function_table_targets()[index_] = 0;
instance_->indirect_function_table_refs().set(
index_, ReadOnlyRoots(instance_->GetIsolate()).undefined_value());
} else {
DCHECK(!table_.is_null());
table_->sig_ids()[index_] = -1;
table_->targets()[index_] = 0;
table_->refs().set(
index_,
ReadOnlyRoots(GetIsolateFromWritableObject(*table_)).undefined_value());
}
}
void IndirectFunctionTableEntry::Set(int sig_id,
@ -1533,21 +1559,34 @@ void IndirectFunctionTableEntry::Set(int sig_id,
void IndirectFunctionTableEntry::Set(int sig_id, Address call_target,
Object ref) {
instance_->indirect_function_table_sig_ids()[index_] = sig_id;
instance_->indirect_function_table_targets()[index_] = call_target;
instance_->indirect_function_table_refs().set(index_, ref);
if (!instance_.is_null()) {
instance_->indirect_function_table_sig_ids()[index_] = sig_id;
instance_->indirect_function_table_targets()[index_] = call_target;
instance_->indirect_function_table_refs().set(index_, ref);
} else {
DCHECK(!table_.is_null());
table_->sig_ids()[index_] = sig_id;
table_->targets()[index_] = call_target;
table_->refs().set(index_, ref);
}
}
Object IndirectFunctionTableEntry::object_ref() const {
return instance_->indirect_function_table_refs().get(index_);
return !instance_.is_null()
? instance_->indirect_function_table_refs().get(index_)
: table_->refs().get(index_);
}
int IndirectFunctionTableEntry::sig_id() const {
return instance_->indirect_function_table_sig_ids()[index_];
return !instance_.is_null()
? instance_->indirect_function_table_sig_ids()[index_]
: table_->sig_ids()[index_];
}
Address IndirectFunctionTableEntry::target() const {
return instance_->indirect_function_table_targets()[index_];
return !instance_.is_null()
? instance_->indirect_function_table_targets()[index_]
: table_->targets()[index_];
}
void IndirectFunctionTableEntry::CopyFrom(
@ -1608,11 +1647,21 @@ constexpr uint16_t WasmInstanceObject::kTaggedFieldOffsets[];
// static
bool WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
Handle<WasmInstanceObject> instance, uint32_t minimum_size) {
Handle<WasmInstanceObject> instance, int table_index,
uint32_t minimum_size) {
Isolate* isolate = instance->GetIsolate();
if (table_index > 0) {
DCHECK_LT(table_index, instance->indirect_function_tables().length());
auto table =
handle(WasmIndirectFunctionTable::cast(
instance->indirect_function_tables().get(table_index)),
isolate);
WasmIndirectFunctionTable::Resize(isolate, table, minimum_size);
return true;
}
uint32_t old_size = instance->indirect_function_table_size();
if (old_size >= minimum_size) return false; // Nothing to do.
Isolate* isolate = instance->GetIsolate();
HandleScope scope(isolate);
auto native_allocations = GetNativeAllocations(*instance);
native_allocations->resize_indirect_function_table(isolate, instance,
@ -1768,20 +1817,37 @@ Address WasmInstanceObject::GetCallTarget(uint32_t func_index) {
return native_module->GetCallTargetForFunction(func_index);
}
int WasmInstanceObject::IndirectFunctionTableSize(
Isolate* isolate, Handle<WasmInstanceObject> instance,
uint32_t table_index) {
if (table_index == 0) {
return instance->indirect_function_table_size();
}
auto table =
handle(WasmIndirectFunctionTable::cast(
instance->indirect_function_tables().get(table_index)),
isolate);
return table->size();
}
namespace {
void CopyTableEntriesImpl(Handle<WasmInstanceObject> instance, uint32_t dst,
uint32_t src, uint32_t count, bool copy_backward) {
DCHECK(IsInBounds(dst, count, instance->indirect_function_table_size()));
if (copy_backward) {
for (uint32_t i = count; i > 0; i--) {
auto to_entry = IndirectFunctionTableEntry(instance, dst + i - 1);
auto from_entry = IndirectFunctionTableEntry(instance, src + i - 1);
// TODO(ahaas): Use a table index here once table.copy supports multiple
// tables.
auto to_entry = IndirectFunctionTableEntry(instance, 0, dst + i - 1);
auto from_entry = IndirectFunctionTableEntry(instance, 0, src + i - 1);
to_entry.CopyFrom(from_entry);
}
} else {
for (uint32_t i = 0; i < count; i++) {
auto to_entry = IndirectFunctionTableEntry(instance, dst + i);
auto from_entry = IndirectFunctionTableEntry(instance, src + i);
// TODO(ahaas): Use a table index here once table.copy supports multiple
// tables.
auto to_entry = IndirectFunctionTableEntry(instance, 0, dst + i);
auto from_entry = IndirectFunctionTableEntry(instance, 0, src + i);
to_entry.CopyFrom(from_entry);
}
}
@ -1934,7 +2000,7 @@ void WasmInstanceObject::SetWasmExportedFunction(
// static
void WasmInstanceObject::ImportWasmJSFunctionIntoTable(
Isolate* isolate, Handle<WasmInstanceObject> instance, int table_index,
Handle<WasmJSFunction> js_function) {
int entry_index, Handle<WasmJSFunction> js_function) {
// Deserialize the signature encapsulated with the {WasmJSFunction}.
// Note that {SignatureMap::Find} may return {-1} if the signature is
// not found; it will simply never match any check.
@ -1974,7 +2040,7 @@ void WasmInstanceObject::ImportWasmJSFunctionIntoTable(
// Update the dispatch table.
Handle<Tuple2> tuple =
isolate->factory()->NewTuple2(instance, callable, AllocationType::kOld);
IndirectFunctionTableEntry(instance, table_index)
IndirectFunctionTableEntry(instance, table_index, entry_index)
.Set(sig_id, call_target, *tuple);
}

View File

@ -44,6 +44,7 @@ class WasmExportedFunction;
class WasmInstanceObject;
class WasmJSFunction;
class WasmModuleObject;
class WasmIndirectFunctionTable;
template <class CppType>
class Managed;
@ -61,7 +62,11 @@ class Managed;
// - target = entrypoint to Wasm code or import wrapper code
class IndirectFunctionTableEntry {
public:
inline IndirectFunctionTableEntry(Handle<WasmInstanceObject>, int index);
inline IndirectFunctionTableEntry(Handle<WasmInstanceObject>, int table_index,
int entry_index);
inline IndirectFunctionTableEntry(Handle<WasmIndirectFunctionTable> table,
int entry_index);
void clear();
V8_EXPORT_PRIVATE void Set(int sig_id,
@ -77,6 +82,7 @@ class IndirectFunctionTableEntry {
private:
Handle<WasmInstanceObject> const instance_;
Handle<WasmIndirectFunctionTable> const table_;
int const index_;
};
@ -552,9 +558,8 @@ class WasmInstanceObject : public JSObject {
V8_EXPORT_PRIVATE const wasm::WasmModule* module();
V8_EXPORT_PRIVATE static bool EnsureIndirectFunctionTableWithMinimumSize(
Handle<WasmInstanceObject> instance, uint32_t minimum_size);
bool has_indirect_function_table();
Handle<WasmInstanceObject> instance, int table_index,
uint32_t minimum_size);
V8_EXPORT_PRIVATE void SetRawMemory(byte* mem_start, size_t mem_size);
@ -568,6 +573,10 @@ class WasmInstanceObject : public JSObject {
Address GetCallTarget(uint32_t func_index);
static int IndirectFunctionTableSize(Isolate* isolate,
Handle<WasmInstanceObject> instance,
uint32_t table_index);
// Copies table entries. Returns {false} if the ranges are out-of-bounds.
static bool CopyTableEntries(Isolate* isolate,
Handle<WasmInstanceObject> instance,
@ -609,7 +618,7 @@ class WasmInstanceObject : public JSObject {
// {WasmJSFunction} is instance-independent and just wraps a JS callable.
static void ImportWasmJSFunctionIntoTable(Isolate* isolate,
Handle<WasmInstanceObject> instance,
int table_index,
int table_index, int entry_index,
Handle<WasmJSFunction> js_function);
OBJECT_CONSTRUCTORS(WasmInstanceObject, JSObject);
@ -734,6 +743,8 @@ class WasmIndirectFunctionTable : public Struct {
DECL_ACCESSORS(refs, FixedArray)
static Handle<WasmIndirectFunctionTable> New(Isolate* isolate, uint32_t size);
static void Resize(Isolate* isolate, Handle<WasmIndirectFunctionTable> table,
uint32_t new_size);
DECL_CAST(WasmIndirectFunctionTable)

View File

@ -172,7 +172,7 @@ void TestingModuleBuilder::AddIndirectFunctionTable(
table.has_maximum_size = true;
table.type = kWasmAnyFunc;
WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
instance_object(), table_size);
instance_object(), 0, table_size);
Handle<WasmTableObject> table_obj =
WasmTableObject::New(isolate_, table.type, table.initial_size,
table.has_maximum_size, table.maximum_size, nullptr);
@ -184,7 +184,7 @@ void TestingModuleBuilder::AddIndirectFunctionTable(
for (uint32_t i = 0; i < table_size; ++i) {
WasmFunction& function = test_module_->functions[function_indexes[i]];
int sig_id = test_module_->signature_map.Find(*function.sig);
IndirectFunctionTableEntry(instance, i)
IndirectFunctionTableEntry(instance, 0, i)
.Set(sig_id, instance, function.func_index);
WasmTableObject::SetFunctionTablePlaceholder(
isolate_, table_obj, i, instance_object_, function_indexes[i]);