Index: runtime/vm/flow_graph_range_analysis.h |
diff --git a/runtime/vm/flow_graph_range_analysis.h b/runtime/vm/flow_graph_range_analysis.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f9ae03664a1851ce62094763615ae290a39b243f |
--- /dev/null |
+++ b/runtime/vm/flow_graph_range_analysis.h |
@@ -0,0 +1,495 @@ |
+// 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 VM_FLOW_GRAPH_RANGE_ANALYSIS_H_ |
+#define VM_FLOW_GRAPH_RANGE_ANALYSIS_H_ |
+ |
+#include "vm/flow_graph.h" |
+#include "vm/intermediate_language.h" |
+ |
+namespace dart { |
+ |
+class RangeBoundary : public ValueObject { |
+ public: |
+ enum Kind { |
+ kUnknown, |
+ kNegativeInfinity, |
+ kPositiveInfinity, |
+ kSymbol, |
+ kConstant, |
+ }; |
+ |
+ enum RangeSize { |
+ kRangeBoundarySmi, |
+ 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 const int64_t kMin = kMinInt64; |
+ static const int64_t kMax = kMaxInt64; |
+ |
+ // Construct a RangeBoundary for a constant value. |
+ static RangeBoundary FromConstant(int64_t val) { |
+ return RangeBoundary(val); |
+ } |
+ |
+ // Construct a RangeBoundary for -inf. |
+ static RangeBoundary NegativeInfinity() { |
+ return RangeBoundary(kNegativeInfinity, 0, 0); |
+ } |
+ |
+ // Construct a RangeBoundary for +inf. |
+ static RangeBoundary PositiveInfinity() { |
+ return RangeBoundary(kPositiveInfinity, 0, 0); |
+ } |
+ |
+ // Construct a RangeBoundary from a definition and offset. |
+ static RangeBoundary FromDefinition(Definition* defn, int64_t offs = 0); |
+ |
+ // Construct a RangeBoundary for the constant MinSmi value. |
+ static RangeBoundary MinSmi() { |
+ return FromConstant(Smi::kMinValue); |
+ } |
+ |
+ // Construct a RangeBoundary for the constant MaxSmi value. |
+ static RangeBoundary MaxSmi() { |
+ return FromConstant(Smi::kMaxValue); |
+ } |
+ |
+ // Construct a RangeBoundary for the constant kMin value. |
+ static RangeBoundary MinConstant() { |
+ return FromConstant(kMin); |
+ } |
+ |
+ // Construct a RangeBoundary for the constant kMax value. |
+ static RangeBoundary MaxConstant() { |
+ return FromConstant(kMax); |
+ } |
+ |
+ // Calculate the minimum of a and b within the given range. |
+ static RangeBoundary Min(RangeBoundary a, RangeBoundary b, RangeSize size); |
+ static RangeBoundary Max(RangeBoundary a, RangeBoundary b, RangeSize size); |
+ |
+ // Returns true when this is a constant that is outside of Smi range. |
+ bool OverflowedSmi() const { |
+ return (IsConstant() && !Smi::IsValid(ConstantValue())) || IsInfinity(); |
+ } |
+ |
+ // Returns true if this outside mint range. |
+ bool OverflowedMint() const { |
+ return IsInfinity(); |
+ } |
+ |
+ // -/+ infinity are clamped to MinConstant/MaxConstant of the given type. |
+ RangeBoundary Clamp(RangeSize size) const { |
+ if (IsNegativeInfinity()) { |
+ return (size == kRangeBoundaryInt64) ? MinConstant() : MinSmi(); |
+ } |
+ if (IsPositiveInfinity()) { |
+ return (size == kRangeBoundaryInt64) ? MaxConstant() : MaxSmi(); |
+ } |
+ if ((size == kRangeBoundarySmi) && IsConstant()) { |
+ if (ConstantValue() <= Smi::kMinValue) { |
+ return MinSmi(); |
+ } |
+ if (ConstantValue() >= Smi::kMaxValue) { |
+ return MaxSmi(); |
+ } |
+ } |
+ // If this range is a symbolic range, we do not clamp it. |
+ // This could lead to some imprecision later on. |
+ return *this; |
+ } |
+ |
+ |
+ bool IsSmiMinimumOrBelow() const { |
+ return IsNegativeInfinity() || |
+ (IsConstant() && (ConstantValue() <= Smi::kMinValue)); |
+ } |
+ |
+ bool IsSmiMaximumOrAbove() const { |
+ return IsPositiveInfinity() || |
+ (IsConstant() && (ConstantValue() >= Smi::kMaxValue)); |
+ } |
+ |
+ bool IsMinimumOrBelow() const { |
+ return IsNegativeInfinity() || (IsConstant() && (ConstantValue() == kMin)); |
+ } |
+ |
+ bool IsMaximumOrAbove() const { |
+ return IsPositiveInfinity() || (IsConstant() && (ConstantValue() == kMax)); |
+ } |
+ |
+ 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; } |
+ bool IsNegativeInfinity() const { return kind_ == kNegativeInfinity; } |
+ bool IsPositiveInfinity() const { return kind_ == kPositiveInfinity; } |
+ bool IsInfinity() const { |
+ return IsNegativeInfinity() || IsPositiveInfinity(); |
+ } |
+ bool IsConstantOrInfinity() const { |
+ return IsConstant() || IsInfinity(); |
+ } |
+ |
+ // 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: |
+ // IsInfinity() -> NegativeInfinity(). |
+ // IsConstant() -> value(). |
+ // IsSymbol() -> lower bound computed from definition + offset. |
+ RangeBoundary LowerBound() const; |
+ |
+ // Computes the UpperBound of this. Three cases: |
+ // IsInfinity() -> PositiveInfinity(). |
+ // IsConstant() -> value(). |
+ // IsSymbol() -> upper bound computed from definition + offset. |
+ RangeBoundary UpperBound() const; |
+ |
+ void PrintTo(BufferFormatter* f) const; |
+ const char* ToCString() const; |
+ |
+ static RangeBoundary Add(const RangeBoundary& a, |
+ const RangeBoundary& b, |
+ const RangeBoundary& overflow); |
+ |
+ static RangeBoundary Sub(const RangeBoundary& a, |
+ const RangeBoundary& b, |
+ const RangeBoundary& overflow); |
+ |
+ static RangeBoundary Shl(const RangeBoundary& value_boundary, |
+ int64_t shift_count, |
+ const RangeBoundary& overflow); |
+ |
+ static RangeBoundary Shr(const RangeBoundary& value_boundary, |
+ int64_t shift_count) { |
+ ASSERT(value_boundary.IsConstant()); |
+ ASSERT(shift_count >= 0); |
+ int64_t value = static_cast<int64_t>(value_boundary.ConstantValue()); |
+ int64_t result = value >> shift_count; |
+ return RangeBoundary(result); |
+ } |
+ |
+ // 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; |
+ |
+ 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(RangeBoundary min, RangeBoundary max) : min_(min), max_(max) { } |
+ |
+ static Range* Unknown() { |
+ return new Range(RangeBoundary::MinConstant(), |
+ RangeBoundary::MaxConstant()); |
+ } |
+ |
+ static Range* UnknownSmi() { |
+ return new Range(RangeBoundary::MinSmi(), |
+ RangeBoundary::MaxSmi()); |
+ } |
+ |
+ void PrintTo(BufferFormatter* f) const; |
+ static const char* ToCString(const Range* range); |
+ |
+ const RangeBoundary& min() const { return min_; } |
+ const RangeBoundary& max() const { return max_; } |
+ |
+ static RangeBoundary ConstantMinSmi(const Range* range) { |
+ if (range == NULL) { |
+ return RangeBoundary::MinSmi(); |
+ } |
+ return range->min().LowerBound().Clamp(RangeBoundary::kRangeBoundarySmi); |
+ } |
+ |
+ static RangeBoundary ConstantMaxSmi(const Range* range) { |
+ if (range == NULL) { |
+ return RangeBoundary::MaxSmi(); |
+ } |
+ return range->max().UpperBound().Clamp(RangeBoundary::kRangeBoundarySmi); |
+ } |
+ |
+ static RangeBoundary ConstantMin(const Range* range) { |
+ if (range == NULL) { |
+ return RangeBoundary::MinConstant(); |
+ } |
+ return range->min().LowerBound().Clamp(RangeBoundary::kRangeBoundaryInt64); |
+ } |
+ |
+ static RangeBoundary ConstantMax(const Range* range) { |
+ if (range == NULL) { |
+ return RangeBoundary::MaxConstant(); |
+ } |
+ return range->max().UpperBound().Clamp(RangeBoundary::kRangeBoundaryInt64); |
+ } |
+ |
+ // [0, +inf] |
+ bool IsPositive() 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 Overlaps(int64_t min_int, int64_t max_int) const; |
+ |
+ bool IsUnsatisfiable() const; |
+ |
+ bool IsFinite() const { |
+ return !min_.IsInfinity() && !max_.IsInfinity(); |
+ } |
+ |
+ // Clamp this to be within size. |
+ void Clamp(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 bool Mul(const Range* left_range, |
+ 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 Shl(const Range* left_range, |
+ const Range* right_range, |
+ RangeBoundary* min, |
+ RangeBoundary* max); |
+ |
+ static bool And(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); |
+ |
+ static Range* BinaryOp(const Token::Kind op, |
+ const Range* left_range, |
+ const Range* right_range, |
+ Definition* left_defn); |
+ |
+ private: |
+ RangeBoundary min_; |
+ RangeBoundary max_; |
+}; |
+ |
+ |
+// Range analysis for integer values. |
+class RangeAnalysis : public ValueObject { |
+ public: |
+ explicit RangeAnalysis(FlowGraph* flow_graph) |
+ : flow_graph_(flow_graph), |
+ marked_defns_(NULL) { } |
+ |
+ // Infer ranges for all values and remove overflow checks from binary smi |
+ // operations when proven redundant. |
+ void Analyze(); |
+ |
+ private: |
+ // Collect all values that were proven to be smi in smi_values_ array and all |
+ // CheckSmi instructions in smi_check_ array. |
+ 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(Definition* defn, |
+ Range* constraint, |
+ Instruction* after); |
+ |
+ void ConstrainValueAfterBranch(Definition* defn, Value* use); |
+ void ConstrainValueAfterCheckArrayBound(Definition* defn, |
+ CheckArrayBoundInstr* check, |
+ intptr_t use_index); |
+ |
+ // Replace uses of the definition def that are dominated by instruction dom |
+ // with uses of other definition. |
+ void RenameDominatedUses(Definition* def, |
+ Instruction* dom, |
+ Definition* other); |
+ |
+ |
+ // Walk the dominator tree and infer ranges for smi values. |
+ void InferRanges(); |
+ void InferRangesRecursive(BlockEntryInstr* block); |
+ |
+ enum Direction { |
+ kUnknown, |
+ kPositive, |
+ kNegative, |
+ kBoth |
+ }; |
+ |
+ Range* InferInductionVariableRange(JoinEntryInstr* loop_header, |
+ PhiInstr* var); |
+ |
+ void ResetWorklist(); |
+ void MarkDefinition(Definition* defn); |
+ |
+ static Direction ToDirection(Value* val); |
+ |
+ static Direction Invert(Direction direction) { |
+ return (direction == kPositive) ? kNegative : kPositive; |
+ } |
+ |
+ static void UpdateDirection(Direction* direction, |
+ Direction new_direction) { |
+ if (*direction != new_direction) { |
+ if (*direction != kUnknown) new_direction = kBoth; |
+ *direction = new_direction; |
+ } |
+ } |
+ |
+ // Remove artificial Constraint instructions and replace them with actual |
+ // unconstrained definitions. |
+ void RemoveConstraints(); |
+ |
+ Range* ConstraintRange(Token::Kind op, Definition* boundary); |
+ |
+ Isolate* isolate() const { return flow_graph_->isolate(); } |
+ |
+ FlowGraph* flow_graph_; |
+ |
+ // Value that are known to be smi or mint. |
+ GrowableArray<Definition*> values_; |
+ // All CheckSmi instructions. |
+ GrowableArray<CheckSmiInstr*> smi_checks_; |
+ |
+ // All Constraints inserted during InsertConstraints phase. They are treated |
+ // as smi values. |
+ GrowableArray<ConstraintInstr*> constraints_; |
+ |
+ // Bitvector for a quick filtering of known smi or mint values. |
+ BitVector* definitions_; |
+ |
+ // Worklist for induction variables analysis. |
+ GrowableArray<Definition*> worklist_; |
+ BitVector* marked_defns_; |
+ |
+ 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(); |
+ |
+ Isolate* isolate() const { return isolate_; } |
+ |
+ GrowableArray<Definition*> potential_uint32_defs_; |
+ BitVector* selected_uint32_defs_; |
+ |
+ FlowGraph* flow_graph_; |
+ Isolate* isolate_; |
+}; |
+ |
+ |
+} // namespace dart |
+ |
+#endif // VM_FLOW_GRAPH_RANGE_ANALYSIS_H_ |