diff --git a/pkg/compiler/lib/src/common_elements.dart b/pkg/compiler/lib/src/common_elements.dart index 346a02510d257..ea0eeb75c3841 100644 --- a/pkg/compiler/lib/src/common_elements.dart +++ b/pkg/compiler/lib/src/common_elements.dart @@ -1239,7 +1239,9 @@ class CommonElementsImpl } return selector.applies(_jsStringSplit) && (receiver == null || - abstractValueDomain.canHit(receiver, jsStringSplit, selector)); + abstractValueDomain + .isTargetingMember(receiver, jsStringSplit, selector) + .isPotentiallyTrue); } FunctionEntity _jsStringSplit; diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart index 868e8bf504c08..e2722a051b640 100644 --- a/pkg/compiler/lib/src/dump_info.dart +++ b/pkg/compiler/lib/src/dump_info.dart @@ -116,7 +116,9 @@ class ElementInfoCollector { AbstractValue inferredType = _resultOfMember(field).type; // If a field has an empty inferred type it is never used. if (inferredType == null || - closedWorld.abstractValueDomain.isEmpty(inferredType)) { + closedWorld.abstractValueDomain + .isEmpty(inferredType) + .isDefinitelyTrue) { return null; } diff --git a/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart b/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart index d6cd5d03d7baa..ee075e1e59194 100644 --- a/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart +++ b/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart @@ -11,7 +11,38 @@ import '../universe/selector.dart'; import '../universe/world_builder.dart'; import '../world.dart'; -enum AbstractBool { True, False, Maybe } +/// Enum-like values used for reporting known and unknown truth values. +class AbstractBool { + final bool _value; + + const AbstractBool._(this._value); + + bool get isDefinitelyTrue => _value == true; + + bool get isPotentiallyTrue => _value != false; + + bool get isDefinitelyFalse => _value == false; + + bool get isPotentiallyFalse => _value != true; + + /// A value of `Abstract.True` is used when the property is known _always_ to + /// be true. + static const AbstractBool True = const AbstractBool._(true); + + /// A value of `Abstract.False` is used when the property is known _never_ to + /// be true. + static const AbstractBool False = const AbstractBool._(false); + + /// A value of `Abstract.Maybe` is used when the property might or might not + /// be true. + static const AbstractBool Maybe = const AbstractBool._(null); + + static AbstractBool trueOrMaybe(bool value) => value ? True : Maybe; + + static AbstractBool trueOrFalse(bool value) => value ? True : False; + + static AbstractBool maybeOrFalse(bool value) => value ? Maybe : False; +} /// Strategy for the abstraction of runtime values used by the global type /// inference. @@ -132,11 +163,13 @@ abstract class AbstractValueDomain { /// [cls]. AbstractValue createNullableSubtype(ClassEntity cls); - /// Returns `true` if [value] is a native typed array or `null` at runtime. - bool isTypedArray(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a native typed + /// array or `null` at runtime. + AbstractBool isTypedArray(covariant AbstractValue value); - /// Returns `true` if [value] could be a native typed array at runtime. - bool couldBeTypedArray(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] could be a native + /// typed array at runtime. + AbstractBool couldBeTypedArray(covariant AbstractValue value); /// Returns the version of the abstract [value] that excludes `null`. AbstractValue excludeNull(covariant AbstractValue value); @@ -144,141 +177,151 @@ abstract class AbstractValueDomain { /// Returns the version of the abstract [value] that includes `null`. AbstractValue includeNull(covariant AbstractValue value); - /// Returns `true` if [value] contains instances of [cls] at runtime. - bool containsType(covariant AbstractValue value, ClassEntity cls); + /// Returns an [AbstractBool] that describes whether [value] contains + /// instances of [cls] at runtime. + AbstractBool containsType(covariant AbstractValue value, ClassEntity cls); - /// Returns `true` if [value] only contains subtypes of [cls] or `null` at - /// runtime. - bool containsOnlyType(covariant AbstractValue value, ClassEntity cls); + /// Returns an [AbstractBool] that describes whether [value] only contains + /// subtypes of [cls] or `null` at runtime. + AbstractBool containsOnlyType(covariant AbstractValue value, ClassEntity cls); - /// Returns `true` if [value] is an instance of [cls] or `null` at runtime. + /// Returns an [AbstractBool] that describes whether [value] is an instance of + /// [cls] or `null` at runtime. // TODO(johnniwinther): Merge this with [isInstanceOf]. - bool isInstanceOfOrNull(covariant AbstractValue value, ClassEntity cls); + AbstractBool isInstanceOfOrNull( + covariant AbstractValue value, ClassEntity cls); - /// Returns an [AbstractBool] that describes how [value] is known to be an + /// Returns an [AbstractBool] that describes whether [value] is known to be an /// instance of [cls] at runtime. - /// - /// If the returned value is `Abstract.True`, [value] is known _always_ to be - /// an instance of [cls]. If the returned value is `Abstract.False`, [value] - /// is known _never_ to be an instance of [cls]. If the returned value is - /// `Abstract.Maybe` [value] might or might not be an instance of [cls] at - /// runtime. AbstractBool isInstanceOf(AbstractValue value, ClassEntity cls); - /// Returns `true` if [value] is empty set of runtime values. - bool isEmpty(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is empty set of + /// runtime values. + AbstractBool isEmpty(covariant AbstractValue value); - /// Returns `true` if [value] is a non-null exact class at runtime. - bool isExact(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a non-null + /// exact class at runtime. + AbstractBool isExact(covariant AbstractValue value); - /// Returns `true` if [value] is an exact class or `null` at runtime. - bool isExactOrNull(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is an exact class + /// or `null` at runtime. + AbstractBool isExactOrNull(covariant AbstractValue value); /// Returns the [ClassEntity] if this [value] is a non-null instance of an /// exact class at runtime, and `null` otherwise. ClassEntity getExactClass(covariant AbstractValue value); - /// Returns `true` if [value] can be `null` at runtime. - bool canBeNull(covariant AbstractValue value); - - /// Returns `true` if [value] is `null` at runtime. - bool isNull(covariant AbstractValue value); - - /// Returns `true` if [value] could be a JavaScript bool, number, string, - /// array or `null` at runtime. - bool canBePrimitive(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is `null` at + /// runtime. + AbstractBool isNull(covariant AbstractValue value); - /// Returns `true` if [value] could be a JavaScript number at runtime. - bool canBePrimitiveNumber(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a JavaScript + /// bool, number, string, array or `null` at runtime. + AbstractBool isPrimitive(covariant AbstractValue value); - /// Returns `true` if [value] could be a JavaScript bool at runtime. - bool canBePrimitiveBoolean(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a JavaScript + /// number at runtime. + AbstractBool isPrimitiveNumber(covariant AbstractValue value); - /// Returns `true` if [value] could be a JavaScript array at runtime. - bool canBePrimitiveArray(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a JavaScript + /// bool at runtime. + AbstractBool isPrimitiveBoolean(covariant AbstractValue value); - /// Returns `true` if [value] is a JavaScript string, array, native HTML list - /// or `null` at runtime. - bool isIndexablePrimitive(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a JavaScript + /// array at runtime. + AbstractBool isPrimitiveArray(covariant AbstractValue value); - /// Returns `true` if [value] is a fixed-size or constant JavaScript array or - /// `null` at - /// runtime. - bool isFixedArray(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a JavaScript + /// string, array, native HTML list or `null` at runtime. + AbstractBool isIndexablePrimitive(covariant AbstractValue value); - /// Returns `true` if [value] is a growable JavaScript array or `null` at - /// runtime. - bool isExtendableArray(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a fixed-size + /// or constant JavaScript array or `null` at runtime. + AbstractBool isFixedArray(covariant AbstractValue value); - /// Returns `true` if [value] is a mutable JavaScript array or `null` at - /// runtime. - bool isMutableArray(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a growable + /// JavaScript array or `null` at runtime. + AbstractBool isExtendableArray(covariant AbstractValue value); - /// Returns `true` if [value] is a mutable JavaScript array, native HTML list - /// or `null` at runtime. - bool isMutableIndexable(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a mutable + /// JavaScript array or `null` at runtime. + AbstractBool isMutableArray(covariant AbstractValue value); - /// Returns `true` if [value] is a JavaScript array or `null` at runtime. - bool isArray(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a mutable + /// JavaScript array, native HTML list or `null` at runtime. + AbstractBool isMutableIndexable(covariant AbstractValue value); - /// Returns `true` if [value] could be a JavaScript string at runtime. - bool canBePrimitiveString(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a JavaScript + /// array or `null` at runtime. + AbstractBool isArray(covariant AbstractValue value); - /// Return `true` if [value] could be an interceptor at runtime. - bool canBeInterceptor(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a JavaScript + /// string at runtime. + AbstractBool isPrimitiveString(covariant AbstractValue value); - /// Returns `true` if [value] is a non-null integer value at runtime. - bool isInteger(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is an interceptor + /// at runtime. + AbstractBool isInterceptor(covariant AbstractValue value); - /// Returns `true` if [value] is a non-null 32 bit unsigned integer value at - /// runtime. - bool isUInt32(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a non-null + /// integer value at runtime. + AbstractBool isInteger(covariant AbstractValue value); - /// Returns `true` if [value] is a non-null 31 bit unsigned integer value at - /// runtime. - bool isUInt31(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a non-null 32 + /// bit unsigned integer value at runtime. + AbstractBool isUInt32(covariant AbstractValue value); - /// Returns `true` if [value] is a non-null unsigned integer value at runtime. - bool isPositiveInteger(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a non-null 31 + /// bit unsigned integer value at runtime. + AbstractBool isUInt31(covariant AbstractValue value); - /// Returns `true` if [value] is an unsigned integer value or `null` at - /// runtime. - bool isPositiveIntegerOrNull(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a non-null + /// unsigned integer value at runtime. + AbstractBool isPositiveInteger(covariant AbstractValue value); - /// Returns `true` if [value] is an integer value or `null` at runtime. - bool isIntegerOrNull(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is an unsigned + /// integer value or `null` at runtime. + AbstractBool isPositiveIntegerOrNull(covariant AbstractValue value); - /// Returns `true` if [value] is a non-null JavaScript number at runtime. - bool isNumber(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is an integer + /// value or `null` at runtime. + AbstractBool isIntegerOrNull(covariant AbstractValue value); - /// Returns `true` if [value] is a JavaScript number or `null` at runtime. - bool isNumberOrNull(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a non-null + /// JavaScript number at runtime. + AbstractBool isNumber(covariant AbstractValue value); - /// Returns `true` if [value] is a non-integer number at runtime. - bool isDouble(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a JavaScript + /// number or `null` at runtime. + AbstractBool isNumberOrNull(covariant AbstractValue value); - /// Returns `true` if [value] is a non-integer number or `null` at runtime. - bool isDoubleOrNull(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a non-integer + /// number at runtime. + AbstractBool isDouble(covariant AbstractValue value); - /// Returns `true` if [value] is a JavaScript bool at runtime. - bool isBoolean(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a non-integer + /// number or `null` at runtime. + AbstractBool isDoubleOrNull(covariant AbstractValue value); - /// Returns `true` if [value] is a JavaScript bool or `null` at runtime. - bool isBooleanOrNull(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a JavaScript + /// bool at runtime. + AbstractBool isBoolean(covariant AbstractValue value); - /// Returns `true` if [value] is a JavaScript string at runtime. - bool isString(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a JavaScript + /// bool or `null` at runtime. + AbstractBool isBooleanOrNull(covariant AbstractValue value); - /// Returns `true` if [value] is a JavaScript string or `null` at runtime. - bool isStringOrNull(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a JavaScript + /// string at runtime. + AbstractBool isString(covariant AbstractValue value); - /// Returns `true` if [value] a non-null JavaScript primitive or `null`? - // TODO(johnniwinther): This should probably not return true on `null`, - // investigate. - bool isPrimitive(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] is a JavaScript + /// string or `null` at runtime. + AbstractBool isStringOrNull(covariant AbstractValue value); - /// Returns `true` if [value] a JavaScript primitive, possible `null`. - bool isPrimitiveOrNull(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes whether [value] a JavaScript + /// primitive, possible `null`. + AbstractBool isPrimitiveOrNull(covariant AbstractValue value); /// Returns [AbstractValue] for the runtime values contained in either [a] or /// [b]. @@ -293,11 +336,14 @@ abstract class AbstractValueDomain { AbstractValue intersection( covariant AbstractValue a, covariant AbstractValue b); - /// Returns `true` if [a] and [b] have no runtime values in common. - bool areDisjoint(covariant AbstractValue a, covariant AbstractValue b); + /// Returns an [AbstractBool] that describes whether [a] and [b] have no + /// runtime values in common. + AbstractBool areDisjoint( + covariant AbstractValue a, covariant AbstractValue b); - /// Returns `true` if [a] contains all non-null runtime values. - bool containsAll(covariant AbstractValue a); + /// Returns an [AbstractBool] that describes whether [a] contains all non-null + /// runtime values. + AbstractBool containsAll(covariant AbstractValue a); /// Computes the [AbstractValue] corresponding to the constant [value]. AbstractValue computeAbstractValueForConstant(ConstantValue value); @@ -419,22 +465,24 @@ abstract class AbstractValueDomain { /// Compute the type of all potential receivers of the set of live [members]. AbstractValue computeReceiver(Iterable members); - /// Returns whether [member] is a potential target when being - /// invoked on a [receiver]. [selector] is used to ensure library privacy is - /// taken into account. - bool canHit(AbstractValue receiver, MemberEntity member, Selector selector); + /// Returns an [AbstractBool] that describes whether [member] is a potential + /// target when being invoked on a [receiver]. [selector] is used to ensure + /// library privacy is taken into account. + AbstractBool isTargetingMember( + AbstractValue receiver, MemberEntity member, Selector selector); - /// Returns whether [selector] invoked on a [receiver] can hit a - /// [noSuchMethod]. - bool needsNoSuchMethodHandling(AbstractValue receiver, Selector selector); + /// Returns an [AbstractBool] that describes whether [selector] invoked on a + /// [receiver] can hit a [noSuchMethod]. + AbstractBool needsNoSuchMethodHandling( + AbstractValue receiver, Selector selector); - /// Returns `true` if the set of runtime values of [subset] are all in the set - /// of runtime values of [superset]. - bool contains(AbstractValue superset, AbstractValue subset); + /// Returns an [AbstractBool] that describes if the set of runtime values of + /// [subset] are known to all be in the set of runtime values of [superset]. + AbstractBool contains(AbstractValue superset, AbstractValue subset); - /// Returns `true` if the set of runtime values of [subset] are all in the set - /// of runtime values of [superset]. - bool isIn(AbstractValue subset, AbstractValue superset); + /// Returns an [AbstractBool] that describes if the set of runtime values of + /// [subset] are known to all be in the set of runtime values of [superset]. + AbstractBool isIn(AbstractValue subset, AbstractValue superset); /// Returns the [MemberEntity] that is known to always be hit at runtime /// [receiver]. @@ -442,18 +490,20 @@ abstract class AbstractValueDomain { /// Returns `null` if 0 or more than 1 member can be hit at runtime. MemberEntity locateSingleMember(AbstractValue receiver, Selector selector); - /// Returns `true` if [value] is an indexable JavaScript value at runtime. - bool isJsIndexable(covariant AbstractValue value); + /// Returns an [AbstractBool] that describes if [value] is known to be an + /// indexable JavaScript value at runtime. + AbstractBool isJsIndexable(covariant AbstractValue value); - /// Returns `true` if [value] is an indexable and iterable JavaScript value at - /// runtime. + /// RReturns an [AbstractBool] that describes if [value] is known to be an + /// indexable or iterable JavaScript value at runtime. /// /// JavaScript arrays are both indexable and iterable whereas JavaScript /// strings are indexable but not iterable. - bool isJsIndexableAndIterable(AbstractValue value); + AbstractBool isJsIndexableAndIterable(AbstractValue value); - /// Returns `true` if [value] is an JavaScript indexable of fixed length. - bool isFixedLengthJsIndexable(AbstractValue value); + /// Returns an [AbstractBool] that describes if [value] is known to be a + /// JavaScript indexable of fixed length. + AbstractBool isFixedLengthJsIndexable(AbstractValue value); /// Returns compact a textual representation for [value] used for debugging. String getCompactText(AbstractValue value); diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart index 139eaccc876ce..ad52f6a30cc27 100644 --- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart +++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart @@ -833,7 +833,7 @@ class InferrerEngineImpl extends InferrerEngine { MemberEntity member = info.calledElement; inferredDataBuilder.addFunctionCalledInLoop(member); } else if (info.mask != null && - !abstractValueDomain.containsAll(info.mask)) { + abstractValueDomain.containsAll(info.mask).isDefinitelyFalse) { // For instance methods, we only register a selector called in a // loop if it is a typed selector, to avoid marking too many // methods as being called from within a loop. This cuts down diff --git a/pkg/compiler/lib/src/inferrer/trivial.dart b/pkg/compiler/lib/src/inferrer/trivial.dart new file mode 100644 index 0000000000000..c37d4930b4d71 --- /dev/null +++ b/pkg/compiler/lib/src/inferrer/trivial.dart @@ -0,0 +1,397 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// 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. + +import '../constants/values.dart' show ConstantValue, PrimitiveConstantValue; +import '../elements/entities.dart'; +import '../serialization/serialization.dart'; +import '../universe/selector.dart'; +import 'abstract_value_domain.dart'; + +class TrivialAbstractValue implements AbstractValue { + const TrivialAbstractValue(); +} + +class TrivialAbstractValueDomain implements AbstractValueDomain { + const TrivialAbstractValueDomain(); + + @override + AbstractValue get dynamicType => const TrivialAbstractValue(); + + @override + void writeAbstractValueToDataSink(DataSink sink, AbstractValue value) {} + + @override + AbstractValue readAbstractValueFromDataSource(DataSource source) => + const TrivialAbstractValue(); + + @override + String getCompactText(AbstractValue value) => '?'; + + @override + AbstractBool isFixedLengthJsIndexable(AbstractValue value) => + AbstractBool.Maybe; + + @override + AbstractBool isJsIndexableAndIterable(AbstractValue value) => + AbstractBool.Maybe; + + @override + AbstractBool isJsIndexable(AbstractValue value) => AbstractBool.Maybe; + + @override + MemberEntity locateSingleMember(AbstractValue receiver, Selector selector) => + null; + + @override + AbstractBool isIn(AbstractValue subset, AbstractValue superset) => + AbstractBool.Maybe; + + @override + AbstractBool contains(AbstractValue superset, AbstractValue subset) => + AbstractBool.Maybe; + + @override + AbstractBool needsNoSuchMethodHandling( + AbstractValue receiver, Selector selector) => + AbstractBool.Maybe; + + @override + AbstractBool isTargetingMember( + AbstractValue receiver, MemberEntity member, Selector selector) => + AbstractBool.Maybe; + + @override + AbstractValue computeReceiver(Iterable members) => + const TrivialAbstractValue(); + + @override + PrimitiveConstantValue getPrimitiveValue(AbstractValue value) => null; + + @override + AbstractValue createPrimitiveValue( + AbstractValue originalValue, PrimitiveConstantValue value) => + const TrivialAbstractValue(); + + @override + bool isPrimitiveValue(AbstractValue value) => false; + + @override + MemberEntity getAllocationElement(AbstractValue value) => null; + + @override + Object getAllocationNode(AbstractValue value) => null; + + @override + AbstractValue getGeneralization(AbstractValue value) => + const TrivialAbstractValue(); + + @override + bool isSpecializationOf( + AbstractValue specialization, AbstractValue generalization) => + false; + + @override + AbstractValue getDictionaryValueForKey(AbstractValue value, String key) { + throw new UnsupportedError( + "TrivialAbstractValueDomain.getDictionaryValueForKey"); + } + + @override + bool containsDictionaryKey(AbstractValue value, String key) { + throw new UnsupportedError( + "TrivialAbstractValueDomain.containsDictionaryKey"); + } + + @override + AbstractValue createDictionaryValue( + AbstractValue originalValue, + Object allocationNode, + MemberEntity allocationElement, + AbstractValue key, + AbstractValue value, + Map mappings) => + const TrivialAbstractValue(); + + @override + bool isDictionary(AbstractValue value) => false; + + @override + AbstractValue getMapValueType(AbstractValue value) { + throw new UnsupportedError("TrivialAbstractValueDomain.getMapValueType"); + } + + @override + AbstractValue getMapKeyType(AbstractValue value) { + throw new UnsupportedError("TrivialAbstractValueDomain.getMapKeyType"); + } + + @override + AbstractValue createMapValue( + AbstractValue originalValue, + Object allocationNode, + MemberEntity allocationElement, + AbstractValue key, + AbstractValue value) => + const TrivialAbstractValue(); + + @override + bool isMap(AbstractValue value) => false; + + @override + int getContainerLength(AbstractValue value) => null; + + @override + AbstractValue getContainerElementType(AbstractValue value) { + throw new UnsupportedError( + "TrivialAbstractValueDomain.getContainerElementType"); + } + + @override + AbstractValue createContainerValue( + AbstractValue originalValue, + Object allocationNode, + MemberEntity allocationElement, + AbstractValue elementType, + int length) => + const TrivialAbstractValue(); + + @override + bool isContainer(AbstractValue value) => false; + + @override + AbstractValue computeAbstractValueForConstant(ConstantValue value) => + const TrivialAbstractValue(); + + @override + AbstractBool containsAll(AbstractValue a) => AbstractBool.Maybe; + + @override + AbstractBool areDisjoint(AbstractValue a, AbstractValue b) => + AbstractBool.Maybe; + + @override + AbstractValue intersection(AbstractValue a, AbstractValue b) => + const TrivialAbstractValue(); + + @override + AbstractValue unionOfMany(Iterable values) => + const TrivialAbstractValue(); + + @override + AbstractValue union(AbstractValue a, AbstractValue b) => + const TrivialAbstractValue(); + + @override + AbstractBool isPrimitiveOrNull(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isStringOrNull(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isString(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isBooleanOrNull(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isBoolean(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isDoubleOrNull(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isDouble(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isNumberOrNull(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isNumber(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isIntegerOrNull(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isPositiveIntegerOrNull(AbstractValue value) => + AbstractBool.Maybe; + + @override + AbstractBool isPositiveInteger(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isUInt31(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isUInt32(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isInteger(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isInterceptor(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isPrimitiveString(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isArray(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isMutableIndexable(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isMutableArray(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isExtendableArray(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isFixedArray(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isIndexablePrimitive(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isPrimitiveArray(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isPrimitiveBoolean(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isPrimitiveNumber(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isPrimitive(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isNull(AbstractValue value) => AbstractBool.Maybe; + + @override + ClassEntity getExactClass(AbstractValue value) => null; + + @override + AbstractBool isExactOrNull(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isExact(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isEmpty(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isInstanceOf(AbstractValue value, ClassEntity cls) => + AbstractBool.Maybe; + + @override + AbstractBool isInstanceOfOrNull(AbstractValue value, ClassEntity cls) => + AbstractBool.Maybe; + + @override + AbstractBool containsOnlyType(AbstractValue value, ClassEntity cls) => + AbstractBool.Maybe; + + @override + AbstractBool containsType(AbstractValue value, ClassEntity cls) => + AbstractBool.Maybe; + + @override + AbstractValue includeNull(AbstractValue value) => + const TrivialAbstractValue(); + + @override + AbstractValue excludeNull(AbstractValue value) => + const TrivialAbstractValue(); + + @override + AbstractBool couldBeTypedArray(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractBool isTypedArray(AbstractValue value) => AbstractBool.Maybe; + + @override + AbstractValue createNullableSubtype(ClassEntity cls) => + const TrivialAbstractValue(); + + @override + AbstractValue createNonNullSubtype(ClassEntity cls) => + const TrivialAbstractValue(); + + @override + AbstractValue createNonNullSubclass(ClassEntity cls) => + const TrivialAbstractValue(); + + @override + AbstractValue createNullableExact(ClassEntity cls) => + const TrivialAbstractValue(); + + @override + AbstractValue createNonNullExact(ClassEntity cls) => + const TrivialAbstractValue(); + + @override + AbstractValue get asyncStarStreamType => const TrivialAbstractValue(); + + @override + AbstractValue get asyncFutureType => const TrivialAbstractValue(); + + @override + AbstractValue get syncStarIterableType => const TrivialAbstractValue(); + + @override + AbstractValue get emptyType => const TrivialAbstractValue(); + + @override + AbstractValue get constMapType => const TrivialAbstractValue(); + + @override + AbstractValue get constListType => const TrivialAbstractValue(); + + @override + AbstractValue get positiveIntType => const TrivialAbstractValue(); + + @override + AbstractValue get uint32Type => const TrivialAbstractValue(); + + @override + AbstractValue get uint31Type => const TrivialAbstractValue(); + + @override + AbstractValue get fixedListType => const TrivialAbstractValue(); + + @override + AbstractValue get growableListType => const TrivialAbstractValue(); + + @override + AbstractValue get nullType => const TrivialAbstractValue(); + + @override + AbstractValue get nonNullType => const TrivialAbstractValue(); + + @override + AbstractValue get mapType => const TrivialAbstractValue(); + + @override + AbstractValue get listType => const TrivialAbstractValue(); + + @override + AbstractValue get stringType => const TrivialAbstractValue(); + + @override + AbstractValue get numType => const TrivialAbstractValue(); + + @override + AbstractValue get doubleType => const TrivialAbstractValue(); + + @override + AbstractValue get intType => const TrivialAbstractValue(); + + @override + AbstractValue get boolType => const TrivialAbstractValue(); + + @override + AbstractValue get functionType => const TrivialAbstractValue(); + + @override + AbstractValue get typeType => const TrivialAbstractValue(); +} diff --git a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart index fd30316d7c0ea..67f830a466a6a 100644 --- a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart +++ b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart @@ -114,7 +114,8 @@ class TypeGraphInferrer implements TypesInferrer { bool throwsAlways = // Always throws if the return type was inferred to be non-null empty. - returnType != null && abstractValueDomain.isEmpty(returnType); + returnType != null && + abstractValueDomain.isEmpty(returnType).isDefinitelyTrue; bool isCalledOnce = typeInformation.isCalledOnce(); //isMemberCalledOnce(member); diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart index d1993c5e4b7c6..08ba6e74a97ca 100644 --- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart +++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart @@ -1084,7 +1084,7 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation { Selector selector, AbstractValue mask, InferrerEngine inferrer) { JClosedWorld closedWorld = inferrer.closedWorld; if (mask == null) return null; - if (!inferrer.abstractValueDomain.isIntegerOrNull(mask)) { + if (inferrer.abstractValueDomain.isIntegerOrNull(mask).isPotentiallyFalse) { return null; } if (!selector.isCall && !selector.isOperator) return null; @@ -1265,7 +1265,8 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation { } })); } - if (isConditional && abstractValueDomain.canBeNull(receiver.type)) { + if (isConditional && + abstractValueDomain.isNull(receiver.type).isPotentiallyTrue) { // Conditional call sites (e.g. `a?.b`) may be null if the receiver is // null. result = abstractValueDomain.includeNull(result); @@ -1487,8 +1488,10 @@ class NarrowTypeInformation extends TypeInformation { AbstractValue intersection = abstractValueDomain.intersection(input, typeAnnotation); if (debug.ANOMALY_WARN) { - if (!abstractValueDomain.contains(input, intersection) || - !abstractValueDomain.contains(typeAnnotation, intersection)) { + if (abstractValueDomain.contains(input, intersection).isDefinitelyFalse || + abstractValueDomain + .contains(typeAnnotation, intersection) + .isDefinitelyFalse) { print("ANOMALY WARNING: narrowed $input to $intersection via " "$typeAnnotation"); } @@ -1732,8 +1735,8 @@ class MapTypeInformation extends TypeInformation with TracedTypeInformation { for (String key in typeInfoMap.keys) { TypeInformation value = typeInfoMap[key]; if (!abstractValueDomain.containsDictionaryKey(type, key) && - !abstractValueDomain.containsAll(value.type) && - !abstractValueDomain.canBeNull(value.type)) { + abstractValueDomain.containsAll(value.type).isDefinitelyFalse && + abstractValueDomain.isNull(value.type).isDefinitelyFalse) { return toTypeMask(inferrer); } if (abstractValueDomain.getDictionaryValueForKey(type, key) != diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart index ae9f8133c6304..cff86a131b8f9 100644 --- a/pkg/compiler/lib/src/inferrer/type_system.dart +++ b/pkg/compiler/lib/src/inferrer/type_system.dart @@ -310,7 +310,9 @@ class TypeSystem { TypeInformation refineReceiver( Selector selector, AbstractValue mask, TypeInformation receiver, {bool isConditional}) { - if (_abstractValueDomain.isExact(receiver.type)) return receiver; + if (_abstractValueDomain.isExact(receiver.type).isDefinitelyTrue) { + return receiver; + } AbstractValue otherType = _closedWorld.computeReceiverType(selector, mask); // Conditional sends (a?.b) can still narrow the possible types of `a`, // however, we still need to consider that `a` may be null. @@ -321,8 +323,8 @@ class TypeSystem { } // If this is refining to nullable subtype of `Object` just return // the receiver. We know the narrowing is useless. - if (_abstractValueDomain.canBeNull(otherType) && - _abstractValueDomain.containsAll(otherType)) { + if (_abstractValueDomain.isNull(otherType).isPotentiallyTrue && + _abstractValueDomain.containsAll(otherType).isPotentiallyTrue) { return receiver; } TypeInformation newType = @@ -365,7 +367,7 @@ class TypeSystem { if (isNullable) { otherType = _abstractValueDomain.includeNull(otherType); } - if (_abstractValueDomain.isExact(type.type)) { + if (_abstractValueDomain.isExact(type.type).isDefinitelyTrue) { return type; } else { TypeInformation newType = @@ -379,7 +381,7 @@ class TypeSystem { * Returns the non-nullable type of [type]. */ TypeInformation narrowNotNull(TypeInformation type) { - if (_abstractValueDomain.isExact(type.type)) { + if (_abstractValueDomain.isExact(type.type).isDefinitelyTrue) { return type; } TypeInformation newType = new NarrowTypeInformation(_abstractValueDomain, @@ -472,7 +474,9 @@ class TypeSystem { ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass; bool isTypedArray = typedDataClass != null && _closedWorld.classHierarchy.isInstantiated(typedDataClass) && - _abstractValueDomain.isInstanceOfOrNull(type.type, typedDataClass); + _abstractValueDomain + .isInstanceOfOrNull(type.type, typedDataClass) + .isDefinitelyTrue; bool isConst = (type.type == _abstractValueDomain.constListType); bool isFixed = (type.type == _abstractValueDomain.fixedListType) || isConst || @@ -648,7 +652,9 @@ class TypeSystem { // work the result will be `dynamic`. // TODO(sigmund): change to `mask == dynamicType` so we can continue to // track the non-nullable bit. - if (_abstractValueDomain.containsAll(mask)) return dynamicType; + if (_abstractValueDomain.containsAll(mask).isPotentiallyTrue) { + return dynamicType; + } list.add(mask); } @@ -657,7 +663,9 @@ class TypeSystem { newType = newType == null ? mask : _abstractValueDomain.union(newType, mask); // Likewise - stop early if we already reach dynamic. - if (_abstractValueDomain.containsAll(newType)) return dynamicType; + if (_abstractValueDomain.containsAll(newType).isPotentiallyTrue) { + return dynamicType; + } } return newType ?? _abstractValueDomain.emptyType; diff --git a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart index f85970fd5e031..2960a7ba89c2a 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart @@ -195,33 +195,33 @@ class CommonMasks implements AbstractValueDomain { commonElements.jsInterceptorClass, _closedWorld); @override - bool isTypedArray(TypeMask mask) { + AbstractBool isTypedArray(TypeMask mask) { // Just checking for `TypedData` is not sufficient, as it is an abstract // class any user-defined class can implement. So we also check for the // interface `JavaScriptIndexingBehavior`. ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass; - return typedDataClass != null && + return AbstractBool.trueOrMaybe(typedDataClass != null && _closedWorld.classHierarchy.isInstantiated(typedDataClass) && mask.satisfies(typedDataClass, _closedWorld) && mask.satisfies(_closedWorld.commonElements.jsIndexingBehaviorInterface, - _closedWorld); + _closedWorld)); } @override - bool couldBeTypedArray(TypeMask mask) { + AbstractBool couldBeTypedArray(TypeMask mask) { bool intersects(TypeMask type1, TypeMask type2) => !type1.intersection(type2, _closedWorld).isEmpty; // TODO(herhut): Maybe cache the TypeMask for typedDataClass and // jsIndexingBehaviourInterface. ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass; - return typedDataClass != null && + return AbstractBool.maybeOrFalse(typedDataClass != null && _closedWorld.classHierarchy.isInstantiated(typedDataClass) && intersects(mask, new TypeMask.subtype(typedDataClass, _closedWorld)) && intersects( mask, new TypeMask.subtype( _closedWorld.commonElements.jsIndexingBehaviorInterface, - _closedWorld)); + _closedWorld))); } @override @@ -256,19 +256,30 @@ class CommonMasks implements AbstractValueDomain { TypeMask includeNull(TypeMask mask) => mask.nullable(); @override - bool containsType(TypeMask typeMask, ClassEntity cls) { + AbstractBool containsType(TypeMask typeMask, ClassEntity cls) { + return AbstractBool.trueOrMaybe(_containsType(typeMask, cls)); + } + + bool _containsType(TypeMask typeMask, ClassEntity cls) { return _closedWorld.classHierarchy.isInstantiated(cls) && typeMask.contains(cls, _closedWorld); } @override - bool containsOnlyType(TypeMask typeMask, ClassEntity cls) { + AbstractBool containsOnlyType(TypeMask typeMask, ClassEntity cls) { + return AbstractBool.trueOrMaybe(_containsOnlyType(typeMask, cls)); + } + + bool _containsOnlyType(TypeMask typeMask, ClassEntity cls) { return _closedWorld.classHierarchy.isInstantiated(cls) && typeMask.containsOnly(cls); } @override - bool isInstanceOfOrNull(TypeMask typeMask, ClassEntity cls) { + AbstractBool isInstanceOfOrNull(TypeMask typeMask, ClassEntity cls) => + AbstractBool.trueOrMaybe(_isInstanceOfOrNull(typeMask, cls)); + + bool _isInstanceOfOrNull(TypeMask typeMask, ClassEntity cls) { return _closedWorld.isImplemented(cls) && typeMask.satisfies(cls, _closedWorld); } @@ -289,13 +300,16 @@ class CommonMasks implements AbstractValueDomain { } @override - bool isEmpty(TypeMask value) => value.isEmpty; + AbstractBool isEmpty(TypeMask value) => + AbstractBool.trueOrMaybe(value.isEmpty); @override - bool isExact(TypeMask value) => value.isExact && !value.isNullable; + AbstractBool isExact(TypeMask value) => + AbstractBool.trueOrMaybe(value.isExact && !value.isNullable); @override - bool isExactOrNull(TypeMask value) => value.isExact || isNull(value); + AbstractBool isExactOrNull(TypeMask value) => + AbstractBool.trueOrMaybe(value.isExact || _isNull(value)); @override ClassEntity getExactClass(TypeMask mask) { @@ -321,167 +335,204 @@ class CommonMasks implements AbstractValueDomain { } @override - bool canBeNull(TypeMask value) => value.isNullable; + AbstractBool isNull(TypeMask value) { + if (value.isNull) { + return AbstractBool.True; + } else if (value.isNullable) { + return AbstractBool.Maybe; + } else { + return AbstractBool.False; + } + } - @override - bool isNull(TypeMask value) => value.isNull; + bool _isNull(TypeMask value) => value.isNull; @override - bool canBePrimitive(TypeMask value) { - return canBePrimitiveNumber(value) || - canBePrimitiveArray(value) || - canBePrimitiveBoolean(value) || - canBePrimitiveString(value) || - isNull(value); + AbstractBool isPrimitive(TypeMask value) { + return AbstractBool.maybeOrFalse(_canBePrimitiveNumber(value) || + _canBePrimitiveArray(value) || + _canBePrimitiveBoolean(value) || + _canBePrimitiveString(value) || + _isNull(value)); } @override - bool canBePrimitiveNumber(TypeMask value) { + AbstractBool isPrimitiveNumber(TypeMask value) => + AbstractBool.maybeOrFalse(_canBePrimitiveNumber(value)); + + bool _canBePrimitiveNumber(TypeMask value) { // TODO(sra): It should be possible to test only jsDoubleClass and // jsUInt31Class, since all others are superclasses of these two. - return containsType(value, commonElements.jsNumberClass) || - containsType(value, commonElements.jsIntClass) || - containsType(value, commonElements.jsPositiveIntClass) || - containsType(value, commonElements.jsUInt32Class) || - containsType(value, commonElements.jsUInt31Class) || - containsType(value, commonElements.jsDoubleClass); + return _containsType(value, commonElements.jsNumberClass) || + _containsType(value, commonElements.jsIntClass) || + _containsType(value, commonElements.jsPositiveIntClass) || + _containsType(value, commonElements.jsUInt32Class) || + _containsType(value, commonElements.jsUInt31Class) || + _containsType(value, commonElements.jsDoubleClass); } @override - bool canBePrimitiveBoolean(TypeMask value) { - return containsType(value, commonElements.jsBoolClass); + AbstractBool isPrimitiveBoolean(TypeMask value) => + AbstractBool.maybeOrFalse(_canBePrimitiveBoolean(value)); + + bool _canBePrimitiveBoolean(TypeMask value) { + return _containsType(value, commonElements.jsBoolClass); } @override - bool canBePrimitiveArray(TypeMask value) { - return containsType(value, commonElements.jsArrayClass) || - containsType(value, commonElements.jsFixedArrayClass) || - containsType(value, commonElements.jsExtendableArrayClass) || - containsType(value, commonElements.jsUnmodifiableArrayClass); + AbstractBool isPrimitiveArray(TypeMask value) => + AbstractBool.maybeOrFalse(_canBePrimitiveArray(value)); + + bool _canBePrimitiveArray(TypeMask value) { + return _containsType(value, commonElements.jsArrayClass) || + _containsType(value, commonElements.jsFixedArrayClass) || + _containsType(value, commonElements.jsExtendableArrayClass) || + _containsType(value, commonElements.jsUnmodifiableArrayClass); } @override - bool isIndexablePrimitive(TypeMask value) { + AbstractBool isIndexablePrimitive(TypeMask value) => + AbstractBool.trueOrMaybe(_isIndexablePrimitive(value)); + + bool _isIndexablePrimitive(TypeMask value) { return value.containsOnlyString(_closedWorld) || - isInstanceOfOrNull(value, commonElements.jsIndexableClass); + _isInstanceOfOrNull(value, commonElements.jsIndexableClass); } @override - bool isFixedArray(TypeMask value) { + AbstractBool isFixedArray(TypeMask value) { // TODO(sra): Recognize the union of these types as well. - return containsOnlyType(value, commonElements.jsFixedArrayClass) || - containsOnlyType(value, commonElements.jsUnmodifiableArrayClass); + return AbstractBool.trueOrMaybe( + _containsOnlyType(value, commonElements.jsFixedArrayClass) || + _containsOnlyType(value, commonElements.jsUnmodifiableArrayClass)); } @override - bool isExtendableArray(TypeMask value) { - return containsOnlyType(value, commonElements.jsExtendableArrayClass); + AbstractBool isExtendableArray(TypeMask value) { + return AbstractBool.trueOrMaybe( + _containsOnlyType(value, commonElements.jsExtendableArrayClass)); } @override - bool isMutableArray(TypeMask value) { - return isInstanceOfOrNull(value, commonElements.jsMutableArrayClass); + AbstractBool isMutableArray(TypeMask value) { + return AbstractBool.trueOrMaybe( + _isInstanceOfOrNull(value, commonElements.jsMutableArrayClass)); } @override - bool isMutableIndexable(TypeMask value) { - return isInstanceOfOrNull(value, commonElements.jsMutableIndexableClass); + AbstractBool isMutableIndexable(TypeMask value) { + return AbstractBool.trueOrMaybe( + _isInstanceOfOrNull(value, commonElements.jsMutableIndexableClass)); } @override - bool isArray(TypeMask value) { - return isInstanceOfOrNull(value, commonElements.jsArrayClass); + AbstractBool isArray(TypeMask value) { + return AbstractBool.trueOrMaybe( + _isInstanceOfOrNull(value, commonElements.jsArrayClass)); } @override - bool canBePrimitiveString(TypeMask value) { - return containsType(value, commonElements.jsStringClass); + AbstractBool isPrimitiveString(TypeMask value) => + AbstractBool.maybeOrFalse(_canBePrimitiveString(value)); + + bool _canBePrimitiveString(TypeMask value) { + return _containsType(value, commonElements.jsStringClass); } @override - bool isInteger(TypeMask value) { - return value.containsOnlyInt(_closedWorld) && !value.isNullable; + AbstractBool isInteger(TypeMask value) { + return AbstractBool.trueOrMaybe( + value.containsOnlyInt(_closedWorld) && !value.isNullable); } @override - bool isUInt32(TypeMask value) { - return !value.isNullable && - isInstanceOfOrNull(value, commonElements.jsUInt32Class); + AbstractBool isUInt32(TypeMask value) { + return AbstractBool.trueOrMaybe(!value.isNullable && + _isInstanceOfOrNull(value, commonElements.jsUInt32Class)); } @override - bool isUInt31(TypeMask value) { - return !value.isNullable && - isInstanceOfOrNull(value, commonElements.jsUInt31Class); + AbstractBool isUInt31(TypeMask value) { + return AbstractBool.trueOrMaybe(!value.isNullable && + _isInstanceOfOrNull(value, commonElements.jsUInt31Class)); } @override - bool isPositiveInteger(TypeMask value) { - return !value.isNullable && - isInstanceOfOrNull(value, commonElements.jsPositiveIntClass); + AbstractBool isPositiveInteger(TypeMask value) { + return AbstractBool.trueOrMaybe(!value.isNullable && + _isInstanceOfOrNull(value, commonElements.jsPositiveIntClass)); } @override - bool isPositiveIntegerOrNull(TypeMask value) { - return isInstanceOfOrNull(value, commonElements.jsPositiveIntClass); + AbstractBool isPositiveIntegerOrNull(TypeMask value) { + return AbstractBool.trueOrMaybe( + _isInstanceOfOrNull(value, commonElements.jsPositiveIntClass)); } @override - bool isIntegerOrNull(TypeMask value) { - return value.containsOnlyInt(_closedWorld); + AbstractBool isIntegerOrNull(TypeMask value) { + return AbstractBool.trueOrMaybe(value.containsOnlyInt(_closedWorld)); } @override - bool isNumber(TypeMask value) { - return value.containsOnlyNum(_closedWorld) && !value.isNullable; + AbstractBool isNumber(TypeMask value) { + return AbstractBool.trueOrMaybe( + value.containsOnlyNum(_closedWorld) && !value.isNullable); } @override - bool isNumberOrNull(TypeMask value) { + AbstractBool isNumberOrNull(TypeMask value) => + AbstractBool.trueOrMaybe(_isNumberOrNull(value)); + + bool _isNumberOrNull(TypeMask value) { return value.containsOnlyNum(_closedWorld); } @override - bool isDouble(TypeMask value) { - return value.containsOnlyDouble(_closedWorld) && !value.isNullable; + AbstractBool isDouble(TypeMask value) { + return AbstractBool.trueOrMaybe( + value.containsOnlyDouble(_closedWorld) && !value.isNullable); } @override - bool isDoubleOrNull(TypeMask value) { - return value.containsOnlyDouble(_closedWorld); + AbstractBool isDoubleOrNull(TypeMask value) { + return AbstractBool.trueOrMaybe(value.containsOnlyDouble(_closedWorld)); } @override - bool isBoolean(TypeMask value) { - return value.containsOnlyBool(_closedWorld) && !value.isNullable; + AbstractBool isBoolean(TypeMask value) { + return AbstractBool.trueOrMaybe( + value.containsOnlyBool(_closedWorld) && !value.isNullable); } @override - bool isBooleanOrNull(TypeMask value) { + AbstractBool isBooleanOrNull(TypeMask value) => + AbstractBool.trueOrMaybe(_isBooleanOrNull(value)); + + bool _isBooleanOrNull(TypeMask value) { return value.containsOnlyBool(_closedWorld); } @override - bool isString(TypeMask value) { - return value.containsOnlyString(_closedWorld) && !value.isNullable; + AbstractBool isString(TypeMask value) { + return AbstractBool.trueOrMaybe( + value.containsOnlyString(_closedWorld) && !value.isNullable); } @override - bool isStringOrNull(TypeMask value) { - return value.containsOnlyString(_closedWorld); + AbstractBool isStringOrNull(TypeMask value) { + return AbstractBool.trueOrMaybe(value.containsOnlyString(_closedWorld)); } @override - bool isPrimitive(TypeMask value) { - return (isPrimitiveOrNull(value) && !value.isNullable) || isNull(value); - } + AbstractBool isPrimitiveOrNull(TypeMask value) => + AbstractBool.trueOrMaybe(_isPrimitiveOrNull(value)); - @override - bool isPrimitiveOrNull(TypeMask value) { - return isIndexablePrimitive(value) || - isNumberOrNull(value) || - isBooleanOrNull(value) || - isNull(value); + bool _isPrimitiveOrNull(TypeMask value) { + return _isIndexablePrimitive(value) || + _isNumberOrNull(value) || + _isBooleanOrNull(value) || + _isNull(value); } @override @@ -492,10 +543,12 @@ class CommonMasks implements AbstractValueDomain { a.intersection(b, _closedWorld); @override - bool areDisjoint(TypeMask a, TypeMask b) => a.isDisjoint(b, _closedWorld); + AbstractBool areDisjoint(TypeMask a, TypeMask b) => + AbstractBool.trueOrMaybe(a.isDisjoint(b, _closedWorld)); @override - bool containsAll(TypeMask a) => a.containsAll(_closedWorld); + AbstractBool containsAll(TypeMask a) => + AbstractBool.maybeOrFalse(a.containsAll(_closedWorld)); @override AbstractValue computeAbstractValueForConstant(ConstantValue value) { @@ -574,25 +627,29 @@ class CommonMasks implements AbstractValueDomain { } @override - bool canHit( + AbstractBool isTargetingMember( covariant TypeMask receiver, MemberEntity member, Selector selector) { - return receiver.canHit(member, selector, _closedWorld); + return AbstractBool.maybeOrFalse( + receiver.canHit(member, selector, _closedWorld)); } @override - bool needsNoSuchMethodHandling( + AbstractBool needsNoSuchMethodHandling( covariant TypeMask receiver, Selector selector) { - return receiver.needsNoSuchMethodHandling(selector, _closedWorld); + return AbstractBool.trueOrFalse( + receiver.needsNoSuchMethodHandling(selector, _closedWorld)); } @override - bool contains(covariant TypeMask superset, covariant TypeMask subset) { - return superset.containsMask(subset, _closedWorld); + AbstractBool contains( + covariant TypeMask superset, covariant TypeMask subset) { + return AbstractBool.maybeOrFalse( + superset.containsMask(subset, _closedWorld)); } @override - bool isIn(covariant TypeMask subset, covariant TypeMask superset) { - return subset.isInMask(superset, _closedWorld); + AbstractBool isIn(covariant TypeMask subset, covariant TypeMask superset) { + return AbstractBool.trueOrMaybe(subset.isInMask(superset, _closedWorld)); } @override @@ -602,41 +659,42 @@ class CommonMasks implements AbstractValueDomain { } @override - bool isJsIndexable(TypeMask mask) { - return mask.satisfies( - _closedWorld.commonElements.jsIndexableClass, _closedWorld); + AbstractBool isJsIndexable(TypeMask mask) { + return AbstractBool.trueOrMaybe(mask.satisfies( + _closedWorld.commonElements.jsIndexableClass, _closedWorld)); } @override - bool isJsIndexableAndIterable(covariant TypeMask mask) { - return mask != null && + AbstractBool isJsIndexableAndIterable(covariant TypeMask mask) { + return AbstractBool.trueOrMaybe(mask != null && mask.satisfies( _closedWorld.commonElements.jsIndexableClass, _closedWorld) && // String is indexable but not iterable. !mask.satisfies( - _closedWorld.commonElements.jsStringClass, _closedWorld); + _closedWorld.commonElements.jsStringClass, _closedWorld)); } @override - bool isFixedLengthJsIndexable(covariant TypeMask mask) { + AbstractBool isFixedLengthJsIndexable(covariant TypeMask mask) { if (mask.isContainer && (mask as ContainerTypeMask).length != null) { // A container on which we have inferred the length. - return true; + return AbstractBool.True; } // TODO(sra): Recognize any combination of fixed length indexables. if (mask.containsOnly(_closedWorld.commonElements.jsFixedArrayClass) || mask.containsOnly( _closedWorld.commonElements.jsUnmodifiableArrayClass) || mask.containsOnlyString(_closedWorld) || - _closedWorld.abstractValueDomain.isTypedArray(mask)) { - return true; + _closedWorld.abstractValueDomain.isTypedArray(mask).isDefinitelyTrue) { + return AbstractBool.True; } - return false; + return AbstractBool.Maybe; } @override - bool canBeInterceptor(TypeMask value) { - return !interceptorType.isDisjoint(value, _closedWorld); + AbstractBool isInterceptor(TypeMask value) { + return AbstractBool.maybeOrFalse( + !interceptorType.isDisjoint(value, _closedWorld)); } @override diff --git a/pkg/compiler/lib/src/js_backend/interceptor_data.dart b/pkg/compiler/lib/src/js_backend/interceptor_data.dart index 87f9f7de631aa..0e364c01f42dd 100644 --- a/pkg/compiler/lib/src/js_backend/interceptor_data.dart +++ b/pkg/compiler/lib/src/js_backend/interceptor_data.dart @@ -182,7 +182,9 @@ class InterceptorDataImpl implements InterceptorData { return elements.any((element) { return selector.applies(element) && (mask == null || - closedWorld.abstractValueDomain.canHit(mask, element, selector)); + closedWorld.abstractValueDomain + .isTargetingMember(mask, element, selector) + .isPotentiallyTrue); }); } diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart index b3d88a5d96b41..22b6f9d819e86 100644 --- a/pkg/compiler/lib/src/js_model/js_strategy.dart +++ b/pkg/compiler/lib/src/js_model/js_strategy.dart @@ -259,7 +259,7 @@ class KernelToTypeInferenceMapImpl implements KernelToTypeInferenceMap { bool isJsIndexableIterator( ir.ForInStatement node, AbstractValueDomain abstractValueDomain) { AbstractValue mask = typeOfIterator(node); - return abstractValueDomain.isJsIndexableAndIterable(mask); + return abstractValueDomain.isJsIndexableAndIterable(mask).isDefinitelyTrue; } AbstractValue inferredIndexType(ir.ForInStatement node) { diff --git a/pkg/compiler/lib/src/js_model/js_world.dart b/pkg/compiler/lib/src/js_model/js_world.dart index 9493cf9922efe..4979d38c8e87b 100644 --- a/pkg/compiler/lib/src/js_model/js_world.dart +++ b/pkg/compiler/lib/src/js_model/js_world.dart @@ -429,8 +429,9 @@ class JsClosedWorld implements JClosedWorld { return selector.name == Identifiers.call && (receiver == null || // TODO(johnniwinther): Should this have been `intersects` instead? - abstractValueDomain.contains( - receiver, abstractValueDomain.functionType)); + abstractValueDomain + .contains(receiver, abstractValueDomain.functionType) + .isPotentiallyTrue); } AbstractValue computeReceiverType(Selector selector, AbstractValue receiver) { diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart index e56c4b8da598a..1f40b69d4e26c 100644 --- a/pkg/compiler/lib/src/ssa/builder_kernel.dart +++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart @@ -1031,8 +1031,8 @@ class KernelSsaGraphBuilder extends ir.Visitor } } if (const bool.fromEnvironment('unreachable-throw')) { - var emptyParameters = parameters.values - .where((p) => abstractValueDomain.isEmpty(p.instructionType)); + var emptyParameters = parameters.values.where((p) => + abstractValueDomain.isEmpty(p.instructionType).isDefinitelyTrue); if (emptyParameters.length > 0) { addComment('${emptyParameters} inferred as [empty]'); add(new HInvokeStatic( @@ -1627,8 +1627,9 @@ class KernelSsaGraphBuilder extends ir.Visitor node.iterable.accept(this); array = pop(); - isFixed = - abstractValueDomain.isFixedLengthJsIndexable(array.instructionType); + isFixed = abstractValueDomain + .isFixedLengthJsIndexable(array.instructionType) + .isDefinitelyTrue; localsHandler.updateLocal( indexVariable, graph.addConstantInt(0, closedWorld), sourceInformation: sourceInformation); @@ -2774,7 +2775,7 @@ class KernelSsaGraphBuilder extends ir.Visitor AbstractValue type = _typeInferenceMap.typeOfListLiteral(node, abstractValueDomain); - if (!abstractValueDomain.containsAll(type)) { + if (abstractValueDomain.containsAll(type).isDefinitelyFalse) { listInstruction.instructionType = type; } stack.add(listInstruction); @@ -2826,8 +2827,8 @@ class KernelSsaGraphBuilder extends ir.Visitor // We lift this common call pattern into a helper function to save space // in the output. - if (typeInputs - .every((HInstruction input) => input.isNull(abstractValueDomain))) { + if (typeInputs.every((HInstruction input) => + input.isNull(abstractValueDomain).isDefinitelyTrue)) { if (constructorArgs.isEmpty) { constructor = _commonElements.mapLiteralUntypedEmptyMaker; } else { @@ -3416,7 +3417,7 @@ class KernelSsaGraphBuilder extends ir.Visitor "Unexpected arguments. " "Expected 1-2 argument, actual: $arguments.")); HInstruction lengthInput = arguments.first; - if (!lengthInput.isNumber(abstractValueDomain)) { + if (lengthInput.isNumber(abstractValueDomain).isPotentiallyFalse) { HTypeConversion conversion = new HTypeConversion( null, HTypeConversion.ARGUMENT_TYPE_CHECK, @@ -3439,14 +3440,15 @@ class KernelSsaGraphBuilder extends ir.Visitor // TODO(sra): Array allocation should be an instruction so that canThrow // can depend on a length type discovered in optimization. bool canThrow = true; - if (lengthInput.isUInt32(abstractValueDomain)) { + if (lengthInput.isUInt32(abstractValueDomain).isDefinitelyTrue) { canThrow = false; } var inferredType = _inferredTypeOfNewList(invocation); - resultType = abstractValueDomain.containsAll(inferredType) - ? abstractValueDomain.fixedListType - : inferredType; + resultType = + abstractValueDomain.containsAll(inferredType).isPotentiallyTrue + ? abstractValueDomain.fixedListType + : inferredType; HForeignCode foreign = new HForeignCode( code, resultType, [lengthInput], nativeBehavior: behavior, @@ -3467,9 +3469,10 @@ class KernelSsaGraphBuilder extends ir.Visitor } else if (isGrowableListConstructorCall) { push(buildLiteralList([])); var inferredType = _inferredTypeOfNewList(invocation); - resultType = abstractValueDomain.containsAll(inferredType) - ? abstractValueDomain.growableListType - : inferredType; + resultType = + abstractValueDomain.containsAll(inferredType).isPotentiallyTrue + ? abstractValueDomain.growableListType + : inferredType; stack.last.instructionType = resultType; } else if (isJSArrayTypedConstructor) { // TODO(sra): Instead of calling the identity-like factory constructor, @@ -4117,11 +4120,14 @@ class KernelSsaGraphBuilder extends ir.Visitor if (trustedMask != null) { // We only allow the type argument to narrow `dynamic`, which probably // comes from an unspecified return type in the NativeBehavior. - if (abstractValueDomain.containsAll(code.instructionType)) { + if (abstractValueDomain + .containsAll(code.instructionType) + .isPotentiallyTrue) { // Overwrite the type with the narrower type. code.instructionType = trustedMask; - } else if (abstractValueDomain.contains( - trustedMask, code.instructionType)) { + } else if (abstractValueDomain + .contains(trustedMask, code.instructionType) + .isPotentiallyTrue) { // It is acceptable for the type parameter to be broader than the // specified type. } else { @@ -4968,7 +4974,9 @@ class KernelSsaGraphBuilder extends ir.Visitor if (selector != null) { if (!selector.applies(function)) return false; if (mask != null && - !abstractValueDomain.canHit(mask, function, selector)) { + abstractValueDomain + .isTargetingMember(mask, function, selector) + .isDefinitelyFalse) { return false; } } @@ -4978,7 +4986,9 @@ class KernelSsaGraphBuilder extends ir.Visitor // Don't inline operator== methods if the parameter can be null. if (function.name == '==') { if (function.enclosingClass != commonElements.objectClass && - providedArguments[1].canBeNull(abstractValueDomain)) { + providedArguments[1] + .isNull(abstractValueDomain) + .isPotentiallyTrue) { return false; } } @@ -5127,7 +5137,8 @@ class KernelSsaGraphBuilder extends ir.Visitor // NoSuchMethodError message as if we had called it. if (function.isInstanceMember && function is! ConstructorBodyEntity && - (mask == null || abstractValueDomain.canBeNull(mask))) { + (mask == null || + abstractValueDomain.isNull(mask).isPotentiallyTrue)) { add(new HFieldGet( null, providedArguments[0], abstractValueDomain.dynamicType, isAssignable: false) diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart index 9e4857ae2b08e..d39b92a1033d4 100644 --- a/pkg/compiler/lib/src/ssa/codegen.dart +++ b/pkg/compiler/lib/src/ssa/codegen.dart @@ -323,7 +323,9 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { } bool requiresUintConversion(HInstruction instruction) { - if (instruction.isUInt31(_abstractValueDomain)) return false; + if (instruction.isUInt31(_abstractValueDomain).isDefinitelyTrue) { + return false; + } if (bitWidth(instruction) <= 31) return false; // If the result of a bit-operation is only used by other bit // operations, we do not have to convert to an unsigned integer. @@ -1546,11 +1548,11 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { visitShiftRight(HShiftRight node) => visitBitInvokeBinary(node, '>>>'); visitTruncatingDivide(HTruncatingDivide node) { - assert(node.isUInt31(_abstractValueDomain)); + assert(node.isUInt31(_abstractValueDomain).isDefinitelyTrue); // TODO(karlklose): Enable this assertion again when type propagation is // fixed. Issue 23555. // assert(node.left.isUInt32(compiler)); - assert(node.right.isPositiveInteger(_abstractValueDomain)); + assert(node.right.isPositiveInteger(_abstractValueDomain).isDefinitelyTrue); use(node.left); js.Expression jsLeft = pop(); use(node.right); @@ -2336,15 +2338,15 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { HInstruction left = relational.left; HInstruction right = relational.right; - if (left.isStringOrNull(_abstractValueDomain) && - right.isStringOrNull(_abstractValueDomain)) { + if (left.isStringOrNull(_abstractValueDomain).isDefinitelyTrue && + right.isStringOrNull(_abstractValueDomain).isDefinitelyTrue) { return true; } // This optimization doesn't work for NaN, so we only do it if the // type is known to be an integer. - return left.isInteger(_abstractValueDomain) && - right.isInteger(_abstractValueDomain); + return left.isInteger(_abstractValueDomain).isDefinitelyTrue && + right.isInteger(_abstractValueDomain).isDefinitelyTrue; } bool handledBySpecialCase = false; @@ -2480,13 +2482,15 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { js.Expression over; if (node.staticChecks != HBoundsCheck.ALWAYS_ABOVE_ZERO) { use(node.index); - if (node.index.isInteger(_abstractValueDomain)) { + if (node.index.isInteger(_abstractValueDomain).isDefinitelyTrue) { under = js.js("# < 0", pop()); } else { js.Expression jsIndex = pop(); under = js.js("# >>> 0 !== #", [jsIndex, jsIndex]); } - } else if (!node.index.isInteger(_abstractValueDomain)) { + } else if (node.index + .isInteger(_abstractValueDomain) + .isPotentiallyFalse) { checkInt(node.index, '!=='); under = pop(); } @@ -2607,10 +2611,10 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { void visitStringify(HStringify node) { HInstruction input = node.inputs.first; - if (input.isString(_abstractValueDomain)) { + if (input.isString(_abstractValueDomain).isDefinitelyTrue) { use(input); - } else if (input.isInteger(_abstractValueDomain) || - input.isBoolean(_abstractValueDomain)) { + } else if (input.isInteger(_abstractValueDomain).isDefinitelyTrue || + input.isBoolean(_abstractValueDomain).isDefinitelyTrue) { // JavaScript's + operator with a string for the left operand will convert // the right operand to a string, and the conversion result is correct. use(input); @@ -3000,9 +3004,9 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { } else if (type.isFunctionType) { checkType(input, interceptor, type, sourceInformation, negative: negative); - } else if ((input.canBePrimitive(_abstractValueDomain) && - !input.canBePrimitiveArray(_abstractValueDomain)) || - input.canBeNull(_abstractValueDomain)) { + } else if ((input.isPrimitive(_abstractValueDomain).isPotentiallyTrue && + input.isPrimitiveArray(_abstractValueDomain).isDefinitelyFalse) || + input.isNull(_abstractValueDomain).isPotentiallyTrue) { checkObject(input, relation, node.sourceInformation); js.Expression objectTest = pop(); checkType(input, interceptor, type, sourceInformation, @@ -3029,13 +3033,14 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { AbstractValue inputType = node.inputType ?? input.instructionType; AbstractValue checkedType = node.checkedType; // This path is no longer used for indexable primitive types. - assert(!_abstractValueDomain.isJsIndexable(checkedType)); + assert(_abstractValueDomain.isJsIndexable(checkedType).isPotentiallyFalse); // Figure out if it is beneficial to use a null check. V8 generally prefers // 'typeof' checks, but for integers we cannot compile this test into a // single typeof check so the null check is cheaper. - bool isIntCheck = _abstractValueDomain.isIntegerOrNull(checkedType); - bool turnIntoNumCheck = - isIntCheck && _abstractValueDomain.isIntegerOrNull(inputType); + bool isIntCheck = + _abstractValueDomain.isIntegerOrNull(checkedType).isDefinitelyTrue; + bool turnIntoNumCheck = isIntCheck && + _abstractValueDomain.isIntegerOrNull(inputType).isDefinitelyTrue; bool turnIntoNullCheck = !turnIntoNumCheck && (_abstractValueDomain.includeNull(checkedType) == inputType) && isIntCheck; @@ -3049,15 +3054,19 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { checkBigInt(input, '!==', input.sourceInformation); return pop(); } else if (turnIntoNumCheck || - _abstractValueDomain.isNumberOrNull(checkedType)) { + _abstractValueDomain.isNumberOrNull(checkedType).isDefinitelyTrue) { // input is !num checkNum(input, '!==', input.sourceInformation); return pop(); - } else if (_abstractValueDomain.isBooleanOrNull(checkedType)) { + } else if (_abstractValueDomain + .isBooleanOrNull(checkedType) + .isDefinitelyTrue) { // input is !bool checkBool(input, '!==', input.sourceInformation); return pop(); - } else if (_abstractValueDomain.isStringOrNull(checkedType)) { + } else if (_abstractValueDomain + .isStringOrNull(checkedType) + .isDefinitelyTrue) { // input is !string checkString(input, '!==', input.sourceInformation); return pop(); diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart index 6cebe4d8218df..8a13587d9a443 100644 --- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart +++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart @@ -88,8 +88,12 @@ class SsaInstructionSelection extends HBaseVisitor { String simpleOp(HInstruction left, HInstruction right) { AbstractValue leftType = left.instructionType; AbstractValue rightType = right.instructionType; - if (!_abstractValueDomain.canBeNull(leftType)) return '==='; - if (!_abstractValueDomain.canBeNull(rightType)) return '==='; + if (_abstractValueDomain.isNull(leftType).isDefinitelyFalse) { + return '==='; + } + if (_abstractValueDomain.isNull(rightType).isDefinitelyFalse) { + return '==='; + } // Dart `null` is implemented by JavaScript `null` and `undefined` which are // not strict-equals, so we can't use `===`. We would like to use `==` but @@ -98,16 +102,16 @@ class SsaInstructionSelection extends HBaseVisitor { return '=='; } - if (_abstractValueDomain.isNumberOrNull(leftType) && - _abstractValueDomain.isNumberOrNull(rightType)) { + if (_abstractValueDomain.isNumberOrNull(leftType).isDefinitelyTrue && + _abstractValueDomain.isNumberOrNull(rightType).isDefinitelyTrue) { return '=='; } - if (_abstractValueDomain.isStringOrNull(leftType) && - _abstractValueDomain.isStringOrNull(rightType)) { + if (_abstractValueDomain.isStringOrNull(leftType).isDefinitelyTrue && + _abstractValueDomain.isStringOrNull(rightType).isDefinitelyTrue) { return '=='; } - if (_abstractValueDomain.isBooleanOrNull(leftType) && - _abstractValueDomain.isBooleanOrNull(rightType)) { + if (_abstractValueDomain.isBooleanOrNull(leftType).isDefinitelyTrue && + _abstractValueDomain.isBooleanOrNull(rightType).isDefinitelyTrue) { return '=='; } @@ -115,7 +119,8 @@ class SsaInstructionSelection extends HBaseVisitor { // primitive (Number, String, Symbol and, indirectly, Boolean). We use // 'intercepted' types as a proxy for all the primitive types. bool intercepted(AbstractValue type) => _abstractValueDomain - .canBeInterceptor(_abstractValueDomain.excludeNull(type)); + .isInterceptor(_abstractValueDomain.excludeNull(type)) + .isPotentiallyTrue; if (intercepted(leftType)) return null; if (intercepted(rightType)) return null; @@ -271,7 +276,9 @@ class SsaInstructionSelection extends HBaseVisitor { HInstruction bitop(String assignOp) { // HBitAnd, HBitOr etc. are more difficult because HBitAnd(a.x, y) // sometimes needs to be forced to unsigned: a.x = (a.x & y) >>> 0. - if (op.isUInt31(_abstractValueDomain)) return simpleBinary(assignOp); + if (op.isUInt31(_abstractValueDomain).isDefinitelyTrue) { + return simpleBinary(assignOp); + } return noMatchingRead(); } diff --git a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart index d8127a332fc04..0ea8717f9ce81 100644 --- a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart +++ b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart @@ -92,11 +92,11 @@ class SsaSimplifyInterceptors extends HBaseVisitor bool canUseSelfForInterceptor(HInstruction receiver, {Set interceptedClasses}) { - if (receiver.canBePrimitive(_abstractValueDomain)) { + if (receiver.isPrimitive(_abstractValueDomain).isPotentiallyTrue) { // Primitives always need interceptors. return false; } - if (receiver.canBeNull(_abstractValueDomain)) { + if (receiver.isNull(_abstractValueDomain).isPotentiallyTrue) { if (interceptedClasses == null || interceptedClasses.contains(_commonElements.jsNullClass)) { // Need the JSNull interceptor. @@ -106,7 +106,9 @@ class SsaSimplifyInterceptors extends HBaseVisitor // All intercepted classes extend `Interceptor`, so if the receiver can't be // a class extending `Interceptor` then it can be called directly. - return !_abstractValueDomain.canBeInterceptor(receiver.instructionType); + return _abstractValueDomain + .isInterceptor(receiver.instructionType) + .isDefinitelyFalse; } HInstruction tryComputeConstantInterceptor( @@ -129,21 +131,21 @@ class SsaSimplifyInterceptors extends HBaseVisitor ClassEntity tryComputeConstantInterceptorFromType( AbstractValue type, Set interceptedClasses) { - if (_abstractValueDomain.canBeNull(type)) { - if (_abstractValueDomain.isNull(type)) { + if (_abstractValueDomain.isNull(type).isPotentiallyTrue) { + if (_abstractValueDomain.isNull(type).isDefinitelyTrue) { return _commonElements.jsNullClass; } - } else if (_abstractValueDomain.isIntegerOrNull(type)) { + } else if (_abstractValueDomain.isIntegerOrNull(type).isDefinitelyTrue) { return _commonElements.jsIntClass; - } else if (_abstractValueDomain.isDoubleOrNull(type)) { + } else if (_abstractValueDomain.isDoubleOrNull(type).isDefinitelyTrue) { return _commonElements.jsDoubleClass; - } else if (_abstractValueDomain.isBooleanOrNull(type)) { + } else if (_abstractValueDomain.isBooleanOrNull(type).isDefinitelyTrue) { return _commonElements.jsBoolClass; - } else if (_abstractValueDomain.isStringOrNull(type)) { + } else if (_abstractValueDomain.isStringOrNull(type).isDefinitelyTrue) { return _commonElements.jsStringClass; - } else if (_abstractValueDomain.isArray(type)) { + } else if (_abstractValueDomain.isArray(type).isDefinitelyTrue) { return _commonElements.jsArrayClass; - } else if (_abstractValueDomain.isNumberOrNull(type) && + } else if (_abstractValueDomain.isNumberOrNull(type).isDefinitelyTrue && !interceptedClasses.contains(_commonElements.jsIntClass) && !interceptedClasses.contains(_commonElements.jsDoubleClass)) { // If the method being intercepted is not defined in [int] or [double] we @@ -302,12 +304,18 @@ class SsaSimplifyInterceptors extends HBaseVisitor // `NoSuchMethodError`s, and if the receiver was not null we would have a // constant interceptor `C`. Then we can use `(receiver && C)` for the // interceptor. - if (receiver.canBeNull(_abstractValueDomain)) { + if (receiver.isNull(_abstractValueDomain).isPotentiallyTrue) { if (!interceptedClasses.contains(_commonElements.jsNullClass)) { // Can use `(receiver && C)` only if receiver is either null or truthy. - if (!(receiver.canBePrimitiveNumber(_abstractValueDomain) || - receiver.canBePrimitiveBoolean(_abstractValueDomain) || - receiver.canBePrimitiveString(_abstractValueDomain))) { + if (!(receiver + .isPrimitiveNumber(_abstractValueDomain) + .isPotentiallyTrue || + receiver + .isPrimitiveBoolean(_abstractValueDomain) + .isPotentiallyTrue || + receiver + .isPrimitiveString(_abstractValueDomain) + .isPotentiallyTrue)) { ClassEntity interceptorClass = tryComputeConstantInterceptorFromType( _abstractValueDomain.excludeNull(receiver.instructionType), interceptedClasses); diff --git a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart index 363a1a596f187..88f06772fb2f9 100644 --- a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart +++ b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart @@ -125,11 +125,13 @@ class IndexAssignSpecializer extends InvokeDynamicSpecializer { JClosedWorld closedWorld) { HInstruction receiver = instruction.inputs[1]; HInstruction index = instruction.inputs[2]; - if (!receiver.isMutableIndexable(closedWorld.abstractValueDomain)) { + if (receiver + .isMutableIndexable(closedWorld.abstractValueDomain) + .isPotentiallyFalse) { return null; } // TODO(johnniwinther): Merge this and the following if statement. - if (!index.isInteger(closedWorld.abstractValueDomain) && + if (index.isInteger(closedWorld.abstractValueDomain).isPotentiallyFalse && options.parameterCheckPolicy.isEmitted) { // We want the right checked mode error. return null; @@ -159,9 +161,11 @@ class IndexAssignSpecializer extends InvokeDynamicSpecializer { if (instruction.element != null) { ClassEntity cls = instruction.element.enclosingClass; if (cls == commonElements.typedArrayOfIntClass) { - return value.isInteger(closedWorld.abstractValueDomain); + return value + .isInteger(closedWorld.abstractValueDomain) + .isDefinitelyTrue; } else if (cls == commonElements.typedArrayOfDoubleClass) { - return value.isNumber(closedWorld.abstractValueDomain); + return value.isNumber(closedWorld.abstractValueDomain).isDefinitelyTrue; } } @@ -187,12 +191,15 @@ class IndexSpecializer extends InvokeDynamicSpecializer { CompilerOptions options, JCommonElements commonElements, JClosedWorld closedWorld) { - if (!instruction.inputs[1] - .isIndexablePrimitive(closedWorld.abstractValueDomain)) { + if (instruction.inputs[1] + .isIndexablePrimitive(closedWorld.abstractValueDomain) + .isPotentiallyFalse) { return null; } // TODO(johnniwinther): Merge this and the following if statement. - if (!instruction.inputs[2].isInteger(closedWorld.abstractValueDomain) && + if (instruction.inputs[2] + .isInteger(closedWorld.abstractValueDomain) + .isPotentiallyFalse && options.parameterCheckPolicy.isEmitted) { // We want the right checked mode error. return null; @@ -221,7 +228,8 @@ class BitNotSpecializer extends InvokeDynamicSpecializer { // All bitwise operations on primitive types either produce an // integer or throw an error. if (instruction.inputs[1] - .isPrimitiveOrNull(closedWorld.abstractValueDomain)) { + .isPrimitiveOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { return closedWorld.abstractValueDomain.uint32Type; } return super @@ -236,7 +244,7 @@ class BitNotSpecializer extends InvokeDynamicSpecializer { JCommonElements commonElements, JClosedWorld closedWorld) { HInstruction input = instruction.inputs[1]; - if (input.isNumber(closedWorld.abstractValueDomain)) { + if (input.isNumber(closedWorld.abstractValueDomain).isDefinitelyTrue) { return new HBitNot( input, instruction.selector, @@ -260,13 +268,19 @@ class UnaryNegateSpecializer extends InvokeDynamicSpecializer { CompilerOptions options, JClosedWorld closedWorld) { HInstruction operand = instruction.inputs[1]; - if (operand.isNumberOrNull(closedWorld.abstractValueDomain)) { + if (operand + .isNumberOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { // We have integer subclasses that represent ranges, so widen any int // subclass to full integer. - if (operand.isIntegerOrNull(closedWorld.abstractValueDomain)) { + if (operand + .isIntegerOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { return closedWorld.abstractValueDomain.intType; } - if (operand.isDoubleOrNull(closedWorld.abstractValueDomain)) { + if (operand + .isDoubleOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { return closedWorld.abstractValueDomain.doubleType; } return closedWorld.abstractValueDomain.numType; @@ -283,7 +297,7 @@ class UnaryNegateSpecializer extends InvokeDynamicSpecializer { JCommonElements commonElements, JClosedWorld closedWorld) { HInstruction input = instruction.inputs[1]; - if (input.isNumber(closedWorld.abstractValueDomain)) { + if (input.isNumber(closedWorld.abstractValueDomain).isDefinitelyTrue) { return new HNegate( input, instruction.selector, @@ -307,7 +321,9 @@ class AbsSpecializer extends InvokeDynamicSpecializer { CompilerOptions options, JClosedWorld closedWorld) { HInstruction input = instruction.inputs[1]; - if (input.isNumberOrNull(closedWorld.abstractValueDomain)) { + if (input + .isNumberOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { return closedWorld.abstractValueDomain.excludeNull(input.instructionType); } return super @@ -322,7 +338,7 @@ class AbsSpecializer extends InvokeDynamicSpecializer { JCommonElements commonElements, JClosedWorld closedWorld) { HInstruction input = instruction.inputs[1]; - if (input.isNumber(closedWorld.abstractValueDomain)) { + if (input.isNumber(closedWorld.abstractValueDomain).isDefinitelyTrue) { return new HAbs( input, instruction.selector, @@ -343,13 +359,21 @@ abstract class BinaryArithmeticSpecializer extends InvokeDynamicSpecializer { JClosedWorld closedWorld) { HInstruction left = instruction.inputs[1]; HInstruction right = instruction.inputs[2]; - if (left.isIntegerOrNull(closedWorld.abstractValueDomain) && - right.isIntegerOrNull(closedWorld.abstractValueDomain)) { + if (left + .isIntegerOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue && + right + .isIntegerOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { return closedWorld.abstractValueDomain.intType; } - if (left.isNumberOrNull(closedWorld.abstractValueDomain)) { - if (left.isDoubleOrNull(closedWorld.abstractValueDomain) || - right.isDoubleOrNull(closedWorld.abstractValueDomain)) { + if (left.isNumberOrNull(closedWorld.abstractValueDomain).isDefinitelyTrue) { + if (left + .isDoubleOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue || + right + .isDoubleOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { return closedWorld.abstractValueDomain.doubleType; } return closedWorld.abstractValueDomain.numType; @@ -359,8 +383,12 @@ abstract class BinaryArithmeticSpecializer extends InvokeDynamicSpecializer { } bool isBuiltin(HInvokeDynamic instruction, JClosedWorld closedWorld) { - return instruction.inputs[1].isNumber(closedWorld.abstractValueDomain) && - instruction.inputs[2].isNumber(closedWorld.abstractValueDomain); + return instruction.inputs[1] + .isNumber(closedWorld.abstractValueDomain) + .isDefinitelyTrue && + instruction.inputs[2] + .isNumber(closedWorld.abstractValueDomain) + .isDefinitelyTrue; } HInstruction tryConvertToBuiltin( @@ -386,15 +414,19 @@ abstract class BinaryArithmeticSpecializer extends InvokeDynamicSpecializer { HInstruction instruction, JClosedWorld closedWorld) { HInstruction left = instruction.inputs[1]; HInstruction right = instruction.inputs[2]; - return left.isPositiveIntegerOrNull(closedWorld.abstractValueDomain) && - right.isPositiveIntegerOrNull(closedWorld.abstractValueDomain); + return left + .isPositiveIntegerOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue && + right + .isPositiveIntegerOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue; } bool inputsAreUInt31(HInstruction instruction, JClosedWorld closedWorld) { HInstruction left = instruction.inputs[1]; HInstruction right = instruction.inputs[2]; - return left.isUInt31(closedWorld.abstractValueDomain) && - right.isUInt31(closedWorld.abstractValueDomain); + return left.isUInt31(closedWorld.abstractValueDomain).isDefinitelyTrue && + right.isUInt31(closedWorld.abstractValueDomain).isDefinitelyTrue; } HInstruction newBuiltinVariant( @@ -452,7 +484,7 @@ class DivideSpecializer extends BinaryArithmeticSpecializer { CompilerOptions options, JClosedWorld closedWorld) { HInstruction left = instruction.inputs[1]; - if (left.isNumberOrNull(closedWorld.abstractValueDomain)) { + if (left.isNumberOrNull(closedWorld.abstractValueDomain).isDefinitelyTrue) { return closedWorld.abstractValueDomain.doubleType; } return super @@ -680,11 +712,14 @@ class TruncatingDivideSpecializer extends BinaryArithmeticSpecializer { bool hasUint31Result(HInstruction instruction, JClosedWorld closedWorld) { HInstruction left = instruction.inputs[1]; HInstruction right = instruction.inputs[2]; - if (right.isPositiveInteger(closedWorld.abstractValueDomain)) { - if (left.isUInt31(closedWorld.abstractValueDomain) && isNotZero(right)) { + if (right + .isPositiveInteger(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { + if (left.isUInt31(closedWorld.abstractValueDomain).isDefinitelyTrue && + isNotZero(right)) { return true; } - if (left.isUInt32(closedWorld.abstractValueDomain) && + if (left.isUInt32(closedWorld.abstractValueDomain).isDefinitelyTrue && isTwoOrGreater(right)) { return true; } @@ -701,7 +736,9 @@ class TruncatingDivideSpecializer extends BinaryArithmeticSpecializer { JClosedWorld closedWorld) { HInstruction right = instruction.inputs[2]; if (isBuiltin(instruction, closedWorld)) { - if (right.isPositiveInteger(closedWorld.abstractValueDomain) && + if (right + .isPositiveInteger(closedWorld.abstractValueDomain) + .isDefinitelyTrue && isNotZero(right)) { if (hasUint31Result(instruction, closedWorld)) { return newBuiltinVariant(instruction, results, options, closedWorld); @@ -740,7 +777,9 @@ abstract class BinaryBitOpSpecializer extends BinaryArithmeticSpecializer { // All bitwise operations on primitive types either produce an // integer or throw an error. HInstruction left = instruction.inputs[1]; - if (left.isPrimitiveOrNull(closedWorld.abstractValueDomain)) { + if (left + .isPrimitiveOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { return closedWorld.abstractValueDomain.uint32Type; } return super @@ -772,7 +811,9 @@ abstract class BinaryBitOpSpecializer extends BinaryArithmeticSpecializer { bool isPositive(HInstruction instruction, JClosedWorld closedWorld) { // TODO: We should use the value range analysis. Currently, ranges // are discarded just after the analysis. - return instruction.isPositiveInteger(closedWorld.abstractValueDomain); + return instruction + .isPositiveInteger(closedWorld.abstractValueDomain) + .isDefinitelyTrue; } } @@ -792,7 +833,7 @@ class ShiftLeftSpecializer extends BinaryBitOpSpecializer { JClosedWorld closedWorld) { HInstruction left = instruction.inputs[1]; HInstruction right = instruction.inputs[2]; - if (left.isNumber(closedWorld.abstractValueDomain)) { + if (left.isNumber(closedWorld.abstractValueDomain).isDefinitelyTrue) { if (argumentLessThan32(right)) { return newBuiltinVariant(instruction, results, options, closedWorld); } @@ -830,8 +871,9 @@ class ShiftRightSpecializer extends BinaryBitOpSpecializer { CompilerOptions options, JClosedWorld closedWorld) { HInstruction left = instruction.inputs[1]; - if (left.isUInt32(closedWorld.abstractValueDomain)) + if (left.isUInt32(closedWorld.abstractValueDomain).isDefinitelyTrue) { return left.instructionType; + } return super .computeTypeFromInputTypes(instruction, results, options, closedWorld); } @@ -845,7 +887,7 @@ class ShiftRightSpecializer extends BinaryBitOpSpecializer { JClosedWorld closedWorld) { HInstruction left = instruction.inputs[1]; HInstruction right = instruction.inputs[2]; - if (left.isNumber(closedWorld.abstractValueDomain)) { + if (left.isNumber(closedWorld.abstractValueDomain).isDefinitelyTrue) { if (argumentLessThan32(right) && isPositive(left, closedWorld)) { return newBuiltinVariant(instruction, results, options, closedWorld); } @@ -857,7 +899,7 @@ class ShiftRightSpecializer extends BinaryBitOpSpecializer { instruction.selector = renameToOptimizedSelector( '_shrBothPositive', instruction.selector, commonElements); } else if (isPositive(left, closedWorld) && - right.isNumber(closedWorld.abstractValueDomain)) { + right.isNumber(closedWorld.abstractValueDomain).isDefinitelyTrue) { instruction.selector = renameToOptimizedSelector( '_shrReceiverPositive', instruction.selector, commonElements); } else if (isPositive(right, closedWorld)) { @@ -899,8 +941,8 @@ class BitOrSpecializer extends BinaryBitOpSpecializer { JClosedWorld closedWorld) { HInstruction left = instruction.inputs[1]; HInstruction right = instruction.inputs[2]; - if (left.isUInt31(closedWorld.abstractValueDomain) && - right.isUInt31(closedWorld.abstractValueDomain)) { + if (left.isUInt31(closedWorld.abstractValueDomain).isDefinitelyTrue && + right.isUInt31(closedWorld.abstractValueDomain).isDefinitelyTrue) { return closedWorld.abstractValueDomain.uint31Type; } return super @@ -934,9 +976,11 @@ class BitAndSpecializer extends BinaryBitOpSpecializer { JClosedWorld closedWorld) { HInstruction left = instruction.inputs[1]; HInstruction right = instruction.inputs[2]; - if (left.isPrimitiveOrNull(closedWorld.abstractValueDomain) && - (left.isUInt31(closedWorld.abstractValueDomain) || - right.isUInt31(closedWorld.abstractValueDomain))) { + if (left + .isPrimitiveOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue && + (left.isUInt31(closedWorld.abstractValueDomain).isDefinitelyTrue || + right.isUInt31(closedWorld.abstractValueDomain).isDefinitelyTrue)) { return closedWorld.abstractValueDomain.uint31Type; } return super @@ -970,8 +1014,8 @@ class BitXorSpecializer extends BinaryBitOpSpecializer { JClosedWorld closedWorld) { HInstruction left = instruction.inputs[1]; HInstruction right = instruction.inputs[2]; - if (left.isUInt31(closedWorld.abstractValueDomain) && - right.isUInt31(closedWorld.abstractValueDomain)) { + if (left.isUInt31(closedWorld.abstractValueDomain).isDefinitelyTrue && + right.isUInt31(closedWorld.abstractValueDomain).isDefinitelyTrue) { return closedWorld.abstractValueDomain.uint31Type; } return super @@ -1000,7 +1044,8 @@ abstract class RelationalSpecializer extends InvokeDynamicSpecializer { CompilerOptions options, JClosedWorld closedWorld) { if (instruction.inputs[1] - .isPrimitiveOrNull(closedWorld.abstractValueDomain)) { + .isPrimitiveOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { return closedWorld.abstractValueDomain.boolType; } return super @@ -1016,8 +1061,8 @@ abstract class RelationalSpecializer extends InvokeDynamicSpecializer { JClosedWorld closedWorld) { HInstruction left = instruction.inputs[1]; HInstruction right = instruction.inputs[2]; - if (left.isNumber(closedWorld.abstractValueDomain) && - right.isNumber(closedWorld.abstractValueDomain)) { + if (left.isNumber(closedWorld.abstractValueDomain).isDefinitelyTrue && + right.isNumber(closedWorld.abstractValueDomain).isDefinitelyTrue) { return newBuiltinVariant(instruction, closedWorld); } return null; @@ -1041,7 +1086,9 @@ class EqualsSpecializer extends RelationalSpecializer { HInstruction right = instruction.inputs[2]; AbstractValue instructionType = left.instructionType; if (right.isConstantNull() || - left.isPrimitiveOrNull(closedWorld.abstractValueDomain)) { + left + .isPrimitiveOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { return newBuiltinVariant(instruction, closedWorld); } if (closedWorld.includesClosureCall( @@ -1144,13 +1191,16 @@ class CodeUnitAtSpecializer extends InvokeDynamicSpecializer { // TODO(sra): Implement a builtin HCodeUnitAt instruction and the same index // bounds checking optimizations as for HIndex. HInstruction receiver = instruction.getDartReceiver(closedWorld); - if (receiver.isStringOrNull(closedWorld.abstractValueDomain)) { + if (receiver + .isStringOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { // Even if there is no builtin equivalent instruction, we know // String.codeUnitAt does not have any side effect (other than throwing), // and that it can be GVN'ed. clearAllSideEffects(instruction); if (instruction.inputs.last - .isPositiveInteger(closedWorld.abstractValueDomain)) { + .isPositiveInteger(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { instruction.selector = renameToOptimizedSelector( '_codeUnitAt', instruction.selector, commonElements); } @@ -1172,15 +1222,27 @@ class CompareToSpecializer extends InvokeDynamicSpecializer { HInstruction receiver = instruction.getDartReceiver(closedWorld); // `compareTo` has no side-effect (other than throwing) and can be GVN'ed // for some known types. - if (receiver.isStringOrNull(closedWorld.abstractValueDomain) || - receiver.isNumberOrNull(closedWorld.abstractValueDomain)) { + if (receiver + .isStringOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue || + receiver + .isNumberOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { // Replace `a.compareTo(a)` with `0`, but only if receiver and argument // are such that no exceptions can be thrown. HInstruction argument = instruction.inputs.last; - if ((receiver.isNumber(closedWorld.abstractValueDomain) && - argument.isNumber(closedWorld.abstractValueDomain)) || - (receiver.isString(closedWorld.abstractValueDomain) && - argument.isString(closedWorld.abstractValueDomain))) { + if ((receiver + .isNumber(closedWorld.abstractValueDomain) + .isDefinitelyTrue && + argument + .isNumber(closedWorld.abstractValueDomain) + .isDefinitelyTrue) || + (receiver + .isString(closedWorld.abstractValueDomain) + .isDefinitelyTrue && + argument + .isString(closedWorld.abstractValueDomain) + .isDefinitelyTrue)) { if (identical(receiver.nonCheck(), argument.nonCheck())) { return graph.addConstantInt(0, closedWorld); } @@ -1202,7 +1264,9 @@ class IdempotentStringOperationSpecializer extends InvokeDynamicSpecializer { JCommonElements commonElements, JClosedWorld closedWorld) { HInstruction receiver = instruction.getDartReceiver(closedWorld); - if (receiver.isStringOrNull(closedWorld.abstractValueDomain)) { + if (receiver + .isStringOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { // String.xxx does not have any side effect (other than throwing), and it // can be GVN'ed. clearAllSideEffects(instruction); @@ -1231,8 +1295,12 @@ class PatternMatchSpecializer extends InvokeDynamicSpecializer { JClosedWorld closedWorld) { HInstruction receiver = instruction.getDartReceiver(closedWorld); HInstruction pattern = instruction.inputs[2]; - if (receiver.isStringOrNull(closedWorld.abstractValueDomain) && - pattern.isStringOrNull(closedWorld.abstractValueDomain)) { + if (receiver + .isStringOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue && + pattern + .isStringOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { // String.contains(String s) does not have any side effect (other than // throwing), and it can be GVN'ed. clearAllSideEffects(instruction); @@ -1256,7 +1324,9 @@ class RoundSpecializer extends InvokeDynamicSpecializer { JCommonElements commonElements, JClosedWorld closedWorld) { HInstruction receiver = instruction.getDartReceiver(closedWorld); - if (receiver.isNumberOrNull(closedWorld.abstractValueDomain)) { + if (receiver + .isNumberOrNull(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { // Even if there is no builtin equivalent instruction, we know the // instruction does not have any side effect, and that it can be GVN'ed. clearAllSideEffects(instruction); diff --git a/pkg/compiler/lib/src/ssa/kernel_string_builder.dart b/pkg/compiler/lib/src/ssa/kernel_string_builder.dart index 3b5245197d471..e30817c68bedb 100644 --- a/pkg/compiler/lib/src/ssa/kernel_string_builder.dart +++ b/pkg/compiler/lib/src/ssa/kernel_string_builder.dart @@ -33,7 +33,7 @@ class KernelStringBuilder extends ir.Visitor { // conversions. // 2. The value can be primitive, because the library stringifier has // fast-path code for most primitives. - if (expression.canBePrimitive(builder.abstractValueDomain)) { + if (expression.isPrimitive(builder.abstractValueDomain).isPotentiallyTrue) { append(stringify(expression)); return; } diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart index aa8a1932e99e2..87a32a702ae40 100644 --- a/pkg/compiler/lib/src/ssa/nodes.dart +++ b/pkg/compiler/lib/src/ssa/nodes.dart @@ -1021,87 +1021,88 @@ abstract class HInstruction implements Spannable { bool isValue(AbstractValueDomain domain) => domain.isPrimitiveValue(instructionType); - bool canBeNull(AbstractValueDomain domain) => - domain.canBeNull(instructionType); + AbstractBool isNull(AbstractValueDomain domain) => + domain.isNull(instructionType); - bool isNull(AbstractValueDomain domain) => domain.isNull(instructionType); - - bool isConflicting(AbstractValueDomain domain) => + AbstractBool isConflicting(AbstractValueDomain domain) => domain.isEmpty(instructionType); - bool canBePrimitive(AbstractValueDomain domain) => - domain.canBePrimitive(instructionType); + AbstractBool isPrimitive(AbstractValueDomain domain) => + domain.isPrimitive(instructionType); - bool canBePrimitiveNumber(AbstractValueDomain domain) => - domain.canBePrimitiveNumber(instructionType); + AbstractBool isPrimitiveNumber(AbstractValueDomain domain) => + domain.isPrimitiveNumber(instructionType); - bool canBePrimitiveBoolean(AbstractValueDomain domain) => - domain.canBePrimitiveBoolean(instructionType); + AbstractBool isPrimitiveBoolean(AbstractValueDomain domain) => + domain.isPrimitiveBoolean(instructionType); - bool canBePrimitiveArray(AbstractValueDomain domain) => - domain.canBePrimitiveArray(instructionType); + AbstractBool isPrimitiveArray(AbstractValueDomain domain) => + domain.isPrimitiveArray(instructionType); - bool isIndexablePrimitive(AbstractValueDomain domain) => + AbstractBool isIndexablePrimitive(AbstractValueDomain domain) => domain.isIndexablePrimitive(instructionType); - bool isFixedArray(AbstractValueDomain domain) => + AbstractBool isFixedArray(AbstractValueDomain domain) => domain.isFixedArray(instructionType); - bool isExtendableArray(AbstractValueDomain domain) => + AbstractBool isExtendableArray(AbstractValueDomain domain) => domain.isExtendableArray(instructionType); - bool isMutableArray(AbstractValueDomain domain) => + AbstractBool isMutableArray(AbstractValueDomain domain) => domain.isMutableArray(instructionType); - bool isMutableIndexable(AbstractValueDomain domain) => + AbstractBool isMutableIndexable(AbstractValueDomain domain) => domain.isMutableIndexable(instructionType); - bool isArray(AbstractValueDomain domain) => domain.isArray(instructionType); + AbstractBool isArray(AbstractValueDomain domain) => + domain.isArray(instructionType); - bool canBePrimitiveString(AbstractValueDomain domain) => - domain.canBePrimitiveString(instructionType); + AbstractBool isPrimitiveString(AbstractValueDomain domain) => + domain.isPrimitiveString(instructionType); - bool isInteger(AbstractValueDomain domain) => + AbstractBool isInteger(AbstractValueDomain domain) => domain.isInteger(instructionType); - bool isUInt32(AbstractValueDomain domain) => domain.isUInt32(instructionType); + AbstractBool isUInt32(AbstractValueDomain domain) => + domain.isUInt32(instructionType); - bool isUInt31(AbstractValueDomain domain) => domain.isUInt31(instructionType); + AbstractBool isUInt31(AbstractValueDomain domain) => + domain.isUInt31(instructionType); - bool isPositiveInteger(AbstractValueDomain domain) => + AbstractBool isPositiveInteger(AbstractValueDomain domain) => domain.isPositiveInteger(instructionType); - bool isPositiveIntegerOrNull(AbstractValueDomain domain) => + AbstractBool isPositiveIntegerOrNull(AbstractValueDomain domain) => domain.isPositiveIntegerOrNull(instructionType); - bool isIntegerOrNull(AbstractValueDomain domain) => + AbstractBool isIntegerOrNull(AbstractValueDomain domain) => domain.isIntegerOrNull(instructionType); - bool isNumber(AbstractValueDomain domain) => domain.isNumber(instructionType); + AbstractBool isNumber(AbstractValueDomain domain) => + domain.isNumber(instructionType); - bool isNumberOrNull(AbstractValueDomain domain) => + AbstractBool isNumberOrNull(AbstractValueDomain domain) => domain.isNumberOrNull(instructionType); - bool isDouble(AbstractValueDomain domain) => domain.isDouble(instructionType); + AbstractBool isDouble(AbstractValueDomain domain) => + domain.isDouble(instructionType); - bool isDoubleOrNull(AbstractValueDomain domain) => + AbstractBool isDoubleOrNull(AbstractValueDomain domain) => domain.isDoubleOrNull(instructionType); - bool isBoolean(AbstractValueDomain domain) => + AbstractBool isBoolean(AbstractValueDomain domain) => domain.isBoolean(instructionType); - bool isBooleanOrNull(AbstractValueDomain domain) => + AbstractBool isBooleanOrNull(AbstractValueDomain domain) => domain.isBooleanOrNull(instructionType); - bool isString(AbstractValueDomain domain) => domain.isString(instructionType); + AbstractBool isString(AbstractValueDomain domain) => + domain.isString(instructionType); - bool isStringOrNull(AbstractValueDomain domain) => + AbstractBool isStringOrNull(AbstractValueDomain domain) => domain.isStringOrNull(instructionType); - bool isPrimitive(AbstractValueDomain domain) => - domain.isPrimitive(instructionType); - - bool isPrimitiveOrNull(AbstractValueDomain domain) => + AbstractBool isPrimitiveOrNull(AbstractValueDomain domain) => domain.isPrimitiveOrNull(instructionType); /** @@ -1328,7 +1329,7 @@ class DominatedUses { /// dominated block. (There can be many such edges on a single phi at the exit /// of a loop with many break statements). If [excludePhiOutEdges] is `true` /// then these edge uses are not included. - static of(HInstruction source, HInstruction dominator, + static DominatedUses of(HInstruction source, HInstruction dominator, {bool excludeDominator: false, bool excludePhiOutEdges: false}) { return new DominatedUses._(source) .._compute(source, dominator, excludeDominator, excludePhiOutEdges); @@ -1727,8 +1728,9 @@ class HInvokeDynamicGetter extends HInvokeDynamicField { List get typeArguments => const []; // There might be an interceptor input, so `inputs.last` is the dart receiver. - bool canThrow(AbstractValueDomain domain) => - isTearOff ? inputs.last.canBeNull(domain) : super.canThrow(domain); + bool canThrow(AbstractValueDomain domain) => isTearOff + ? inputs.last.isNull(domain).isPotentiallyTrue + : super.canThrow(domain); String toString() => 'invoke dynamic getter: selector=$selector, mask=$mask'; } @@ -1900,7 +1902,8 @@ class HFieldGet extends HFieldAccess { return false; } - bool canThrow(AbstractValueDomain domain) => receiver.canBeNull(domain); + bool canThrow(AbstractValueDomain domain) => + receiver.isNull(domain).isPotentiallyTrue; HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver; bool onlyThrowsNSM() => true; @@ -1923,7 +1926,8 @@ class HFieldSet extends HFieldAccess { sideEffects.setChangesInstanceProperty(); } - bool canThrow(AbstractValueDomain domain) => receiver.canBeNull(domain); + bool canThrow(AbstractValueDomain domain) => + receiver.isNull(domain).isPotentiallyTrue; HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver; bool onlyThrowsNSM() => true; @@ -1951,7 +1955,8 @@ class HGetLength extends HInstruction { HInstruction get receiver => inputs.single; - bool canThrow(AbstractValueDomain domain) => receiver.canBeNull(domain); + bool canThrow(AbstractValueDomain domain) => + receiver.isNull(domain).isPotentiallyTrue; HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver; bool onlyThrowsNSM() => true; @@ -2004,7 +2009,8 @@ class HReadModifyWrite extends HLateInstruction { bool get isPostOp => opKind == POST_OP; bool get isAssignOp => opKind == ASSIGN_OP; - bool canThrow(AbstractValueDomain domain) => receiver.canBeNull(domain); + bool canThrow(AbstractValueDomain domain) => + receiver.isNull(domain).isPotentiallyTrue; HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver; bool onlyThrowsNSM() => true; @@ -2107,7 +2113,7 @@ class HForeignCode extends HForeign { bool isJsStatement() => isStatement; bool canThrow(AbstractValueDomain domain) { if (inputs.length > 0) { - return inputs.first.canBeNull(domain) + return inputs.first.isNull(domain).isPotentiallyTrue ? throwBehavior.canThrow : throwBehavior.onNonNull.canThrow; } @@ -2119,7 +2125,7 @@ class HForeignCode extends HForeign { bool isAllocation(AbstractValueDomain domain) => nativeBehavior != null && nativeBehavior.isAllocation && - !canBeNull(domain); + isNull(domain).isDefinitelyFalse; int typeCode() => HInstruction.FOREIGN_CODE_TYPECODE; bool typeEquals(other) => other is HForeignCode; @@ -2834,7 +2840,7 @@ class HOneShotInterceptor extends HInvokeDynamic { this.interceptedClasses) : super(selector, mask, null, inputs, true, type) { assert(inputs[0] is HConstant); - assert(inputs[0].isNull(domain)); + assert(inputs[0].isNull(domain).isDefinitelyTrue); assert(selector.callStructure.typeArgumentCount == typeArguments.length); } bool isCallOnInterceptor(JClosedWorld closedWorld) => true; @@ -2919,7 +2925,8 @@ class HIndex extends HInstruction { HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver; bool onlyThrowsNSM() => true; - bool canThrow(AbstractValueDomain domain) => receiver.canBeNull(domain); + bool canThrow(AbstractValueDomain domain) => + receiver.isNull(domain).isPotentiallyTrue; int typeCode() => HInstruction.INDEX_TYPECODE; bool typeEquals(HInstruction other) => other is HIndex; @@ -2952,7 +2959,8 @@ class HIndexAssign extends HInstruction { HInstruction getDartReceiver(JClosedWorld closedWorld) => receiver; bool onlyThrowsNSM() => true; - bool canThrow(AbstractValueDomain domain) => receiver.canBeNull(domain); + bool canThrow(AbstractValueDomain domain) => + receiver.isNull(domain).isPotentiallyTrue; } class HIs extends HInstruction { @@ -3204,26 +3212,34 @@ class HTypeConversion extends HCheck { } if (type.isFutureOr) { // `null` always passes type conversion. - if (checkedInput.isNull(abstractValueDomain)) return true; + if (checkedInput.isNull(abstractValueDomain).isDefinitelyTrue) { + return true; + } // TODO(johnniwinther): Optimize FutureOr type conversions. return false; } if (!type.treatAsRaw) { // `null` always passes type conversion. - if (checkedInput.isNull(abstractValueDomain)) return true; + if (checkedInput.isNull(abstractValueDomain).isDefinitelyTrue) { + return true; + } return false; } if (type.isFunctionType) { // `null` always passes type conversion. - if (checkedInput.isNull(abstractValueDomain)) return true; + if (checkedInput.isNull(abstractValueDomain).isDefinitelyTrue) { + 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; + if (abstractValueDomain.containsAll(checkedType).isPotentiallyTrue) { + return false; + } AbstractValue inputType = checkedInput.instructionType; - return abstractValueDomain.isIn(inputType, checkedType); + return abstractValueDomain.isIn(inputType, checkedType).isDefinitelyTrue; } String toString() => 'HTypeConversion(type=$typeExpression, kind=$kind, ' @@ -3270,9 +3286,11 @@ class HTypeKnown extends HCheck { bool isRedundant(JClosedWorld closedWorld) { AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain; - if (abstractValueDomain.containsAll(knownType)) return false; + if (abstractValueDomain.containsAll(knownType).isPotentiallyTrue) { + return false; + } AbstractValue inputType = checkedInput.instructionType; - return abstractValueDomain.isIn(inputType, knownType); + return abstractValueDomain.isIn(inputType, knownType).isDefinitelyTrue; } } diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart index d67f5814b476d..822a58310c564 100644 --- a/pkg/compiler/lib/src/ssa/optimize.dart +++ b/pkg/compiler/lib/src/ssa/optimize.dart @@ -155,16 +155,17 @@ class SsaOptimizerTask extends CompilerTask { /// cannot change. The current implementation is conservative for the purpose /// of identifying gvn-able lengths and mis-identifies some unions of fixed /// length indexables (see TODO) as not fixed length. -bool isFixedLength(mask, JClosedWorld closedWorld) { - if (mask.isContainer && mask.length != null) { +bool isFixedLength(AbstractValue mask, JClosedWorld closedWorld) { + AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain; + if (abstractValueDomain.isContainer(mask) && + abstractValueDomain.getContainerLength(mask) != null) { // A container on which we have inferred the length. return true; } // TODO(sra): Recognize any combination of fixed length indexables. - if (mask.containsOnly(closedWorld.commonElements.jsFixedArrayClass) || - mask.containsOnly(closedWorld.commonElements.jsUnmodifiableArrayClass) || - mask.containsOnlyString(closedWorld) || - closedWorld.abstractValueDomain.isTypedArray(mask)) { + if (abstractValueDomain.isFixedArray(mask).isDefinitelyTrue || + abstractValueDomain.isStringOrNull(mask).isDefinitelyTrue || + abstractValueDomain.isTypedArray(mask).isDefinitelyTrue) { return true; } return false; @@ -220,8 +221,12 @@ class SsaInstructionSimplifier extends HBaseVisitor // might be that an operation thought to return double, can be // simplified to an int. For example: // `2.5 * 10`. - if (!(replacement.isNumberOrNull(_abstractValueDomain) && - instruction.isNumberOrNull(_abstractValueDomain))) { + if (!(replacement + .isNumberOrNull(_abstractValueDomain) + .isDefinitelyTrue && + instruction + .isNumberOrNull(_abstractValueDomain) + .isDefinitelyTrue)) { // If we can replace [instruction] with [replacement], then // [replacement]'s type can be narrowed. AbstractValue newType = _abstractValueDomain.intersection( @@ -278,9 +283,9 @@ class SsaInstructionSimplifier extends HBaseVisitor if (test is! HIdentity) return; HInstruction tested; - if (test.inputs[0].isNull(_abstractValueDomain)) { + if (test.inputs[0].isNull(_abstractValueDomain).isDefinitelyTrue) { tested = test.inputs[1]; - } else if (test.inputs[1].isNull(_abstractValueDomain)) { + } else if (test.inputs[1].isNull(_abstractValueDomain).isDefinitelyTrue) { tested = test.inputs[0]; } else { return; @@ -298,7 +303,9 @@ class SsaInstructionSimplifier extends HBaseVisitor // This ofen comes from the dart code `x ?? false`. if (_sameOrRefinementOf(tested, whenNotNullValue) && _isBoolConstant(whenNullValue, false) && - whenNotNullValue.isBooleanOrNull(_abstractValueDomain) && + whenNotNullValue + .isBooleanOrNull(_abstractValueDomain) + .isDefinitelyTrue && _mostlyEmpty(whenNullBlock) && _mostlyEmpty(whenNotNullBlock)) { HInstruction trueConstant = _graph.addConstantBool(true, _closedWorld); @@ -318,7 +325,9 @@ class SsaInstructionSimplifier extends HBaseVisitor // This ofen comes from the dart code `x ?? true`. if (_sameOrRefinementOf(tested, whenNotNullValue) && _isBoolConstant(whenNullValue, true) && - whenNotNullValue.isBooleanOrNull(_abstractValueDomain) && + whenNotNullValue + .isBooleanOrNull(_abstractValueDomain) + .isDefinitelyTrue && _mostlyEmpty(whenNullBlock) && _mostlyEmpty(whenNotNullBlock)) { HInstruction falseConstant = _graph.addConstantBool(false, _closedWorld); @@ -372,7 +381,7 @@ class SsaInstructionSimplifier extends HBaseVisitor ConstantValue getConstantFromType(HInstruction node) { if (node.isValue(_abstractValueDomain) && - !node.canBeNull(_abstractValueDomain)) { + node.isNull(_abstractValueDomain).isDefinitelyFalse) { ConstantValue value = _abstractValueDomain.getPrimitiveValue(node.instructionType); if (value.isBool) { @@ -423,17 +432,23 @@ class SsaInstructionSimplifier extends HBaseVisitor List inputs = node.inputs; assert(inputs.length == 1); HInstruction input = inputs[0]; - if (input.isBoolean(_abstractValueDomain)) return input; + if (input.isBoolean(_abstractValueDomain).isDefinitelyTrue) { + return input; + } // If the code is unreachable, remove the HBoolify. This can happen when // there is a throw expression in a short-circuit conditional. Removing the // unreachable HBoolify makes it easier to reconstruct the short-circuit // operation. - if (_abstractValueDomain.isEmpty(input.instructionType)) return input; + if (_abstractValueDomain.isEmpty(input.instructionType).isDefinitelyTrue) { + return input; + } // All values that cannot be 'true' are boolified to false. AbstractValue mask = input.instructionType; - if (!_abstractValueDomain.containsType(mask, commonElements.jsBoolClass)) { + if (_abstractValueDomain + .containsType(mask, commonElements.jsBoolClass) + .isPotentiallyFalse) { return _graph.addConstantBool(false, _closedWorld); } return node; @@ -470,7 +485,9 @@ class SsaInstructionSimplifier extends HBaseVisitor HInstruction tryOptimizeLengthInterceptedGetter(HInvokeDynamic node) { HInstruction actualReceiver = node.inputs[1]; - if (actualReceiver.isIndexablePrimitive(_abstractValueDomain)) { + if (actualReceiver + .isIndexablePrimitive(_abstractValueDomain) + .isDefinitelyTrue) { if (actualReceiver.isConstantString()) { HConstant constantInput = actualReceiver; StringConstantValue constant = constantInput.constant; @@ -485,11 +502,13 @@ class SsaInstructionSimplifier extends HBaseVisitor AbstractValue actualType = node.instructionType; AbstractValue resultType = _abstractValueDomain.positiveIntType; // If we already have computed a more specific type, keep that type. - if (_abstractValueDomain.isInstanceOfOrNull( - actualType, commonElements.jsUInt31Class)) { + if (_abstractValueDomain + .isInstanceOfOrNull(actualType, commonElements.jsUInt31Class) + .isDefinitelyTrue) { resultType = _abstractValueDomain.uint31Type; - } else if (_abstractValueDomain.isInstanceOfOrNull( - actualType, commonElements.jsUInt32Class)) { + } else if (_abstractValueDomain + .isInstanceOfOrNull(actualType, commonElements.jsUInt32Class) + .isDefinitelyTrue) { resultType = _abstractValueDomain.uint32Type; } HGetLength result = @@ -530,12 +549,14 @@ class SsaInstructionSimplifier extends HBaseVisitor bool applies(MemberEntity element) { return selector.applies(element) && (mask == null || - _abstractValueDomain.canHit(mask, element, selector)); + _abstractValueDomain + .isTargetingMember(mask, element, selector) + .isPotentiallyTrue); } if (selector.isCall || selector.isOperator) { FunctionEntity target; - if (input.isExtendableArray(_abstractValueDomain)) { + if (input.isExtendableArray(_abstractValueDomain).isDefinitelyTrue) { if (applies(commonElements.jsArrayRemoveLast)) { target = commonElements.jsArrayRemoveLast; } else if (applies(commonElements.jsArrayAdd)) { @@ -546,7 +567,7 @@ class SsaInstructionSimplifier extends HBaseVisitor target = commonElements.jsArrayAdd; } } - } else if (input.isStringOrNull(_abstractValueDomain)) { + } else if (input.isStringOrNull(_abstractValueDomain).isDefinitelyTrue) { if (commonElements.appliesToJsStringSplit( selector, mask, _abstractValueDomain)) { return handleStringSplit(node); @@ -555,12 +576,12 @@ class SsaInstructionSimplifier extends HBaseVisitor // make sure the receiver and the argument are not null. // TODO(sra): Do this via [node.specializer]. HInstruction argument = node.inputs[2]; - if (argument.isString(_abstractValueDomain) && - !input.canBeNull(_abstractValueDomain)) { + if (argument.isString(_abstractValueDomain).isDefinitelyTrue && + input.isNull(_abstractValueDomain).isDefinitelyFalse) { return new HStringConcat(input, argument, node.instructionType); } } else if (applies(commonElements.jsStringToString) && - !input.canBeNull(_abstractValueDomain)) { + input.isNull(_abstractValueDomain).isDefinitelyFalse) { return input; } } @@ -596,7 +617,9 @@ class SsaInstructionSimplifier extends HBaseVisitor HInstruction handleStringSplit(HInvokeDynamic node) { HInstruction argument = node.inputs[2]; - if (!argument.isString(_abstractValueDomain)) return node; + if (!argument.isString(_abstractValueDomain).isDefinitelyTrue) { + return node; + } // Replace `s.split$1(pattern)` with // @@ -787,7 +810,9 @@ class SsaInstructionSimplifier extends HBaseVisitor HInstruction visitBoundsCheck(HBoundsCheck node) { HInstruction index = node.index; - if (index.isInteger(_abstractValueDomain)) return node; + if (index.isInteger(_abstractValueDomain).isDefinitelyTrue) { + return node; + } if (index.isConstant()) { HConstant constantInstruction = index; assert(!constantInstruction.constant.isInt); @@ -815,8 +840,8 @@ class SsaInstructionSimplifier extends HBaseVisitor HInstruction right = node.right; // We can only perform this rewriting on Integer, as it is not // valid for -0.0. - if (left.isInteger(_abstractValueDomain) && - right.isInteger(_abstractValueDomain)) { + if (left.isInteger(_abstractValueDomain).isDefinitelyTrue && + right.isInteger(_abstractValueDomain).isDefinitelyTrue) { if (left is HConstant && left.constant.isZero) return right; if (right is HConstant && right.constant.isZero) return left; } @@ -826,8 +851,8 @@ class SsaInstructionSimplifier extends HBaseVisitor HInstruction visitMultiply(HMultiply node) { HInstruction left = node.left; HInstruction right = node.right; - if (left.isNumber(_abstractValueDomain) && - right.isNumber(_abstractValueDomain)) { + if (left.isNumber(_abstractValueDomain).isDefinitelyTrue && + right.isNumber(_abstractValueDomain).isDefinitelyTrue) { if (left is HConstant && left.constant.isOne) return right; if (right is HConstant && right.constant.isOne) return left; } @@ -873,15 +898,17 @@ class SsaInstructionSimplifier extends HBaseVisitor // Intersection of int and double return conflicting, so // we don't optimize on numbers to preserve the runtime semantics. - if (!(left.isNumberOrNull(_abstractValueDomain) && - right.isNumberOrNull(_abstractValueDomain))) { - if (_abstractValueDomain.areDisjoint(leftType, rightType)) { + if (!(left.isNumberOrNull(_abstractValueDomain).isDefinitelyTrue && + right.isNumberOrNull(_abstractValueDomain).isDefinitelyTrue)) { + if (_abstractValueDomain + .areDisjoint(leftType, rightType) + .isDefinitelyTrue) { return makeFalse(); } } - if (left.isNull(_abstractValueDomain) && - right.isNull(_abstractValueDomain)) { + if (left.isNull(_abstractValueDomain).isDefinitelyTrue && + right.isNull(_abstractValueDomain).isDefinitelyTrue) { return makeTrue(); } @@ -893,11 +920,13 @@ class SsaInstructionSimplifier extends HBaseVisitor } } - if (left.isConstantBoolean() && right.isBoolean(_abstractValueDomain)) { + if (left.isConstantBoolean() && + right.isBoolean(_abstractValueDomain).isDefinitelyTrue) { return compareConstant(left, right); } - if (right.isConstantBoolean() && left.isBoolean(_abstractValueDomain)) { + if (right.isConstantBoolean() && + left.isBoolean(_abstractValueDomain).isDefinitelyTrue) { return compareConstant(right, left); } @@ -906,8 +935,12 @@ class SsaInstructionSimplifier extends HBaseVisitor // dart2js runtime has not always been consistent with the Dart // specification (section 16.0.1), which makes distinctions on NaNs and // -0.0 that are hard to implement efficiently. - if (left.isIntegerOrNull(_abstractValueDomain)) return makeTrue(); - if (!left.canBePrimitiveNumber(_abstractValueDomain)) return makeTrue(); + if (left.isIntegerOrNull(_abstractValueDomain).isDefinitelyTrue) { + return makeTrue(); + } + if (left.isPrimitiveNumber(_abstractValueDomain).isDefinitelyFalse) { + return makeTrue(); + } } return null; @@ -939,7 +972,7 @@ class SsaInstructionSimplifier extends HBaseVisitor // This is mostly harmless (if a little confusing) but does cause a lot of // `x = false;` copies to be inserted when a loop body has many continue // statements or ends with a switch. - var uses = + DominatedUses uses = DominatedUses.of(condition, block.first, excludePhiOutEdges: true); if (uses.isEmpty) return; uses.replaceWith(_graph.addConstantBool(value, _closedWorld)); @@ -989,7 +1022,7 @@ class SsaInstructionSimplifier extends HBaseVisitor InterfaceType interfaceType = type; ClassEntity element = interfaceType.element; HInstruction expression = node.expression; - if (expression.isInteger(_abstractValueDomain)) { + if (expression.isInteger(_abstractValueDomain).isDefinitelyTrue) { if (element == commonElements.intClass || element == commonElements.numClass || commonElements.isNumberOrStringSupertype(element)) { @@ -1001,7 +1034,7 @@ class SsaInstructionSimplifier extends HBaseVisitor } else { return _graph.addConstantBool(false, _closedWorld); } - } else if (expression.isDouble(_abstractValueDomain)) { + } else if (expression.isDouble(_abstractValueDomain).isDefinitelyTrue) { if (element == commonElements.doubleClass || element == commonElements.numClass || commonElements.isNumberOrStringSupertype(element)) { @@ -1014,14 +1047,16 @@ class SsaInstructionSimplifier extends HBaseVisitor } else { return _graph.addConstantBool(false, _closedWorld); } - } else if (expression.isNumber(_abstractValueDomain)) { + } else if (expression.isNumber(_abstractValueDomain).isDefinitelyTrue) { if (element == commonElements.numClass) { return _graph.addConstantBool(true, _closedWorld); } else { // We cannot just return false, because the expression may be of // type int or double. } - } else if (expression.canBePrimitiveNumber(_abstractValueDomain) && + } else if (expression + .isPrimitiveNumber(_abstractValueDomain) + .isPotentiallyTrue && element == commonElements.intClass) { // We let the JS semantics decide for that check. return node; @@ -1033,9 +1068,9 @@ class SsaInstructionSimplifier extends HBaseVisitor AbstractValue expressionMask = expression.instructionType; AbstractBool isInstanceOf = _abstractValueDomain.isInstanceOf(expressionMask, element); - if (isInstanceOf == AbstractBool.True) { + if (isInstanceOf.isDefinitelyTrue) { return _graph.addConstantBool(true, _closedWorld); - } else if (isInstanceOf == AbstractBool.False) { + } else if (isInstanceOf.isDefinitelyFalse) { return _graph.addConstantBool(false, _closedWorld); } } @@ -1095,23 +1130,30 @@ class SsaInstructionSimplifier extends HBaseVisitor } HInstruction visitGetLength(HGetLength node) { - dynamic receiver = node.receiver; + HInstruction receiver = node.receiver; if (_graph.allocatedFixedLists.contains(receiver)) { // TODO(ngeoffray): checking if the second input is an integer // should not be necessary but it currently makes it easier for // other optimizations to reason about a fixed length constructor // that we know takes an int. - if (receiver.inputs[0].isInteger(_abstractValueDomain)) { + if (receiver.inputs[0].isInteger(_abstractValueDomain).isDefinitelyTrue) { return receiver.inputs[0]; } - } else if (receiver.isConstantList() || receiver.isConstantString()) { - return _graph.addConstantInt(receiver.constant.length, _closedWorld); + } else if (receiver.isConstantList()) { + HConstant constantReceiver = receiver; + ListConstantValue constant = constantReceiver.constant; + return _graph.addConstantInt(constant.length, _closedWorld); + } else if (receiver.isConstantString()) { + HConstant constantReceiver = receiver; + StringConstantValue constant = constantReceiver.constant; + return _graph.addConstantInt(constant.length, _closedWorld); } else { - dynamic type = receiver.instructionType; - if (type.isContainer && type.length != null) { - HInstruction constant = - _graph.addConstantInt(type.length, _closedWorld); - if (type.isNullable) { + AbstractValue type = receiver.instructionType; + if (_abstractValueDomain.isContainer(type) && + _abstractValueDomain.getContainerLength(type) != null) { + HInstruction constant = _graph.addConstantInt( + _abstractValueDomain.getContainerLength(type), _closedWorld); + if (_abstractValueDomain.isNull(type).isPotentiallyTrue) { // If the container can be null, we update all uses of the length // access to use the constant instead, but keep the length access in // the graph, to ensure we still have a null check. @@ -1273,17 +1315,23 @@ class SsaInstructionSimplifier extends HBaseVisitor } else if (commonElements.isCheckInt(element)) { if (node.inputs.length == 1) { HInstruction argument = node.inputs[0]; - if (argument.isInteger(_abstractValueDomain)) return argument; + if (argument.isInteger(_abstractValueDomain).isDefinitelyTrue) { + return argument; + } } } else if (commonElements.isCheckNum(element)) { if (node.inputs.length == 1) { HInstruction argument = node.inputs[0]; - if (argument.isNumber(_abstractValueDomain)) return argument; + if (argument.isNumber(_abstractValueDomain).isDefinitelyTrue) { + return argument; + } } } else if (commonElements.isCheckString(element)) { if (node.inputs.length == 1) { HInstruction argument = node.inputs[0]; - if (argument.isString(_abstractValueDomain)) return argument; + if (argument.isString(_abstractValueDomain).isDefinitelyTrue) { + return argument; + } } } return node; @@ -1293,7 +1341,9 @@ class SsaInstructionSimplifier extends HBaseVisitor // If type information is not needed, use the raw Array. HInstruction source = node.inputs[0]; if (source.usedBy.length != 1) return node; - if (!source.isArray(_abstractValueDomain)) return node; + if (source.isArray(_abstractValueDomain).isPotentiallyFalse) { + return node; + } for (HInstruction user in node.usedBy) { if (user is HGetLength) continue; if (user is HIndex) continue; @@ -1371,7 +1421,9 @@ class SsaInstructionSimplifier extends HBaseVisitor HInstruction visitStringify(HStringify node) { HInstruction input = node.inputs[0]; - if (input.isString(_abstractValueDomain)) return input; + if (input.isString(_abstractValueDomain).isDefinitelyTrue) { + return input; + } HInstruction asString(String string) => _graph.addConstant(constantSystem.createString(string), _closedWorld); @@ -1407,18 +1459,26 @@ class SsaInstructionSimplifier extends HBaseVisitor // it directly. Keep the stringifier for primitives (since they have fast // path code in the stringifier) and for classes requiring interceptors // (since SsaInstructionSimplifier runs after SsaSimplifyInterceptors). - if (input.canBePrimitive(_abstractValueDomain)) return null; - if (input.canBeNull(_abstractValueDomain)) return null; + if (input.isPrimitive(_abstractValueDomain).isPotentiallyTrue) { + return null; + } + if (input.isNull(_abstractValueDomain).isPotentiallyTrue) { + return null; + } Selector selector = Selectors.toString_; AbstractValue toStringType = AbstractValueFactory.inferredTypeForSelector( selector, input.instructionType, _globalInferenceResults); - if (!_abstractValueDomain.containsOnlyType( - toStringType, _closedWorld.commonElements.jsStringClass)) { + if (_abstractValueDomain + .containsOnlyType( + toStringType, _closedWorld.commonElements.jsStringClass) + .isPotentiallyFalse) { return null; } // All intercepted classes extend `Interceptor`, so if the receiver can't // be a class extending `Interceptor` then it can be called directly. - if (!_abstractValueDomain.canBeInterceptor(input.instructionType)) { + if (_abstractValueDomain + .isInterceptor(input.instructionType) + .isDefinitelyFalse) { var inputs = [input, input]; // [interceptor, receiver]. HInstruction result = new HInvokeDynamicMethod( selector, @@ -1628,7 +1688,9 @@ class SsaInstructionSimplifier extends HBaseVisitor // be a class extending `Interceptor` then the substitution methods can be // called directly. (We don't care about Null since contexts reading class // type variables originate from instance methods.) - if (!_abstractValueDomain.canBeInterceptor(object.instructionType)) { + if (_abstractValueDomain + .isInterceptor(object.instructionType) + .isDefinitelyFalse) { return new HTypeInfoReadVariable.noInterceptor( variable, object, node.instructionType); } @@ -1677,9 +1739,10 @@ class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase { isAssignable: !isFixedLength(array.instructionType, closedWorld)); indexNode.block.addBefore(indexNode, length); - AbstractValue type = indexArgument.isPositiveInteger(_abstractValueDomain) - ? indexArgument.instructionType - : closedWorld.abstractValueDomain.positiveIntType; + AbstractValue type = + indexArgument.isPositiveInteger(_abstractValueDomain).isDefinitelyTrue + ? indexArgument.instructionType + : closedWorld.abstractValueDomain.positiveIntType; HBoundsCheck check = new HBoundsCheck(indexArgument, length, array, type) ..sourceInformation = indexNode.sourceInformation; indexNode.block.addBefore(indexNode, check); @@ -1690,7 +1753,7 @@ class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase { // the index eg. if it is a constant. The range information from the // BoundsCheck instruction is attached to the input directly by // visitBoundsCheck in the SsaValueRangeAnalyzer. - if (!indexArgument.isInteger(_abstractValueDomain)) { + if (indexArgument.isInteger(_abstractValueDomain).isPotentiallyFalse) { indexArgument.replaceAllUsersDominatedBy(indexNode, check); } boundsChecked.add(indexNode); @@ -2117,7 +2180,7 @@ class SsaLiveBlockAnalyzer extends HBaseVisitor { } void visitSwitch(HSwitch node) { - if (node.expression.isInteger(_abstractValueDomain)) { + if (node.expression.isInteger(_abstractValueDomain).isDefinitelyTrue) { Range switchRange = ranges[node.expression]; if (switchRange != null && switchRange.lower is IntValue && @@ -2672,7 +2735,9 @@ class SsaTypeConversionInserter extends HBaseVisitor return; } - if (!_abstractValueDomain.canBeNull(input.instructionType)) return; + if (_abstractValueDomain.isNull(input.instructionType).isDefinitelyFalse) { + return; + } List trueTargets = []; List falseTargets = []; @@ -3043,8 +3108,9 @@ class MemorySet { if (nonEscapingReceivers.contains(second)) return false; // Typed arrays of different types might have a shared buffer. if (couldBeTypedArray(first) && couldBeTypedArray(second)) return true; - return !_abstractValueDomain.areDisjoint( - first.instructionType, second.instructionType); + return _abstractValueDomain + .areDisjoint(first.instructionType, second.instructionType) + .isPotentiallyFalse; } bool isFinal(Object element) { @@ -3059,7 +3125,8 @@ class MemorySet { bool couldBeTypedArray(HInstruction receiver) { return closedWorld.abstractValueDomain - .couldBeTypedArray(receiver.instructionType); + .couldBeTypedArray(receiver.instructionType) + .isPotentiallyTrue; } /// Returns whether [receiver] escapes the current function. diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart index be34f85a9d358..a4b3de81fc98d 100644 --- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart +++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart @@ -126,31 +126,43 @@ class HInstructionStringifier implements HVisitor { String temporaryId(HInstruction instruction) { String prefix; - if (instruction.isNull(_abstractValueDomain)) { + if (instruction.isNull(_abstractValueDomain).isDefinitelyTrue) { prefix = 'u'; - } else if (instruction.isConflicting(_abstractValueDomain)) { + } else if (instruction + .isConflicting(_abstractValueDomain) + .isDefinitelyTrue) { prefix = 'c'; - } else if (instruction.isExtendableArray(_abstractValueDomain)) { + } else if (instruction + .isExtendableArray(_abstractValueDomain) + .isDefinitelyTrue) { prefix = 'e'; - } else if (instruction.isFixedArray(_abstractValueDomain)) { + } else if (instruction + .isFixedArray(_abstractValueDomain) + .isDefinitelyTrue) { prefix = 'f'; - } else if (instruction.isMutableArray(_abstractValueDomain)) { + } else if (instruction + .isMutableArray(_abstractValueDomain) + .isDefinitelyTrue) { prefix = 'm'; - } else if (instruction.isArray(_abstractValueDomain)) { + } else if (instruction.isArray(_abstractValueDomain).isDefinitelyTrue) { prefix = 'a'; - } else if (instruction.isString(_abstractValueDomain)) { + } else if (instruction.isString(_abstractValueDomain).isDefinitelyTrue) { prefix = 's'; - } else if (instruction.isIndexablePrimitive(_abstractValueDomain)) { + } else if (instruction + .isIndexablePrimitive(_abstractValueDomain) + .isDefinitelyTrue) { prefix = 'r'; - } else if (instruction.isBoolean(_abstractValueDomain)) { + } else if (instruction.isBoolean(_abstractValueDomain).isDefinitelyTrue) { prefix = 'b'; - } else if (instruction.isInteger(_abstractValueDomain)) { + } else if (instruction.isInteger(_abstractValueDomain).isDefinitelyTrue) { prefix = 'i'; - } else if (instruction.isDouble(_abstractValueDomain)) { + } else if (instruction.isDouble(_abstractValueDomain).isDefinitelyTrue) { prefix = 'd'; - } else if (instruction.isNumber(_abstractValueDomain)) { + } else if (instruction.isNumber(_abstractValueDomain).isDefinitelyTrue) { prefix = 'n'; - } else if (_abstractValueDomain.containsAll(instruction.instructionType)) { + } else if (_abstractValueDomain + .containsAll(instruction.instructionType) + .isPotentiallyTrue) { prefix = 'v'; } else { prefix = 'U'; diff --git a/pkg/compiler/lib/src/ssa/types.dart b/pkg/compiler/lib/src/ssa/types.dart index 17a2f89c158f5..4fa5ee5a6f977 100644 --- a/pkg/compiler/lib/src/ssa/types.dart +++ b/pkg/compiler/lib/src/ssa/types.dart @@ -64,7 +64,7 @@ class AbstractValueFactory { AbstractValue result = abstractValueDomain.unionOfMany(typesReturned.map(fromNativeType)); - assert(!abstractValueDomain.isEmpty(result), + assert(abstractValueDomain.isEmpty(result).isPotentiallyFalse, "Unexpected empty return value for $nativeBehavior."); return result; } diff --git a/pkg/compiler/lib/src/ssa/types_propagation.dart b/pkg/compiler/lib/src/ssa/types_propagation.dart index 061ec21de2e6f..b95f1e0083735 100644 --- a/pkg/compiler/lib/src/ssa/types_propagation.dart +++ b/pkg/compiler/lib/src/ssa/types_propagation.dart @@ -129,11 +129,11 @@ class SsaTypePropagator extends HBaseVisitor implements OptimizationPhase { AbstractValue visitBinaryArithmetic(HBinaryArithmetic instruction) { HInstruction left = instruction.left; HInstruction right = instruction.right; - if (left.isInteger(abstractValueDomain) && - right.isInteger(abstractValueDomain)) { + if (left.isInteger(abstractValueDomain).isDefinitelyTrue && + right.isInteger(abstractValueDomain).isDefinitelyTrue) { return abstractValueDomain.intType; } - if (left.isDouble(abstractValueDomain)) { + if (left.isDouble(abstractValueDomain).isDefinitelyTrue) { return abstractValueDomain.doubleType; } return abstractValueDomain.numType; @@ -142,8 +142,8 @@ class SsaTypePropagator extends HBaseVisitor implements OptimizationPhase { AbstractValue checkPositiveInteger(HBinaryArithmetic instruction) { HInstruction left = instruction.left; HInstruction right = instruction.right; - if (left.isPositiveInteger(abstractValueDomain) && - right.isPositiveInteger(abstractValueDomain)) { + if (left.isPositiveInteger(abstractValueDomain).isDefinitelyTrue && + right.isPositiveInteger(abstractValueDomain).isDefinitelyTrue) { return abstractValueDomain.positiveIntType; } return visitBinaryArithmetic(instruction); @@ -176,7 +176,7 @@ class SsaTypePropagator extends HBaseVisitor implements OptimizationPhase { HInstruction operand = instruction.operand; // We have integer subclasses that represent ranges, so widen any int // subclass to full integer. - if (operand.isInteger(abstractValueDomain)) { + if (operand.isInteger(abstractValueDomain).isDefinitelyTrue) { return abstractValueDomain.intType; } return instruction.operand.instructionType; @@ -209,28 +209,30 @@ class SsaTypePropagator extends HBaseVisitor implements OptimizationPhase { // We must make sure a type conversion for receiver or argument check // does not try to do an int check, because an int check is not enough. // We only do an int check if the input is integer or null. - if (abstractValueDomain.isNumberOrNull(checkedType) && - !abstractValueDomain.isDoubleOrNull(checkedType) && - input.isIntegerOrNull(abstractValueDomain)) { + if (abstractValueDomain.isNumberOrNull(checkedType).isDefinitelyTrue && + abstractValueDomain.isDoubleOrNull(checkedType).isDefinitelyFalse && + input.isIntegerOrNull(abstractValueDomain).isDefinitelyTrue) { instruction.checkedType = abstractValueDomain.intType; - } else if (abstractValueDomain.isIntegerOrNull(checkedType) && - !input.isIntegerOrNull(abstractValueDomain)) { + } else if (abstractValueDomain + .isIntegerOrNull(checkedType) + .isDefinitelyTrue && + input.isIntegerOrNull(abstractValueDomain).isPotentiallyFalse) { instruction.checkedType = abstractValueDomain.numType; } } AbstractValue outputType = abstractValueDomain.intersection(checkedType, inputType); - if (abstractValueDomain.isEmpty(outputType)) { + if (abstractValueDomain.isEmpty(outputType).isDefinitelyTrue) { // Intersection of double and integer conflicts (is empty), but JS numbers // can be both int and double at the same time. For example, the input // can be a literal double '8.0' that is marked as an integer (because 'is // int' will return 'true'). What we really need to do is make the // overlap between int and double values explicit in the TypeMask system. - if (abstractValueDomain.isIntegerOrNull(inputType) && - abstractValueDomain.isDoubleOrNull(checkedType)) { - if (abstractValueDomain.canBeNull(inputType) && - abstractValueDomain.canBeNull(checkedType)) { + if (abstractValueDomain.isIntegerOrNull(inputType).isDefinitelyTrue && + abstractValueDomain.isDoubleOrNull(checkedType).isDefinitelyTrue) { + if (abstractValueDomain.isNull(inputType).isPotentiallyTrue && + abstractValueDomain.isNull(checkedType).isPotentiallyTrue) { outputType = abstractValueDomain.includeNull(abstractValueDomain.doubleType); } else { @@ -283,11 +285,11 @@ class SsaTypePropagator extends HBaseVisitor implements OptimizationPhase { // In some cases, we want the receiver to be an integer, // but that does not mean we will get a NoSuchMethodError // if it's not: the receiver could be a double. - if (abstractValueDomain.isIntegerOrNull(type)) { + if (abstractValueDomain.isIntegerOrNull(type).isDefinitelyTrue) { // If the instruction's type is integer or null, the codegen // will emit a null check, which is enough to know if it will // hit a noSuchMethod. - return instruction.isIntegerOrNull(abstractValueDomain); + return instruction.isIntegerOrNull(abstractValueDomain).isDefinitelyTrue; } return true; } @@ -298,8 +300,10 @@ class SsaTypePropagator extends HBaseVisitor implements OptimizationPhase { bool checkReceiver(HInvokeDynamic instruction) { assert(instruction.isInterceptedCall); HInstruction receiver = instruction.inputs[1]; - if (receiver.isNumber(abstractValueDomain)) return false; - if (receiver.isNumberOrNull(abstractValueDomain)) { + if (receiver.isNumber(abstractValueDomain).isDefinitelyTrue) { + return false; + } + if (receiver.isNumberOrNull(abstractValueDomain).isDefinitelyTrue) { convertInput( instruction, receiver, @@ -318,8 +322,8 @@ class SsaTypePropagator extends HBaseVisitor implements OptimizationPhase { ClassEntity cls = target.enclosingClass; AbstractValue type = abstractValueDomain.createNonNullSubclass(cls); // We currently only optimize on some primitive types. - if (!abstractValueDomain.isNumberOrNull(type) && - !abstractValueDomain.isBooleanOrNull(type)) { + if (abstractValueDomain.isNumberOrNull(type).isPotentiallyFalse && + abstractValueDomain.isBooleanOrNull(type).isPotentiallyFalse) { return false; } if (!isCheckEnoughForNsmOrAe(receiver, type)) return false; @@ -340,11 +344,15 @@ class SsaTypePropagator extends HBaseVisitor implements OptimizationPhase { HInstruction right = instruction.inputs[2]; Selector selector = instruction.selector; - if (selector.isOperator && left.isNumber(abstractValueDomain)) { - if (right.isNumber(abstractValueDomain)) return false; - AbstractValue type = right.isIntegerOrNull(abstractValueDomain) - ? abstractValueDomain.excludeNull(right.instructionType) - : abstractValueDomain.numType; + if (selector.isOperator && + left.isNumber(abstractValueDomain).isDefinitelyTrue) { + if (right.isNumber(abstractValueDomain).isDefinitelyTrue) { + return false; + } + AbstractValue type = + right.isIntegerOrNull(abstractValueDomain).isDefinitelyTrue + ? abstractValueDomain.excludeNull(right.instructionType) + : abstractValueDomain.numType; // TODO(ngeoffray): Some number operations don't have a builtin // variant and will do the check in their method anyway. We // still add a check because it allows to GVN these operations, diff --git a/pkg/compiler/lib/src/ssa/value_range_analyzer.dart b/pkg/compiler/lib/src/ssa/value_range_analyzer.dart index 075ee0eed1496..acdfd27a55db2 100644 --- a/pkg/compiler/lib/src/ssa/value_range_analyzer.dart +++ b/pkg/compiler/lib/src/ssa/value_range_analyzer.dart @@ -633,7 +633,9 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { void visitBasicBlock(HBasicBlock block) { void visit(HInstruction instruction) { Range range = instruction.accept(this); - if (instruction.isInteger(closedWorld.abstractValueDomain)) { + if (instruction + .isInteger(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { assert(range != null); ranges[instruction] = range; } @@ -644,10 +646,14 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { } Range visitInstruction(HInstruction instruction) { - if (instruction.isPositiveInteger(closedWorld.abstractValueDomain)) { + if (instruction + .isPositiveInteger(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { return info.newNormalizedRange( info.intZero, info.newPositiveValue(instruction)); - } else if (instruction.isInteger(closedWorld.abstractValueDomain)) { + } else if (instruction + .isInteger(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { InstructionValue value = info.newInstructionValue(instruction); return info.newNormalizedRange(value, value); } else { @@ -656,13 +662,14 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { } Range visitPhi(HPhi phi) { - if (!phi.isInteger(closedWorld.abstractValueDomain)) + if (phi.isInteger(closedWorld.abstractValueDomain).isPotentiallyFalse) return info.newUnboundRange(); // Some phases may replace instructions that change the inputs of // this phi. Only the [SsaTypesPropagation] phase will update the // phi type. Play it safe by assuming the [SsaTypesPropagation] // phase is not necessarily run before the [ValueRangeAnalyzer]. - if (phi.inputs.any((i) => !i.isInteger(closedWorld.abstractValueDomain))) { + if (phi.inputs.any((i) => + i.isInteger(closedWorld.abstractValueDomain).isPotentiallyFalse)) { return info.newUnboundRange(); } if (phi.block.isLoopHeader()) { @@ -680,8 +687,11 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { } Range visitConstant(HConstant hConstant) { - if (!hConstant.isInteger(closedWorld.abstractValueDomain)) + if (hConstant + .isInteger(closedWorld.abstractValueDomain) + .isPotentiallyFalse) { return info.newUnboundRange(); + } ConstantValue constant = hConstant.constant; NumConstantValue constantNum; if (constant is DeferredGlobalConstantValue) { @@ -723,7 +733,9 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { Range lengthRange = ranges[check.length]; if (indexRange == null) { indexRange = info.newUnboundRange(); - assert(!check.index.isInteger(closedWorld.abstractValueDomain)); + assert(check.index + .isInteger(closedWorld.abstractValueDomain) + .isPotentiallyFalse); } if (lengthRange == null) { // We might have lost the length range due to a type conversion that @@ -731,7 +743,9 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { // get to this point anyway, so no need to try and refine ranges. return indexRange; } - assert(check.length.isInteger(closedWorld.abstractValueDomain)); + assert(check.length + .isInteger(closedWorld.abstractValueDomain) + .isDefinitelyTrue); // Check if the index is strictly below the upper bound of the length // range. @@ -784,10 +798,12 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { Range visitRelational(HRelational relational) { HInstruction right = relational.right; HInstruction left = relational.left; - if (!left.isInteger(closedWorld.abstractValueDomain)) + if (left.isInteger(closedWorld.abstractValueDomain).isPotentiallyFalse) { return info.newUnboundRange(); - if (!right.isInteger(closedWorld.abstractValueDomain)) + } + if (right.isInteger(closedWorld.abstractValueDomain).isPotentiallyFalse) { return info.newUnboundRange(); + } BinaryOperation operation = relational.operation(constantSystem); Range rightRange = ranges[relational.right]; Range leftRange = ranges[relational.left]; @@ -822,8 +838,8 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { if (divisor != null) { // For Integer values we can be precise in the upper bound, so special // case those. - if (left.isInteger(closedWorld.abstractValueDomain) && - right.isInteger(closedWorld.abstractValueDomain)) { + if (left.isInteger(closedWorld.abstractValueDomain).isDefinitelyTrue && + right.isInteger(closedWorld.abstractValueDomain).isDefinitelyTrue) { if (divisor.isPositive) { return info.newNormalizedRange( info.intZero, divisor.upper - info.intOne); @@ -831,8 +847,10 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { return info.newNormalizedRange( info.intZero, info.newNegateValue(divisor.lower) - info.intOne); } - } else if (left.isNumber(closedWorld.abstractValueDomain) && - right.isNumber(closedWorld.abstractValueDomain)) { + } else if (left + .isNumber(closedWorld.abstractValueDomain) + .isDefinitelyTrue && + right.isNumber(closedWorld.abstractValueDomain).isDefinitelyTrue) { if (divisor.isPositive) { return info.newNormalizedRange(info.intZero, divisor.upper); } else if (divisor.isNegative) { @@ -850,18 +868,26 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { Range dividend = ranges[left]; // If both operands are >=0, the result is >= 0 and bounded by the divisor. if ((dividend != null && dividend.isPositive) || - left.isPositiveInteger(closedWorld.abstractValueDomain)) { + left + .isPositiveInteger(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { Range divisor = ranges[right]; if (divisor != null) { if (divisor.isPositive) { // For Integer values we can be precise in the upper bound. - if (left.isInteger(closedWorld.abstractValueDomain) && - right.isInteger(closedWorld.abstractValueDomain)) { + if (left + .isInteger(closedWorld.abstractValueDomain) + .isDefinitelyTrue && + right + .isInteger(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { return info.newNormalizedRange( info.intZero, divisor.upper - info.intOne); } - if (left.isNumber(closedWorld.abstractValueDomain) && - right.isNumber(closedWorld.abstractValueDomain)) { + if (left.isNumber(closedWorld.abstractValueDomain).isDefinitelyTrue && + right + .isNumber(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { return info.newNormalizedRange(info.intZero, divisor.upper); } } @@ -877,7 +903,9 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { } Range handleBinaryOperation(HBinaryArithmetic instruction) { - if (!instruction.isInteger(closedWorld.abstractValueDomain)) { + if (instruction + .isInteger(closedWorld.abstractValueDomain) + .isPotentiallyFalse) { return info.newUnboundRange(); } return instruction @@ -894,13 +922,13 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { } Range visitBitAnd(HBitAnd node) { - if (!node.isInteger(closedWorld.abstractValueDomain)) { + if (node.isInteger(closedWorld.abstractValueDomain).isPotentiallyFalse) { return info.newUnboundRange(); } HInstruction right = node.right; HInstruction left = node.left; - if (left.isInteger(closedWorld.abstractValueDomain) && - right.isInteger(closedWorld.abstractValueDomain)) { + if (left.isInteger(closedWorld.abstractValueDomain).isDefinitelyTrue && + right.isInteger(closedWorld.abstractValueDomain).isDefinitelyTrue) { return ranges[left] & ranges[right]; } @@ -914,9 +942,11 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { return info.newUnboundRange(); } - if (left.isInteger(closedWorld.abstractValueDomain)) { + if (left.isInteger(closedWorld.abstractValueDomain).isDefinitelyTrue) { return tryComputeRange(left); - } else if (right.isInteger(closedWorld.abstractValueDomain)) { + } else if (right + .isInteger(closedWorld.abstractValueDomain) + .isDefinitelyTrue) { return tryComputeRange(right); } return info.newUnboundRange(); @@ -995,10 +1025,10 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { if (condition is HIdentity) return info.newUnboundRange(); HInstruction right = condition.right; HInstruction left = condition.left; - if (!left.isInteger(closedWorld.abstractValueDomain)) { + if (left.isInteger(closedWorld.abstractValueDomain).isPotentiallyFalse) { return info.newUnboundRange(); } - if (!right.isInteger(closedWorld.abstractValueDomain)) { + if (right.isInteger(closedWorld.abstractValueDomain).isPotentiallyFalse) { return info.newUnboundRange(); } @@ -1090,7 +1120,11 @@ class LoopUpdateRecognizer extends HBaseVisitor { } Range visit(HInstruction instruction) { - if (!instruction.isInteger(closedWorld.abstractValueDomain)) return null; + if (instruction + .isInteger(closedWorld.abstractValueDomain) + .isPotentiallyFalse) { + return null; + } if (ranges[instruction] != null) return ranges[instruction]; return instruction.accept(this); } diff --git a/pkg/compiler/lib/src/universe/function_set.dart b/pkg/compiler/lib/src/universe/function_set.dart index 0c5d0f86454d7..f48915879dfb8 100644 --- a/pkg/compiler/lib/src/universe/function_set.dart +++ b/pkg/compiler/lib/src/universe/function_set.dart @@ -108,11 +108,15 @@ class SelectorMask { bool applies(MemberEntity element, AbstractValueDomain domain) { if (!selector.appliesUnnamed(element)) return false; - return domain.canHit(receiver, element, selector); + return domain + .isTargetingMember(receiver, element, selector) + .isPotentiallyTrue; } bool needsNoSuchMethodHandling(AbstractValueDomain domain) { - return domain.needsNoSuchMethodHandling(receiver, selector); + return domain + .needsNoSuchMethodHandling(receiver, selector) + .isPotentiallyTrue; } bool operator ==(other) { diff --git a/tests/compiler/dart2js/analyses/dart2js_allowed.json b/tests/compiler/dart2js/analyses/dart2js_allowed.json index 73b5fde2d1fa9..7f837eb42fd8d 100644 --- a/tests/compiler/dart2js/analyses/dart2js_allowed.json +++ b/tests/compiler/dart2js/analyses/dart2js_allowed.json @@ -101,9 +101,6 @@ "Dynamic invocation of 'nonNullable'.": 1, "Dynamic invocation of 'every'.": 1 }, - "pkg/compiler/lib/src/ssa/nodes.dart": { - "Dynamic invocation of 'replaceWith'.": 1 - }, "pkg/compiler/lib/src/helpers/expensive_map.dart": { "Dynamic access of 'length'.": 1, "Dynamic access of 'isEmpty'.": 1, @@ -160,6 +157,12 @@ "pkg/compiler/lib/src/native/enqueue.dart": { "Dynamic access of 'isDynamic'.": 1 }, + "pkg/compiler/lib/src/inferrer/inferrer_engine.dart": { + "Dynamic access of 'isVoid'.": 1, + "Dynamic access of 'isDynamic'.": 1, + "Dynamic access of 'isInterfaceType'.": 1, + "Dynamic access of 'element'.": 1 + }, "pkg/compiler/lib/src/js_backend/checked_mode_helpers.dart": { "Dynamic access of 'name'.": 1 }, @@ -187,20 +190,6 @@ "Dynamic access of 'usedBy'.": 2, "Dynamic access of 'inputs'.": 1 }, - "pkg/compiler/lib/src/inferrer/inferrer_engine.dart": { - "Dynamic access of 'isVoid'.": 1, - "Dynamic access of 'isDynamic'.": 1, - "Dynamic access of 'isInterfaceType'.": 1, - "Dynamic access of 'element'.": 1 - }, - "pkg/compiler/lib/src/ssa/optimize.dart": { - "Dynamic access of 'isEmpty'.": 1, - "Dynamic invocation of 'replaceWith'.": 1, - "Dynamic access of 'length'.": 2, - "Dynamic access of 'isContainer'.": 1, - "Dynamic invocation of 'containsOnly'.": 2, - "Dynamic invocation of 'containsOnlyString'.": 1 - }, "pkg/compiler/lib/src/constant_system_dart.dart": { "Dynamic invocation of '|'.": 1, "Dynamic invocation of '&'.": 1, @@ -248,10 +237,6 @@ "Dynamic access of 'isEmpty'.": 1, "Dynamic invocation of 'satisfies'.": 2 }, - "pkg/compiler/lib/src/universe/function_set.dart": { - "Dynamic access of 'selector'.": 1, - "Dynamic access of 'receiver'.": 1 - }, "pkg/compiler/lib/src/inferrer/locals_handler.dart": { "Dynamic access of 'isEmpty'.": 1, "Dynamic access of 'positional'.": 2, @@ -259,6 +244,10 @@ "Dynamic access of 'named'.": 2, "Dynamic invocation of '[]'.": 2 }, + "pkg/compiler/lib/src/universe/function_set.dart": { + "Dynamic access of 'selector'.": 1, + "Dynamic access of 'receiver'.": 1 + }, "pkg/compiler/lib/src/ssa/variable_allocator.dart": { "Dynamic access of 'usedBy'.": 1, "Dynamic access of 'isEmpty'.": 1,