[dart2wasm] Special runtime type classes for special interface types

Adds special representations of runtime types for the interface types
`Object`, `Function` and `Record`. With this change, all types with
non-interface subtypes have special representations, which avoids some
special cases down the line.

Change-Id: I61b4da20fa1cc62d42e1770278a3272028c9e2a0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/318681
Reviewed-by: Joshua Litt <joshualitt@google.com>
This commit is contained in:
Aske Simon Christensen 2023-08-11 09:08:32 +00:00 committed by Commit Queue
parent 166c5d7b41
commit 0c1bb28ad4
7 changed files with 142 additions and 35 deletions

View file

@ -506,6 +506,11 @@ class ClassInfoCollector {
void collect() { void collect() {
_initializeTop(); _initializeTop();
// Initialize the record base class early to give enough space for special
// values in the type category table before the first masquerade class
// (which is `Type`).
_initialize(translator.coreTypes.recordClass);
// Subclasses of the `_Closure` class are generated on the fly as fields // Subclasses of the `_Closure` class are generated on the fly as fields
// with function types are encountered. Therefore, `_Closure` class must // with function types are encountered. Therefore, `_Closure` class must
// be early in the initialization order. // be early in the initialization order.
@ -526,11 +531,6 @@ class ClassInfoCollector {
_initialize(cls); _initialize(cls);
} }
// Initialize the record base class if we have record classes.
if (translator.recordClasses.isNotEmpty) {
_initialize(translator.coreTypes.recordClass);
}
for (Library library in translator.component.libraries) { for (Library library in translator.component.libraries) {
for (Class cls in library.classes) { for (Class cls in library.classes) {
if (cls.superclass == translator.coreTypes.recordClass) { if (cls.superclass == translator.coreTypes.recordClass) {

View file

@ -801,7 +801,7 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
ClassInfo info = translator.classInfo[types.classForType(type)]!; ClassInfo info = translator.classInfo[types.classForType(type)]!;
translator.functions.allocateClass(info.classId); translator.functions.allocateClass(info.classId);
if (type is InterfaceType) { if (type is InterfaceType && !types.isSpecializedClass(type.classNode)) {
return _makeInterfaceType(constant, type, info); return _makeInterfaceType(constant, type, info);
} else if (type is FutureOrType) { } else if (type is FutureOrType) {
return _makeFutureOrType(constant, type, info); return _makeFutureOrType(constant, type, info);
@ -861,7 +861,8 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
assert(type is VoidType || assert(type is VoidType ||
type is NeverType || type is NeverType ||
type is NullType || type is NullType ||
type is DynamicType); type is DynamicType ||
type is InterfaceType && types.isSpecializedClass(type.classNode));
return createConstant(constant, info.nonNullableType, (function, b) { return createConstant(constant, info.nonNullableType, (function, b) {
b.i32_const(info.classId); b.i32_const(info.classId);
b.i32_const(initialIdentityHash); b.i32_const(initialIdentityHash);

View file

@ -1149,6 +1149,7 @@ class Intrinsifier {
w.Label notMasqueraded = b.block(); w.Label notMasqueraded = b.block();
w.Label recordType = b.block(); w.Label recordType = b.block();
w.Label functionType = b.block(); w.Label functionType = b.block();
w.Label objectType = b.block();
w.Label abstractClass = b.block(); w.Label abstractClass = b.block();
// Look up the type category by class ID and switch on it. // Look up the type category by class ID and switch on it.
@ -1161,6 +1162,7 @@ class Intrinsifier {
b.local_tee(resultClassId); b.local_tee(resultClassId);
b.br_table([ b.br_table([
abstractClass, abstractClass,
objectType,
functionType, functionType,
recordType, recordType,
if (masqueraded) notMasqueraded if (masqueraded) notMasqueraded
@ -1170,6 +1172,16 @@ class Intrinsifier {
// We should never see class IDs for abstract types. // We should never see class IDs for abstract types.
b.unreachable(); b.unreachable();
b.end(); // objectType
ClassInfo objectTypeInfo =
translator.classInfo[translator.objectTypeClass]!;
b.i32_const(objectTypeInfo.classId);
b.i32_const(initialIdentityHash);
// Runtime types are never nullable.
b.i32_const(0);
b.struct_new(objectTypeInfo.struct);
b.return_();
b.end(); // functionType b.end(); // functionType
w.StructType closureBase = translator.closureLayouter.closureBaseStruct; w.StructType closureBase = translator.closureLayouter.closureBaseStruct;
b.local_get(object); b.local_get(object);

View file

@ -68,6 +68,9 @@ mixin KernelNodes {
late final Class typeClass = index.getClass("dart:core", "_Type"); late final Class typeClass = index.getClass("dart:core", "_Type");
late final Class dynamicTypeClass = late final Class dynamicTypeClass =
index.getClass("dart:core", "_DynamicType"); index.getClass("dart:core", "_DynamicType");
late final Class objectTypeClass = index.getClass("dart:core", "_ObjectType");
late final Class abstractFunctionTypeClass =
index.getClass("dart:core", "_AbstractFunctionType");
late final Class functionTypeClass = late final Class functionTypeClass =
index.getClass("dart:core", "_FunctionType"); index.getClass("dart:core", "_FunctionType");
late final Class functionTypeParameterTypeClass = late final Class functionTypeParameterTypeClass =
@ -86,6 +89,8 @@ mixin KernelNodes {
late final Class stackTraceClass = index.getClass("dart:core", "StackTrace"); late final Class stackTraceClass = index.getClass("dart:core", "StackTrace");
late final Class typeUniverseClass = late final Class typeUniverseClass =
index.getClass("dart:core", "_TypeUniverse"); index.getClass("dart:core", "_TypeUniverse");
late final Class abstractRecordTypeClass =
index.getClass("dart:core", "_AbstractRecordType");
late final Class recordTypeClass = index.getClass("dart:core", "_RecordType"); late final Class recordTypeClass = index.getClass("dart:core", "_RecordType");
// dart:core sync* support classes // dart:core sync* support classes

View file

@ -18,10 +18,11 @@ import 'package:wasm_builder/wasm_builder.dart' as w;
/// the class ID of the masquerade. /// the class ID of the masquerade.
class TypeCategory { class TypeCategory {
static const abstractClass = 0; static const abstractClass = 0;
static const function = 1; static const object = 1;
static const record = 2; static const function = 2;
static const notMasqueraded = 3; static const record = 3;
static const minMasqueradeClassId = 4; static const notMasqueraded = 4;
static const minMasqueradeClassId = 5;
static const maxMasqueradeClassId = 63; // Leaves 2 unused bits for future use static const maxMasqueradeClassId = 63; // Leaves 2 unused bits for future use
} }
@ -279,6 +280,8 @@ class Types {
int category; int category;
if (cls == null || cls.isAbstract) { if (cls == null || cls.isAbstract) {
category = TypeCategory.abstractClass; category = TypeCategory.abstractClass;
} else if (cls == coreTypes.objectClass) {
category = TypeCategory.object;
} else if (cls == translator.closureClass) { } else if (cls == translator.closureClass) {
category = TypeCategory.function; category = TypeCategory.function;
} else if (recordClasses.contains(cls)) { } else if (recordClasses.contains(cls)) {
@ -355,6 +358,15 @@ class Types {
} else if (type is FutureOrType) { } else if (type is FutureOrType) {
return translator.futureOrTypeClass; return translator.futureOrTypeClass;
} else if (type is InterfaceType) { } else if (type is InterfaceType) {
if (type.classNode == coreTypes.objectClass) {
return translator.objectTypeClass;
}
if (type.classNode == coreTypes.functionClass) {
return translator.abstractFunctionTypeClass;
}
if (type.classNode == coreTypes.recordClass) {
return translator.abstractRecordTypeClass;
}
return translator.interfaceTypeClass; return translator.interfaceTypeClass;
} else if (type is FunctionType) { } else if (type is FunctionType) {
return translator.functionTypeClass; return translator.functionTypeClass;
@ -372,6 +384,12 @@ class Types {
throw "Unexpected DartType: $type"; throw "Unexpected DartType: $type";
} }
bool isSpecializedClass(Class cls) {
return cls == coreTypes.objectClass ||
cls == coreTypes.functionClass ||
cls == coreTypes.recordClass;
}
/// Allocates a `List<_Type>` from [types] and pushes it to the stack. /// Allocates a `List<_Type>` from [types] and pushes it to the stack.
void _makeTypeList(CodeGenerator codeGen, Iterable<DartType> types) { void _makeTypeList(CodeGenerator codeGen, Iterable<DartType> types) {
if (types.every(_isTypeConstant)) { if (types.every(_isTypeConstant)) {

View file

@ -58,12 +58,18 @@ class ClassID {
external static int get cidFutureOrType; external static int get cidFutureOrType;
@pragma("wasm:class-id", "dart.core#_InterfaceType") @pragma("wasm:class-id", "dart.core#_InterfaceType")
external static int get cidInterfaceType; external static int get cidInterfaceType;
@pragma("wasm:class-id", "dart.core#_ObjectType")
external static int get cidObjectType;
@pragma("wasm:class-id", "dart.core#_AbstractFunctionType")
external static int get cidAbstractFunctionType;
@pragma("wasm:class-id", "dart.core#_FunctionType") @pragma("wasm:class-id", "dart.core#_FunctionType")
external static int get cidFunctionType; external static int get cidFunctionType;
@pragma("wasm:class-id", "dart.core#_FunctionTypeParameterType") @pragma("wasm:class-id", "dart.core#_FunctionTypeParameterType")
external static int get cidFunctionTypeParameterType; external static int get cidFunctionTypeParameterType;
@pragma("wasm:class-id", "dart.core#_InterfaceTypeParameterType") @pragma("wasm:class-id", "dart.core#_InterfaceTypeParameterType")
external static int get cidInterfaceTypeParameterType; external static int get cidInterfaceTypeParameterType;
@pragma("wasm:class-id", "dart.core#_AbstractRecordType")
external static int get cidAbstractRecordType;
@pragma("wasm:class-id", "dart.core#_RecordType") @pragma("wasm:class-id", "dart.core#_RecordType")
external static int get cidRecordType; external static int get cidRecordType;

View file

@ -13,7 +13,6 @@ part of 'core_patch.dart';
// file: // file:
// * [_Type.asNonNullable] // * [_Type.asNonNullable]
// * [_FutureOrType.asFuture]. // * [_FutureOrType.asFuture].
// TODO(joshualitt): Make `Function` a canonical type.
abstract class _Type implements Type { abstract class _Type implements Type {
final bool isDeclaredNullable; final bool isDeclaredNullable;
@ -30,7 +29,10 @@ abstract class _Type implements Type {
_testID(ClassID.cidInterfaceTypeParameterType); _testID(ClassID.cidInterfaceTypeParameterType);
bool get isFunctionTypeParameterType => bool get isFunctionTypeParameterType =>
_testID(ClassID.cidFunctionTypeParameterType); _testID(ClassID.cidFunctionTypeParameterType);
bool get isObject => _testID(ClassID.cidObjectType);
bool get isAbstractFunction => _testID(ClassID.cidAbstractFunctionType);
bool get isFunction => _testID(ClassID.cidFunctionType); bool get isFunction => _testID(ClassID.cidFunctionType);
bool get isAbstractRecord => _testID(ClassID.cidAbstractRecordType);
bool get isRecord => _testID(ClassID.cidRecordType); bool get isRecord => _testID(ClassID.cidRecordType);
T as<T>() => unsafeCast<T>(this); T as<T>() => unsafeCast<T>(this);
@ -308,6 +310,57 @@ class _NamedParameter {
} }
} }
class _ObjectType extends _Type {
@pragma("wasm:entry-point")
const _ObjectType(super.isDeclaredNullable);
@override
_Type get _asNonNullable => _ObjectType(false);
@override
_Type get _asNullable => _ObjectType(true);
@override
bool operator ==(Object o) {
if (!(super == o)) return false;
_ObjectType other = unsafeCast<_ObjectType>(o);
return isDeclaredNullable == other.isDeclaredNullable;
}
@override
int get hashCode {
int hash = super.hashCode;
return mix64(hash ^ (isDeclaredNullable ? 1 : 0));
}
@override
String toString() {
StringBuffer s = StringBuffer();
s.write("Object");
if (isDeclaredNullable) s.write("?");
return s.toString();
}
}
class _AbstractFunctionType extends _Type {
@pragma("wasm:entry-point")
const _AbstractFunctionType(super.isDeclaredNullable);
@override
_Type get _asNonNullable => _AbstractFunctionType(false);
@override
_Type get _asNullable => _AbstractFunctionType(true);
@override
String toString() {
StringBuffer s = StringBuffer();
s.write("Function");
if (isDeclaredNullable) s.write("?");
return s.toString();
}
}
class _FunctionType extends _Type { class _FunctionType extends _Type {
// TODO(askesc): The [typeParameterOffset] is 0 except in the rare case where // TODO(askesc): The [typeParameterOffset] is 0 except in the rare case where
// the function type contains a nested generic function type that contains a // the function type contains a nested generic function type that contains a
@ -438,6 +491,25 @@ class _FunctionType extends _Type {
} }
} }
class _AbstractRecordType extends _Type {
@pragma("wasm:entry-point")
const _AbstractRecordType(super.isDeclaredNullable);
@override
_Type get _asNonNullable => _AbstractRecordType(false);
@override
_Type get _asNullable => _AbstractRecordType(true);
@override
String toString() {
StringBuffer s = StringBuffer();
s.write("Record");
if (isDeclaredNullable) s.write("?");
return s.toString();
}
}
class _RecordType extends _Type { class _RecordType extends _Type {
final List<String> names; final List<String> names;
final List<_Type> fieldTypes; final List<_Type> fieldTypes;
@ -575,30 +647,20 @@ class _TypeUniverse {
return _TypeUniverse._(_getTypeRulesSupers(), _getTypeRulesSubstitutions()); return _TypeUniverse._(_getTypeRulesSupers(), _getTypeRulesSubstitutions());
} }
static bool isSpecificInterfaceType(_Type t, int classId) { static bool isTopType(_Type type) {
if (!t.isInterface) return false; return type.isObject && type.isDeclaredNullable ||
_InterfaceType type = t.as<_InterfaceType>(); type.isDynamic ||
return type.classId == classId; type.isVoid;
} }
static bool isObjectQuestionType(_Type t) => static bool isObjectOrTopType(_Type type) {
isObjectType(t) && t.isDeclaredNullable; return type.isObject || type.isDynamic || type.isVoid;
static bool isObjectType(_Type t) =>
isSpecificInterfaceType(t, ClassID.cidObject);
static bool isTopType(_Type type) {
return isObjectQuestionType(type) || type.isDynamic || type.isVoid;
} }
static bool isBottomType(_Type type) { static bool isBottomType(_Type type) {
return type.isNever; return type.isNever;
} }
static bool isFunctionType(_Type t) =>
isSpecificInterfaceType(t, ClassID.cidFunction) ||
isSpecificInterfaceType(t, ClassID.cid_Closure);
static _Type substituteInterfaceTypeParameter( static _Type substituteInterfaceTypeParameter(
_InterfaceTypeParameterType typeParameter, List<_Type> substitutions) { _InterfaceTypeParameterType typeParameter, List<_Type> substitutions) {
// If the type parameter is non-nullable, or the substitution type is // If the type parameter is non-nullable, or the substitution type is
@ -643,7 +705,11 @@ class _TypeUniverse {
/// substituted. /// substituted.
static _Type substituteTypeArgument( static _Type substituteTypeArgument(
_Type type, List<_Type> substitutions, _FunctionType? rootFunction) { _Type type, List<_Type> substitutions, _FunctionType? rootFunction) {
if (type.isNever || type.isDynamic || type.isVoid || type.isNull) { if (type.isNever ||
type.isDynamic ||
type.isVoid ||
type.isNull ||
type.isObject) {
return type; return type;
} else if (type.isFutureOr) { } else if (type.isFutureOr) {
return createNormalizedFutureOrType( return createNormalizedFutureOrType(
@ -725,7 +791,7 @@ class _TypeUniverse {
static _Type createNormalizedFutureOrType( static _Type createNormalizedFutureOrType(
bool isDeclaredNullable, _Type typeArgument) { bool isDeclaredNullable, _Type typeArgument) {
if (isTopType(typeArgument) || isObjectType(typeArgument)) { if (isObjectOrTopType(typeArgument)) {
return typeArgument; return typeArgument;
} else if (typeArgument.isNever) { } else if (typeArgument.isNever) {
return _InterfaceType( return _InterfaceType(
@ -940,7 +1006,7 @@ class _TypeUniverse {
} }
// Right Object: // Right Object:
if (isObjectType(t)) { if (t.isObject) {
if (s.isFutureOr) { if (s.isFutureOr) {
return isSubtype(s.as<_FutureOrType>().typeArgument, sEnv, t, tEnv); return isSubtype(s.as<_FutureOrType>().typeArgument, sEnv, t, tEnv);
} }
@ -984,7 +1050,7 @@ class _TypeUniverse {
// Left Promoted Variable does not apply at runtime. // Left Promoted Variable does not apply at runtime.
// Function Type / Function: // Function Type / Function:
if (s.isFunction && isFunctionType(t)) { if (s.isFunction && t.isAbstractFunction) {
return true; return true;
} }
@ -1000,9 +1066,8 @@ class _TypeUniverse {
} }
// Records are subtypes of the `Record` type: // Records are subtypes of the `Record` type:
if (s.isRecord && t.isInterface) { if (s.isRecord && t.isAbstractRecord) {
final tInterfaceType = t.as<_InterfaceType>(); return true;
return tInterfaceType.classId == ClassID.cidRecord;
} }
// Interface Compositionality + Super-Interface: // Interface Compositionality + Super-Interface: