[dart2wasm] Implement set literals in the backend

Change-Id: I678a6c16248bfafce2e39c6c3e39505109250f35
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/242861
Reviewed-by: Joshua Litt <joshualitt@google.com>
Commit-Queue: Aske Simon Christensen <askesc@google.com>
This commit is contained in:
Aske Simon Christensen 2022-04-29 15:02:51 +00:00 committed by Commit Bot
parent e31cc74d54
commit c6a061f41d
8 changed files with 113 additions and 80 deletions

View file

@ -1975,6 +1975,34 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
return mapLocal.type;
}
@override
w.ValueType visitSetLiteral(SetLiteral node, w.ValueType expectedType) {
w.BaseFunction setFactory =
translator.functions.getFunction(translator.setFactory.reference);
w.ValueType factoryReturnType = setFactory.type.outputs.single;
types.makeType(this, node.typeArgument, node);
b.call(setFactory);
if (node.expressions.isEmpty) {
return factoryReturnType;
}
w.BaseFunction setAdd =
translator.functions.getFunction(translator.setAdd.reference);
w.ValueType addReceiverType = setAdd.type.inputs[0];
w.ValueType addKeyType = setAdd.type.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);
translator.convertType(function, setLocal.type, addReceiverType);
wrap(element, addKeyType);
b.call(setAdd);
b.drop();
}
b.local_get(setLocal);
return setLocal.type;
}
@override
w.ValueType visitTypeLiteral(TypeLiteral node, w.ValueType expectedType) {
return types.makeType(this, node.type, node);

View file

@ -596,13 +596,6 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
translator.functions.allocateClass(info.classId);
w.RefType type = info.nonNullableType;
return createConstant(constant, type, (function, b) {
// This computation of the hash mask follows the computations in
// [_ImmutableLinkedHashMapMixin._createIndex] and
// [_HashBase._indexSizeToHashMask].
const int initialIndexSize = 8;
final int indexSize = max(dataElements.length, initialIndexSize);
final int hashMask = (1 << (31 - (indexSize - 1).bitLength)) - 1;
w.RefType indexType =
info.struct.fields[FieldIndex.hashBaseIndex].type as w.RefType;
w.RefType dataType =
@ -611,7 +604,7 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
b.ref_null(indexType.heapType); // _index
b.i64_const(hashMask); // _hashMask
b.i64_const(_computeHashMask(constant.entries.length)); // _hashMask
constants.instantiateConstant(function, b, dataList, dataType); // _data
b.i64_const(dataElements.length); // _usedData
b.i64_const(0); // _deletedKeys
@ -623,6 +616,46 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
});
}
@override
ConstantInfo? visitSetConstant(SetConstant constant) {
Constant elementTypeConstant = TypeLiteralConstant(constant.typeArgument);
ensureConstant(elementTypeConstant);
ListConstant dataList = ListConstant(const DynamicType(), constant.entries);
ensureConstant(dataList);
ClassInfo info = translator.classInfo[translator.immutableSetClass]!;
translator.functions.allocateClass(info.classId);
w.RefType type = info.nonNullableType;
return createConstant(constant, type, (function, b) {
w.RefType indexType =
info.struct.fields[FieldIndex.hashBaseIndex].type as w.RefType;
w.RefType dataType =
info.struct.fields[FieldIndex.hashBaseData].type as w.RefType;
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
b.ref_null(indexType.heapType); // _index
b.i64_const(_computeHashMask(constant.entries.length)); // _hashMask
constants.instantiateConstant(function, b, dataList, dataType); // _data
b.i64_const(constant.entries.length); // _usedData
b.i64_const(0); // _deletedKeys
constants.instantiateConstant(
function, b, elementTypeConstant, constants.typeInfo.nullableType);
translator.struct_new(b, info);
});
}
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) {
w.DefinedFunction closureFunction =

View file

@ -1,59 +0,0 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// 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 'package:kernel/ast.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/core_types.dart';
class WasmConstantsBackend extends ConstantsBackend {
final Class unmodifiableSetClass;
final Field unmodifiableSetMap;
WasmConstantsBackend._(this.unmodifiableSetMap, this.unmodifiableSetClass);
factory WasmConstantsBackend(CoreTypes coreTypes) {
Field unmodifiableSetMap =
coreTypes.index.getField('dart:collection', '_UnmodifiableSet', '_map');
return new WasmConstantsBackend._(
unmodifiableSetMap, unmodifiableSetMap.enclosingClass!);
}
@override
Constant lowerSetConstant(SetConstant constant) {
final DartType elementType = constant.typeArgument;
final List<Constant> entries = constant.entries;
final List<ConstantMapEntry> mapEntries =
new List<ConstantMapEntry>.generate(entries.length, (int index) {
return new ConstantMapEntry(entries[index], new NullConstant());
});
Constant map = lowerMapConstant(
new MapConstant(elementType, const NullType(), mapEntries));
return new InstanceConstant(unmodifiableSetClass.reference, [elementType],
<Reference, Constant>{unmodifiableSetMap.getterReference: map});
}
@override
bool isLoweredSetConstant(Constant constant) {
if (constant is InstanceConstant &&
constant.classNode == unmodifiableSetClass) {
InstanceConstant instance = constant;
return isLoweredMapConstant(
instance.fieldValues[unmodifiableSetMap.getterReference]!);
}
return false;
}
@override
void forEachLoweredSetConstantElement(
Constant constant, void Function(Constant element) f) {
assert(isLoweredSetConstant(constant));
final InstanceConstant instance = constant as InstanceConstant;
final Constant mapConstant =
instance.fieldValues[unmodifiableSetMap.getterReference]!;
forEachLoweredMapConstantEntry(mapConstant, (Constant key, Constant value) {
f(key);
});
}
}

View file

@ -168,7 +168,7 @@ class Intrinsifier {
}
// _HashAbstractImmutableBase._indexNullable
if (target == translator.immutableMapIndexNullable) {
if (target == translator.hashImmutableIndexNullable) {
ClassInfo info = translator.classInfo[translator.hashFieldBaseClass]!;
codeGen.wrap(receiver, info.nullableType);
b.struct_get(info.struct, FieldIndex.hashBaseIndex);

View file

@ -21,21 +21,20 @@ import 'package:vm/transformations/ffi/definitions.dart'
import 'package:vm/transformations/ffi/use_sites.dart' as transformFfiUseSites
show transformLibraries;
import 'package:dart2wasm/constants_backend.dart';
import 'package:dart2wasm/transformers.dart' as wasmTrans;
class WasmTarget extends Target {
Class? _growableList;
Class? _immutableList;
Class? _wasmImmutableLinkedHashMap;
Class? _unmodifiableSet;
Class? _wasmImmutableLinkedHashSet;
Class? _compactLinkedCustomHashMap;
Class? _compactLinkedHashSet;
Class? _compactLinkedCustomHashSet;
Class? _oneByteString;
Class? _twoByteString;
@override
late final ConstantsBackend constantsBackend;
ConstantsBackend get constantsBackend => const ConstantsBackend();
@override
String get name => 'wasm';
@ -85,7 +84,6 @@ class WasmTarget extends Target {
DiagnosticReporter diagnosticReporter,
{void Function(String msg)? logger,
ChangedStructureNotifier? changedStructureNotifier}) {
constantsBackend = WasmConstantsBackend(coreTypes);
_patchHostEndian(coreTypes);
}
@ -167,7 +165,7 @@ class WasmTarget extends Target {
}
@override
bool get supportsSetLiterals => false;
bool get supportsSetLiterals => true;
@override
int get enabledLateLowerings => LateLowering.all;
@ -213,14 +211,14 @@ class WasmTarget extends Target {
@override
Class concreteSetLiteralClass(CoreTypes coreTypes) {
return _compactLinkedHashSet ??=
coreTypes.index.getClass('dart:collection', '_CompactLinkedHashSet');
return _compactLinkedCustomHashSet ??= coreTypes.index
.getClass('dart:collection', '_CompactLinkedCustomHashSet');
}
@override
Class concreteConstSetLiteralClass(CoreTypes coreTypes) {
return _unmodifiableSet ??=
coreTypes.index.getClass('dart:collection', '_UnmodifiableSet');
return _wasmImmutableLinkedHashSet ??= coreTypes.index
.getClass('dart:collection', '_WasmImmutableLinkedHashSet');
}
@override

View file

@ -83,6 +83,7 @@ class Translator {
late final Class growableListClass;
late final Class immutableListClass;
late final Class immutableMapClass;
late final Class immutableSetClass;
late final Class hashFieldBaseClass;
late final Class stringBaseClass;
late final Class oneByteStringClass;
@ -105,7 +106,9 @@ class Translator {
late final Procedure throwWasmRefError;
late final Procedure mapFactory;
late final Procedure mapPut;
late final Procedure immutableMapIndexNullable;
late final Procedure setFactory;
late final Procedure setAdd;
late final Procedure hashImmutableIndexNullable;
late final Map<Class, w.StorageType> builtinTypes;
late final Map<w.ValueType, Class> boxedClasses;
@ -183,6 +186,7 @@ class Translator {
growableListClass = lookupCore("_GrowableList");
immutableListClass = lookupCore("_ImmutableList");
immutableMapClass = lookupCollection("_WasmImmutableLinkedHashMap");
immutableSetClass = lookupCollection("_WasmImmutableLinkedHashSet");
hashFieldBaseClass = lookupCollection("_HashFieldBase");
stringBaseClass = lookupCore("_StringBase");
oneByteStringClass = lookupCore("_OneByteString");
@ -216,7 +220,13 @@ class Translator {
.superclass! // _LinkedHashMapMixin<K, V>
.procedures
.firstWhere((p) => p.name.text == "[]=");
immutableMapIndexNullable = lookupCollection("_HashAbstractImmutableBase")
setFactory = lookupCollection("LinkedHashSet").procedures.firstWhere(
(p) => p.kind == ProcedureKind.Factory && p.name.text == "_default");
setAdd = lookupCollection("_CompactLinkedCustomHashSet")
.superclass! // _LinkedHashSetMixin<K, V>
.procedures
.firstWhere((p) => p.name.text == "add");
hashImmutableIndexNullable = lookupCollection("_HashAbstractImmutableBase")
.procedures
.firstWhere((p) => p.name.text == "_indexNullable");
builtinTypes = {

View file

@ -1096,6 +1096,9 @@ class _CompactLinkedCustomHashSet<E> extends _HashFieldBase
E? lookup(Object? o) => _validKey(o) ? super.lookup(o) : null;
bool remove(Object? o) => _validKey(o) ? super.remove(o) : false;
@pragma("wasm:entry-point")
bool add(E key);
_CompactLinkedCustomHashSet(this._equality, this._hasher, validKey)
: _validKey = (validKey != null) ? validKey : new _TypeTest<E>().test;

View file

@ -46,6 +46,10 @@ class LinkedHashSet<E> {
return new _CompactLinkedCustomHashSet<E>(equals, hashCode, isValidKey);
}
@pragma("wasm:entry-point")
factory LinkedHashSet._default() =>
_CompactLinkedCustomHashSet<E>(_defaultEquals, _defaultHashCode, null);
@patch
factory LinkedHashSet.identity() => new _CompactLinkedIdentityHashSet<E>();
}
@ -70,3 +74,19 @@ class _WasmImmutableLinkedHashMap<K, V> extends _HashWasmImmutableBase
"Immutable maps can only be instantiated via constants");
}
}
@pragma("wasm:entry-point")
class _WasmImmutableLinkedHashSet<E> extends _HashWasmImmutableBase
with
SetMixin<E>,
_HashBase,
_OperatorEqualsAndHashCode,
_LinkedHashSetMixin<E>,
_UnmodifiableSetMixin<E>,
_ImmutableLinkedHashSetMixin<E>
implements LinkedHashSet<E> {
factory _WasmImmutableLinkedHashSet._uninstantiable() {
throw new UnsupportedError(
"Immutable sets can only be instantiated via constants");
}
}