[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 <mliedtke@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Cr-Commit-Position: refs/heads/main@{#84955}
This commit is contained in:
parent
605e46479a
commit
2f852102d9
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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());
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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<ArrayType>(element_type, type.array_type->mutability()),
|
||||
canonical_supertype);
|
||||
canonical_supertype, type.is_final);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
@ -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<Decoder::FullValidationTag>(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;
|
||||
|
@ -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.
|
||||
|
@ -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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<const char> name,
|
||||
FunctionSig* sig,
|
||||
base::Vector<const char> 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<uint32_t>(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: {
|
||||
|
@ -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,
|
||||
|
@ -334,23 +334,30 @@ constexpr uint32_t kNoSuperType = std::numeric_limits<uint32_t>::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;
|
||||
|
@ -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);
|
||||
|
@ -81,23 +81,24 @@ class WasmGCTester {
|
||||
}
|
||||
|
||||
byte DefineStruct(std::initializer_list<F> fields,
|
||||
uint32_t supertype = kNoSuperType) {
|
||||
uint32_t supertype = kNoSuperType, bool is_final = false) {
|
||||
StructType::Builder type_builder(&zone_,
|
||||
static_cast<uint32_t>(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<ArrayType>(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) {
|
||||
|
@ -132,8 +132,9 @@ class TestingModuleBuilder {
|
||||
return reinterpret_cast<T*>(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());
|
||||
|
@ -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<ArrayType>(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<uint8_t>() % kMaxExceptions);
|
||||
for (int i = 0; i < num_exceptions; ++i) {
|
||||
|
@ -8,14 +8,14 @@ Module instantiated.
|
||||
Tables populated.
|
||||
Setting breakpoint
|
||||
{
|
||||
columnNumber : 260
|
||||
columnNumber : 264
|
||||
lineNumber : 0
|
||||
scriptId : <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):
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -87,7 +87,7 @@ class TestModuleBuilder {
|
||||
return static_cast<byte>(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<byte>(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<byte>(mod.types.size() - 1);
|
||||
}
|
||||
|
||||
byte AddArray(ValueType type, bool mutability) {
|
||||
ArrayType* array = mod.signature_zone.New<ArrayType>(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<byte>(mod.types.size() - 1);
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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<FieldInit> 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<uint32_t>(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<ArrayType>(
|
||||
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<ValueType> params,
|
||||
std::initializer_list<ValueType> 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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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[] = {
|
||||
|
Loading…
Reference in New Issue
Block a user