Use WasmObjectArray instead of List in RTT metadata

This reduces flute's complex.dart slightly

  complex.wasm 1596131 -> 1563506 (-2%)
  complex.wasm.unopt 3562573 -> 3535797 (-0.7%)

And makes the code probably slightly faster

Change-Id: Id35f2b156d39b6e77b62240a83f78914999bb744
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/340565
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
This commit is contained in:
Martin Kustermann 2023-12-11 12:24:04 +00:00 committed by Commit Queue
parent 17f4344336
commit 72f6db9261
7 changed files with 133 additions and 57 deletions

View file

@ -93,6 +93,16 @@ class Constants {
ListConstant(elementType, entries),
});
/// Creates a `WasmIntArray<T>` with the given [Constant]s
InstanceConstant makeIntArrayOf(
InterfaceType elementType, List<IntConstant> entries) =>
InstanceConstant(translator.wasmIntArrayClass.reference, [
elementType,
], {
translator.wasmIntArrayValueField.fieldReference:
ListConstant(elementType, entries),
});
/// Ensure that the constant has a Wasm global assigned.
///
/// Sub-constants must have Wasm globals assigned before the global for the
@ -186,6 +196,10 @@ class ConstantInstantiator extends ConstantVisitor<w.ValueType>
@override
w.ValueType visitIntConstant(IntConstant constant) {
if (expectedType is w.RefType) return defaultConstant(constant);
if (expectedType == w.NumType.i32) {
b.i32_const(constant.value);
return w.NumType.i32;
}
b.i64_const(constant.value);
return w.NumType.i64;
}
@ -382,6 +396,9 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
if (cls == translator.wasmObjectArrayClass) {
return _makeWasmArrayLiteral(constant);
}
if (cls == translator.wasmIntArrayClass) {
return _makeWasmArrayLiteral(constant);
}
ClassInfo info = translator.classInfo[cls]!;
translator.functions.allocateClass(info.classId);

View file

