[js_runtime] Faster List.of copying a JSArray

Improved inferrer to handle some cases of JS-fragments.
The JS-fragment to copy the array caused the inferrer to be too
pessimistic about list-tracing into List.of.


Change-Id: I0cb4f7610d971d7927d973bd2b0b532d4cef9a7e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/173883
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
This commit is contained in:
Stephen Adams 2020-12-02 07:53:47 +00:00 committed by commit-bot@chromium.org
parent 0d145f836b
commit f0be731480
7 changed files with 93 additions and 20 deletions

View file

@ -6,6 +6,7 @@ library compiler.src.inferrer.list_tracer;
import '../common/names.dart';
import '../elements/entities.dart';
import '../native/behavior.dart';
import '../universe/selector.dart' show Selector;
import '../util/util.dart' show Setlet;
import 'node_tracer.dart';
@ -162,10 +163,16 @@ class ListTracerVisitor extends TracerVisitor {
@override
visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) {
super.visitStaticCallSiteTypeInformation(info);
final commonElements = inferrer.closedWorld.commonElements;
MemberEntity called = info.calledElement;
if (inferrer.closedWorld.commonElements.isForeign(called) &&
called.name == Identifiers.JS) {
bailout('Used in JS ${info.debugName}');
if (commonElements.isForeign(called) && called.name == Identifiers.JS) {
NativeBehavior nativeBehavior = inferrer.closedWorld.elementMap
.getNativeBehaviorForJsCall(info.invocationNode);
// Assume side-effects means that the list has escaped to some unknown
// location.
if (nativeBehavior.sideEffects.hasSideEffects()) {
bailout('Used in JS ${info.debugName}');
}
}
}

View file

@ -968,6 +968,8 @@ class StaticCallSiteTypeInformation extends CallSiteTypeInformation {
: super(abstractValueDomain, context, call, enclosing, selector,
arguments, inLoop);
ir.StaticInvocation get invocationNode => _call as ir.StaticInvocation;
MemberTypeInformation _getCalledTypeInfo(InferrerEngine inferrer) {
return inferrer.types.getInferredTypeOfMember(calledElement);
}

View file

@ -51,22 +51,22 @@ testStoredInInstance() {
return res;
}
/*member: testStoredInMapOfList:[null|subclass=Object]*/
/*member: testStoredInMapOfList:[null|exact=JSUInt31]*/
testStoredInMapOfList() {
var res;
/*[null|subclass=Object]*/ closure(/*[null|subclass=Object]*/ a) => res = a;
/*[exact=JSUInt31]*/ closure(/*[exact=JSUInt31]*/ a) => res = a;
dynamic a = <dynamic>[closure];
dynamic b = <dynamic, dynamic>{'foo': 1};
b
/*update: Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSExtendableArray], [exact=JSUInt31]), map: {foo: [exact=JSUInt31], bar: Container([null|exact=JSExtendableArray], element: [null|subclass=Object], length: null)})*/
/*update: Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSExtendableArray], [exact=JSUInt31]), map: {foo: [exact=JSUInt31], bar: Container([null|exact=JSExtendableArray], element: [subclass=Closure], length: 1)})*/
['bar'] = a;
b
/*Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSExtendableArray], [exact=JSUInt31]), map: {foo: [exact=JSUInt31], bar: Container([null|exact=JSExtendableArray], element: [null|subclass=Object], length: null)})*/
/*Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSExtendableArray], [exact=JSUInt31]), map: {foo: [exact=JSUInt31], bar: Container([null|exact=JSExtendableArray], element: [subclass=Closure], length: 1)})*/
['bar']
/*Container([null|exact=JSExtendableArray], element: [null|subclass=Object], length: null)*/
/*Container([null|exact=JSExtendableArray], element: [subclass=Closure], length: 1)*/
[0](42);
return res;
}

View file

@ -0,0 +1,60 @@
// Copyright (c) 2020, 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.
// Test effect of NativeBehavior on list tracing.
/// ignore: IMPORT_INTERNAL_LIBRARY
import 'dart:_foreign_helper' show JS;
/*member: main:[null]*/
main() {
test1();
test2();
test3();
test4();
}
/*member: test1:[null]*/
test1() {
var list = [42];
JS('', '#', list); // '#' is by default a no-op.
witness1(list);
}
/*member: witness1:[null]*/
witness1(
/*Container([exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)*/ x) {}
/*member: test2:[null]*/
test2() {
var list = [42];
JS('effects:all;depends:all', '#', list);
witness2(list);
}
/*member: witness2:[null]*/
witness2(
/*Container([exact=JSExtendableArray], element: [null|subclass=Object], length: null)*/ x) {}
/*member: test3:[null]*/
test3() {
var list = [42];
JS('', '#.slice(0)', list);
witness3(list);
}
/*member: witness3:[null]*/
witness3(
/*Container([exact=JSExtendableArray], element: [null|subclass=Object], length: null)*/ x) {}
/*member: test4:[null]*/
test4() {
var list = [42];
JS('effects:none;depends:all', '#.slice(0)', list);
witness4(list);
}
/*member: witness4:[null]*/
witness4(
/*Container([exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)*/ x) {}

View file

@ -79,28 +79,28 @@ test2() {
/*member: aDouble3:Union(null, [exact=JSDouble], [exact=JSExtendableArray])*/
dynamic aDouble3 = 42.5;
/*member: aList3:Container([exact=JSExtendableArray], element: [null|subclass=Object], length: null)*/
/*member: aList3:Container([exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)*/
dynamic aList3 = [42];
/*member: consume3:Container([exact=JSExtendableArray], element: [null|subclass=Object], length: null)*/
/*member: consume3:Container([exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)*/
consume3(
/*Container([exact=JSExtendableArray], element: [null|subclass=Object], length: null)*/ x) =>
/*Container([exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)*/ x) =>
x;
/*member: test3:[null]*/
test3() {
dynamic theMap = <dynamic, dynamic>{'a': 2.2, 'b': 3.3, 'c': 4.4};
theMap
/*update: Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSDouble], [exact=JSExtendableArray]), map: {a: [exact=JSDouble], b: [exact=JSDouble], c: [exact=JSDouble], d: Container([null|exact=JSExtendableArray], element: [null|subclass=Object], length: null)})*/
/*update: Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSDouble], [exact=JSExtendableArray]), map: {a: [exact=JSDouble], b: [exact=JSDouble], c: [exact=JSDouble], d: Container([null|exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)})*/
['d'] = aList3;
/*iterator: [exact=LinkedHashMapKeyIterable]*/
/*current: [exact=LinkedHashMapKeyIterator]*/
/*moveNext: [exact=LinkedHashMapKeyIterator]*/
for (var key in theMap.
/*Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSDouble], [exact=JSExtendableArray]), map: {a: [exact=JSDouble], b: [exact=JSDouble], c: [exact=JSDouble], d: Container([null|exact=JSExtendableArray], element: [null|subclass=Object], length: null)})*/
/*Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSDouble], [exact=JSExtendableArray]), map: {a: [exact=JSDouble], b: [exact=JSDouble], c: [exact=JSDouble], d: Container([null|exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)})*/
keys) {
aDouble3 = theMap
/*Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSDouble], [exact=JSExtendableArray]), map: {a: [exact=JSDouble], b: [exact=JSDouble], c: [exact=JSDouble], d: Container([null|exact=JSExtendableArray], element: [null|subclass=Object], length: null)})*/
/*Dictionary([subclass=JsLinkedHashMap], key: [exact=JSString], value: Union(null, [exact=JSDouble], [exact=JSExtendableArray]), map: {a: [exact=JSDouble], b: [exact=JSDouble], c: [exact=JSDouble], d: Container([null|exact=JSExtendableArray], element: [exact=JSUInt31], length: 1)})*/
[key];
}
// We have to reference it somewhere, so that it always gets resolved.

View file

@ -237,9 +237,7 @@ doTest(String allocation, {bool nullify}) async {
checkType('listEscapingInSetterValue', commonMasks.numType);
checkType('listEscapingInIndex', commonMasks.numType);
checkType('listEscapingInIndexSet', commonMasks.uint31Type);
// TODO(johnniwinther): Since Iterable.iterableToString is part of the closed
// world we find the `dynamicType` instead of `numType`.
checkType('listEscapingTwiceInIndexSet', commonMasks.dynamicType);
checkType('listEscapingTwiceInIndexSet', commonMasks.numType);
checkType('listSetInNonFinalField', commonMasks.numType);
checkType('listWithChangedLength', commonMasks.uint31Type.nullable());

View file

@ -26,7 +26,7 @@ import 'dart:_js_helper'
getTraceFromException,
RuntimeError;
import 'dart:_foreign_helper' show JS, JS_GET_FLAG;
import 'dart:_foreign_helper' show JS;
import 'dart:_native_typed_data' show NativeUint8List;
import 'dart:_rti' show getRuntimeType;
@ -459,9 +459,15 @@ class List<E> {
assertUnreachable();
}
factory List._ofArray(Iterable<E> elements) {
return JSArray<E>.markGrowable(
JS('effects:none;depends:no-static', '#.slice(0)', elements));
}
factory List._of(Iterable<E> elements) {
// This is essentially `addAll`, but without a check for modifiability or
// ConcurrentModificationError on the receiver.
if (elements is JSArray) return List._ofArray(elements);
// This is essentially `<E>[]..addAll(elements)`, but without a check for
// modifiability or ConcurrentModificationError on the receiver.
List<E> list = <E>[];
for (final e in elements) {
list.add(e);