Skip to content

Commit

Permalink
Version 3.4.0-221.0.dev
Browse files Browse the repository at this point in the history
Merge f4c8c9b into dev
  • Loading branch information
Dart CI committed Mar 11, 2024
2 parents c3bd630 + f4c8c9b commit 09a1a1e
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 196 deletions.
152 changes: 85 additions & 67 deletions pkg/dart2wasm/lib/code_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2921,31 +2921,33 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>

@override
w.ValueType visitListLiteral(ListLiteral node, w.ValueType expectedType) {
return makeListFromExpressions(node.expressions, node.typeArgument,
isGrowable: true);
}
final useSharedCreator = types.isTypeConstant(node.typeArgument);

/// Allocate a Dart `List` with element type [typeArg], length [length] and
/// push the list to the stack.
///
/// [generateItem] will be called [length] times to initialize list elements.
///
/// Concrete type of the list will be `_GrowableList` if [isGrowable] is
/// true, `_List` otherwise.
w.ValueType makeList(DartType typeArg, int length,
void Function(w.ValueType, int) generateItem,
{bool isGrowable = false}) {
return translator.makeList(
function, (b) => types.makeType(this, typeArg), length, generateItem,
isGrowable: isGrowable);
}

w.ValueType makeListFromExpressions(
List<Expression> expressions, DartType typeArg,
{bool isGrowable = false}) =>
makeList(typeArg, expressions.length,
(w.ValueType elementType, int i) => wrap(expressions[i], elementType),
isGrowable: isGrowable);
final passType = !useSharedCreator;
final passArray = node.expressions.isNotEmpty;

final targetReference = passArray
? translator.growableListFromWasmArray.reference
: translator.growableListEmpty.reference;

final w.BaseFunction target = useSharedCreator
? translator.partialInstantiator.getOneTypeArgumentForwarder(
targetReference,
node.typeArgument,
'create${passArray ? '' : 'Empty'}List<${node.typeArgument}>')
: translator.functions.getFunction(targetReference);

if (passType) {
types.makeType(this, node.typeArgument);
}
if (passArray) {
makeArrayFromExpressions(node.expressions,
translator.coreTypes.objectRawType(Nullability.nullable));
}

b.call(target);
return target.type.outputs.single;
}

