mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 12:30:03 +00:00
[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:
parent
4cf51e6c1a
commit
929b79e865
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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*/
|
||||
|
|
|
@ -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*/
|
||||
|
|
Loading…
Reference in a new issue