[wasm-gc] Implement iso-recursive types
This CL replaces the equirecursive type system for wasm-gc with the
isorecursive hybrid type system presented here:
https://github.com/WebAssembly/gc/issues/257.
In broad strokes, this includes the following changes:
- In the module decoder, remove equirecursive types. Implement recursive
type groups, subtype definitions, and function/struct/array
definitions. Treat nominal modules as syntactic sugar of an
isorecursive module, where all types belong in the same recursive
group.
- Remove rtt.sub and all related infrastructure.
- Change subtyping to work with explicit supertypes only.
- Add ValidSubtypeDefinition in subtyping, to check that subtype
declarations are valid during decoding.
- Remove the subtyping cache.
- Add support for functions to have specific signature index in
WasmModuleBuilder and in test-gc.cc.
- Adapt tests.
Current restrictions:
- Recursive groups are not stored beyond decoding.
- Type canonicalization is not implemented. No tests relying on types
being considered identical post-canonicalization.
- No cross-module subtyping is possible. Tests relying on cross-module
subtyping have been commented out.
Bug: v8:7748
Change-Id: I69fd04ecc5611f6230c95d5c89d1c520163fffae
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3416239
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78816}
diff --git a/src/builtins/wasm.tq b/src/builtins/wasm.tq
index 5f46124..99e1591 100644
--- a/src/builtins/wasm.tq
+++ b/src/builtins/wasm.tq
@@ -34,7 +34,6 @@
Context, WasmInstanceObject, Number, Number, BigInt): Smi;
extern runtime WasmI64AtomicWait(
Context, WasmInstanceObject, Number, BigInt, BigInt): Smi;
-extern runtime WasmAllocateRtt(Context, Smi, Map, Smi): Map;
extern runtime WasmArrayCopy(
Context, WasmArray, Smi, WasmArray, Smi, Smi): JSAny;
extern runtime WasmArrayInitFromData(
@@ -54,10 +53,6 @@
const kExternNonNullTableType: constexpr int31
generates 'wasm::kWasmExternNonNullableRef.raw_bit_field()';
-const kRttSubCanonicalize: constexpr int31
- generates 'WasmRttSubMode::kCanonicalize';
-const kRttSubFresh: constexpr int31 generates 'WasmRttSubMode::kFresh';
-
extern macro WasmBuiltinsAssembler::LoadInstanceFromFrame(): WasmInstanceObject;
// WasmInstanceObject has a field layout that Torque can't handle yet.
@@ -306,18 +301,6 @@
return AllocateJSArray(ElementsKind::PACKED_ELEMENTS, map, size, size);
}
-builtin WasmAllocateRtt(typeIndex: intptr, parent: Map): Map {
- tail runtime::WasmAllocateRtt(
- LoadContextFromFrame(), SmiTag(typeIndex), parent,
- SmiConstant(kRttSubCanonicalize));
-}
-
-builtin WasmAllocateFreshRtt(typeIndex: intptr, parent: Map): Map {
- tail runtime::WasmAllocateRtt(
- LoadContextFromFrame(), SmiTag(typeIndex), parent,
- SmiConstant(kRttSubFresh));
-}
-
builtin WasmAllocateStructWithRtt(rtt: Map): HeapObject {
const typeInfo: WasmTypeInfo = %RawDownCast<WasmTypeInfo>(
rtt.constructor_or_back_pointer_or_native_context);
diff --git a/src/common/globals.h b/src/common/globals.h
index a71775b..64fcff2 100644
--- a/src/common/globals.h
+++ b/src/common/globals.h
@@ -1772,8 +1772,6 @@
V(TrapArrayOutOfBounds) \
V(TrapArrayTooLarge)
-enum WasmRttSubMode { kCanonicalize, kFresh };
-
enum KeyedAccessLoadMode {
STANDARD_LOAD,
LOAD_IGNORE_OUT_OF_BOUNDS,
diff --git a/src/compiler/loop-analysis.cc b/src/compiler/loop-analysis.cc
index 14acd92..2b9b5fa 100644
--- a/src/compiler/loop-analysis.cc
+++ b/src/compiler/loop-analysis.cc
@@ -629,8 +629,7 @@
WasmCode::kWasmAllocateFixedArray, WasmCode::kWasmThrow,
WasmCode::kWasmRethrow, WasmCode::kWasmRethrowExplicitContext,
// Fast wasm-gc operations.
- WasmCode::kWasmRefFunc, WasmCode::kWasmAllocateRtt,
- WasmCode::kWasmAllocateFreshRtt};
+ WasmCode::kWasmRefFunc};
if (std::count(unrollable_builtins,
unrollable_builtins + arraysize(unrollable_builtins),
info) == 0) {
diff --git a/src/compiler/node-properties.cc b/src/compiler/node-properties.cc
index 1cd60b0..5e55cdf 100644
--- a/src/compiler/node-properties.cc
+++ b/src/compiler/node-properties.cc
@@ -620,7 +620,7 @@
Builtin callee = static_cast<Builtin>(matcher.ResolvedValue());
// Note: Make sure to only add builtins which are guaranteed to return a
// fresh object. E.g. kWasmAllocateFixedArray may return the canonical
- // empty array, and kWasmAllocateRtt may return a cached rtt.
+ // empty array.
return callee == Builtin::kWasmAllocateArray_Uninitialized ||
callee == Builtin::kWasmAllocateArray_InitNull ||
callee == Builtin::kWasmAllocateArray_InitZero ||
diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc
index d435dac..9109ae2 100644
--- a/src/compiler/wasm-compiler.cc
+++ b/src/compiler/wasm-compiler.cc
@@ -5655,15 +5655,6 @@
wasm::ObjectAccess::ElementOffsetInTaggedFixedArray(type_index));
}
-Node* WasmGraphBuilder::RttSub(uint32_t type_index, Node* parent_rtt,
- WasmRttSubMode mode) {
- Builtin target = mode == WasmRttSubMode::kCanonicalize
- ? Builtin::kWasmAllocateRtt
- : Builtin::kWasmAllocateFreshRtt;
- return gasm_->CallBuiltin(target, Operator::kEliminatable,
- Int32Constant(type_index), parent_rtt);
-}
-
WasmGraphBuilder::Callbacks WasmGraphBuilder::TestCallbacks(
GraphAssemblerLabel<1>* label) {
return {// succeed_if
diff --git a/src/compiler/wasm-compiler.h b/src/compiler/wasm-compiler.h
index f9fb89f..dbfa99a 100644
--- a/src/compiler/wasm-compiler.h
+++ b/src/compiler/wasm-compiler.h
@@ -514,7 +514,6 @@
Node* I31GetS(Node* input);
Node* I31GetU(Node* input);
Node* RttCanon(uint32_t type_index);
- Node* RttSub(uint32_t type_index, Node* parent_rtt, WasmRttSubMode mode);
Node* RefTest(Node* object, Node* rtt, ObjectReferenceKnowledge config);
Node* RefCast(Node* object, Node* rtt, ObjectReferenceKnowledge config,
diff --git a/src/runtime/runtime-wasm.cc b/src/runtime/runtime-wasm.cc
index 0775d1a..e6a2331 100644
--- a/src/runtime/runtime-wasm.cc
+++ b/src/runtime/runtime-wasm.cc
@@ -639,19 +639,6 @@
return ReadOnlyRoots(isolate).undefined_value();
}
-RUNTIME_FUNCTION(Runtime_WasmAllocateRtt) {
- ClearThreadInWasmScope flag_scope(isolate);
- HandleScope scope(isolate);
- DCHECK_EQ(3, args.length());
- CONVERT_UINT32_ARG_CHECKED(type_index, 0);
- CONVERT_ARG_HANDLE_CHECKED(Map, parent, 1);
- CONVERT_SMI_ARG_CHECKED(raw_mode, 2);
- Handle<WasmInstanceObject> instance(GetWasmInstanceOnStackTop(isolate),
- isolate);
- return *wasm::AllocateSubRtt(isolate, instance, type_index, parent,
- static_cast<WasmRttSubMode>(raw_mode));
-}
-
namespace {
inline void* ArrayElementAddress(Handle<WasmArray> array, uint32_t index,
int element_size_bytes) {
diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
index 37ecee1..7259b66d 100644
--- a/src/runtime/runtime.h
+++ b/src/runtime/runtime.h
@@ -587,7 +587,6 @@
F(WasmCompileWrapper, 2, 1) \
F(WasmTriggerTierUp, 1, 1) \
F(WasmDebugBreak, 0, 1) \
- F(WasmAllocateRtt, 3, 1) \
F(WasmArrayCopy, 5, 1) \
F(WasmArrayInitFromData, 5, 1) \
F(WasmAllocateContinuation, 1, 1) \
diff --git a/src/wasm/baseline/liftoff-compiler.cc b/src/wasm/baseline/liftoff-compiler.cc
index 6c192fa..dcf5a3a 100644
--- a/src/wasm/baseline/liftoff-compiler.cc
+++ b/src/wasm/baseline/liftoff-compiler.cc
@@ -5449,27 +5449,6 @@
__ PushRegister(kRttWithDepth, rtt);
}
- void RttSub(FullDecoder* decoder, uint32_t type_index, const Value& parent,
- Value* result, WasmRttSubMode mode) {
- ValueKind parent_value_kind = parent.type.kind();
- ValueKind rtt_value_kind = kRttWithDepth;
- LiftoffRegister type_reg = __ GetUnusedRegister(kGpReg, {});
- LiftoffAssembler::VarState parent_var =
- __ cache_state()->stack_state.end()[-1];
- __ LoadConstant(type_reg, WasmValue(type_index));
- LiftoffAssembler::VarState type_var(kI32, type_reg, 0);
- WasmCode::RuntimeStubId target = mode == WasmRttSubMode::kCanonicalize
- ? WasmCode::kWasmAllocateRtt
- : WasmCode::kWasmAllocateFreshRtt;
- CallRuntimeStub(
- target,
- MakeSig::Returns(rtt_value_kind).Params(kI32, parent_value_kind),
- {type_var, parent_var}, decoder->position());
- // Drop the parent RTT.
- __ cache_state()->stack_state.pop_back(1);
- __ PushRegister(rtt_value_kind, LiftoffRegister(kReturnRegister0));
- }
-
enum NullSucceeds : bool { // --
kNullSucceeds = true,
kNullFails = false
diff --git a/src/wasm/function-body-decoder-impl.h b/src/wasm/function-body-decoder-impl.h
index 055885f..312e229 100644
--- a/src/wasm/function-body-decoder-impl.h
+++ b/src/wasm/function-body-decoder-impl.h
@@ -944,8 +944,6 @@
const IndexImmediate<validate>& data_segment, const Value& offset, \
const Value& length, const Value& rtt, Value* result) \
F(RttCanon, uint32_t type_index, Value* result) \
- F(RttSub, uint32_t type_index, const Value& parent, Value* result, \
- WasmRttSubMode mode) \
F(DoReturn, uint32_t drop_values)
#define INTERFACE_NON_CONSTANT_FUNCTIONS(F) /* force 80 columns */ \
@@ -1900,8 +1898,6 @@
return length + imm.length;
}
case kExprRttCanon:
- case kExprRttSub:
- case kExprRttFreshSub:
case kExprRefTestStatic:
case kExprRefCastStatic:
case kExprBrOnCastStatic:
@@ -2068,8 +2064,6 @@
case kExprI31GetU:
case kExprArrayNewDefault:
case kExprArrayLen:
- case kExprRttSub:
- case kExprRttFreshSub:
case kExprRefTestStatic:
case kExprRefCastStatic:
case kExprBrOnCastStatic:
@@ -4499,39 +4493,6 @@
Push(value);
return opcode_length + imm.length;
}
- case kExprRttFreshSub:
- case kExprRttSub: {
- IndexImmediate<validate> imm(this, this->pc_ + opcode_length,
- "type index");
- if (!this->ValidateType(this->pc_ + opcode_length, imm)) return 0;
- Value parent = Peek(0, 0);
- if (parent.type.is_bottom()) {
- DCHECK(!current_code_reachable_and_ok_);
- // Just leave the unreachable/bottom value on the stack.
- } else {
- if (!VALIDATE(parent.type.is_rtt() &&
- IsHeapSubtypeOf(imm.index, parent.type.ref_index(),
- this->module_))) {
- PopTypeError(
- 0, parent,
- "rtt for a supertype of type " + std::to_string(imm.index));
- return 0;
- }
- Value value = parent.type.has_depth()
- ? CreateValue(ValueType::Rtt(
- imm.index, parent.type.depth() + 1))
- : CreateValue(ValueType::Rtt(imm.index));
-
- WasmRttSubMode mode = opcode == kExprRttSub
- ? WasmRttSubMode::kCanonicalize
- : WasmRttSubMode::kFresh;
- CALL_INTERFACE_IF_OK_AND_REACHABLE(RttSub, imm.index, parent, &value,
- mode);
- Drop(parent);
- Push(value);
- }
- return opcode_length + imm.length;
- }
case kExprRefTest:
case kExprRefTestStatic: {
NON_CONST_ONLY
diff --git a/src/wasm/graph-builder-interface.cc b/src/wasm/graph-builder-interface.cc
index eec52e6..f60258a 100644
--- a/src/wasm/graph-builder-interface.cc
+++ b/src/wasm/graph-builder-interface.cc
@@ -1138,11 +1138,6 @@
result->node = builder_->RttCanon(type_index);
}
- void RttSub(FullDecoder* decoder, uint32_t type_index, const Value& parent,
- Value* result, WasmRttSubMode mode) {
- result->node = builder_->RttSub(type_index, parent.node, mode);
- }
-
using StaticKnowledge = compiler::WasmGraphBuilder::ObjectReferenceKnowledge;
StaticKnowledge ComputeStaticKnowledge(ValueType object_type,
diff --git a/src/wasm/init-expr-interface.cc b/src/wasm/init-expr-interface.cc
index 2de5496..ad9f243 100644
--- a/src/wasm/init-expr-interface.cc
+++ b/src/wasm/init-expr-interface.cc
@@ -195,20 +195,6 @@
ValueType::Rtt(type_index, 0));
}
-void InitExprInterface::RttSub(FullDecoder* decoder, uint32_t type_index,
- const Value& parent, Value* result,
- WasmRttSubMode mode) {
- if (!generate_result()) return;
- ValueType type = parent.type.has_depth()
- ? ValueType::Rtt(type_index, parent.type.depth() + 1)
- : ValueType::Rtt(type_index);
- result->runtime_value =
- WasmValue(Handle<Object>::cast(AllocateSubRtt(
- isolate_, instance_, type_index,
- Handle<Map>::cast(parent.runtime_value.to_ref()), mode)),
- type);
-}
-
void InitExprInterface::DoReturn(FullDecoder* decoder,
uint32_t /*drop_values*/) {
end_found_ = true;
diff --git a/src/wasm/module-decoder.cc b/src/wasm/module-decoder.cc
index 1f47f7a..463e1f1 100644
--- a/src/wasm/module-decoder.cc
+++ b/src/wasm/module-decoder.cc
@@ -554,109 +554,183 @@
}
}
+ TypeDefinition consume_base_type_definition() {
+ DCHECK(enabled_features_.has_gc());
+ uint8_t kind = consume_u8("type kind");
+ switch (kind) {
+ case kWasmFunctionTypeCode: {
+ const FunctionSig* sig = consume_sig(module_->signature_zone.get());
+ return {sig, kNoSuperType};
+ }
+ case kWasmStructTypeCode: {
+ const StructType* type = consume_struct(module_->signature_zone.get());
+ return {type, kNoSuperType};
+ }
+ case kWasmArrayTypeCode: {
+ const ArrayType* type = consume_array(module_->signature_zone.get());
+ return {type, kNoSuperType};
+ }
+ case kWasmFunctionNominalCode:
+ case kWasmArrayNominalCode:
+ case kWasmStructNominalCode:
+ errorf(pc() - 1,
+ "mixing nominal and isorecursive types is not allowed");
+ return {};
+ default:
+ errorf(pc() - 1, "unknown type form: %d", kind);
+ return {};
+ }
+ }
+
+ TypeDefinition consume_nominal_type_definition() {
+ DCHECK(enabled_features_.has_gc());
+ size_t num_types = module_->types.size();
+ uint8_t kind = consume_u8("type 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:
+ errorf(pc() - 1,
+ "mixing nominal and isorecursive types is not allowed");
+ return {};
+ default:
+ 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");
+ if (kind == kWasmSubtypeCode) {
+ consume_bytes(1, "subtype definition");
+ constexpr uint32_t kMaximumSupertypes = 1;
+ uint32_t supertype_count =
+ consume_count("supertype count", kMaximumSupertypes);
+ uint32_t supertype =
+ supertype_count == 1 ? consume_u32v("supertype") : kNoSuperType;
+ if (V8_UNLIKELY(supertype >= module_->types.capacity())) {
+ errorf(pc(), "type %zu: invalid supertype %d", module_->types.size(),
+ supertype);
+ return {};
+ }
+ TypeDefinition type = consume_base_type_definition();
+ type.supertype = supertype;
+ return type;
+ } else {
+ return consume_base_type_definition();
+ }
+ }
+
void DecodeTypeSection() {
uint32_t types_count = consume_count("types count", kV8MaxWasmTypes);
- module_->types.reserve(types_count);
- for (uint32_t i = 0; ok() && i < types_count; ++i) {
- TRACE("DecodeSignature[%d] module+%d\n", i,
- static_cast<int>(pc_ - start_));
- uint8_t kind = consume_u8("type kind");
- switch (kind) {
- case kWasmFunctionTypeCode:
- case kWasmFunctionSubtypeCode: {
- const FunctionSig* s = consume_sig(module_->signature_zone.get());
- uint32_t super_index = kNoSuperType;
- if (kind == kWasmFunctionSubtypeCode) {
- if (!enabled_features_.has_gc()) {
- errorf(pc(),
- "invalid function type definition, enable with "
- "--experimental-wasm-gc");
- break;
- }
- HeapType super_type = consume_super_type();
- if (super_type == HeapType::kFunc) {
- super_index = kGenericSuperType;
- } else if (super_type.is_index()) {
- super_index = super_type.representation();
- } else {
- errorf(pc(), "type %d: invalid supertype %d", i,
- super_type.code());
- break;
- }
- }
- module_->add_signature(s, super_index);
- break;
+
+ // Non wasm-gc type section decoding.
+ if (!enabled_features_.has_gc()) {
+ for (uint32_t i = 0; ok() && i < types_count; ++i) {
+ TRACE("DecodeSignature[%d] module+%d\n", i,
+ static_cast<int>(pc_ - start_));
+ expect_u8("signature definition", kWasmFunctionTypeCode);
+ const FunctionSig* sig = consume_sig(module_->signature_zone.get());
+ if (!ok()) break;
+ module_->add_signature(sig, kNoSuperType);
+ }
+ 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_));
+ TypeDefinition type = consume_nominal_type_definition();
+ if (ok()) module_->add_type(type);
}
- case kWasmStructTypeCode:
- case kWasmStructSubtypeCode: {
- if (!enabled_features_.has_gc()) {
- errorf(pc(),
- "invalid struct type definition, enable with "
- "--experimental-wasm-gc");
- break;
- }
- const StructType* s = consume_struct(module_->signature_zone.get());
- uint32_t super_index = kNoSuperType;
- if (kind == kWasmStructSubtypeCode) {
- HeapType super_type = consume_super_type();
- if (super_type == HeapType::kData) {
- super_index = kGenericSuperType;
- } else if (super_type.is_index()) {
- super_index = super_type.representation();
- } else {
- errorf(pc(), "type %d: invalid supertype %d", i,
- super_type.code());
- break;
+ } 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");
+ uint32_t group_size =
+ consume_count("recursive group size", kV8MaxWasmTypes);
+ if (module_->types.size() + group_size > kV8MaxWasmTypes) {
+ errorf(pc(), "Type definition count exeeds maximum %zu",
+ kV8MaxWasmTypes);
+ return;
}
- }
- module_->add_struct_type(s, super_index);
- // TODO(7748): Should we canonicalize struct types, like
- // {signature_map} does for function signatures?
- break;
- }
- case kWasmArrayTypeCode:
- case kWasmArraySubtypeCode: {
- if (!enabled_features_.has_gc()) {
- errorf(pc(),
- "invalid array type definition, enable with "
- "--experimental-wasm-gc");
- break;
- }
- const ArrayType* type = consume_array(module_->signature_zone.get());
- uint32_t super_index = kNoSuperType;
- if (kind == kWasmArraySubtypeCode) {
- HeapType super_type = consume_super_type();
- if (super_type == HeapType::kData) {
- super_index = kGenericSuperType;
- } else if (super_type.is_index()) {
- super_index = super_type.representation();
- } else {
- errorf(pc(), "type %d: invalid supertype %d", i,
- super_type.code());
- break;
+ // 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 i = 0; i < group_size; i++) {
+ TypeDefinition type = consume_subtype_definition();
+ if (ok()) module_->add_type(type);
}
+ } else {
+ TypeDefinition type = consume_subtype_definition();
+ if (ok()) module_->add_type(type);
}
- module_->add_array_type(type, super_index);
- break;
}
- default:
- errorf(pc(), "unknown type form: %d", kind);
- break;
}
}
+
// Check validity of explicitly defined supertypes.
const WasmModule* module = module_.get();
for (uint32_t i = 0; ok() && i < types_count; ++i) {
uint32_t explicit_super = module_->supertype(i);
if (explicit_super == kNoSuperType) continue;
- if (explicit_super == kGenericSuperType) continue;
DCHECK_LT(explicit_super, types_count); // {consume_super_type} checks.
- // Only types that have an explicit supertype themselves can be explicit
- // supertypes of other types.
- if (!module->has_supertype(explicit_super)) {
- errorf("type %d has invalid explicit supertype %d", i, explicit_super);
- continue;
- }
int depth = GetSubtypingDepth(module, i);
if (depth > static_cast<int>(kV8MaxRttSubtypingDepth)) {
errorf("type %d: subtyping depth is greater than allowed", i);
@@ -666,23 +740,10 @@
errorf("type %d: cyclic inheritance", i);
continue;
}
- switch (module_->types[i].kind) {
- case TypeDefinition::kStruct:
- if (!module->has_struct(explicit_super)) break;
- if (!StructIsSubtypeOf(i, explicit_super, module, module)) break;
- continue;
- case TypeDefinition::kArray:
- if (!module->has_array(explicit_super)) break;
- if (!ArrayIsSubtypeOf(i, explicit_super, module, module)) break;
- continue;
- case TypeDefinition::kFunction:
- if (!module->has_signature(explicit_super)) break;
- if (!FunctionIsSubtypeOf(i, explicit_super, module, module)) break;
- continue;
- default:
- UNREACHABLE();
+ if (!ValidSubtypeDefinition(i, explicit_super, module, module)) {
+ errorf("type %d has invalid explicit supertype %d", i, explicit_super);
+ continue;
}
- errorf("type %d has invalid explicit supertype %d", i, explicit_super);
}
module_->signature_map.Freeze();
}
@@ -735,10 +796,9 @@
const byte* type_position = pc();
ValueType type = consume_reference_type();
if (!WasmTable::IsValidTableType(type, module_.get())) {
- error(
- type_position,
- "Currently, only externref and function references are allowed "
- "as table types");
+ error(type_position,
+ "Currently, only externref and function references are "
+ "allowed as table types");
break;
}
table->type = type;
@@ -1234,8 +1294,8 @@
hint.top_tier =
static_cast<WasmCompilationHintTier>(hint_byte >> 4 & 0x3);
- // Ensure that the top tier never downgrades a compilation result.
- // If baseline and top tier are the same compilation will be invoked only
+ // Ensure that the top tier never downgrades a compilation result. If
+ // baseline and top tier are the same compilation will be invoked only
// once.
if (hint.top_tier < hint.baseline_tier &&
hint.top_tier != WasmCompilationHintTier::kDefault) {
@@ -1377,10 +1437,10 @@
ModuleResult FinishDecoding(bool verify_functions = true) {
if (ok() && CheckMismatchedCounts()) {
- // We calculate the global offsets here, because there may not be a global
- // section and code section that would have triggered the calculation
- // before. Even without the globals section the calculation is needed
- // because globals can also be defined in the import section.
+ // We calculate the global offsets here, because there may not be a
+ // global section and code section that would have triggered the
+ // calculation before. Even without the globals section the calculation
+ // is needed because globals can also be defined in the import section.
CalculateGlobalOffsets(module_.get());
}
@@ -1526,9 +1586,9 @@
}
}
- // Calculate individual global offsets and total size of globals table.
- // This function should be called after all globals have been defined, which
- // is after the import section and the global section, but before the global
+ // Calculate individual global offsets and total size of globals table. This
+ // function should be called after all globals have been defined, which is
+ // after the import section and the global section, but before the global
// offsets are accessed, e.g. by the function compilers. The moment when this
// function should be called is not well-defined, as the global section may
// not exist. Therefore this function is called multiple times.
@@ -1745,6 +1805,7 @@
}
}
+ // Consumes a byte, and emits an error if it does not equal {expected}.
bool expect_u8(const char* name, uint8_t expected) {
const byte* pos = pc();
uint8_t value = consume_u8(name);
diff --git a/src/wasm/module-instantiate.cc b/src/wasm/module-instantiate.cc
index 3379286..aab0cc9 100644
--- a/src/wasm/module-instantiate.cc
+++ b/src/wasm/module-instantiate.cc
@@ -223,7 +223,7 @@
// map for that supertype is created first, so that the supertypes list
// that's cached on every RTT can be set up correctly.
uint32_t supertype = module->supertype(type_index);
- if (supertype != kNoSuperType && supertype != kGenericSuperType) {
+ if (supertype != kNoSuperType) {
// This recursion is safe, because kV8MaxRttSubtypingDepth limits the
// number of recursive steps, so we won't overflow the stack.
CreateMapForType(isolate, module, supertype, instance, maps);
@@ -246,73 +246,6 @@
maps->set(type_index, *map);
}
-namespace {
-
-// TODO(7748): Consider storing this array in Maps'
-// "transitions_or_prototype_info" slot.
-// Also consider being more memory-efficient, e.g. use inline storage for
-// single entries, and/or adapt the growth strategy.
-class RttSubtypes : public ArrayList {
- public:
- static Handle<ArrayList> Insert(Isolate* isolate, Handle<ArrayList> array,
- uint32_t type_index, Handle<Map> sub_rtt) {
- Handle<Smi> key = handle(Smi::FromInt(type_index), isolate);
- return Add(isolate, array, key, sub_rtt);
- }
-
- static Map SearchSubtype(Handle<ArrayList> array, uint32_t type_index) {
- // Linear search for now.
- // TODO(7748): Consider keeping the array sorted and using binary search
- // here, if empirical data indicates that that would be worthwhile.
- int count = array->Length();
- for (int i = 0; i < count; i += 2) {
- if (Smi::cast(array->Get(i)).value() == static_cast<int>(type_index)) {
- return Map::cast(array->Get(i + 1));
- }
- }
- return {};
- }
-};
-
-} // namespace
-
-Handle<Map> AllocateSubRtt(Isolate* isolate,
- Handle<WasmInstanceObject> instance, uint32_t type,
- Handle<Map> parent, WasmRttSubMode mode) {
- DCHECK(parent->IsWasmStructMap() || parent->IsWasmArrayMap() ||
- parent->IsWasmInternalFunctionMap());
-
- const wasm::WasmModule* module = instance->module();
- if (module->has_signature(type)) {
- // Function references are implicitly allocated with their canonical rtt,
- // and type checks against sub-rtts will always fail. Therefore, we simply
- // create a fresh function map here.
- return CreateFuncRefMap(isolate, module, Handle<Map>(), instance);
- }
- // If canonicalization is requested, check for an existing RTT first.
- Handle<ArrayList> cache;
- if (mode == WasmRttSubMode::kCanonicalize) {
- cache = handle(parent->wasm_type_info().subtypes(), isolate);
- Map maybe_cached = RttSubtypes::SearchSubtype(cache, type);
- if (!maybe_cached.is_null()) return handle(maybe_cached, isolate);
- }
-
- // Allocate a fresh RTT otherwise.
- Handle<Map> rtt;
- if (module->has_struct(type)) {
- rtt = wasm::CreateStructMap(isolate, module, type, parent, instance);
- } else {
- DCHECK(module->has_array(type));
- rtt = wasm::CreateArrayMap(isolate, module, type, parent, instance);
- }
-
- if (mode == WasmRttSubMode::kCanonicalize) {
- cache = RttSubtypes::Insert(isolate, cache, type, rtt);
- parent->wasm_type_info().set_subtypes(*cache);
- }
- return rtt;
-}
-
// A helper class to simplify instantiating a module from a module object.
// It closes over the {Isolate}, the {ErrorThrower}, etc.
class InstanceBuilder {
diff --git a/src/wasm/wasm-code-manager.h b/src/wasm/wasm-code-manager.h
index fec51c7..e9d3c0d 100644
--- a/src/wasm/wasm-code-manager.h
+++ b/src/wasm/wasm-code-manager.h
@@ -121,8 +121,6 @@
V(WasmArrayCopy) \
V(WasmArrayCopyWithChecks) \
V(WasmArrayInitFromData) \
- V(WasmAllocateRtt) \
- V(WasmAllocateFreshRtt) \
V(WasmAllocateStructWithRtt) \
V(WasmSubtypeCheck) \
V(WasmOnStackReplace) \
diff --git a/src/wasm/wasm-constants.h b/src/wasm/wasm-constants.h
index 7c67a4c..0442f3d 100644
--- a/src/wasm/wasm-constants.h
+++ b/src/wasm/wasm-constants.h
@@ -49,13 +49,16 @@
kDataRefCode = 0x67,
kArrayRefCode = 0x66
};
-// Binary encoding of other types.
+
+// Binary encoding of type definitions.
constexpr uint8_t kWasmFunctionTypeCode = 0x60;
constexpr uint8_t kWasmStructTypeCode = 0x5f;
constexpr uint8_t kWasmArrayTypeCode = 0x5e;
-constexpr uint8_t kWasmFunctionSubtypeCode = 0x5d;
-constexpr uint8_t kWasmStructSubtypeCode = 0x5c;
-constexpr uint8_t kWasmArraySubtypeCode = 0x5b;
+constexpr uint8_t kWasmFunctionNominalCode = 0x5d;
+constexpr uint8_t kWasmStructNominalCode = 0x5c;
+constexpr uint8_t kWasmArrayNominalCode = 0x5b;
+constexpr uint8_t kWasmSubtypeCode = 0x50;
+constexpr uint8_t kWasmRecursiveTypeGroupCode = 0x49;
// Binary encoding of import/export kinds.
enum ImportExportKindCode : uint8_t {
diff --git a/src/wasm/wasm-init-expr.cc b/src/wasm/wasm-init-expr.cc
index db7e003..20cdf81 100644
--- a/src/wasm/wasm-init-expr.cc
+++ b/src/wasm/wasm-init-expr.cc
@@ -47,16 +47,6 @@
return ValueType::Ref(immediate().index, kNonNullable);
case kRttCanon:
return ValueType::Rtt(immediate().heap_type, 0);
- case kRttSub:
- case kRttFreshSub: {
- ValueType operand_type = (*operands())[0].type(module, enabled_features);
- if (!operand_type.is_rtt()) return kWasmBottom;
- if (operand_type.has_depth()) {
- return ValueType::Rtt(immediate().heap_type, operand_type.depth() + 1);
- } else {
- return ValueType::Rtt(immediate().heap_type);
- }
- }
}
}
diff --git a/src/wasm/wasm-init-expr.h b/src/wasm/wasm-init-expr.h
index 6ae4e5f..44fadd4 100644
--- a/src/wasm/wasm-init-expr.h
+++ b/src/wasm/wasm-init-expr.h
@@ -43,8 +43,6 @@
kArrayInit,
kArrayInitStatic,
kRttCanon,
- kRttSub,
- kRttFreshSub,
};
union Immediate {
@@ -149,25 +147,6 @@
return expr;
}
- static WasmInitExpr RttSub(Zone* zone, uint32_t index,
- WasmInitExpr supertype) {
- WasmInitExpr expr(
- kRttSub, zone->New<ZoneVector<WasmInitExpr>>(
- std::initializer_list<WasmInitExpr>{supertype}, zone));
- expr.immediate_.index = index;
- return expr;
- }
-
- static WasmInitExpr RttFreshSub(Zone* zone, uint32_t index,
- WasmInitExpr supertype) {
- WasmInitExpr expr(
- kRttFreshSub,
- zone->New<ZoneVector<WasmInitExpr>>(
- std::initializer_list<WasmInitExpr>{supertype}, zone));
- expr.immediate_.index = index;
- return expr;
- }
-
Immediate immediate() const { return immediate_; }
Operator kind() const { return kind_; }
const ZoneVector<WasmInitExpr>* operands() const { return operands_; }
@@ -211,10 +190,6 @@
if (operands()[i] != other.operands()[i]) return false;
}
return true;
- case kRttSub:
- case kRttFreshSub:
- return immediate().index == other.immediate().index &&
- operands()[0] == other.operands()[0];
}
}
diff --git a/src/wasm/wasm-module-builder.cc b/src/wasm/wasm-module-builder.cc
index 33d6177..9d4495e 100644
--- a/src/wasm/wasm-module-builder.cc
+++ b/src/wasm/wasm-module-builder.cc
@@ -64,6 +64,13 @@
signature_index_ = builder_->AddSignature(sig);
}
+void WasmFunctionBuilder::SetSignature(uint32_t sig_index) {
+ DCHECK(!locals_.has_sig());
+ DCHECK_EQ(builder_->types_[sig_index].kind, TypeDefinition::kFunction);
+ signature_index_ = sig_index;
+ locals_.set_sig(builder_->types_[sig_index].function_sig);
+}
+
uint32_t WasmFunctionBuilder::AddLocal(ValueType type) {
DCHECK(locals_.has_sig());
return locals_.AddLocals(1, type);
@@ -281,6 +288,12 @@
return functions_.back();
}
+WasmFunctionBuilder* WasmModuleBuilder::AddFunction(uint32_t sig_index) {
+ functions_.push_back(zone_->New<WasmFunctionBuilder>(this));
+ functions_.back()->SetSignature(sig_index);
+ return functions_.back();
+}
+
void WasmModuleBuilder::AddDataSegment(const byte* data, uint32_t size,
uint32_t dest) {
data_segments_.push_back({ZoneVector<byte>(zone()), dest});
@@ -564,19 +577,6 @@
buffer->write_u8(static_cast<uint8_t>(kExprRttCanon));
buffer->write_i32v(static_cast<int32_t>(init.immediate().index));
break;
- case WasmInitExpr::kRttSub:
- case WasmInitExpr::kRttFreshSub:
- // The operand to rtt.sub must be emitted first.
- WriteInitializerExpressionWithEnd(buffer, (*init.operands())[0],
- kWasmBottom);
- STATIC_ASSERT((kExprRttSub >> 8) == kGCPrefix);
- STATIC_ASSERT((kExprRttFreshSub >> 8) == kGCPrefix);
- buffer->write_u8(kGCPrefix);
- buffer->write_u8(static_cast<uint8_t>(init.kind() == WasmInitExpr::kRttSub
- ? kExprRttSub
- : kExprRttFreshSub));
- buffer->write_i32v(static_cast<int32_t>(init.immediate().index));
- break;
}
}
@@ -597,13 +597,17 @@
size_t start = EmitSection(kTypeSectionCode, buffer);
buffer->write_size(types_.size());
+ // TODO(7748): Add support for recursive groups.
for (const TypeDefinition& type : types_) {
- bool has_super = type.supertype != kNoSuperType;
+ if (type.supertype != kNoSuperType) {
+ buffer->write_u8(kWasmSubtypeCode);
+ buffer->write_u8(1); // The supertype count is always 1.
+ buffer->write_u32v(type.supertype);
+ }
switch (type.kind) {
case TypeDefinition::kFunction: {
const FunctionSig* sig = type.function_sig;
- buffer->write_u8(has_super ? kWasmFunctionSubtypeCode
- : kWasmFunctionTypeCode);
+ buffer->write_u8(kWasmFunctionTypeCode);
buffer->write_size(sig->parameter_count());
for (auto param : sig->parameters()) {
WriteValueType(buffer, param);
@@ -612,40 +616,23 @@
for (auto ret : sig->returns()) {
WriteValueType(buffer, ret);
}
- if (type.supertype == kGenericSuperType) {
- buffer->write_u8(kFuncRefCode);
- } else if (has_super) {
- buffer->write_i32v(type.supertype);
- }
break;
}
case TypeDefinition::kStruct: {
const StructType* struct_type = type.struct_type;
- buffer->write_u8(has_super ? kWasmStructSubtypeCode
- : kWasmStructTypeCode);
+ buffer->write_u8(kWasmStructTypeCode);
buffer->write_size(struct_type->field_count());
for (uint32_t i = 0; i < struct_type->field_count(); i++) {
WriteValueType(buffer, struct_type->field(i));
buffer->write_u8(struct_type->mutability(i) ? 1 : 0);
}
- if (type.supertype == kGenericSuperType) {
- buffer->write_u8(kDataRefCode);
- } else if (has_super) {
- buffer->write_i32v(type.supertype);
- }
break;
}
case TypeDefinition::kArray: {
const ArrayType* array_type = type.array_type;
- buffer->write_u8(has_super ? kWasmArraySubtypeCode
- : kWasmArrayTypeCode);
+ buffer->write_u8(kWasmArrayTypeCode);
WriteValueType(buffer, array_type->element_type());
buffer->write_u8(array_type->mutability() ? 1 : 0);
- if (type.supertype == kGenericSuperType) {
- buffer->write_u8(kDataRefCode);
- } else if (has_super) {
- buffer->write_i32v(type.supertype);
- }
break;
}
}
diff --git a/src/wasm/wasm-module-builder.h b/src/wasm/wasm-module-builder.h
index 80bbcaf..dc8ccf5 100644
--- a/src/wasm/wasm-module-builder.h
+++ b/src/wasm/wasm-module-builder.h
@@ -167,6 +167,7 @@
public:
// Building methods.
void SetSignature(const FunctionSig* sig);
+ void SetSignature(uint32_t sig_index);
uint32_t AddLocal(ValueType type);
void EmitByte(byte b);
void EmitI32V(int32_t val);
@@ -312,6 +313,7 @@
uint32_t AddImport(base::Vector<const char> name, FunctionSig* sig,
base::Vector<const char> module = {});
WasmFunctionBuilder* AddFunction(const FunctionSig* sig = nullptr);
+ WasmFunctionBuilder* AddFunction(uint32_t sig_index);
uint32_t AddGlobal(ValueType type, bool mutability = true,
WasmInitExpr init = WasmInitExpr());
uint32_t AddGlobalImport(base::Vector<const char> name, ValueType type,
diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc
index 762e954..ea9a891 100644
--- a/src/wasm/wasm-module.cc
+++ b/src/wasm/wasm-module.cc
@@ -119,11 +119,8 @@
int GetSubtypingDepth(const WasmModule* module, uint32_t type_index) {
uint32_t starting_point = type_index;
int depth = 0;
- while ((type_index = module->supertype(type_index)) != kGenericSuperType) {
+ while ((type_index = module->supertype(type_index)) != kNoSuperType) {
if (type_index == starting_point) return -1; // Cycle detected.
- // This is disallowed and will be rejected by validation, but might occur
- // when this function is called.
- if (type_index == kNoSuperType) break;
depth++;
if (depth > static_cast<int>(kV8MaxRttSubtypingDepth)) break;
}
@@ -224,8 +221,6 @@
WasmModule::WasmModule(std::unique_ptr<Zone> signature_zone)
: signature_zone(std::move(signature_zone)) {}
-WasmModule::~WasmModule() { DeleteCachedTypeJudgementsForModule(this); }
-
bool IsWasmCodegenAllowed(Isolate* isolate, Handle<Context> context) {
// TODO(wasm): Once wasm has its own CSP policy, we should introduce a
// separate callback that includes information about the module about to be
diff --git a/src/wasm/wasm-module.h b/src/wasm/wasm-module.h
index 69c067e..d14040d 100644
--- a/src/wasm/wasm-module.h
+++ b/src/wasm/wasm-module.h
@@ -344,6 +344,9 @@
std::unique_ptr<AsmJsOffsets> decoded_offsets_;
};
+// Used as the supertype for a type at the top of the type hierarchy.
+constexpr uint32_t kNoSuperType = std::numeric_limits<uint32_t>::max();
+
struct TypeDefinition {
enum Kind { kFunction, kStruct, kArray };
@@ -353,6 +356,8 @@
: struct_type(type), supertype(supertype), kind(kStruct) {}
TypeDefinition(const ArrayType* type, uint32_t supertype)
: array_type(type), supertype(supertype), kind(kArray) {}
+ TypeDefinition()
+ : function_sig(nullptr), supertype(kNoSuperType), kind(kFunction) {}
union {
const FunctionSig* function_sig;
@@ -386,11 +391,6 @@
struct WasmTable;
-// End of a chain of explicit supertypes.
-constexpr uint32_t kGenericSuperType = std::numeric_limits<uint32_t>::max() - 1;
-// Used for types that have no explicit supertype.
-constexpr uint32_t kNoSuperType = std::numeric_limits<uint32_t>::max();
-
// Static representation of a module.
struct V8_EXPORT_PRIVATE WasmModule {
std::unique_ptr<Zone> signature_zone;
@@ -418,11 +418,20 @@
WireBytesRef code = {0, 0};
WireBytesRef name = {0, 0};
+ void add_type(TypeDefinition type) {
+ types.push_back(type);
+ uint32_t canonical_id = type.kind == TypeDefinition::kFunction
+ ? signature_map.FindOrInsert(*type.function_sig)
+ : 0;
+ canonicalized_type_ids.push_back(canonical_id);
+ }
+
bool has_type(uint32_t index) const { return index < types.size(); }
void add_signature(const FunctionSig* sig, uint32_t supertype) {
types.push_back(TypeDefinition(sig, supertype));
- uint32_t canonical_id = sig ? signature_map.FindOrInsert(*sig) : 0;
+ DCHECK_NOT_NULL(sig);
+ uint32_t canonical_id = signature_map.FindOrInsert(*sig);
canonicalized_type_ids.push_back(canonical_id);
}
bool has_signature(uint32_t index) const {
@@ -498,7 +507,6 @@
explicit WasmModule(std::unique_ptr<Zone> signature_zone = nullptr);
WasmModule(const WasmModule&) = delete;
- ~WasmModule();
WasmModule& operator=(const WasmModule&) = delete;
};
diff --git a/src/wasm/wasm-objects.h b/src/wasm/wasm-objects.h
index 2f780ec..1cea466 100644
--- a/src/wasm/wasm-objects.h
+++ b/src/wasm/wasm-objects.h
@@ -1042,9 +1042,6 @@
Handle<Map> CreateArrayMap(Isolate* isolate, const WasmModule* module,
int array_index, MaybeHandle<Map> rtt_parent,
Handle<WasmInstanceObject> instance);
-Handle<Map> AllocateSubRtt(Isolate* isolate,
- Handle<WasmInstanceObject> instance, uint32_t type,
- Handle<Map> parent, WasmRttSubMode mode);
bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
Handle<Object> value, ValueType expected,
diff --git a/src/wasm/wasm-opcodes-inl.h b/src/wasm/wasm-opcodes-inl.h
index 5ee594c..8e191e5 100644
--- a/src/wasm/wasm-opcodes-inl.h
+++ b/src/wasm/wasm-opcodes-inl.h
@@ -419,8 +419,6 @@
CASE_OP(I31GetS, "i31.get_s")
CASE_OP(I31GetU, "i31.get_u")
CASE_OP(RttCanon, "rtt.canon")
- CASE_OP(RttSub, "rtt.sub")
- CASE_OP(RttFreshSub, "rtt.fresh_sub")
CASE_OP(RefTest, "ref.test")
CASE_OP(RefTestStatic, "ref.test_static")
CASE_OP(RefCast, "ref.cast")
diff --git a/src/wasm/wasm-opcodes.h b/src/wasm/wasm-opcodes.h
index 03c382d..59ee645 100644
--- a/src/wasm/wasm-opcodes.h
+++ b/src/wasm/wasm-opcodes.h
@@ -686,13 +686,11 @@
V(ArrayInitStatic, 0xfb1a, _) \
V(ArrayNew, 0xfb1b, _) \
V(ArrayNewDefault, 0xfb1c, _) \
- V(ArrayInitFromDataStatic, 0xfb1d, _) /* n/s - V8 experimental */ \
+ V(ArrayInitFromDataStatic, 0xfb1d, _) /* n/st - V8 experimental */ \
V(I31New, 0xfb20, _) \
V(I31GetS, 0xfb21, _) \
V(I31GetU, 0xfb22, _) \
V(RttCanon, 0xfb30, _) \
- V(RttSub, 0xfb31, _) \
- V(RttFreshSub, 0xfb32, _) /* not standardized - V8 experimental */ \
V(RefTest, 0xfb40, _) \
V(RefCast, 0xfb41, _) \
V(BrOnCast, 0xfb42, _) \
diff --git a/src/wasm/wasm-subtyping.cc b/src/wasm/wasm-subtyping.cc
index 7a0454e5..6f02324 100644
--- a/src/wasm/wasm-subtyping.cc
+++ b/src/wasm/wasm-subtyping.cc
@@ -14,220 +14,21 @@
namespace {
-using CacheKey =
- std::tuple<uint32_t, uint32_t, const WasmModule*, const WasmModule*>;
-
-struct CacheKeyHasher {
- size_t operator()(CacheKey key) const {
- static constexpr size_t large_prime = 14887;
- return std::get<0>(key) + (std::get<1>(key) * large_prime) +
- (reinterpret_cast<size_t>(std::get<2>(key)) * large_prime *
- large_prime) +
- (reinterpret_cast<size_t>(std::get<3>(key)) * large_prime *
- large_prime * large_prime);
- }
-};
-
-class TypeJudgementCache {
- public:
- TypeJudgementCache()
- : zone_(new AccountingAllocator(), "type judgement zone"),
- subtyping_cache_(&zone_),
- type_equivalence_cache_(&zone_) {}
-
- static TypeJudgementCache* instance() {
- static base::LazyInstance<TypeJudgementCache>::type instance_ =
- LAZY_INSTANCE_INITIALIZER;
- return instance_.Pointer();
- }
-
- base::RecursiveMutex* type_cache_mutex() { return &type_cache_mutex_; }
- bool is_cached_subtype(uint32_t subtype, uint32_t supertype,
- const WasmModule* sub_module,
- const WasmModule* super_module) const {
- return subtyping_cache_.count(std::make_tuple(
- subtype, supertype, sub_module, super_module)) == 1;
- }
- void cache_subtype(uint32_t subtype, uint32_t supertype,
- const WasmModule* sub_module,
- const WasmModule* super_module) {
- subtyping_cache_.emplace(subtype, supertype, sub_module, super_module);
- }
- void uncache_subtype(uint32_t subtype, uint32_t supertype,
- const WasmModule* sub_module,
- const WasmModule* super_module) {
- subtyping_cache_.erase(
- std::make_tuple(subtype, supertype, sub_module, super_module));
- }
- bool is_cached_equivalent_type(uint32_t type1, uint32_t type2,
- const WasmModule* module1,
- const WasmModule* module2) const {
- if (type1 > type2) std::swap(type1, type2);
- if (reinterpret_cast<uintptr_t>(module1) >
- reinterpret_cast<uintptr_t>(module2)) {
- std::swap(module1, module2);
- }
- return type_equivalence_cache_.count(
- std::make_tuple(type1, type2, module1, module2)) == 1;
- }
- void cache_type_equivalence(uint32_t type1, uint32_t type2,
- const WasmModule* module1,
- const WasmModule* module2) {
- if (type1 > type2) std::swap(type1, type2);
- if (reinterpret_cast<uintptr_t>(module1) >
- reinterpret_cast<uintptr_t>(module2)) {
- std::swap(module1, module2);
- }
- type_equivalence_cache_.emplace(type1, type2, module1, module2);
- }
- void uncache_type_equivalence(uint32_t type1, uint32_t type2,
- const WasmModule* module1,
- const WasmModule* module2) {
- if (type1 > type2) std::swap(type1, type2);
- if (reinterpret_cast<uintptr_t>(module1) >
- reinterpret_cast<uintptr_t>(module2)) {
- std::swap(module1, module2);
- }
- type_equivalence_cache_.erase(
- std::make_tuple(type1, type2, module1, module2));
- }
- void delete_module(const WasmModule* module) {
- for (auto iterator = type_equivalence_cache_.begin();
- iterator != type_equivalence_cache_.end();) {
- if (std::get<2>(*iterator) == module ||
- std::get<3>(*iterator) == module) {
- iterator = type_equivalence_cache_.erase(iterator);
- } else {
- iterator++;
- }
- }
- for (auto iterator = subtyping_cache_.begin();
- iterator != subtyping_cache_.end();) {
- if (std::get<2>(*iterator) == module ||
- std::get<3>(*iterator) == module) {
- iterator = subtyping_cache_.erase(iterator);
- } else {
- iterator++;
- }
- }
- }
-
- private:
- Zone zone_;
- ZoneUnorderedSet<CacheKey, CacheKeyHasher>
- // Cache for discovered subtyping pairs.
- subtyping_cache_,
- // Cache for discovered equivalent type pairs.
- // Indexes and modules are stored in increasing order.
- type_equivalence_cache_;
- // The above two caches are used from background compile jobs, so they
- // must be protected from concurrent modifications:
- base::RecursiveMutex type_cache_mutex_;
-};
-
-bool ArrayEquivalentIndices(uint32_t type_index_1, uint32_t type_index_2,
- const WasmModule* module1,
- const WasmModule* module2) {
- const ArrayType* sub_array = module1->types[type_index_1].array_type;
- const ArrayType* super_array = module2->types[type_index_2].array_type;
- if (sub_array->mutability() != super_array->mutability()) return false;
-
- // Temporarily cache type equivalence for the recursive call.
- TypeJudgementCache::instance()->cache_type_equivalence(
- type_index_1, type_index_2, module1, module2);
- if (EquivalentTypes(sub_array->element_type(), super_array->element_type(),
- module1, module2)) {
- return true;
- } else {
- TypeJudgementCache::instance()->uncache_type_equivalence(
- type_index_1, type_index_2, module1, module2);
- // TODO(7748): Consider caching negative results as well.
- return false;
- }
-}
-
-bool StructEquivalentIndices(uint32_t type_index_1, uint32_t type_index_2,
- const WasmModule* module1,
- const WasmModule* module2) {
- const StructType* sub_struct = module1->types[type_index_1].struct_type;
- const StructType* super_struct = module2->types[type_index_2].struct_type;
-
- if (sub_struct->field_count() != super_struct->field_count()) {
- return false;
- }
-
- // Temporarily cache type equivalence for the recursive call.
- TypeJudgementCache::instance()->cache_type_equivalence(
- type_index_1, type_index_2, module1, module2);
- for (uint32_t i = 0; i < sub_struct->field_count(); i++) {
- if (sub_struct->mutability(i) != super_struct->mutability(i) ||
- !EquivalentTypes(sub_struct->field(i), super_struct->field(i), module1,
- module2)) {
- TypeJudgementCache::instance()->uncache_type_equivalence(
- type_index_1, type_index_2, module1, module2);
- return false;
- }
- }
- return true;
-}
-
-bool FunctionEquivalentIndices(uint32_t type_index_1, uint32_t type_index_2,
- const WasmModule* module1,
- const WasmModule* module2) {
- const FunctionSig* sig1 = module1->types[type_index_1].function_sig;
- const FunctionSig* sig2 = module2->types[type_index_2].function_sig;
-
- if (sig1->parameter_count() != sig2->parameter_count() ||
- sig1->return_count() != sig2->return_count()) {
- return false;
- }
-
- auto iter1 = sig1->all();
- auto iter2 = sig2->all();
-
- // Temporarily cache type equivalence for the recursive call.
- TypeJudgementCache::instance()->cache_type_equivalence(
- type_index_1, type_index_2, module1, module2);
- for (int i = 0; i < iter1.size(); i++) {
- if (!EquivalentTypes(iter1[i], iter2[i], module1, module2)) {
- TypeJudgementCache::instance()->uncache_type_equivalence(
- type_index_1, type_index_2, module1, module2);
- return false;
- }
- }
- return true;
-}
-
V8_INLINE bool EquivalentIndices(uint32_t index1, uint32_t index2,
const WasmModule* module1,
const WasmModule* module2) {
DCHECK(index1 != index2 || module1 != module2);
- TypeDefinition::Kind kind1 = module1->types[index1].kind;
-
- if (kind1 != module2->types[index2].kind) return false;
-
- base::RecursiveMutexGuard type_cache_access(
- TypeJudgementCache::instance()->type_cache_mutex());
- if (TypeJudgementCache::instance()->is_cached_equivalent_type(
- index1, index2, module1, module2)) {
- return true;
- }
-
- if (kind1 == TypeDefinition::kStruct) {
- return StructEquivalentIndices(index1, index2, module1, module2);
- } else if (kind1 == TypeDefinition::kArray) {
- return ArrayEquivalentIndices(index1, index2, module1, module2);
- } else {
- DCHECK_EQ(kind1, TypeDefinition::kFunction);
- return FunctionEquivalentIndices(index1, index2, module1, module2);
- }
+ // TODO(7748): Canonicalize types.
+ return false;
}
-} // namespace
+bool ValidStructSubtypeDefinition(uint32_t subtype_index,
+ uint32_t supertype_index,
+ const WasmModule* sub_module,
+ const WasmModule* super_module) {
+ // TODO(7748): Figure out the cross-module story.
+ if (sub_module != super_module) return false;
-bool StructIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
- const WasmModule* sub_module,
- const WasmModule* super_module) {
const StructType* sub_struct = sub_module->types[subtype_index].struct_type;
const StructType* super_struct =
super_module->types[supertype_index].struct_type;
@@ -236,10 +37,6 @@
return false;
}
- if (!sub_module->has_supertype(subtype_index)) {
- TypeJudgementCache::instance()->cache_subtype(
- subtype_index, supertype_index, sub_module, super_module);
- }
for (uint32_t i = 0; i < super_struct->field_count(); i++) {
bool sub_mut = sub_struct->mutability(i);
bool super_mut = super_struct->mutability(i);
@@ -249,48 +46,41 @@
sub_module, super_module)) ||
(!sub_mut && !IsSubtypeOf(sub_struct->field(i), super_struct->field(i),
sub_module, super_module))) {
- TypeJudgementCache::instance()->uncache_subtype(
- subtype_index, supertype_index, sub_module, super_module);
return false;
}
}
return true;
}
-bool ArrayIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
- const WasmModule* sub_module,
- const WasmModule* super_module) {
+bool ValidArraySubtypeDefinition(uint32_t subtype_index,
+ uint32_t supertype_index,
+ const WasmModule* sub_module,
+ const WasmModule* super_module) {
+ // TODO(7748): Figure out the cross-module story.
+ if (sub_module != super_module) return false;
+
const ArrayType* sub_array = sub_module->types[subtype_index].array_type;
const ArrayType* super_array =
super_module->types[supertype_index].array_type;
bool sub_mut = sub_array->mutability();
bool super_mut = super_array->mutability();
- if (!sub_module->has_supertype(subtype_index)) {
- TypeJudgementCache::instance()->cache_subtype(
- subtype_index, supertype_index, sub_module, super_module);
- }
- if (sub_mut != super_mut ||
- (sub_mut &&
- !EquivalentTypes(sub_array->element_type(), super_array->element_type(),
- sub_module, super_module)) ||
- (!sub_mut &&
- !IsSubtypeOf(sub_array->element_type(), super_array->element_type(),
- sub_module, super_module))) {
- TypeJudgementCache::instance()->uncache_subtype(
- subtype_index, supertype_index, sub_module, super_module);
- return false;
- } else {
- return true;
- }
+
+ return (sub_mut && super_mut &&
+ EquivalentTypes(sub_array->element_type(),
+ super_array->element_type(), sub_module,
+ super_module)) ||
+ (!sub_mut && !super_mut &&
+ IsSubtypeOf(sub_array->element_type(), super_array->element_type(),
+ sub_module, super_module));
}
-bool FunctionIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
- const WasmModule* sub_module,
- const WasmModule* super_module) {
- if (!FLAG_experimental_wasm_gc) {
- return FunctionEquivalentIndices(subtype_index, supertype_index, sub_module,
- super_module);
- }
+bool ValidFunctionSubtypeDefinition(uint32_t subtype_index,
+ uint32_t supertype_index,
+ const WasmModule* sub_module,
+ const WasmModule* super_module) {
+ // TODO(7748): Figure out the cross-module story.
+ if (sub_module != super_module) return false;
+
const FunctionSig* sub_func = sub_module->types[subtype_index].function_sig;
const FunctionSig* super_func =
super_module->types[supertype_index].function_sig;
@@ -300,17 +90,10 @@
return false;
}
- if (!sub_module->has_supertype(subtype_index)) {
- TypeJudgementCache::instance()->cache_subtype(
- subtype_index, supertype_index, sub_module, super_module);
- }
-
for (uint32_t i = 0; i < sub_func->parameter_count(); i++) {
// Contravariance for params.
if (!IsSubtypeOf(super_func->parameters()[i], sub_func->parameters()[i],
super_module, sub_module)) {
- TypeJudgementCache::instance()->uncache_subtype(
- subtype_index, supertype_index, sub_module, super_module);
return false;
}
}
@@ -318,8 +101,6 @@
// Covariance for returns.
if (!IsSubtypeOf(sub_func->returns()[i], super_func->returns()[i],
sub_module, super_module)) {
- TypeJudgementCache::instance()->uncache_subtype(
- subtype_index, supertype_index, sub_module, super_module);
return false;
}
}
@@ -327,6 +108,27 @@
return true;
}
+} // namespace
+
+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) {
+ case TypeDefinition::kFunction:
+ return ValidFunctionSubtypeDefinition(subtype_index, supertype_index,
+ sub_module, super_module);
+ case TypeDefinition::kStruct:
+ return ValidStructSubtypeDefinition(subtype_index, supertype_index,
+ sub_module, super_module);
+ case TypeDefinition::kArray:
+ return ValidArraySubtypeDefinition(subtype_index, supertype_index,
+ sub_module, super_module);
+ }
+}
+
V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
ValueType subtype, ValueType supertype, const WasmModule* sub_module,
const WasmModule* super_module) {
@@ -426,49 +228,15 @@
// equality; here we catch (ref $x) being a subtype of (ref null $x).
if (sub_module == super_module && sub_index == super_index) return true;
- TypeDefinition::Kind sub_kind = sub_module->types[sub_index].kind;
+ // TODO(7748): Figure out cross-module story.
+ if (sub_module != super_module) return false;
- if (sub_kind != super_module->types[super_index].kind) return false;
-
- // Types with explicit supertypes just check those.
- if (sub_module->has_supertype(sub_index)) {
- // TODO(7748): Figure out cross-module story.
- if (sub_module != super_module) return false;
-
- uint32_t explicit_super = sub_module->supertype(sub_index);
- while (true) {
- if (explicit_super == super_index) return true;
- // Reached the end of the explicitly defined inheritance chain.
- if (explicit_super == kGenericSuperType) return false;
- // Types without explicit supertype can't occur here, they would have
- // failed validation.
- DCHECK_NE(explicit_super, kNoSuperType);
- explicit_super = sub_module->supertype(explicit_super);
- }
- } else {
- // A structural type (without explicit supertype) is never a subtype of
- // a nominal type (with explicit supertype).
- if (super_module->has_supertype(super_index)) return false;
- }
-
- // Accessing the caches for subtyping and equivalence from multiple background
- // threads is protected by a lock.
- base::RecursiveMutexGuard type_cache_access(
- TypeJudgementCache::instance()->type_cache_mutex());
- if (TypeJudgementCache::instance()->is_cached_subtype(
- sub_index, super_index, sub_module, super_module)) {
- return true;
- }
-
- switch (sub_kind) {
- case TypeDefinition::kStruct:
- return StructIsSubtypeOf(sub_index, super_index, sub_module,
- super_module);
- case TypeDefinition::kArray:
- return ArrayIsSubtypeOf(sub_index, super_index, sub_module, super_module);
- case TypeDefinition::kFunction:
- return FunctionIsSubtypeOf(sub_index, super_index, sub_module,
- super_module);
+ uint32_t explicit_super = sub_module->supertype(sub_index);
+ while (true) {
+ if (explicit_super == super_index) return true;
+ // Reached the end of the explicitly defined inheritance chain.
+ if (explicit_super == kNoSuperType) return false;
+ explicit_super = sub_module->supertype(explicit_super);
}
}
@@ -492,14 +260,6 @@
module2);
}
-void DeleteCachedTypeJudgementsForModule(const WasmModule* module) {
- // Accessing the caches for subtyping and equivalence from multiple background
- // threads is protected by a lock.
- base::RecursiveMutexGuard type_cache_access(
- TypeJudgementCache::instance()->type_cache_mutex());
- TypeJudgementCache::instance()->delete_module(module);
-}
-
} // namespace wasm
} // namespace internal
} // namespace v8
diff --git a/src/wasm/wasm-subtyping.h b/src/wasm/wasm-subtyping.h
index 96141b2..f4b4670 100644
--- a/src/wasm/wasm-subtyping.h
+++ b/src/wasm/wasm-subtyping.h
@@ -23,18 +23,12 @@
// Checks if type1, defined in module1, is equivalent with type2, defined in
// module2.
-// Type equivalence (~) is described by the following rules (structural
-// equivalence):
+// Type equivalence (~) is described by the following rules:
// - Two numeric types are equivalent iff they are equal.
// - T(ht1) ~ T(ht2) iff ht1 ~ ht2 for T in {ref, optref, rtt}.
-// - rtt(d1, ht1) ~ rtt(d2, ht2) iff (d1 = d2 and ht1 ~ ht2).
// Equivalence of heap types ht1 ~ ht2 is defined as follows:
-// - Two generic heap types are equivalent iff they are equal.
-// - Two structs are equivalent iff they contain the same number of fields and
-// these are pairwise equivalent.
-// - Two functions are equivalent iff they contain the same number of parameters
-// and returns and these are pairwise equivalent.
-// - Two arrays are equivalent iff their element types are equivalent.
+// - Two heap types are equivalent iff they are equal.
+// - TODO(7748): Implement iso-recursive canonicalization.
V8_NOINLINE bool EquivalentTypes(ValueType type1, ValueType type2,
const WasmModule* module1,
const WasmModule* module2);
@@ -46,8 +40,7 @@
// - numeric types are subtype-related iff they are equal.
// - optref(ht1) <: optref(ht2) iff ht1 <: ht2.
// - ref(ht1) <: ref/optref(ht2) iff ht1 <: ht2.
-// - rtt1(d, ht1) <: rtt2(ht2) iff ht1 ~ ht2.
-// - rtt1 <: rtt2 iff rtt1 ~ rtt2, otherwise
+// - rtt1 <: rtt2 iff rtt1 ~ rtt2.
// For heap types, the following subtyping rules hold:
// - The abstract heap types form the following type hierarchy:
// any
@@ -60,14 +53,8 @@
// - All functions are subtypes of func.
// - All structs are subtypes of data.
// - All arrays are subtypes of array.
-// - Struct subtyping: Subtype must have at least as many fields as supertype,
-// covariance for immutable fields, equivalence for mutable fields.
-// - Array subtyping: subtyping of respective element types for immutable
-// arrays, equivalence of element types for mutable arrays.
-// - Function subtyping depends on the enabled wasm features: if
-// --experimental-wasm-gc is enabled, then subtyping is computed
-// contravariantly for parameter types and covariantly for return types.
-// Otherwise, the subtyping relation is the equivalence relation.
+// - An indexed heap type h1 is a subtype of indexed heap type h2 if h2 is
+// transitively an explicit supertype of h1.
V8_INLINE bool IsSubtypeOf(ValueType subtype, ValueType supertype,
const WasmModule* sub_module,
const WasmModule* super_module) {
@@ -97,24 +84,20 @@
ValueType::Ref(supertype_index, kNonNullable), module);
}
-// Call this function in {module}'s destructor to avoid spurious cache hits in
-// case another WasmModule gets allocated in the same address later.
-void DeleteCachedTypeJudgementsForModule(const WasmModule* module);
-
-// Checks whether {subtype_index} is a legal subtype of {supertype_index}.
-// These are the same checks that {IsSubtypeOf} uses for comparing types without
-// explicitly given supertypes; for validating such explicit supertypes they
-// can be called directly.
-bool StructIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
- const WasmModule* sub_module,
- const WasmModule* super_module);
-bool ArrayIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
- const WasmModule* sub_module,
- const WasmModule* super_module);
-bool FunctionIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
- const WasmModule* sub_module,
- const WasmModule* super_module);
-
+// Checks whether {subtype_index} is valid as a declared subtype of
+// {supertype_index}.
+// - Both type must be of the same kind (function, struct, or array).
+// - Structs: Subtype must have at least as many fields as supertype,
+// covariance for respective immutable fields, equivalence for respective
+// mutable fields.
+// - Arrays: subtyping of respective element types for immutable arrays,
+// equivalence of element types for mutable arrays.
+// - Functions: equal number of parameter and return types. Contravariance for
+// respective parameter types, covariance for respective return types.
+V8_EXPORT_PRIVATE bool ValidSubtypeDefinition(uint32_t subtype_index,
+ uint32_t supertype_index,
+ const WasmModule* sub_module,
+ const WasmModule* super_module);
} // namespace wasm
} // namespace internal
} // namespace v8
diff --git a/test/cctest/wasm/test-gc.cc b/test/cctest/wasm/test-gc.cc
index f7ca97e..5a94bb0 100644
--- a/test/cctest/wasm/test-gc.cc
+++ b/test/cctest/wasm/test-gc.cc
@@ -54,12 +54,13 @@
byte DefineFunction(FunctionSig* sig, std::initializer_list<ValueType> locals,
std::initializer_list<byte> code) {
- WasmFunctionBuilder* fun = builder_.AddFunction(sig);
- for (ValueType local : locals) {
- fun->AddLocal(local);
- }
- fun->EmitCode(code.begin(), static_cast<uint32_t>(code.size()));
- return fun->func_index();
+ return DefineFunctionImpl(builder_.AddFunction(sig), locals, code);
+ }
+
+ byte DefineFunction(uint32_t sig_index,
+ std::initializer_list<ValueType> locals,
+ std::initializer_list<byte> code) {
+ return DefineFunctionImpl(builder_.AddFunction(sig_index), locals, code);
}
void DefineExportedFunction(const char* name, FunctionSig* sig,
@@ -94,7 +95,9 @@
supertype);
}
- byte DefineSignature(FunctionSig* sig) { return builder_.AddSignature(sig); }
+ byte DefineSignature(FunctionSig* sig, uint32_t supertype = kNoSuperType) {
+ return builder_.AddSignature(sig, supertype);
+ }
byte DefineTable(ValueType type, uint32_t min_size, uint32_t max_size) {
return builder_.AddTable(type, min_size, max_size);
@@ -180,6 +183,16 @@
const FlagScope<bool> flag_liftoff_only;
const FlagScope<bool> flag_tierup;
+ byte DefineFunctionImpl(WasmFunctionBuilder* fun,
+ std::initializer_list<ValueType> locals,
+ std::initializer_list<byte> code) {
+ for (ValueType local : locals) {
+ fun->AddLocal(local);
+ }
+ fun->EmitCode(code.begin(), static_cast<uint32_t>(code.size()));
+ return fun->func_index();
+ }
+
void CheckResultImpl(uint32_t function_index, const FunctionSig* sig,
CWasmArgumentsPacker* packer, int32_t expected) {
CallFunctionImpl(function_index, sig, packer);
@@ -457,35 +470,27 @@
WasmGCTester tester(execution_tier);
const byte supertype_index = tester.DefineStruct({F(kWasmI32, true)});
- const byte subtype1_index =
- tester.DefineStruct({F(kWasmI32, true), F(kWasmF32, false)});
- const byte subtype2_index =
- tester.DefineStruct({F(kWasmI32, true), F(kWasmI64, false)});
+ const byte subtype1_index = tester.DefineStruct(
+ {F(kWasmI32, true), F(kWasmF32, false)}, supertype_index);
+ const byte subtype2_index = tester.DefineStruct(
+ {F(kWasmI32, true), F(kWasmI64, false)}, supertype_index);
const byte kTestSuccessful = tester.DefineFunction(
tester.sigs.i_v(), {ValueType::Ref(supertype_index, kNullable)},
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
- subtype1_index,
- WASM_RTT_SUB(subtype1_index,
- WASM_RTT_CANON(supertype_index)))),
+ subtype1_index, WASM_RTT_CANON(subtype1_index))),
WASM_STRUCT_GET(
subtype1_index, 0,
- WASM_REF_CAST(
- WASM_LOCAL_GET(0),
- WASM_RTT_SUB(subtype1_index, WASM_RTT_CANON(supertype_index)))),
+ WASM_REF_CAST(WASM_LOCAL_GET(0), WASM_RTT_CANON(subtype1_index))),
WASM_END});
const byte kTestFailed = tester.DefineFunction(
tester.sigs.i_v(), {ValueType::Ref(supertype_index, kNullable)},
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
- subtype1_index,
- WASM_RTT_SUB(subtype1_index,
- WASM_RTT_CANON(supertype_index)))),
+ subtype1_index, WASM_RTT_CANON(subtype1_index))),
WASM_STRUCT_GET(
subtype2_index, 0,
- WASM_REF_CAST(
- WASM_LOCAL_GET(0),
- WASM_RTT_SUB(subtype2_index, WASM_RTT_CANON(supertype_index)))),
+ WASM_REF_CAST(WASM_LOCAL_GET(0), WASM_RTT_CANON(subtype2_index))),
WASM_END});
tester.CompileModule();
@@ -496,8 +501,7 @@
WASM_COMPILED_EXEC_TEST(RefCastStatic) {
WasmGCTester tester(execution_tier);
- const byte supertype_index =
- tester.DefineStruct({F(kWasmI32, true)}, kGenericSuperType);
+ const byte supertype_index = tester.DefineStruct({F(kWasmI32, true)});
const byte subtype1_index = tester.DefineStruct(
{F(kWasmI32, true), F(kWasmF32, false)}, supertype_index);
const byte subtype2_index = tester.DefineStruct(
@@ -526,8 +530,7 @@
FlagScope<bool> scope(&FLAG_experimental_wasm_assume_ref_cast_succeeds, true);
WasmGCTester tester(execution_tier);
- const byte supertype_index =
- tester.DefineStruct({F(kWasmI32, true)}, kGenericSuperType);
+ const byte supertype_index = tester.DefineStruct({F(kWasmI32, true)});
const byte subtype1_index = tester.DefineStruct(
{F(kWasmI32, true), F(kWasmF32, false)}, supertype_index);
const byte subtype2_index = tester.DefineStruct(
@@ -1301,6 +1304,7 @@
tester.CheckResult(kZeroLength, 0); // Does not throw.
}
+/* TODO(7748): This test requires for recursive groups.
WASM_COMPILED_EXEC_TEST(NewDefault) {
WasmGCTester tester(execution_tier);
const byte struct_type = tester.DefineStruct(
@@ -1334,13 +1338,14 @@
tester.CheckResult(allocate_struct, 0);
tester.CheckResult(allocate_array, 0);
}
+*/
WASM_COMPILED_EXEC_TEST(BasicRtt) {
WasmGCTester tester(execution_tier);
const byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
- const byte subtype_index =
- tester.DefineStruct({F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)});
+ const byte subtype_index = tester.DefineStruct(
+ {F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)}, type_index);
ValueType kRttTypes[] = {ValueType::Rtt(type_index, 0)};
FunctionSig sig_t_v(1, 0, kRttTypes);
@@ -1354,8 +1359,7 @@
const byte kRttCanon = tester.DefineFunction(
&sig_t_v, {}, {WASM_RTT_CANON(type_index), kExprEnd});
const byte kRttSub = tester.DefineFunction(
- &sig_t2_v, {},
- {WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index)), kExprEnd});
+ &sig_t2_v, {}, {WASM_RTT_CANON(subtype_index), kExprEnd});
const byte kStructWithRtt = tester.DefineFunction(
&sig_q_v, {},
{WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42),
@@ -1415,40 +1419,12 @@
tester.CheckResult(kRefCast, 43);
}
-WASM_COMPILED_EXEC_TEST(RttFreshSub) {
- WasmGCTester tester(execution_tier);
- const byte kType = tester.DefineStruct({F(wasm::kWasmI32, true)});
- HeapType::Representation type_repr =
- static_cast<HeapType::Representation>(kType);
-
- const byte kRtt = tester.AddGlobal(
- ValueType::Rtt(kType, 1), false,
- WasmInitExpr::RttFreshSub(tester.zone(), type_repr,
- WasmInitExpr::RttCanon(type_repr)));
-
- // A struct allocated with a fresh RTT does not match other fresh RTTs
- // created for the same type.
- const byte kRefTest = tester.DefineFunction(
- tester.sigs.i_v(), {optref(kType)},
- {WASM_LOCAL_SET(0, WASM_STRUCT_NEW_WITH_RTT(
- kType, WASM_I32V(11),
- WASM_RTT_FRESH_SUB(kType, WASM_RTT_CANON(kType)))),
- WASM_I32_ADD(
- WASM_REF_TEST(WASM_LOCAL_GET(0),
- WASM_RTT_FRESH_SUB(kType, WASM_RTT_CANON(kType))),
- WASM_REF_TEST(WASM_LOCAL_GET(0), WASM_GLOBAL_GET(kRtt))),
- kExprEnd});
-
- tester.CompileModule();
- tester.CheckResult(kRefTest, 0);
-}
-
WASM_COMPILED_EXEC_TEST(RefTrivialCasts) {
// TODO(7748): Add tests for branch_on_*.
WasmGCTester tester(execution_tier);
byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
- byte subtype_index =
- tester.DefineStruct({F(wasm::kWasmI32, true), F(wasm::kWasmS128, false)});
+ byte subtype_index = tester.DefineStruct(
+ {F(wasm::kWasmI32, true), F(wasm::kWasmS128, false)}, type_index);
ValueType sig_types[] = {kWasmS128, kWasmI32, kWasmF64};
FunctionSig sig(1, 2, sig_types);
byte sig_index = tester.DefineSignature(&sig);
@@ -1460,14 +1436,6 @@
// Upcasts should not be optimized away for structural types.
const byte kRefTestUpcast = tester.DefineFunction(
tester.sigs.i_v(), {},
- {WASM_REF_TEST(
- WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
- subtype_index,
- WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index))),
- WASM_RTT_CANON(type_index)),
- kExprEnd});
- const byte kRefTestUpcastFail = tester.DefineFunction(
- tester.sigs.i_v(), {},
{WASM_REF_TEST(WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
subtype_index, WASM_RTT_CANON(subtype_index)),
WASM_RTT_CANON(type_index)),
@@ -1478,11 +1446,9 @@
kExprEnd});
const byte kRefTestUnrelated = tester.DefineFunction(
tester.sigs.i_v(), {},
- {WASM_REF_TEST(
- WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
- subtype_index,
- WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index))),
- WASM_RTT_CANON(sig_index)),
+ {WASM_REF_TEST(WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
+ subtype_index, WASM_RTT_CANON(subtype_index)),
+ WASM_RTT_CANON(sig_index)),
kExprEnd});
const byte kRefTestUnrelatedNull = tester.DefineFunction(
tester.sigs.i_v(), {},
@@ -1502,11 +1468,10 @@
kExprEnd});
const byte kRefCastUpcast = tester.DefineFunction(
tester.sigs.i_v(), {},
- {WASM_REF_IS_NULL(WASM_REF_CAST(
- WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
- subtype_index,
- WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index))),
- WASM_RTT_CANON(type_index))),
+ {WASM_REF_IS_NULL(
+ WASM_REF_CAST(WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
+ subtype_index, WASM_RTT_CANON(subtype_index)),
+ WASM_RTT_CANON(type_index))),
kExprEnd});
const byte kRefCastUpcastNull = tester.DefineFunction(
tester.sigs.i_v(), {},
@@ -1515,11 +1480,10 @@
kExprEnd});
const byte kRefCastUnrelated = tester.DefineFunction(
tester.sigs.i_v(), {},
- {WASM_REF_IS_NULL(WASM_REF_CAST(
- WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
- subtype_index,
- WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index))),
- WASM_RTT_CANON(sig_index))),
+ {WASM_REF_IS_NULL(
+ WASM_REF_CAST(WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
+ subtype_index, WASM_RTT_CANON(subtype_index)),
+ WASM_RTT_CANON(sig_index))),
kExprEnd});
const byte kRefCastUnrelatedNull = tester.DefineFunction(
tester.sigs.i_v(), {},
@@ -1538,7 +1502,6 @@
tester.CheckResult(kRefTestNull, 0);
tester.CheckResult(kRefTestUpcast, 1);
- tester.CheckResult(kRefTestUpcastFail, 0);
tester.CheckResult(kRefTestUpcastNull, 0);
tester.CheckResult(kRefTestUnrelated, 0);
tester.CheckResult(kRefTestUnrelatedNull, 0);
@@ -1555,8 +1518,7 @@
WASM_COMPILED_EXEC_TEST(RefTrivialCastsStatic) {
// TODO(7748): Add tests for branch_on_*.
WasmGCTester tester(execution_tier);
- byte type_index =
- tester.DefineStruct({F(wasm::kWasmI32, true)}, kGenericSuperType);
+ byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
byte subtype_index = tester.DefineStruct(
{F(wasm::kWasmI32, true), F(wasm::kWasmS128, false)}, type_index);
ValueType sig_types[] = {kWasmS128, kWasmI32, kWasmF64};
@@ -1641,23 +1603,16 @@
WasmGCTester tester(execution_tier);
const byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
- const byte subtype_index =
- tester.DefineStruct({F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)});
- const byte empty_struct_index = tester.DefineStruct({});
+ const byte subtype_index = tester.DefineStruct(
+ {F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)}, type_index);
ValueType kRttTypeNoDepth = ValueType::Rtt(type_index);
FunctionSig sig_t1_v_nd(1, 0, &kRttTypeNoDepth);
ValueType kRttSubtypeNoDepth = ValueType::Rtt(subtype_index);
FunctionSig sig_t2_v_nd(1, 0, &kRttSubtypeNoDepth);
- const byte kRttTypeCanon = tester.DefineFunction(
- &sig_t1_v_nd, {}, {WASM_RTT_CANON(type_index), kExprEnd});
const byte kRttSubtypeCanon = tester.DefineFunction(
&sig_t2_v_nd, {}, {WASM_RTT_CANON(subtype_index), kExprEnd});
- const byte kRttSubtypeSub = tester.DefineFunction(
- &sig_t2_v_nd, {},
- {WASM_RTT_SUB(subtype_index, WASM_CALL_FUNCTION0(kRttTypeCanon)),
- kExprEnd});
const byte kTestCanon = tester.DefineFunction(
tester.sigs.i_v(), {optref(type_index)},
@@ -1667,56 +1622,9 @@
WASM_REF_TEST(WASM_LOCAL_GET(0), WASM_CALL_FUNCTION0(kRttSubtypeCanon)),
kExprEnd});
- const byte kTestSub = tester.DefineFunction(
- tester.sigs.i_v(), {optref(type_index)},
- {WASM_LOCAL_SET(
- 0, WASM_STRUCT_NEW_WITH_RTT(
- subtype_index, WASM_I32V(11), WASM_I32V(42),
- WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index)))),
- WASM_REF_TEST(WASM_LOCAL_GET(0), WASM_CALL_FUNCTION0(kRttSubtypeSub)),
- kExprEnd});
-
- const byte kTestSubVsEmpty = tester.DefineFunction(
- tester.sigs.i_v(), {optref(type_index)},
- {WASM_LOCAL_SET(0, WASM_STRUCT_NEW_WITH_RTT(
- subtype_index, WASM_I32V(11), WASM_I32V(42),
- WASM_RTT_SUB(subtype_index,
- WASM_RTT_CANON(empty_struct_index)))),
- WASM_REF_TEST(WASM_LOCAL_GET(0), WASM_CALL_FUNCTION0(kRttSubtypeSub)),
- kExprEnd});
-
- const byte kTestSubVsCanon = tester.DefineFunction(
- tester.sigs.i_v(), {optref(type_index)},
- {WASM_LOCAL_SET(0, WASM_STRUCT_NEW_WITH_RTT(
- subtype_index, WASM_I32V(11), WASM_I32V(42),
- WASM_RTT_CANON(subtype_index))),
- WASM_REF_TEST(WASM_LOCAL_GET(0), WASM_CALL_FUNCTION0(kRttSubtypeSub)),
- kExprEnd});
-
- const byte kTestCanonVsSub = tester.DefineFunction(
- tester.sigs.i_v(), {optref(type_index)},
- {WASM_LOCAL_SET(
- 0, WASM_STRUCT_NEW_WITH_RTT(
- subtype_index, WASM_I32V(11), WASM_I32V(42),
- WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index)))),
- WASM_REF_TEST(WASM_LOCAL_GET(0), WASM_CALL_FUNCTION0(kRttSubtypeCanon)),
- kExprEnd});
-
- const byte kTestSuperVsSub = tester.DefineFunction(
- tester.sigs.i_v(), {optref(type_index)},
- {WASM_LOCAL_SET(0, WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42),
- WASM_RTT_CANON(type_index))),
- WASM_REF_TEST(WASM_LOCAL_GET(0), WASM_CALL_FUNCTION0(kRttSubtypeCanon)),
- kExprEnd});
-
tester.CompileModule();
tester.CheckResult(kTestCanon, 1);
- tester.CheckResult(kTestSub, 1);
- tester.CheckResult(kTestSubVsEmpty, 0);
- tester.CheckResult(kTestSubVsCanon, 0);
- tester.CheckResult(kTestCanonVsSub, 0);
- tester.CheckResult(kTestSuperVsSub, 0);
}
WASM_COMPILED_EXEC_TEST(ArrayNewMap) {
@@ -1787,19 +1695,12 @@
{WASM_LOCAL_SET(0, WASM_REF_FUNC(func_index)),
WASM_REF_TEST(WASM_LOCAL_GET(0), WASM_RTT_CANON(sig_index)), kExprEnd});
- const byte test_fail_1 = tester.DefineFunction(
+ const byte test_fail = tester.DefineFunction(
tester.sigs.i_v(), {kWasmFuncRef},
{WASM_LOCAL_SET(0, WASM_REF_FUNC(func_index)),
WASM_REF_TEST(WASM_LOCAL_GET(0), WASM_RTT_CANON(other_sig_index)),
kExprEnd});
- const byte test_fail_2 = tester.DefineFunction(
- tester.sigs.i_v(), {kWasmFuncRef},
- {WASM_LOCAL_SET(0, WASM_REF_FUNC(func_index)),
- WASM_REF_TEST(WASM_LOCAL_GET(0),
- WASM_RTT_SUB(sig_index, WASM_RTT_CANON(sig_index))),
- kExprEnd});
-
tester.CompileModule();
Handle<Object> result_canon =
@@ -1825,8 +1726,7 @@
cast_function_reference->code().raw_instruction_start());
tester.CheckResult(test, 1);
- tester.CheckResult(test_fail_1, 0);
- tester.CheckResult(test_fail_2, 0);
+ tester.CheckResult(test_fail, 0);
}
WASM_COMPILED_EXEC_TEST(CallRef) {
@@ -2119,8 +2019,8 @@
WASM_COMPILED_EXEC_TEST(CastsBenchmark) {
WasmGCTester tester(execution_tier);
const byte SuperType = tester.DefineStruct({F(wasm::kWasmI32, true)});
- const byte SubType =
- tester.DefineStruct({F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)});
+ const byte SubType = tester.DefineStruct(
+ {F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)}, SuperType);
ValueType kDataRefNull = ValueType::Ref(HeapType::kData, kNullable);
const byte ListType = tester.DefineArray(kDataRefNull, true);
@@ -2132,11 +2032,8 @@
const byte RttSuper = tester.AddGlobal(
ValueType::Rtt(SuperType, 0), false,
WasmInitExpr::RttCanon(static_cast<HeapType::Representation>(SuperType)));
- const byte RttSub = tester.AddGlobal(
- ValueType::Rtt(SubType, 1), false,
- WasmInitExpr::RttSub(tester.zone(),
- static_cast<HeapType::Representation>(SubType),
- WasmInitExpr::GlobalGet(RttSuper)));
+ const byte RttSub = tester.AddGlobal(ValueType::Rtt(SubType, 1), false,
+ WasmInitExpr::RttCanon(SubType));
const byte RttList = tester.AddGlobal(
ValueType::Rtt(ListType, 0), false,
WasmInitExpr::RttCanon(static_cast<HeapType::Representation>(ListType)));
@@ -2234,25 +2131,25 @@
WasmGCTester tester(execution_tier);
byte super_struct = tester.DefineStruct({F(kWasmI32, false)});
- byte sub_struct =
- tester.DefineStruct({F(kWasmI32, false), F(kWasmI32, true)});
+ byte sub_struct = tester.DefineStruct({F(kWasmI32, false), F(kWasmI32, true)},
+ super_struct);
FunctionSig* super_sig =
FunctionSig::Build(tester.zone(), {kWasmI32}, {optref(sub_struct)});
byte super_sig_index = tester.DefineSignature(super_sig);
FunctionSig* sub_sig =
FunctionSig::Build(tester.zone(), {kWasmI32}, {optref(super_struct)});
- byte sub_sig_index = tester.DefineSignature(sub_sig);
+ byte sub_sig_index = tester.DefineSignature(sub_sig, super_sig_index);
tester.DefineTable(optref(super_sig_index), 10, 10);
byte super_func = tester.DefineFunction(
- super_sig, {},
+ super_sig_index, {},
{WASM_I32_ADD(WASM_STRUCT_GET(sub_struct, 0, WASM_LOCAL_GET(0)),
WASM_STRUCT_GET(sub_struct, 1, WASM_LOCAL_GET(0))),
WASM_END});
byte sub_func = tester.DefineFunction(
- sub_sig, {},
+ sub_sig_index, {},
{WASM_STRUCT_GET(super_struct, 0, WASM_LOCAL_GET(0)), WASM_END});
byte setup_func = tester.DefineFunction(
diff --git a/test/common/wasm/wasm-macro-gen.h b/test/common/wasm/wasm-macro-gen.h
index fd61c5a..0cdaa3a 100644
--- a/test/common/wasm/wasm-macro-gen.h
+++ b/test/common/wasm/wasm-macro-gen.h
@@ -589,10 +589,6 @@
#define WASM_RTT(typeidx) kRttCode, U32V_1(typeidx)
#define WASM_RTT_CANON(typeidx) \
WASM_GC_OP(kExprRttCanon), static_cast<byte>(typeidx)
-#define WASM_RTT_SUB(typeidx, supertype) \
- supertype, WASM_GC_OP(kExprRttSub), static_cast<byte>(typeidx)
-#define WASM_RTT_FRESH_SUB(typeidx, supertype) \
- supertype, WASM_GC_OP(kExprRttFreshSub), static_cast<byte>(typeidx)
#define WASM_I31_NEW(val) val, WASM_GC_OP(kExprI31New)
#define WASM_I31_GET_S(val) val, WASM_GC_OP(kExprI31GetS)
diff --git a/test/fuzzer/wasm-fuzzer-common.cc b/test/fuzzer/wasm-fuzzer-common.cc
index d2e22ea..a89950b 100644
--- a/test/fuzzer/wasm-fuzzer-common.cc
+++ b/test/fuzzer/wasm-fuzzer-common.cc
@@ -421,12 +421,6 @@
result->init_expr = WasmInitExpr::RttCanon(type_index);
}
- void RttSub(FullDecoder* decoder, uint32_t type_index, const Value& parent,
- Value* result, WasmRttSubMode mode) {
- result->init_expr =
- WasmInitExpr::RttSub(zone_, type_index, parent.init_expr);
- }
-
void DoReturn(FullDecoder* decoder, uint32_t /*drop_values*/) {
// End decoding on "end".
decoder->set_end(decoder->pc() + 1);
@@ -504,14 +498,6 @@
case WasmInitExpr::kRttCanon:
os << "RttCanon(" << expr.immediate().index;
break;
- case WasmInitExpr::kRttSub:
- os << "RttSub(" << expr.immediate().index << ", ";
- AppendInitExpr(os, (*expr.operands())[0]);
- break;
- case WasmInitExpr::kRttFreshSub:
- os << "RttFreshSub(" << expr.immediate().index << ", ";
- AppendInitExpr(os, (*expr.operands())[0]);
- break;
}
if (append_operands) {
diff --git a/test/inspector/debugger/wasm-gc-breakpoints-expected.txt b/test/inspector/debugger/wasm-gc-breakpoints-expected.txt
index df10788..0106c69 100644
--- a/test/inspector/debugger/wasm-gc-breakpoints-expected.txt
+++ b/test/inspector/debugger/wasm-gc-breakpoints-expected.txt
@@ -3,12 +3,12 @@
Running test: test
Instantiating.
Waiting for wasm script (ignoring first non-wasm script).
-Setting breakpoint at offset 107 on script wasm://wasm/151aafd6
+Setting breakpoint at offset 109 on script wasm://wasm/856247ba
Calling main()
Paused:
-Script wasm://wasm/151aafd6 byte offset 107: Wasm opcode 0x21 (kExprLocalSet)
+Script wasm://wasm/856247ba byte offset 109: Wasm opcode 0x21 (kExprLocalSet)
Scope:
-at $main (0:107):
+at $main (0:109):
- scope (wasm-expression-stack):
0: Array ((ref $ArrC))
object details:
diff --git a/test/inspector/debugger/wasm-gc-breakpoints.js b/test/inspector/debugger/wasm-gc-breakpoints.js
index 4306e20..1d20a1a 100644
--- a/test/inspector/debugger/wasm-gc-breakpoints.js
+++ b/test/inspector/debugger/wasm-gc-breakpoints.js
@@ -14,8 +14,10 @@
0x00, 0x61, 0x73, 0x6d, 1, 0, 0, 0, // wasm magic
0x01, // type section
- 0x16, // section length
- 0x04, // number of types
+ 0x18, // section length
+ 0x01, // number of type section entries
+ 0x49, // recursive type group
+ 0x04, // number of types in the recursive group
// type 0: struct $StrA (field ($byte i8) ($word i16) ($pointer (ref $StrB)))
0x5f, // struct
0x03, // field count
@@ -219,7 +221,7 @@
// Ignore javascript and full module wasm script, get scripts for functions.
const [, {params: wasm_script}] =
await Protocol.Debugger.onceScriptParsed(2);
- let offset = 107; // "local.set $varC" at the end.
+ let offset = 109; // "local.set $varC" at the end.
await setBreakpoint(offset, wasm_script.scriptId, wasm_script.url);
InspectorTest.log('Calling main()');
await WasmInspectorTest.evalWithUrl('instance.exports.main()', 'runWasm');
diff --git a/test/mjsunit/wasm/call-ref.js b/test/mjsunit/wasm/call-ref.js
index 812a764..2a38bba 100644
--- a/test/mjsunit/wasm/call-ref.js
+++ b/test/mjsunit/wasm/call-ref.js
@@ -96,10 +96,11 @@
print("--imported function from another module--");
assertEquals(57, instance.exports.test_wasm_import());
+ /* TODO(7748): Implement cross-module type canonicalization.
print("--not imported function defined in another module--");
assertEquals(19, instance.exports.main(
exporting_instance.exports.addition, 12, 7));
-
+*/
print("--imported WebAssembly.Function--")
assertEquals(21, instance.exports.test_js_api_import());
print("--not imported WebAssembly.Function--")
diff --git a/test/mjsunit/wasm/gc-nominal.js b/test/mjsunit/wasm/gc-nominal.js
index 957381f..49221c3 100644
--- a/test/mjsunit/wasm/gc-nominal.js
+++ b/test/mjsunit/wasm/gc-nominal.js
@@ -9,12 +9,13 @@
(function TestNominalTypesBasic() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
- let struct1 = builder.addStructSubtype([makeField(kWasmI32, true)]);
- let struct2 = builder.addStructSubtype(
+ builder.setNominal();
+ let struct1 = builder.addStruct([makeField(kWasmI32, true)]);
+ let struct2 = builder.addStruct(
[makeField(kWasmI32, true), makeField(kWasmI32, true)], struct1);
- let array1 = builder.addArraySubtype(kWasmI32, true);
- let array2 = builder.addArraySubtype(kWasmI32, true, array1);
+ let array1 = builder.addArray(kWasmI32, true);
+ let array2 = builder.addArray(kWasmI32, true, array1);
builder.addFunction("main", kSig_v_v)
.addLocals(wasmOptRefType(struct1), 1)
@@ -29,14 +30,13 @@
kGCPrefix, kExprStructNewDefault, struct2, kExprLocalSet, 0,
// Check that we can create an array with explicit RTT...
kExprI32Const, 10, // length
- kGCPrefix, kExprRttCanon, array2, kGCPrefix,
- kExprArrayNewDefaultWithRtt, array2,
+ kGCPrefix, kExprRttCanon, array2,
+ kGCPrefix, kExprArrayNewDefaultWithRtt, array2,
// ...and upcast it.
kExprLocalSet, 1,
// Check that we can create an array with implicit RTT.
kExprI32Const, 10, // length
- kGCPrefix, kExprArrayNewDefault, array2, kExprLocalSet, 1
- ])
+ kGCPrefix, kExprArrayNewDefault, array2, kExprLocalSet, 1])
.exportFunc();
// This test is only interested in type checking.
@@ -46,10 +46,9 @@
(function TestSubtypingDepthTooLarge() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
- builder.addStructSubtype([]);
- for (let i = 0; i < 32; i++) {
- builder.addStructSubtype([], i);
- }
+ builder.setNominal();
+ builder.addStruct([]);
+ for (let i = 0; i < 32; i++) builder.addStruct([], i);
assertThrows(
() => builder.instantiate(), WebAssembly.CompileError,
/subtyping depth is greater than allowed/);
@@ -58,7 +57,8 @@
(function TestArrayInitFromDataStatic() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
- let array_type_index = builder.addArraySubtype(kWasmI16, true);
+ builder.setNominal();
+ let array_type_index = builder.addArray(kWasmI16, true);
let dummy_byte = 0xff;
let element_0 = 1000;
diff --git a/test/mjsunit/wasm/gc-optimizations.js b/test/mjsunit/wasm/gc-optimizations.js
index 9fcf9cf..145977c 100644
--- a/test/mjsunit/wasm/gc-optimizations.js
+++ b/test/mjsunit/wasm/gc-optimizations.js
@@ -376,9 +376,10 @@
(function AllocationFolding() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
+ builder.setNominal();
- let struct_index = builder.addStructSubtype([makeField(kWasmI32, true)]);
- let struct_2 = builder.addStructSubtype([
+ let struct_index = builder.addStruct([makeField(kWasmI32, true)]);
+ let struct_2 = builder.addStruct([
makeField(wasmRefType(struct_index), false),
makeField(wasmRefType(struct_index), false)
]);
diff --git a/test/mjsunit/wasm/imported-function-types.js b/test/mjsunit/wasm/imported-function-types.js
index 8956440..756296c 100644
--- a/test/mjsunit/wasm/imported-function-types.js
+++ b/test/mjsunit/wasm/imported-function-types.js
@@ -35,8 +35,9 @@
return builder.instantiate({other: {func: imported_function}});
};
+// TODO(7748): Implement cross-module subtyping.
// Same form/different index should be fine.
-importing_module(exporting_module.exports.func2);
+// importing_module(exporting_module.exports.func2);
// Same index/different form should throw.
assertThrows(
() => importing_module(exporting_module.exports.func1),
diff --git a/test/mjsunit/wasm/load-immutable.js b/test/mjsunit/wasm/load-immutable.js
index b8d60c8..567175d 100644
--- a/test/mjsunit/wasm/load-immutable.js
+++ b/test/mjsunit/wasm/load-immutable.js
@@ -77,7 +77,8 @@
(function ImmutableLoadThroughEffect() {
var builder = new WasmModuleBuilder();
- var struct = builder.addStructSubtype([
+ builder.setNominal();
+ var struct = builder.addStruct([
makeField(kWasmI32, false), makeField(kWasmI32, true)]);
let effect = builder.addImport('m', 'f', kSig_v_v);
diff --git a/test/mjsunit/wasm/reference-globals.js b/test/mjsunit/wasm/reference-globals.js
index 6ab071f..361708d 100644
--- a/test/mjsunit/wasm/reference-globals.js
+++ b/test/mjsunit/wasm/reference-globals.js
@@ -6,6 +6,7 @@
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
+/* TODO(7748): Implement cross-module subtyping.
(function TestReferenceGlobals() {
print(arguments.callee.name);
@@ -105,6 +106,7 @@
// The correct function reference has been passed.
assertEquals(66, instance.exports.test_import(42, 24));
})();
+*/
(function TestStructInitExpr() {
print(arguments.callee.name);
diff --git a/test/mjsunit/wasm/reference-tables.js b/test/mjsunit/wasm/reference-tables.js
index d73a241..5494bbf 100644
--- a/test/mjsunit/wasm/reference-tables.js
+++ b/test/mjsunit/wasm/reference-tables.js
@@ -5,7 +5,7 @@
// Flags: --experimental-wasm-typed-funcref
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
-
+/* TODO(7748): Implement cross-module subtyping.
(function TestTables() {
print(arguments.callee.name);
var exporting_instance = (function() {
@@ -102,6 +102,7 @@
TypeError,
/Argument 1 must be null or a WebAssembly function of type compatible to/);
})();
+*/
(function TestNonNullableTables() {
print(arguments.callee.name);
@@ -109,11 +110,11 @@
var binary_type = builder.addType(kSig_i_ii);
- var addition = builder.addFunction('addition', kSig_i_ii).addBody([
+ var addition = builder.addFunction('addition', binary_type).addBody([
kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add
]);
var subtraction =
- builder.addFunction('subtraction', kSig_i_ii)
+ builder.addFunction('subtraction', binary_type)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Sub])
.exportFunc();
diff --git a/test/mjsunit/wasm/speculative-inlining.js b/test/mjsunit/wasm/speculative-inlining.js
index e783be5..0572e74 100644
--- a/test/mjsunit/wasm/speculative-inlining.js
+++ b/test/mjsunit/wasm/speculative-inlining.js
@@ -44,17 +44,19 @@
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
+ let sig_index = builder.addType(kSig_i_i);
+
// h(x) = x - 1
- let callee0 = builder.addFunction("callee0", kSig_i_i)
+ let callee0 = builder.addFunction("callee0", sig_index)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub]);
// f(x) = x - 2
- let callee1 = builder.addFunction("callee1", kSig_i_i)
+ let callee1 = builder.addFunction("callee1", sig_index)
.addBody([kExprLocalGet, 0, kExprI32Const, 2, kExprI32Sub]);
- let global0 = builder.addGlobal(wasmRefType(1), false,
+ let global0 = builder.addGlobal(wasmRefType(sig_index), false,
WasmInitExpr.RefFunc(callee0.index));
- let global1 = builder.addGlobal(wasmRefType(1), false,
+ let global1 = builder.addGlobal(wasmRefType(sig_index), false,
WasmInitExpr.RefFunc(callee1.index));
// g(x, y) = if (y) { h(5) + x } else { f(7) + x }
@@ -114,17 +116,19 @@
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
+ let sig_index = builder.addType(kSig_i_i);
+
// h(x) = x - 1
- let callee0 = builder.addFunction("callee0", kSig_i_i)
+ let callee0 = builder.addFunction("callee0", sig_index)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub]);
// f(x) = x - 2
- let callee1 = builder.addFunction("callee1", kSig_i_i)
+ let callee1 = builder.addFunction("callee1", sig_index)
.addBody([kExprLocalGet, 0, kExprI32Const, 2, kExprI32Sub]);
- let global0 = builder.addGlobal(wasmRefType(1), false,
+ let global0 = builder.addGlobal(wasmRefType(sig_index), false,
WasmInitExpr.RefFunc(callee0.index));
- let global1 = builder.addGlobal(wasmRefType(1), false,
+ let global1 = builder.addGlobal(wasmRefType(sig_index), false,
WasmInitExpr.RefFunc(callee1.index));
// g(x, y) = if (y) { h(x) } else { f(x) }
@@ -151,6 +155,7 @@
assertEquals(8, instance.exports.main(10, 0));
})();
+/* TODO(7748): Implement cross-module subtyping.
(function CallRefImportedFunction() {
print(arguments.callee.name);
@@ -191,6 +196,7 @@
// The function f1 defined in another module should not be inlined.
assertEquals(1, instance2.exports.main(0, instance1.exports.f1));
})();
+*/
// Check that we handle WasmJSFunctions properly and do not inline them, both
// in the monomorphic and polymorphic case.
diff --git a/test/mjsunit/wasm/wasm-module-builder.js b/test/mjsunit/wasm/wasm-module-builder.js
index e644794..b6666d5 100644
--- a/test/mjsunit/wasm/wasm-module-builder.js
+++ b/test/mjsunit/wasm/wasm-module-builder.js
@@ -77,9 +77,13 @@
let kWasmFunctionTypeForm = 0x60;
let kWasmStructTypeForm = 0x5f;
let kWasmArrayTypeForm = 0x5e;
-let kWasmFunctionSubtypeForm = 0x5d;
-let kWasmStructSubtypeForm = 0x5c;
-let kWasmArraySubtypeForm = 0x5b;
+let kWasmFunctionNominalForm = 0x5d;
+let kWasmStructNominalForm = 0x5c;
+let kWasmArrayNominalForm = 0x5b;
+let kWasmSubtypeForm = 0x50;
+let kWasmRecursiveTypeGroupForm = 0x49;
+
+let kNoSuperType = 0xFFFFFFFF;
let kLimitsNoMaximum = 0x00;
let kLimitsWithMaximum = 0x01;
@@ -1299,38 +1303,25 @@
}
class WasmStruct {
- constructor(fields) {
+ constructor(fields, supertype_idx) {
if (!Array.isArray(fields)) {
throw new Error('struct fields must be an array');
}
this.fields = fields;
this.type_form = kWasmStructTypeForm;
- }
-}
-
-class WasmStructSubtype extends WasmStruct {
- constructor(fields, supertype_idx) {
- super(fields);
this.supertype = supertype_idx;
- this.type_form = kWasmStructSubtypeForm;
}
}
class WasmArray {
- constructor(type, mutability) {
+ constructor(type, mutability, supertype_idx) {
this.type = type;
this.mutability = mutability;
this.type_form = kWasmArrayTypeForm;
+ this.supertype = supertype_idx;
}
}
-class WasmArraySubtype extends WasmArray {
- constructor(type, mutability, supertype_idx) {
- super(type, mutability);
- this.supertype = supertype_idx;
- this.type_form = kWasmArraySubtypeForm;
- }
-}
class WasmElemSegment {
constructor(table, offset, type, elements, is_decl) {
this.table = table;
@@ -1383,6 +1374,7 @@
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.
this.early_data_count_section = false;
return this;
}
@@ -1442,6 +1434,9 @@
this.explicit.push(this.createCustomSection(name, bytes));
}
+ // TODO(7748): Support recursive groups.
+
+ // TODO(7748): Support function supertypes.
addType(type) {
this.types.push(type);
var pl = type.params.length; // should have params
@@ -1449,24 +1444,13 @@
return this.types.length - 1;
}
- addStruct(fields) {
- this.types.push(new WasmStruct(fields));
+ addStruct(fields, supertype_idx = kNoSuperType) {
+ this.types.push(new WasmStruct(fields, supertype_idx));
return this.types.length - 1;
}
- kGenericSuperType = 0xFFFFFFFE;
- addStructSubtype(fields, supertype_idx = this.kGenericSuperType) {
- this.types.push(new WasmStructSubtype(fields, supertype_idx));
- return this.types.length - 1;
- }
-
- addArray(type, mutability) {
- this.types.push(new WasmArray(type, mutability));
- return this.types.length - 1;
- }
-
- addArraySubtype(type, mutability, supertype_idx = this.kGenericSuperType) {
- this.types.push(new WasmArraySubtype(type, mutability, supertype_idx));
+ addArray(type, mutability, supertype_idx = kNoSuperType) {
+ this.types.push(new WasmArray(type, mutability, supertype_idx));
return this.types.length - 1;
}
@@ -1679,6 +1663,10 @@
return this;
}
+ setNominal() {
+ this.nominal = true;
+ }
+
setName(name) {
this.name = name;
return this;
@@ -1698,32 +1686,51 @@
section.emit_u32v(wasm.types.length);
for (let type of wasm.types) {
if (type instanceof WasmStruct) {
- section.emit_u8(type.type_form);
+ 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_u32v(type.fields.length);
for (let field of type.fields) {
section.emit_type(field.type);
section.emit_u8(field.mutability ? 1 : 0);
}
- if (type instanceof WasmStructSubtype) {
- if (type.supertype === this.kGenericSuperType) {
+ if (this.nominal) {
+ if (type.supertype === kNoSuperType) {
section.emit_u8(kDataRefCode);
} else {
section.emit_heap_type(type.supertype);
}
}
} else if (type instanceof WasmArray) {
- section.emit_u8(type.type_form);
+ 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_type(type.type);
section.emit_u8(type.mutability ? 1 : 0);
- if (type instanceof WasmArraySubtype) {
- if (type.supertype === this.kGenericSuperType) {
+ if (this.nominal) {
+ if (type.supertype === kNoSuperType) {
section.emit_u8(kDataRefCode);
} else {
section.emit_heap_type(type.supertype);
}
}
} else {
- section.emit_u8(kWasmFunctionTypeForm);
+ /* 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_u32v(type.params.length);
for (let param of type.params) {
section.emit_type(param);
@@ -1732,6 +1739,15 @@
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);
+ }
}
}
});
diff --git a/test/unittests/wasm/function-body-decoder-unittest.cc b/test/unittests/wasm/function-body-decoder-unittest.cc
index 0848cf5..ad272ca 100644
--- a/test/unittests/wasm/function-body-decoder-unittest.cc
+++ b/test/unittests/wasm/function-body-decoder-unittest.cc
@@ -86,24 +86,19 @@
CHECK_LE(mod.globals.size(), kMaxByteSizedLeb128);
return static_cast<byte>(mod.globals.size() - 1);
}
- byte AddSignature(const FunctionSig* sig) {
- mod.add_signature(sig, kNoSuperType);
+ byte AddSignature(const FunctionSig* sig, uint32_t supertype = kNoSuperType) {
+ mod.add_signature(sig, supertype);
CHECK_LE(mod.types.size(), kMaxByteSizedLeb128);
return static_cast<byte>(mod.types.size() - 1);
}
byte AddFunction(const FunctionSig* sig, bool declared = true) {
byte sig_index = AddSignature(sig);
- mod.functions.push_back(
- {sig, // sig
- static_cast<uint32_t>(mod.functions.size()), // func_index
- sig_index, // sig_index
- {0, 0}, // code
- 0, // feedback slots
- false, // import
- false, // export
- declared}); // declared
- CHECK_LE(mod.functions.size(), kMaxByteSizedLeb128);
- return static_cast<byte>(mod.functions.size() - 1);
+ return AddFunctionImpl(sig, sig_index, declared);
+ }
+ byte AddFunction(uint32_t sig_index, bool declared = true) {
+ DCHECK(mod.has_signature(sig_index));
+ return AddFunctionImpl(mod.types[sig_index].function_sig, sig_index,
+ declared);
}
byte AddImport(const FunctionSig* sig) {
byte result = AddFunction(sig);
@@ -181,6 +176,21 @@
WasmModule* module() { return &mod; }
private:
+ byte AddFunctionImpl(const FunctionSig* sig, uint32_t sig_index,
+ bool declared) {
+ mod.functions.push_back(
+ {sig, // sig
+ static_cast<uint32_t>(mod.functions.size()), // func_index
+ sig_index, // sig_index
+ {0, 0}, // code
+ 0, // feedback slots
+ false, // import
+ false, // export
+ declared}); // declared
+ CHECK_LE(mod.functions.size(), kMaxByteSizedLeb128);
+ return static_cast<byte>(mod.functions.size() - 1);
+ }
+
AccountingAllocator allocator;
WasmModule mod;
};
@@ -1164,13 +1174,6 @@
WASM_GC_OP(kExprRefCast), struct_index,
struct_index, kExprDrop});
- ExpectValidates(sigs.v_v(),
- {WASM_UNREACHABLE, WASM_GC_OP(kExprRttSub), array_index,
- WASM_GC_OP(kExprRttSub), array_index, kExprDrop});
- ExpectValidates(sigs.v_v(),
- {WASM_UNREACHABLE, WASM_GC_OP(kExprRttFreshSub), array_index,
- WASM_GC_OP(kExprRttFreshSub), array_index, kExprDrop});
-
ExpectValidates(sigs.v_v(), {WASM_UNREACHABLE, kExprBrOnNull, 0, WASM_DROP});
ExpectValidates(&sig_v_s, {WASM_UNREACHABLE, WASM_LOCAL_GET(0), kExprBrOnNull,
@@ -1958,21 +1961,23 @@
EXPERIMENTAL_FLAG_SCOPE(gc);
byte empty_struct = builder.AddStruct({});
- byte super_struct = builder.AddStruct({F(kWasmI32, false)});
- byte sub_struct = builder.AddStruct({F(kWasmI32, false), F(kWasmF64, false)});
+ byte super_struct = builder.AddStruct({F(kWasmI32, false)}, empty_struct);
+ byte sub_struct =
+ builder.AddStruct({F(kWasmI32, false), F(kWasmF64, false)}, super_struct);
- byte table_type = builder.AddSignature(
- FunctionSig::Build(zone(), {ValueType::Ref(super_struct, kNullable)},
- {ValueType::Ref(sub_struct, kNullable)}));
byte table_supertype = builder.AddSignature(
FunctionSig::Build(zone(), {ValueType::Ref(empty_struct, kNullable)},
{ValueType::Ref(sub_struct, kNullable)}));
+ byte table_type = builder.AddSignature(
+ FunctionSig::Build(zone(), {ValueType::Ref(super_struct, kNullable)},
+ {ValueType::Ref(sub_struct, kNullable)}),
+ table_supertype);
auto function_sig =
FunctionSig::Build(zone(), {ValueType::Ref(sub_struct, kNullable)},
{ValueType::Ref(super_struct, kNullable)});
- byte function_type = builder.AddSignature(function_sig);
+ byte function_type = builder.AddSignature(function_sig, table_type);
- byte function = builder.AddFunction(function_sig);
+ byte function = builder.AddFunction(function_type);
byte table = builder.InitializeTable(ValueType::Ref(table_type, kNullable));
@@ -2677,10 +2682,11 @@
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
- byte supertype1 = builder.AddStruct({F(kWasmI8, true), F(kWasmI16, false)});
- byte supertype2 = builder.AddStruct({F(kWasmI8, true)});
+ byte supertype1 = builder.AddStruct({F(kWasmI8, true)});
+ byte supertype2 =
+ builder.AddStruct({F(kWasmI8, true), F(kWasmI16, false)}, supertype1);
byte subtype = builder.AddStruct(
- {F(kWasmI8, true), F(kWasmI16, false), F(kWasmI32, true)});
+ {F(kWasmI8, true), F(kWasmI16, false), F(kWasmI32, true)}, supertype2);
ExpectValidates(
sigs.v_v(),
{WASM_BLOCK_R(wasm::ValueType::Ref(supertype1, kNonNullable),
@@ -3640,7 +3646,7 @@
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
byte structural_type = builder.AddStruct({F(kWasmI32, true)});
- byte nominal_type = builder.AddStruct({F(kWasmI32, true)}, kGenericSuperType);
+ byte nominal_type = builder.AddStruct({F(kWasmI32, true)});
AddLocals(optref(structural_type), 1);
AddLocals(optref(nominal_type), 1);
// Try to assign a nominally-typed value to a structurally-typed local.
@@ -4330,94 +4336,6 @@
}
}
-TEST_F(FunctionBodyDecoderTest, RttSub) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
-
- uint8_t array_type_index = builder.AddArray(kWasmI8, true);
- uint8_t super_struct_type_index = builder.AddStruct({F(kWasmI16, true)});
- uint8_t sub_struct_type_index =
- builder.AddStruct({F(kWasmI16, true), F(kWasmI32, false)});
-
- // Trivial type error.
- ExpectFailure(
- sigs.v_v(), {WASM_RTT_SUB(array_type_index, WASM_I32V(42)), kExprDrop},
- kAppendEnd, "rtt.sub[0] expected rtt for a supertype of type 0");
- ExpectFailure(
- sigs.v_v(),
- {WASM_RTT_FRESH_SUB(array_type_index, WASM_I32V(42)), kExprDrop},
- kAppendEnd, "rtt.fresh_sub[0] expected rtt for a supertype of type 0");
-
- {
- ValueType type = ValueType::Rtt(array_type_index, 1);
- FunctionSig sig(1, 0, &type);
- // Can build an rtt.sub with self type for an array type.
- ExpectValidates(&sig, {WASM_RTT_SUB(array_type_index,
- WASM_RTT_CANON(array_type_index))});
- ExpectValidates(&sig,
- {WASM_RTT_FRESH_SUB(array_type_index,
- WASM_RTT_CANON(array_type_index))});
- // Fails when argument to rtt.sub is not a supertype.
- ExpectFailure(sigs.v_v(),
- {WASM_RTT_SUB(super_struct_type_index,
- WASM_RTT_CANON(array_type_index)),
- kExprDrop},
- kAppendEnd,
- "rtt.sub[0] expected rtt for a supertype of type 1");
- ExpectFailure(sigs.v_v(),
- {WASM_RTT_FRESH_SUB(super_struct_type_index,
- WASM_RTT_CANON(array_type_index)),
- kExprDrop},
- kAppendEnd,
- "rtt.fresh_sub[0] expected rtt for a supertype of type 1");
- }
-
- {
- ValueType type = ValueType::Rtt(super_struct_type_index, 1);
- FunctionSig sig(1, 0, &type);
- // Can build an rtt.sub with self type for a struct type.
- ExpectValidates(&sig,
- {WASM_RTT_SUB(super_struct_type_index,
- WASM_RTT_CANON(super_struct_type_index))});
- ExpectValidates(
- &sig, {WASM_RTT_FRESH_SUB(super_struct_type_index,
- WASM_RTT_CANON(super_struct_type_index))});
- // Fails when argument to rtt.sub is not a supertype.
- ExpectFailure(sigs.v_v(),
- {WASM_RTT_SUB(super_struct_type_index,
- WASM_RTT_CANON(array_type_index))},
- kAppendEnd,
- "rtt.sub[0] expected rtt for a supertype of type 1");
- ExpectFailure(sigs.v_v(),
- {WASM_RTT_FRESH_SUB(super_struct_type_index,
- WASM_RTT_CANON(array_type_index))},
- kAppendEnd,
- "rtt.fresh_sub[0] expected rtt for a supertype of type 1");
- ExpectFailure(sigs.v_v(),
- {WASM_RTT_SUB(super_struct_type_index,
- WASM_RTT_CANON(sub_struct_type_index))},
- kAppendEnd,
- "rtt.sub[0] expected rtt for a supertype of type 1");
- ExpectFailure(sigs.v_v(),
- {WASM_RTT_FRESH_SUB(super_struct_type_index,
- WASM_RTT_CANON(sub_struct_type_index))},
- kAppendEnd,
- "rtt.fresh_sub[0] expected rtt for a supertype of type 1");
- }
-
- {
- // Can build an rtt from a stuct supertype.
- ValueType type = ValueType::Rtt(sub_struct_type_index, 1);
- FunctionSig sig(1, 0, &type);
- ExpectValidates(&sig,
- {WASM_RTT_SUB(sub_struct_type_index,
- WASM_RTT_CANON(super_struct_type_index))});
- ExpectValidates(
- &sig, {WASM_RTT_FRESH_SUB(sub_struct_type_index,
- WASM_RTT_CANON(super_struct_type_index))});
- }
-}
-
TEST_F(FunctionBodyDecoderTest, RefTestCast) {
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
@@ -4516,7 +4434,8 @@
WASM_FEATURE_SCOPE(gc);
byte super_struct = builder.AddStruct({F(kWasmI16, true)});
- byte sub_struct = builder.AddStruct({F(kWasmI16, true), F(kWasmI32, false)});
+ byte sub_struct =
+ builder.AddStruct({F(kWasmI16, true), F(kWasmI32, false)}, super_struct);
ValueType supertype = ValueType::Ref(super_struct, kNullable);
ValueType subtype = ValueType::Ref(sub_struct, kNullable);
diff --git a/test/unittests/wasm/module-decoder-unittest.cc b/test/unittests/wasm/module-decoder-unittest.cc
index 2a4a09c..19c9ea6 100644
--- a/test/unittests/wasm/module-decoder-unittest.cc
+++ b/test/unittests/wasm/module-decoder-unittest.cc
@@ -567,15 +567,15 @@
WASM_FEATURE_SCOPE(gc);
static const byte referencing_undefined_global_nested[] = {
SECTION(Type, ENTRY_COUNT(1), WASM_ARRAY_DEF(kI32Code, true)),
- SECTION(Global, ENTRY_COUNT(2), // --
- WASM_RTT_WITH_DEPTH(1, 0), // type
- 0, // mutable
- WASM_RTT_SUB(0, // init value
- WASM_GLOBAL_GET(1)), // --
- kExprEnd, // --
- WASM_RTT_WITH_DEPTH(0, 0), // type
- 0, // mutable
- WASM_RTT_CANON(0), kExprEnd) // init value
+ SECTION(Global, ENTRY_COUNT(2), // --
+ kRefCode, 0, // type
+ 0, // mutable
+ WASM_ARRAY_NEW_DEFAULT(0, // init value
+ WASM_GLOBAL_GET(1)), // --
+ kExprEnd, // --
+ kI32Code, // type
+ 0, // mutable
+ WASM_I32V(10), kExprEnd) // init value
};
EXPECT_FAILURE_WITH_MSG(referencing_undefined_global_nested,
"Invalid global index: 1");
@@ -809,229 +809,6 @@
"type error in init. expression[0] (expected (rtt 1 0), got (rtt 0 0))");
}
-TEST_F(WasmModuleVerifyTest, GlobalRttSubOfCanon) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
- static const byte data[] = {
- SECTION(Type, ENTRY_COUNT(2),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
- WASM_STRUCT_DEF(FIELD_COUNT(2), STRUCT_FIELD(kI32Code, true),
- STRUCT_FIELD(kI32Code, true))),
- SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(1, 1), 1,
- WASM_RTT_SUB(1, WASM_RTT_CANON(0)), kExprEnd)};
- ModuleResult result = DecodeModule(data, data + sizeof(data));
- EXPECT_OK(result);
-}
-
-TEST_F(WasmModuleVerifyTest, GlobalRttFreshSubOfCanon) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
-
- static const byte data[] = {
- SECTION(Type, ENTRY_COUNT(2),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
- WASM_STRUCT_DEF(FIELD_COUNT(2), STRUCT_FIELD(kI32Code, true),
- STRUCT_FIELD(kI32Code, true))),
- SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(1, 1), 1,
- WASM_RTT_FRESH_SUB(1, WASM_RTT_CANON(0)), kExprEnd)};
- ModuleResult result = DecodeModule(data, data + sizeof(data));
- EXPECT_OK(result);
-}
-
-TEST_F(WasmModuleVerifyTest, GlobalRttSubOfSubOfCanon) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
- static const byte data[] = {
- SECTION(Type, ENTRY_COUNT(2),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
- WASM_STRUCT_DEF(FIELD_COUNT(2), STRUCT_FIELD(kI32Code, true),
- STRUCT_FIELD(kI32Code, true))),
- SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(2, 1), 1,
- WASM_RTT_SUB(1, WASM_RTT_SUB(1, WASM_RTT_CANON(0))), kExprEnd)};
- ModuleResult result = DecodeModule(data, data + sizeof(data));
- EXPECT_OK(result);
-}
-
-TEST_F(WasmModuleVerifyTest, GlobalRttFreshSubOfSubOfCanon) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
-
- static const byte data[] = {
- SECTION(Type, ENTRY_COUNT(2),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
- WASM_STRUCT_DEF(FIELD_COUNT(2), STRUCT_FIELD(kI32Code, true),
- STRUCT_FIELD(kI32Code, true))),
- SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(2, 1), 1,
- WASM_RTT_FRESH_SUB(1, WASM_RTT_SUB(1, WASM_RTT_CANON(0))),
- kExprEnd)};
- ModuleResult result = DecodeModule(data, data + sizeof(data));
- EXPECT_OK(result);
-}
-
-TEST_F(WasmModuleVerifyTest, GlobalRttFreshSubOfFreshSubOfCanon) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
-
- static const byte data[] = {
- SECTION(Type, ENTRY_COUNT(2),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
- WASM_STRUCT_DEF(FIELD_COUNT(2), STRUCT_FIELD(kI32Code, true),
- STRUCT_FIELD(kI32Code, true))),
- SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(2, 1), 1,
- WASM_RTT_FRESH_SUB(1, WASM_RTT_FRESH_SUB(1, WASM_RTT_CANON(0))),
- kExprEnd)};
- ModuleResult result = DecodeModule(data, data + sizeof(data));
- EXPECT_OK(result);
-}
-
-TEST_F(WasmModuleVerifyTest, GlobalRttSubOfGlobal) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
- static const byte data[] = {
- SECTION(Type, ENTRY_COUNT(2),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
- WASM_STRUCT_DEF(FIELD_COUNT(2), STRUCT_FIELD(kI32Code, true),
- STRUCT_FIELD(kI32Code, true))),
- SECTION(Import, // section header
- ENTRY_COUNT(1), // number of imports
- ADD_COUNT('m'), // module name
- ADD_COUNT('f'), // global name
- kExternalGlobal, // import kind
- WASM_RTT_WITH_DEPTH(0, 0), // type
- 0), // mutability
- SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(1, 1), 1,
- WASM_RTT_SUB(1, WASM_GLOBAL_GET(0)), kExprEnd)};
- ModuleResult result = DecodeModule(data, data + sizeof(data));
- EXPECT_OK(result);
-}
-
-TEST_F(WasmModuleVerifyTest, GlobalRttFreshSubOfGlobal) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
-
- static const byte data[] = {
- SECTION(Type, ENTRY_COUNT(2),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
- WASM_STRUCT_DEF(FIELD_COUNT(2), STRUCT_FIELD(kI32Code, true),
- STRUCT_FIELD(kI32Code, true))),
- SECTION(Import, // section header
- ENTRY_COUNT(1), // number of imports
- ADD_COUNT('m'), // module name
- ADD_COUNT('f'), // global name
- kExternalGlobal, // import kind
- WASM_RTT_WITH_DEPTH(0, 0), // type
- 0), // mutability
- SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(1, 1), 1,
- WASM_RTT_FRESH_SUB(1, WASM_GLOBAL_GET(0)), kExprEnd)};
- ModuleResult result = DecodeModule(data, data + sizeof(data));
- EXPECT_OK(result);
-}
-
-TEST_F(WasmModuleVerifyTest, GlobalRttSubOfGlobalTypeError) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
- static const byte data[] = {
- SECTION(Type, ENTRY_COUNT(1),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true))),
- SECTION(Import, // section header
- ENTRY_COUNT(1), // number of imports
- ADD_COUNT('m'), // module name
- ADD_COUNT('f'), // global name
- kExternalGlobal, // import kind
- kI32Code, // type
- 0), // mutability
- SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(1, 0), 1,
- WASM_RTT_SUB(0, WASM_GLOBAL_GET(0)), kExprEnd)};
- ModuleResult result = DecodeModule(data, data + sizeof(data));
- EXPECT_NOT_OK(result,
- "rtt.sub[0] expected rtt for a supertype of type 0, found "
- "global.get of type i32");
-}
-
-TEST_F(WasmModuleVerifyTest, GlobalRttFreshSubOfGlobalTypeError) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
-
- static const byte data[] = {
- SECTION(Type, ENTRY_COUNT(1),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true))),
- SECTION(Import, // section header
- ENTRY_COUNT(1), // number of imports
- ADD_COUNT('m'), // module name
- ADD_COUNT('f'), // global name
- kExternalGlobal, // import kind
- kI32Code, // type
- 0), // mutability
- SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(1, 0), 1,
- WASM_RTT_FRESH_SUB(0, WASM_GLOBAL_GET(0)), kExprEnd)};
- ModuleResult result = DecodeModule(data, data + sizeof(data));
- EXPECT_NOT_OK(result,
- "rtt.fresh_sub[0] expected rtt for a supertype of type 0, "
- "found global.get of type i32");
-}
-
-#if !V8_OS_FUCHSIA
-TEST_F(WasmModuleVerifyTest, GlobalRttSubIllegalParent) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
- static const byte data[] = {
- SECTION(Type, ENTRY_COUNT(2),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kF32Code, true))),
- SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(1, 1), 1,
- WASM_RTT_SUB(1, WASM_RTT_CANON(0)), kExprEnd)};
- ModuleResult result = DecodeModule(data, data + sizeof(data));
- EXPECT_NOT_OK(result,
- "rtt.sub[0] expected rtt for a supertype of type 1, found "
- "rtt.canon of type (rtt 0 0)");
-}
-
-TEST_F(WasmModuleVerifyTest, GlobalRttFreshSubIllegalParent) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
-
- static const byte data[] = {
- SECTION(Type, ENTRY_COUNT(2),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true)),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kF32Code, true))),
- SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(1, 1), 1,
- WASM_RTT_FRESH_SUB(1, WASM_RTT_CANON(0)), kExprEnd)};
- ModuleResult result = DecodeModule(data, data + sizeof(data));
- EXPECT_NOT_OK(result,
- "rtt.fresh_sub[0] expected rtt for a supertype of type 1, "
- "found rtt.canon of type (rtt 0 0)");
-}
-#endif // !V8_OS_FUCHSIA
-
-TEST_F(WasmModuleVerifyTest, RttSubGlobalTypeError) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
- static const byte data[] = {
- SECTION(Type, ENTRY_COUNT(1),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true))),
- SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(0, 0), 1,
- WASM_RTT_SUB(0, WASM_RTT_CANON(0)), kExprEnd)};
- ModuleResult result = DecodeModule(data, data + sizeof(data));
- EXPECT_NOT_OK(
- result,
- "type error in init. expression[0] (expected (rtt 0 0), got (rtt 1 0))");
-}
-
-TEST_F(WasmModuleVerifyTest, RttFreshSubGlobalTypeError) {
- WASM_FEATURE_SCOPE(typed_funcref);
- WASM_FEATURE_SCOPE(gc);
-
- static const byte data[] = {
- SECTION(Type, ENTRY_COUNT(1),
- WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true))),
- SECTION(Global, ENTRY_COUNT(1), WASM_RTT_WITH_DEPTH(0, 0), 1,
- WASM_RTT_FRESH_SUB(0, WASM_RTT_CANON(0)), kExprEnd)};
- ModuleResult result = DecodeModule(data, data + sizeof(data));
- EXPECT_NOT_OK(
- result,
- "type error in init. expression[0] (expected (rtt 0 0), got (rtt 1 0))");
-}
-
TEST_F(WasmModuleVerifyTest, StructNewInitExpr) {
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
@@ -1047,12 +824,12 @@
static const byte global_args[] = {
SECTION(Type, ENTRY_COUNT(1), // --
WASM_STRUCT_DEF(FIELD_COUNT(1), STRUCT_FIELD(kI32Code, true))),
- SECTION(Global, ENTRY_COUNT(3), // --
- kI32Code, 0, // type, mutability
- WASM_INIT_EXPR_I32V_1(10), // --
- kRttWithDepthCode, 1, 0, 0, // type, mutability
- WASM_RTT_SUB(0, WASM_RTT_CANON(0)), kExprEnd, // --
- kRefCode, 0, 0, // type, mutability
+ SECTION(Global, ENTRY_COUNT(3), // --
+ kI32Code, 0, // type, mutability
+ WASM_INIT_EXPR_I32V_1(10), // --
+ kRttWithDepthCode, 0, 0, 0, // type, mutability
+ WASM_RTT_CANON(0), kExprEnd, // --
+ kRefCode, 0, 0, // type, mutability
WASM_INIT_EXPR_STRUCT_NEW(0, WASM_GLOBAL_GET(0),
WASM_GLOBAL_GET(1)))};
EXPECT_VERIFIES(global_args);
@@ -1230,18 +1007,18 @@
// Inheritance: t1 <: t2 <: t0
static const byte all_good[] = {
SECTION(Type, ENTRY_COUNT(3), // --
- kWasmStructSubtypeCode, // type #0
+ kWasmStructNominalCode, // type #0
1, // field count
kI32Code, 1, // mut i32
kDataRefCode, // root of type hierarchy
- kWasmStructSubtypeCode, // type #1
+ kWasmStructNominalCode, // type #1
2, // field count
kI32Code, 1, // mut i32 (inherited)
kI64Code, 1, // mut i32 (added)
2, // supertype
- kWasmStructSubtypeCode, // type #2
+ kWasmStructNominalCode, // type #2
1, // field count
kI32Code, 1, // mut i32 (inherited)
0)}; // supertype
@@ -1249,26 +1026,26 @@
ModuleResult result = DecodeModule(all_good, all_good + sizeof(all_good));
EXPECT_OK(result);
WasmModule* module = result.value().get();
- EXPECT_EQ(kGenericSuperType, module->supertype(0));
+ 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), // --
- kWasmStructSubtypeCode, 0, // empty struct
+ kWasmStructNominalCode, 0, // empty struct
kDataRefCode, // root of hierarchy
- kWasmStructSubtypeCode, // type1
+ kWasmStructNominalCode, // type1
1, // field count
kOptRefCode, 1, 1, // mut optref type1
0, // supertype
- kWasmStructSubtypeCode, // type 2
+ kWasmStructNominalCode, // type 2
1, // field count
kOptRefCode, 3, 1, // mut optref type3
0, // supertype
- kWasmStructSubtypeCode, // type 3
+ kWasmStructNominalCode, // type 3
1, // field count
kOptRefCode, 2, 1, // mut optref type2
0)}; // supertype
@@ -1277,17 +1054,17 @@
static const byte mutual_ref_with_subtyping[] = {
SECTION(Type,
ENTRY_COUNT(3), // --
- kWasmStructSubtypeCode, //
+ kWasmStructNominalCode, //
1, // field count
kOptRefCode, 0, 0, // ref type0
kDataRefCode, // root of hierarchy
- kWasmStructSubtypeCode, // --
+ kWasmStructNominalCode, // --
1, // field count
kOptRefCode, 2, 0, // ref type2
0, // supertype
- kWasmStructSubtypeCode, // --
+ kWasmStructNominalCode, // --
1, // field count
kOptRefCode, 1, 0, // ref type1
0)}; // supertype
@@ -1295,28 +1072,31 @@
static const byte inheritance_cycle[] = {
SECTION(Type, ENTRY_COUNT(2), // --
- kWasmStructSubtypeCode, 0, 1, // no fields, supertype 1
- kWasmStructSubtypeCode, 0, 0)}; // no fields, supertype 0
+ 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]
- kWasmStructSubtypeCode, U32V_1(2), // t1:
+ 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, "invalid explicit supertype");
+ 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
- kWasmStructSubtypeCode, 0, // also empty
+ kWasmStructNominalCode, 0, // also empty
0)}; // supertype is structural type
- EXPECT_FAILURE_WITH_MSG(structural_supertype, "invalid explicit supertype");
+ EXPECT_FAILURE_WITH_MSG(
+ structural_supertype,
+ "mixing nominal and isorecursive types is not allowed");
static const byte supertype_oob[] = {
SECTION(Type, ENTRY_COUNT(1), // --
- kWasmStructSubtypeCode,
+ kWasmStructNominalCode,
0, // empty struct
13)}; // supertype with invalid index
EXPECT_FAILURE_WITH_MSG(supertype_oob, "Type index 13 is out of bounds");
@@ -1329,14 +1109,14 @@
static const byte all_good[] = {
SECTION(Type, ENTRY_COUNT(2), // --
- kWasmFunctionSubtypeCode, // type #0
+ kWasmFunctionNominalCode, // type #0
1, // params count
kRefCode, 0, // ref #0
1, // results count
kOptRefCode, 0, // optref #0
kFuncRefCode, // root of type hierarchy
- kWasmFunctionSubtypeCode, // type #1
+ kWasmFunctionNominalCode, // type #1
1, // params count
kOptRefCode, 0, // refined (contravariant)
1, // results count
@@ -1346,7 +1126,7 @@
ModuleResult result = DecodeModule(all_good, all_good + sizeof(all_good));
EXPECT_OK(result);
WasmModule* module = result.value().get();
- EXPECT_EQ(kGenericSuperType, module->supertype(0));
+ EXPECT_EQ(kNoSuperType, module->supertype(0));
EXPECT_EQ(0u, module->supertype(1));
}
@@ -3547,6 +3327,7 @@
EXPECT_NOT_OK(result, "data segments count 0 mismatch (1 expected)");
}
+/* TODO(7748): Add support for rec. groups.
TEST_F(WasmModuleVerifyTest, GcStructIdsPass) {
WASM_FEATURE_SCOPE(gc);
WASM_FEATURE_SCOPE(typed_funcref);
@@ -3561,7 +3342,7 @@
WASM_ARRAY_DEF(WASM_OPT_REF(0), true))};
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
-}
+}*/
TEST_F(WasmModuleVerifyTest, OutOfBoundsTypeInGlobal) {
WASM_FEATURE_SCOPE(typed_funcref);
diff --git a/test/unittests/wasm/subtyping-unittest.cc b/test/unittests/wasm/subtyping-unittest.cc
index 0e8f67f..ec9e9c0 100644
--- a/test/unittests/wasm/subtyping-unittest.cc
+++ b/test/unittests/wasm/subtyping-unittest.cc
@@ -24,27 +24,30 @@
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) {
+void DefineStruct(WasmModule* module, std::initializer_list<FieldInit> fields,
+ uint32_t supertype = kNoSuperType) {
StructType::Builder builder(module->signature_zone.get(),
static_cast<uint32_t>(fields.size()));
for (FieldInit field : fields) {
builder.AddField(field.first, field.second);
}
- return module->add_struct_type(builder.Build(), kNoSuperType);
+ return module->add_struct_type(builder.Build(), supertype);
}
-void DefineArray(WasmModule* module, FieldInit element_type) {
+void DefineArray(WasmModule* module, FieldInit element_type,
+ uint32_t supertype = kNoSuperType) {
module->add_array_type(module->signature_zone->New<ArrayType>(
element_type.first, element_type.second),
- kNoSuperType);
+ supertype);
}
void DefineSignature(WasmModule* module,
std::initializer_list<ValueType> params,
- std::initializer_list<ValueType> returns) {
+ std::initializer_list<ValueType> returns,
+ uint32_t supertype = kNoSuperType) {
module->add_signature(
FunctionSig::Build(module->signature_zone.get(), returns, params),
- kNoSuperType);
+ supertype);
}
TEST_F(WasmSubtypingTest, Subtyping) {
@@ -58,22 +61,22 @@
// Set up two identical modules.
for (WasmModule* module : {module1, module2}) {
/* 0 */ DefineStruct(module, {mut(ref(2)), immut(optRef(2))});
- /* 1 */ DefineStruct(module, {mut(ref(2)), immut(ref(2))});
+ /* 1 */ DefineStruct(module, {mut(ref(2)), immut(ref(2))}, 0);
/* 2 */ DefineArray(module, immut(ref(0)));
- /* 3 */ DefineArray(module, immut(ref(1)));
- /* 4 */ DefineStruct(module,
- {mut(ref(2)), immut(ref(3)), immut(kWasmF64)});
+ /* 3 */ DefineArray(module, immut(ref(1)), 2);
+ /* 4 */ DefineStruct(module, {mut(ref(2)), immut(ref(3)), immut(kWasmF64)},
+ 1);
/* 5 */ DefineStruct(module, {mut(optRef(2)), immut(ref(2))});
/* 6 */ DefineArray(module, mut(kWasmI32));
/* 7 */ DefineArray(module, immut(kWasmI32));
/* 8 */ DefineStruct(module, {mut(kWasmI32), immut(optRef(8))});
- /* 9 */ DefineStruct(module, {mut(kWasmI32), immut(optRef(8))});
+ /* 9 */ DefineStruct(module, {mut(kWasmI32), immut(optRef(8))}, 8);
/* 10 */ DefineSignature(module, {}, {});
/* 11 */ DefineSignature(module, {kWasmI32}, {kWasmI32});
/* 12 */ DefineSignature(module, {kWasmI32, kWasmI32}, {kWasmI32});
/* 13 */ DefineSignature(module, {ref(1)}, {kWasmI32});
- /* 14 */ DefineSignature(module, {ref(0)}, {kWasmI32});
- /* 15 */ DefineSignature(module, {ref(0)}, {ref(4)});
+ /* 14 */ DefineSignature(module, {ref(0)}, {kWasmI32}, 13);
+ /* 15 */ DefineSignature(module, {ref(0)}, {ref(4)}, 16);
/* 16 */ DefineSignature(module, {ref(0)}, {ref(0)});
}
@@ -86,13 +89,22 @@
#define SUBTYPE(type1, type2) \
EXPECT_TRUE(IsSubtypeOf(type1, type2, module1, module))
-#define NOT_SUBTYPE(type1, type2) \
- EXPECT_FALSE(IsSubtypeOf(type1, type2, module1, module))
#define SUBTYPE_IFF(type1, type2, condition) \
EXPECT_EQ(IsSubtypeOf(type1, type2, module1, module), condition)
+#define NOT_SUBTYPE(type1, type2) \
+ EXPECT_FALSE(IsSubtypeOf(type1, type2, module1, module))
+// Use only with indexed types.
+#define VALID_SUBTYPE(type1, type2) \
+ EXPECT_TRUE(ValidSubtypeDefinition(type1.ref_index(), type2.ref_index(), \
+ module1, module)); \
+ EXPECT_TRUE(IsSubtypeOf(type1, type2, module1, module));
+#define NOT_VALID_SUBTYPE(type1, type2) \
+ EXPECT_FALSE(ValidSubtypeDefinition(type1.ref_index(), type2.ref_index(), \
+ module1, module));
// Type judgements across modules should work the same as within one module.
- for (WasmModule* module : {module1, module2}) {
+ // TODO(7748): add module2 once we have a cross-module story.
+ for (WasmModule* module : {module1 /* , module2 */}) {
// Value types are unrelated, except if they are equal.
for (ValueType subtype : numeric_types) {
for (ValueType supertype : numeric_types) {
@@ -144,30 +156,30 @@
}
// Unrelated refs are unrelated.
- NOT_SUBTYPE(ref(0), ref(2));
- NOT_SUBTYPE(optRef(3), optRef(1));
+ NOT_VALID_SUBTYPE(ref(0), ref(2));
+ NOT_VALID_SUBTYPE(optRef(3), optRef(1));
// ref is a subtype of optref for the same struct/array.
- SUBTYPE(ref(0), optRef(0));
- SUBTYPE(ref(2), optRef(2));
+ VALID_SUBTYPE(ref(0), optRef(0));
+ VALID_SUBTYPE(ref(2), optRef(2));
// optref is not a subtype of ref for the same struct/array.
NOT_SUBTYPE(optRef(0), ref(0));
NOT_SUBTYPE(optRef(2), ref(2));
// ref is a subtype of optref if the same is true for the underlying
// structs/arrays.
- SUBTYPE(ref(3), optRef(2));
+ VALID_SUBTYPE(ref(3), optRef(2));
// Prefix subtyping for structs.
- SUBTYPE(optRef(4), optRef(0));
+ VALID_SUBTYPE(optRef(4), optRef(0));
// Mutable fields are invariant.
- NOT_SUBTYPE(ref(0), ref(5));
+ NOT_VALID_SUBTYPE(ref(0), ref(5));
// Immutable fields are covariant.
- SUBTYPE(ref(1), ref(0));
+ VALID_SUBTYPE(ref(1), ref(0));
// Prefix subtyping + immutable field covariance for structs.
- SUBTYPE(optRef(4), optRef(1));
+ VALID_SUBTYPE(optRef(4), optRef(1));
// No subtyping between mutable/immutable fields.
- NOT_SUBTYPE(ref(7), ref(6));
- NOT_SUBTYPE(ref(6), ref(7));
+ NOT_VALID_SUBTYPE(ref(7), ref(6));
+ NOT_VALID_SUBTYPE(ref(6), ref(7));
// Recursive types.
- SUBTYPE(ref(9), ref(8));
+ VALID_SUBTYPE(ref(9), ref(8));
// Identical rtts are subtypes of each other.
SUBTYPE(ValueType::Rtt(5, 3), ValueType::Rtt(5, 3));
@@ -180,8 +192,9 @@
NOT_SUBTYPE(ValueType::Rtt(5, 1), ValueType::Rtt(5, 3));
NOT_SUBTYPE(ValueType::Rtt(5, 8), ValueType::Rtt(5, 3));
// Rtts of identical types are subtype-related.
- SUBTYPE(ValueType::Rtt(8, 1), ValueType::Rtt(9, 1));
- SUBTYPE(ValueType::Rtt(8), ValueType::Rtt(9));
+ // TODO(7748): Implement type canonicalization.
+ // SUBTYPE(ValueType::Rtt(8, 1), ValueType::Rtt(9, 1));
+ // SUBTYPE(ValueType::Rtt(8), ValueType::Rtt(9));
// Rtts of subtypes are not related.
NOT_SUBTYPE(ValueType::Rtt(1, 1), ValueType::Rtt(0, 1));
NOT_SUBTYPE(ValueType::Rtt(1), ValueType::Rtt(0));
@@ -190,36 +203,18 @@
SUBTYPE(ValueType::Rtt(1, depth), ValueType::Rtt(1));
}
- // Function subtyping depends on the selected wasm features.
- // Without wasm-gc:
-
+ // Function subtyping;
// Unrelated function types are unrelated.
- NOT_SUBTYPE(ref(10), ref(11));
+ NOT_VALID_SUBTYPE(ref(10), ref(11));
// Function type with different parameter counts are unrelated.
- NOT_SUBTYPE(ref(12), ref(11));
- // Parameter contravariance does not hold.
- NOT_SUBTYPE(ref(14), ref(13));
- // Return type covariance does not hold.
- NOT_SUBTYPE(ref(15), ref(16));
- // Only identical types are subtype-related.
- SUBTYPE(ref(10), ref(10));
- SUBTYPE(ref(11), ref(11));
-
- {
- // With wasm-gc:
- EXPERIMENTAL_FLAG_SCOPE(gc);
- // Unrelated function types are unrelated.
- NOT_SUBTYPE(ref(10), ref(11));
- // Function type with different parameter counts are unrelated.
- NOT_SUBTYPE(ref(12), ref(11));
- // Parameter contravariance holds.
- SUBTYPE(ref(14), ref(13));
- // Return type covariance holds.
- SUBTYPE(ref(15), ref(16));
- // Identical types are subtype-related.
- SUBTYPE(ref(10), ref(10));
- SUBTYPE(ref(11), ref(11));
- }
+ NOT_VALID_SUBTYPE(ref(12), ref(11));
+ // Parameter contravariance holds.
+ VALID_SUBTYPE(ref(14), ref(13));
+ // Return type covariance holds.
+ VALID_SUBTYPE(ref(15), ref(16));
+ // Identical types are subtype-related.
+ VALID_SUBTYPE(ref(10), ref(10));
+ VALID_SUBTYPE(ref(11), ref(11));
}
#undef SUBTYPE
#undef NOT_SUBTYPE