w.ValueType makeArrayFromExpressions(
List<Expression> expressions, InterfaceType elementType) {
Expand All @@ -2963,55 +2965,71 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>

@override
w.ValueType visitMapLiteral(MapLiteral node, w.ValueType expectedType) {
types.makeType(this, node.keyType);
types.makeType(this, node.valueType);
w.ValueType factoryReturnType =
call(translator.mapFactory.reference).single;
if (node.entries.isEmpty) {
return factoryReturnType;
}
w.FunctionType mapPutType =
translator.functions.getFunctionType(translator.mapPut.reference);
w.ValueType putReceiverType = mapPutType.inputs[0];
w.ValueType putKeyType = mapPutType.inputs[1];
w.ValueType putValueType = mapPutType.inputs[2];
w.Local mapLocal = addLocal(putReceiverType);
translator.convertType(function, factoryReturnType, mapLocal.type);
b.local_set(mapLocal);
for (MapLiteralEntry entry in node.entries) {
b.local_get(mapLocal);
wrap(entry.key, putKeyType);
wrap(entry.value, putValueType);
call(translator.mapPut.reference);
b.drop();
final useSharedCreator = types.isTypeConstant(node.keyType) &&
types.isTypeConstant(node.valueType);

final passTypes = !useSharedCreator;
final passArray = node.entries.isNotEmpty;

final targetReference = passArray
? translator.mapFromWasmArray.reference
: translator.mapFactory.reference;

final w.BaseFunction target = useSharedCreator
? translator.partialInstantiator.getTwoTypeArgumentForwarder(
targetReference,
node.keyType,
node.valueType,
'create${passArray ? '' : 'Empty'}Map<${node.keyType}, ${node.valueType}>')
: translator.functions.getFunction(targetReference);

if (passTypes) {
types.makeType(this, node.keyType);
types.makeType(this, node.valueType);
}
if (passArray) {
makeArray(translator.nullableObjectArrayType, 2 * node.entries.length,
(elementType, elementIndex) {
final index = elementIndex ~/ 2;
final entry = node.entries[index];
if (elementIndex % 2 == 0) {
wrap(entry.key, elementType);
} else {
wrap(entry.value, elementType);
}
});
}
b.local_get(mapLocal);
return mapLocal.type;
b.call(target);
return target.type.outputs.single;
}

@override
w.ValueType visitSetLiteral(SetLiteral node, w.ValueType expectedType) {
types.makeType(this, node.typeArgument);
w.ValueType factoryReturnType =
call(translator.setFactory.reference).single;
if (node.expressions.isEmpty) {
return factoryReturnType;
}
w.FunctionType setAddType =
translator.functions.getFunctionType(translator.setAdd.reference);
w.ValueType addReceiverType = setAddType.inputs[0];
w.ValueType addKeyType = setAddType.inputs[1];
w.Local setLocal = addLocal(addReceiverType);
translator.convertType(function, factoryReturnType, setLocal.type);
b.local_set(setLocal);
for (Expression element in node.expressions) {
b.local_get(setLocal);
wrap(element, addKeyType);
call(translator.setAdd.reference);
b.drop();
final useSharedCreator = types.isTypeConstant(node.typeArgument);

final passType = !useSharedCreator;
final passArray = node.expressions.isNotEmpty;

final targetReference = passArray
? translator.setFromWasmArray.reference
: translator.setFactory.reference;

final w.BaseFunction target = useSharedCreator
? translator.partialInstantiator.getOneTypeArgumentForwarder(
targetReference,
node.typeArgument,
'create${passArray ? '' : 'Empty'}Set<${node.typeArgument}>')
: translator.functions.getFunction(targetReference);

if (passType) {
types.makeType(this, node.typeArgument);
}
if (passArray) {
makeArrayFromExpressions(node.expressions,
translator.coreTypes.objectRawType(Nullability.nullable));
}
b.local_get(setLocal);
return setLocal.type;
b.call(target);
return target.type.outputs.single;
}

@override
Expand Down
18 changes: 2 additions & 16 deletions pkg/dart2wasm/lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:math';
import 'dart:typed_data';

import 'package:kernel/ast.dart';
Expand Down Expand Up @@ -519,8 +518,7 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
_uninitializedHashBaseIndexConstant,

// _hashMask
translator.hashFieldBaseHashMaskField.fieldReference:
IntConstant(_computeHashMask(constant.entries.length)),
translator.hashFieldBaseHashMaskField.fieldReference: IntConstant(0),

// _data
translator.hashFieldBaseDataField.fieldReference:
Expand Down Expand Up @@ -553,8 +551,7 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
_uninitializedHashBaseIndexConstant,

// _hashMask
translator.hashFieldBaseHashMaskField.fieldReference:
IntConstant(_computeHashMask(constant.entries.length)),
translator.hashFieldBaseHashMaskField.fieldReference: IntConstant(0),

// _data
translator.hashFieldBaseDataField.fieldReference:
Expand All @@ -576,17 +573,6 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
return ensureConstant(instanceConstant);
}

int _computeHashMask(int entries) {
// This computation of the hash mask follows the computations in
// [_ImmutableLinkedHashMapMixin._createIndex],
// [_ImmutableLinkedHashSetMixin._createIndex] and
// [_HashBase._indexSizeToHashMask].
const int initialIndexSize = 8;
final int indexSize = max(entries * 2, initialIndexSize);
final int hashMask = (1 << (31 - (indexSize - 1).bitLength)) - 1;
return hashMask;
}

@override
ConstantInfo? visitStaticTearOffConstant(StaticTearOffConstant constant) {
Procedure member = constant.targetReference.asProcedure;
Expand Down
20 changes: 8 additions & 12 deletions pkg/dart2wasm/lib/kernel_nodes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -174,20 +174,16 @@ mixin KernelNodes {
// dart:collection procedures and fields
late final Procedure mapFactory =
index.getProcedure("dart:collection", "LinkedHashMap", "_default");
late final Procedure mapPut = index
.getClass("dart:collection", "_WasmDefaultMap")
.superclass! // _LinkedHashMapMixin<K, V>
.procedures
.firstWhere((p) => p.name.text == "[]=");
late final Procedure mapFromWasmArray =
index.getProcedure("dart:collection", "_WasmDefaultMap", "fromWasmArray");
late final Procedure setFactory =
index.getProcedure("dart:collection", "LinkedHashSet", "_default");
late final Procedure setAdd = index
.getClass("dart:collection", "_WasmDefaultSet")
.superclass! // _LinkedHashSetMixin<K, V>
.procedures
.firstWhere((p) => p.name.text == "add");
late final Procedure growableListAdd =
index.getProcedure("dart:core", "_GrowableList", "add");
late final Procedure setFromWasmArray =
index.getProcedure("dart:collection", "_WasmDefaultSet", "fromWasmArray");
late final Procedure growableListEmpty =
index.getProcedure("dart:core", "_GrowableList", "empty");
late final Constructor growableListFromWasmArray =
index.getConstructor("dart:core", "_GrowableList", "_withData");
late final Procedure hashImmutableIndexNullable = index.getProcedure(
"dart:collection", "_HashAbstractImmutableBase", "get:_indexNullable");
late final Field hashFieldBaseIndexField =
Expand Down
116 changes: 93 additions & 23 deletions pkg/dart2wasm/lib/translator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ class Translator with KernelNodes {
late final w.RefType nullableObjectArrayTypeRef =
w.RefType.def(nullableObjectArrayType, nullable: false);

late final PartialInstantiator partialInstantiator =
PartialInstantiator(this);

/// Dart types that have specialized Wasm representations.
late final Map<Class, w.StorageType> builtinTypes = {
coreTypes.boolClass: w.NumType.i32,
Expand Down Expand Up @@ -994,29 +997,6 @@ class Translator with KernelNodes {
return null;
}

w.ValueType makeList(
w.FunctionBuilder function,
void Function(w.InstructionsBuilder b) generateType,
int length,
void Function(w.ValueType, int) generateItem,
{bool isGrowable = false}) {
final b = function.body;

final Class cls = isGrowable ? growableListClass : fixedLengthListClass;
final ClassInfo info = classInfo[cls]!;
functions.recordClassAllocation(info.classId);
final w.ArrayType arrayType = listArrayType;

b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
generateType(b);
b.i64_const(length);
makeArray(function, arrayType, length, generateItem);
b.struct_new(info.struct);

return info.nonNullableType;
}

w.ValueType makeArray(w.FunctionBuilder function, w.ArrayType arrayType,
int length, void Function(w.ValueType, int) generateItem) {
final b = function.body;
Expand Down Expand Up @@ -1356,3 +1336,93 @@ class NodeCounter extends VisitorDefault<void> with VisitorVoidMixin {
node.visitChildren(this);
}
}

/// Creates forwarders for generic functions where the caller passes a constant
/// type argument.
///
/// Let's say we have
///
/// foo<T>(args) => ...;
///
/// and 3 call sites
///
/// foo<int>(args)
/// foo<int>(args)
/// foo<double>(args)
///
/// the callsites can instead call a forwarder
///
/// fooInt(args)
/// fooInt(args)
/// fooDouble(args)
///
/// fooInt(args) => foo<int>(args)
/// fooDouble(args) => foo<double>(args)
///
/// This saves code size on the call site.
class PartialInstantiator {
final Translator translator;

final Map<(Reference, DartType), w.BaseFunction> _oneTypeArgument = {};
final Map<(Reference, DartType, DartType), w.BaseFunction> _twoTypeArguments =
{};

PartialInstantiator(this.translator);

w.BaseFunction getOneTypeArgumentForwarder(
Reference target, DartType type, String name) {
assert(translator.types.isTypeConstant(type));

return _oneTypeArgument.putIfAbsent((target, type), () {
final wasmTarget = translator.functions.getFunction(target);

final function = translator.m.functions.define(
translator.m.types.defineFunction(
[...wasmTarget.type.inputs.skip(1)],
wasmTarget.type.outputs,
),
name);
final b = function.body;
translator.constants.instantiateConstant(function, b,
TypeLiteralConstant(type), translator.types.nonNullableTypeType);
for (int i = 1; i < wasmTarget.type.inputs.length; ++i) {
b.local_get(b.locals[i - 1]);
}
b.call(wasmTarget);
b.return_();
b.end();

return function;
});
}

w.BaseFunction getTwoTypeArgumentForwarder(
Reference target, DartType type1, DartType type2, String name) {
assert(translator.types.isTypeConstant(type1));
assert(translator.types.isTypeConstant(type2));

return _twoTypeArguments.putIfAbsent((target, type1, type2), () {
final wasmTarget = translator.functions.getFunction(target);

final function = translator.m.functions.define(
translator.m.types.defineFunction(
[...wasmTarget.type.inputs.skip(2)],
wasmTarget.type.outputs,
),
name);
final b = function.body;
translator.constants.instantiateConstant(function, b,
TypeLiteralConstant(type1), translator.types.nonNullableTypeType);
translator.constants.instantiateConstant(function, b,
TypeLiteralConstant(type2), translator.types.nonNullableTypeType);
for (int i = 2; i < wasmTarget.type.inputs.length; ++i) {
b.local_get(b.locals[i - 2]);
}
b.call(wasmTarget);
b.return_();
b.end();

return function;
});
}
}
Loading

0 comments on commit 09a1a1e

Please sign in to comment.