Commit bc3b9332 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by V8 LUCI CQ

[wasm-gc] Implement nominal types

Per https://github.com/WebAssembly/gc/issues/234, this implements
"nominal" type definitions with explicit supertypes, and statically
typed RTT-less instructions for allocation and testing/casting.
This should be fully backwards compatible with existing Wasm modules.

Spec: https://bit.ly/3cWcm6Q ("version 4")

Bug: v8:7748
Change-Id: Id5a1399b368fdfad22036cfd66f1bef593e640f7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3144916
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarManos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76844}
parent 62acef54
This diff is collapsed.
......@@ -550,35 +550,40 @@ class ModuleDecoderImpl : public Decoder {
}
void DecodeTypeSection() {
uint32_t signatures_count = consume_count("types count", kV8MaxWasmTypes);
module_->types.reserve(signatures_count);
for (uint32_t i = 0; ok() && i < signatures_count; ++i) {
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 kWasmFunctionTypeCode:
case kWasmFunctionSubtypeCode: {
const FunctionSig* s = consume_sig(module_->signature_zone.get());
module_->add_signature(s);
break;
}
case kWasmFunctionExtendingTypeCode: {
if (!enabled_features_.has_gc()) {
errorf(pc(),
"invalid function type definition, enable with "
"--experimental-wasm-gc");
break;
}
const FunctionSig* s = consume_sig(module_->signature_zone.get());
module_->add_signature(s);
uint32_t super_index = consume_u32v("supertype");
if (!module_->has_signature(super_index)) {
errorf(pc(), "invalid function supertype index: %d", super_index);
break;
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;
}
case kWasmStructTypeCode: {
case kWasmStructTypeCode:
case kWasmStructSubtypeCode: {
if (!enabled_features_.has_gc()) {
errorf(pc(),
"invalid struct type definition, enable with "
......@@ -586,39 +591,26 @@ class ModuleDecoderImpl : public Decoder {
break;
}
const StructType* s = consume_struct(module_->signature_zone.get());
module_->add_struct_type(s);
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;
}
}
module_->add_struct_type(s, super_index);
// TODO(7748): Should we canonicalize struct types, like
// {signature_map} does for function signatures?
break;
}
case kWasmStructExtendingTypeCode: {
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());
module_->add_struct_type(s);
uint32_t super_index = consume_u32v("supertype");
if (!module_->has_struct(super_index)) {
errorf(pc(), "invalid struct supertype: %d", super_index);
break;
}
break;
}
case kWasmArrayTypeCode: {
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());
module_->add_array_type(type);
break;
}
case kWasmArrayExtendingTypeCode: {
case kWasmArrayTypeCode:
case kWasmArraySubtypeCode: {
if (!enabled_features_.has_gc()) {
errorf(pc(),
"invalid array type definition, enable with "
......@@ -626,12 +618,20 @@ class ModuleDecoderImpl : public Decoder {
break;
}
const ArrayType* type = consume_array(module_->signature_zone.get());
module_->add_array_type(type);
uint32_t super_index = consume_u32v("supertype");
if (!module_->has_array(super_index)) {
errorf(pc(), "invalid array supertype: %d", super_index);
break;
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;
}
}
module_->add_array_type(type, super_index);
break;
}
default:
......@@ -639,6 +639,46 @@ class ModuleDecoderImpl : public Decoder {
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);
continue;
}
if (depth == -1) {
errorf("type %d: cyclic inheritance", i);
continue;
}
switch (module_->type_kinds[i]) {
case kWasmStructTypeCode:
if (!module->has_struct(explicit_super)) break;
if (!StructIsSubtypeOf(i, explicit_super, module, module)) break;
continue;
case kWasmArrayTypeCode:
if (!module->has_array(explicit_super)) break;
if (!ArrayIsSubtypeOf(i, explicit_super, module, module)) break;
continue;
case kWasmFunctionTypeCode:
if (!module->has_signature(explicit_super)) break;
if (!FunctionIsSubtypeOf(i, explicit_super, module, module)) break;
continue;
default:
UNREACHABLE();
}
errorf("type %d has invalid explicit supertype %d", i, explicit_super);
}
module_->signature_map.Freeze();
}
......@@ -1787,6 +1827,15 @@ class ModuleDecoderImpl : public Decoder {
return result;
}
HeapType consume_super_type() {
uint32_t type_length;
HeapType result = value_type_reader::read_heap_type<kFullValidation>(
this, this->pc(), &type_length, module_.get(),
origin_ == kWasmOrigin ? enabled_features_ : WasmFeatures::None());
consume_bytes(type_length, "supertype");
return result;
}
ValueType consume_storage_type() {
uint8_t opcode = read_u8<kFullValidation>(this->pc());
switch (opcode) {
......
......@@ -194,6 +194,41 @@ Handle<Map> CreateArrayMap(Isolate* isolate, const WasmModule* module,
return map;
}
void CreateMapForType(Isolate* isolate, const WasmModule* module,
int type_index, Handle<WasmInstanceObject> instance,
Handle<FixedArray> maps) {
// Recursive calls for supertypes may already have created this map.
if (maps->get(type_index).IsMap()) return;
Handle<Map> rtt_parent;
// If the type with {type_index} has an explicit supertype, make sure the
// 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) {
// 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);
rtt_parent = handle(Map::cast(maps->get(supertype)), isolate);
}
Handle<Map> map;
switch (module->type_kinds[type_index]) {
case kWasmStructTypeCode:
map = CreateStructMap(isolate, module, type_index, rtt_parent, instance);
break;
case kWasmArrayTypeCode:
map = CreateArrayMap(isolate, module, type_index, rtt_parent, instance);
break;
case kWasmFunctionTypeCode:
// TODO(7748): Think about canonicalizing rtts to make them work for
// identical function types.
map = Map::Copy(isolate, isolate->wasm_exported_function_map(),
"fresh function map for function type canonical rtt "
"initialization");
break;
}
maps->set(type_index, *map);
}
namespace {
// TODO(7748): Consider storing this array in Maps'
......@@ -661,28 +696,8 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
if (enabled_.has_gc()) {
Handle<FixedArray> maps = isolate_->factory()->NewFixedArray(
static_cast<int>(module_->type_kinds.size()));
for (int map_index = 0;
map_index < static_cast<int>(module_->type_kinds.size());
map_index++) {
Handle<Map> map;
switch (module_->type_kinds[map_index]) {
case kWasmStructTypeCode:
map = CreateStructMap(isolate_, module_, map_index, Handle<Map>(),
instance);
break;
case kWasmArrayTypeCode:
map = CreateArrayMap(isolate_, module_, map_index, Handle<Map>(),
instance);
break;
case kWasmFunctionTypeCode:
// TODO(7748): Think about canonicalizing rtts to make them work for
// identical function types.
map = Map::Copy(isolate_, isolate_->wasm_exported_function_map(),
"fresh function map for function type canonical rtt "
"initialization");
break;
}
maps->set(map_index, *map);
for (uint32_t index = 0; index < module_->type_kinds.size(); index++) {
CreateMapForType(isolate_, module_, index, instance, maps);
}
instance->set_managed_object_maps(*maps);
}
......
......@@ -50,9 +50,9 @@ enum ValueTypeCode : uint8_t {
constexpr uint8_t kWasmFunctionTypeCode = 0x60;
constexpr uint8_t kWasmStructTypeCode = 0x5f;
constexpr uint8_t kWasmArrayTypeCode = 0x5e;
constexpr uint8_t kWasmFunctionExtendingTypeCode = 0x5d;
constexpr uint8_t kWasmStructExtendingTypeCode = 0x5c;
constexpr uint8_t kWasmArrayExtendingTypeCode = 0x5b;
constexpr uint8_t kWasmFunctionSubtypeCode = 0x5d;
constexpr uint8_t kWasmStructSubtypeCode = 0x5c;
constexpr uint8_t kWasmArraySubtypeCode = 0x5b;
// Binary encoding of import/export kinds.
enum ImportExportKindCode : uint8_t {
......
......@@ -39,7 +39,9 @@ ValueType WasmInitExpr::type(const WasmModule* module,
case kRefNullConst:
return ValueType::Ref(immediate().heap_type, kNullable);
case kStructNewWithRtt:
case kStructNew:
case kArrayInit:
case kArrayInitStatic:
return ValueType::Ref(immediate().index, kNonNullable);
case kRttCanon:
return ValueType::Rtt(immediate().heap_type, 0);
......
......@@ -34,7 +34,9 @@ class WasmInitExpr {
kRefNullConst,
kRefFuncConst,
kStructNewWithRtt,
kStructNew,
kArrayInit,
kArrayInitStatic,
kRttCanon,
kRttSub,
kRttFreshSub,
......@@ -99,6 +101,15 @@ class WasmInitExpr {
return expr;
}
static WasmInitExpr StructNew(uint32_t index,
std::vector<WasmInitExpr> elements) {
WasmInitExpr expr;
expr.kind_ = kStructNew;
expr.immediate_.index = index;
expr.operands_ = std::move(elements);
return expr;
}
static WasmInitExpr ArrayInit(uint32_t index,
std::vector<WasmInitExpr> elements) {
WasmInitExpr expr;
......@@ -108,6 +119,15 @@ class WasmInitExpr {
return expr;
}
static WasmInitExpr ArrayInitStatic(uint32_t index,
std::vector<WasmInitExpr> elements) {
WasmInitExpr expr;
expr.kind_ = kArrayInitStatic;
expr.immediate_.index = index;
expr.operands_ = std::move(elements);
return expr;
}
static WasmInitExpr RttCanon(uint32_t index) {
WasmInitExpr expr;
expr.kind_ = kRttCanon;
......@@ -157,6 +177,7 @@ class WasmInitExpr {
case kRefNullConst:
return immediate().heap_type == other.immediate().heap_type;
case kStructNewWithRtt:
case kStructNew:
if (immediate().index != other.immediate().index) return false;
DCHECK_EQ(operands().size(), other.operands().size());
for (uint32_t i = 0; i < operands().size(); i++) {
......@@ -164,6 +185,7 @@ class WasmInitExpr {
}
return true;
case kArrayInit:
case kArrayInitStatic:
if (immediate().index != other.immediate().index) return false;
if (operands().size() != other.operands().size()) return false;
for (uint32_t i = 0; i < operands().size(); i++) {
......
......@@ -290,12 +290,12 @@ void WasmModuleBuilder::AddDataSegment(const byte* data, uint32_t size,
}
}
uint32_t WasmModuleBuilder::AddSignature(FunctionSig* sig) {
uint32_t WasmModuleBuilder::AddSignature(FunctionSig* sig, uint32_t supertype) {
auto sig_entry = signature_map_.find(*sig);
if (sig_entry != signature_map_.end()) return sig_entry->second;
uint32_t index = static_cast<uint32_t>(types_.size());
signature_map_.emplace(*sig, index);
types_.push_back(Type(sig));
types_.push_back(Type(sig, supertype));
return index;
}
......@@ -307,15 +307,16 @@ uint32_t WasmModuleBuilder::AddException(FunctionSig* type) {
return except_index;
}
uint32_t WasmModuleBuilder::AddStructType(StructType* type) {
uint32_t WasmModuleBuilder::AddStructType(StructType* type,
uint32_t supertype) {
uint32_t index = static_cast<uint32_t>(types_.size());
types_.push_back(Type(type));
types_.push_back(Type(type, supertype));
return index;
}
uint32_t WasmModuleBuilder::AddArrayType(ArrayType* type) {
uint32_t WasmModuleBuilder::AddArrayType(ArrayType* type, uint32_t supertype) {
uint32_t index = static_cast<uint32_t>(types_.size());
types_.push_back(Type(type));
types_.push_back(Type(type, supertype));
return index;
}
......@@ -509,22 +510,30 @@ void WriteInitializerExpressionWithEnd(ZoneBuffer* buffer,
}
break;
}
case WasmInitExpr::kStructNew:
case WasmInitExpr::kStructNewWithRtt:
STATIC_ASSERT((kExprStructNew >> 8) == kGCPrefix);
STATIC_ASSERT((kExprStructNewWithRtt >> 8) == kGCPrefix);
for (const WasmInitExpr& operand : init.operands()) {
WriteInitializerExpressionWithEnd(buffer, operand, kWasmBottom);
}
buffer->write_u8(kGCPrefix);
buffer->write_u8(static_cast<uint8_t>(kExprStructNewWithRtt));
buffer->write_u8(static_cast<uint8_t>(
init.kind() == WasmInitExpr::kStructNew ? kExprStructNew
: kExprStructNewWithRtt));
buffer->write_u32v(init.immediate().index);
break;
case WasmInitExpr::kArrayInit:
case WasmInitExpr::kArrayInitStatic:
STATIC_ASSERT((kExprArrayInit >> 8) == kGCPrefix);
STATIC_ASSERT((kExprArrayInitStatic >> 8) == kGCPrefix);
for (const WasmInitExpr& operand : init.operands()) {
WriteInitializerExpressionWithEnd(buffer, operand, kWasmBottom);
}
buffer->write_u8(kGCPrefix);
buffer->write_u8(static_cast<uint8_t>(kExprArrayInit));
buffer->write_u8(static_cast<uint8_t>(
init.kind() == WasmInitExpr::kArrayInit ? kExprArrayInit
: kExprArrayInitStatic));
buffer->write_u32v(init.immediate().index);
buffer->write_u32v(static_cast<uint32_t>(init.operands().size() - 1));
break;
......@@ -568,10 +577,12 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
buffer->write_size(types_.size());
for (const Type& type : types_) {
bool has_super = type.supertype != kNoSuperType;
switch (type.kind) {
case Type::kFunctionSig: {
FunctionSig* sig = type.sig;
buffer->write_u8(kWasmFunctionTypeCode);
buffer->write_u8(has_super ? kWasmFunctionSubtypeCode
: kWasmFunctionTypeCode);
buffer->write_size(sig->parameter_count());
for (auto param : sig->parameters()) {
WriteValueType(buffer, param);
......@@ -580,23 +591,40 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
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 Type::kStructType: {
StructType* struct_type = type.struct_type;
buffer->write_u8(kWasmStructTypeCode);
buffer->write_u8(has_super ? kWasmStructSubtypeCode
: 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 Type::kArrayType: {
ArrayType* array_type = type.array_type;
buffer->write_u8(kWasmArrayTypeCode);
buffer->write_u8(has_super ? kWasmArraySubtypeCode
: 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;
}
}
......
......@@ -331,10 +331,10 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
// size, or the maximum uint32_t value if the maximum table size has been
// exceeded.
uint32_t IncreaseTableMinSize(uint32_t table_index, uint32_t count);
uint32_t AddSignature(FunctionSig* sig);
uint32_t AddSignature(FunctionSig* sig, uint32_t supertype = kNoSuperType);
uint32_t AddException(FunctionSig* type);
uint32_t AddStructType(StructType* type);
uint32_t AddArrayType(ArrayType* type);
uint32_t AddStructType(StructType* type, uint32_t supertype = kNoSuperType);
uint32_t AddArrayType(ArrayType* type, uint32_t supertype = kNoSuperType);
uint32_t AddTable(ValueType type, uint32_t min_size);
uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size);
uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size,
......@@ -399,13 +399,14 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
private:
struct Type {
enum Kind { kFunctionSig, kStructType, kArrayType };
explicit Type(FunctionSig* signature)
: kind(kFunctionSig), sig(signature) {}
explicit Type(StructType* struct_type)
: kind(kStructType), struct_type(struct_type) {}
explicit Type(ArrayType* array_type)
: kind(kArrayType), array_type(array_type) {}
explicit Type(FunctionSig* signature, uint32_t supertype)
: kind(kFunctionSig), supertype(supertype), sig(signature) {}
explicit Type(StructType* struct_type, uint32_t supertype)
: kind(kStructType), supertype(supertype), struct_type(struct_type) {}
explicit Type(ArrayType* array_type, uint32_t supertype)
: kind(kArrayType), supertype(supertype), array_type(array_type) {}
Kind kind;
uint32_t supertype;
union {
FunctionSig* sig;
StructType* struct_type;
......
......@@ -113,6 +113,23 @@ int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset) {
return func_index;
}
// TODO(7748): Measure whether this iterative implementation is fast enough.
// We could cache the result on the module, in yet another vector indexed by
// type index.
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) {
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;
}
return depth;
}
void LazilyGeneratedNames::AddForTesting(int function_index,
WireBytesRef name) {
base::MutexGuard lock(&mutex_);
......
......@@ -259,6 +259,11 @@ struct V8_EXPORT_PRIVATE WasmDebugSymbols {
struct WasmTable;
// End of a chain of explicit supertypes.
constexpr uint32_t kGenericSuperType = 0xFFFFFFFE;
// Used for types that have no explicit supertype.
constexpr uint32_t kNoSuperType = 0xFFFFFFFF;
// Static representation of a module.
struct V8_EXPORT_PRIVATE WasmModule {
std::unique_ptr<Zone> signature_zone;
......@@ -288,6 +293,7 @@ struct V8_EXPORT_PRIVATE WasmModule {
WireBytesRef name = {0, 0};
std::vector<TypeDefinition> types; // by type index
std::vector<uint8_t> type_kinds; // by type index
std::vector<uint32_t> supertypes; // by type index
// Map from each type index to the index of its corresponding canonical type.
// Note: right now, only functions are canonicalized, and arrays and structs
// map to themselves.
......@@ -295,9 +301,10 @@ struct V8_EXPORT_PRIVATE WasmModule {
bool has_type(uint32_t index) const { return index < types.size(); }
void add_signature(const FunctionSig* sig) {
void add_signature(const FunctionSig* sig, uint32_t supertype) {
types.push_back(TypeDefinition(sig));
type_kinds.push_back(kWasmFunctionTypeCode);
supertypes.push_back(supertype);
uint32_t canonical_id = sig ? signature_map.FindOrInsert(*sig) : 0;
canonicalized_type_ids.push_back(canonical_id);
}
......@@ -309,9 +316,10 @@ struct V8_EXPORT_PRIVATE WasmModule {
return types[index].function_sig;
}
void add_struct_type(const StructType* type) {
void add_struct_type(const StructType* type, uint32_t supertype) {
types.push_back(TypeDefinition(type));
type_kinds.push_back(kWasmStructTypeCode);
supertypes.push_back(supertype);
// No canonicalization for structs.
canonicalized_type_ids.push_back(0);
}
......@@ -323,9 +331,10 @@ struct V8_EXPORT_PRIVATE WasmModule {
return types[index].struct_type;
}
void add_array_type(const ArrayType* type) {
void add_array_type(const ArrayType* type, uint32_t supertype) {
types.push_back(TypeDefinition(type));
type_kinds.push_back(kWasmArrayTypeCode);
supertypes.push_back(supertype);
// No canonicalization for arrays.
canonicalized_type_ids.push_back(0);
}
......@@ -337,6 +346,14 @@ struct V8_EXPORT_PRIVATE WasmModule {
return types[index].array_type;
}
uint32_t supertype(uint32_t index) const {
DCHECK(index < supertypes.size());
return supertypes[index];
}
bool has_supertype(uint32_t index) const {
return supertype(index) != kNoSuperType;
}
std::vector<WasmFunction> functions;
std::vector<WasmDataSegment> data_segments;
std::vector<WasmTable> tables;
......@@ -418,6 +435,12 @@ int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset);
// contained within a function.
int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset);
// Gets the explicitly defined subtyping depth for the given type.
// Returns 0 if the type has no explicit supertype.
// The result is capped to {kV8MaxRttSubtypingDepth + 1}.
// Invalid cyclic hierarchies will return -1.
int GetSubtypingDepth(const WasmModule* module, uint32_t type_index);
// Interface to the storage (wire bytes) of a wasm module.
// It is illegal for anyone receiving a ModuleWireBytes to store pointers based
// on module_bytes, as this storage is only guaranteed to be alive as long as
......
......@@ -382,12 +382,16 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
// GC operations.
CASE_OP(StructNewWithRtt, "struct.new_with_rtt")
CASE_OP(StructNewDefaultWithRtt, "struct.new_default_with_rtt")
CASE_OP(StructNew, "struct.new")
CASE_OP(StructNewDefault, "struct.new_default")
CASE_OP(StructGet, "struct.get")
CASE_OP(StructGetS, "struct.get_s")
CASE_OP(StructGetU, "struct.get_u")
CASE_OP(StructSet, "struct.set")
CASE_OP(ArrayNewWithRtt, "array.new_with_rtt")
CASE_OP(ArrayNewDefaultWithRtt, "array.new_default_with_rtt")
CASE_OP(ArrayNew, "array.new")
CASE_OP(ArrayNewDefault, "array.new_default")
CASE_OP(ArrayGet, "array.get")
CASE_OP(ArrayGetS, "array.get_s")
......@@ -396,6 +400,7 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(ArrayLen, "array.len")
CASE_OP(ArrayCopy, "array.copy")
CASE_OP(ArrayInit, "array.init")
CASE_OP(ArrayInitStatic, "array.init_static")
CASE_OP(I31New, "i31.new")
CASE_OP(I31GetS, "i31.get_s")
CASE_OP(I31GetU, "i31.get_u")
......@@ -403,9 +408,13 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
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")
CASE_OP(RefCastStatic, "ref.cast_static")
CASE_OP(BrOnCast, "br_on_cast")
CASE_OP(BrOnCastStatic, "br_on_cast_static")
CASE_OP(BrOnCastFail, "br_on_cast_fail")
CASE_OP(BrOnCastStaticFail, "br_on_cast_static_fail")
CASE_OP(RefIsFunc, "ref.is_func")
CASE_OP(RefIsData, "ref.is_data")
CASE_OP(RefIsI31, "ref.is_i31")
......
......@@ -650,13 +650,15 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
#define FOREACH_GC_OPCODE(V) \
V(StructNewWithRtt, 0xfb01, _) \
V(StructNewDefault, 0xfb02, _) \
V(StructNewDefaultWithRtt, 0xfb02, _) \
V(StructGet, 0xfb03, _) \
V(StructGetS, 0xfb04, _) \
V(StructGetU, 0xfb05, _) \
V(StructSet, 0xfb06, _) \
V(StructNew, 0xfb07, _) \
V(StructNewDefault, 0xfb08, _) \
V(ArrayNewWithRtt, 0xfb11, _) \
V(ArrayNewDefault, 0xfb12, _) \
V(ArrayNewDefaultWithRtt, 0xfb12, _) \
V(ArrayGet, 0xfb13, _) \
V(ArrayGetS, 0xfb14, _) \
V(ArrayGetU, 0xfb15, _) \
......@@ -664,6 +666,9 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(ArrayLen, 0xfb17, _) \
V(ArrayCopy, 0xfb18, _) /* not standardized - V8 experimental */ \
V(ArrayInit, 0xfb19, _) /* not standardized - V8 experimental */ \
V(ArrayInitStatic, 0xfb1a, _) \
V(ArrayNew, 0xfb1b, _) \
V(ArrayNewDefault, 0xfb1c, _) \
V(I31New, 0xfb20, _) \
V(I31GetS, 0xfb21, _) \
V(I31GetU, 0xfb22, _) \
......@@ -674,6 +679,10 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(RefCast, 0xfb41, _) \
V(BrOnCast, 0xfb42, _) \
V(BrOnCastFail, 0xfb43, _) \
V(RefTestStatic, 0xfb44, _) \
V(RefCastStatic, 0xfb45, _) \
V(BrOnCastStatic, 0xfb46, _) \
V(BrOnCastStaticFail, 0xfb47, _) \
V(RefIsFunc, 0xfb50, _) \
V(RefIsData, 0xfb51, _) \
V(RefIsI31, 0xfb52, _) \
......
......@@ -223,6 +223,8 @@ V8_INLINE bool EquivalentIndices(uint32_t index1, uint32_t index2,
}
}
} // namespace
bool StructIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
const WasmModule* sub_module,
const WasmModule* super_module) {
......@@ -234,8 +236,10 @@ bool StructIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
return false;
}
TypeJudgementCache::instance()->cache_subtype(subtype_index, supertype_index,
sub_module, super_module);
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);
......@@ -261,8 +265,10 @@ bool ArrayIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
super_module->types[supertype_index].array_type;
bool sub_mut = sub_array->mutability();
bool super_mut = super_array->mutability();
TypeJudgementCache::instance()->cache_subtype(subtype_index, supertype_index,
sub_module, super_module);
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(),
......@@ -294,8 +300,10 @@ bool FunctionIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
return false;
}
TypeJudgementCache::instance()->cache_subtype(subtype_index, supertype_index,
sub_module, super_module);
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.
......@@ -318,7 +326,6 @@ bool FunctionIsSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
return true;
}
} // namespace
V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
ValueType subtype, ValueType supertype, const WasmModule* sub_module,
......@@ -410,11 +417,35 @@ V8_NOINLINE V8_EXPORT_PRIVATE bool IsSubtypeOfImpl(
DCHECK(super_heap.is_index());
uint32_t super_index = super_heap.ref_index();
DCHECK(super_module->has_type(super_index));
// The {IsSubtypeOf} entry point already has a fast path checking ValueType
// equality; here we catch (ref $x) being a subtype of (ref null $x).
if (sub_module == super_module && sub_index == super_index) return true;
uint8_t sub_kind = sub_module->type_kinds[sub_index];
if (sub_kind != super_module->type_kinds[super_index]) 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(
......
......@@ -97,6 +97,20 @@ V8_INLINE bool IsHeapSubtypeOf(uint32_t subtype_index, uint32_t supertype_index,
// 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);
} // namespace wasm
} // namespace internal
} // namespace v8
......
This diff is collapsed.
......@@ -132,7 +132,7 @@ class TestingModuleBuilder {
byte AddSignature(const FunctionSig* sig) {
DCHECK_EQ(test_module_->types.size(),
test_module_->canonicalized_type_ids.size());
test_module_->add_signature(sig);
test_module_->add_signature(sig, kNoSuperType);
size_t size = test_module_->types.size();
CHECK_GT(127, size);
return static_cast<byte>(size - 1);
......
......@@ -491,10 +491,14 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
// Heap-allocated object operations.
//------------------------------------------------------------------------------
#define WASM_GC_OP(op) kGCPrefix, static_cast<byte>(op)
#define WASM_STRUCT_NEW(index, ...) \
__VA_ARGS__, WASM_GC_OP(kExprStructNew), static_cast<byte>(index)
#define WASM_STRUCT_NEW_WITH_RTT(index, ...) \
__VA_ARGS__, WASM_GC_OP(kExprStructNewWithRtt), static_cast<byte>(index)
#define WASM_STRUCT_NEW_DEFAULT(index, rtt) \
rtt, WASM_GC_OP(kExprStructNewDefault), static_cast<byte>(index)
#define WASM_STRUCT_NEW_DEFAULT(index) \
WASM_GC_OP(kExprStructNewDefault), static_cast<byte>(index)
#define WASM_STRUCT_NEW_DEFAULT_WITH_RTT(index, rtt) \
rtt, WASM_GC_OP(kExprStructNewDefaultWithRtt), static_cast<byte>(index)
#define WASM_STRUCT_GET(typeidx, fieldidx, struct_obj) \
struct_obj, WASM_GC_OP(kExprStructGet), static_cast<byte>(typeidx), \
static_cast<byte>(fieldidx)
......@@ -513,13 +517,23 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_REF_AS_NON_NULL(val) val, kExprRefAsNonNull
#define WASM_REF_EQ(lhs, rhs) lhs, rhs, kExprRefEq
#define WASM_REF_TEST(ref, rtt) ref, rtt, WASM_GC_OP(kExprRefTest)
#define WASM_REF_TEST_STATIC(ref, typeidx) \
ref, WASM_GC_OP(kExprRefTestStatic), static_cast<byte>(typeidx)
#define WASM_REF_CAST(ref, rtt) ref, rtt, WASM_GC_OP(kExprRefCast)
#define WASM_REF_CAST_STATIC(ref, typeidx) \
ref, WASM_GC_OP(kExprRefCastStatic), static_cast<byte>(typeidx)
// Takes a reference value from the value stack to allow sequences of
// conditional branches.
#define WASM_BR_ON_CAST(depth, rtt) \
rtt, WASM_GC_OP(kExprBrOnCast), static_cast<byte>(depth)
#define WASM_BR_ON_CAST_STATIC(depth, typeidx) \
WASM_GC_OP(kExprBrOnCastStatic), static_cast<byte>(depth), \
static_cast<byte>(typeidx)
#define WASM_BR_ON_CAST_FAIL(depth, rtt) \
rtt, WASM_GC_OP(kExprBrOnCastFail), static_cast<byte>(depth)
#define WASM_BR_ON_CAST_STATIC_FAIL(depth, typeidx) \
WASM_GC_OP(kExprBrOnCastStaticFail), static_cast<byte>(depth), \
static_cast<byte>(typeidx)
#define WASM_REF_IS_FUNC(ref) ref, WASM_GC_OP(kExprRefIsFunc)
#define WASM_REF_IS_DATA(ref) ref, WASM_GC_OP(kExprRefIsData)
......@@ -539,11 +553,15 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_BR_ON_NON_I31(depth) \
WASM_GC_OP(kExprBrOnNonI31), static_cast<byte>(depth)
#define WASM_ARRAY_NEW(index, default_value, length) \
default_value, length, WASM_GC_OP(kExprArrayNew), static_cast<byte>(index)
#define WASM_ARRAY_NEW_WITH_RTT(index, default_value, length, rtt) \
default_value, length, rtt, WASM_GC_OP(kExprArrayNewWithRtt), \
static_cast<byte>(index)
#define WASM_ARRAY_NEW_DEFAULT(index, length, rtt) \
length, rtt, WASM_GC_OP(kExprArrayNewDefault), static_cast<byte>(index)
#define WASM_ARRAY_NEW_DEFAULT(index, length) \
length, WASM_GC_OP(kExprArrayNewDefault), static_cast<byte>(index)
#define WASM_ARRAY_NEW_DEFAULT_WITH_RTT(index, length, rtt) \
length, rtt, WASM_GC_OP(kExprArrayNewDefaultWithRtt), static_cast<byte>(index)
#define WASM_ARRAY_GET(typeidx, array, index) \
array, index, WASM_GC_OP(kExprArrayGet), static_cast<byte>(typeidx)
#define WASM_ARRAY_GET_U(typeidx, array, index) \
......@@ -562,6 +580,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_ARRAY_INIT(index, length, ...) \
__VA_ARGS__, WASM_GC_OP(kExprArrayInit), static_cast<byte>(index), \
static_cast<byte>(length)
#define WASM_ARRAY_INIT_STATIC(index, length, ...) \
__VA_ARGS__, WASM_GC_OP(kExprArrayInitStatic), static_cast<byte>(index), \
static_cast<byte>(length)
#define WASM_RTT_WITH_DEPTH(depth, typeidx) \
kRttWithDepthCode, U32V_1(depth), U32V_1(typeidx)
......
......@@ -810,7 +810,7 @@ class WasmGenerator {
if (new_default) {
builder_->EmitWithPrefix(kExprRttCanon);
builder_->EmitU32V(index);
builder_->EmitWithPrefix(kExprStructNewDefault);
builder_->EmitWithPrefix(kExprStructNewDefaultWithRtt);
builder_->EmitU32V(index);
} else {
StructType* struct_gen = builder_->builder()->GetStructType(index);
......@@ -828,7 +828,7 @@ class WasmGenerator {
Generate(kWasmI32, data);
builder_->EmitWithPrefix(kExprRttCanon);
builder_->EmitU32V(index);
builder_->EmitWithPrefix(kExprArrayNewDefault);
builder_->EmitWithPrefix(kExprArrayNewDefaultWithRtt);
builder_->EmitU32V(index);
} else {
Generate(builder_->builder()->GetArrayType(index)->element_type(),
......
......@@ -14,7 +14,7 @@ builder.addFunction("main", kSig_i_i)
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprRttCanon, array_index,
kGCPrefix, kExprArrayNewDefault, array_index,
kGCPrefix, kExprArrayNewDefaultWithRtt, array_index,
kGCPrefix, kExprArrayLen, array_index,
])
.exportFunc();
......
......@@ -32,11 +32,11 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
.addBody([
...wasmI32Const(array_length),
kGCPrefix, kExprRttCanon, array_index,
kGCPrefix, kExprArrayNewDefault, array_index,
kGCPrefix, kExprArrayNewDefaultWithRtt, array_index,
kExprGlobalSet, from.index,
...wasmI32Const(array_length),
kGCPrefix, kExprRttCanon, array_index,
kGCPrefix, kExprArrayNewDefault, array_index,
kGCPrefix, kExprArrayNewDefaultWithRtt, array_index,
kExprGlobalSet, to.index
])
.exportFunc();
......
......@@ -6,26 +6,49 @@
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
var builder = new WasmModuleBuilder();
let struct1 = builder.addStruct([makeField(kWasmI32, true)]);
let struct2 = builder.addStructExtending(
[makeField(kWasmI32, true), makeField(kWasmI32, true)], struct1);
(function() {
var builder = new WasmModuleBuilder();
let struct1 = builder.addStructSubtype([makeField(kWasmI32, true)]);
let struct2 = builder.addStructSubtype(
[makeField(kWasmI32, true), makeField(kWasmI32, true)], struct1);
let array1 = builder.addArray(kWasmI32, true);
let array2 = builder.addArrayExtending(kWasmI32, true, array1);
let array1 = builder.addArraySubtype(kWasmI32, true);
let array2 = builder.addArraySubtype(kWasmI32, true, array1);
builder.addFunction("main", kSig_v_v)
.addLocals(wasmOptRefType(struct1), 1)
.addLocals(wasmOptRefType(array1), 1)
.addBody([
kGCPrefix, kExprRttCanon, struct2,
kGCPrefix, kExprStructNewDefault, struct2,
builder.addFunction("main", kSig_v_v)
.addLocals(wasmOptRefType(struct1), 1)
.addLocals(wasmOptRefType(array1), 1)
.addBody([
// Check that we can create a struct with explicit RTT...
kGCPrefix, kExprRttCanon, struct2, kGCPrefix,
kExprStructNewDefaultWithRtt, struct2,
// ...and upcast it.
kExprLocalSet, 0,
// Check that we can create a struct with implicit RTT.
kGCPrefix, kExprStructNewDefault, struct2, kExprLocalSet, 0,
// Check that we can create an array with explicit RTT...
kExprI32Const, 10, // length
kGCPrefix, kExprRttCanon, array2,
kGCPrefix, kExprArrayNewDefault, array2,
kExprLocalSet, 1
]);
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
])
.exportFunc();
// This test is only interested in type checking.
builder.instantiate();
})();
// This test is only interested in type checking.
builder.instantiate();
(function () {
let builder = new WasmModuleBuilder();
let t0 = builder.addStructSubtype([]);
for (let i = 0; i < 32; i++) {
builder.addStructSubtype([], i);
}
assertThrows(
() => builder.instantiate(), WebAssembly.CompileError,
/subtyping depth is greater than allowed/);
})();
......@@ -182,7 +182,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
.addBody([
kExprI32Const, 5,
kGCPrefix, kExprRttCanon, array,
kGCPrefix, kExprArrayNewDefault, array,
kGCPrefix, kExprArrayNewDefaultWithRtt, array,
kExprLocalSet, 1,
kExprLocalGet, 1, // a[i] = i for i = {0..4}
......@@ -308,7 +308,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
builder.addFunction("main", kSig_i_i)
.addBody([
kExprI32Const, 10, kGCPrefix, kExprRttCanon, array,
kGCPrefix, kExprArrayNewDefault, array,
kGCPrefix, kExprArrayNewDefaultWithRtt, array,
kExprI32Const, 7,
kExprCallFunction, tester.index,
])
......
......@@ -18,15 +18,15 @@ let instance = (() => {
builder.addFunction('struct_producer', makeSig([], [kWasmDataRef]))
.addBody([
kGCPrefix, kExprRttCanon, struct, kGCPrefix, kExprStructNewDefault,
struct
kGCPrefix, kExprRttCanon, struct, kGCPrefix,
kExprStructNewDefaultWithRtt, struct
])
.exportFunc();
builder.addFunction('array_producer', makeSig([], [kWasmDataRef]))
.addBody([
kExprI32Const, 10, kGCPrefix, kExprRttCanon, array, kGCPrefix,
kExprArrayNewDefault, array
kExprArrayNewDefaultWithRtt, array
])
.exportFunc();
......
......@@ -77,9 +77,9 @@ let kLocalNamesCode = 2;
let kWasmFunctionTypeForm = 0x60;
let kWasmStructTypeForm = 0x5f;
let kWasmArrayTypeForm = 0x5e;
let kWasmFunctionExtendingTypeForm = 0x5d;
let kWasmStructExtendingTypeForm = 0x5c;
let kWasmArrayExtendingTypeForm = 0x5b;
let kWasmFunctionSubtypeForm = 0x5d;
let kWasmStructSubtypeForm = 0x5c;
let kWasmArraySubtypeForm = 0x5b;
let kLimitsNoMaximum = 0x00;
let kLimitsWithMaximum = 0x01;
......@@ -469,13 +469,15 @@ for (let prefix in kPrefixOpcodes) {
// GC opcodes
let kExprStructNewWithRtt = 0x01;
let kExprStructNewDefault = 0x02;
let kExprStructNewDefaultWithRtt = 0x02;
let kExprStructGet = 0x03;
let kExprStructGetS = 0x04;
let kExprStructGetU = 0x05;
let kExprStructSet = 0x06;
let kExprStructNew = 0x07;
let kExprStructNewDefault = 0x08;
let kExprArrayNewWithRtt = 0x11;
let kExprArrayNewDefault = 0x12;
let kExprArrayNewDefaultWithRtt = 0x12;
let kExprArrayGet = 0x13;
let kExprArrayGetS = 0x14;
let kExprArrayGetU = 0x15;
......@@ -483,6 +485,9 @@ let kExprArraySet = 0x16;
let kExprArrayLen = 0x17;
let kExprArrayCopy = 0x18;
let kExprArrayInit = 0x19;
let kExprArrayInitStatic = 0x1a;
let kExprArrayNew = 0x1b;
let kExprArrayNewDefault = 0x1c;
let kExprI31New = 0x20;
let kExprI31GetS = 0x21;
let kExprI31GetU = 0x22;
......@@ -493,6 +498,10 @@ let kExprRefTest = 0x40;
let kExprRefCast = 0x41;
let kExprBrOnCast = 0x42;
let kExprBrOnCastFail = 0x43;
let kExprRefTestStatic = 0x44;
let kExprRefCastStatic = 0x45;
let kExprBrOnCastStatic = 0x46;
let kExprBrOnCastStaticFail = 0x47;
let kExprRefIsFunc = 0x50;
let kExprRefIsData = 0x51;
let kExprRefIsI31 = 0x52;
......@@ -1004,20 +1013,22 @@ class Binary {
this.emit_u8(kExprRefNull);
this.emit_heap_type(expr.value);
break;
case kExprStructNew:
case kExprStructNewWithRtt:
for (let operand of expr.operands) {
this.emit_init_expr_recursive(operand);
}
this.emit_u8(kGCPrefix);
this.emit_u8(kExprStructNewWithRtt);
this.emit_u8(expr.kind);
this.emit_u32v(expr.value);
break;
case kExprArrayInit:
case kExprArrayInitStatic:
for (let operand of expr.operands) {
this.emit_init_expr_recursive(operand);
}
this.emit_u8(kGCPrefix);
this.emit_u8(kExprArrayInit);
this.emit_u8(expr.kind);
this.emit_u32v(expr.value);
this.emit_u32v(expr.operands.length - 1);
break;
......@@ -1170,9 +1181,15 @@ class WasmInitExpr {
static StructNewWithRtt(type, args) {
return {kind: kExprStructNewWithRtt, value: type, operands: args};
}
static StructNew(type, args) {
return {kind: kExprStructNew, value: type, operands: args};
}
static ArrayInit(type, args) {
return {kind: kExprArrayInit, value: type, operands: args};
}
static ArrayInitStatic(type, args) {
return {kind: kExprArrayInitStatic, value: type, operands: args};
}
static RttCanon(type) {
return {kind: kExprRttCanon, value: type};
}
......@@ -1256,11 +1273,11 @@ class WasmStruct {
}
}
class WasmStructExtending extends WasmStruct {
class WasmStructSubtype extends WasmStruct {
constructor(fields, supertype_idx) {
super(fields);
this.supertype = supertype_idx;
this.type_form = kWasmStructExtendingTypeForm;
this.type_form = kWasmStructSubtypeForm;
}
}
......@@ -1273,11 +1290,11 @@ class WasmArray {
}
}
class WasmArrayExtending extends WasmArray {
class WasmArraySubtype extends WasmArray {
constructor(type, mutability, supertype_idx) {
super(type, mutability);
this.supertype = supertype_idx;
this.type_form = kWasmArrayExtendingTypeForm;
this.type_form = kWasmArraySubtypeForm;
}
}
class WasmElemSegment {
......@@ -1402,8 +1419,9 @@ class WasmModuleBuilder {
return this.types.length - 1;
}
addStructExtending(fields, supertype_idx) {
this.types.push(new WasmStructExtending(fields, supertype_idx));
kGenericSuperType = 0xFFFFFFFE;
addStructSubtype(fields, supertype_idx = this.kGenericSuperType) {
this.types.push(new WasmStructSubtype(fields, supertype_idx));
return this.types.length - 1;
}
......@@ -1412,8 +1430,8 @@ class WasmModuleBuilder {
return this.types.length - 1;
}
addArrayExtending(type, mutability, supertype_idx) {
this.types.push(new WasmArrayExtending(type, mutability, supertype_idx));
addArraySubtype(type, mutability, supertype_idx = this.kGenericSuperType) {
this.types.push(new WasmArraySubtype(type, mutability, supertype_idx));
return this.types.length - 1;
}
......@@ -1651,15 +1669,23 @@ class WasmModuleBuilder {
section.emit_type(field.type);
section.emit_u8(field.mutability ? 1 : 0);
}
if (type instanceof WasmStructExtending) {
section.emit_u32v(type.supertype);
if (type instanceof WasmStructSubtype) {
if (type.supertype === this.kGenericSuperType) {
section.emit_u8(kDataRefCode);
} else {
section.emit_heap_type(type.supertype);
}
}
} else if (type instanceof WasmArray) {
section.emit_u8(type.type_form);
section.emit_type(type.type);
section.emit_u8(type.mutability ? 1 : 0);
if (type instanceof WasmArrayExtending) {
section.emit_u32v(type.supertype);
if (type instanceof WasmArraySubtype) {
if (type.supertype === this.kGenericSuperType) {
section.emit_u8(kDataRefCode);
} else {
section.emit_heap_type(type.supertype);
}
}
} else {
section.emit_u8(kWasmFunctionTypeForm);
......
......@@ -87,7 +87,7 @@ class TestModuleBuilder {
return static_cast<byte>(mod.globals.size() - 1);
}
byte AddSignature(const FunctionSig* sig) {
mod.add_signature(sig);
mod.add_signature(sig, kNoSuperType);
CHECK_LE(mod.types.size(), kMaxByteSizedLeb128);
return static_cast<byte>(mod.types.size() - 1);
}
......@@ -127,19 +127,20 @@ class TestModuleBuilder {
return static_cast<byte>(mod.tables.size() - 1);
}
byte AddStruct(std::initializer_list<F> fields) {
byte AddStruct(std::initializer_list<F> fields,
uint32_t supertype = kNoSuperType) {
StructType::Builder type_builder(mod.signature_zone.get(),
static_cast<uint32_t>(fields.size()));
for (F field : fields) {
type_builder.AddField(field.first, field.second);
}
mod.add_struct_type(type_builder.Build());
mod.add_struct_type(type_builder.Build(), supertype);
return static_cast<byte>(mod.type_kinds.size() - 1);
}
byte AddArray(ValueType type, bool mutability) {
ArrayType* array = mod.signature_zone->New<ArrayType>(type, mutability);
mod.add_array_type(array);
mod.add_array_type(array, kNoSuperType);
return static_cast<byte>(mod.type_kinds.size() - 1);
}
......@@ -1123,11 +1124,12 @@ TEST_F(FunctionBodyDecoderTest, UnreachableRefTypes) {
WASM_GC_OP(kExprStructNewWithRtt), struct_index,
kExprCallFunction, struct_consumer});
ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_GC_OP(kExprStructNewDefault),
{WASM_UNREACHABLE, WASM_GC_OP(kExprStructNewDefaultWithRtt),
struct_index, kExprDrop});
ExpectValidates(sigs.v_v(), {WASM_UNREACHABLE, WASM_RTT_CANON(struct_index),
WASM_GC_OP(kExprStructNewDefault), struct_index,
kExprCallFunction, struct_consumer});
ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_RTT_CANON(struct_index),
WASM_GC_OP(kExprStructNewDefaultWithRtt), struct_index,
kExprCallFunction, struct_consumer});
ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_GC_OP(kExprArrayNewWithRtt),
......@@ -1139,11 +1141,11 @@ TEST_F(FunctionBodyDecoderTest, UnreachableRefTypes) {
{WASM_UNREACHABLE, WASM_I32V(42), WASM_RTT_CANON(array_index),
WASM_GC_OP(kExprArrayNewWithRtt), array_index, kExprDrop});
ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_GC_OP(kExprArrayNewDefault),
{WASM_UNREACHABLE, WASM_GC_OP(kExprArrayNewDefaultWithRtt),
array_index, kExprDrop});
ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_RTT_CANON(array_index),
WASM_GC_OP(kExprArrayNewDefault), array_index, kExprDrop});
ExpectValidates(sigs.v_v(), {WASM_UNREACHABLE, WASM_RTT_CANON(array_index),
WASM_GC_OP(kExprArrayNewDefaultWithRtt),
array_index, kExprDrop});
ExpectValidates(sigs.i_v(), {WASM_UNREACHABLE, WASM_GC_OP(kExprRefTest),
struct_index, struct_index});
......@@ -1976,10 +1978,10 @@ TEST_F(FunctionBodyDecoderTest, TablesWithFunctionSubtyping) {
// that is a subtype of the table type.
ExpectValidates(
FunctionSig::Build(zone(), {ValueType::Ref(sub_struct, kNullable)}, {}),
{WASM_CALL_INDIRECT_TABLE(
table, function_type,
WASM_STRUCT_NEW_DEFAULT(super_struct, WASM_RTT_CANON(super_struct)),
WASM_ZERO)});
{WASM_CALL_INDIRECT_TABLE(table, function_type,
WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
super_struct, WASM_RTT_CANON(super_struct)),
WASM_ZERO)});
// table.set's subtyping works as expected.
ExpectValidates(sigs.v_i(), {WASM_TABLE_SET(0, WASM_LOCAL_GET(0),
......@@ -2688,13 +2690,13 @@ TEST_F(FunctionBodyDecoderTest, BrTableSubtyping) {
{F(kWasmI8, true), F(kWasmI16, false), F(kWasmI32, true)});
ExpectValidates(
sigs.v_v(),
{WASM_BLOCK_R(
wasm::ValueType::Ref(supertype1, kNonNullable),
WASM_BLOCK_R(
wasm::ValueType::Ref(supertype2, kNonNullable),
WASM_STRUCT_NEW_DEFAULT(subtype, WASM_RTT_CANON(subtype)),
WASM_BR_TABLE(WASM_I32V(5), 1, BR_TARGET(0), BR_TARGET(1))),
WASM_UNREACHABLE),
{WASM_BLOCK_R(wasm::ValueType::Ref(supertype1, kNonNullable),
WASM_BLOCK_R(wasm::ValueType::Ref(supertype2, kNonNullable),
WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
subtype, WASM_RTT_CANON(subtype)),
WASM_BR_TABLE(WASM_I32V(5), 1, BR_TARGET(0),
BR_TARGET(1))),
WASM_UNREACHABLE),
WASM_DROP});
}
......@@ -3630,7 +3632,7 @@ ValueType optref(byte type_index) {
return ValueType::Ref(type_index, kNullable);
}
TEST_F(FunctionBodyDecoderTest, StructNewDefault) {
TEST_F(FunctionBodyDecoderTest, StructNewDefaultWithRtt) {
WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
......@@ -3639,12 +3641,12 @@ TEST_F(FunctionBodyDecoderTest, StructNewDefault) {
byte type_index = builder.AddStruct({F(kWasmI32, true)});
byte bad_type_index = builder.AddStruct({F(ref(type_index), true)});
module = builder.module();
ExpectValidates(sigs.v_v(), {WASM_STRUCT_NEW_DEFAULT(
ExpectValidates(sigs.v_v(), {WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
type_index, WASM_RTT_CANON(type_index)),
WASM_DROP});
ExpectFailure(sigs.v_v(),
{WASM_STRUCT_NEW_DEFAULT(bad_type_index,
WASM_RTT_CANON(bad_type_index)),
{WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
bad_type_index, WASM_RTT_CANON(bad_type_index)),
WASM_DROP});
}
{
......@@ -3653,16 +3655,43 @@ TEST_F(FunctionBodyDecoderTest, StructNewDefault) {
byte bad_type_index = builder.AddArray(ref(type_index), true);
module = builder.module();
ExpectValidates(sigs.v_v(),
{WASM_ARRAY_NEW_DEFAULT(type_index, WASM_I32V(3),
WASM_RTT_CANON(type_index)),
{WASM_ARRAY_NEW_DEFAULT_WITH_RTT(
type_index, WASM_I32V(3), WASM_RTT_CANON(type_index)),
WASM_DROP});
ExpectFailure(sigs.v_v(),
{WASM_ARRAY_NEW_DEFAULT(bad_type_index, WASM_I32V(3),
WASM_RTT_CANON(bad_type_index)),
WASM_DROP});
ExpectFailure(sigs.v_v(), {WASM_ARRAY_NEW_DEFAULT_WITH_RTT(
bad_type_index, WASM_I32V(3),
WASM_RTT_CANON(bad_type_index)),
WASM_DROP});
}
}
TEST_F(FunctionBodyDecoderTest, NominalStructSubtyping) {
WASM_FEATURE_SCOPE(reftypes);
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);
AddLocals(optref(structural_type), 1);
AddLocals(optref(nominal_type), 1);
// Try to assign a nominally-typed value to a structurally-typed local.
ExpectFailure(sigs.v_v(),
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT(nominal_type))},
kAppendEnd, "expected type (ref null 0)");
// Try to assign a structurally-typed value to a nominally-typed local.
ExpectFailure(sigs.v_v(),
{WASM_LOCAL_SET(
1, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
structural_type, WASM_RTT_CANON(structural_type)))},
kAppendEnd, "expected type (ref null 1)");
// But assigning to the correctly typed local works.
ExpectValidates(sigs.v_v(),
{WASM_LOCAL_SET(1, WASM_STRUCT_NEW_DEFAULT(nominal_type))});
ExpectValidates(sigs.v_v(),
{WASM_LOCAL_SET(0, WASM_STRUCT_NEW_DEFAULT_WITH_RTT(
structural_type,
WASM_RTT_CANON(structural_type)))});
}
TEST_F(FunctionBodyDecoderTest, DefaultableLocal) {
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(reftypes);
......@@ -4642,11 +4671,11 @@ TEST_F(FunctionBodyDecoderTest, LocalTeeTyping) {
AddLocals(ValueType::Ref(array_type, kNullable), 1);
ExpectFailure(
&sig,
{WASM_LOCAL_TEE(0, WASM_ARRAY_NEW_DEFAULT(array_type, WASM_I32V(5),
WASM_RTT_CANON(array_type)))},
kAppendEnd, "expected (ref 0), got (ref null 0)");
ExpectFailure(&sig,
{WASM_LOCAL_TEE(0, WASM_ARRAY_NEW_DEFAULT_WITH_RTT(
array_type, WASM_I32V(5),
WASM_RTT_CANON(array_type)))},
kAppendEnd, "expected (ref 0), got (ref null 0)");
}
TEST_F(FunctionBodyDecoderTest, MergeNullableTypes) {
......@@ -4664,7 +4693,7 @@ TEST_F(FunctionBodyDecoderTest, MergeNullableTypes) {
// Regression test for crbug.com/1234453.
ExpectValidates(sigs.v_v(),
{WASM_GC_OP(kExprRttCanon), struct_type_index,
WASM_GC_OP(kExprStructNewDefault), struct_type_index,
WASM_GC_OP(kExprStructNewDefaultWithRtt), struct_type_index,
WASM_LOOP_X(loop_sig_index, kExprDrop, kExprRefNull,
struct_type_index, kExprBr, 0)});
}
......
......@@ -39,6 +39,8 @@ namespace module_decoder_unittest {
WASM_STRUCT_NEW_WITH_RTT(index, __VA_ARGS__), kExprEnd
#define WASM_INIT_EXPR_ARRAY_INIT(index, length, ...) \
WASM_ARRAY_INIT(index, length, __VA_ARGS__), kExprEnd
#define WASM_INIT_EXPR_ARRAY_INIT_STATIC(index, length, ...) \
WASM_ARRAY_INIT_STATIC(index, length, __VA_ARGS__), kExprEnd
#define WASM_INIT_EXPR_RTT_CANON(index) WASM_RTT_CANON(index), kExprEnd
#define REF_NULL_ELEMENT kExprRefNull, kFuncRefCode, kExprEnd
......@@ -1116,6 +1118,13 @@ TEST_F(WasmModuleVerifyTest, ArrayInitInitExpr) {
WASM_INIT_EXPR_ARRAY_INIT(0, 3, WASM_I32V(10), WASM_I32V(20),
WASM_I32V(30), WASM_RTT_CANON(0)))};
EXPECT_VERIFIES(basic);
static const byte basic_nominal[] = {
SECTION(Type, ENTRY_COUNT(1), WASM_ARRAY_DEF(kI16Code, true)),
SECTION(Global, ENTRY_COUNT(1), // --
kRefCode, 0, 0, // type, mutability
WASM_INIT_EXPR_ARRAY_INIT_STATIC(0, 3, WASM_I32V(10),
WASM_I32V(20), WASM_I32V(30)))};
EXPECT_VERIFIES(basic_nominal);
static const byte type_error[] = {
SECTION(Type, ENTRY_COUNT(2), // --
......@@ -1234,6 +1243,135 @@ TEST_F(WasmModuleVerifyTest, InvalidStructTypeDef) {
EXPECT_FAILURE_WITH_MSG(invalid_mutability, "invalid mutability");
}
TEST_F(WasmModuleVerifyTest, NominalStructTypeDef) {
WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
// Inheritance: t1 <: t2 <: t0
static const byte all_good[] = {
SECTION(Type, ENTRY_COUNT(3), // --
kWasmStructSubtypeCode, // type #0
1, // field count
kI32Code, 1, // mut i32
kDataRefCode, // root of type hierarchy
kWasmStructSubtypeCode, // type #1
2, // field count
kI32Code, 1, // mut i32 (inherited)
kI64Code, 1, // mut i32 (added)
2, // supertype
kWasmStructSubtypeCode, // type #2
1, // field count
kI32Code, 1, // mut i32 (inherited)
0)}; // supertype
EXPECT_VERIFIES(all_good);
ModuleResult result = DecodeModule(all_good, all_good + sizeof(all_good));
EXPECT_OK(result);
WasmModule* module = result.value().get();
EXPECT_EQ(kGenericSuperType, 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
kDataRefCode, // root of hierarchy
kWasmStructSubtypeCode, // type1
1, // field count
kOptRefCode, 1, 1, // mut optref type1
0, // supertype
kWasmStructSubtypeCode, // type 2
1, // field count
kOptRefCode, 3, 1, // mut optref type3
0, // supertype
kWasmStructSubtypeCode, // type 3
1, // field count
kOptRefCode, 2, 1, // mut optref type2
0)}; // supertype
EXPECT_VERIFIES(self_or_mutual_ref);
static const byte mutual_ref_with_subtyping[] = {
SECTION(Type,
ENTRY_COUNT(3), // --
kWasmStructSubtypeCode, //
1, // field count
kOptRefCode, 0, 0, // ref type0
kDataRefCode, // root of hierarchy
kWasmStructSubtypeCode, // --
1, // field count
kOptRefCode, 2, 0, // ref type2
0, // supertype
kWasmStructSubtypeCode, // --
1, // field count
kOptRefCode, 1, 0, // ref type1
0)}; // supertype
EXPECT_VERIFIES(mutual_ref_with_subtyping);
static const byte inheritance_cycle[] = {
SECTION(Type, ENTRY_COUNT(2), // --
kWasmStructSubtypeCode, 0, 1, // no fields, supertype 1
kWasmStructSubtypeCode, 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:
kI64Code, 1, // i64 (invalid inheritance)
kI32Code, 1, U32V_1(0))}; // i32 (added), supertype 0
EXPECT_FAILURE_WITH_MSG(invalid_field, "invalid explicit supertype");
static const byte structural_supertype[] = {
SECTION(Type, ENTRY_COUNT(2), // --
kWasmStructTypeCode, 0, // empty struct
kWasmStructSubtypeCode, 0, // also empty
0)}; // supertype is structural type
EXPECT_FAILURE_WITH_MSG(structural_supertype, "invalid explicit supertype");
static const byte supertype_oob[] = {
SECTION(Type, ENTRY_COUNT(1), // --
kWasmStructSubtypeCode,
0, // empty struct
13)}; // supertype with invalid index
EXPECT_FAILURE_WITH_MSG(supertype_oob, "Type index 13 is out of bounds");
}
TEST_F(WasmModuleVerifyTest, NominalFunctionTypeDef) {
WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(gc); // Needed for subtype checking.
static const byte all_good[] = {
SECTION(Type, ENTRY_COUNT(2), // --
kWasmFunctionSubtypeCode, // type #0
1, // params count
kRefCode, 0, // ref #0
1, // results count
kOptRefCode, 0, // optref #0
kFuncRefCode, // root of type hierarchy
kWasmFunctionSubtypeCode, // type #1
1, // params count
kOptRefCode, 0, // refined (contravariant)
1, // results count
kRefCode, 0, // refined (covariant)
0)}; // supertype
EXPECT_VERIFIES(all_good);
ModuleResult result = DecodeModule(all_good, all_good + sizeof(all_good));
EXPECT_OK(result);
WasmModule* module = result.value().get();
EXPECT_EQ(kGenericSuperType, module->supertype(0));
EXPECT_EQ(0u, module->supertype(1));
}
TEST_F(WasmModuleVerifyTest, InvalidArrayTypeDef) {
WASM_FEATURE_SCOPE(reftypes);
WASM_FEATURE_SCOPE(typed_funcref);
......
......@@ -26,19 +26,21 @@ void DefineStruct(WasmModule* module, std::initializer_list<FieldInit> fields) {
for (FieldInit field : fields) {
builder.AddField(field.first, field.second);
}
return module->add_struct_type(builder.Build());
return module->add_struct_type(builder.Build(), kNoSuperType);
}
void DefineArray(WasmModule* module, FieldInit element_type) {
module->add_array_type(module->signature_zone->New<ArrayType>(
element_type.first, element_type.second));
element_type.first, element_type.second),
kNoSuperType);
}
void DefineSignature(WasmModule* module,
std::initializer_list<ValueType> params,
std::initializer_list<ValueType> returns) {
module->add_signature(
FunctionSig::Build(module->signature_zone.get(), returns, params));
FunctionSig::Build(module->signature_zone.get(), returns, params),
kNoSuperType);
}
TEST_F(WasmSubtypingTest, Subtyping) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment