[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:
Manos Koukoutos 2022-07-29 06:29:12 +02:00 committed by V8 LUCI CQ
parent 637b591934
commit 4f0ef8c31d
18 changed files with 287 additions and 111 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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));
})();

View File

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