From 2f852102d9cdc891659866728d6b1420c6e32f42 Mon Sep 17 00:00:00 2001 From: Manos Koukoutos Date: Tue, 20 Dec 2022 11:37:35 +0100 Subject: [PATCH] [wasm-gc] Final types We add final types to wasm-gc. - We introduce a `kWasmSubtypeFinalCode` as an alternative to `kWasmSubtypeCode`. - Behind a flag, we interpret types behind this code as final, as well as types outside a subtype definition by default. - For final types, type checks for call_indirect and ref.test etc. are reduced to simple type identity checks. Bug: v8:7748 Change-Id: Iabf147b2a15f43abc4c7d1c582f460dbdc645d66 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4096478 Reviewed-by: Matthias Liedtke Reviewed-by: Clemens Backes Commit-Queue: Manos Koukoutos Cr-Commit-Position: refs/heads/main@{#84955} --- src/asmjs/asm-parser.cc | 2 +- src/compiler/wasm-compiler.cc | 7 +- src/compiler/wasm-gc-lowering.cc | 137 ++++++++++-------- src/flags/flag-definitions.h | 2 + src/wasm/baseline/liftoff-compiler.cc | 3 +- src/wasm/canonical-types.cc | 13 +- src/wasm/canonical-types.h | 2 + src/wasm/module-decoder-impl.h | 23 ++- src/wasm/wasm-constants.h | 1 + src/wasm/wasm-module-builder.cc | 30 ++-- src/wasm/wasm-module-builder.h | 10 +- src/wasm/wasm-module.h | 56 ++++--- src/wasm/wasm-subtyping.cc | 9 +- test/cctest/wasm/test-gc.cc | 13 +- test/cctest/wasm/wasm-run-utils.h | 3 +- test/fuzzer/wasm-compile.cc | 23 +-- .../debugger/wasm-gc-anyref-expected.txt | 6 +- test/mjsunit/wasm/call-ref.js | 9 +- test/mjsunit/wasm/gc-casts-subtypes.js | 6 +- test/mjsunit/wasm/wasm-module-builder.js | 27 ++-- .../wasm/function-body-decoder-unittest.cc | 7 +- .../unittests/wasm/module-decoder-unittest.cc | 30 +++- test/unittests/wasm/subtyping-unittest.cc | 92 +++++++----- .../wasm-disassembler-unittest-gc.wasm.inc | 4 +- test/wasm-api-tests/table.cc | 2 +- 25 files changed, 314 insertions(+), 203 deletions(-) diff --git a/src/asmjs/asm-parser.cc b/src/asmjs/asm-parser.cc index d1c59ea601..9346be040b 100644 --- a/src/asmjs/asm-parser.cc +++ b/src/asmjs/asm-parser.cc @@ -2240,7 +2240,7 @@ AsmType* AsmJsParser::ValidateCall() { function_type->AsFunctionType()->AddArgument(t); } FunctionSig* sig = ConvertSignature(return_type, param_types); - uint32_t signature_index = module_builder_->AddSignature(sig); + uint32_t signature_index = module_builder_->AddSignature(sig, true); // Emit actual function invocation depending on the kind. At this point we // also determined the complete function type and can perform checking against diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc index 22b7379e6b..8e8e45247a 100644 --- a/src/compiler/wasm-compiler.cc +++ b/src/compiler/wasm-compiler.cc @@ -2849,8 +2849,6 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index, LoadIndirectFunctionTable(table_index, &ift_size, &ift_sig_ids, &ift_targets, &ift_instances); - const wasm::FunctionSig* sig = env_->module->signature(sig_index); - Node* key = args[0]; // Bounds check against the table size. @@ -2869,7 +2867,8 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index, int32_scaled_key); Node* sig_match = gasm_->Word32Equal(loaded_sig, expected_sig_id); - if (v8_flags.experimental_wasm_gc) { + if (v8_flags.experimental_wasm_gc && + !env_->module->types[sig_index].is_final) { // Do a full subtyping check. // TODO(7748): Optimize for non-nullable tables. // TODO(7748): Optimize if type annotation matches table type. @@ -2938,6 +2937,8 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index, args[0] = target; + const wasm::FunctionSig* sig = env_->module->signature(sig_index); + switch (continuation) { case kCallContinues: return BuildWasmCall(sig, args, rets, position, target_instance); diff --git a/src/compiler/wasm-gc-lowering.cc b/src/compiler/wasm-gc-lowering.cc index 0ab9e1aa4b..67e7108262 100644 --- a/src/compiler/wasm-gc-lowering.cc +++ b/src/compiler/wasm-gc-lowering.cc @@ -121,41 +121,45 @@ Reduction WasmGCLowering::ReduceWasmTypeCheck(Node* node) { Node* map = gasm_.LoadMap(object); - // First, check if types happen to be equal. This has been shown to give large - // speedups. - gasm_.GotoIf(gasm_.TaggedEqual(map, rtt), &end_label, BranchHint::kTrue, - gasm_.Int32Constant(1)); + if (module_->types[config.to.ref_index()].is_final) { + gasm_.Goto(&end_label, gasm_.TaggedEqual(map, rtt)); + } else { + // First, check if types happen to be equal. This has been shown to give + // large speedups. + gasm_.GotoIf(gasm_.TaggedEqual(map, rtt), &end_label, BranchHint::kTrue, + gasm_.Int32Constant(1)); - // Check if map instance type identifies a wasm object. - if (is_cast_from_any) { - Node* is_wasm_obj = gasm_.IsDataRefMap(map); - gasm_.GotoIfNot(is_wasm_obj, &end_label, BranchHint::kTrue, - gasm_.Int32Constant(0)); + // Check if map instance type identifies a wasm object. + if (is_cast_from_any) { + Node* is_wasm_obj = gasm_.IsDataRefMap(map); + gasm_.GotoIfNot(is_wasm_obj, &end_label, BranchHint::kTrue, + gasm_.Int32Constant(0)); + } + + Node* type_info = gasm_.LoadWasmTypeInfo(map); + DCHECK_GE(rtt_depth, 0); + // If the depth of the rtt is known to be less that the minimum supertype + // array length, we can access the supertype without bounds-checking the + // supertype array. + if (static_cast(rtt_depth) >= wasm::kMinimumSupertypeArraySize) { + Node* supertypes_length = + gasm_.BuildChangeSmiToIntPtr(gasm_.LoadImmutableFromObject( + MachineType::TaggedSigned(), type_info, + wasm::ObjectAccess::ToTagged( + WasmTypeInfo::kSupertypesLengthOffset))); + gasm_.GotoIfNot(gasm_.UintLessThan(gasm_.IntPtrConstant(rtt_depth), + supertypes_length), + &end_label, BranchHint::kTrue, gasm_.Int32Constant(0)); + } + + Node* maybe_match = gasm_.LoadImmutableFromObject( + MachineType::TaggedPointer(), type_info, + wasm::ObjectAccess::ToTagged(WasmTypeInfo::kSupertypesOffset + + kTaggedSize * rtt_depth)); + + gasm_.Goto(&end_label, gasm_.TaggedEqual(maybe_match, rtt)); } - Node* type_info = gasm_.LoadWasmTypeInfo(map); - DCHECK_GE(rtt_depth, 0); - // If the depth of the rtt is known to be less that the minimum supertype - // array length, we can access the supertype without bounds-checking the - // supertype array. - if (static_cast(rtt_depth) >= wasm::kMinimumSupertypeArraySize) { - Node* supertypes_length = - gasm_.BuildChangeSmiToIntPtr(gasm_.LoadImmutableFromObject( - MachineType::TaggedSigned(), type_info, - wasm::ObjectAccess::ToTagged( - WasmTypeInfo::kSupertypesLengthOffset))); - gasm_.GotoIfNot( - gasm_.UintLessThan(gasm_.IntPtrConstant(rtt_depth), supertypes_length), - &end_label, BranchHint::kTrue, gasm_.Int32Constant(0)); - } - - Node* maybe_match = gasm_.LoadImmutableFromObject( - MachineType::TaggedPointer(), type_info, - wasm::ObjectAccess::ToTagged(WasmTypeInfo::kSupertypesOffset + - kTaggedSize * rtt_depth)); - - gasm_.Goto(&end_label, gasm_.TaggedEqual(maybe_match, rtt)); - gasm_.Bind(&end_label); ReplaceWithValue(node, end_label.PhiAt(0), gasm_.effect(), gasm_.control()); @@ -199,41 +203,46 @@ Reduction WasmGCLowering::ReduceWasmTypeCast(Node* node) { Node* map = gasm_.LoadMap(object); - // First, check if types happen to be equal. This has been shown to give large - // speedups. - gasm_.GotoIf(gasm_.TaggedEqual(map, rtt), &end_label, BranchHint::kTrue); + if (module_->types[config.to.ref_index()].is_final) { + gasm_.TrapUnless(gasm_.TaggedEqual(map, rtt), TrapId::kTrapIllegalCast); + gasm_.Goto(&end_label); + } else { + // First, check if types happen to be equal. This has been shown to give + // large speedups. + gasm_.GotoIf(gasm_.TaggedEqual(map, rtt), &end_label, BranchHint::kTrue); - // Check if map instance type identifies a wasm object. - if (is_cast_from_any) { - Node* is_wasm_obj = gasm_.IsDataRefMap(map); - gasm_.TrapUnless(is_wasm_obj, TrapId::kTrapIllegalCast); + // Check if map instance type identifies a wasm object. + if (is_cast_from_any) { + Node* is_wasm_obj = gasm_.IsDataRefMap(map); + gasm_.TrapUnless(is_wasm_obj, TrapId::kTrapIllegalCast); + } + + Node* type_info = gasm_.LoadWasmTypeInfo(map); + DCHECK_GE(rtt_depth, 0); + // If the depth of the rtt is known to be less that the minimum supertype + // array length, we can access the supertype without bounds-checking the + // supertype array. + if (static_cast(rtt_depth) >= wasm::kMinimumSupertypeArraySize) { + Node* supertypes_length = + gasm_.BuildChangeSmiToIntPtr(gasm_.LoadImmutableFromObject( + MachineType::TaggedSigned(), type_info, + wasm::ObjectAccess::ToTagged( + WasmTypeInfo::kSupertypesLengthOffset))); + gasm_.TrapUnless(gasm_.UintLessThan(gasm_.IntPtrConstant(rtt_depth), + supertypes_length), + TrapId::kTrapIllegalCast); + } + + Node* maybe_match = gasm_.LoadImmutableFromObject( + MachineType::TaggedPointer(), type_info, + wasm::ObjectAccess::ToTagged(WasmTypeInfo::kSupertypesOffset + + kTaggedSize * rtt_depth)); + + gasm_.TrapUnless(gasm_.TaggedEqual(maybe_match, rtt), + TrapId::kTrapIllegalCast); + gasm_.Goto(&end_label); } - Node* type_info = gasm_.LoadWasmTypeInfo(map); - DCHECK_GE(rtt_depth, 0); - // If the depth of the rtt is known to be less that the minimum supertype - // array length, we can access the supertype without bounds-checking the - // supertype array. - if (static_cast(rtt_depth) >= wasm::kMinimumSupertypeArraySize) { - Node* supertypes_length = - gasm_.BuildChangeSmiToIntPtr(gasm_.LoadImmutableFromObject( - MachineType::TaggedSigned(), type_info, - wasm::ObjectAccess::ToTagged( - WasmTypeInfo::kSupertypesLengthOffset))); - gasm_.TrapUnless( - gasm_.UintLessThan(gasm_.IntPtrConstant(rtt_depth), supertypes_length), - TrapId::kTrapIllegalCast); - } - - Node* maybe_match = gasm_.LoadImmutableFromObject( - MachineType::TaggedPointer(), type_info, - wasm::ObjectAccess::ToTagged(WasmTypeInfo::kSupertypesOffset + - kTaggedSize * rtt_depth)); - - gasm_.TrapUnless(gasm_.TaggedEqual(maybe_match, rtt), - TrapId::kTrapIllegalCast); - gasm_.Goto(&end_label); - gasm_.Bind(&end_label); ReplaceWithValue(node, object, gasm_.effect(), gasm_.control()); diff --git a/src/flags/flag-definitions.h b/src/flags/flag-definitions.h index 210357eecf..5ea43974f3 100644 --- a/src/flags/flag-definitions.h +++ b/src/flags/flag-definitions.h @@ -1173,6 +1173,8 @@ DEFINE_BOOL(trace_wasm_inlining, false, "trace wasm inlining") DEFINE_BOOL(trace_wasm_speculative_inlining, false, "trace wasm speculative inlining") DEFINE_BOOL(trace_wasm_typer, false, "trace wasm typer") +DEFINE_BOOL(wasm_final_types, false, + "enable final types as default for wasm-gc") DEFINE_IMPLICATION(wasm_speculative_inlining, wasm_inlining) DEFINE_WEAK_IMPLICATION(experimental_wasm_gc, wasm_speculative_inlining) diff --git a/src/wasm/baseline/liftoff-compiler.cc b/src/wasm/baseline/liftoff-compiler.cc index 58e2fb689a..f1894df85d 100644 --- a/src/wasm/baseline/liftoff-compiler.cc +++ b/src/wasm/baseline/liftoff-compiler.cc @@ -7409,7 +7409,8 @@ class LiftoffCompiler { AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapFuncSigMismatch); __ DropValues(1); - if (v8_flags.experimental_wasm_gc) { + if (v8_flags.experimental_wasm_gc && + !decoder->module_->types[imm.sig_imm.index].is_final) { Label success_label; FREEZE_STATE(frozen); __ emit_cond_jump(kEqual, &success_label, kI32, real_sig_id, diff --git a/src/wasm/canonical-types.cc b/src/wasm/canonical-types.cc index fa183c74e2..fc50d6ec54 100644 --- a/src/wasm/canonical-types.cc +++ b/src/wasm/canonical-types.cc @@ -68,7 +68,8 @@ uint32_t TypeCanonicalizer::AddRecursiveGroup(const FunctionSig* sig) { #endif CanonicalGroup group; group.types.resize(1); - group.types[0].type_def = TypeDefinition(sig, kNoSuperType); + group.types[0].type_def = + TypeDefinition(sig, kNoSuperType, v8_flags.wasm_final_types); group.types[0].is_relative_supertype = false; int canonical_index = FindCanonicalGroup(group); if (canonical_index < 0) { @@ -80,7 +81,8 @@ uint32_t TypeCanonicalizer::AddRecursiveGroup(const FunctionSig* sig) { for (auto type : sig->returns()) builder.AddReturn(type); for (auto type : sig->parameters()) builder.AddParam(type); const FunctionSig* allocated_sig = builder.Build(); - group.types[0].type_def = TypeDefinition(allocated_sig, kNoSuperType); + group.types[0].type_def = + TypeDefinition(allocated_sig, kNoSuperType, v8_flags.wasm_final_types); group.types[0].is_relative_supertype = false; canonical_groups_.emplace(group, canonical_index); canonical_supertypes_.emplace_back(kNoSuperType); @@ -150,7 +152,8 @@ TypeCanonicalizer::CanonicalType TypeCanonicalizer::CanonicalizeTypeDef( builder.AddParam( CanonicalizeValueType(module, param, recursive_group_start)); } - result = TypeDefinition(builder.Build(), canonical_supertype); + result = + TypeDefinition(builder.Build(), canonical_supertype, type.is_final); break; } case TypeDefinition::kStruct: { @@ -165,7 +168,7 @@ TypeCanonicalizer::CanonicalType TypeCanonicalizer::CanonicalizeTypeDef( builder.set_total_fields_size(original_type->total_fields_size()); result = TypeDefinition( builder.Build(StructType::Builder::kUseProvidedOffsets), - canonical_supertype); + canonical_supertype, type.is_final); break; } case TypeDefinition::kArray: { @@ -173,7 +176,7 @@ TypeCanonicalizer::CanonicalType TypeCanonicalizer::CanonicalizeTypeDef( module, type.array_type->element_type(), recursive_group_start); result = TypeDefinition( zone_.New(element_type, type.array_type->mutability()), - canonical_supertype); + canonical_supertype, type.is_final); break; } } diff --git a/src/wasm/canonical-types.h b/src/wasm/canonical-types.h index a404db2979..1750ebba9c 100644 --- a/src/wasm/canonical-types.h +++ b/src/wasm/canonical-types.h @@ -83,6 +83,8 @@ class TypeCanonicalizer { // TODO(manoskouk): Improve this. size_t hash_value() const { return base::hash_combine(base::hash_value(type_def.kind), + base::hash_value(type_def.supertype), + base::hash_value(type_def.is_final), base::hash_value(is_relative_supertype)); } }; diff --git a/src/wasm/module-decoder-impl.h b/src/wasm/module-decoder-impl.h index f709e3ccdf..c2d8c36d9c 100644 --- a/src/wasm/module-decoder-impl.h +++ b/src/wasm/module-decoder-impl.h @@ -581,15 +581,15 @@ class ModuleDecoderTemplate : public Decoder { switch (kind) { case kWasmFunctionTypeCode: { const FunctionSig* sig = consume_sig(&module_->signature_zone); - return {sig, kNoSuperType}; + return {sig, kNoSuperType, v8_flags.wasm_final_types}; } case kWasmStructTypeCode: { const StructType* type = consume_struct(&module_->signature_zone); - return {type, kNoSuperType}; + return {type, kNoSuperType, v8_flags.wasm_final_types}; } case kWasmArrayTypeCode: { const ArrayType* type = consume_array(&module_->signature_zone); - return {type, kNoSuperType}; + return {type, kNoSuperType, v8_flags.wasm_final_types}; } default: tracer_.NextLine(); @@ -601,8 +601,11 @@ class ModuleDecoderTemplate : public Decoder { TypeDefinition consume_subtype_definition() { DCHECK(enabled_features_.has_gc()); uint8_t kind = read_u8(pc(), "type kind"); - if (kind == kWasmSubtypeCode) { - consume_bytes(1, " subtype, ", tracer_); + if (kind == kWasmSubtypeCode || kind == kWasmSubtypeFinalCode) { + bool is_final = + v8_flags.wasm_final_types && kind == kWasmSubtypeFinalCode; + consume_bytes(1, is_final ? " subtype final, " : " subtype extensible, ", + tracer_); constexpr uint32_t kMaximumSupertypes = 1; uint32_t supertype_count = consume_count("supertype count", kMaximumSupertypes); @@ -621,6 +624,7 @@ class ModuleDecoderTemplate : public Decoder { } TypeDefinition type = consume_base_type_definition(); type.supertype = supertype; + type.is_final = is_final; return type; } else { return consume_base_type_definition(); @@ -650,13 +654,14 @@ class ModuleDecoderTemplate : public Decoder { consume_bytes(1, "function"); const FunctionSig* sig = consume_sig(&module_->signature_zone); if (!ok()) break; - module_->types[i] = {sig, kNoSuperType}; + module_->types[i] = {sig, kNoSuperType, v8_flags.wasm_final_types}; type_canon->AddRecursiveGroup(module_.get(), 1, i); break; } case kWasmArrayTypeCode: case kWasmStructTypeCode: case kWasmSubtypeCode: + case kWasmSubtypeFinalCode: case kWasmRecursiveTypeGroupCode: errorf( "Unknown type code 0x%02x, enable with --experimental-wasm-gc", @@ -726,6 +731,12 @@ class ModuleDecoderTemplate : public Decoder { errorf("type %u: subtyping depth is greater than allowed", i); continue; } + // This check is technically redundant; we include for the improved error + // message. + if (module->types[explicit_super].is_final) { + errorf("type %u extends final type %u", i, explicit_super); + continue; + } if (!ValidSubtypeDefinition(i, explicit_super, module, module)) { errorf("type %u has invalid explicit supertype %u", i, explicit_super); continue; diff --git a/src/wasm/wasm-constants.h b/src/wasm/wasm-constants.h index 8d40b4646f..ef0d7f44ff 100644 --- a/src/wasm/wasm-constants.h +++ b/src/wasm/wasm-constants.h @@ -62,6 +62,7 @@ constexpr uint8_t kWasmFunctionTypeCode = 0x60; constexpr uint8_t kWasmStructTypeCode = 0x5f; constexpr uint8_t kWasmArrayTypeCode = 0x5e; constexpr uint8_t kWasmSubtypeCode = 0x50; +constexpr uint8_t kWasmSubtypeFinalCode = 0x4e; constexpr uint8_t kWasmRecursiveTypeGroupCode = 0x4f; // Binary encoding of import/export kinds. diff --git a/src/wasm/wasm-module-builder.cc b/src/wasm/wasm-module-builder.cc index 0e485bd09b..b3fbdfd171 100644 --- a/src/wasm/wasm-module-builder.cc +++ b/src/wasm/wasm-module-builder.cc @@ -57,7 +57,7 @@ void WasmFunctionBuilder::EmitU32V(uint32_t val) { body_.write_u32v(val); } void WasmFunctionBuilder::SetSignature(const FunctionSig* sig) { DCHECK(!locals_.has_sig()); locals_.set_sig(sig); - signature_index_ = builder_->AddSignature(sig); + signature_index_ = builder_->AddSignature(sig, true); } void WasmFunctionBuilder::SetSignature(uint32_t sig_index) { @@ -305,38 +305,40 @@ void WasmModuleBuilder::AddDataSegment(const byte* data, uint32_t size, } uint32_t WasmModuleBuilder::ForceAddSignature(const FunctionSig* sig, + bool is_final, uint32_t supertype) { uint32_t index = static_cast(types_.size()); signature_map_.emplace(*sig, index); - types_.emplace_back(sig, supertype); + types_.emplace_back(sig, supertype, is_final); return index; } -uint32_t WasmModuleBuilder::AddSignature(const FunctionSig* sig, +uint32_t WasmModuleBuilder::AddSignature(const FunctionSig* sig, bool is_final, uint32_t supertype) { auto sig_entry = signature_map_.find(*sig); if (sig_entry != signature_map_.end()) return sig_entry->second; - return ForceAddSignature(sig, supertype); + return ForceAddSignature(sig, is_final, supertype); } uint32_t WasmModuleBuilder::AddException(const FunctionSig* type) { DCHECK_EQ(0, type->return_count()); - int type_index = AddSignature(type); + int type_index = AddSignature(type, true); uint32_t except_index = static_cast(exceptions_.size()); exceptions_.push_back(type_index); return except_index; } -uint32_t WasmModuleBuilder::AddStructType(StructType* type, +uint32_t WasmModuleBuilder::AddStructType(StructType* type, bool is_final, uint32_t supertype) { uint32_t index = static_cast(types_.size()); - types_.emplace_back(type, supertype); + types_.emplace_back(type, supertype, is_final); return index; } -uint32_t WasmModuleBuilder::AddArrayType(ArrayType* type, uint32_t supertype) { +uint32_t WasmModuleBuilder::AddArrayType(ArrayType* type, bool is_final, + uint32_t supertype) { uint32_t index = static_cast(types_.size()); - types_.emplace_back(type, supertype); + types_.emplace_back(type, supertype, is_final); return index; } @@ -390,7 +392,7 @@ uint32_t WasmModuleBuilder::AddImport(base::Vector name, FunctionSig* sig, base::Vector module) { DCHECK(adding_imports_allowed_); - function_imports_.push_back({module, name, AddSignature(sig)}); + function_imports_.push_back({module, name, AddSignature(sig, true)}); return static_cast(function_imports_.size() - 1); } @@ -613,9 +615,13 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const { const TypeDefinition& type = types_[i]; if (type.supertype != kNoSuperType) { - buffer->write_u8(kWasmSubtypeCode); - buffer->write_u8(1); // The supertype count is always 1. + buffer->write_u8(type.is_final ? kWasmSubtypeFinalCode + : kWasmSubtypeCode); + buffer->write_u8(1); buffer->write_u32v(type.supertype); + } else if (!type.is_final) { + buffer->write_u8(kWasmSubtypeCode); + buffer->write_u8(0); } switch (type.kind) { case TypeDefinition::kFunction: { diff --git a/src/wasm/wasm-module-builder.h b/src/wasm/wasm-module-builder.h index 09c0b68c92..dbf29bd429 100644 --- a/src/wasm/wasm-module-builder.h +++ b/src/wasm/wasm-module-builder.h @@ -334,14 +334,16 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { // exceeded. uint32_t IncreaseTableMinSize(uint32_t table_index, uint32_t count); // Adds the signature to the module if it does not already exist. - uint32_t AddSignature(const FunctionSig* sig, + uint32_t AddSignature(const FunctionSig* sig, bool is_final, uint32_t supertype = kNoSuperType); // Does not deduplicate function signatures. - uint32_t ForceAddSignature(const FunctionSig* sig, + uint32_t ForceAddSignature(const FunctionSig* sig, bool is_final, uint32_t supertype = kNoSuperType); uint32_t AddException(const FunctionSig* type); - uint32_t AddStructType(StructType* type, uint32_t supertype = kNoSuperType); - uint32_t AddArrayType(ArrayType* type, uint32_t supertype = kNoSuperType); + uint32_t AddStructType(StructType* type, bool is_final, + uint32_t supertype = kNoSuperType); + uint32_t AddArrayType(ArrayType* type, bool is_final, + uint32_t supertype = kNoSuperType); uint32_t AddTable(ValueType type, uint32_t min_size); uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size); uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size, diff --git a/src/wasm/wasm-module.h b/src/wasm/wasm-module.h index afc91db194..a6a6094cf5 100644 --- a/src/wasm/wasm-module.h +++ b/src/wasm/wasm-module.h @@ -334,23 +334,30 @@ constexpr uint32_t kNoSuperType = std::numeric_limits::max(); struct TypeDefinition { enum Kind { kFunction, kStruct, kArray }; - TypeDefinition(const FunctionSig* sig, uint32_t supertype) - : function_sig(sig), supertype(supertype), kind(kFunction) {} - TypeDefinition(const StructType* type, uint32_t supertype) - : struct_type(type), supertype(supertype), kind(kStruct) {} - TypeDefinition(const ArrayType* type, uint32_t supertype) - : array_type(type), supertype(supertype), kind(kArray) {} + TypeDefinition(const FunctionSig* sig, uint32_t supertype, bool is_final) + : function_sig(sig), + supertype(supertype), + kind(kFunction), + is_final(is_final) {} + TypeDefinition(const StructType* type, uint32_t supertype, bool is_final) + : struct_type(type), + supertype(supertype), + kind(kStruct), + is_final(is_final) {} + TypeDefinition(const ArrayType* type, uint32_t supertype, bool is_final) + : array_type(type), + supertype(supertype), + kind(kArray), + is_final(is_final) {} TypeDefinition() - : function_sig(nullptr), supertype(kNoSuperType), kind(kFunction) {} - - union { - const FunctionSig* function_sig; - const StructType* struct_type; - const ArrayType* array_type; - }; + : function_sig(nullptr), + supertype(kNoSuperType), + kind(kFunction), + is_final(false) {} bool operator==(const TypeDefinition& other) const { - if (supertype != other.supertype || kind != other.kind) { + if (supertype != other.supertype || kind != other.kind || + is_final != other.is_final) { return false; } switch (kind) { @@ -367,8 +374,14 @@ struct TypeDefinition { return !(*this == other); } + union { + const FunctionSig* function_sig; + const StructType* struct_type; + const ArrayType* array_type; + }; uint32_t supertype; Kind kind; + bool is_final; }; struct V8_EXPORT_PRIVATE WasmDebugSymbols { @@ -558,9 +571,10 @@ struct V8_EXPORT_PRIVATE WasmModule { bool has_type(uint32_t index) const { return index < types.size(); } - void add_signature(const FunctionSig* sig, uint32_t supertype) { + void add_signature(const FunctionSig* sig, uint32_t supertype, + bool is_final) { DCHECK_NOT_NULL(sig); - add_type(TypeDefinition(sig, supertype)); + add_type(TypeDefinition(sig, supertype, is_final)); } bool has_signature(uint32_t index) const { return index < types.size() && @@ -571,9 +585,10 @@ struct V8_EXPORT_PRIVATE WasmModule { return types[index].function_sig; } - void add_struct_type(const StructType* type, uint32_t supertype) { + void add_struct_type(const StructType* type, uint32_t supertype, + bool is_final) { DCHECK_NOT_NULL(type); - add_type(TypeDefinition(type, supertype)); + add_type(TypeDefinition(type, supertype, is_final)); } bool has_struct(uint32_t index) const { return index < types.size() && types[index].kind == TypeDefinition::kStruct; @@ -583,9 +598,10 @@ struct V8_EXPORT_PRIVATE WasmModule { return types[index].struct_type; } - void add_array_type(const ArrayType* type, uint32_t supertype) { + void add_array_type(const ArrayType* type, uint32_t supertype, + bool is_final) { DCHECK_NOT_NULL(type); - add_type(TypeDefinition(type, supertype)); + add_type(TypeDefinition(type, supertype, is_final)); } bool has_array(uint32_t index) const { return index < types.size() && types[index].kind == TypeDefinition::kArray; diff --git a/src/wasm/wasm-subtyping.cc b/src/wasm/wasm-subtyping.cc index 0b74af4c70..0945a5f96f 100644 --- a/src/wasm/wasm-subtyping.cc +++ b/src/wasm/wasm-subtyping.cc @@ -141,10 +141,11 @@ bool IsNullSentinel(HeapType type) { bool ValidSubtypeDefinition(uint32_t subtype_index, uint32_t supertype_index, const WasmModule* sub_module, const WasmModule* super_module) { - TypeDefinition::Kind sub_kind = sub_module->types[subtype_index].kind; - TypeDefinition::Kind super_kind = super_module->types[supertype_index].kind; - if (sub_kind != super_kind) return false; - switch (sub_kind) { + const TypeDefinition& subtype = sub_module->types[subtype_index]; + const TypeDefinition& supertype = super_module->types[supertype_index]; + if (subtype.kind != supertype.kind) return false; + if (supertype.is_final) return false; + switch (subtype.kind) { case TypeDefinition::kFunction: return ValidFunctionSubtypeDefinition(subtype_index, supertype_index, sub_module, super_module); diff --git a/test/cctest/wasm/test-gc.cc b/test/cctest/wasm/test-gc.cc index 864c4dac23..96a4d25ed9 100644 --- a/test/cctest/wasm/test-gc.cc +++ b/test/cctest/wasm/test-gc.cc @@ -81,23 +81,24 @@ class WasmGCTester { } byte DefineStruct(std::initializer_list fields, - uint32_t supertype = kNoSuperType) { + uint32_t supertype = kNoSuperType, bool is_final = false) { StructType::Builder type_builder(&zone_, static_cast(fields.size())); for (F field : fields) { type_builder.AddField(field.first, field.second); } - return builder_.AddStructType(type_builder.Build(), supertype); + return builder_.AddStructType(type_builder.Build(), is_final, supertype); } byte DefineArray(ValueType element_type, bool mutability, - uint32_t supertype = kNoSuperType) { + uint32_t supertype = kNoSuperType, bool is_final = false) { return builder_.AddArrayType(zone_.New(element_type, mutability), - supertype); + is_final, supertype); } - byte DefineSignature(FunctionSig* sig, uint32_t supertype = kNoSuperType) { - return builder_.ForceAddSignature(sig, supertype); + byte DefineSignature(FunctionSig* sig, uint32_t supertype = kNoSuperType, + bool is_final = false) { + return builder_.ForceAddSignature(sig, is_final, supertype); } byte DefineTable(ValueType type, uint32_t min_size, uint32_t max_size) { diff --git a/test/cctest/wasm/wasm-run-utils.h b/test/cctest/wasm/wasm-run-utils.h index 319cd7fd28..fef9e16c2f 100644 --- a/test/cctest/wasm/wasm-run-utils.h +++ b/test/cctest/wasm/wasm-run-utils.h @@ -132,8 +132,9 @@ class TestingModuleBuilder { return reinterpret_cast(globals_data_ + global->offset); } + // TODO(7748): Allow selecting type finality. byte AddSignature(const FunctionSig* sig) { - test_module_->add_signature(sig, kNoSuperType); + test_module_->add_signature(sig, kNoSuperType, v8_flags.wasm_final_types); GetTypeCanonicalizer()->AddRecursiveGroup(test_module_.get(), 1); instance_object_->set_isorecursive_canonical_types( test_module_->isorecursive_canonical_type_ids.data()); diff --git a/test/fuzzer/wasm-compile.cc b/test/fuzzer/wasm-compile.cc index c6bbdaf061..7525e56370 100644 --- a/test/fuzzer/wasm-compile.cc +++ b/test/fuzzer/wasm-compile.cc @@ -192,7 +192,8 @@ class WasmGenerator { builder.AddReturn(type); } FunctionSig* sig = builder.Build(); - int sig_id = gen->builder_->builder()->AddSignature(sig); + int sig_id = gen->builder_->builder()->AddSignature( + sig, v8_flags.wasm_final_types); gen->builder_->EmitI32V(sig_id); } @@ -2428,7 +2429,7 @@ class WasmCompileFuzzer : public WasmExecutionFuzzer { struct_builder.AddField(type, mutability); } StructType* struct_fuz = struct_builder.Build(); - builder.AddStructType(struct_fuz); + builder.AddStructType(struct_fuz, false); } for (int array_index = 0; array_index < num_arrays; array_index++) { @@ -2436,18 +2437,20 @@ class WasmCompileFuzzer : public WasmExecutionFuzzer { &range, builder.NumTypes(), builder.NumTypes(), kAllowNonNullables, kIncludePackedTypes, kIncludeGenerics); ArrayType* array_fuz = zone->New(type, true); - builder.AddArrayType(array_fuz); + builder.AddArrayType(array_fuz, false); } // We keep the signature for the first (main) function constant. - function_signatures.push_back(builder.ForceAddSignature(sigs.i_iii())); + function_signatures.push_back( + builder.ForceAddSignature(sigs.i_iii(), v8_flags.wasm_final_types)); - for (uint8_t i = 1; i < num_functions; i++) { - FunctionSig* sig = - GenerateSig(zone, &range, kFunctionSig, builder.NumTypes()); - uint32_t signature_index = builder.ForceAddSignature(sig); - function_signatures.push_back(signature_index); - } + for (uint8_t i = 1; i < num_functions; i++) { + FunctionSig* sig = + GenerateSig(zone, &range, kFunctionSig, builder.NumTypes()); + uint32_t signature_index = + builder.ForceAddSignature(sig, v8_flags.wasm_final_types); + function_signatures.push_back(signature_index); + } int num_exceptions = 1 + (range.get() % kMaxExceptions); for (int i = 0; i < num_exceptions; ++i) { diff --git a/test/inspector/debugger/wasm-gc-anyref-expected.txt b/test/inspector/debugger/wasm-gc-anyref-expected.txt index 30bbccd2ce..582a98053f 100644 --- a/test/inspector/debugger/wasm-gc-anyref-expected.txt +++ b/test/inspector/debugger/wasm-gc-anyref-expected.txt @@ -8,14 +8,14 @@ Module instantiated. Tables populated. Setting breakpoint { - columnNumber : 260 + columnNumber : 264 lineNumber : 0 scriptId : } Paused: -Script wasm://wasm/8fd0ec76 byte offset 260: Wasm opcode 0x01 (kExprNop) +Script wasm://wasm/19fa3802 byte offset 264: Wasm opcode 0x01 (kExprNop) Scope: -at $main (0:260): +at $main (0:264): - scope (wasm-expression-stack): stack: - scope (local): diff --git a/test/mjsunit/wasm/call-ref.js b/test/mjsunit/wasm/call-ref.js index 23d118ee7c..9880f0ef6d 100644 --- a/test/mjsunit/wasm/call-ref.js +++ b/test/mjsunit/wasm/call-ref.js @@ -32,7 +32,6 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); var imported_wasm_function_index = builder.addImport("imports", "wasm_add", sig_index); - var locally_defined_function = builder.addFunction("sub", sig_index) .addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Sub]) @@ -146,7 +145,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); let sub_struct = builder.addStruct( [makeField(kWasmI32, true), makeField(kWasmI64, true)], super_struct); let super_sig = builder.addType(makeSig([wasmRefNullType(sub_struct)], - [kWasmI32])) + [kWasmI32]), kNoSuperType, false) let sub_sig = builder.addType(makeSig([wasmRefNullType(super_struct)], [kWasmI32]), super_sig) @@ -162,8 +161,8 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); let super_struct = builder.addStruct([makeField(kWasmI32, true)]); let sub_struct = builder.addStruct( [makeField(kWasmI32, true), makeField(kWasmI64, true)], super_struct); - let super_sig = builder.addType(makeSig([wasmRefNullType(sub_struct)], - [kWasmI32])) + let super_sig = builder.addType( + makeSig([wasmRefNullType(sub_struct)], [kWasmI32]), kNoSuperType, false); builder.addImport("m", "f", super_sig); // Import is a function of the declared type. @@ -189,7 +188,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); (function () { var builder = new WasmModuleBuilder(); - let sig = builder.addType(kSig_i_i); + let sig = builder.addType(kSig_i_i, kNoSuperType, false); let sig_sub = builder.addType(kSig_i_i, sig); builder.addImport("m", "f", sig_sub); diff --git a/test/mjsunit/wasm/gc-casts-subtypes.js b/test/mjsunit/wasm/gc-casts-subtypes.js index 0023eef604..8c674bfed1 100644 --- a/test/mjsunit/wasm/gc-casts-subtypes.js +++ b/test/mjsunit/wasm/gc-casts-subtypes.js @@ -84,7 +84,7 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js'); (function RefTestFuncRef() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); - let sigSuper = builder.addType(makeSig([kWasmI32], [])); + let sigSuper = builder.addType(makeSig([kWasmI32], []), kNoSuperType, false); let sigSub = builder.addType(makeSig([kWasmI32], []), sigSuper); builder.addFunction('fctSuper', sigSuper).addBody([]).exportFunc(); @@ -125,7 +125,7 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js'); (function RefCastFuncRef() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); - let sigSuper = builder.addType(makeSig([kWasmI32], [])); + let sigSuper = builder.addType(makeSig([kWasmI32], []), kNoSuperType, false); let sigSub = builder.addType(makeSig([kWasmI32], []), sigSuper); builder.addFunction('fctSuper', sigSuper).addBody([]).exportFunc(); @@ -206,7 +206,7 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js'); (function BrOnCastFuncRef() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); - let sigSuper = builder.addType(makeSig([kWasmI32], [])); + let sigSuper = builder.addType(makeSig([kWasmI32], []), kNoSuperType, false); let sigSub = builder.addType(makeSig([kWasmI32], []), sigSuper); builder.addFunction('fctSuper', sigSuper).addBody([]).exportFunc(); diff --git a/test/mjsunit/wasm/wasm-module-builder.js b/test/mjsunit/wasm/wasm-module-builder.js index 1a0dcbfe7a..3701198936 100644 --- a/test/mjsunit/wasm/wasm-module-builder.js +++ b/test/mjsunit/wasm/wasm-module-builder.js @@ -80,6 +80,7 @@ let kWasmFunctionTypeForm = 0x60; let kWasmStructTypeForm = 0x5f; let kWasmArrayTypeForm = 0x5e; let kWasmSubtypeForm = 0x50; +let kWasmSubtypeFinalForm = 0x4e; let kWasmRecursiveTypeGroupForm = 0x4f; let kNoSuperType = 0xFFFFFFFF; @@ -1207,21 +1208,23 @@ function makeField(type, mutability) { } class WasmStruct { - constructor(fields, supertype_idx) { + constructor(fields, is_final, supertype_idx) { if (!Array.isArray(fields)) { throw new Error('struct fields must be an array'); } this.fields = fields; this.type_form = kWasmStructTypeForm; + this.is_final = is_final; this.supertype = supertype_idx; } } class WasmArray { - constructor(type, mutability, supertype_idx) { + constructor(type, mutability, is_final, supertype_idx) { this.type = type; this.mutability = mutability; this.type_form = kWasmArrayTypeForm; + this.is_final = is_final; this.supertype = supertype_idx; } } @@ -1338,11 +1341,13 @@ class WasmModuleBuilder { this.explicit.push(this.createCustomSection(name, bytes)); } - addType(type, supertype_idx = kNoSuperType) { + // We use {is_final = true} so that the MVP syntax is generated for + // signatures. + addType(type, supertype_idx = kNoSuperType, is_final = true) { var pl = type.params.length; // should have params var rl = type.results.length; // should have results var type_copy = {params: type.params, results: type.results, - supertype: supertype_idx}; + is_final: is_final, supertype: supertype_idx}; this.types.push(type_copy); return this.types.length - 1; } @@ -1352,13 +1357,13 @@ class WasmModuleBuilder { return this.stringrefs.length - 1; } - addStruct(fields, supertype_idx = kNoSuperType) { - this.types.push(new WasmStruct(fields, supertype_idx)); + addStruct(fields, supertype_idx = kNoSuperType, is_final = false) { + this.types.push(new WasmStruct(fields, is_final, supertype_idx)); return this.types.length - 1; } - addArray(type, mutability, supertype_idx = kNoSuperType) { - this.types.push(new WasmArray(type, mutability, supertype_idx)); + addArray(type, mutability, supertype_idx = kNoSuperType, is_final = false) { + this.types.push(new WasmArray(type, mutability, is_final, supertype_idx)); return this.types.length - 1; } @@ -1654,9 +1659,13 @@ class WasmModuleBuilder { let type = wasm.types[i]; if (type.supertype != kNoSuperType) { - section.emit_u8(kWasmSubtypeForm); + section.emit_u8(type.is_final ? kWasmSubtypeFinalForm + : kWasmSubtypeForm); section.emit_u8(1); // supertype count section.emit_u32v(type.supertype); + } else if (!type.is_final) { + section.emit_u8(kWasmSubtypeForm); + section.emit_u8(0); // no supertypes } if (type instanceof WasmStruct) { section.emit_u8(kWasmStructTypeForm); diff --git a/test/unittests/wasm/function-body-decoder-unittest.cc b/test/unittests/wasm/function-body-decoder-unittest.cc index 1d44be21aa..7c74c6a72e 100644 --- a/test/unittests/wasm/function-body-decoder-unittest.cc +++ b/test/unittests/wasm/function-body-decoder-unittest.cc @@ -87,7 +87,7 @@ class TestModuleBuilder { return static_cast(mod.globals.size() - 1); } byte AddSignature(const FunctionSig* sig, uint32_t supertype = kNoSuperType) { - mod.add_signature(sig, supertype); + mod.add_signature(sig, supertype, v8_flags.wasm_final_types); CHECK_LE(mod.types.size(), kMaxByteSizedLeb128); GetTypeCanonicalizer()->AddRecursiveGroup(module(), 1); return static_cast(mod.types.size() - 1); @@ -131,14 +131,15 @@ class TestModuleBuilder { for (F field : fields) { type_builder.AddField(field.first, field.second); } - mod.add_struct_type(type_builder.Build(), supertype); + mod.add_struct_type(type_builder.Build(), supertype, + v8_flags.wasm_final_types); GetTypeCanonicalizer()->AddRecursiveGroup(module(), 1); return static_cast(mod.types.size() - 1); } byte AddArray(ValueType type, bool mutability) { ArrayType* array = mod.signature_zone.New(type, mutability); - mod.add_array_type(array, kNoSuperType); + mod.add_array_type(array, kNoSuperType, v8_flags.wasm_final_types); GetTypeCanonicalizer()->AddRecursiveGroup(module(), 1); return static_cast(mod.types.size() - 1); } diff --git a/test/unittests/wasm/module-decoder-unittest.cc b/test/unittests/wasm/module-decoder-unittest.cc index e06d86c923..37b7c405a1 100644 --- a/test/unittests/wasm/module-decoder-unittest.cc +++ b/test/unittests/wasm/module-decoder-unittest.cc @@ -1058,8 +1058,9 @@ TEST_F(WasmModuleVerifyTest, InvalidSupertypeInRecGroup) { static const byte invalid_supertype[] = { SECTION(Type, ENTRY_COUNT(1), // -- kWasmRecursiveTypeGroupCode, ENTRY_COUNT(2), // -- - kWasmArrayTypeCode, kI32Code, 0, // -- - kWasmSubtypeCode, 1, 0, // supertype count, supertype + kWasmSubtypeCode, 0, // 0 supertypes, non-final + kWasmArrayTypeCode, kI32Code, 0, // -- + kWasmSubtypeCode, 1, 0, // supertype count, supertype kWasmArrayTypeCode, kI64Code, 0)}; EXPECT_FAILURE_WITH_MSG(invalid_supertype, @@ -1091,6 +1092,31 @@ TEST_F(WasmModuleVerifyTest, NoSupertypeSupertype) { no_supertype, "is greater than the maximum number of type definitions"); } +TEST_F(WasmModuleVerifyTest, NonSpecifiedFinalType) { + WASM_FEATURE_SCOPE(typed_funcref); + WASM_FEATURE_SCOPE(gc); + FLAG_SCOPE(wasm_final_types); + static const byte final_supertype[] = { + SECTION(Type, ENTRY_COUNT(2), // -- + kWasmStructTypeCode, 1, kI32Code, 1, // -- + kWasmSubtypeCode, 1, 0, // -- + kWasmStructTypeCode, 2, kI32Code, 1, kI32Code, 1)}; + EXPECT_FAILURE_WITH_MSG(final_supertype, "type 1 extends final type 0"); +} + +TEST_F(WasmModuleVerifyTest, SpecifiedFinalType) { + WASM_FEATURE_SCOPE(typed_funcref); + WASM_FEATURE_SCOPE(gc); + FLAG_SCOPE(wasm_final_types); + static const byte final_supertype[] = { + SECTION(Type, ENTRY_COUNT(2), // -- + kWasmSubtypeFinalCode, 0, // -- + kWasmStructTypeCode, 1, kI32Code, 1, // -- + kWasmSubtypeCode, 1, 0, // -- + kWasmStructTypeCode, 2, kI32Code, 1, kI32Code, 1)}; + EXPECT_FAILURE_WITH_MSG(final_supertype, "type 1 extends final type 0"); +} + TEST_F(WasmModuleVerifyTest, ZeroExceptions) { static const byte data[] = {SECTION(Tag, ENTRY_COUNT(0))}; ModuleResult result = DecodeModule(base::ArrayVector(data)); diff --git a/test/unittests/wasm/subtyping-unittest.cc b/test/unittests/wasm/subtyping-unittest.cc index 75e59df3bf..fd3681ee73 100644 --- a/test/unittests/wasm/subtyping-unittest.cc +++ b/test/unittests/wasm/subtyping-unittest.cc @@ -25,25 +25,25 @@ FieldInit mut(ValueType type) { return FieldInit(type, true); } FieldInit immut(ValueType type) { return FieldInit(type, false); } void DefineStruct(WasmModule* module, std::initializer_list fields, - uint32_t supertype = kNoSuperType, + uint32_t supertype = kNoSuperType, bool is_final = false, bool in_singleton_rec_group = true) { StructType::Builder builder(&module->signature_zone, static_cast(fields.size())); for (FieldInit field : fields) { builder.AddField(field.first, field.second); } - module->add_struct_type(builder.Build(), supertype); + module->add_struct_type(builder.Build(), supertype, is_final); if (in_singleton_rec_group) { GetTypeCanonicalizer()->AddRecursiveGroup(module, 1); } } void DefineArray(WasmModule* module, FieldInit element_type, - uint32_t supertype = kNoSuperType, + uint32_t supertype = kNoSuperType, bool is_final = false, bool in_singleton_rec_group = true) { module->add_array_type(module->signature_zone.New( element_type.first, element_type.second), - supertype); + supertype, is_final); if (in_singleton_rec_group) { GetTypeCanonicalizer()->AddRecursiveGroup(module, 1); } @@ -52,10 +52,11 @@ void DefineArray(WasmModule* module, FieldInit element_type, void DefineSignature(WasmModule* module, std::initializer_list params, std::initializer_list returns, - uint32_t supertype = kNoSuperType, + uint32_t supertype = kNoSuperType, bool is_final = false, bool in_singleton_rec_group = true) { module->add_signature( - FunctionSig::Build(&module->signature_zone, returns, params), supertype); + FunctionSig::Build(&module->signature_zone, returns, params), supertype, + is_final); if (in_singleton_rec_group) { GetTypeCanonicalizer()->AddRecursiveGroup(module, 1); } @@ -96,34 +97,43 @@ TEST_F(WasmSubtypingTest, Subtyping) { // Rec. group. /* 18 */ DefineStruct(module, {mut(kWasmI32), immut(refNull(17))}, 17, false); - /* 19 */ DefineArray(module, {mut(refNull(21))}, kNoSuperType, false); + /* 19 */ DefineArray(module, {mut(refNull(21))}, kNoSuperType, false, + false); /* 20 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType, - false); - /* 21 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 20, false); + false, false); + /* 21 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 20, false, false); GetTypeCanonicalizer()->AddRecursiveGroup(module, 4); // Identical rec. group. /* 22 */ DefineStruct(module, {mut(kWasmI32), immut(refNull(17))}, 17, - false); - /* 23 */ DefineArray(module, {mut(refNull(25))}, kNoSuperType, false); + false, false); + /* 23 */ DefineArray(module, {mut(refNull(25))}, kNoSuperType, false, + false); /* 24 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType, - false); - /* 25 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 24, false); + false, false); + /* 25 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 24, false, false); GetTypeCanonicalizer()->AddRecursiveGroup(module, 4); // Nonidentical rec. group: the last function extends a type outside the // recursive group. /* 26 */ DefineStruct(module, {mut(kWasmI32), immut(refNull(17))}, 17, - false); - /* 27 */ DefineArray(module, {mut(refNull(29))}, kNoSuperType, false); + false, false); + /* 27 */ DefineArray(module, {mut(refNull(29))}, kNoSuperType, false, + false); /* 28 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType, - false); - /* 29 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 20, false); + false, false); + /* 29 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 20, false, false); GetTypeCanonicalizer()->AddRecursiveGroup(module, 4); /* 30 */ DefineStruct(module, {mut(kWasmI32), immut(refNull(18))}, 18); /* 31 */ DefineStruct( module, {mut(ref(2)), immut(refNull(2)), immut(kWasmS128)}, 1); + + // Final types + /* 32 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, true); + /* 33 */ DefineStruct(module, {mut(kWasmI32), mut(kWasmI64)}, 32, true); + /* 34 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, true); + /* 35 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, false); } constexpr ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64, @@ -305,32 +315,38 @@ TEST_F(WasmSubtypingTest, Subtyping) { VALID_SUBTYPE(ref(10), ref(10)); VALID_SUBTYPE(ref(11), ref(11)); - { - // Canonicalization tests. + // Canonicalization tests. - // Groups should only be canonicalized to identical groups. - IDENTICAL(18, 22); - IDENTICAL(19, 23); - IDENTICAL(20, 24); - IDENTICAL(21, 25); + // Groups should only be canonicalized to identical groups. + IDENTICAL(18, 22); + IDENTICAL(19, 23); + IDENTICAL(20, 24); + IDENTICAL(21, 25); - DISTINCT(18, 26); - DISTINCT(19, 27); - DISTINCT(20, 28); - DISTINCT(21, 29); + DISTINCT(18, 26); + DISTINCT(19, 27); + DISTINCT(20, 28); + DISTINCT(21, 29); - // A type should not be canonicalized to an identical one with a different - // group structure. - DISTINCT(18, 17); + // A type should not be canonicalized to an identical one with a different + // group structure. + DISTINCT(18, 17); - // A subtype should also be subtype of an equivalent type. - VALID_SUBTYPE(ref(30), ref(18)); - VALID_SUBTYPE(ref(30), ref(22)); - NOT_SUBTYPE(ref(30), ref(26)); + // A subtype should also be subtype of an equivalent type. + VALID_SUBTYPE(ref(30), ref(18)); + VALID_SUBTYPE(ref(30), ref(22)); + NOT_SUBTYPE(ref(30), ref(26)); - // Rtts of identical types are subtype-related. - SUBTYPE(ValueType::Rtt(8), ValueType::Rtt(17)); - } + // Final types + + // A type is not a valid subtype of a final type. + NOT_VALID_SUBTYPE(ref(33), ref(32)); + IDENTICAL(32, 34); + // A final and a non-final + DISTINCT(32, 35); + + // Rtts of identical types are subtype-related. + SUBTYPE(ValueType::Rtt(8), ValueType::Rtt(17)); // Unions and intersections. diff --git a/test/unittests/wasm/wasm-disassembler-unittest-gc.wasm.inc b/test/unittests/wasm/wasm-disassembler-unittest-gc.wasm.inc index 67eac63803..01fdedfd1b 100644 --- a/test/unittests/wasm/wasm-disassembler-unittest-gc.wasm.inc +++ b/test/unittests/wasm/wasm-disassembler-unittest-gc.wasm.inc @@ -2,9 +2,9 @@ 0x01, 0x00, 0x00, 0x00, // wasm version 0x01, // section kind: Type - 0x3a, // section length 58 + 0x3c, // section length 60 0x0b, // types count 11 - 0x5f, 0x00, // type #0 $type0 kind: struct, field count 0 + 0x50, 0x00, 0x5f, 0x00, // type #0 $type0 subtype, supertype count 0, kind: struct, field count 0 0x5f, 0x01, 0x7f, 0x00, // type #1 $type1 kind: struct, field count 1: i32 immutable 0x5f, 0x02, // type #2 $type2 kind: struct, field count 2 0x7f, 0x01, // i32 mutable diff --git a/test/wasm-api-tests/table.cc b/test/wasm-api-tests/table.cc index c28dcce4a7..4425a678f0 100644 --- a/test/wasm-api-tests/table.cc +++ b/test/wasm-api-tests/table.cc @@ -39,7 +39,7 @@ void ExpectResult(int expected, const Func* func, int arg1, int arg2) { TEST_F(WasmCapiTest, Table) { const uint32_t table_index = builder()->AddTable(kWasmFuncRef, 2, 10); builder()->AddExport(base::CStrVector("table"), kExternalTable, table_index); - const uint32_t sig_i_i_index = builder()->AddSignature(wasm_i_i_sig()); + const uint32_t sig_i_i_index = builder()->AddSignature(wasm_i_i_sig(), true); ValueType reps[] = {kWasmI32, kWasmI32, kWasmI32}; FunctionSig call_sig(1, 2, reps); byte call_code[] = {