OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 #ifndef VM_FLOW_GRAPH_RANGE_ANALYSIS_H_ |
| 6 #define VM_FLOW_GRAPH_RANGE_ANALYSIS_H_ |
| 7 |
| 8 #include "vm/flow_graph.h" |
| 9 #include "vm/intermediate_language.h" |
| 10 |
| 11 namespace dart { |
| 12 |
| 13 class RangeBoundary : public ValueObject { |
| 14 public: |
| 15 enum Kind { |
| 16 kUnknown, |
| 17 kNegativeInfinity, |
| 18 kPositiveInfinity, |
| 19 kSymbol, |
| 20 kConstant, |
| 21 }; |
| 22 |
| 23 enum RangeSize { |
| 24 kRangeBoundarySmi, |
| 25 kRangeBoundaryInt64, |
| 26 }; |
| 27 |
| 28 RangeBoundary() : kind_(kUnknown), value_(0), offset_(0) { } |
| 29 |
| 30 RangeBoundary(const RangeBoundary& other) |
| 31 : ValueObject(), |
| 32 kind_(other.kind_), |
| 33 value_(other.value_), |
| 34 offset_(other.offset_) { } |
| 35 |
| 36 explicit RangeBoundary(int64_t val) |
| 37 : kind_(kConstant), value_(val), offset_(0) { } |
| 38 |
| 39 RangeBoundary& operator=(const RangeBoundary& other) { |
| 40 kind_ = other.kind_; |
| 41 value_ = other.value_; |
| 42 offset_ = other.offset_; |
| 43 return *this; |
| 44 } |
| 45 |
| 46 static const int64_t kMin = kMinInt64; |
| 47 static const int64_t kMax = kMaxInt64; |
| 48 |
| 49 // Construct a RangeBoundary for a constant value. |
| 50 static RangeBoundary FromConstant(int64_t val) { |
| 51 return RangeBoundary(val); |
| 52 } |
| 53 |
| 54 // Construct a RangeBoundary for -inf. |
| 55 static RangeBoundary NegativeInfinity() { |
| 56 return RangeBoundary(kNegativeInfinity, 0, 0); |
| 57 } |
| 58 |
| 59 // Construct a RangeBoundary for +inf. |
| 60 static RangeBoundary PositiveInfinity() { |
| 61 return RangeBoundary(kPositiveInfinity, 0, 0); |
| 62 } |
| 63 |
| 64 // Construct a RangeBoundary from a definition and offset. |
| 65 static RangeBoundary FromDefinition(Definition* defn, int64_t offs = 0); |
| 66 |
| 67 // Construct a RangeBoundary for the constant MinSmi value. |
| 68 static RangeBoundary MinSmi() { |
| 69 return FromConstant(Smi::kMinValue); |
| 70 } |
| 71 |
| 72 // Construct a RangeBoundary for the constant MaxSmi value. |
| 73 static RangeBoundary MaxSmi() { |
| 74 return FromConstant(Smi::kMaxValue); |
| 75 } |
| 76 |
| 77 // Construct a RangeBoundary for the constant kMin value. |
| 78 static RangeBoundary MinConstant() { |
| 79 return FromConstant(kMin); |
| 80 } |
| 81 |
| 82 // Construct a RangeBoundary for the constant kMax value. |
| 83 static RangeBoundary MaxConstant() { |
| 84 return FromConstant(kMax); |
| 85 } |
| 86 |
| 87 // Calculate the minimum of a and b within the given range. |
| 88 static RangeBoundary Min(RangeBoundary a, RangeBoundary b, RangeSize size); |
| 89 static RangeBoundary Max(RangeBoundary a, RangeBoundary b, RangeSize size); |
| 90 |
| 91 // Returns true when this is a constant that is outside of Smi range. |
| 92 bool OverflowedSmi() const { |
| 93 return (IsConstant() && !Smi::IsValid(ConstantValue())) || IsInfinity(); |
| 94 } |
| 95 |
| 96 // Returns true if this outside mint range. |
| 97 bool OverflowedMint() const { |
| 98 return IsInfinity(); |
| 99 } |
| 100 |
| 101 // -/+ infinity are clamped to MinConstant/MaxConstant of the given type. |
| 102 RangeBoundary Clamp(RangeSize size) const { |
| 103 if (IsNegativeInfinity()) { |
| 104 return (size == kRangeBoundaryInt64) ? MinConstant() : MinSmi(); |
| 105 } |
| 106 if (IsPositiveInfinity()) { |
| 107 return (size == kRangeBoundaryInt64) ? MaxConstant() : MaxSmi(); |
| 108 } |
| 109 if ((size == kRangeBoundarySmi) && IsConstant()) { |
| 110 if (ConstantValue() <= Smi::kMinValue) { |
| 111 return MinSmi(); |
| 112 } |
| 113 if (ConstantValue() >= Smi::kMaxValue) { |
| 114 return MaxSmi(); |
| 115 } |
| 116 } |
| 117 // If this range is a symbolic range, we do not clamp it. |
| 118 // This could lead to some imprecision later on. |
| 119 return *this; |
| 120 } |
| 121 |
| 122 |
| 123 bool IsSmiMinimumOrBelow() const { |
| 124 return IsNegativeInfinity() || |
| 125 (IsConstant() && (ConstantValue() <= Smi::kMinValue)); |
| 126 } |
| 127 |
| 128 bool IsSmiMaximumOrAbove() const { |
| 129 return IsPositiveInfinity() || |
| 130 (IsConstant() && (ConstantValue() >= Smi::kMaxValue)); |
| 131 } |
| 132 |
| 133 bool IsMinimumOrBelow() const { |
| 134 return IsNegativeInfinity() || (IsConstant() && (ConstantValue() == kMin)); |
| 135 } |
| 136 |
| 137 bool IsMaximumOrAbove() const { |
| 138 return IsPositiveInfinity() || (IsConstant() && (ConstantValue() == kMax)); |
| 139 } |
| 140 |
| 141 intptr_t kind() const { |
| 142 return kind_; |
| 143 } |
| 144 |
| 145 // Kind tests. |
| 146 bool IsUnknown() const { return kind_ == kUnknown; } |
| 147 bool IsConstant() const { return kind_ == kConstant; } |
| 148 bool IsSymbol() const { return kind_ == kSymbol; } |
| 149 bool IsNegativeInfinity() const { return kind_ == kNegativeInfinity; } |
| 150 bool IsPositiveInfinity() const { return kind_ == kPositiveInfinity; } |
| 151 bool IsInfinity() const { |
| 152 return IsNegativeInfinity() || IsPositiveInfinity(); |
| 153 } |
| 154 bool IsConstantOrInfinity() const { |
| 155 return IsConstant() || IsInfinity(); |
| 156 } |
| 157 |
| 158 // Returns the value of a kConstant RangeBoundary. |
| 159 int64_t ConstantValue() const; |
| 160 |
| 161 // Returns the Definition associated with a kSymbol RangeBoundary. |
| 162 Definition* symbol() const { |
| 163 ASSERT(IsSymbol()); |
| 164 return reinterpret_cast<Definition*>(value_); |
| 165 } |
| 166 |
| 167 // Offset from symbol. |
| 168 int64_t offset() const { |
| 169 return offset_; |
| 170 } |
| 171 |
| 172 // Computes the LowerBound of this. Three cases: |
| 173 // IsInfinity() -> NegativeInfinity(). |
| 174 // IsConstant() -> value(). |
| 175 // IsSymbol() -> lower bound computed from definition + offset. |
| 176 RangeBoundary LowerBound() const; |
| 177 |
| 178 // Computes the UpperBound of this. Three cases: |
| 179 // IsInfinity() -> PositiveInfinity(). |
| 180 // IsConstant() -> value(). |
| 181 // IsSymbol() -> upper bound computed from definition + offset. |
| 182 RangeBoundary UpperBound() const; |
| 183 |
| 184 void PrintTo(BufferFormatter* f) const; |
| 185 const char* ToCString() const; |
| 186 |
| 187 static RangeBoundary Add(const RangeBoundary& a, |
| 188 const RangeBoundary& b, |
| 189 const RangeBoundary& overflow); |
| 190 |
| 191 static RangeBoundary Sub(const RangeBoundary& a, |
| 192 const RangeBoundary& b, |
| 193 const RangeBoundary& overflow); |
| 194 |
| 195 static RangeBoundary Shl(const RangeBoundary& value_boundary, |
| 196 int64_t shift_count, |
| 197 const RangeBoundary& overflow); |
| 198 |
| 199 static RangeBoundary Shr(const RangeBoundary& value_boundary, |
| 200 int64_t shift_count) { |
| 201 ASSERT(value_boundary.IsConstant()); |
| 202 ASSERT(shift_count >= 0); |
| 203 int64_t value = static_cast<int64_t>(value_boundary.ConstantValue()); |
| 204 int64_t result = value >> shift_count; |
| 205 return RangeBoundary(result); |
| 206 } |
| 207 |
| 208 // Attempts to calculate a + b when: |
| 209 // a is a symbol and b is a constant OR |
| 210 // a is a constant and b is a symbol |
| 211 // returns true if it succeeds, output is in result. |
| 212 static bool SymbolicAdd(const RangeBoundary& a, |
| 213 const RangeBoundary& b, |
| 214 RangeBoundary* result); |
| 215 |
| 216 // Attempts to calculate a - b when: |
| 217 // a is a symbol and b is a constant |
| 218 // returns true if it succeeds, output is in result. |
| 219 static bool SymbolicSub(const RangeBoundary& a, |
| 220 const RangeBoundary& b, |
| 221 RangeBoundary* result); |
| 222 |
| 223 bool Equals(const RangeBoundary& other) const; |
| 224 |
| 225 private: |
| 226 RangeBoundary(Kind kind, int64_t value, int64_t offset) |
| 227 : kind_(kind), value_(value), offset_(offset) { } |
| 228 |
| 229 Kind kind_; |
| 230 int64_t value_; |
| 231 int64_t offset_; |
| 232 }; |
| 233 |
| 234 |
| 235 class Range : public ZoneAllocated { |
| 236 public: |
| 237 Range(RangeBoundary min, RangeBoundary max) : min_(min), max_(max) { } |
| 238 |
| 239 static Range* Unknown() { |
| 240 return new Range(RangeBoundary::MinConstant(), |
| 241 RangeBoundary::MaxConstant()); |
| 242 } |
| 243 |
| 244 static Range* UnknownSmi() { |
| 245 return new Range(RangeBoundary::MinSmi(), |
| 246 RangeBoundary::MaxSmi()); |
| 247 } |
| 248 |
| 249 void PrintTo(BufferFormatter* f) const; |
| 250 static const char* ToCString(const Range* range); |
| 251 |
| 252 const RangeBoundary& min() const { return min_; } |
| 253 const RangeBoundary& max() const { return max_; } |
| 254 |
| 255 static RangeBoundary ConstantMinSmi(const Range* range) { |
| 256 if (range == NULL) { |
| 257 return RangeBoundary::MinSmi(); |
| 258 } |
| 259 return range->min().LowerBound().Clamp(RangeBoundary::kRangeBoundarySmi); |
| 260 } |
| 261 |
| 262 static RangeBoundary ConstantMaxSmi(const Range* range) { |
| 263 if (range == NULL) { |
| 264 return RangeBoundary::MaxSmi(); |
| 265 } |
| 266 return range->max().UpperBound().Clamp(RangeBoundary::kRangeBoundarySmi); |
| 267 } |
| 268 |
| 269 static RangeBoundary ConstantMin(const Range* range) { |
| 270 if (range == NULL) { |
| 271 return RangeBoundary::MinConstant(); |
| 272 } |
| 273 return range->min().LowerBound().Clamp(RangeBoundary::kRangeBoundaryInt64); |
| 274 } |
| 275 |
| 276 static RangeBoundary ConstantMax(const Range* range) { |
| 277 if (range == NULL) { |
| 278 return RangeBoundary::MaxConstant(); |
| 279 } |
| 280 return range->max().UpperBound().Clamp(RangeBoundary::kRangeBoundaryInt64); |
| 281 } |
| 282 |
| 283 // [0, +inf] |
| 284 bool IsPositive() const; |
| 285 |
| 286 // [-inf, val]. |
| 287 bool OnlyLessThanOrEqualTo(int64_t val) const; |
| 288 |
| 289 // [val, +inf]. |
| 290 bool OnlyGreaterThanOrEqualTo(int64_t val) const; |
| 291 |
| 292 // Inclusive. |
| 293 bool IsWithin(int64_t min_int, int64_t max_int) const; |
| 294 |
| 295 // Inclusive. |
| 296 bool Overlaps(int64_t min_int, int64_t max_int) const; |
| 297 |
| 298 bool IsUnsatisfiable() const; |
| 299 |
| 300 bool IsFinite() const { |
| 301 return !min_.IsInfinity() && !max_.IsInfinity(); |
| 302 } |
| 303 |
| 304 // Clamp this to be within size. |
| 305 void Clamp(RangeBoundary::RangeSize size); |
| 306 |
| 307 static void Add(const Range* left_range, |
| 308 const Range* right_range, |
| 309 RangeBoundary* min, |
| 310 RangeBoundary* max, |
| 311 Definition* left_defn); |
| 312 |
| 313 static void Sub(const Range* left_range, |
| 314 const Range* right_range, |
| 315 RangeBoundary* min, |
| 316 RangeBoundary* max, |
| 317 Definition* left_defn); |
| 318 |
| 319 static bool Mul(const Range* left_range, |
| 320 const Range* right_range, |
| 321 RangeBoundary* min, |
| 322 RangeBoundary* max); |
| 323 static void Shr(const Range* left_range, |
| 324 const Range* right_range, |
| 325 RangeBoundary* min, |
| 326 RangeBoundary* max); |
| 327 |
| 328 static void Shl(const Range* left_range, |
| 329 const Range* right_range, |
| 330 RangeBoundary* min, |
| 331 RangeBoundary* max); |
| 332 |
| 333 static bool And(const Range* left_range, |
| 334 const Range* right_range, |
| 335 RangeBoundary* min, |
| 336 RangeBoundary* max); |
| 337 |
| 338 |
| 339 // Both the a and b ranges are >= 0. |
| 340 static bool OnlyPositiveOrZero(const Range& a, const Range& b); |
| 341 |
| 342 // Both the a and b ranges are <= 0. |
| 343 static bool OnlyNegativeOrZero(const Range& a, const Range& b); |
| 344 |
| 345 // Return the maximum absolute value included in range. |
| 346 static int64_t ConstantAbsMax(const Range* range); |
| 347 |
| 348 static Range* BinaryOp(const Token::Kind op, |
| 349 const Range* left_range, |
| 350 const Range* right_range, |
| 351 Definition* left_defn); |
| 352 |
| 353 private: |
| 354 RangeBoundary min_; |
| 355 RangeBoundary max_; |
| 356 }; |
| 357 |
| 358 |
| 359 // Range analysis for integer values. |
| 360 class RangeAnalysis : public ValueObject { |
| 361 public: |
| 362 explicit RangeAnalysis(FlowGraph* flow_graph) |
| 363 : flow_graph_(flow_graph), |
| 364 marked_defns_(NULL) { } |
| 365 |
| 366 // Infer ranges for all values and remove overflow checks from binary smi |
| 367 // operations when proven redundant. |
| 368 void Analyze(); |
| 369 |
| 370 private: |
| 371 // Collect all values that were proven to be smi in smi_values_ array and all |
| 372 // CheckSmi instructions in smi_check_ array. |
| 373 void CollectValues(); |
| 374 |
| 375 // Iterate over smi values and constrain them at branch successors. |
| 376 // Additionally constraint values after CheckSmi instructions. |
| 377 void InsertConstraints(); |
| 378 |
| 379 // Iterate over uses of the given definition and discover branches that |
| 380 // constrain it. Insert appropriate Constraint instructions at true |
| 381 // and false successor and rename all dominated uses to refer to a |
| 382 // Constraint instead of this definition. |
| 383 void InsertConstraintsFor(Definition* defn); |
| 384 |
| 385 // Create a constraint for defn, insert it after given instruction and |
| 386 // rename all uses that are dominated by it. |
| 387 ConstraintInstr* InsertConstraintFor(Definition* defn, |
| 388 Range* constraint, |
| 389 Instruction* after); |
| 390 |
| 391 void ConstrainValueAfterBranch(Definition* defn, Value* use); |
| 392 void ConstrainValueAfterCheckArrayBound(Definition* defn, |
| 393 CheckArrayBoundInstr* check, |
| 394 intptr_t use_index); |
| 395 |
| 396 // Replace uses of the definition def that are dominated by instruction dom |
| 397 // with uses of other definition. |
| 398 void RenameDominatedUses(Definition* def, |
| 399 Instruction* dom, |
| 400 Definition* other); |
| 401 |
| 402 |
| 403 // Walk the dominator tree and infer ranges for smi values. |
| 404 void InferRanges(); |
| 405 void InferRangesRecursive(BlockEntryInstr* block); |
| 406 |
| 407 enum Direction { |
| 408 kUnknown, |
| 409 kPositive, |
| 410 kNegative, |
| 411 kBoth |
| 412 }; |
| 413 |
| 414 Range* InferInductionVariableRange(JoinEntryInstr* loop_header, |
| 415 PhiInstr* var); |
| 416 |
| 417 void ResetWorklist(); |
| 418 void MarkDefinition(Definition* defn); |
| 419 |
| 420 static Direction ToDirection(Value* val); |
| 421 |
| 422 static Direction Invert(Direction direction) { |
| 423 return (direction == kPositive) ? kNegative : kPositive; |
| 424 } |
| 425 |
| 426 static void UpdateDirection(Direction* direction, |
| 427 Direction new_direction) { |
| 428 if (*direction != new_direction) { |
| 429 if (*direction != kUnknown) new_direction = kBoth; |
| 430 *direction = new_direction; |
| 431 } |
| 432 } |
| 433 |
| 434 // Remove artificial Constraint instructions and replace them with actual |
| 435 // unconstrained definitions. |
| 436 void RemoveConstraints(); |
| 437 |
| 438 Range* ConstraintRange(Token::Kind op, Definition* boundary); |
| 439 |
| 440 Isolate* isolate() const { return flow_graph_->isolate(); } |
| 441 |
| 442 FlowGraph* flow_graph_; |
| 443 |
| 444 // Value that are known to be smi or mint. |
| 445 GrowableArray<Definition*> values_; |
| 446 // All CheckSmi instructions. |
| 447 GrowableArray<CheckSmiInstr*> smi_checks_; |
| 448 |
| 449 // All Constraints inserted during InsertConstraints phase. They are treated |
| 450 // as smi values. |
| 451 GrowableArray<ConstraintInstr*> constraints_; |
| 452 |
| 453 // Bitvector for a quick filtering of known smi or mint values. |
| 454 BitVector* definitions_; |
| 455 |
| 456 // Worklist for induction variables analysis. |
| 457 GrowableArray<Definition*> worklist_; |
| 458 BitVector* marked_defns_; |
| 459 |
| 460 DISALLOW_COPY_AND_ASSIGN(RangeAnalysis); |
| 461 }; |
| 462 |
| 463 |
| 464 // Replaces Mint IL instructions with Uint32 IL instructions |
| 465 // when possible. Uses output of RangeAnalysis. |
| 466 class IntegerInstructionSelector : public ValueObject { |
| 467 public: |
| 468 explicit IntegerInstructionSelector(FlowGraph* flow_graph); |
| 469 |
| 470 void Select(); |
| 471 |
| 472 private: |
| 473 bool IsPotentialUint32Definition(Definition* def); |
| 474 void FindPotentialUint32Definitions(); |
| 475 bool IsUint32NarrowingDefinition(Definition* def); |
| 476 void FindUint32NarrowingDefinitions(); |
| 477 bool AllUsesAreUint32Narrowing(Value* list_head); |
| 478 bool CanBecomeUint32(Definition* def); |
| 479 void Propagate(); |
| 480 Definition* ConstructReplacementFor(Definition* def); |
| 481 void ReplaceInstructions(); |
| 482 |
| 483 Isolate* isolate() const { return isolate_; } |
| 484 |
| 485 GrowableArray<Definition*> potential_uint32_defs_; |
| 486 BitVector* selected_uint32_defs_; |
| 487 |
| 488 FlowGraph* flow_graph_; |
| 489 Isolate* isolate_; |
| 490 }; |
| 491 |
| 492 |
| 493 } // namespace dart |
| 494 |
| 495 #endif // VM_FLOW_GRAPH_RANGE_ANALYSIS_H_ |
OLD | NEW |