@@ -82,8 +82,6 @@ class SsaSimplifyInterceptors extends HBaseVisitor
82
82
// the interceptor is already available in a local variable, but it is
83
83
// possible that all uses can be rewritten to use different constants.
84
84
85
- // TODO(sra): Also do self-interceptor rewrites on a per-use basis.
86
-
87
85
HInstruction constant = tryComputeConstantInterceptor (
88
86
invoke.inputs[1 ], interceptor.interceptedClasses);
89
87
if (constant != null ) {
@@ -92,16 +90,18 @@ class SsaSimplifyInterceptors extends HBaseVisitor
92
90
return false ;
93
91
}
94
92
95
- bool canUseSelfForInterceptor (
96
- HInstruction receiver, Set <ClassEntity > interceptedClasses) {
93
+ bool canUseSelfForInterceptor (HInstruction receiver,
94
+ { Set <ClassEntity > interceptedClasses} ) {
97
95
if (receiver.canBePrimitive (_abstractValueDomain)) {
98
96
// Primitives always need interceptors.
99
97
return false ;
100
98
}
101
- if (receiver.canBeNull (_abstractValueDomain) &&
102
- interceptedClasses.contains (_commonElements.jsNullClass)) {
103
- // Need the JSNull interceptor.
104
- return false ;
99
+ if (receiver.canBeNull (_abstractValueDomain)) {
100
+ if (interceptedClasses == null ||
101
+ interceptedClasses.contains (_commonElements.jsNullClass)) {
102
+ // Need the JSNull interceptor.
103
+ return false ;
104
+ }
105
105
}
106
106
107
107
// All intercepted classes extend `Interceptor`, so if the receiver can't be
@@ -111,13 +111,6 @@ class SsaSimplifyInterceptors extends HBaseVisitor
111
111
112
112
HInstruction tryComputeConstantInterceptor (
113
113
HInstruction input, Set <ClassEntity > interceptedClasses) {
114
- if (input == _graph.explicitReceiverParameter) {
115
- // If `explicitReceiverParameter` is set it means the current method is an
116
- // interceptor method, and `this` is the interceptor. The caller just did
117
- // `getInterceptor(foo).currentMethod(foo)` to enter the current method.
118
- return _graph.thisInstruction;
119
- }
120
-
121
114
ClassEntity constantInterceptor = tryComputeConstantInterceptorFromType (
122
115
input.instructionType, interceptedClasses);
123
116
@@ -191,8 +184,21 @@ class SsaSimplifyInterceptors extends HBaseVisitor
191
184
return result;
192
185
}
193
186
187
+ static int useCount (HInstruction user, HInstruction used) =>
188
+ user.inputs.where ((input) => input == used).length;
189
+
194
190
bool visitInterceptor (HInterceptor node) {
195
- if (node.isConstant ()) return false ;
191
+ if (node.receiver.nonCheck () == _graph.explicitReceiverParameter) {
192
+ // If `explicitReceiverParameter` is set it means the current method is an
193
+ // interceptor method, and `this` is the interceptor. The caller just did
194
+ // `getInterceptor(foo).currentMethod(foo)` to enter the current method.
195
+ node.block.rewrite (node, _graph.thisInstruction);
196
+ return true ;
197
+ }
198
+
199
+ rewriteSelfInterceptorUses (node);
200
+
201
+ if (node.usedBy.isEmpty) return true ;
196
202
197
203
// Specialize the interceptor with set of classes it intercepts, considering
198
204
// all uses. (The specialized interceptor has a shorter dispatch chain).
@@ -209,9 +215,6 @@ class SsaSimplifyInterceptors extends HBaseVisitor
209
215
// if `a.length` succeeds, which is indicated by the hashCode receiver being
210
216
// a HTypeKnown instruction.
211
217
212
- int useCount (HInstruction user, HInstruction used) =>
213
- user.inputs.where ((input) => input == used).length;
214
-
215
218
Set <ClassEntity > interceptedClasses;
216
219
HInstruction dominator = findDominator (node.usedBy);
217
220
// If there is a call that dominates all other uses, we can use just the
@@ -282,18 +285,6 @@ class SsaSimplifyInterceptors extends HBaseVisitor
282
285
283
286
HInstruction receiver = node.receiver;
284
287
285
- // TODO(sra): We should consider each use individually and then all uses
286
- // together. Each use might permit a different rewrite due to a refined
287
- // receiver type. Self-interceptor rewrites are always beneficial since the
288
- // receiver is live at a invocation. Constant-interceptor rewrites are only
289
- // guaranteed to be beneficial if they can eliminate the need for the
290
- // interceptor or reduce the uses to one that can be simplified with a
291
- // one-shot interceptor or optimized is-check.
292
-
293
- if (canUseSelfForInterceptor (receiver, interceptedClasses)) {
294
- return rewriteToUseSelfAsInterceptor (node, receiver);
295
- }
296
-
297
288
// Try computing a constant interceptor.
298
289
HInstruction constantInterceptor =
299
290
tryComputeConstantInterceptor (receiver, interceptedClasses);
@@ -381,23 +372,62 @@ class SsaSimplifyInterceptors extends HBaseVisitor
381
372
return false ;
382
373
}
383
374
384
- bool rewriteToUseSelfAsInterceptor (HInterceptor node, HInstruction receiver) {
375
+ void rewriteSelfInterceptorUses (HInterceptor node) {
376
+ HInstruction receiver = node.receiver;
377
+
378
+ // At instructions that use the interceptor and its receiver, the receiver
379
+ // might be refined at the use site.
380
+
381
+ // dynamic x = ...
382
+ // if (x is Mumble) {
383
+ // print(x.length); // Self-interceptor here.
384
+ // } else {
385
+ // print(x.length); //
386
+ // }
387
+
388
+ finishInvoke (HInvoke invoke, Selector selector) {
389
+ HInstruction callReceiver = invoke.getDartReceiver (_closedWorld);
390
+ if (receiver.nonCheck () == callReceiver.nonCheck ()) {
391
+ Set <ClassEntity > interceptedClasses = _interceptorData
392
+ .getInterceptedClassesOn (selector.name, _closedWorld);
393
+
394
+ if (canUseSelfForInterceptor (callReceiver,
395
+ interceptedClasses: interceptedClasses)) {
396
+ invoke.changeUse (node, callReceiver);
397
+ }
398
+ }
399
+ }
400
+
385
401
for (HInstruction user in node.usedBy.toList ()) {
386
402
if (user is HIs ) {
387
- user.changeUse (node, receiver);
403
+ if (user.interceptor == node) {
404
+ HInstruction expression = user.expression;
405
+ if (canUseSelfForInterceptor (expression)) {
406
+ user.changeUse (node, expression);
407
+ }
408
+ }
409
+ } else if (user is HInvokeDynamic ) {
410
+ if (user.isCallOnInterceptor (_closedWorld) &&
411
+ node == user.inputs[0 ] &&
412
+ useCount (user, node) == 1 ) {
413
+ finishInvoke (user, user.selector);
414
+ }
415
+ } else if (user is HInvokeSuper ) {
416
+ if (user.isCallOnInterceptor (_closedWorld) &&
417
+ node == user.inputs[0 ] &&
418
+ useCount (user, node) == 1 ) {
419
+ finishInvoke (user, user.selector);
420
+ }
388
421
} else {
389
- // Use the potentially self-argument as new receiver. Note that the
390
- // self-argument could potentially have a tighter type than the
391
- // receiver which was the input to the interceptor.
392
- assert (user.inputs[0 ] == node);
393
- assert (receiver.nonCheck () == user.inputs[1 ].nonCheck ());
394
- user.changeUse (node, user.inputs[1 ]);
422
+ // TODO(sra): Are there other paired uses of the receiver and
423
+ // interceptor where we can make use of a strengthened receiver?
395
424
}
396
425
}
397
- return false ;
398
426
}
399
427
400
428
bool visitOneShotInterceptor (HOneShotInterceptor node) {
429
+ // 'Undo' the one-shot transformation if the receiver has a constant
430
+ // interceptor.
401
431
HInstruction constant =
402
432
tryComputeConstantInterceptor (node.inputs[1 ], node.interceptedClasses);
403
433
0 commit comments