|  | // Copyright (c) 2014, 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. | 
|  |  | 
|  | #ifndef RUNTIME_VM_COMPILER_BACKEND_RANGE_ANALYSIS_H_ | 
|  | #define RUNTIME_VM_COMPILER_BACKEND_RANGE_ANALYSIS_H_ | 
|  |  | 
|  | #if defined(DART_PRECOMPILED_RUNTIME) | 
|  | #error "AOT runtime should not use compiler sources (including header files)" | 
|  | #endif  // defined(DART_PRECOMPILED_RUNTIME) | 
|  |  | 
|  | #include "vm/compiler/backend/flow_graph.h" | 
|  | #include "vm/compiler/backend/il.h" | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | class RangeBoundary : public ValueObject { | 
|  | public: | 
|  | #define FOR_EACH_RANGE_BOUNDARY_KIND(V)                                        \ | 
|  | V(Unknown)                                                                   \ | 
|  | V(Symbol)                                                                    \ | 
|  | V(Constant) | 
|  |  | 
|  | #define KIND_DEFN(name) k##name, | 
|  | enum Kind { FOR_EACH_RANGE_BOUNDARY_KIND(KIND_DEFN) }; | 
|  | #undef KIND_DEFN | 
|  |  | 
|  | enum RangeSize { | 
|  | kRangeBoundarySmi, | 
|  | kRangeBoundaryInt8, | 
|  | kRangeBoundaryInt16, | 
|  | kRangeBoundaryInt32, | 
|  | kRangeBoundaryInt64, | 
|  | }; | 
|  |  | 
|  | RangeBoundary() : kind_(kUnknown), value_(0), offset_(0) {} | 
|  |  | 
|  | RangeBoundary(const RangeBoundary& other) | 
|  | : ValueObject(), | 
|  | kind_(other.kind_), | 
|  | value_(other.value_), | 
|  | offset_(other.offset_) {} | 
|  |  | 
|  | explicit RangeBoundary(int64_t val) | 
|  | : kind_(kConstant), value_(val), offset_(0) {} | 
|  |  | 
|  | RangeBoundary& operator=(const RangeBoundary& other) { | 
|  | kind_ = other.kind_; | 
|  | value_ = other.value_; | 
|  | offset_ = other.offset_; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | static constexpr int64_t kMin = kMinInt64; | 
|  | static constexpr int64_t kMax = kMaxInt64; | 
|  |  | 
|  | // Construct a RangeBoundary for a constant value. | 
|  | static RangeBoundary FromConstant(int64_t val) { return RangeBoundary(val); } | 
|  |  | 
|  | // Construct a RangeBoundary from a definition and offset. | 
|  | static RangeBoundary FromDefinition(Definition* defn, int64_t offs = 0); | 
|  |  | 
|  | static bool IsValidOffsetForSymbolicRangeBoundary(int64_t offset) { | 
|  | if ((offset > (kMaxInt64 - compiler::target::kSmiMax)) || | 
|  | (offset < (kMinInt64 - compiler::target::kSmiMin))) { | 
|  | // Avoid creating symbolic range boundaries which can wrap around. | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Construct a RangeBoundary for the constant MinSmi value. | 
|  | static RangeBoundary MinSmi() { | 
|  | return FromConstant(compiler::target::kSmiMin); | 
|  | } | 
|  |  | 
|  | // Construct a RangeBoundary for the constant MaxSmi value. | 
|  | static RangeBoundary MaxSmi() { | 
|  | return FromConstant(compiler::target::kSmiMax); | 
|  | } | 
|  |  | 
|  | // Construct a RangeBoundary for the constant kMin value. | 
|  | static RangeBoundary MinConstant(RangeSize size) { | 
|  | switch (size) { | 
|  | case kRangeBoundarySmi: | 
|  | return FromConstant(compiler::target::kSmiMin); | 
|  | case kRangeBoundaryInt8: | 
|  | return FromConstant(kMinInt8); | 
|  | case kRangeBoundaryInt16: | 
|  | return FromConstant(kMinInt16); | 
|  | case kRangeBoundaryInt32: | 
|  | return FromConstant(kMinInt32); | 
|  | case kRangeBoundaryInt64: | 
|  | return FromConstant(kMinInt64); | 
|  | } | 
|  | UNREACHABLE(); | 
|  | return FromConstant(kMinInt64); | 
|  | } | 
|  |  | 
|  | static RangeBoundary MaxConstant(RangeSize size) { | 
|  | switch (size) { | 
|  | case kRangeBoundarySmi: | 
|  | return FromConstant(compiler::target::kSmiMax); | 
|  | case kRangeBoundaryInt8: | 
|  | return FromConstant(kMaxInt8); | 
|  | case kRangeBoundaryInt16: | 
|  | return FromConstant(kMaxInt16); | 
|  | case kRangeBoundaryInt32: | 
|  | return FromConstant(kMaxInt32); | 
|  | case kRangeBoundaryInt64: | 
|  | return FromConstant(kMaxInt64); | 
|  | } | 
|  | UNREACHABLE(); | 
|  | return FromConstant(kMaxInt64); | 
|  | } | 
|  |  | 
|  | // Given two boundaries a and b, select one of them as c so that | 
|  | // | 
|  | //   inf {[a, ...) ^ [b, ...)} >= inf {c} | 
|  | // | 
|  | static RangeBoundary IntersectionMin(RangeBoundary a, RangeBoundary b); | 
|  |  | 
|  | // Given two boundaries a and b, select one of them as c so that | 
|  | // | 
|  | //   sup {(..., a] ^ (..., b]} <= sup {c} | 
|  | // | 
|  | static RangeBoundary IntersectionMax(RangeBoundary a, RangeBoundary b); | 
|  |  | 
|  | // Given two boundaries a and b compute boundary c such that | 
|  | // | 
|  | //   inf {[a, ...) U  [b, ...)} >= inf {c} | 
|  | // | 
|  | // Try to select c such that it is as close to inf {[a, ...) U [b, ...)} | 
|  | // as possible. | 
|  | static RangeBoundary JoinMin(RangeBoundary a, | 
|  | RangeBoundary b, | 
|  | RangeBoundary::RangeSize size); | 
|  |  | 
|  | // Given two boundaries a and b compute boundary c such that | 
|  | // | 
|  | //   sup {(..., a] U (..., b]} <= sup {c} | 
|  | // | 
|  | // Try to select c such that it is as close to sup {(..., a] U (..., b]} | 
|  | // as possible. | 
|  | static RangeBoundary JoinMax(RangeBoundary a, | 
|  | RangeBoundary b, | 
|  | RangeBoundary::RangeSize size); | 
|  |  | 
|  | // Returns true when this is a constant that is outside of Smi range. | 
|  | bool OverflowedSmi() const { | 
|  | return IsConstant() && !compiler::target::IsSmi(ConstantValue()); | 
|  | } | 
|  |  | 
|  | bool Overflowed(RangeBoundary::RangeSize size) const { | 
|  | ASSERT(IsConstant()); | 
|  | return !Equals(Clamp(size)); | 
|  | } | 
|  |  | 
|  | // Clamp constant boundary to MinConstant/MaxConstant of the given size. | 
|  | RangeBoundary Clamp(RangeSize size) const { | 
|  | if (IsConstant()) { | 
|  | const RangeBoundary range_min = RangeBoundary::MinConstant(size); | 
|  | const RangeBoundary range_max = RangeBoundary::MaxConstant(size); | 
|  |  | 
|  | if (ConstantValue() <= range_min.ConstantValue()) { | 
|  | return range_min; | 
|  | } | 
|  | if (ConstantValue() >= range_max.ConstantValue()) { | 
|  | return range_max; | 
|  | } | 
|  | } | 
|  |  | 
|  | // If this range is a symbolic range, we do not clamp it. | 
|  | // This could lead to some imprecision later on. | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | bool IsMinimumOrBelow(RangeSize size) const { | 
|  | return IsConstant() && (ConstantValue() <= | 
|  | RangeBoundary::MinConstant(size).ConstantValue()); | 
|  | } | 
|  |  | 
|  | bool IsMaximumOrAbove(RangeSize size) const { | 
|  | return IsConstant() && (ConstantValue() >= | 
|  | RangeBoundary::MaxConstant(size).ConstantValue()); | 
|  | } | 
|  |  | 
|  | intptr_t kind() const { return kind_; } | 
|  |  | 
|  | // Kind tests. | 
|  | bool IsUnknown() const { return kind_ == kUnknown; } | 
|  | bool IsConstant() const { return kind_ == kConstant; } | 
|  | bool IsSymbol() const { return kind_ == kSymbol; } | 
|  |  | 
|  | // Returns the value of a kConstant RangeBoundary. | 
|  | int64_t ConstantValue() const; | 
|  |  | 
|  | // Returns the Definition associated with a kSymbol RangeBoundary. | 
|  | Definition* symbol() const { | 
|  | ASSERT(IsSymbol()); | 
|  | return reinterpret_cast<Definition*>(value_); | 
|  | } | 
|  |  | 
|  | // Offset from symbol. | 
|  | int64_t offset() const { return offset_; } | 
|  |  | 
|  | // Computes the LowerBound of this. Three cases: | 
|  | // IsConstant() -> value(). | 
|  | // IsSymbol() -> lower bound computed from definition + offset. | 
|  | RangeBoundary LowerBound() const; | 
|  |  | 
|  | // Computes the UpperBound of this. Three cases: | 
|  | // IsConstant() -> value(). | 
|  | // IsSymbol() -> upper bound computed from definition + offset. | 
|  | RangeBoundary UpperBound() const; | 
|  |  | 
|  | void PrintTo(BaseTextBuffer* f) const; | 
|  | const char* ToCString() const; | 
|  |  | 
|  | static bool WillAddOverflow(const RangeBoundary& a, const RangeBoundary& b); | 
|  |  | 
|  | static RangeBoundary Add(const RangeBoundary& a, const RangeBoundary& b); | 
|  |  | 
|  | static bool WillSubOverflow(const RangeBoundary& a, const RangeBoundary& b); | 
|  |  | 
|  | static RangeBoundary Sub(const RangeBoundary& a, const RangeBoundary& b); | 
|  |  | 
|  | static bool WillShlOverflow(const RangeBoundary& a, int64_t shift_count); | 
|  |  | 
|  | static RangeBoundary Shl(const RangeBoundary& value_boundary, | 
|  | int64_t shift_count); | 
|  |  | 
|  | static RangeBoundary Shr(const RangeBoundary& value_boundary, | 
|  | int64_t shift_count); | 
|  |  | 
|  | // Attempts to calculate a + b when: | 
|  | // a is a symbol and b is a constant OR | 
|  | // a is a constant and b is a symbol | 
|  | // returns true if it succeeds, output is in result. | 
|  | static bool SymbolicAdd(const RangeBoundary& a, | 
|  | const RangeBoundary& b, | 
|  | RangeBoundary* result); | 
|  |  | 
|  | // Attempts to calculate a - b when: | 
|  | // a is a symbol and b is a constant | 
|  | // returns true if it succeeds, output is in result. | 
|  | static bool SymbolicSub(const RangeBoundary& a, | 
|  | const RangeBoundary& b, | 
|  | RangeBoundary* result); | 
|  |  | 
|  | bool Equals(const RangeBoundary& other) const; | 
|  |  | 
|  | int64_t UpperBound(RangeSize size) const { | 
|  | return UpperBound().Clamp(size).ConstantValue(); | 
|  | } | 
|  |  | 
|  | int64_t LowerBound(RangeSize size) const { | 
|  | return LowerBound().Clamp(size).ConstantValue(); | 
|  | } | 
|  |  | 
|  | int64_t SmiUpperBound() const { return UpperBound(kRangeBoundarySmi); } | 
|  |  | 
|  | int64_t SmiLowerBound() const { return LowerBound(kRangeBoundarySmi); } | 
|  |  | 
|  | void Write(FlowGraphSerializer* s) const; | 
|  | explicit RangeBoundary(FlowGraphDeserializer* d); | 
|  |  | 
|  | private: | 
|  | RangeBoundary(Kind kind, int64_t value, int64_t offset) | 
|  | : kind_(kind), value_(value), offset_(offset) {} | 
|  |  | 
|  | Kind kind_; | 
|  | int64_t value_; | 
|  | int64_t offset_; | 
|  | }; | 
|  |  | 
|  | class Range : public ZoneAllocated { | 
|  | public: | 
|  | Range() : min_(), max_() {} | 
|  |  | 
|  | Range(RangeBoundary min, RangeBoundary max) : min_(min), max_(max) { | 
|  | ASSERT(min_.IsUnknown() == max_.IsUnknown()); | 
|  | } | 
|  |  | 
|  | Range(const Range& other) | 
|  | : ZoneAllocated(), min_(other.min_), max_(other.max_) {} | 
|  |  | 
|  | Range& operator=(const Range& other) { | 
|  | min_ = other.min_; | 
|  | max_ = other.max_; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | static bool IsUnknown(const Range* other) { | 
|  | if (other == nullptr) { | 
|  | return true; | 
|  | } | 
|  | return other->min().IsUnknown(); | 
|  | } | 
|  |  | 
|  | static Range Full(RangeBoundary::RangeSize size) { | 
|  | return Range(RangeBoundary::MinConstant(size), | 
|  | RangeBoundary::MaxConstant(size)); | 
|  | } | 
|  |  | 
|  | static Range Full(Representation rep); | 
|  |  | 
|  | void PrintTo(BaseTextBuffer* f) const; | 
|  | static const char* ToCString(const Range* range); | 
|  |  | 
|  | bool Equals(const Range* other) { | 
|  | ASSERT(min_.IsUnknown() == max_.IsUnknown()); | 
|  | if (other == nullptr) { | 
|  | return min_.IsUnknown(); | 
|  | } | 
|  | return min_.Equals(other->min_) && max_.Equals(other->max_); | 
|  | } | 
|  |  | 
|  | const RangeBoundary& min() const { return min_; } | 
|  | const RangeBoundary& max() const { return max_; } | 
|  |  | 
|  | void set_min(const RangeBoundary& value) { min_ = value; } | 
|  |  | 
|  | void set_max(const RangeBoundary& value) { max_ = value; } | 
|  |  | 
|  | static RangeBoundary ConstantMinSmi(const Range* range) { | 
|  | return ConstantMin(range, RangeBoundary::kRangeBoundarySmi); | 
|  | } | 
|  |  | 
|  | static RangeBoundary ConstantMaxSmi(const Range* range) { | 
|  | return ConstantMax(range, RangeBoundary::kRangeBoundarySmi); | 
|  | } | 
|  |  | 
|  | static RangeBoundary ConstantMin(const Range* range) { | 
|  | return ConstantMin(range, RangeBoundary::kRangeBoundaryInt64); | 
|  | } | 
|  |  | 
|  | static RangeBoundary ConstantMax(const Range* range) { | 
|  | return ConstantMax(range, RangeBoundary::kRangeBoundaryInt64); | 
|  | } | 
|  |  | 
|  | static RangeBoundary ConstantMin(const Range* range, | 
|  | RangeBoundary::RangeSize size) { | 
|  | if (range == nullptr) { | 
|  | return RangeBoundary::MinConstant(size); | 
|  | } | 
|  | return range->min().LowerBound().Clamp(size); | 
|  | } | 
|  |  | 
|  | static RangeBoundary ConstantMax(const Range* range, | 
|  | RangeBoundary::RangeSize size) { | 
|  | if (range == nullptr) { | 
|  | return RangeBoundary::MaxConstant(size); | 
|  | } | 
|  | return range->max().UpperBound().Clamp(size); | 
|  | } | 
|  |  | 
|  | // [0, +inf] | 
|  | bool IsPositive() const; | 
|  |  | 
|  | // [-inf, -1] | 
|  | bool IsNegative() const; | 
|  |  | 
|  | // [-inf, val]. | 
|  | bool OnlyLessThanOrEqualTo(int64_t val) const; | 
|  |  | 
|  | // [val, +inf]. | 
|  | bool OnlyGreaterThanOrEqualTo(int64_t val) const; | 
|  |  | 
|  | // Inclusive. | 
|  | bool IsWithin(int64_t min_int, int64_t max_int) const; | 
|  |  | 
|  | // Inclusive. | 
|  | bool IsWithin(const Range* other) const; | 
|  |  | 
|  | // Inclusive. | 
|  | bool Overlaps(int64_t min_int, int64_t max_int) const; | 
|  |  | 
|  | bool IsUnsatisfiable() const; | 
|  |  | 
|  | bool IsSingleton() const { | 
|  | return min_.IsConstant() && max_.IsConstant() && | 
|  | min_.ConstantValue() == max_.ConstantValue(); | 
|  | } | 
|  |  | 
|  | int64_t Singleton() const { | 
|  | ASSERT(IsSingleton()); | 
|  | return min_.ConstantValue(); | 
|  | } | 
|  |  | 
|  | Range Intersect(const Range* other) const { | 
|  | return Range(RangeBoundary::IntersectionMin(min(), other->min()), | 
|  | RangeBoundary::IntersectionMax(max(), other->max())); | 
|  | } | 
|  |  | 
|  | bool Fits(RangeBoundary::RangeSize size) const { | 
|  | return !min().LowerBound().Overflowed(size) && | 
|  | !max().UpperBound().Overflowed(size); | 
|  | } | 
|  |  | 
|  | // Returns true if this range fits without truncation into | 
|  | // the given representation. | 
|  | static bool Fits(Range* range, Representation rep) { | 
|  | if (range == nullptr) return false; | 
|  | if (!RepresentationUtils::IsUnboxedInteger(rep)) return false; | 
|  | const Range& other = Range::Full(rep); | 
|  | return range->IsWithin(&other); | 
|  | } | 
|  |  | 
|  | // Clamp this to be within size. | 
|  | void Clamp(RangeBoundary::RangeSize size); | 
|  |  | 
|  | // Clamp this to be within size and eliminate symbols. | 
|  | void ClampToConstant(RangeBoundary::RangeSize size); | 
|  |  | 
|  | static void Add(const Range* left_range, | 
|  | const Range* right_range, | 
|  | RangeBoundary* min, | 
|  | RangeBoundary* max, | 
|  | Definition* left_defn); | 
|  |  | 
|  | static void Sub(const Range* left_range, | 
|  | const Range* right_range, | 
|  | RangeBoundary* min, | 
|  | RangeBoundary* max, | 
|  | Definition* left_defn); | 
|  |  | 
|  | static void Mul(const Range* left_range, | 
|  | const Range* right_range, | 
|  | RangeBoundary* min, | 
|  | RangeBoundary* max); | 
|  |  | 
|  | static void TruncDiv(const Range* left_range, | 
|  | const Range* right_range, | 
|  | RangeBoundary* min, | 
|  | RangeBoundary* max); | 
|  |  | 
|  | static void Mod(const Range* right_range, | 
|  | RangeBoundary* min, | 
|  | RangeBoundary* max); | 
|  |  | 
|  | static void Shr(const Range* left_range, | 
|  | const Range* right_range, | 
|  | RangeBoundary* min, | 
|  | RangeBoundary* max); | 
|  |  | 
|  | static void Ushr(const Range* left_range, | 
|  | const Range* right_range, | 
|  | RangeBoundary* min, | 
|  | RangeBoundary* max); | 
|  |  | 
|  | static void Shl(const Range* left_range, | 
|  | const Range* right_range, | 
|  | RangeBoundary* min, | 
|  | RangeBoundary* max); | 
|  |  | 
|  | static void And(const Range* left_range, | 
|  | const Range* right_range, | 
|  | RangeBoundary* min, | 
|  | RangeBoundary* max); | 
|  |  | 
|  | static void BitwiseOp(const Range* left_range, | 
|  | const Range* right_range, | 
|  | RangeBoundary* min, | 
|  | RangeBoundary* max); | 
|  |  | 
|  | // Both the a and b ranges are >= 0. | 
|  | static bool OnlyPositiveOrZero(const Range& a, const Range& b); | 
|  |  | 
|  | // Both the a and b ranges are <= 0. | 
|  | static bool OnlyNegativeOrZero(const Range& a, const Range& b); | 
|  |  | 
|  | // Return the maximum absolute value included in range. | 
|  | static int64_t ConstantAbsMax(const Range* range); | 
|  |  | 
|  | // Return the minimum absolute value included in range. | 
|  | static int64_t ConstantAbsMin(const Range* range); | 
|  |  | 
|  | static void BinaryOp(const Token::Kind op, | 
|  | const Range* left_range, | 
|  | const Range* right_range, | 
|  | Definition* left_defn, | 
|  | Range* result); | 
|  |  | 
|  | void Write(FlowGraphSerializer* s) const; | 
|  | explicit Range(FlowGraphDeserializer* d); | 
|  |  | 
|  | private: | 
|  | RangeBoundary min_; | 
|  | RangeBoundary max_; | 
|  | }; | 
|  |  | 
|  | class RangeUtils : public AllStatic { | 
|  | public: | 
|  | static bool Fits(Range* range, RangeBoundary::RangeSize size) { | 
|  | return !Range::IsUnknown(range) && range->Fits(size); | 
|  | } | 
|  |  | 
|  | static bool IsWithin(const Range* range, int64_t min, int64_t max) { | 
|  | return !Range::IsUnknown(range) && range->IsWithin(min, max); | 
|  | } | 
|  |  | 
|  | static bool IsWithin(const Range* range, const Range* other) { | 
|  | return !Range::IsUnknown(range) && range->IsWithin(other); | 
|  | } | 
|  |  | 
|  | static bool IsPositive(Range* range) { | 
|  | return !Range::IsUnknown(range) && range->IsPositive(); | 
|  | } | 
|  | static bool IsNegative(Range* range) { | 
|  | return !Range::IsUnknown(range) && range->IsNegative(); | 
|  | } | 
|  |  | 
|  | static bool Overlaps(Range* range, intptr_t min, intptr_t max) { | 
|  | return Range::IsUnknown(range) || range->Overlaps(min, max); | 
|  | } | 
|  |  | 
|  | static bool CanBeZero(Range* range) { return Overlaps(range, 0, 0); } | 
|  |  | 
|  | static bool OnlyLessThanOrEqualTo(Range* range, intptr_t value) { | 
|  | return !Range::IsUnknown(range) && range->OnlyLessThanOrEqualTo(value); | 
|  | } | 
|  |  | 
|  | static bool IsSingleton(Range* range) { | 
|  | return !Range::IsUnknown(range) && range->IsSingleton(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Range analysis for integer values. | 
|  | class RangeAnalysis : public ValueObject { | 
|  | public: | 
|  | explicit RangeAnalysis(FlowGraph* flow_graph) | 
|  | : flow_graph_(flow_graph), | 
|  | smi_range_(Range::Full(RangeBoundary::kRangeBoundarySmi)), | 
|  | int64_range_(Range::Full(RangeBoundary::kRangeBoundaryInt64)) {} | 
|  |  | 
|  | // Infer ranges for all values and remove overflow checks from binary smi | 
|  | // operations when proven redundant. | 
|  | void Analyze(); | 
|  |  | 
|  | // Helper that should be used to access ranges of inputs during range | 
|  | // inference. | 
|  | // Returns meaningful results for uses of non-smi/non-int definitions that | 
|  | // have smi/int as a reaching type. | 
|  | const Range* GetSmiRange(Value* value) const; | 
|  | const Range* GetIntRange(Value* value) const; | 
|  |  | 
|  | static bool IsIntegerDefinition(Definition* defn) { | 
|  | return defn->Type()->IsInt(); | 
|  | } | 
|  |  | 
|  | void AssignRangesRecursively(Definition* defn); | 
|  |  | 
|  | private: | 
|  | enum JoinOperator { NONE, WIDEN, NARROW }; | 
|  | static char OpPrefix(JoinOperator op); | 
|  |  | 
|  | // Collect all integer values (smi or int), all 64-bit binary | 
|  | // and shift operations, and all check bounds. | 
|  | void CollectValues(); | 
|  |  | 
|  | // Iterate over smi values and constrain them at branch successors. | 
|  | // Additionally constraint values after CheckSmi instructions. | 
|  | void InsertConstraints(); | 
|  |  | 
|  | // Iterate over uses of the given definition and discover branches that | 
|  | // constrain it. Insert appropriate Constraint instructions at true | 
|  | // and false successor and rename all dominated uses to refer to a | 
|  | // Constraint instead of this definition. | 
|  | void InsertConstraintsFor(Definition* defn); | 
|  |  | 
|  | // Create a constraint for defn, insert it after given instruction and | 
|  | // rename all uses that are dominated by it. | 
|  | ConstraintInstr* InsertConstraintFor(Value* use, | 
|  | Definition* defn, | 
|  | Range* constraint, | 
|  | Instruction* after); | 
|  |  | 
|  | bool ConstrainValueAfterBranch(Value* use, Definition* defn); | 
|  | void ConstrainValueAfterCheckBound(Value* use, | 
|  | CheckBoundBaseInstr* check, | 
|  | Definition* defn); | 
|  |  | 
|  | // Infer ranges for integer (smi or mint) definitions. | 
|  | void InferRanges(); | 
|  |  | 
|  | // Collect integer definition in the reverse postorder. | 
|  | void CollectDefinitions(BitVector* set); | 
|  |  | 
|  | // Recompute ranges of all definitions until they stop changing. | 
|  | // Apply the given JoinOperator when computing Phi ranges. | 
|  | void Iterate(JoinOperator op, intptr_t max_iterations); | 
|  | bool InferRange(JoinOperator op, Definition* defn, intptr_t iteration); | 
|  |  | 
|  | // Based on computed ranges find and eliminate redundant CheckArrayBound | 
|  | // instructions. | 
|  | void EliminateRedundantBoundsChecks(); | 
|  |  | 
|  | // Find unsatisfiable constraints and mark corresponding blocks unreachable. | 
|  | void MarkUnreachableBlocks(); | 
|  |  | 
|  | // Convert mint operations that stay within int32 range into Int32 operations. | 
|  | void NarrowMintToInt32(); | 
|  |  | 
|  | // Remove artificial Constraint instructions and replace them with actual | 
|  | // unconstrained definitions. | 
|  | void RemoveConstraints(); | 
|  |  | 
|  | Range* ConstraintSmiRange(Token::Kind op, Definition* boundary); | 
|  |  | 
|  | Zone* zone() const { return flow_graph_->zone(); } | 
|  |  | 
|  | FlowGraph* flow_graph_; | 
|  |  | 
|  | // Range object representing full Smi range. | 
|  | Range smi_range_; | 
|  |  | 
|  | Range int64_range_; | 
|  |  | 
|  | // All values that are known to be smi or mint. | 
|  | GrowableArray<Definition*> values_; | 
|  |  | 
|  | // All 64-bit binary and shift operations. | 
|  | GrowableArray<BinaryInt64OpInstr*> binary_int64_ops_; | 
|  | GrowableArray<ShiftIntegerOpInstr*> shift_int64_ops_; | 
|  |  | 
|  | // All CheckArrayBound/GenericCheckBound instructions. | 
|  | GrowableArray<CheckBoundBaseInstr*> bounds_checks_; | 
|  |  | 
|  | // All Constraints inserted during InsertConstraints phase. They are treated | 
|  | // as smi values. | 
|  | GrowableArray<ConstraintInstr*> constraints_; | 
|  |  | 
|  | // List of integer (smi or mint) definitions including constraints sorted | 
|  | // in the reverse postorder. | 
|  | GrowableArray<Definition*> definitions_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(RangeAnalysis); | 
|  | }; | 
|  |  | 
|  | // Replaces Mint IL instructions with Uint32 IL instructions | 
|  | // when possible. Uses output of RangeAnalysis. | 
|  | class IntegerInstructionSelector : public ValueObject { | 
|  | public: | 
|  | explicit IntegerInstructionSelector(FlowGraph* flow_graph); | 
|  |  | 
|  | void Select(); | 
|  |  | 
|  | private: | 
|  | bool IsPotentialUint32Definition(Definition* def); | 
|  | void FindPotentialUint32Definitions(); | 
|  | bool IsUint32NarrowingDefinition(Definition* def); | 
|  | void FindUint32NarrowingDefinitions(); | 
|  | bool AllUsesAreUint32Narrowing(Value* list_head); | 
|  | bool CanBecomeUint32(Definition* def); | 
|  | void Propagate(); | 
|  | Definition* ConstructReplacementFor(Definition* def); | 
|  | void ReplaceInstructions(); | 
|  |  | 
|  | Zone* zone() const { return zone_; } | 
|  |  | 
|  | GrowableArray<Definition*> potential_uint32_defs_; | 
|  | BitVector* selected_uint32_defs_; | 
|  |  | 
|  | FlowGraph* flow_graph_; | 
|  | Zone* zone_; | 
|  | }; | 
|  |  | 
|  | }  // namespace dart | 
|  |  | 
|  | #endif  // RUNTIME_VM_COMPILER_BACKEND_RANGE_ANALYSIS_H_ |