Skip to content

Commit 32082be

Browse files
rakudramaCommit Queue
authored andcommitted
[dart2js] invariance optimization for field setters under -O2
Calls to the implicit setters for fields are replaced with field assignments (HFieldSet). This is inhibited when the setter needs to check its argument, which happens when the field type is covariant and the checks not 'trusted', i.e. at optimizations level `-O2` and lower (aka 'spec' mode). This optimization detects when the check is unnecessary due to invariance. If the receiver of a setter instance call to the the implicit setter has `this` as a receiver and the setter does not have an explicit `covariant` declaration, then the check will always pass, so the setter call can be replaced with HFieldSet. This optimization works well for Iterators for generic collections at the assignment to the `_current` field, with some iteration-heavy benchmarks improved by 20-50% at `-O2`. Change-Id: I4f3ffd08b45ba28de721535afc3c7a6d1e486a3d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/405448 Reviewed-by: Nate Biggs <[email protected]> Commit-Queue: Stephen Adams <[email protected]>
1 parent 70fbbd2 commit 32082be

File tree

8 files changed

+169
-8
lines changed

8 files changed

+169
-8
lines changed

pkg/compiler/lib/src/common/elements.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,6 +1738,9 @@ abstract class JElementEnvironment extends ElementEnvironment {
17381738
/// parameter of the Future, Stream or Iterable.
17391739
DartType getFunctionAsyncOrSyncStarElementType(FunctionEntity function);
17401740

1741+
/// Returns `true` if [field] has an explicit `covariant` declaration.
1742+
bool isFieldCovariantByDeclaration(FieldEntity field);
1743+
17411744
/// Calls [f] with every instance field, together with its declarer, in an
17421745
/// instance of [cls]. All fields inherited from superclasses and mixins are
17431746
/// included.

pkg/compiler/lib/src/js_model/closure.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,6 +1472,9 @@ class ClosureFieldData extends ClosureMemberData implements JFieldData {
14721472
return _type = elementMap.getDartType(type);
14731473
}
14741474

1475+
@override
1476+
bool get isCovariantByDeclaration => false;
1477+
14751478
@override
14761479
ClassTypeVariableAccess get classTypeVariableAccess =>
14771480
ClassTypeVariableAccess.none;

pkg/compiler/lib/src/js_model/element_map_impl.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,12 @@ class JsKernelToElementMap implements JsToElementMap, IrToElementMap {
10151015
return data.getFieldType(this);
10161016
}
10171017

1018+
bool _isFieldCovariantByDeclaration(JField field) {
1019+
assert(checkFamily(field));
1020+
final data = members.getData(field) as JFieldData;
1021+
return data.isCovariantByDeclaration;
1022+
}
1023+
10181024
@override
10191025
DartType getTypeVariableBound(JTypeVariable typeVariable) {
10201026
assert(checkFamily(typeVariable));
@@ -2509,6 +2515,11 @@ class JsElementEnvironment extends ElementEnvironment
25092515
return elementMap._getFieldType(field as JField);
25102516
}
25112517

2518+
@override
2519+
bool isFieldCovariantByDeclaration(FieldEntity field) {
2520+
return elementMap._isFieldCovariantByDeclaration(field as JField);
2521+
}
2522+
25122523
@override
25132524
FunctionType getLocalFunctionType(covariant JLocalFunction function) {
25142525
return function.functionType;

pkg/compiler/lib/src/js_model/env.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,7 @@ class ConstructorBodyDataImpl extends FunctionDataImpl {
11241124

11251125
abstract class JFieldData extends JMemberData {
11261126
DartType getFieldType(IrToElementMap elementMap);
1127+
bool get isCovariantByDeclaration;
11271128
}
11281129

11291130
class JFieldDataImpl extends JMemberDataImpl implements JFieldData {
@@ -1163,6 +1164,11 @@ class JFieldDataImpl extends JMemberDataImpl implements JFieldData {
11631164
return _type ??= elementMap.getDartType(node.type);
11641165
}
11651166

1167+
@override
1168+
bool get isCovariantByDeclaration {
1169+
return node.isCovariantByDeclaration;
1170+
}
1171+
11661172
@override
11671173
ClassTypeVariableAccess get classTypeVariableAccess {
11681174
if (node.isInstanceMember) return ClassTypeVariableAccess.instanceField;

pkg/compiler/lib/src/ssa/builder.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7039,7 +7039,19 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault<void>
70397039
if (node is ir.InstanceInvocation) {
70407040
invoke.isInvariant = node.isInvariant;
70417041
invoke.isBoundsSafe = node.isBoundsSafe;
7042+
if (node.receiver is ir.ThisExpression) {
7043+
// If the receiver of an instance invocation is `this` then the call is
7044+
// invariant with respect to the class type variables.
7045+
invoke.isInvariant = true;
7046+
}
7047+
} else if (node is ir.InstanceSet) {
7048+
if (node.receiver is ir.ThisExpression) {
7049+
// If the receiver of an instance invocation is `this` then the call is
7050+
// invariant with respect to the class type variables.
7051+
invoke.isInvariant = true;
7052+
}
70427053
}
7054+
70437055
if (node is ir.InstanceInvocation || node is ir.FunctionInvocation) {
70447056
final staticType =
70457057
_abstractValueDomain

pkg/compiler/lib/src/ssa/nodes.dart

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1910,15 +1910,16 @@ abstract class HInvokeDynamic extends HInvoke implements InstructionContext {
19101910
/// Static type at call-site, often better than union-over-targets.
19111911
AbstractValue? staticType;
19121912

1913-
/// `true` if the type parameters at the call known to be invariant with
1913+
/// `true` if the type parameters at the call are known to be invariant with
19141914
/// respect to the type parameters of the receiver instance. This corresponds
1915-
/// to the [ir.MethodInvocation.isInvariant] property and may be updated with
1916-
/// additional analysis.
1915+
/// to the [ir.InstanceInvocation.isInvariant] property. Parametric
1916+
/// covariance checks of the target may be omitted. If the target has explicit
1917+
/// `covariant` checks, these might still need to be checked.
19171918
bool isInvariant = false;
19181919

19191920
/// `true` for an indexed getter or setter if the index is known to be in
1920-
/// range. This corresponds to the [ir.MethodInvocation.isBoundsSafe] property
1921-
/// but and may updated with additional analysis.
1921+
/// range. This corresponds to the [ir.InstanceInvocation.isBoundsSafe]
1922+
/// property but and may updated with additional analysis.
19221923
bool isBoundsSafe = false;
19231924

19241925
// Cached target when non-nullable receiver type and selector determine a

pkg/compiler/lib/src/ssa/optimize.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2147,6 +2147,13 @@ class SsaInstructionSimplifier extends HBaseVisitor<HInstruction>
21472147
}
21482148
}
21492149

2150+
if (node.isInvariant &&
2151+
!_closedWorld.elementEnvironment.isFieldCovariantByDeclaration(
2152+
member,
2153+
)) {
2154+
return assignField();
2155+
}
2156+
21502157
if (!_closedWorld.annotationsData
21512158
.getParameterCheckPolicy(member)
21522159
.isEmitted) {

pkg/compiler/test/dump_info/data/closures.dart

Lines changed: 121 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"id": "library/memory:sdk/tests/web/native/main.dart::",
3939
"kind": "library",
4040
"name": "<unnamed>",
41-
"size": 12352,
41+
"size": 12268,
4242
"children": [
4343
"class/memory:sdk/tests/web/native/main.dart::Class1",
4444
"function/memory:sdk/tests/web/native/main.dart::main",
@@ -132,7 +132,37 @@
132132
"imports": []
133133
}]
134134
*/
135-
/*class: Class1:class=[{
135+
/*spec.class: Class1:class=[{
136+
"id": "class/memory:sdk/tests/web/native/main.dart::Class1",
137+
"kind": "class",
138+
"name": "Class1",
139+
"size": 6067,
140+
"outputUnit": "outputUnit/main",
141+
"parent": "library/memory:sdk/tests/web/native/main.dart::",
142+
"modifiers": {
143+
"abstract": false
144+
},
145+
"children": [
146+
"field/memory:sdk/tests/web/native/main.dart::Class1.field",
147+
"field/memory:sdk/tests/web/native/main.dart::Class1.funcField",
148+
"function/memory:sdk/tests/web/native/main.dart::Class1.Class1",
149+
"function/memory:sdk/tests/web/native/main.dart::Class1.Class1.fact",
150+
"function/memory:sdk/tests/web/native/main.dart::Class1.Class1.fact2",
151+
"function/memory:sdk/tests/web/native/main.dart::Class1.Class1.setFunc",
152+
"function/memory:sdk/tests/web/native/main.dart::Class1.method1",
153+
"function/memory:sdk/tests/web/native/main.dart::Class1.method2",
154+
"function/memory:sdk/tests/web/native/main.dart::Class1.method3",
155+
"function/memory:sdk/tests/web/native/main.dart::Class1.method4",
156+
"function/memory:sdk/tests/web/native/main.dart::Class1.method5",
157+
"function/memory:sdk/tests/web/native/main.dart::Class1.method6",
158+
"function/memory:sdk/tests/web/native/main.dart::Class1.staticMethod1",
159+
"function/memory:sdk/tests/web/native/main.dart::Class1.staticMethod2",
160+
"function/memory:sdk/tests/web/native/main.dart::Class1.staticMethod3",
161+
"function/memory:sdk/tests/web/native/main.dart::Class1.staticMethod4"
162+
],
163+
"supers": []
164+
}]*/
165+
/*kernel.class: Class1:class=[{
136166
"id": "class/memory:sdk/tests/web/native/main.dart::Class1",
137167
"kind": "class",
138168
"name": "Class1",
@@ -163,7 +193,95 @@
163193
"supers": []
164194
}]*/
165195
class Class1<T> {
166-
/*member: Class1.field:
196+
/*spec.member: Class1.field:
197+
closure=[{
198+
"id": "closure/memory:sdk/tests/web/native/main.dart::Class1.field.Class1_field_closure",
199+
"kind": "closure",
200+
"name": "Class1_field_closure",
201+
"size": 242,
202+
"outputUnit": "outputUnit/main",
203+
"parent": "field/memory:sdk/tests/web/native/main.dart::Class1.field",
204+
"function": "function/memory:sdk/tests/web/native/main.dart::Class1.field.Class1_field_closure.call"
205+
}],
206+
function=[
207+
{
208+
"id": "field/memory:sdk/tests/web/native/main.dart::Class1.field",
209+
"kind": "field",
210+
"name": "field",
211+
"size": 242,
212+
"outputUnit": "outputUnit/main",
213+
"parent": "class/memory:sdk/tests/web/native/main.dart::Class1",
214+
"children": [
215+
"closure/memory:sdk/tests/web/native/main.dart::Class1.field.Class1_field_closure"
216+
],
217+
"inferredType": "[subclass=Closure]",
218+
"code": "",
219+
"type": "Type Function()"
220+
},
221+
{
222+
"id": "function/memory:sdk/tests/web/native/main.dart::Class1.field.Class1_field_closure.call",
223+
"kind": "function",
224+
"name": "call",
225+
"size": 58,
226+
"outputUnit": "outputUnit/main",
227+
"parent": "closure/memory:sdk/tests/web/native/main.dart::Class1.field.Class1_field_closure",
228+
"children": [],
229+
"modifiers": {
230+
"static": false,
231+
"const": false,
232+
"factory": false,
233+
"external": false
234+
},
235+
"returnType": "Type",
236+
"inferredReturnType": "[exact=_Type]",
237+
"parameters": [],
238+
"sideEffects": "SideEffects(reads nothing; writes nothing)",
239+
"inlinedCount": 0,
240+
"code": "call$0() {\n return A.createRuntimeType(this.T);\n }",
241+
"type": "Type Function()",
242+
"functionKind": 2
243+
}],
244+
holding=[
245+
{"id":"field/memory:sdk/tests/web/native/main.dart::Class1.field"},
246+
{"id":"function/dart:_js_helper::throwCyclicInit"},
247+
{"id":"function/dart:_late_helper::throwLateFieldADI"},
248+
{"id":"function/dart:_rti::Rti._bind"},
249+
{"id":"function/dart:_rti::Rti._eval"},
250+
{"id":"function/dart:_rti::_arrayInstanceType"},
251+
{"id":"function/dart:_rti::_asBool"},
252+
{"id":"function/dart:_rti::_asBoolQ"},
253+
{"id":"function/dart:_rti::_asBoolS"},
254+
{"id":"function/dart:_rti::_asDouble"},
255+
{"id":"function/dart:_rti::_asDoubleQ"},
256+
{"id":"function/dart:_rti::_asDoubleS"},
257+
{"id":"function/dart:_rti::_asInt"},
258+
{"id":"function/dart:_rti::_asIntQ"},
259+
{"id":"function/dart:_rti::_asIntS"},
260+
{"id":"function/dart:_rti::_asNum"},
261+
{"id":"function/dart:_rti::_asNumQ"},
262+
{"id":"function/dart:_rti::_asNumS"},
263+
{"id":"function/dart:_rti::_asObject"},
264+
{"id":"function/dart:_rti::_asString"},
265+
{"id":"function/dart:_rti::_asStringQ"},
266+
{"id":"function/dart:_rti::_asStringS"},
267+
{"id":"function/dart:_rti::_asTop"},
268+
{"id":"function/dart:_rti::_generalAsCheckImplementation"},
269+
{"id":"function/dart:_rti::_generalIsTestImplementation"},
270+
{"id":"function/dart:_rti::_generalNullableAsCheckImplementation"},
271+
{"id":"function/dart:_rti::_generalNullableIsTestImplementation"},
272+
{"id":"function/dart:_rti::_installSpecializedAsCheck"},
273+
{"id":"function/dart:_rti::_installSpecializedIsTest"},
274+
{"id":"function/dart:_rti::_instanceType"},
275+
{"id":"function/dart:_rti::_isBool"},
276+
{"id":"function/dart:_rti::_isInt"},
277+
{"id":"function/dart:_rti::_isNum"},
278+
{"id":"function/dart:_rti::_isObject"},
279+
{"id":"function/dart:_rti::_isString"},
280+
{"id":"function/dart:_rti::_isTop"},
281+
{"id":"function/dart:_rti::findType"},
282+
{"id":"function/dart:_rti::instanceType"}]
283+
*/
284+
/*kernel.member: Class1.field:
167285
closure=[{
168286
"id": "closure/memory:sdk/tests/web/native/main.dart::Class1.field.Class1_field_closure",
169287
"kind": "closure",

0 commit comments

Comments
 (0)