| Index: pkg/compiler/lib/src/ssa/types_propagation.dart
|
| diff --git a/pkg/compiler/lib/src/ssa/types_propagation.dart b/pkg/compiler/lib/src/ssa/types_propagation.dart
|
| index 756417621fe38ddd4cb6ae024aacc2d4ad1f5ee2..298d928f756c1df1e6c35382004fc0a8cd041a5c 100644
|
| --- a/pkg/compiler/lib/src/ssa/types_propagation.dart
|
| +++ b/pkg/compiler/lib/src/ssa/types_propagation.dart
|
| @@ -361,31 +361,47 @@ class SsaTypePropagator extends HBaseVisitor implements OptimizationPhase {
|
| TypeMask receiverType = receiver.instructionType;
|
| instruction.mask = receiverType;
|
|
|
| - // Try to specialize the receiver after this call.
|
| - if (receiver.dominatedUsers(instruction).length != 1 &&
|
| - !instruction.selector.isClosureCall) {
|
| - TypeMask newType = compiler.world.allFunctions
|
| - .receiverType(instruction.selector, instruction.mask);
|
| - newType = newType.intersection(receiverType, classWorld);
|
| + // Try to specialize the receiver after this call by instering a refinement
|
| + // node (HTypeKnown). There are two potentially expensive tests - are there
|
| + // any uses of the receiver dominated by and following this call?, and what
|
| + // is the refined type? The first is expensive if the receiver has many
|
| + // uses, the second is expensive if many classes implement the selector. So
|
| + // we try to do the least expensive test first.
|
| + const int _MAX_QUICK_USERS = 50;
|
| + if (!instruction.selector.isClosureCall) {
|
| + TypeMask newType;
|
| + TypeMask computeNewType() {
|
| + newType = compiler.world.allFunctions
|
| + .receiverType(instruction.selector, instruction.mask);
|
| + newType = newType.intersection(receiverType, classWorld);
|
| + return newType;
|
| + }
|
| +
|
| var next = instruction.next;
|
| if (next is HTypeKnown && next.checkedInput == receiver) {
|
| - // We already have refined [receiver]. We still update the
|
| - // type of the [HTypeKnown] instruction because it may have
|
| - // been refined with a correct type at the time, but
|
| - // incorrect now.
|
| - if (next.instructionType != newType) {
|
| + // On a previous pass or iteration we already refined [receiver] by
|
| + // inserting a [HTypeKnown] instruction. That replaced several dominated
|
| + // uses with the refinement. We update the type of the [HTypeKnown]
|
| + // instruction because it may have been refined with a correct type at
|
| + // the time, but incorrect now.
|
| + if (next.instructionType != computeNewType()) {
|
| next.knownType = next.instructionType = newType;
|
| addDependentInstructionsToWorkList(next);
|
| }
|
| - } else if (newType != receiverType) {
|
| - // Insert a refinement node after the call and update all
|
| - // users dominated by the call to use that node instead of
|
| - // [receiver].
|
| - HTypeKnown converted =
|
| - new HTypeKnown.witnessed(newType, receiver, instruction);
|
| - instruction.block.addBefore(instruction.next, converted);
|
| - receiver.replaceAllUsersDominatedBy(converted.next, converted);
|
| - addDependentInstructionsToWorkList(converted);
|
| + } else {
|
| + bool hasCandidates() => receiver.dominatedUsers(instruction).length > 1;
|
| +
|
| + if ((receiver.usedBy.length <= _MAX_QUICK_USERS)
|
| + ? (hasCandidates() && computeNewType() != receiverType)
|
| + : (computeNewType() != receiverType && hasCandidates())) {
|
| + // Insert a refinement node after the call and update all users
|
| + // dominated by the call to use that node instead of [receiver].
|
| + HTypeKnown converted =
|
| + new HTypeKnown.witnessed(newType, receiver, instruction);
|
| + instruction.block.addBefore(instruction.next, converted);
|
| + receiver.replaceAllUsersDominatedBy(converted.next, converted);
|
| + addDependentInstructionsToWorkList(converted);
|
| + }
|
| }
|
| }
|
|
|
|
|