Skip to content

Commit fc349bd

Browse files
iinozemtsevcommit-bot@chromium.org
authored andcommitted
Revert "[dart2js] Avoid premature interceptor optimizations"
This reverts commit db6f65e. Reason for revert: b/184605569 Original change's description: > [dart2js] Avoid premature interceptor optimizations > > Add a SsaFinalizeInterceptors phase that does optimizations with the > interceptor calling convention. > > This puts the the caller and callee sides of the dummy explicit > receiver optimization in the same phase. > > Change-Id: I07fe0483c2658c3744b76a235edb24720d7ea60f > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193140 > Commit-Queue: Stephen Adams <[email protected]> > Reviewed-by: Mayank Patke <[email protected]> # Not skipping CQ checks because original CL landed > 1 day ago. Change-Id: I0e2959f329030f12a43a9aef16cfee9b94bbf250 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/194061 Reviewed-by: Ivan Inozemtsev <[email protected]> Commit-Queue: Ivan Inozemtsev <[email protected]>
1 parent b7b8863 commit fc349bd

File tree

6 files changed

+151
-259
lines changed

6 files changed

+151
-259
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,8 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
414414
assert(graph.isValid(), 'Graph not valid after ${phase.name}');
415415
}
416416

417-
runPhase(new SsaInstructionSelection(_options, _closedWorld));
417+
runPhase(
418+
new SsaInstructionSelection(_options, _closedWorld, _interceptorData));
418419
runPhase(new SsaTypeKnownRemover());
419420
runPhase(new SsaTrustedCheckRemover(_options));
420421
runPhase(new SsaAssignmentChaining(_closedWorld));

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

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import '../constants/values.dart';
66
import '../elements/entities.dart';
77
import '../inferrer/abstract_value_domain.dart';
8+
import '../js_backend/interceptor_data.dart';
89
import '../options.dart';
910
import '../universe/selector.dart' show Selector;
1011
import '../world.dart' show JClosedWorld;
@@ -26,14 +27,21 @@ bool canUseAliasedSuperMember(MemberEntity member, Selector selector) {
2627
///
2728
/// - Remove NullChecks where the next instruction would fail on the operand.
2829
///
30+
/// - Dummy receiver optimization.
31+
///
32+
/// - One-shot interceptor optimization.
33+
///
2934
/// - Combine read/modify/write sequences into HReadModifyWrite instructions to
3035
/// simplify codegen of expressions like `a.x += y`.
36+
3137
class SsaInstructionSelection extends HBaseVisitor with CodegenPhase {
3238
final JClosedWorld _closedWorld;
39+
final InterceptorData _interceptorData;
3340
final CompilerOptions _options;
3441
HGraph graph;
3542

36-
SsaInstructionSelection(this._options, this._closedWorld);
43+
SsaInstructionSelection(
44+
this._options, this._closedWorld, this._interceptorData);
3745

3846
AbstractValueDomain get _abstractValueDomain =>
3947
_closedWorld.abstractValueDomain;
@@ -220,6 +228,128 @@ class SsaInstructionSelection extends HBaseVisitor with CodegenPhase {
220228
.isInterceptor(_abstractValueDomain.excludeNull(type))
221229
.isPotentiallyTrue;
222230

231+
@override
232+
HInstruction visitInvokeDynamic(HInvokeDynamic node) {
233+
if (!node.isInterceptedCall) return node;
234+
235+
tryReplaceExplicitReceiverWithDummy(
236+
node, node.selector, node.element, node.receiverType);
237+
238+
// Try to replace
239+
//
240+
// getInterceptor(o).method(o, ...)
241+
//
242+
// with a 'one shot interceptor' which is a call to a synthesized static
243+
// helper function that combines the two operations.
244+
//
245+
// oneShotMethod(o, 1, 2)
246+
//
247+
// This saves code size and makes the receiver of an intercepted call a
248+
// candidate for being generated at use site.
249+
//
250+
// Avoid combining a hoisted interceptor back into a loop, and the faster
251+
// almost-constant kind of interceptor.
252+
253+
HInstruction interceptor = node.inputs[0];
254+
if (interceptor is HInterceptor &&
255+
interceptor.usedBy.length == 1 &&
256+
!interceptor.isConditionalConstantInterceptor &&
257+
interceptor.hasSameLoopHeaderAs(node)) {
258+
// Copy inputs and replace interceptor with `null`.
259+
List<HInstruction> inputs = List.of(node.inputs);
260+
inputs[0] = graph.addConstantNull(_closedWorld);
261+
262+
HOneShotInterceptor oneShot = HOneShotInterceptor(
263+
node.selector,
264+
node.receiverType,
265+
inputs,
266+
node.instructionType,
267+
node.typeArguments,
268+
interceptor.interceptedClasses);
269+
oneShot.sourceInformation = node.sourceInformation;
270+
oneShot.sourceElement = node.sourceElement;
271+
oneShot.sideEffects.setTo(node.sideEffects);
272+
273+
HBasicBlock block = node.block;
274+
block.addAfter(node, oneShot);
275+
block.rewrite(node, oneShot);
276+
block.remove(node);
277+
interceptor.block.remove(interceptor);
278+
return null;
279+
}
280+
281+
return node;
282+
}
283+
284+
@override
285+
HInstruction visitInvokeSuper(HInvokeSuper node) {
286+
tryReplaceExplicitReceiverWithDummy(
287+
node, node.selector, node.element, null);
288+
return node;
289+
}
290+
291+
@override
292+
HInstruction visitOneShotInterceptor(HOneShotInterceptor node) {
293+
throw StateError('Should not see HOneShotInterceptor: $node');
294+
}
295+
296+
void tryReplaceExplicitReceiverWithDummy(HInvoke node, Selector selector,
297+
MemberEntity target, AbstractValue mask) {
298+
// Calls of the form
299+
//
300+
// a.foo$1(a, x)
301+
//
302+
// where the interceptor calling convention is used come from recognizing
303+
// that 'a' is a 'self-interceptor'. If the selector matches only methods
304+
// that ignore the explicit receiver parameter, replace occurrences of the
305+
// receiver argument with a dummy receiver '0':
306+
//
307+
// a.foo$1(a, x) ---> a.foo$1(0, x)
308+
//
309+
// This often reduces the number of references to 'a' to one, allowing 'a'
310+
// to be generated at use to avoid a temporary, e.g.
311+
//
312+
// t1 = b.get$thing();
313+
// t1.foo$1(t1, x)
314+
// --->
315+
// b.get$thing().foo$1(0, x)
316+
//
317+
assert(target != null || mask != null);
318+
319+
if (!node.isInterceptedCall) return;
320+
321+
// TODO(15933): Make automatically generated property extraction closures
322+
// work with the dummy receiver optimization.
323+
if (selector.isGetter) return;
324+
325+
// This assignment of inputs is uniform for HInvokeDynamic and HInvokeSuper.
326+
HInstruction interceptor = node.inputs[0];
327+
HInstruction receiverArgument = node.inputs[1];
328+
329+
// A 'self-interceptor'?
330+
if (interceptor.nonCheck() != receiverArgument.nonCheck()) return;
331+
332+
// TODO(sra): Should this be an assert?
333+
if (!_interceptorData.isInterceptedSelector(selector)) return;
334+
335+
if (target != null) {
336+
// A call that resolves to a single instance method (element) requires the
337+
// calling convention consistent with the method.
338+
ClassEntity cls = target.enclosingClass;
339+
assert(_interceptorData.isInterceptedMethod(target));
340+
if (_interceptorData.isInterceptedClass(cls)) return;
341+
} else if (_interceptorData.isInterceptedMixinSelector(
342+
selector, mask, _closedWorld)) {
343+
return;
344+
}
345+
346+
ConstantValue constant = DummyInterceptorConstantValue();
347+
HConstant dummy = graph.addConstant(constant, _closedWorld);
348+
receiverArgument.usedBy.remove(node);
349+
node.inputs[1] = dummy;
350+
dummy.usedBy.add(node);
351+
}
352+
223353
@override
224354
HInstruction visitFieldSet(HFieldSet setter) {
225355
// Pattern match

0 commit comments

Comments
 (0)