mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 16:59:47 +00:00
[dart2wasm] Use [WasmArray]s for type arguments in dynamic calls
This avoids allocating lists in dynamic calls for type arguments. So far the closure shape checking functionality also - as a side-effect of shape checking - added default type arguments to a growable list. We now cleanly separate the logic to use default type arguments vs shape checking. => Doing so allows removing the remaining usages of [List] in the RTT. => Probably also results in smaller & faster dynamic calls. The list allocation is now pushed to the slow path where we create [Invocation] object that is then used for NoSuchMethod handling. Change-Id: I4823cda0aa9b5f1f137813bc5848c365665da5fd Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/344822 Reviewed-by: Ömer Ağacan <omersa@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
parent
3343b7a841
commit
cbe0e6443d
|
@ -570,24 +570,13 @@ class ClosureLayouter extends RecursiveVisitor {
|
|||
b.struct_get(
|
||||
instantiationContextStruct, FieldIndex.instantiationContextInner);
|
||||
|
||||
// Push types, as list
|
||||
translator.makeList(
|
||||
function,
|
||||
(b) {
|
||||
translator.constants.instantiateConstant(
|
||||
function,
|
||||
b,
|
||||
TypeLiteralConstant(
|
||||
InterfaceType(translator.typeClass, Nullability.nonNullable)),
|
||||
translator.types.nonNullableTypeType);
|
||||
},
|
||||
typeCount,
|
||||
// Push types
|
||||
translator.makeArray(function, translator.typeArrayType, typeCount,
|
||||
(elementType, elementIdx) {
|
||||
b.local_get(instantiationContextLocal);
|
||||
b.struct_get(instantiationContextStruct,
|
||||
FieldIndex.instantiationContextTypeArgumentsBase + elementIdx);
|
||||
},
|
||||
isGrowable: true);
|
||||
b.local_get(instantiationContextLocal);
|
||||
b.struct_get(instantiationContextStruct,
|
||||
FieldIndex.instantiationContextTypeArgumentsBase + elementIdx);
|
||||
});
|
||||
|
||||
b.local_get(posArgsListLocal);
|
||||
b.local_get(namedArgsListLocal);
|
||||
|
|
|
@ -1896,15 +1896,12 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
function.addLocal(translator.topInfo.nullableType);
|
||||
b.local_set(nullableReceiverLocal);
|
||||
|
||||
// Evaluate type arguments. Type argument list is growable as we may want
|
||||
// to add default bounds when the callee has type parameters but no type
|
||||
// arguments are passed.
|
||||
makeList(InterfaceType(translator.typeClass, Nullability.nonNullable),
|
||||
typeArguments.length, (elementType, elementIdx) {
|
||||
translator.types.makeType(this, typeArguments[elementIdx]);
|
||||
}, isGrowable: true);
|
||||
// Evaluate type arguments.
|
||||
final typeArgsLocal = function.addLocal(
|
||||
translator.classInfo[translator.growableListClass]!.nonNullableType);
|
||||
makeArray(translator.typeArrayType, typeArguments.length,
|
||||
(elementType, elementIdx) {
|
||||
translator.types.makeType(this, typeArguments[elementIdx]);
|
||||
}));
|
||||
b.local_set(typeArgsLocal);
|
||||
|
||||
// Evaluate positional arguments
|
||||
|
@ -3399,7 +3396,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
// Type argument list is either empty or have the right number of types
|
||||
// (checked by the forwarder).
|
||||
b.local_get(typeArgsLocal);
|
||||
translator.getListLength(b);
|
||||
b.array_len();
|
||||
b.i32_eqz();
|
||||
b.if_([], List.generate(memberTypeParams.length, (_) => typeType));
|
||||
// No type arguments passed, initialize with defaults
|
||||
|
@ -3411,9 +3408,8 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
|
|||
typeParamIdx < memberTypeParams.length;
|
||||
typeParamIdx += 1) {
|
||||
b.local_get(typeArgsLocal);
|
||||
translator.indexList(b, (b) => b.i32_const(typeParamIdx));
|
||||
translator.convertType(
|
||||
function, translator.topInfo.nullableType, typeType);
|
||||
b.i32_const(typeParamIdx);
|
||||
b.array_get(translator.typeArrayType);
|
||||
}
|
||||
b.end();
|
||||
|
||||
|
|
|
@ -58,10 +58,6 @@ class Constants {
|
|||
|
||||
w.ModuleBuilder get m => translator.m;
|
||||
|
||||
/// Makes a type list [ListConstant].
|
||||
ListConstant makeTypeList(Iterable<DartType> types) => ListConstant(
|
||||
translator.typeType, types.map((t) => TypeLiteralConstant(t)).toList());
|
||||
|
||||
/// Makes a `WasmArray<_Type>` [InstanceConstant].
|
||||
InstanceConstant makeTypeArray(Iterable<DartType> types) => makeArrayOf(
|
||||
translator.typeType, types.map((t) => TypeLiteralConstant(t)).toList());
|
||||
|
@ -647,7 +643,7 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
|
|||
final namedArgsListLocal = function.locals[3];
|
||||
|
||||
b.local_get(closureLocal);
|
||||
final ListConstant typeArgs = constants.makeTypeList(constant.types);
|
||||
final InstanceConstant typeArgs = constants.makeTypeArray(constant.types);
|
||||
constants.instantiateConstant(
|
||||
function, b, typeArgs, typeArgsListLocal.type);
|
||||
b.local_get(posArgsListLocal);
|
||||
|
|
|
@ -200,7 +200,7 @@ class Forwarder {
|
|||
final b = function.body;
|
||||
|
||||
final receiverLocal = function.locals[0]; // ref #Top
|
||||
final typeArgsLocal = function.locals[1]; // ref _ListBase
|
||||
final typeArgsLocal = function.locals[1]; // ref WasmArray
|
||||
final positionalArgsLocal = function.locals[2]; // ref WasmArray
|
||||
final namedArgsLocal = function.locals[3]; // ref WasmArray
|
||||
|
||||
|
@ -277,12 +277,12 @@ class Forwarder {
|
|||
if (targetMemberParamInfo.typeParamCount == 0) {
|
||||
// typeArgs.length == 0
|
||||
b.local_get(typeArgsLocal);
|
||||
translator.getListLength(b);
|
||||
b.array_len();
|
||||
b.i32_eqz();
|
||||
} else {
|
||||
// typeArgs.length == 0 || typeArgs.length == typeParams.length
|
||||
b.local_get(typeArgsLocal);
|
||||
translator.getListLength(b);
|
||||
b.array_len();
|
||||
b.local_tee(numArgsLocal);
|
||||
b.i32_eqz();
|
||||
b.local_get(numArgsLocal);
|
||||
|
@ -673,9 +673,7 @@ void generateDynamicFunctionCall(
|
|||
w.Local namedArgsLocal,
|
||||
w.Label noSuchMethodBlock,
|
||||
) {
|
||||
final listArgumentType =
|
||||
translator.classInfo[translator.listBaseClass]!.nonNullableType;
|
||||
assert(typeArgsLocal.type == listArgumentType);
|
||||
assert(typeArgsLocal.type == translator.typeArrayTypeRef);
|
||||
assert(posArgsLocal.type == translator.nullableObjectArrayTypeRef);
|
||||
assert(namedArgsLocal.type == translator.nullableObjectArrayTypeRef);
|
||||
|
||||
|
@ -689,6 +687,20 @@ void generateDynamicFunctionCall(
|
|||
FieldIndex.closureRuntimeType);
|
||||
b.local_tee(functionTypeLocal);
|
||||
|
||||
// If no type arguments were supplied but the closure has type parameters, use
|
||||
// the default values.
|
||||
b.local_get(typeArgsLocal);
|
||||
b.array_len();
|
||||
b.i32_eqz();
|
||||
b.if_();
|
||||
b.local_get(functionTypeLocal);
|
||||
b.struct_get(
|
||||
translator.classInfo[translator.functionTypeClass]!.struct,
|
||||
translator
|
||||
.fieldIndex[translator.functionTypeTypeParameterDefaultsField]!);
|
||||
b.local_set(typeArgsLocal);
|
||||
b.end();
|
||||
|
||||
// Check closure shape
|
||||
b.local_get(typeArgsLocal);
|
||||
b.local_get(posArgsLocal);
|
||||
|
@ -743,6 +755,8 @@ void createInvocationObject(
|
|||
translator.classInfo[translator.symbolClass]!.nonNullableType);
|
||||
|
||||
b.local_get(typeArgsLocal);
|
||||
b.call(translator.functions
|
||||
.getFunction(translator.typeArgumentsToList.reference));
|
||||
b.local_get(positionalArgsLocal);
|
||||
b.call(translator.functions
|
||||
.getFunction(translator.positionalParametersToList.reference));
|
||||
|
|
|
@ -1710,21 +1710,9 @@ class Intrinsifier {
|
|||
final posArgsNullableLocal = function.locals[1]; // ref null Object,
|
||||
final namedArgsLocal = function.locals[2]; // ref null Object
|
||||
|
||||
final listArgumentType =
|
||||
translator.classInfo[translator.listBaseClass]!.nonNullableType;
|
||||
|
||||
// Create type argument list. It will be initialized as empty and it
|
||||
// needs to be growable as `_checkClosureShape` updates it with default
|
||||
// bounds if the function being invokes has type parameters.
|
||||
final typeArgsLocal = function.addLocal(listArgumentType);
|
||||
translator.makeList(function, (b) {
|
||||
translator.constants.instantiateConstant(
|
||||
function,
|
||||
b,
|
||||
TypeLiteralConstant(
|
||||
InterfaceType(translator.typeClass, Nullability.nonNullable)),
|
||||
translator.types.nonNullableTypeType);
|
||||
}, 0, (elementType, elementIndex) {}, isGrowable: true);
|
||||
// Create empty type arguments array.
|
||||
final typeArgsLocal = function.addLocal(translator.makeArray(function,
|
||||
translator.typeArrayType, 0, (elementType, elementIndex) {}));
|
||||
b.local_set(typeArgsLocal);
|
||||
|
||||
// Create empty list for positional args if the argument is null
|
||||
|
|
|
@ -71,6 +71,8 @@ mixin KernelNodes {
|
|||
index.getClass("dart:core", "_AbstractFunctionType");
|
||||
late final Class functionTypeClass =
|
||||
index.getClass("dart:core", "_FunctionType");
|
||||
late final Field functionTypeTypeParameterDefaultsField =
|
||||
index.getField("dart:core", "_FunctionType", "typeParameterDefaults");
|
||||
late final Class functionTypeParameterTypeClass =
|
||||
index.getClass("dart:core", "_FunctionTypeParameterType");
|
||||
late final Class futureOrTypeClass =
|
||||
|
@ -275,6 +277,8 @@ mixin KernelNodes {
|
|||
// dart:core dynamic invocation helper procedures
|
||||
late final Procedure getNamedParameterIndex =
|
||||
index.getTopLevelProcedure("dart:core", "_getNamedParameterIndex");
|
||||
late final Procedure typeArgumentsToList =
|
||||
index.getTopLevelProcedure("dart:core", "_typeArgumentsToList");
|
||||
late final Procedure positionalParametersToList =
|
||||
index.getTopLevelProcedure("dart:core", "_positionalParametersToList");
|
||||
late final Procedure namedParametersToMap =
|
||||
|
|
|
@ -130,6 +130,8 @@ class Translator with KernelNodes {
|
|||
.heapType as w.ArrayType;
|
||||
late final w.ArrayType nullableObjectArrayType =
|
||||
arrayTypeForDartType(coreTypes.objectRawType(Nullability.nullable));
|
||||
late final w.RefType typeArrayTypeRef =
|
||||
w.RefType.def(typeArrayType, nullable: false);
|
||||
late final w.RefType nullableObjectArrayTypeRef =
|
||||
w.RefType.def(nullableObjectArrayType, nullable: false);
|
||||
|
||||
|
@ -185,7 +187,7 @@ class Translator with KernelNodes {
|
|||
w.RefType.def(closureLayouter.closureBaseStruct, nullable: false),
|
||||
|
||||
// Type arguments
|
||||
classInfo[listBaseClass]!.nonNullableType,
|
||||
typeArrayTypeRef,
|
||||
|
||||
// Positional arguments
|
||||
nullableObjectArrayTypeRef,
|
||||
|
@ -203,7 +205,7 @@ class Translator with KernelNodes {
|
|||
topInfo.nonNullableType,
|
||||
|
||||
// Type arguments
|
||||
classInfo[listBaseClass]!.nonNullableType,
|
||||
typeArrayTypeRef,
|
||||
|
||||
// Positional arguments
|
||||
nullableObjectArrayTypeRef,
|
||||
|
@ -1202,7 +1204,8 @@ class _ClosureDynamicEntryGenerator implements _FunctionGenerator {
|
|||
// Push type arguments
|
||||
for (int typeIdx = 0; typeIdx < typeCount; typeIdx += 1) {
|
||||
b.local_get(typeArgsListLocal);
|
||||
translator.indexList(b, (b) => b.i32_const(typeIdx));
|
||||
b.i32_const(typeIdx);
|
||||
b.array_get(translator.typeArrayType);
|
||||
translator.convertType(
|
||||
function, translator.topInfo.nullableType, targetInputs[inputIdx]);
|
||||
inputIdx += 1;
|
||||
|
|
|
@ -18,6 +18,17 @@ int? _getNamedParameterIndex(
|
|||
return null;
|
||||
}
|
||||
|
||||
/// Converts type arguments passed to a dynamic forwarder to a
|
||||
/// list that can be passed to `Invocation` constructors.
|
||||
@pragma("wasm:entry-point")
|
||||
List<_Type?> _typeArgumentsToList(WasmArray<_Type> typeArgs) {
|
||||
final result = <_Type>[];
|
||||
for (int i = 0; i < typeArgs.length; ++i) {
|
||||
result.add(typeArgs[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Converts a positional parameter list passed to a dynamic forwarder to a
|
||||
/// list that can be passed to `Invocation` constructors.
|
||||
@pragma("wasm:entry-point")
|
||||
|
|
|
@ -57,19 +57,6 @@ extension on WasmArray<String> {
|
|||
bool get isNotEmpty => length != 0;
|
||||
}
|
||||
|
||||
// TODO: Remove any occurence of `List`s in this file.
|
||||
extension on List<_Type> {
|
||||
@pragma("wasm:prefer-inline")
|
||||
WasmArray<_Type> toWasmArray() {
|
||||
if (isEmpty) return const WasmArray<_Type>.literal(<_Type>[]);
|
||||
final result = WasmArray<_Type>.filled(length, this[0]);
|
||||
for (int i = 1; i < length; ++i) {
|
||||
result[i] = this[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(joshualitt): We can cache the result of [_FutureOrType.asFuture].
|
||||
abstract class _Type implements Type {
|
||||
final bool isDeclaredNullable;
|
||||
|
@ -393,6 +380,7 @@ class _FunctionType extends _Type {
|
|||
// representations that don't have this overhead in the common case.
|
||||
final int typeParameterOffset;
|
||||
final WasmArray<_Type> typeParameterBounds;
|
||||
@pragma("wasm:entry-point")
|
||||
final WasmArray<_Type> typeParameterDefaults;
|
||||
final _Type returnType;
|
||||
final WasmArray<_Type> positionalParameters;
|
||||
|
@ -1165,16 +1153,12 @@ class _TypeCheckVerificationError extends Error {
|
|||
///
|
||||
/// [namedArguments] is a list of `Symbol` and `Object?` pairs.
|
||||
@pragma("wasm:entry-point")
|
||||
bool _checkClosureShape(_FunctionType functionType, List<_Type> typeArguments,
|
||||
WasmArray<Object?> positionalArguments, WasmArray<dynamic> namedArguments) {
|
||||
// Check type args, add default types to the type list if its empty
|
||||
if (typeArguments.isEmpty) {
|
||||
final defaults = functionType.typeParameterDefaults;
|
||||
for (int i = 0; i < defaults.length; ++i) {
|
||||
typeArguments.add(defaults[i]);
|
||||
}
|
||||
} else if (typeArguments.length !=
|
||||
functionType.typeParameterDefaults.length) {
|
||||
bool _checkClosureShape(
|
||||
_FunctionType functionType,
|
||||
WasmArray<_Type> typeArguments,
|
||||
WasmArray<Object?> positionalArguments,
|
||||
WasmArray<dynamic> namedArguments) {
|
||||
if (typeArguments.length != functionType.typeParameterDefaults.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1236,16 +1220,18 @@ bool _checkClosureShape(_FunctionType functionType, List<_Type> typeArguments,
|
|||
///
|
||||
/// [namedArguments] is a list of `Symbol` and `Object?` pairs.
|
||||
@pragma("wasm:entry-point")
|
||||
void _checkClosureType(_FunctionType functionType, List<_Type> typeArguments,
|
||||
WasmArray<Object?> positionalArguments, WasmArray<dynamic> namedArguments) {
|
||||
void _checkClosureType(
|
||||
_FunctionType functionType,
|
||||
WasmArray<_Type> typeArguments,
|
||||
WasmArray<Object?> positionalArguments,
|
||||
WasmArray<dynamic> namedArguments) {
|
||||
assert(functionType.typeParameterBounds.length == typeArguments.length);
|
||||
|
||||
if (!typeArguments.isEmpty) {
|
||||
final typesAsArray = typeArguments.toWasmArray();
|
||||
for (int i = 0; i < typesAsArray.length; i += 1) {
|
||||
final typeArgument = typesAsArray[i];
|
||||
for (int i = 0; i < typeArguments.length; i += 1) {
|
||||
final typeArgument = typeArguments[i];
|
||||
final paramBound = _TypeUniverse.substituteTypeArgument(
|
||||
functionType.typeParameterBounds[i], typesAsArray, functionType);
|
||||
functionType.typeParameterBounds[i], typeArguments, functionType);
|
||||
if (!_typeUniverse.isSubtype(typeArgument, null, paramBound, null)) {
|
||||
final stackTrace = StackTrace.current;
|
||||
final typeError = _TypeError.fromMessageAndStackTrace(
|
||||
|
@ -1257,7 +1243,7 @@ void _checkClosureType(_FunctionType functionType, List<_Type> typeArguments,
|
|||
}
|
||||
|
||||
functionType = _TypeUniverse.substituteFunctionTypeArgument(
|
||||
functionType, typesAsArray);
|
||||
functionType, typeArguments);
|
||||
}
|
||||
|
||||
// Check positional arguments
|
||||
|
|
Loading…
Reference in a new issue