[wasm-gc] Remove nominal types

Some tests and testing infrastructure had to be changed because it
relied on nominal types.
Drive-by: Support function supertypes in wasm-module-builder.js.

Bug: v8:7748
Change-Id: Ife92431d1842ff9de91e296a50421aa48f02c0de
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3776197
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81862}
This commit is contained in:
Manos Koukoutos 2022-07-20 05:05:57 +00:00 committed by V8 LUCI CQ
parent 884226c1d0
commit af39b32154
9 changed files with 56 additions and 326 deletions

View File

@ -597,9 +597,6 @@ class ModuleDecoderTemplate : public Decoder {
case kWasmFunctionTypeCode: return "func";
case kWasmStructTypeCode: return "struct";
case kWasmArrayTypeCode: return "array";
case kWasmFunctionNominalCode: return "function-nominal";
case kWasmStructNominalCode: return "struct-nominal";
case kWasmArrayNominalCode: return "array-nominal";
default: return "unknown";
// clang-format on
}
@ -622,13 +619,6 @@ class ModuleDecoderTemplate : public Decoder {
const ArrayType* type = consume_array(module_->signature_zone.get());
return {type, kNoSuperType};
}
case kWasmFunctionNominalCode:
case kWasmArrayNominalCode:
case kWasmStructNominalCode:
tracer_.NextLine();
errorf(pc() - 1,
"mixing nominal and isorecursive types is not allowed");
return {};
default:
tracer_.NextLine();
errorf(pc() - 1, "unknown type form: %d", kind);
@ -645,67 +635,6 @@ class ModuleDecoderTemplate : public Decoder {
return true;
}
TypeDefinition consume_nominal_type_definition() {
DCHECK(enabled_features_.has_gc());
size_t num_types = module_->types.size();
uint8_t kind = consume_u8(" kind: ", tracer_);
tracer_.Description(TypeKindName(kind));
switch (kind) {
case kWasmFunctionNominalCode: {
const FunctionSig* sig = consume_sig(module_->signature_zone.get());
uint32_t super_index = kNoSuperType;
HeapType super_type = consume_super_type();
if (super_type.is_index()) {
super_index = super_type.representation();
} else if (V8_UNLIKELY(super_type != HeapType::kFunc)) {
errorf(pc() - 1, "type %zu: invalid supertype %d", num_types,
super_type.code());
return {};
}
return {sig, super_index};
}
case kWasmStructNominalCode: {
const StructType* type = consume_struct(module_->signature_zone.get());
uint32_t super_index = kNoSuperType;
HeapType super_type = consume_super_type();
if (super_type.is_index()) {
super_index = super_type.representation();
} else if (V8_UNLIKELY(super_type != HeapType::kData)) {
errorf(pc() - 1, "type %zu: invalid supertype %d", num_types,
super_type.code());
return {};
}
return {type, super_index};
}
case kWasmArrayNominalCode: {
const ArrayType* type = consume_array(module_->signature_zone.get());
uint32_t super_index = kNoSuperType;
HeapType super_type = consume_super_type();
if (super_type.is_index()) {
super_index = super_type.representation();
} else if (V8_UNLIKELY(super_type != HeapType::kData)) {
errorf(pc() - 1, "type %zu: invalid supertype %d", num_types,
super_type.code());
return {};
}
return {type, super_index};
}
case kWasmFunctionTypeCode:
case kWasmArrayTypeCode:
case kWasmStructTypeCode:
case kWasmSubtypeCode:
case kWasmRecursiveTypeGroupCode:
tracer_.NextLine();
errorf(pc() - 1,
"mixing nominal and isorecursive types is not allowed");
return {};
default:
tracer_.NextLine();
errorf(pc() - 1, "unknown type form: %d", kind);
return {};
}
}
TypeDefinition consume_subtype_definition() {
DCHECK(enabled_features_.has_gc());
uint8_t kind = read_u8<Decoder::kFullValidation>(pc(), "type kind");
@ -761,9 +690,6 @@ class ModuleDecoderTemplate : public Decoder {
case kWasmStructTypeCode:
case kWasmSubtypeCode:
case kWasmRecursiveTypeGroupCode:
case kWasmFunctionNominalCode:
case kWasmStructNominalCode:
case kWasmArrayNominalCode:
errorf(
"Unknown type code 0x%02x, enable with --experimental-wasm-gc",
opcode);
@ -777,62 +703,37 @@ class ModuleDecoderTemplate : public Decoder {
return;
}
if (types_count > 0) {
uint8_t first_type_opcode = this->read_u8<Decoder::kFullValidation>(pc());
if (first_type_opcode == kWasmFunctionNominalCode ||
first_type_opcode == kWasmStructNominalCode ||
first_type_opcode == kWasmArrayNominalCode) {
// wasm-gc nominal type section decoding.
// In a nominal module, all types belong in the same recursive group. We
// use the type vector's capacity to mark the end of the current
// recursive group.
module_->types.reserve(types_count);
for (uint32_t i = 0; ok() && i < types_count; ++i) {
TRACE("DecodeType[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
for (uint32_t i = 0; ok() && i < types_count; ++i) {
TRACE("DecodeType[%d] module+%d\n", i, static_cast<int>(pc_ - start_));
uint8_t kind = read_u8<Decoder::kFullValidation>(pc(), "type kind");
if (kind == kWasmRecursiveTypeGroupCode) {
consume_bytes(1, "rec. group definition", tracer_);
tracer_.NextLine();
uint32_t group_size =
consume_count("recursive group size", kV8MaxWasmTypes);
if (module_->types.size() + group_size > kV8MaxWasmTypes) {
errorf(pc(), "Type definition count exceeds maximum %zu",
kV8MaxWasmTypes);
return;
}
// Reserve space for the current recursive group, so we are
// allowed to reference its elements.
module_->types.reserve(module_->types.size() + group_size);
for (uint32_t j = 0; j < group_size; j++) {
tracer_.TypeOffset(pc_offset());
TypeDefinition type = consume_nominal_type_definition();
TypeDefinition type = consume_subtype_definition();
if (ok()) module_->add_type(type);
}
if (ok() && FLAG_wasm_type_canonicalization) {
type_canon->AddRecursiveGroup(module_.get(), types_count);
type_canon->AddRecursiveGroup(module_.get(), group_size);
}
} else {
// wasm-gc isorecursive type section decoding.
for (uint32_t i = 0; ok() && i < types_count; ++i) {
TRACE("DecodeType[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
uint8_t kind = read_u8<Decoder::kFullValidation>(pc(), "type kind");
if (kind == kWasmRecursiveTypeGroupCode) {
consume_bytes(1, "rec. group definition", tracer_);
tracer_.NextLine();
uint32_t group_size =
consume_count("recursive group size", kV8MaxWasmTypes);
if (module_->types.size() + group_size > kV8MaxWasmTypes) {
errorf(pc(), "Type definition count exceeds maximum %zu",
kV8MaxWasmTypes);
return;
}
// Reserve space for the current recursive group, so we are
// allowed to reference its elements.
module_->types.reserve(module_->types.size() + group_size);
for (uint32_t j = 0; j < group_size; j++) {
tracer_.TypeOffset(pc_offset());
TypeDefinition type = consume_subtype_definition();
if (ok()) module_->add_type(type);
}
if (ok() && FLAG_wasm_type_canonicalization) {
type_canon->AddRecursiveGroup(module_.get(), group_size);
}
} else {
tracer_.TypeOffset(pc_offset());
TypeDefinition type = consume_subtype_definition();
if (ok()) {
module_->add_type(type);
if (FLAG_wasm_type_canonicalization) {
type_canon->AddRecursiveGroup(module_.get(), 1);
}
}
tracer_.TypeOffset(pc_offset());
TypeDefinition type = consume_subtype_definition();
if (ok()) {
module_->add_type(type);
if (FLAG_wasm_type_canonicalization) {
type_canon->AddRecursiveGroup(module_.get(), 1);
}
}
}
@ -846,16 +747,11 @@ class ModuleDecoderTemplate : public Decoder {
// {consume_super_type} has checked this.
DCHECK_LT(explicit_super, module_->types.size());
int depth = GetSubtypingDepth(module, i);
DCHECK_GE(depth, 0);
if (depth > static_cast<int>(kV8MaxRttSubtypingDepth)) {
errorf("type %d: subtyping depth is greater than allowed", i);
continue;
}
// TODO(7748): Replace this with a DCHECK once we reject inheritance
// cycles for nominal modules.
if (depth == -1) {
errorf("type %d: cyclic inheritance", i);
continue;
}
if (!ValidSubtypeDefinition(i, explicit_super, module, module)) {
errorf("type %d has invalid explicit supertype %d", i, explicit_super);
continue;

View File

@ -61,9 +61,6 @@ enum ValueTypeCode : uint8_t {
constexpr uint8_t kWasmFunctionTypeCode = 0x60;
constexpr uint8_t kWasmStructTypeCode = 0x5f;
constexpr uint8_t kWasmArrayTypeCode = 0x5e;
constexpr uint8_t kWasmFunctionNominalCode = 0x5d;
constexpr uint8_t kWasmStructNominalCode = 0x5c;
constexpr uint8_t kWasmArrayNominalCode = 0x5b;
constexpr uint8_t kWasmSubtypeCode = 0x50;
constexpr uint8_t kWasmRecursiveTypeGroupCode = 0x4f;

View File

@ -8,7 +8,6 @@
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
var builder = new WasmModuleBuilder();
builder.setNominal();
let supertype = builder.addStruct([makeField(kWasmI32, true)]);
let subtype = builder.addStruct(
[makeField(kWasmI32, true), makeField(kWasmI32, true)], supertype);

View File

@ -9,7 +9,6 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestNominalTypesBasic() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
builder.setNominal();
let struct1 = builder.addStruct([makeField(kWasmI32, true)]);
let struct2 = builder.addStruct(
[makeField(kWasmI32, true), makeField(kWasmI32, true)], struct1);
@ -39,7 +38,6 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestSubtypingDepthTooLarge() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.setNominal();
builder.addStruct([]);
for (let i = 0; i < 32; i++) builder.addStruct([], i);
assertThrows(
@ -50,7 +48,6 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestArrayNewDataStatic() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.setNominal();
builder.setEarlyDataCountSection();
let array_type_index = builder.addArray(kWasmI16, true);
@ -110,7 +107,6 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestArrayNewData() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.setNominal();
builder.setEarlyDataCountSection();
let array_type_index = builder.addArray(kWasmI16, true);

View File

@ -363,7 +363,6 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function AllocationFolding() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
builder.setNominal();
let struct_index = builder.addStruct([makeField(kWasmI32, true)]);
let struct_2 = builder.addStruct([

View File

@ -77,7 +77,6 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function ImmutableLoadThroughEffect() {
var builder = new WasmModuleBuilder();
builder.setNominal();
var struct = builder.addStruct([
makeField(kWasmI32, false), makeField(kWasmI32, true)]);

View File

@ -7,6 +7,7 @@
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
let builder = new WasmModuleBuilder();
builder.setSingletonRecGroups();
let struct_index = builder.addStruct([makeField(kWasmI32, true)]);
let identical_struct_index = builder.addStruct([makeField(kWasmI32, true)]);

View File

@ -79,9 +79,6 @@ let kLocalNamesCode = 2;
let kWasmFunctionTypeForm = 0x60;
let kWasmStructTypeForm = 0x5f;
let kWasmArrayTypeForm = 0x5e;
let kWasmFunctionNominalForm = 0x5d;
let kWasmStructNominalForm = 0x5c;
let kWasmArrayNominalForm = 0x5b;
let kWasmSubtypeForm = 0x50;
let kWasmRecursiveTypeGroupForm = 0x4f;
@ -1265,7 +1262,11 @@ class WasmModuleBuilder {
this.num_imported_globals = 0;
this.num_imported_tables = 0;
this.num_imported_tags = 0;
this.nominal = false; // Controls only how gc-modules are printed.
// If a wasm-gc type is detected, all types are put by default into a single
// recursive group. This field overrides this behavior and puts each type in
// a separate rec. group instead.
// TODO(7748): Support more flexible rec. groups.
this.singleton_rec_groups = false;
this.early_data_count_section = false;
return this;
}
@ -1327,11 +1328,11 @@ class WasmModuleBuilder {
// TODO(7748): Support recursive groups.
// TODO(7748): Support function supertypes.
addType(type) {
this.types.push(type);
addType(type, supertype_idx = kNoSuperType) {
var pl = type.params.length; // should have params
var rl = type.results.length; // should have results
type.supertype = supertype_idx;
this.types.push(type);
return this.types.length - 1;
}
@ -1592,8 +1593,8 @@ class WasmModuleBuilder {
return this;
}
setNominal() {
this.nominal = true;
setSingletonRecGroups() {
this.singleton_rec_groups = true;
}
setEarlyDataCountSection() {
@ -1616,54 +1617,35 @@ class WasmModuleBuilder {
if (wasm.types.length > 0) {
if (debug) print('emitting types @ ' + binary.length);
binary.emit_section(kTypeSectionCode, section => {
// If any type is a wasm-gc type, wrap everything in a recursive group.
// TODO(7748): Support more flexible rec. groups.
if (!this.singleton_rec_groups &&
wasm.types.findIndex(type => type instanceof WasmStruct ||
type instanceof WasmArray) >= 0) {
section.emit_u32v(1);
section.emit_u8(kWasmRecursiveTypeGroupForm);
}
section.emit_u32v(wasm.types.length);
for (let type of wasm.types) {
if (type.supertype != kNoSuperType) {
section.emit_u8(kWasmSubtypeForm);
section.emit_u8(1); // supertype count
section.emit_u32v(type.supertype);
}
if (type instanceof WasmStruct) {
if (!this.nominal && type.supertype != kNoSuperType) {
section.emit_u8(kWasmSubtypeForm);
section.emit_u8(1); // supertype count
section.emit_u32v(type.supertype);
}
section.emit_u8(this.nominal ? kWasmStructNominalForm
: kWasmStructTypeForm);
section.emit_u8(kWasmStructTypeForm);
section.emit_u32v(type.fields.length);
for (let field of type.fields) {
section.emit_type(field.type);
section.emit_u8(field.mutability ? 1 : 0);
}
if (this.nominal) {
if (type.supertype === kNoSuperType) {
section.emit_u8(kDataRefCode);
} else {
section.emit_heap_type(type.supertype);
}
}
} else if (type instanceof WasmArray) {
if (!this.nominal && type.supertype != kNoSuperType) {
section.emit_u8(kWasmSubtypeForm);
section.emit_u8(1); // supertype count
section.emit_u32v(type.supertype);
}
section.emit_u8(this.nominal ? kWasmArrayNominalForm
: kWasmArrayTypeForm);
section.emit_u8(kWasmArrayTypeForm);
section.emit_type(type.type);
section.emit_u8(type.mutability ? 1 : 0);
if (this.nominal) {
if (type.supertype === kNoSuperType) {
section.emit_u8(kDataRefCode);
} else {
section.emit_heap_type(type.supertype);
}
}
} else {
/* TODO(7748): Support function supertypes.
if (!this.nominal && type.supertype != kNoSuperType) {
section.emit_u8(kWasmSubtypeForm);
section.emit_u8(1); // supertype count
section.emit_u32v(type.supertype);
} */
section.emit_u8(this.nominal ? kWasmFunctionNominalForm
: kWasmFunctionTypeForm);
section.emit_u8(kWasmFunctionTypeForm);
section.emit_u32v(type.params.length);
for (let param of type.params) {
section.emit_type(param);
@ -1672,15 +1654,6 @@ class WasmModuleBuilder {
for (let result of type.results) {
section.emit_type(result);
}
if (this.nominal) {
/* TODO(7748): Support function supertypes.
if (type.supertype === kNoSuperType) {
section.emit_u8(kFuncRefCode);
} else {
section.emit_heap_type(type.supertype);
}*/
section.emit_u8(kFuncRefCode);
}
}
}
});

View File

@ -857,13 +857,13 @@ TEST_F(WasmModuleVerifyTest, ArrayNewFixedInitExpr) {
0, 3, WASM_I32V(10), WASM_I32V(20), WASM_I32V(30)))};
EXPECT_VERIFIES(basic);
static const byte basic_nominal[] = {
static const byte basic_static[] = {
SECTION(Type, ENTRY_COUNT(1), WASM_ARRAY_DEF(kI16Code, true)),
SECTION(Global, ENTRY_COUNT(1), // --
kRefCode, 0, 0, // type, mutability
WASM_INIT_EXPR_ARRAY_NEW_FIXED_STATIC(
0, 3, WASM_I32V(10), WASM_I32V(20), WASM_I32V(30)))};
EXPECT_VERIFIES(basic_nominal);
EXPECT_VERIFIES(basic_static);
static const byte basic_immutable[] = {
SECTION(Type, ENTRY_COUNT(1), WASM_ARRAY_DEF(kI32Code, false)),
@ -987,136 +987,6 @@ TEST_F(WasmModuleVerifyTest, InvalidStructTypeDef) {
EXPECT_FAILURE_WITH_MSG(invalid_mutability, "invalid mutability");
}
TEST_F(WasmModuleVerifyTest, NominalStructTypeDef) {
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
// Inheritance: t1 <: t2 <: t0
static const byte all_good[] = {
SECTION(Type, ENTRY_COUNT(3), // --
kWasmStructNominalCode, // type #0
1, // field count
kI32Code, 1, // mut i32
kDataRefCode, // root of type hierarchy
kWasmStructNominalCode, // type #1
2, // field count
kI32Code, 1, // mut i32 (inherited)
kI64Code, 1, // mut i32 (added)
2, // supertype
kWasmStructNominalCode, // type #2
1, // field count
kI32Code, 1, // mut i32 (inherited)
0)}; // supertype
EXPECT_VERIFIES(all_good);
ModuleResult result = DecodeModule(all_good, all_good + sizeof(all_good));
EXPECT_OK(result);
WasmModule* module = result.value().get();
EXPECT_EQ(kNoSuperType, module->supertype(0));
EXPECT_EQ(2u, module->supertype(1));
EXPECT_EQ(0u, module->supertype(2));
static const byte self_or_mutual_ref[] = {
SECTION(Type, ENTRY_COUNT(4), // --
kWasmStructNominalCode, 0, // empty struct
kDataRefCode, // root of hierarchy
kWasmStructNominalCode, // type1
1, // field count
kRefNullCode, 1, 1, // mut (ref null type1)
0, // supertype
kWasmStructNominalCode, // type 2
1, // field count
kRefNullCode, 3, 1, // mut (ref null type3)
0, // supertype
kWasmStructNominalCode, // type 3
1, // field count
kRefNullCode, 2, 1, // mut (ref null type2)
0)}; // supertype
EXPECT_VERIFIES(self_or_mutual_ref);
static const byte mutual_ref_with_subtyping[] = {
SECTION(Type,
ENTRY_COUNT(3), // --
kWasmStructNominalCode, //
1, // field count
kRefNullCode, 0, 0, // ref type0
kDataRefCode, // root of hierarchy
kWasmStructNominalCode, // --
1, // field count
kRefNullCode, 2, 0, // ref type2
0, // supertype
kWasmStructNominalCode, // --
1, // field count
kRefNullCode, 1, 0, // ref type1
0)}; // supertype
EXPECT_VERIFIES(mutual_ref_with_subtyping);
static const byte inheritance_cycle[] = {
SECTION(Type, ENTRY_COUNT(2), // --
kWasmStructNominalCode, 0, 1, // no fields, supertype 1
kWasmStructNominalCode, 0, 0)}; // no fields, supertype 0
EXPECT_FAILURE_WITH_MSG(inheritance_cycle, "cyclic inheritance");
static const byte invalid_field[] = {
SECTION(Type, ENTRY_COUNT(2), // --
kWasmStructTypeCode, U32V_1(1), kI32Code, 1, // t0: [i32]
kWasmStructNominalCode, U32V_1(2), // t1:
kI64Code, 1, // i64 (invalid inheritance)
kI32Code, 1, U32V_1(0))}; // i32 (added), supertype 0
EXPECT_FAILURE_WITH_MSG(
invalid_field, "mixing nominal and isorecursive types is not allowed");
static const byte structural_supertype[] = {
SECTION(Type, ENTRY_COUNT(2), // --
kWasmStructTypeCode, 0, // empty struct
kWasmStructNominalCode, 0, // also empty
0)}; // supertype is structural type
EXPECT_FAILURE_WITH_MSG(
structural_supertype,
"mixing nominal and isorecursive types is not allowed");
static const byte supertype_oob[] = {
SECTION(Type, ENTRY_COUNT(1), // --
kWasmStructNominalCode,
0, // empty struct
13)}; // supertype with invalid index
EXPECT_FAILURE_WITH_MSG(supertype_oob, "Type index 13 is out of bounds");
}
TEST_F(WasmModuleVerifyTest, NominalFunctionTypeDef) {
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(gc); // Needed for subtype checking.
static const byte all_good[] = {
SECTION(Type, ENTRY_COUNT(2), // --
kWasmFunctionNominalCode, // type #0
1, // params count
kRefCode, 0, // ref #0
1, // results count
kRefNullCode, 0, // (ref null 0)
kFuncRefCode, // root of type hierarchy
kWasmFunctionNominalCode, // type #1
1, // params count
kRefNullCode, 0, // refined (contravariant)
1, // results count
kRefCode, 0, // refined (covariant)
0)}; // supertype
EXPECT_VERIFIES(all_good);
ModuleResult result = DecodeModule(all_good, all_good + sizeof(all_good));
EXPECT_OK(result);
WasmModule* module = result.value().get();
EXPECT_EQ(kNoSuperType, module->supertype(0));
EXPECT_EQ(0u, module->supertype(1));
}
TEST_F(WasmModuleVerifyTest, InvalidArrayTypeDef) {
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);