[wasm] Use isorecursive canonical types for call_indirect
Currently, we canonicalize types for call_indirect by looking in the current module for a signature of the same shape. This is not enough as of wasm-gc. Instead, the canonical identifier representing a type has to be computed via isorecursive canonicalization. This change is implemented behind a flag for now. Future work: Also integrate export wrappers with isorecursive canonical types. We need to store wrappers in instance-independent storage. Drive-by: - Always emit type check for call_indirect. We did not emit a check only when typed-function-references was enabled, but not gc. This is not something that will be possible long-term. - Fix some wasm cctests. Bug: v8:7748 Change-Id: I7cced187009ac148c833dff5e720a8bb9a717e68 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3784600 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Cr-Commit-Position: refs/heads/main@{#82064}
This commit is contained in:
parent
637b591934
commit
4f0ef8c31d
@ -2840,29 +2840,32 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index,
|
||||
Node* in_bounds = gasm_->Uint32LessThan(key, ift_size);
|
||||
TrapIfFalse(wasm::kTrapTableOutOfBounds, in_bounds, position);
|
||||
|
||||
const wasm::ValueType table_type = env_->module->tables[table_index].type;
|
||||
// Check that the table entry is not null and that the type of the function is
|
||||
// **identical with** the function type declared at the call site (no
|
||||
// subtyping of functions is allowed).
|
||||
// Note: Since null entries are identified by having ift_sig_id (-1), we only
|
||||
// need one comparison.
|
||||
// TODO(9495): Change this if we should do full function subtyping instead.
|
||||
const bool needs_signature_check =
|
||||
FLAG_experimental_wasm_gc ||
|
||||
table_type.is_reference_to(wasm::HeapType::kFunc) ||
|
||||
table_type.is_nullable();
|
||||
if (needs_signature_check) {
|
||||
Node* int32_scaled_key = gasm_->BuildChangeUint32ToUintPtr(
|
||||
gasm_->Word32Shl(key, Int32Constant(2)));
|
||||
|
||||
Node* loaded_sig = gasm_->LoadFromObject(MachineType::Int32(), ift_sig_ids,
|
||||
int32_scaled_key);
|
||||
int32_t expected_sig_id = env_->module->canonicalized_type_ids[sig_index];
|
||||
Node* sig_match =
|
||||
gasm_->Word32Equal(loaded_sig, Int32Constant(expected_sig_id));
|
||||
TrapIfFalse(wasm::kTrapFuncSigMismatch, sig_match, position);
|
||||
Node* expected_sig_id;
|
||||
if (FLAG_wasm_type_canonicalization) {
|
||||
Node* isorecursive_canonical_types =
|
||||
LOAD_INSTANCE_FIELD(IsorecursiveCanonicalTypes, MachineType::Pointer());
|
||||
expected_sig_id = gasm_->LoadImmutable(
|
||||
MachineType::Uint32(), isorecursive_canonical_types,
|
||||
gasm_->IntPtrConstant(sig_index * kInt32Size));
|
||||
} else {
|
||||
expected_sig_id =
|
||||
Int32Constant(env_->module->per_module_canonical_type_ids[sig_index]);
|
||||
}
|
||||
|
||||
Node* int32_scaled_key = gasm_->BuildChangeUint32ToUintPtr(
|
||||
gasm_->Word32Shl(key, Int32Constant(2)));
|
||||
Node* loaded_sig = gasm_->LoadFromObject(MachineType::Int32(), ift_sig_ids,
|
||||
int32_scaled_key);
|
||||
Node* sig_match = gasm_->Word32Equal(loaded_sig, expected_sig_id);
|
||||
|
||||
TrapIfFalse(wasm::kTrapFuncSigMismatch, sig_match, position);
|
||||
|
||||
Node* key_intptr = gasm_->BuildChangeUint32ToUintPtr(key);
|
||||
|
||||
Node* target_instance = gasm_->LoadFixedArrayElement(
|
||||
|
@ -2013,6 +2013,8 @@ void WasmInstanceObject::WasmInstanceObjectPrint(std::ostream& os) {
|
||||
PRINT_WASM_INSTANCE_FIELD(indirect_function_table_size, +);
|
||||
PRINT_WASM_INSTANCE_FIELD(indirect_function_table_sig_ids, to_void_ptr);
|
||||
PRINT_WASM_INSTANCE_FIELD(indirect_function_table_targets, to_void_ptr);
|
||||
PRINT_WASM_INSTANCE_FIELD(isorecursive_canonical_types,
|
||||
reinterpret_cast<const uint32_t*>);
|
||||
PRINT_WASM_INSTANCE_FIELD(jump_table_start, to_void_ptr);
|
||||
PRINT_WASM_INSTANCE_FIELD(data_segment_starts, to_void_ptr);
|
||||
PRINT_WASM_INSTANCE_FIELD(data_segment_sizes, to_void_ptr);
|
||||
|
@ -7069,11 +7069,6 @@ class LiftoffCompiler {
|
||||
Label* invalid_func_label =
|
||||
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapTableOutOfBounds);
|
||||
|
||||
uint32_t canonical_sig_num =
|
||||
env_->module->canonicalized_type_ids[imm.sig_imm.index];
|
||||
DCHECK_GE(canonical_sig_num, 0);
|
||||
DCHECK_GE(kMaxInt, canonical_sig_num);
|
||||
|
||||
// Compare against table size stored in
|
||||
// {instance->indirect_function_table_size}.
|
||||
if (imm.table_imm.index == 0) {
|
||||
@ -7108,7 +7103,18 @@ class LiftoffCompiler {
|
||||
__ Load(LiftoffRegister(scratch), table, index, 0, LoadType::kI32Load);
|
||||
|
||||
// Compare against expected signature.
|
||||
__ LoadConstant(LiftoffRegister(tmp_const), WasmValue(canonical_sig_num));
|
||||
if (FLAG_wasm_type_canonicalization) {
|
||||
LOAD_INSTANCE_FIELD(tmp_const, IsorecursiveCanonicalTypes,
|
||||
kSystemPointerSize, pinned);
|
||||
__ Load(LiftoffRegister(tmp_const), tmp_const, no_reg,
|
||||
imm.sig_imm.index * kInt32Size, LoadType::kI32Load);
|
||||
} else {
|
||||
uint32_t canonical_sig_num =
|
||||
env_->module->per_module_canonical_type_ids[imm.sig_imm.index];
|
||||
DCHECK_GE(canonical_sig_num, 0);
|
||||
DCHECK_GE(kMaxInt, canonical_sig_num);
|
||||
__ LoadConstant(LiftoffRegister(tmp_const), WasmValue(canonical_sig_num));
|
||||
}
|
||||
|
||||
Label* sig_mismatch_label =
|
||||
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapFuncSigMismatch);
|
||||
|
@ -66,8 +66,9 @@ class TypeCanonicalizer {
|
||||
is_relative_supertype != other.is_relative_supertype;
|
||||
}
|
||||
|
||||
// TODO(manoskouk): Improve this.
|
||||
size_t hash_value() const {
|
||||
return base::hash_combine(type_def.kind,
|
||||
return base::hash_combine(base::hash_value(type_def.kind),
|
||||
base::hash_value(is_relative_supertype));
|
||||
}
|
||||
};
|
||||
|
@ -601,6 +601,10 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
|
||||
//--------------------------------------------------------------------------
|
||||
// Set up table storage space.
|
||||
//--------------------------------------------------------------------------
|
||||
if (FLAG_wasm_type_canonicalization) {
|
||||
instance->set_isorecursive_canonical_types(
|
||||
module_->isorecursive_canonical_type_ids.data());
|
||||
}
|
||||
int table_count = static_cast<int>(module_->tables.size());
|
||||
{
|
||||
for (int i = 0; i < table_count; i++) {
|
||||
@ -1196,18 +1200,20 @@ bool InstanceBuilder::InitializeImportedIndirectFunctionTable(
|
||||
|
||||
Handle<WasmInstanceObject> target_instance =
|
||||
maybe_target_instance.ToHandleChecked();
|
||||
const FunctionSig* sig = target_instance->module_object()
|
||||
.module()
|
||||
->functions[function_index]
|
||||
.sig;
|
||||
const WasmModule* target_module = target_instance->module_object().module();
|
||||
const WasmFunction& function = target_module->functions[function_index];
|
||||
|
||||
// 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.
|
||||
// Look up the signature's canonical id. In the case of
|
||||
// !FLAG_wasm_type_canonicalization, 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.
|
||||
FunctionTargetAndRef entry(target_instance, function_index);
|
||||
uint32_t canonicalized_sig_index =
|
||||
FLAG_wasm_type_canonicalization
|
||||
? target_module->isorecursive_canonical_type_ids[function.sig_index]
|
||||
: module_->signature_map.Find(*function.sig);
|
||||
instance->GetIndirectFunctionTable(isolate_, table_index)
|
||||
->Set(i, module_->signature_map.Find(*sig), entry.call_target(),
|
||||
*entry.ref());
|
||||
->Set(i, canonicalized_sig_index, entry.call_target(), *entry.ref());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -85,7 +85,8 @@ int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig,
|
||||
|
||||
int GetExportWrapperIndex(const WasmModule* module, uint32_t sig_index,
|
||||
bool is_import) {
|
||||
uint32_t canonical_sig_index = module->canonicalized_type_ids[sig_index];
|
||||
uint32_t canonical_sig_index =
|
||||
module->per_module_canonical_type_ids[sig_index];
|
||||
return GetExportWrapperIndexInternal(module, canonical_sig_index, is_import);
|
||||
}
|
||||
|
||||
@ -648,7 +649,8 @@ size_t EstimateStoredSize(const WasmModule* module) {
|
||||
(module->signature_zone ? module->signature_zone->allocation_size()
|
||||
: 0) +
|
||||
VectorSize(module->types) +
|
||||
VectorSize(module->canonicalized_type_ids) +
|
||||
VectorSize(module->per_module_canonical_type_ids) +
|
||||
VectorSize(module->isorecursive_canonical_type_ids) +
|
||||
VectorSize(module->functions) + VectorSize(module->data_segments) +
|
||||
VectorSize(module->tables) + VectorSize(module->import_table) +
|
||||
VectorSize(module->export_table) + VectorSize(module->tags) +
|
||||
|
@ -508,8 +508,8 @@ struct V8_EXPORT_PRIVATE WasmModule {
|
||||
uint32_t canonical_id = type.kind == TypeDefinition::kFunction
|
||||
? signature_map.FindOrInsert(*type.function_sig)
|
||||
: 0;
|
||||
canonicalized_type_ids.push_back(canonical_id);
|
||||
// Canonical type will be computed later.
|
||||
per_module_canonical_type_ids.push_back(canonical_id);
|
||||
// Isorecursive canonical type will be computed later.
|
||||
isorecursive_canonical_type_ids.push_back(kNoSuperType);
|
||||
}
|
||||
|
||||
@ -563,7 +563,7 @@ struct V8_EXPORT_PRIVATE WasmModule {
|
||||
std::vector<TypeDefinition> types; // by type index
|
||||
// TODO(7748): Unify the following two arrays.
|
||||
// Maps each type index to a canonical index for purposes of call_indirect.
|
||||
std::vector<uint32_t> canonicalized_type_ids;
|
||||
std::vector<uint32_t> per_module_canonical_type_ids;
|
||||
// Maps each type index to its global (cross-module) canonical index as per
|
||||
// isorecursive type canonicalization.
|
||||
std::vector<uint32_t> isorecursive_canonical_type_ids;
|
||||
|
@ -205,6 +205,8 @@ PRIMITIVE_ACCESSORS(WasmInstanceObject, old_allocation_limit_address, Address*,
|
||||
kOldAllocationLimitAddressOffset)
|
||||
PRIMITIVE_ACCESSORS(WasmInstanceObject, old_allocation_top_address, Address*,
|
||||
kOldAllocationTopAddressOffset)
|
||||
PRIMITIVE_ACCESSORS(WasmInstanceObject, isorecursive_canonical_types,
|
||||
const uint32_t*, kIsorecursiveCanonicalTypesOffset)
|
||||
PRIMITIVE_ACCESSORS(WasmInstanceObject, imported_function_targets, Address*,
|
||||
kImportedFunctionTargetsOffset)
|
||||
SANDBOXED_POINTER_ACCESSORS(WasmInstanceObject, globals_start, byte*,
|
||||
|
@ -481,6 +481,7 @@ void WasmTableObject::Fill(Isolate* isolate, Handle<WasmTableObject> table,
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void WasmTableObject::UpdateDispatchTables(Isolate* isolate,
|
||||
WasmTableObject table,
|
||||
int entry_index,
|
||||
@ -511,19 +512,22 @@ void WasmTableObject::UpdateDispatchTables(Isolate* isolate,
|
||||
WasmInstanceObject instance = WasmInstanceObject::cast(
|
||||
dispatch_tables.get(i + kDispatchTableInstanceOffset));
|
||||
const WasmModule* module = instance.module();
|
||||
// Try to avoid the signature map lookup by checking if the signature in
|
||||
// {module} at {original_sig_id} matches {func->sig}.
|
||||
int sig_id;
|
||||
// TODO(7748): wasm-gc signatures cannot be canonicalized this way because
|
||||
// references could wrongly be detected as identical.
|
||||
if (module->has_signature(original_sig_id) &&
|
||||
*module->signature(original_sig_id) == *func->sig) {
|
||||
sig_id = module->canonicalized_type_ids[original_sig_id];
|
||||
DCHECK_EQ(sig_id, module->signature_map.Find(*func->sig));
|
||||
if (FLAG_wasm_type_canonicalization) {
|
||||
sig_id = target_instance.module()
|
||||
->isorecursive_canonical_type_ids[original_sig_id];
|
||||
} else {
|
||||
// Note that {SignatureMap::Find} may return {-1} if the signature is
|
||||
// not found; it will simply never match any check.
|
||||
sig_id = module->signature_map.Find(*func->sig);
|
||||
// Try to avoid the signature map lookup by checking if the signature in
|
||||
// {module} at {original_sig_id} matches {func->sig}.
|
||||
if (module->has_signature(original_sig_id) &&
|
||||
*module->signature(original_sig_id) == *func->sig) {
|
||||
sig_id = module->per_module_canonical_type_ids[original_sig_id];
|
||||
DCHECK_EQ(sig_id, module->signature_map.Find(*func->sig));
|
||||
} else {
|
||||
// Note that {SignatureMap::Find} may return {-1} if the signature is
|
||||
// not found; it will simply never match any check.
|
||||
sig_id = module->signature_map.Find(*func->sig);
|
||||
}
|
||||
}
|
||||
WasmIndirectFunctionTable ift = WasmIndirectFunctionTable::cast(
|
||||
instance.indirect_function_tables().get(table_index));
|
||||
@ -531,6 +535,7 @@ void WasmTableObject::UpdateDispatchTables(Isolate* isolate,
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void WasmTableObject::UpdateDispatchTables(Isolate* isolate,
|
||||
Handle<WasmTableObject> table,
|
||||
int entry_index,
|
||||
@ -553,6 +558,7 @@ void WasmTableObject::UpdateDispatchTables(Isolate* isolate,
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void WasmTableObject::UpdateDispatchTables(
|
||||
Isolate* isolate, Handle<WasmTableObject> table, int entry_index,
|
||||
Handle<WasmCapiFunction> capi_function) {
|
||||
@ -608,6 +614,8 @@ void WasmTableObject::UpdateDispatchTables(
|
||||
}
|
||||
// Note that {SignatureMap::Find} may return {-1} if the signature is
|
||||
// not found; it will simply never match any check.
|
||||
// It is safe to use this even when FLAG_wasm_type_canonicalization, as the
|
||||
// C API cannot refer to user-defined types.
|
||||
auto sig_id = instance->module()->signature_map.Find(sig);
|
||||
instance->GetIndirectFunctionTable(isolate, table_index)
|
||||
->Set(entry_index, sig_id, wasm_code->instruction_start(),
|
||||
@ -1454,6 +1462,9 @@ void WasmInstanceObject::ImportWasmJSFunctionIntoTable(
|
||||
// not found; it will simply never match any check.
|
||||
Zone zone(isolate->allocator(), ZONE_NAME);
|
||||
const wasm::FunctionSig* sig = js_function->GetSignature(&zone);
|
||||
// It is safe to look up the signature this way even if
|
||||
// FLAG_wasm_type_canonicalization: Signatures created in the JS API cannot
|
||||
// contain user-defined (module-dependent) types.
|
||||
auto sig_id = instance->module()->signature_map.Find(*sig);
|
||||
|
||||
// Compile a wrapper for the target callable.
|
||||
@ -1502,9 +1513,14 @@ void WasmInstanceObject::ImportWasmJSFunctionIntoTable(
|
||||
wasm::Suspend suspend = js_function->GetSuspend();
|
||||
Handle<WasmApiFunctionRef> ref =
|
||||
isolate->factory()->NewWasmApiFunctionRef(callable, suspend, instance);
|
||||
uint32_t canonicalized_sig_id =
|
||||
FLAG_wasm_type_canonicalization && sig_id >= 0
|
||||
? instance->module()->isorecursive_canonical_type_ids[sig_id]
|
||||
: sig_id;
|
||||
|
||||
WasmIndirectFunctionTable::cast(
|
||||
instance->indirect_function_tables().get(table_index))
|
||||
.Set(entry_index, sig_id, call_target, *ref);
|
||||
.Set(entry_index, canonicalized_sig_id, call_target, *ref);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -352,6 +352,7 @@ class V8_EXPORT_PRIVATE WasmInstanceObject : public JSObject {
|
||||
DECL_PRIMITIVE_ACCESSORS(new_allocation_top_address, Address*)
|
||||
DECL_PRIMITIVE_ACCESSORS(old_allocation_limit_address, Address*)
|
||||
DECL_PRIMITIVE_ACCESSORS(old_allocation_top_address, Address*)
|
||||
DECL_PRIMITIVE_ACCESSORS(isorecursive_canonical_types, const uint32_t*)
|
||||
DECL_PRIMITIVE_ACCESSORS(imported_function_targets, Address*)
|
||||
DECL_SANDBOXED_POINTER_ACCESSORS(globals_start, byte*)
|
||||
DECL_PRIMITIVE_ACCESSORS(imported_mutable_globals, Address*)
|
||||
@ -386,6 +387,7 @@ class V8_EXPORT_PRIVATE WasmInstanceObject : public JSObject {
|
||||
V(kMemoryStartOffset, kSystemPointerSize) \
|
||||
V(kMemorySizeOffset, kSizetSize) \
|
||||
V(kStackLimitAddressOffset, kSystemPointerSize) \
|
||||
V(kIsorecursiveCanonicalTypesOffset, kSystemPointerSize) \
|
||||
V(kImportedFunctionTargetsOffset, kSystemPointerSize) \
|
||||
V(kIndirectFunctionTableTargetsOffset, kSystemPointerSize) \
|
||||
V(kIndirectFunctionTableSigIdsOffset, kSystemPointerSize) \
|
||||
|
@ -372,8 +372,6 @@ WASM_EXEC_TEST(TryCatchCallIndirect) {
|
||||
// Build a throwing helper function.
|
||||
WasmFunctionCompiler& throw_func = r.NewFunction(sigs.i_ii());
|
||||
BUILD(throw_func, WASM_THROW(except));
|
||||
byte sig_index = r.builder().AddSignature(sigs.i_ii());
|
||||
throw_func.SetSigIndex(0);
|
||||
|
||||
// Add an indirect function table.
|
||||
uint16_t indirect_function_table[] = {
|
||||
@ -382,16 +380,16 @@ WASM_EXEC_TEST(TryCatchCallIndirect) {
|
||||
arraysize(indirect_function_table));
|
||||
|
||||
// Build the main test function.
|
||||
BUILD(r,
|
||||
WASM_TRY_CATCH_T(
|
||||
kWasmI32,
|
||||
WASM_STMTS(WASM_I32V(kResult1),
|
||||
WASM_IF(WASM_I32_EQZ(WASM_LOCAL_GET(0)),
|
||||
WASM_STMTS(WASM_CALL_INDIRECT(
|
||||
sig_index, WASM_I32V(7),
|
||||
WASM_I32V(9), WASM_LOCAL_GET(0)),
|
||||
WASM_DROP))),
|
||||
WASM_I32V(kResult0), except));
|
||||
BUILD(r, WASM_TRY_CATCH_T(
|
||||
kWasmI32,
|
||||
WASM_STMTS(
|
||||
WASM_I32V(kResult1),
|
||||
WASM_IF(WASM_I32_EQZ(WASM_LOCAL_GET(0)),
|
||||
WASM_STMTS(WASM_CALL_INDIRECT(
|
||||
throw_func.sig_index(), WASM_I32V(7),
|
||||
WASM_I32V(9), WASM_LOCAL_GET(0)),
|
||||
WASM_DROP))),
|
||||
WASM_I32V(kResult0), except));
|
||||
|
||||
if (execution_tier != TestExecutionTier::kInterpreter) {
|
||||
// Need to call through JS to allow for creation of stack traces.
|
||||
@ -414,8 +412,6 @@ WASM_EXEC_TEST(TryCatchAllCallIndirect) {
|
||||
// Build a throwing helper function.
|
||||
WasmFunctionCompiler& throw_func = r.NewFunction(sigs.i_ii());
|
||||
BUILD(throw_func, WASM_THROW(except));
|
||||
byte sig_index = r.builder().AddSignature(sigs.i_ii());
|
||||
throw_func.SetSigIndex(0);
|
||||
|
||||
// Add an indirect function table.
|
||||
uint16_t indirect_function_table[] = {
|
||||
@ -424,16 +420,16 @@ WASM_EXEC_TEST(TryCatchAllCallIndirect) {
|
||||
arraysize(indirect_function_table));
|
||||
|
||||
// Build the main test function.
|
||||
BUILD(r,
|
||||
WASM_TRY_CATCH_ALL_T(
|
||||
kWasmI32,
|
||||
WASM_STMTS(WASM_I32V(kResult1),
|
||||
WASM_IF(WASM_I32_EQZ(WASM_LOCAL_GET(0)),
|
||||
WASM_STMTS(WASM_CALL_INDIRECT(
|
||||
sig_index, WASM_I32V(7),
|
||||
WASM_I32V(9), WASM_LOCAL_GET(0)),
|
||||
WASM_DROP))),
|
||||
WASM_I32V(kResult0)));
|
||||
BUILD(r, WASM_TRY_CATCH_ALL_T(
|
||||
kWasmI32,
|
||||
WASM_STMTS(
|
||||
WASM_I32V(kResult1),
|
||||
WASM_IF(WASM_I32_EQZ(WASM_LOCAL_GET(0)),
|
||||
WASM_STMTS(WASM_CALL_INDIRECT(
|
||||
throw_func.sig_index(), WASM_I32V(7),
|
||||
WASM_I32V(9), WASM_LOCAL_GET(0)),
|
||||
WASM_DROP))),
|
||||
WASM_I32V(kResult0)));
|
||||
|
||||
if (execution_tier != TestExecutionTier::kInterpreter) {
|
||||
// Need to call through JS to allow for creation of stack traces.
|
||||
|
@ -218,9 +218,6 @@ TEST(Run_Wasm_returnCallIndirectFactorial) {
|
||||
WasmRunner<uint32_t, uint32_t> r(TestExecutionTier::kInterpreter);
|
||||
|
||||
WasmFunctionCompiler& fact_aux_fn = r.NewFunction(sigs.i_ii(), "fact_aux");
|
||||
fact_aux_fn.SetSigIndex(0);
|
||||
|
||||
byte sig_index = r.builder().AddSignature(sigs.i_ii());
|
||||
|
||||
// Function table.
|
||||
uint16_t indirect_function_table[] = {
|
||||
@ -229,15 +226,16 @@ TEST(Run_Wasm_returnCallIndirectFactorial) {
|
||||
r.builder().AddIndirectFunctionTable(indirect_function_table,
|
||||
arraysize(indirect_function_table));
|
||||
|
||||
BUILD(r, WASM_RETURN_CALL_INDIRECT(sig_index, WASM_LOCAL_GET(0), WASM_I32V(1),
|
||||
WASM_ZERO));
|
||||
BUILD(r, WASM_RETURN_CALL_INDIRECT(fact_aux_fn.sig_index(), WASM_LOCAL_GET(0),
|
||||
WASM_I32V(1), WASM_ZERO));
|
||||
|
||||
BUILD(
|
||||
fact_aux_fn,
|
||||
WASM_IF_ELSE_I(
|
||||
WASM_I32_EQ(WASM_I32V(1), WASM_LOCAL_GET(0)), WASM_LOCAL_GET(1),
|
||||
WASM_RETURN_CALL_INDIRECT(
|
||||
sig_index, WASM_I32_SUB(WASM_LOCAL_GET(0), WASM_I32V(1)),
|
||||
fact_aux_fn.sig_index(),
|
||||
WASM_I32_SUB(WASM_LOCAL_GET(0), WASM_I32V(1)),
|
||||
WASM_I32_MUL(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1)), WASM_ZERO)));
|
||||
|
||||
uint32_t test_values[] = {1, 2, 5, 10, 20};
|
||||
|
@ -6,10 +6,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "src/api/api-inl.h"
|
||||
#include "src/base/overflowing-math.h"
|
||||
#include "src/base/platform/elapsed-timer.h"
|
||||
#include "src/codegen/assembler-inl.h"
|
||||
#include "src/utils/utils.h"
|
||||
#include "src/wasm/code-space-access.h"
|
||||
#include "src/wasm/wasm-opcodes-inl.h"
|
||||
@ -3147,20 +3144,12 @@ WASM_EXEC_TEST(CallIndirect_canonical) {
|
||||
|
||||
WasmFunctionCompiler& t1 = r.NewFunction(sigs.i_ii());
|
||||
BUILD(t1, WASM_I32_ADD(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1)));
|
||||
t1.SetSigIndex(0);
|
||||
|
||||
WasmFunctionCompiler& t2 = r.NewFunction(sigs.i_ii());
|
||||
BUILD(t2, WASM_I32_SUB(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1)));
|
||||
t2.SetSigIndex(1);
|
||||
|
||||
WasmFunctionCompiler& t3 = r.NewFunction(sigs.f_ff());
|
||||
BUILD(t3, WASM_F32_SUB(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1)));
|
||||
t3.SetSigIndex(2);
|
||||
|
||||
// Signature table.
|
||||
r.builder().AddSignature(sigs.i_ii());
|
||||
r.builder().AddSignature(sigs.i_ii());
|
||||
r.builder().AddSignature(sigs.f_ff());
|
||||
|
||||
// Function table.
|
||||
uint16_t i1 = static_cast<uint16_t>(t1.function_index());
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "src/base/optional.h"
|
||||
#include "src/codegen/assembler-inl.h"
|
||||
#include "src/compiler/pipeline.h"
|
||||
#include "src/diagnostics/code-tracer.h"
|
||||
#include "src/heap/heap-inl.h"
|
||||
#include "src/wasm/baseline/liftoff-compiler.h"
|
||||
@ -239,7 +240,11 @@ void TestingModuleBuilder::AddIndirectFunctionTable(
|
||||
if (function_indexes) {
|
||||
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);
|
||||
int sig_id =
|
||||
FLAG_wasm_type_canonicalization
|
||||
? test_module_
|
||||
->isorecursive_canonical_type_ids[function.sig_index]
|
||||
: test_module_->signature_map.Find(*function.sig);
|
||||
FunctionTargetAndRef entry(instance, function.func_index);
|
||||
instance->GetIndirectFunctionTable(isolate_, table_index)
|
||||
->Set(i, sig_id, entry.call_target(), *entry.ref());
|
||||
|
@ -14,16 +14,13 @@
|
||||
#include <memory>
|
||||
|
||||
#include "src/base/utils/random-number-generator.h"
|
||||
#include "src/codegen/optimized-compilation-info.h"
|
||||
#include "src/compiler/compiler-source-position-table.h"
|
||||
#include "src/compiler/graph-visualizer.h"
|
||||
#include "src/compiler/int64-lowering.h"
|
||||
#include "src/compiler/js-graph.h"
|
||||
#include "src/compiler/node.h"
|
||||
#include "src/compiler/pipeline.h"
|
||||
#include "src/compiler/wasm-compiler.h"
|
||||
#include "src/compiler/zone-stats.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
#include "src/wasm/canonical-types.h"
|
||||
#include "src/wasm/function-body-decoder.h"
|
||||
#include "src/wasm/local-decl-encoder.h"
|
||||
#include "src/wasm/wasm-code-manager.h"
|
||||
@ -139,8 +136,13 @@ class TestingModuleBuilder {
|
||||
|
||||
byte AddSignature(const FunctionSig* sig) {
|
||||
DCHECK_EQ(test_module_->types.size(),
|
||||
test_module_->canonicalized_type_ids.size());
|
||||
test_module_->per_module_canonical_type_ids.size());
|
||||
test_module_->add_signature(sig, kNoSuperType);
|
||||
if (FLAG_wasm_type_canonicalization) {
|
||||
GetTypeCanonicalizer()->AddRecursiveGroup(test_module_.get(), 1);
|
||||
instance_object_->set_isorecursive_canonical_types(
|
||||
test_module_->isorecursive_canonical_type_ids.data());
|
||||
}
|
||||
size_t size = test_module_->types.size();
|
||||
CHECK_GT(127, size);
|
||||
return static_cast<byte>(size - 1);
|
||||
@ -374,6 +376,7 @@ class WasmFunctionCompiler : public compiler::GraphAndBuilders {
|
||||
return descriptor_;
|
||||
}
|
||||
uint32_t function_index() { return function_->func_index; }
|
||||
uint32_t sig_index() { return function_->sig_index; }
|
||||
|
||||
void Build(const byte* start, const byte* end);
|
||||
|
||||
@ -574,13 +577,13 @@ inline WasmValue WasmValueInitializer(int16_t value) {
|
||||
template <typename ReturnType, typename... ParamTypes>
|
||||
class WasmRunner : public WasmRunnerBase {
|
||||
public:
|
||||
WasmRunner(TestExecutionTier execution_tier,
|
||||
ManuallyImportedJSFunction* maybe_import = nullptr,
|
||||
const char* main_fn_name = "main",
|
||||
RuntimeExceptionSupport runtime_exception_support =
|
||||
kNoRuntimeExceptionSupport,
|
||||
TestingModuleMemoryType mem_type = kMemory32,
|
||||
Isolate* isolate = nullptr)
|
||||
explicit WasmRunner(TestExecutionTier execution_tier,
|
||||
ManuallyImportedJSFunction* maybe_import = nullptr,
|
||||
const char* main_fn_name = "main",
|
||||
RuntimeExceptionSupport runtime_exception_support =
|
||||
kNoRuntimeExceptionSupport,
|
||||
TestingModuleMemoryType mem_type = kMemory32,
|
||||
Isolate* isolate = nullptr)
|
||||
: WasmRunnerBase(maybe_import, execution_tier, sizeof...(ParamTypes),
|
||||
runtime_exception_support, mem_type, isolate) {
|
||||
WasmFunctionCompiler& main_fn =
|
||||
|
@ -4102,9 +4102,14 @@ class WasmInterpreterInternals {
|
||||
CallResult CallIndirectFunction(uint32_t table_index, uint32_t entry_index,
|
||||
uint32_t sig_index) {
|
||||
HandleScope handle_scope(isolate_); // Avoid leaking handles.
|
||||
uint32_t expected_sig_id = module()->canonicalized_type_ids[sig_index];
|
||||
DCHECK_EQ(expected_sig_id,
|
||||
module()->signature_map.Find(*module()->signature(sig_index)));
|
||||
uint32_t expected_sig_id;
|
||||
if (FLAG_wasm_type_canonicalization) {
|
||||
expected_sig_id = module()->isorecursive_canonical_type_ids[sig_index];
|
||||
} else {
|
||||
expected_sig_id = module()->per_module_canonical_type_ids[sig_index];
|
||||
DCHECK_EQ(static_cast<int>(expected_sig_id),
|
||||
module()->signature_map.Find(*module()->signature(sig_index)));
|
||||
}
|
||||
|
||||
Handle<WasmIndirectFunctionTable> table =
|
||||
instance_object_->GetIndirectFunctionTable(isolate_, table_index);
|
||||
|
140
test/mjsunit/wasm/call_indirect.js
Normal file
140
test/mjsunit/wasm/call_indirect.js
Normal file
@ -0,0 +1,140 @@
|
||||
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --experimental-wasm-return-call --experimental-wasm-gc
|
||||
|
||||
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
|
||||
(function TestCallIndirectJSFunction() {
|
||||
print(arguments.callee.name);
|
||||
let js_function = function(a, b, c) { return c ? a : b; };
|
||||
|
||||
let test = function(is_tail_call) {
|
||||
const builder = new WasmModuleBuilder();
|
||||
|
||||
builder.addType(kSig_i_i);
|
||||
let sig = builder.addType(kSig_i_iii);
|
||||
|
||||
let callee = builder.addImport("m", "f", kSig_i_iii);
|
||||
|
||||
let table = builder.addTable(kWasmFuncRef, 10, 10);
|
||||
|
||||
builder.addActiveElementSegment(table, wasmI32Const(0), [callee]);
|
||||
|
||||
let left = -2;
|
||||
let right = 3;
|
||||
|
||||
builder.addFunction("main", kSig_i_i)
|
||||
.addBody([...wasmI32Const(left), ...wasmI32Const(right), kExprLocalGet, 0,
|
||||
...wasmI32Const(0),
|
||||
is_tail_call ? kExprReturnCallIndirect : kExprCallIndirect,
|
||||
sig, table.index])
|
||||
.exportFunc();
|
||||
|
||||
let instance = builder.instantiate({m: {f: js_function}});
|
||||
|
||||
assertEquals(left, instance.exports.main(1));
|
||||
assertEquals(right, instance.exports.main(0));
|
||||
}
|
||||
|
||||
test(true);
|
||||
test(false);
|
||||
})();
|
||||
|
||||
(function TestCallIndirectIsorecursiveTypeCanonicalization() {
|
||||
print(arguments.callee.name);
|
||||
|
||||
let exporting_instance = (function() {
|
||||
const builder = new WasmModuleBuilder();
|
||||
|
||||
builder.setSingletonRecGroups();
|
||||
|
||||
let struct_mistyped = builder.addStruct([]);
|
||||
|
||||
let struct = builder.addStruct([makeField(kWasmI32, true)]);
|
||||
|
||||
let sig_mistyped = builder.addType(
|
||||
makeSig([wasmRefNullType(struct_mistyped)], [kWasmI32]));
|
||||
let sig = builder.addType(makeSig([wasmRefNullType(struct)], [kWasmI32]));
|
||||
|
||||
builder.addFunction("export", sig)
|
||||
.addBody([kExprLocalGet, 0, kGCPrefix, kExprStructGet, struct, 0,
|
||||
...wasmI32Const(0), kExprI32GeS])
|
||||
.exportFunc();
|
||||
|
||||
builder.addFunction("export_mistyped", sig_mistyped)
|
||||
.addBody([...wasmI32Const(0)])
|
||||
.exportFunc();
|
||||
|
||||
return builder.instantiate();
|
||||
})();
|
||||
|
||||
const builder = new WasmModuleBuilder();
|
||||
|
||||
builder.setSingletonRecGroups();
|
||||
|
||||
// Have these in the reverse order than before.
|
||||
let struct = builder.addStruct([makeField(kWasmI32, true)]);
|
||||
let struct_mistyped = builder.addStruct([]);
|
||||
|
||||
let sig = builder.addType(makeSig([wasmRefNullType(struct)], [kWasmI32]));
|
||||
let sig_identical = builder.addType(
|
||||
makeSig([wasmRefNullType(struct)], [kWasmI32]));
|
||||
let sig_mistyped = builder.addType(
|
||||
makeSig([wasmRefNullType(struct_mistyped)], [kWasmI32]));
|
||||
|
||||
let imported = builder.addImport("m", "imp", sig);
|
||||
let imported_mistyped = builder.addImport("m", "imp_m", sig_mistyped);
|
||||
|
||||
let local1 = builder.addFunction("local1", sig)
|
||||
.addBody([kExprLocalGet, 0, kGCPrefix, kExprStructGet, struct, 0,
|
||||
...wasmI32Const(0), kExprI32LtS])
|
||||
.index;
|
||||
|
||||
let local2 = builder.addFunction("local2", sig_identical)
|
||||
.addBody([kExprLocalGet, 0, kGCPrefix, kExprStructGet, struct, 0,
|
||||
...wasmI32Const(0), kExprI32Eq])
|
||||
.index;
|
||||
|
||||
let mistyped = builder.addFunction("mistyped", kSig_i_i)
|
||||
.addBody([kExprLocalGet, 0])
|
||||
.index;
|
||||
|
||||
let table_size = 10;
|
||||
let table = builder.addTable(kWasmFuncRef, table_size, table_size);
|
||||
|
||||
builder.addActiveElementSegment(
|
||||
table.index, wasmI32Const(0),
|
||||
[[kExprRefFunc, imported], [kExprRefFunc, imported_mistyped],
|
||||
[kExprRefFunc, local1], [kExprRefFunc, local2], [kExprRefFunc, mistyped]],
|
||||
table.type);
|
||||
|
||||
// Params: Struct field, table index
|
||||
builder.addFunction("main", kSig_i_ii)
|
||||
.addBody([kExprLocalGet, 0, kGCPrefix, kExprStructNew, struct,
|
||||
kExprLocalGet, 1, kExprCallIndirect, sig, table.index])
|
||||
.exportFunc();
|
||||
|
||||
let importing_instance =
|
||||
builder.instantiate(
|
||||
{m: {imp: exporting_instance.exports.export,
|
||||
imp_m: exporting_instance.exports.export_mistyped}});
|
||||
|
||||
assertEquals(1, importing_instance.exports.main(10, imported))
|
||||
assertEquals(0, importing_instance.exports.main(-5, imported))
|
||||
assertEquals(0, importing_instance.exports.main(10, local1))
|
||||
assertEquals(1, importing_instance.exports.main(-5, local1))
|
||||
assertEquals(0, importing_instance.exports.main(10, local2))
|
||||
assertEquals(1, importing_instance.exports.main(0, local2))
|
||||
// Mistyped entries
|
||||
assertTraps(kTrapFuncSigMismatch,
|
||||
() => importing_instance.exports.main(10, mistyped));
|
||||
assertTraps(kTrapFuncSigMismatch,
|
||||
() => importing_instance.exports.main(10, imported_mistyped));
|
||||
// Null entry
|
||||
assertTraps(kTrapFuncSigMismatch,
|
||||
() => importing_instance.exports.main(10, table_size - 1));
|
||||
assertTraps(kTrapTableOutOfBounds,
|
||||
() => importing_instance.exports.main(10, table_size));
|
||||
})();
|
@ -1299,16 +1299,16 @@ TEST_F(WasmModuleVerifyTest, CanonicalTypeIds) {
|
||||
const WasmModule* module = result.value().get();
|
||||
|
||||
EXPECT_EQ(5u, module->types.size());
|
||||
EXPECT_EQ(5u, module->canonicalized_type_ids.size());
|
||||
EXPECT_EQ(5u, module->per_module_canonical_type_ids.size());
|
||||
EXPECT_EQ(2u, module->signature_map.size());
|
||||
|
||||
// No canonicalization for structs.
|
||||
EXPECT_EQ(0u, module->canonicalized_type_ids[0]);
|
||||
EXPECT_EQ(0u, module->canonicalized_type_ids[1]);
|
||||
EXPECT_EQ(1u, module->canonicalized_type_ids[2]);
|
||||
EXPECT_EQ(0u, module->canonicalized_type_ids[3]);
|
||||
EXPECT_EQ(0u, module->per_module_canonical_type_ids[0]);
|
||||
EXPECT_EQ(0u, module->per_module_canonical_type_ids[1]);
|
||||
EXPECT_EQ(1u, module->per_module_canonical_type_ids[2]);
|
||||
EXPECT_EQ(0u, module->per_module_canonical_type_ids[3]);
|
||||
// No canonicalization for arrays.
|
||||
EXPECT_EQ(0u, module->canonicalized_type_ids[4]);
|
||||
EXPECT_EQ(0u, module->per_module_canonical_type_ids[4]);
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, DataSegmentWithImmutableImportedGlobal) {
|
||||
|
Loading…
Reference in New Issue
Block a user