@ -973,6 +973,7 @@ class Intrinsifier {
b.local_set(receiverLocal);
ClassInfo newInfo = translator.classInfo[newClass]!;
translator.functions.allocateClass(newInfo.classId);
b.i32_const(newInfo.classId);
b.i32_const(initialIdentityHash);
b.local_get(receiverLocal);
@ -993,8 +994,10 @@ class Intrinsifier {
w.ValueType? generateConstructorIntrinsic(ConstructorInvocation node) {
String name = node.name.text;
// WasmObjectArray.literal
if (node.target.enclosingClass == translator.wasmObjectArrayClass &&
// WasmObjectArray.literal & WasmIntArray.literal
final klass = node.target.enclosingClass;
if ((klass == translator.wasmObjectArrayClass ||
klass == translator.wasmIntArrayClass) &&
name == "literal") {
w.ArrayType arrayType =
translator.arrayTypeForDartType(node.arguments.types.single);

View file

@ -153,6 +153,10 @@ mixin KernelNodes {
index.getClass("dart:_wasm", "WasmObjectArray");
late final Field wasmObjectArrayValueField =
index.getField("dart:_wasm", "WasmObjectArray", "_value");
late final Class wasmIntArrayClass =
index.getClass("dart:_wasm", "WasmIntArray");
late final Field wasmIntArrayValueField =
index.getField("dart:_wasm", "WasmIntArray", "_value");
// dart:_internal procedures
late final Procedure loadLibrary =

View file

@ -224,69 +224,84 @@ class Types {
/// TODO(joshualitt): This implementation is just temporary. Eventually we
/// should move to a data structure more closely resembling [typeRules].
w.ValueType makeTypeRulesSupers(w.InstructionsBuilder b) {
w.ValueType expectedType =
translator.classInfo[translator.immutableListClass]!.nonNullableType;
DartType listIntType = InterfaceType(translator.immutableListClass,
Nullability.nonNullable, [translator.coreTypes.intNonNullableRawType]);
List<ListConstant> listIntConstant = [];
final wasmI32Type =
InterfaceType(translator.wasmI32Class, Nullability.nonNullable);
final supersOfClasses = <Constant>[];
for (List<int> supers in typeRulesSupers) {
listIntConstant.add(ListConstant(
listIntType, supers.map((i) => IntConstant(i)).toList()));
supersOfClasses.add(translator.constants.makeIntArrayOf(
wasmI32Type, [for (final cid in supers) IntConstant(cid)]));
}
DartType listListIntType = InterfaceType(
translator.immutableListClass, Nullability.nonNullable, [listIntType]);
translator.constants.instantiateConstant(
null, b, ListConstant(listListIntType, listIntConstant), expectedType);
return expectedType;
final arrayOfWasmI32Type = InterfaceType(
translator.wasmIntArrayClass, Nullability.nonNullable, [wasmI32Type]);
final typeRuleSupers =
translator.constants.makeArrayOf(arrayOfWasmI32Type, supersOfClasses);
final arrayOfArrayOfWasmI32Type = InterfaceType(
translator.wasmObjectArrayClass,
Nullability.nonNullable,
[arrayOfWasmI32Type]);
final typeRulesSupersType =
translator.translateStorageType(arrayOfArrayOfWasmI32Type).unpacked;
translator.constants
.instantiateConstant(null, b, typeRuleSupers, typeRulesSupersType);
return typeRulesSupersType;
}
/// Similar to the above, but provides the substitutions required for each
/// supertype.
/// TODO(joshualitt): Like [makeTypeRulesSupers], this is just temporary.
w.ValueType makeTypeRulesSubstitutions(w.InstructionsBuilder b) {
w.ValueType expectedType =
translator.classInfo[translator.immutableListClass]!.nonNullableType;
DartType listTypeType = InterfaceType(
translator.immutableListClass,
final typeType =
InterfaceType(translator.typeClass, Nullability.nonNullable);
final arrayOfType = InterfaceType(
translator.wasmObjectArrayClass, Nullability.nonNullable, [typeType]);
final arrayOfArrayOfType = InterfaceType(translator.wasmObjectArrayClass,
Nullability.nonNullable, [arrayOfType]);
final arrayOfArrayOfArrayOfType = InterfaceType(
translator.wasmObjectArrayClass,
Nullability.nonNullable,
[translator.typeClass.getThisType(coreTypes, Nullability.nonNullable)]);
DartType listListTypeType = InterfaceType(
translator.immutableListClass, Nullability.nonNullable, [listTypeType]);
DartType listListListTypeType = InterfaceType(translator.immutableListClass,
Nullability.nonNullable, [listListTypeType]);
List<ListConstant> substitutionsConstantL0 = [];
[arrayOfArrayOfType]);
final substitutionsConstantL0 = <Constant>[];
for (List<List<DartType>> substitutionsL1 in typeRulesSubstitutions) {
List<ListConstant> substitutionsConstantL1 = [];
final substitutionsConstantL1 = <Constant>[];
for (List<DartType> substitutionsL2 in substitutionsL1) {
substitutionsConstantL1.add(ListConstant(listTypeType,
substitutionsL2.map((t) => TypeLiteralConstant(t)).toList()));
substitutionsConstantL1.add(translator.constants.makeArrayOf(typeType,
[for (final t in substitutionsL2) TypeLiteralConstant(t)]));
}
substitutionsConstantL0
.add(ListConstant(listListTypeType, substitutionsConstantL1));
substitutionsConstantL0.add(translator.constants
.makeArrayOf(arrayOfType, substitutionsConstantL1));
}
final typeRulesSubstitutionsType =
translator.translateStorageType(arrayOfArrayOfArrayOfType).unpacked;
translator.constants.instantiateConstant(
null,
b,
ListConstant(listListListTypeType, substitutionsConstantL0),
expectedType);
return expectedType;
translator.constants
.makeArrayOf(arrayOfArrayOfType, substitutionsConstantL0),
typeRulesSubstitutionsType);
return typeRulesSubstitutionsType;
}
/// Returns a list of string type names for pretty printing types.
w.ValueType makeTypeNames(w.InstructionsBuilder b) {
w.ValueType expectedType =
translator.classInfo[translator.immutableListClass]!.nonNullableType;
List<StringConstant> listStringConstant = [];
for (String name in typeNames) {
listStringConstant.add(StringConstant(name));
}
DartType listStringType = InterfaceType(
translator.immutableListClass,
Nullability.nonNullable,
[translator.coreTypes.stringNonNullableRawType]);
translator.constants.instantiateConstant(null, b,
ListConstant(listStringType, listStringConstant), expectedType);
return expectedType;
final stringType =
translator.coreTypes.stringRawType(Nullability.nonNullable);
final arrayOfStringType = InterfaceType(
translator.wasmObjectArrayClass, Nullability.nonNullable, [stringType]);
final arrayOfStrings = translator.constants.makeArrayOf(
stringType, [for (final name in typeNames) StringConstant(name)]);
final typeNamesType =
translator.translateStorageType(arrayOfStringType).unpacked;
translator.constants
.instantiateConstant(null, b, arrayOfStrings, typeNamesType);
return typeNamesType;
}
/// Build a global array of byte values used to categorize runtime types.

View file

@ -65,6 +65,26 @@ extension on WasmObjectArray<_NamedParameter> {
}
}
extension on WasmObjectArray<String> {
@pragma("wasm:prefer-inline")
String operator [](int index) => read(index);
}
extension on WasmObjectArray<WasmIntArray<WasmI32>> {
@pragma("wasm:prefer-inline")
WasmIntArray<WasmI32> operator [](int index) => read(index);
}
extension on WasmObjectArray<WasmObjectArray<_Type>> {
@pragma("wasm:prefer-inline")
WasmObjectArray<_Type> operator [](int index) => read(index);
}
extension on WasmObjectArray<WasmObjectArray<WasmObjectArray<_Type>>> {
@pragma("wasm:prefer-inline")
WasmObjectArray<WasmObjectArray<_Type>> operator [](int index) => read(index);
}
// TODO: Remove any occurence of `List`s in this file.
extension on List<_Type> {
@pragma("wasm:prefer-inline")
@ -630,9 +650,10 @@ class _RecordType extends _Type {
identical(names, other.names);
}
external List<List<int>> _getTypeRulesSupers();
external List<List<List<_Type>>> _getTypeRulesSubstitutions();
external List<String> _getTypeNames();
external WasmObjectArray<WasmIntArray<WasmI32>> _getTypeRulesSupers();
external WasmObjectArray<WasmObjectArray<WasmObjectArray<_Type>>>
_getTypeRulesSubstitutions();
external WasmObjectArray<String> _getTypeNames();
/// Type parameter environment used while comparing function types.
///
@ -681,11 +702,12 @@ class _Environment {
class _TypeUniverse {
/// 'Map' of classId to the transitive set of super classes it implements.
final List<List<int>> typeRulesSupers;
final WasmObjectArray<WasmIntArray<WasmI32>> typeRulesSupers;
/// 'Map' of classId, and super offset(from [typeRulesSupers]) to a list of
/// type substitutions.
final List<List<List<_Type>>> typeRulesSubstitutions;
final WasmObjectArray<WasmObjectArray<WasmObjectArray<_Type>>>
typeRulesSubstitutions;
const _TypeUniverse._(this.typeRulesSupers, this.typeRulesSubstitutions);
@ -870,21 +892,21 @@ class _TypeUniverse {
// Otherwise, check if [s] is a subtype of [t], and if it is then compare
// [s]'s type substitutions with [t]'s type arguments.
List<int> sSupers = typeRulesSupers._getUnchecked(sId);
if (sSupers.isEmpty) return false;
final WasmIntArray<WasmI32> sSupers = typeRulesSupers[sId];
if (sSupers.length == 0) return false;
int sSuperIndexOfT = -1;
for (int i = 0; i < sSupers.length; i++) {
if (sSupers._getUnchecked(i) == tId) {
if (sSupers.readUnsigned(i) == tId) {
sSuperIndexOfT = i;
break;
}
}
if (sSuperIndexOfT == -1) return false;
assert(sSuperIndexOfT < typeRulesSubstitutions._getUnchecked(sId).length);
assert(sSuperIndexOfT < typeRulesSubstitutions[sId].length);
// Return early if we don't have to check type arguments.
List<_Type> substitutions =
typeRulesSubstitutions._getUnchecked(sId)._getUnchecked(sSuperIndexOfT);
WasmObjectArray<_Type> substitutions =
typeRulesSubstitutions[sId][sSuperIndexOfT];
if (substitutions.isEmpty && sTypeArguments.isEmpty) {
return true;
}
@ -904,7 +926,7 @@ class _TypeUniverse {
WasmObjectArray<_Type>(substitutions.length, _literal<dynamic>());
for (int i = 0; i < substitutions.length; i++) {
substituted[i] = substituteTypeArgument(
substitutions._getUnchecked(i), typeArgumentsForSubstitution, null);
substitutions[i], typeArgumentsForSubstitution, null);
}
return areTypeArgumentsSubtypes(substituted, sEnv, t.typeArguments, tEnv);
}

View file

@ -194,8 +194,14 @@ class WasmF64 extends _WasmFloat {
/// A Wasm array with integer element type.
@pragma("wasm:entry-point")
class WasmIntArray<T extends _WasmInt> extends WasmArrayRef {
/// Dummy value field to contain the value for constant instances.
@pragma("wasm:entry-point")
final List<int> _value;
external factory WasmIntArray(int length);
const WasmIntArray.literal(this._value) : super._();
external int readSigned(int index);
external int readUnsigned(int index);
external void write(int index, int value);

View file

@ -144,6 +144,15 @@ test() {
Expect.notIdentical(arrayAlit3.read(0), arrayAlit3.read(1));
Expect.identical(arrayAlit3.read(0), arrayAlit3.read(2));
final int32Array = WasmIntArray<WasmI32>.literal([0, 1, 2, 3]);
final int32ArrayC = const WasmIntArray<WasmI32>.literal([0, 10, 20, 30]);
for (int i = 0; i < 4; ++i) {
Expect.equals(int32Array.readSigned(i), i);
}
for (int i = 0; i < 4; ++i) {
Expect.equals(int32ArrayC.readSigned(i), i * 10);
}
Expect.isFalse(arrayA == arrayAlit1);
Expect.isFalse(arrayAlit2 == arrayAlit3);
Expect.isTrue(arrayAlit3 == arrayAlit4);