[dart2js] Don't insert redundant checks

This is in preparation for detecting if a generator header has checks.

By not registering the redundant checks (which should really done in codegen) we remove a few $isXXX flags.

Change-Id: I30deb2bc498858540cd326243798248c51ce9375
Reviewed-on: https://dart-review.googlesource.com/56006
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
Stephen Adams 2018-05-21 22:23:18 +00:00 committed by commit-bot@chromium.org
parent 4cf51e6c1a
commit 929b79e865
5 changed files with 54 additions and 47 deletions

View file

@ -16,6 +16,7 @@ import '../js/js.dart' as js;
import '../js_backend/js_backend.dart';
import '../native/native.dart' as native;
import '../types/abstract_value_domain.dart';
import '../types/masks.dart' show TypeMask;
import '../universe/selector.dart' show Selector;
import '../universe/side_effects.dart' show SideEffects;
import '../util/util.dart';
@ -3164,6 +3165,42 @@ class HTypeConversion extends HCheck {
receiverTypeCheckSelector == other.receiverTypeCheckSelector;
}
bool isRedundant(ClosedWorld closedWorld) {
AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
DartType type = typeExpression;
if (type != null) {
if (type.isMalformed) {
// Malformed types are treated as dynamic statically, but should
// throw a type error at runtime.
return false;
}
if (type.isTypeVariable) {
return false;
}
if (type.isFutureOr) {
// `null` always passes type conversion.
if (checkedInput.isNull(abstractValueDomain)) return true;
// TODO(johnniwinther): Optimize FutureOr type conversions.
return false;
}
if (!type.treatAsRaw) {
// `null` always passes type conversion.
if (checkedInput.isNull(abstractValueDomain)) return true;
return false;
}
if (type.isFunctionType) {
// `null` always passes type conversion.
if (checkedInput.isNull(abstractValueDomain)) return true;
// TODO(johnniwinther): Optimize function type conversions.
return false;
}
}
// Type is refined from `dynamic`, so it might become non-redundant.
if (abstractValueDomain.containsAll(checkedType)) return false;
TypeMask inputType = checkedInput.instructionType;
return inputType.isInMask(checkedType, closedWorld);
}
String toString() => 'HTypeConversion(type=$typeExpression, kind=$kind, '
'${hasTypeRepresentation ? 'representation=$typeRepresentation, ' : ''}'
'checkedInput=$checkedInput)';
@ -3205,6 +3242,13 @@ class HTypeKnown extends HCheck {
return knownType == other.knownType &&
instructionType == other.instructionType;
}
bool isRedundant(ClosedWorld closedWorld) {
AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
if (abstractValueDomain.containsAll(knownType)) return false;
TypeMask inputType = checkedInput.instructionType;
return inputType.isInMask(knownType, closedWorld);
}
}
class HRangeConversion extends HCheck {

View file

@ -925,54 +925,11 @@ class SsaInstructionSimplifier extends HBaseVisitor
}
HInstruction visitTypeConversion(HTypeConversion node) {
DartType type = node.typeExpression;
if (type != null) {
if (type.isMalformed) {
// Malformed types are treated as dynamic statically, but should
// throw a type error at runtime.
return node;
}
if (type.isTypeVariable) {
return node;
}
if (type.isFutureOr) {
HInstruction input = node.checkedInput;
// `null` always passes type conversion.
if (input.isNull(_abstractValueDomain)) return input;
// TODO(johnniwinther): Optimize FutureOr type conversions.
return node;
}
if (!type.treatAsRaw) {
HInstruction input = node.checkedInput;
// `null` always passes type conversion.
if (input.isNull(_abstractValueDomain)) return input;
// TODO(sra): We can statically check [input] if it is a constructor.
// TODO(sra): We can statically check [input] if it is load from a field
// of the same ground type, or load from a field of a parameterized type
// with the same receiver.
return node;
}
if (type.isFunctionType) {
HInstruction input = node.checkedInput;
// `null` always passes type conversion.
if (input.isNull(_abstractValueDomain)) return input;
// TODO(johnniwinther): Optimize function type conversions.
// TODO(sra): We can statically check [input] if it is a closure getter.
return node;
}
}
return removeIfCheckAlwaysSucceeds(node, node.checkedType);
return node.isRedundant(_closedWorld) ? node.checkedInput : node;
}
HInstruction visitTypeKnown(HTypeKnown node) {
return removeIfCheckAlwaysSucceeds(node, node.knownType);
}
HInstruction removeIfCheckAlwaysSucceeds(HCheck node, TypeMask checkedType) {
if (checkedType.containsAll(_closedWorld)) return node;
HInstruction input = node.checkedInput;
TypeMask inputType = input.instructionType;
return inputType.isInMask(checkedType, _closedWorld) ? input : node;
return node.isRedundant(_closedWorld) ? node.checkedInput : node;
}
FieldEntity findConcreteFieldForDynamicAccess(

View file

@ -66,6 +66,9 @@ abstract class TypeBuilder {
assert(type != null);
type = builder.localsHandler.substInContext(type);
HInstruction other = buildTypeConversion(original, type, kind);
if (other is HTypeConversion && other.isRedundant(builder.closedWorld)) {
return original;
}
// TODO(johnniwinther): This operation on `registry` may be inconsistent.
// If it is needed then it seems likely that similar invocations of
// `buildTypeConversion` in `SsaBuilder.visitAs` should also be followed by
@ -79,6 +82,9 @@ abstract class TypeBuilder {
if (type == null) return original;
HInstruction trusted = _trustType(original, type);
if (trusted == original) return original;
if (trusted is HTypeKnown && trusted.isRedundant(builder.closedWorld)) {
return original;
}
builder.add(trusted);
return trusted;
}

View file

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
/*kernel.class: global#Map:instance*/
/*strong.class: global#Map:checkedInstance,checks=[],instance*/
/*strong.class: global#Map:checks=[],instance*/
/*class: global#LinkedHashMap:*/
/*class: global#JsLinkedHashMap:checks=[],instance*/

View file

@ -2,7 +2,7 @@
// 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.
/*class: global#Map:checkedInstance,checks=[],instance*/
/*class: global#Map:checks=[],instance*/
/*class: global#LinkedHashMap:checkedInstance*/
/*class: global#JsLinkedHashMap:checkedInstance,checks=[$isLinkedHashMap],instance*/
/*class: global#double:checkedInstance,checks=[],instance,typeArgument*/