mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[dart2wasm] Transform List factory calls to implementation class calls
This is mainly used in [1] to allow using unboxed int lists when a factory type argument is `int`, which then allows inlining unboxed int list `[]` and `[]=` and storing and loading `int` values unboxed. This implementation is mostly a copy of VM's transformer with the same name. However we can't reuse VM's pass as we do different transformations in [1]. [1]: https://dart-review.googlesource.com/c/sdk/+/318680 Change-Id: I16c06fc2b2edb1a5498807fc5c0fee839c003965 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/318921 Reviewed-by: Aske Simon Christensen <askesc@google.com> Commit-Queue: Ömer Ağacan <omersa@google.com> Reviewed-by: Joshua Litt <joshualitt@google.com>
This commit is contained in:
parent
3df48faff1
commit
605b5a2e61
214
pkg/dart2wasm/lib/list_factory_specializer.dart
Normal file
214
pkg/dart2wasm/lib/list_factory_specializer.dart
Normal file
|
@ -0,0 +1,214 @@
|
|||
// Copyright (c) 2023, 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/core_types.dart' show CoreTypes;
|
||||
|
||||
/// Replaces invocation of `List` factory constructors with factories of
|
||||
/// dart2wasm-specific classes.
|
||||
///
|
||||
/// ```
|
||||
/// List.empty() => _List.empty()
|
||||
/// List.empty(growable: false) => _List.empty()
|
||||
/// List.empty(growable: true) => _GrowableList.empty()
|
||||
/// List.filled(n, null, growable: true) => _GrowableList(n)
|
||||
/// List.filled(n, x, growable: true) => _GrowableList.filled(n, x)
|
||||
/// List.filled(n, null) => _List(n)
|
||||
/// List.filled(n, x) => _List.filled(n, x)
|
||||
/// List.generate(n, y) => _GrowableList.generate(n, y)
|
||||
/// List.generate(n, y, growable: false) => _List.generate(n, y)
|
||||
/// ```
|
||||
class ListFactorySpecializer {
|
||||
final Map<Member, TreeNode Function(StaticInvocation node)> _transformers =
|
||||
{};
|
||||
|
||||
final Procedure _fixedListEmptyFactory;
|
||||
final Procedure _fixedListFactory;
|
||||
final Procedure _fixedListFilledFactory;
|
||||
final Procedure _fixedListGenerateFactory;
|
||||
final Procedure _growableListEmptyFactory;
|
||||
final Procedure _growableListFactory;
|
||||
final Procedure _growableListFilledFactory;
|
||||
final Procedure _growableListGenerateFactory;
|
||||
final Procedure _listEmptyFactory;
|
||||
final Procedure _listFilledFactory;
|
||||
final Procedure _listGenerateFactory;
|
||||
|
||||
ListFactorySpecializer(CoreTypes coreTypes)
|
||||
: _listEmptyFactory =
|
||||
coreTypes.index.getProcedure('dart:core', 'List', 'empty'),
|
||||
_listFilledFactory =
|
||||
coreTypes.index.getProcedure('dart:core', 'List', 'filled'),
|
||||
_listGenerateFactory =
|
||||
coreTypes.index.getProcedure('dart:core', 'List', 'generate'),
|
||||
_growableListFactory =
|
||||
coreTypes.index.getProcedure('dart:core', '_GrowableList', ''),
|
||||
_growableListEmptyFactory =
|
||||
coreTypes.index.getProcedure('dart:core', '_GrowableList', 'empty'),
|
||||
_growableListFilledFactory = coreTypes.index
|
||||
.getProcedure('dart:core', '_GrowableList', 'filled'),
|
||||
_growableListGenerateFactory = coreTypes.index
|
||||
.getProcedure('dart:core', '_GrowableList', 'generate'),
|
||||
_fixedListFactory =
|
||||
coreTypes.index.getProcedure('dart:core', '_List', ''),
|
||||
_fixedListEmptyFactory =
|
||||
coreTypes.index.getProcedure('dart:core', '_List', 'empty'),
|
||||
_fixedListFilledFactory =
|
||||
coreTypes.index.getProcedure('dart:core', '_List', 'filled'),
|
||||
_fixedListGenerateFactory =
|
||||
coreTypes.index.getProcedure('dart:core', '_List', 'generate') {
|
||||
_transformers[_listFilledFactory] = _transformListFilledFactory;
|
||||
_transformers[_listEmptyFactory] = _transformListEmptyFactory;
|
||||
_transformers[_listGenerateFactory] = _transformListGenerateFactory;
|
||||
}
|
||||
|
||||
TreeNode transformStaticInvocation(StaticInvocation invocation) {
|
||||
final target = invocation.target;
|
||||
final transformer = _transformers[target];
|
||||
if (transformer != null) {
|
||||
return transformer(invocation);
|
||||
}
|
||||
return invocation;
|
||||
}
|
||||
|
||||
// List.filled(n, null, growable: true) => _GrowableList(n)
|
||||
// List.filled(n, x, growable: true) => _GrowableList.filled(n, x)
|
||||
// List.filled(n, null) => _List(n)
|
||||
// List.filled(n, x) => _List.filled(n, x)
|
||||
TreeNode _transformListFilledFactory(StaticInvocation node) {
|
||||
final args = node.arguments;
|
||||
assert(args.positional.length == 2);
|
||||
final length = args.positional[0];
|
||||
final fill = args.positional[1];
|
||||
final fillingWithNull = _isNullConstant(fill);
|
||||
|
||||
// Null when the argument is not a constant or a `bool` literal, e.g.
|
||||
// `List.filled(..., growable: f())`.
|
||||
final bool? growable =
|
||||
_getConstantOptionalArgument(args, 'growable', false);
|
||||
|
||||
if (growable == null) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (growable) {
|
||||
if (fillingWithNull) {
|
||||
// List.filled(n, null, growable: true) => _GrowableList(n)
|
||||
return StaticInvocation(
|
||||
_growableListFactory, Arguments([length], types: args.types))
|
||||
..fileOffset = node.fileOffset;
|
||||
} else {
|
||||
// List.filled(n, x, growable: true) => _GrowableList.filled(n, x)
|
||||
return StaticInvocation(_growableListFilledFactory,
|
||||
Arguments([length, fill], types: args.types))
|
||||
..fileOffset = node.fileOffset;
|
||||
}
|
||||
} else {
|
||||
if (fillingWithNull) {
|
||||
// List.filled(n, null, growable: false) => _List(n)
|
||||
return StaticInvocation(
|
||||
_fixedListFactory, Arguments([length], types: args.types))
|
||||
..fileOffset = node.fileOffset;
|
||||
} else {
|
||||
// List.filled(n, x, growable: false) => _List.filled(n, x)
|
||||
return StaticInvocation(_fixedListFilledFactory,
|
||||
Arguments([length, fill], types: args.types))
|
||||
..fileOffset = node.fileOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// List.empty() => _List.empty()
|
||||
// List.empty(growable: false) => _List.empty()
|
||||
// List.empty(growable: true) => _GrowableList.empty()
|
||||
TreeNode _transformListEmptyFactory(StaticInvocation node) {
|
||||
final args = node.arguments;
|
||||
assert(args.positional.isEmpty);
|
||||
final bool? growable =
|
||||
_getConstantOptionalArgument(args, 'growable', false);
|
||||
if (growable == null) {
|
||||
return node;
|
||||
}
|
||||
if (growable) {
|
||||
// List.empty(growable: true) => _GrowableList.empty()
|
||||
return StaticInvocation(
|
||||
_growableListEmptyFactory, Arguments([], types: args.types))
|
||||
..fileOffset = node.fileOffset;
|
||||
} else {
|
||||
// List.empty() => _List.empty()
|
||||
// List.empty(growable: false) => _List.empty()
|
||||
return StaticInvocation(
|
||||
_fixedListEmptyFactory, Arguments([], types: args.types))
|
||||
..fileOffset = node.fileOffset;
|
||||
}
|
||||
}
|
||||
|
||||
// List.generate(n, y) => _GrowableList.generate(n, y)
|
||||
// List.generate(n, y, growable: false) => _List.generate(n, y)
|
||||
TreeNode _transformListGenerateFactory(StaticInvocation node) {
|
||||
final args = node.arguments;
|
||||
assert(args.positional.length == 2);
|
||||
final length = args.positional[0];
|
||||
final generator = args.positional[1];
|
||||
final bool? growable = _getConstantOptionalArgument(args, 'growable', true);
|
||||
if (growable == null) {
|
||||
return node;
|
||||
}
|
||||
if (growable) {
|
||||
// List.generate(n, y) => _GrowableList.generate(n, y)
|
||||
return StaticInvocation(_growableListGenerateFactory,
|
||||
Arguments([length, generator], types: args.types))
|
||||
..fileOffset = node.fileOffset;
|
||||
} else {
|
||||
// List.generate(n, y, growable: false) => _List.generate(n, y)
|
||||
return StaticInvocation(_fixedListGenerateFactory,
|
||||
Arguments([length, generator], types: args.types))
|
||||
..fileOffset = node.fileOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns constant value of the only optional argument in [args], or null
|
||||
/// if it is not a constant. Returns [defaultValue] if optional argument is
|
||||
/// not passed. Argument is asserted to have the given [name].
|
||||
bool? _getConstantOptionalArgument(
|
||||
Arguments args, String name, bool defaultValue) {
|
||||
if (args.named.isEmpty) {
|
||||
return defaultValue;
|
||||
}
|
||||
final namedArg = args.named.single;
|
||||
assert(namedArg.name == name);
|
||||
final value = _unwrapFinalVariableGet(namedArg.value);
|
||||
if (value is BoolLiteral) {
|
||||
return value.value;
|
||||
} else if (value is ConstantExpression) {
|
||||
final constant = value.constant;
|
||||
if (constant is BoolConstant) {
|
||||
return constant.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool _isNullConstant(Expression value) {
|
||||
value = _unwrapFinalVariableGet(value);
|
||||
return value is NullLiteral ||
|
||||
(value is ConstantExpression && value.constant is NullConstant);
|
||||
}
|
||||
|
||||
// Front-end can create extra temporary variables ("Let v = e, call(v)") to
|
||||
// hoist expressions when rearraning named parameters. Unwrap such variables
|
||||
// and return their initializers.
|
||||
Expression _unwrapFinalVariableGet(Expression expr) {
|
||||
if (expr is VariableGet) {
|
||||
final variable = expr.variable;
|
||||
if (variable.isFinal) {
|
||||
final initializer = variable.initializer;
|
||||
if (initializer != null) {
|
||||
return initializer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
|
@ -9,6 +9,8 @@ import 'package:kernel/target/targets.dart';
|
|||
import 'package:kernel/type_environment.dart';
|
||||
import 'package:kernel/type_algebra.dart';
|
||||
|
||||
import 'package:dart2wasm/list_factory_specializer.dart';
|
||||
|
||||
void transformLibraries(List<Library> libraries, CoreTypes coreTypes,
|
||||
ClassHierarchy hierarchy, DiagnosticReporter diagnosticReporter) {
|
||||
final transformer =
|
||||
|
@ -42,6 +44,8 @@ class _WasmTransformer extends Transformer {
|
|||
Nullability.nonNullable,
|
||||
[coreTypes.boolNonNullableRawType]);
|
||||
|
||||
final ListFactorySpecializer _listFactorySpecializer;
|
||||
|
||||
StaticTypeContext get typeContext =>
|
||||
_cachedTypeContext ??= StaticTypeContext(_currentMember!, env);
|
||||
|
||||
|
@ -54,7 +58,8 @@ class _WasmTransformer extends Transformer {
|
|||
.getClass('dart:core', '_Type')
|
||||
.getThisType(coreTypes, Nullability.nonNullable),
|
||||
_wasmBaseClass = coreTypes.index.getClass('dart:_wasm', '_WasmBase'),
|
||||
_coreLibrary = coreTypes.index.getLibrary('dart:core');
|
||||
_coreLibrary = coreTypes.index.getLibrary('dart:core'),
|
||||
_listFactorySpecializer = ListFactorySpecializer(coreTypes);
|
||||
|
||||
@override
|
||||
defaultMember(Member node) {
|
||||
|
@ -696,6 +701,12 @@ class _WasmTransformer extends Transformer {
|
|||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitStaticInvocation(StaticInvocation node) {
|
||||
node.transformChildren(this);
|
||||
return _listFactorySpecializer.transformStaticInvocation(node);
|
||||
}
|
||||
}
|
||||
|
||||
class _AsyncStarFrame {
|
||||
|
|
|
@ -6,6 +6,83 @@ part of "core_patch.dart";
|
|||
|
||||
@pragma("wasm:entry-point")
|
||||
class _GrowableList<E> extends _ModifiableList<E> {
|
||||
_GrowableList._(int length, int capacity) : super(length, capacity);
|
||||
|
||||
_GrowableList._withData(WasmObjectArray<Object?> data)
|
||||
: super._withData(data.length, data);
|
||||
|
||||
factory _GrowableList(int length) {
|
||||
return _GrowableList<E>._(length, length);
|
||||
}
|
||||
|
||||
factory _GrowableList.withCapacity(int capacity) {
|
||||
return _GrowableList<E>._(0, capacity);
|
||||
}
|
||||
|
||||
// Specialization of List.empty constructor for growable == true.
|
||||
// Used by pkg/dart2wasm/lib/list_factory_specializer.dart.
|
||||
factory _GrowableList.empty() => _GrowableList(0);
|
||||
|
||||
// Specialization of List.filled constructor for growable == true.
|
||||
// Used by pkg/dart2wasm/lib/list_factory_specializer.dart.
|
||||
factory _GrowableList.filled(int length, E fill) {
|
||||
final result = _GrowableList<E>(length);
|
||||
if (fill != null) {
|
||||
result._data.fill(0, fill, length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Specialization of List.generate constructor for growable == true.
|
||||
// Used by pkg/dart2wasm/lib/list_factory_specializer.dart.
|
||||
factory _GrowableList.generate(int length, E generator(int index)) {
|
||||
final result = _GrowableList<E>(length);
|
||||
for (int i = 0; i < result.length; ++i) {
|
||||
result._data.write(i, generator(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Specialization of List.of constructor for growable == true.
|
||||
factory _GrowableList.of(Iterable<E> elements) {
|
||||
if (elements is _ListBase) {
|
||||
return _GrowableList._ofListBase(unsafeCast(elements));
|
||||
}
|
||||
if (elements is EfficientLengthIterable) {
|
||||
return _GrowableList._ofEfficientLengthIterable(unsafeCast(elements));
|
||||
}
|
||||
return _GrowableList._ofOther(elements);
|
||||
}
|
||||
|
||||
factory _GrowableList._ofListBase(_ListBase<E> elements) {
|
||||
final int length = elements.length;
|
||||
final list = _GrowableList<E>(length);
|
||||
list._data.copy(0, elements._data, 0, length);
|
||||
return list;
|
||||
}
|
||||
|
||||
factory _GrowableList._ofEfficientLengthIterable(
|
||||
EfficientLengthIterable<E> elements) {
|
||||
final int length = elements.length;
|
||||
final list = _GrowableList<E>(length);
|
||||
if (length > 0) {
|
||||
int i = 0;
|
||||
for (var element in elements) {
|
||||
list[i++] = element;
|
||||
}
|
||||
if (i != length) throw ConcurrentModificationError(elements);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
factory _GrowableList._ofOther(Iterable<E> elements) {
|
||||
final list = _GrowableList<E>(0);
|
||||
for (var elements in elements) {
|
||||
list.add(elements);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void insert(int index, E element) {
|
||||
if (index == length) {
|
||||
return add(element);
|
||||
|
@ -89,83 +166,6 @@ class _GrowableList<E> extends _ModifiableList<E> {
|
|||
this.length = this.length - (end - start);
|
||||
}
|
||||
|
||||
_GrowableList._(int length, int capacity) : super(length, capacity);
|
||||
|
||||
factory _GrowableList(int length) {
|
||||
return _GrowableList<E>._(length, length);
|
||||
}
|
||||
|
||||
factory _GrowableList.withCapacity(int capacity) {
|
||||
return _GrowableList<E>._(0, capacity);
|
||||
}
|
||||
|
||||
// Specialization of List.empty constructor for growable == true.
|
||||
// Used by pkg/vm/lib/transformations/list_factory_specializer.dart.
|
||||
factory _GrowableList.empty() => _GrowableList(0);
|
||||
|
||||
// Specialization of List.filled constructor for growable == true.
|
||||
// Used by pkg/vm/lib/transformations/list_factory_specializer.dart.
|
||||
factory _GrowableList.filled(int length, E fill) {
|
||||
final result = _GrowableList<E>(length);
|
||||
if (fill != null) {
|
||||
result._data.fill(0, fill, length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Specialization of List.generate constructor for growable == true.
|
||||
// Used by pkg/vm/lib/transformations/list_factory_specializer.dart.
|
||||
factory _GrowableList.generate(int length, E generator(int index)) {
|
||||
final result = _GrowableList<E>(length);
|
||||
for (int i = 0; i < result.length; ++i) {
|
||||
result._data.write(i, generator(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Specialization of List.of constructor for growable == true.
|
||||
factory _GrowableList.of(Iterable<E> elements) {
|
||||
if (elements is _ListBase) {
|
||||
return _GrowableList._ofListBase(unsafeCast(elements));
|
||||
}
|
||||
if (elements is EfficientLengthIterable) {
|
||||
return _GrowableList._ofEfficientLengthIterable(unsafeCast(elements));
|
||||
}
|
||||
return _GrowableList._ofOther(elements);
|
||||
}
|
||||
|
||||
factory _GrowableList._ofListBase(_ListBase<E> elements) {
|
||||
final int length = elements.length;
|
||||
final list = _GrowableList<E>(length);
|
||||
list._data.copy(0, elements._data, 0, length);
|
||||
return list;
|
||||
}
|
||||
|
||||
factory _GrowableList._ofEfficientLengthIterable(
|
||||
EfficientLengthIterable<E> elements) {
|
||||
final int length = elements.length;
|
||||
final list = _GrowableList<E>(length);
|
||||
if (length > 0) {
|
||||
int i = 0;
|
||||
for (var element in elements) {
|
||||
list[i++] = element;
|
||||
}
|
||||
if (i != length) throw ConcurrentModificationError(elements);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
factory _GrowableList._ofOther(Iterable<E> elements) {
|
||||
final list = _GrowableList<E>(0);
|
||||
for (var elements in elements) {
|
||||
list.add(elements);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
_GrowableList._withData(WasmObjectArray<Object?> data)
|
||||
: super._withData(data.length, data);
|
||||
|
||||
int get _capacity => _data.length;
|
||||
|
||||
void set length(int new_length) {
|
||||
|
|
|
@ -114,11 +114,11 @@ class _List<E> extends _ModifiableList<E> with FixedLengthListMixin<E> {
|
|||
factory _List(int length) => _List._(length);
|
||||
|
||||
// Specialization of List.empty constructor for growable == false.
|
||||
// Used by pkg/vm/lib/transformations/list_factory_specializer.dart.
|
||||
// Used by pkg/dart2wasm/lib/list_factory_specializer.dart.
|
||||
factory _List.empty() => _List<E>(0);
|
||||
|
||||
// Specialization of List.filled constructor for growable == false.
|
||||
// Used by pkg/vm/lib/transformations/list_factory_specializer.dart.
|
||||
// Used by pkg/dart2wasm/lib/list_factory_specializer.dart.
|
||||
factory _List.filled(int length, E fill) {
|
||||
final result = _List<E>(length);
|
||||
if (fill != null) {
|
||||
|
@ -128,7 +128,7 @@ class _List<E> extends _ModifiableList<E> with FixedLengthListMixin<E> {
|
|||
}
|
||||
|
||||
// Specialization of List.generate constructor for growable == false.
|
||||
// Used by pkg/vm/lib/transformations/list_factory_specializer.dart.
|
||||
// Used by pkg/dart2wasm/lib/list_factory_specializer.dart.
|
||||
factory _List.generate(int length, E generator(int index)) {
|
||||
final result = _List<E>(length);
|
||||
for (int i = 0; i < result.length; ++i) {
|
||||
|
|
Loading…
Reference in a new issue