| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, 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 part of ssa; | |
| 6 | |
| 7 | |
| 8 class ValueRangeInfo { | |
| 9 final ConstantSystem constantSystem; | |
| 10 | |
| 11 IntValue intZero; | |
| 12 IntValue intOne; | |
| 13 | |
| 14 ValueRangeInfo(this.constantSystem) { | |
| 15 intZero = newIntValue(0); | |
| 16 intOne = newIntValue(1); | |
| 17 } | |
| 18 | |
| 19 Value newIntValue(int value) { | |
| 20 return new IntValue(value, this); | |
| 21 } | |
| 22 | |
| 23 Value newInstructionValue(HInstruction instruction) { | |
| 24 return new InstructionValue(instruction, this); | |
| 25 } | |
| 26 | |
| 27 Value newPositiveValue(HInstruction instruction) { | |
| 28 return new PositiveValue(instruction, this); | |
| 29 } | |
| 30 | |
| 31 Value newAddValue(Value left, Value right) { | |
| 32 return new AddValue(left, right, this); | |
| 33 } | |
| 34 | |
| 35 Value newSubtractValue(Value left, Value right) { | |
| 36 return new SubtractValue(left, right, this); | |
| 37 } | |
| 38 | |
| 39 Value newNegateValue(Value value) { | |
| 40 return new NegateValue(value, this); | |
| 41 } | |
| 42 | |
| 43 Range newUnboundRange() { | |
| 44 return new Range.unbound(this); | |
| 45 } | |
| 46 | |
| 47 Range newNormalizedRange(Value low, Value up) { | |
| 48 return new Range.normalize(low, up, this); | |
| 49 } | |
| 50 | |
| 51 Range newMarkerRange() { | |
| 52 return new Range(new MarkerValue(false, this), | |
| 53 new MarkerValue(true, this), | |
| 54 this); | |
| 55 } | |
| 56 } | |
| 57 | |
| 58 /** | |
| 59 * A [Value] represents both symbolic values like the value of a | |
| 60 * parameter, or the length of an array, and concrete values, like | |
| 61 * constants. | |
| 62 */ | |
| 63 abstract class Value { | |
| 64 final ValueRangeInfo info; | |
| 65 const Value(this.info); | |
| 66 | |
| 67 Value operator +(Value other) => const UnknownValue(); | |
| 68 Value operator -(Value other) => const UnknownValue(); | |
| 69 Value operator -() => const UnknownValue(); | |
| 70 Value operator &(Value other) => const UnknownValue(); | |
| 71 | |
| 72 Value min(Value other) { | |
| 73 if (this == other) return this; | |
| 74 if (other == const MinIntValue()) return other; | |
| 75 if (other == const MaxIntValue()) return this; | |
| 76 Value value = this - other; | |
| 77 if (value.isPositive) return other; | |
| 78 if (value.isNegative) return this; | |
| 79 return const UnknownValue(); | |
| 80 } | |
| 81 | |
| 82 Value max(Value other) { | |
| 83 if (this == other) return this; | |
| 84 if (other == const MinIntValue()) return this; | |
| 85 if (other == const MaxIntValue()) return other; | |
| 86 Value value = this - other; | |
| 87 if (value.isPositive) return this; | |
| 88 if (value.isNegative) return other; | |
| 89 return const UnknownValue(); | |
| 90 } | |
| 91 | |
| 92 bool get isNegative => false; | |
| 93 bool get isPositive => false; | |
| 94 bool get isZero => false; | |
| 95 } | |
| 96 | |
| 97 /** | |
| 98 * The [MarkerValue] class is used to recognize ranges of loop | |
| 99 * updates. | |
| 100 */ | |
| 101 class MarkerValue extends Value { | |
| 102 /// If [positive] is true (respectively false), the marker goes | |
| 103 /// to [MaxIntValue] (respectively [MinIntValue]) when being added | |
| 104 /// to a positive (respectively negative) value. | |
| 105 final bool positive; | |
| 106 | |
| 107 const MarkerValue(this.positive, info) : super(info); | |
| 108 | |
| 109 Value operator +(Value other) { | |
| 110 if (other.isPositive && positive) return const MaxIntValue(); | |
| 111 if (other.isNegative && !positive) return const MinIntValue(); | |
| 112 if (other is IntValue) return this; | |
| 113 return const UnknownValue(); | |
| 114 } | |
| 115 | |
| 116 Value operator -(Value other) { | |
| 117 if (other.isPositive && !positive) return const MinIntValue(); | |
| 118 if (other.isNegative && positive) return const MaxIntValue(); | |
| 119 if (other is IntValue) return this; | |
| 120 return const UnknownValue(); | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 /** | |
| 125 * An [IntValue] contains a constant integer value. | |
| 126 */ | |
| 127 class IntValue extends Value { | |
| 128 final int value; | |
| 129 | |
| 130 const IntValue(this.value, info) : super(info); | |
| 131 | |
| 132 Value operator +(other) { | |
| 133 if (other.isZero) return this; | |
| 134 if (other is !IntValue) return other + this; | |
| 135 ConstantSystem constantSystem = info.constantSystem; | |
| 136 var constant = constantSystem.add.fold( | |
| 137 constantSystem.createInt(value), constantSystem.createInt(other.value)); | |
| 138 if (!constant.isInt) return const UnknownValue(); | |
| 139 return info.newIntValue(constant.primitiveValue); | |
| 140 } | |
| 141 | |
| 142 Value operator -(other) { | |
| 143 if (other.isZero) return this; | |
| 144 if (other is !IntValue) return -other + this; | |
| 145 ConstantSystem constantSystem = info.constantSystem; | |
| 146 var constant = constantSystem.subtract.fold( | |
| 147 constantSystem.createInt(value), constantSystem.createInt(other.value)); | |
| 148 if (!constant.isInt) return const UnknownValue(); | |
| 149 return info.newIntValue(constant.primitiveValue); | |
| 150 } | |
| 151 | |
| 152 Value operator -() { | |
| 153 if (isZero) return this; | |
| 154 ConstantSystem constantSystem = info.constantSystem; | |
| 155 var constant = constantSystem.negate.fold( | |
| 156 constantSystem.createInt(value)); | |
| 157 if (!constant.isInt) return const UnknownValue(); | |
| 158 return info.newIntValue(constant.primitiveValue); | |
| 159 } | |
| 160 | |
| 161 Value operator &(other) { | |
| 162 if (other is !IntValue) return const UnknownValue(); | |
| 163 ConstantSystem constantSystem = info.constantSystem; | |
| 164 var constant = constantSystem.bitAnd.fold( | |
| 165 constantSystem.createInt(value), constantSystem.createInt(other.value)); | |
| 166 return info.newIntValue(constant.primitiveValue); | |
| 167 } | |
| 168 | |
| 169 Value min(other) { | |
| 170 if (other is !IntValue) return other.min(this); | |
| 171 return this.value < other.value ? this : other; | |
| 172 } | |
| 173 | |
| 174 Value max(other) { | |
| 175 if (other is !IntValue) return other.max(this); | |
| 176 return this.value < other.value ? other : this; | |
| 177 } | |
| 178 | |
| 179 bool operator ==(other) { | |
| 180 if (other is !IntValue) return false; | |
| 181 return this.value == other.value; | |
| 182 } | |
| 183 | |
| 184 int get hashCode => throw new UnsupportedError('IntValue.hashCode'); | |
| 185 | |
| 186 String toString() => 'IntValue $value'; | |
| 187 bool get isNegative => value < 0; | |
| 188 bool get isPositive => value >= 0; | |
| 189 bool get isZero => value == 0; | |
| 190 } | |
| 191 | |
| 192 /** | |
| 193 * The [MaxIntValue] represents the maximum value an integer can have, | |
| 194 * which is currently +infinity. | |
| 195 */ | |
| 196 class MaxIntValue extends Value { | |
| 197 const MaxIntValue() : super(null); | |
| 198 Value operator +(Value other) => this; | |
| 199 Value operator -(Value other) => this; | |
| 200 Value operator -() => const MinIntValue(); | |
| 201 Value min(Value other) => other; | |
| 202 Value max(Value other) => this; | |
| 203 String toString() => 'Max'; | |
| 204 bool get isNegative => false; | |
| 205 bool get isPositive => true; | |
| 206 } | |
| 207 | |
| 208 /** | |
| 209 * The [MinIntValue] represents the minimum value an integer can have, | |
| 210 * which is currently -infinity. | |
| 211 */ | |
| 212 class MinIntValue extends Value { | |
| 213 const MinIntValue() : super(null); | |
| 214 Value operator +(Value other) => this; | |
| 215 Value operator -(Value other) => this; | |
| 216 Value operator -() => const MaxIntValue(); | |
| 217 Value min(Value other) => this; | |
| 218 Value max(Value other) => other; | |
| 219 String toString() => 'Min'; | |
| 220 bool get isNegative => true; | |
| 221 bool get isPositive => false; | |
| 222 } | |
| 223 | |
| 224 /** | |
| 225 * The [UnknownValue] is the sentinel in our analysis to mark an | |
| 226 * operation that could not be done because of too much complexity. | |
| 227 */ | |
| 228 class UnknownValue extends Value { | |
| 229 const UnknownValue() : super(null); | |
| 230 Value operator +(Value other) => const UnknownValue(); | |
| 231 Value operator -(Value other) => const UnknownValue(); | |
| 232 Value operator -() => const UnknownValue(); | |
| 233 Value min(Value other) => const UnknownValue(); | |
| 234 Value max(Value other) => const UnknownValue(); | |
| 235 bool get isNegative => false; | |
| 236 bool get isPositive => false; | |
| 237 String toString() => 'Unknown'; | |
| 238 } | |
| 239 | |
| 240 /** | |
| 241 * A symbolic value representing an [HInstruction]. | |
| 242 */ | |
| 243 class InstructionValue extends Value { | |
| 244 final HInstruction instruction; | |
| 245 InstructionValue(this.instruction, info) : super(info); | |
| 246 | |
| 247 bool operator ==(other) { | |
| 248 if (other is !InstructionValue) return false; | |
| 249 return this.instruction == other.instruction; | |
| 250 } | |
| 251 | |
| 252 int get hashCode => throw new UnsupportedError('InstructionValue.hashCode'); | |
| 253 | |
| 254 Value operator +(Value other) { | |
| 255 if (other.isZero) return this; | |
| 256 if (other is IntValue) { | |
| 257 if (other.isNegative) { | |
| 258 return info.newSubtractValue(this, -other); | |
| 259 } | |
| 260 return info.newAddValue(this, other); | |
| 261 } | |
| 262 if (other is InstructionValue) { | |
| 263 return info.newAddValue(this, other); | |
| 264 } | |
| 265 return other + this; | |
| 266 } | |
| 267 | |
| 268 Value operator -(Value other) { | |
| 269 if (other.isZero) return this; | |
| 270 if (this == other) return info.intZero; | |
| 271 if (other is IntValue) { | |
| 272 if (other.isNegative) { | |
| 273 return info.newAddValue(this, -other); | |
| 274 } | |
| 275 return info.newSubtractValue(this, other); | |
| 276 } | |
| 277 if (other is InstructionValue) { | |
| 278 return info.newSubtractValue(this, other); | |
| 279 } | |
| 280 return -other + this; | |
| 281 } | |
| 282 | |
| 283 Value operator -() { | |
| 284 return info.newNegateValue(this); | |
| 285 } | |
| 286 | |
| 287 bool get isNegative => false; | |
| 288 bool get isPositive => false; | |
| 289 | |
| 290 String toString() => 'Instruction: $instruction'; | |
| 291 } | |
| 292 | |
| 293 /** | |
| 294 * Special value for instructions whose type is a positive integer. | |
| 295 */ | |
| 296 class PositiveValue extends InstructionValue { | |
| 297 PositiveValue(HInstruction instruction, info) : super(instruction, info); | |
| 298 bool get isPositive => true; | |
| 299 } | |
| 300 | |
| 301 /** | |
| 302 * Represents a binary operation on two [Value], where the operation | |
| 303 * did not yield a canonical value. | |
| 304 */ | |
| 305 class BinaryOperationValue extends Value { | |
| 306 final Value left; | |
| 307 final Value right; | |
| 308 BinaryOperationValue(this.left, this.right, info) : super(info); | |
| 309 } | |
| 310 | |
| 311 class AddValue extends BinaryOperationValue { | |
| 312 AddValue(left, right, info) : super(left, right, info); | |
| 313 | |
| 314 bool operator ==(other) { | |
| 315 if (other is !AddValue) return false; | |
| 316 return (left == other.left && right == other.right) | |
| 317 || (left == other.right && right == other.left); | |
| 318 } | |
| 319 | |
| 320 int get hashCode => throw new UnsupportedError('AddValue.hashCode'); | |
| 321 | |
| 322 Value operator -() => -left - right; | |
| 323 | |
| 324 Value operator +(Value other) { | |
| 325 if (other.isZero) return this; | |
| 326 Value value = left + other; | |
| 327 if (value != const UnknownValue() && value is! BinaryOperationValue) { | |
| 328 return value + right; | |
| 329 } | |
| 330 // If the result is not simple enough, we try the same approach | |
| 331 // with [right]. | |
| 332 value = right + other; | |
| 333 if (value != const UnknownValue() && value is! BinaryOperationValue) { | |
| 334 return left + value; | |
| 335 } | |
| 336 return const UnknownValue(); | |
| 337 } | |
| 338 | |
| 339 Value operator -(Value other) { | |
| 340 if (other.isZero) return this; | |
| 341 Value value = left - other; | |
| 342 if (value != const UnknownValue() && value is! BinaryOperationValue) { | |
| 343 return value + right; | |
| 344 } | |
| 345 // If the result is not simple enough, we try the same approach | |
| 346 // with [right]. | |
| 347 value = right - other; | |
| 348 if (value != const UnknownValue() && value is! BinaryOperationValue) { | |
| 349 return left + value; | |
| 350 } | |
| 351 return const UnknownValue(); | |
| 352 } | |
| 353 | |
| 354 bool get isNegative => left.isNegative && right.isNegative; | |
| 355 bool get isPositive => left.isPositive && right.isPositive; | |
| 356 String toString() => '$left + $right'; | |
| 357 } | |
| 358 | |
| 359 class SubtractValue extends BinaryOperationValue { | |
| 360 SubtractValue(left, right, info) : super(left, right, info); | |
| 361 | |
| 362 bool operator ==(other) { | |
| 363 if (other is !SubtractValue) return false; | |
| 364 return left == other.left && right == other.right; | |
| 365 } | |
| 366 | |
| 367 int get hashCode => throw new UnsupportedError('SubtractValue.hashCode'); | |
| 368 | |
| 369 Value operator -() => right - left; | |
| 370 | |
| 371 Value operator +(Value other) { | |
| 372 if (other.isZero) return this; | |
| 373 Value value = left + other; | |
| 374 if (value != const UnknownValue() && value is! BinaryOperationValue) { | |
| 375 return value - right; | |
| 376 } | |
| 377 // If the result is not simple enough, we try the same approach | |
| 378 // with [right]. | |
| 379 value = other - right; | |
| 380 if (value != const UnknownValue() && value is! BinaryOperationValue) { | |
| 381 return left + value; | |
| 382 } | |
| 383 return const UnknownValue(); | |
| 384 } | |
| 385 | |
| 386 Value operator -(Value other) { | |
| 387 if (other.isZero) return this; | |
| 388 Value value = left - other; | |
| 389 if (value != const UnknownValue() && value is! BinaryOperationValue) { | |
| 390 return value - right; | |
| 391 } | |
| 392 // If the result is not simple enough, we try the same approach | |
| 393 // with [right]. | |
| 394 value = right + other; | |
| 395 if (value != const UnknownValue() && value is! BinaryOperationValue) { | |
| 396 return left - value; | |
| 397 } | |
| 398 return const UnknownValue(); | |
| 399 } | |
| 400 | |
| 401 bool get isNegative => left.isNegative && right.isPositive; | |
| 402 bool get isPositive => left.isPositive && right.isNegative; | |
| 403 String toString() => '$left - $right'; | |
| 404 } | |
| 405 | |
| 406 class NegateValue extends Value { | |
| 407 final Value value; | |
| 408 NegateValue(this.value, info) : super(info); | |
| 409 | |
| 410 bool operator ==(other) { | |
| 411 if (other is !NegateValue) return false; | |
| 412 return value == other.value; | |
| 413 } | |
| 414 | |
| 415 int get hashCode => throw new UnsupportedError('Negate.hashCode'); | |
| 416 | |
| 417 Value operator +(other) { | |
| 418 if (other.isZero) return this; | |
| 419 if (other == value) return info.intZero; | |
| 420 if (other is NegateValue) return this - other.value; | |
| 421 if (other is IntValue) { | |
| 422 if (other.isNegative) { | |
| 423 return info.newSubtractValue(this, -other); | |
| 424 } | |
| 425 return info.newSubtractValue(other, value); | |
| 426 } | |
| 427 if (other is InstructionValue) { | |
| 428 return info.newSubtractValue(other, value); | |
| 429 } | |
| 430 return other - value; | |
| 431 } | |
| 432 | |
| 433 Value operator &(Value other) => const UnknownValue(); | |
| 434 | |
| 435 Value operator -(other) { | |
| 436 if (other.isZero) return this; | |
| 437 if (other is IntValue) { | |
| 438 if (other.isNegative) { | |
| 439 return info.newSubtractValue(-other, value); | |
| 440 } | |
| 441 return info.newSubtractValue(this, other); | |
| 442 } | |
| 443 if (other is InstructionValue) { | |
| 444 return info.newSubtractValue(this, other); | |
| 445 } | |
| 446 if (other is NegateValue) return this + other.value; | |
| 447 return -other - value; | |
| 448 } | |
| 449 | |
| 450 Value operator -() => value; | |
| 451 | |
| 452 bool get isNegative => value.isPositive; | |
| 453 bool get isPositive => value.isNegative; | |
| 454 String toString() => '-$value'; | |
| 455 } | |
| 456 | |
| 457 /** | |
| 458 * A [Range] represents the possible integer values an instruction | |
| 459 * can have, from its [lower] bound to its [upper] bound, both | |
| 460 * included. | |
| 461 */ | |
| 462 class Range { | |
| 463 final Value lower; | |
| 464 final Value upper; | |
| 465 final ValueRangeInfo info; | |
| 466 Range(this.lower, this.upper, this.info) { | |
| 467 assert(lower != const UnknownValue()); | |
| 468 assert(upper != const UnknownValue()); | |
| 469 } | |
| 470 | |
| 471 Range.unbound(info) : this(const MinIntValue(), const MaxIntValue(), info); | |
| 472 | |
| 473 /** | |
| 474 * Checks if the given values are unknown, and creates a | |
| 475 * range that does not have any unknown values. | |
| 476 */ | |
| 477 Range.normalize(Value low, Value up, info) : this( | |
| 478 low == const UnknownValue() ? const MinIntValue() : low, | |
| 479 up == const UnknownValue() ? const MaxIntValue() : up, | |
| 480 info); | |
| 481 | |
| 482 Range union(Range other) { | |
| 483 return info.newNormalizedRange( | |
| 484 lower.min(other.lower), upper.max(other.upper)); | |
| 485 } | |
| 486 | |
| 487 Range intersection(Range other) { | |
| 488 Value low = lower.max(other.lower); | |
| 489 Value up = upper.min(other.upper); | |
| 490 // If we could not compute max or min, pick a value in the two | |
| 491 // ranges, with priority to [IntValue]s because they are simpler. | |
| 492 if (low == const UnknownValue()) { | |
| 493 if (lower is IntValue) low = lower; | |
| 494 else if (other.lower is IntValue) low = other.lower; | |
| 495 else low = lower; | |
| 496 } | |
| 497 if (up == const UnknownValue()) { | |
| 498 if (upper is IntValue) up = upper; | |
| 499 else if (other.upper is IntValue) up = other.upper; | |
| 500 else up = upper; | |
| 501 } | |
| 502 return info.newNormalizedRange(low, up); | |
| 503 } | |
| 504 | |
| 505 Range operator +(Range other) { | |
| 506 return info.newNormalizedRange(lower + other.lower, upper + other.upper); | |
| 507 } | |
| 508 | |
| 509 Range operator -(Range other) { | |
| 510 return info.newNormalizedRange(lower - other.upper, upper - other.lower); | |
| 511 } | |
| 512 | |
| 513 Range operator -() { | |
| 514 return info.newNormalizedRange(-upper, -lower); | |
| 515 } | |
| 516 | |
| 517 Range operator &(Range other) { | |
| 518 if (isSingleValue | |
| 519 && other.isSingleValue | |
| 520 && lower is IntValue | |
| 521 && other.lower is IntValue) { | |
| 522 return info.newNormalizedRange(lower & other.lower, upper & other.upper); | |
| 523 } | |
| 524 if (isPositive && other.isPositive) { | |
| 525 Value up = upper.min(other.upper); | |
| 526 if (up == const UnknownValue()) { | |
| 527 // If we could not find a trivial bound, just try to use the | |
| 528 // one that is an int. | |
| 529 up = upper is IntValue ? upper : other.upper; | |
| 530 // Make sure we get the same upper bound, whether it's a & b | |
| 531 // or b & a. | |
| 532 if (up is! IntValue && upper != other.upper) up = const MaxIntValue(); | |
| 533 } | |
| 534 return info.newNormalizedRange(info.intZero, up); | |
| 535 } else if (isPositive) { | |
| 536 return info.newNormalizedRange(info.intZero, upper); | |
| 537 } else if (other.isPositive) { | |
| 538 return info.newNormalizedRange(info.intZero, other.upper); | |
| 539 } else { | |
| 540 return info.newUnboundRange(); | |
| 541 } | |
| 542 } | |
| 543 | |
| 544 bool operator ==(other) { | |
| 545 if (other is! Range) return false; | |
| 546 return other.lower == lower && other.upper == upper; | |
| 547 } | |
| 548 | |
| 549 int get hashCode => throw new UnsupportedError('Range.hashCode'); | |
| 550 | |
| 551 bool operator <(Range other) { | |
| 552 return upper != other.lower && upper.min(other.lower) == upper; | |
| 553 } | |
| 554 | |
| 555 bool operator >(Range other) { | |
| 556 return lower != other.upper && lower.max(other.upper) == lower; | |
| 557 } | |
| 558 | |
| 559 bool operator <=(Range other) { | |
| 560 return upper.min(other.lower) == upper; | |
| 561 } | |
| 562 | |
| 563 bool operator >=(Range other) { | |
| 564 return lower.max(other.upper) == lower; | |
| 565 } | |
| 566 | |
| 567 bool get isNegative => upper.isNegative; | |
| 568 bool get isPositive => lower.isPositive; | |
| 569 bool get isSingleValue => lower == upper; | |
| 570 | |
| 571 String toString() => '[$lower, $upper]'; | |
| 572 } | |
| 573 | |
| 574 /** | |
| 575 * Visits the graph in dominator order, and computes value ranges for | |
| 576 * integer instructions. While visiting the graph, this phase also | |
| 577 * removes unnecessary bounds checks, and comparisons that are proven | |
| 578 * to be true or false. | |
| 579 */ | |
| 580 class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { | |
| 581 String get name => 'SSA value range builder'; | |
| 582 | |
| 583 /** | |
| 584 * List of [HRangeConversion] instructions created by the phase. We | |
| 585 * save them here in order to remove them once the phase is done. | |
| 586 */ | |
| 587 final List<HRangeConversion> conversions = <HRangeConversion>[]; | |
| 588 | |
| 589 /** | |
| 590 * Value ranges for integer instructions. This map gets populated by | |
| 591 * the dominator tree visit. | |
| 592 */ | |
| 593 final Map<HInstruction, Range> ranges = new Map<HInstruction, Range>(); | |
| 594 | |
| 595 final Compiler compiler; | |
| 596 final ConstantSystem constantSystem; | |
| 597 final ValueRangeInfo info; | |
| 598 | |
| 599 CodegenWorkItem work; | |
| 600 HGraph graph; | |
| 601 | |
| 602 SsaValueRangeAnalyzer(this.compiler, constantSystem, this.work) | |
| 603 : info = new ValueRangeInfo(constantSystem), | |
| 604 this.constantSystem = constantSystem; | |
| 605 | |
| 606 void visitGraph(HGraph graph) { | |
| 607 this.graph = graph; | |
| 608 visitDominatorTree(graph); | |
| 609 // We remove the range conversions after visiting the graph so | |
| 610 // that the graph does not get polluted with these instructions | |
| 611 // only necessary for this phase. | |
| 612 removeRangeConversion(); | |
| 613 JavaScriptBackend backend = compiler.backend; | |
| 614 // TODO(herhut): Find a cleaner way to pass around ranges. | |
| 615 backend.optimizer.ranges = ranges; | |
| 616 } | |
| 617 | |
| 618 void removeRangeConversion() { | |
| 619 conversions.forEach((HRangeConversion instruction) { | |
| 620 instruction.block.rewrite(instruction, instruction.inputs[0]);; | |
| 621 instruction.block.remove(instruction); | |
| 622 }); | |
| 623 } | |
| 624 | |
| 625 void visitBasicBlock(HBasicBlock block) { | |
| 626 | |
| 627 void visit(HInstruction instruction) { | |
| 628 Range range = instruction.accept(this); | |
| 629 if (instruction.isInteger(compiler)) { | |
| 630 assert(range != null); | |
| 631 ranges[instruction] = range; | |
| 632 } | |
| 633 } | |
| 634 | |
| 635 block.forEachPhi(visit); | |
| 636 block.forEachInstruction(visit); | |
| 637 } | |
| 638 | |
| 639 Range visitInstruction(HInstruction instruction) { | |
| 640 if (instruction.isPositiveInteger(compiler)) { | |
| 641 return info.newNormalizedRange( | |
| 642 info.intZero, info.newPositiveValue(instruction)); | |
| 643 } else if (instruction.isInteger(compiler)) { | |
| 644 InstructionValue value = info.newInstructionValue(instruction); | |
| 645 return info.newNormalizedRange(value, value); | |
| 646 } else { | |
| 647 return info.newUnboundRange(); | |
| 648 } | |
| 649 } | |
| 650 | |
| 651 Range visitPhi(HPhi phi) { | |
| 652 if (!phi.isInteger(compiler)) return info.newUnboundRange(); | |
| 653 // Some phases may replace instructions that change the inputs of | |
| 654 // this phi. Only the [SsaTypesPropagation] phase will update the | |
| 655 // phi type. Play it safe by assuming the [SsaTypesPropagation] | |
| 656 // phase is not necessarily run before the [ValueRangeAnalyzer]. | |
| 657 if (phi.inputs.any((i) => !i.isInteger(compiler))) { | |
| 658 return info.newUnboundRange(); | |
| 659 } | |
| 660 if (phi.block.isLoopHeader()) { | |
| 661 Range range = new LoopUpdateRecognizer(compiler, ranges, info).run(phi); | |
| 662 if (range == null) return info.newUnboundRange(); | |
| 663 return range; | |
| 664 } | |
| 665 | |
| 666 Range range = ranges[phi.inputs[0]]; | |
| 667 for (int i = 1; i < phi.inputs.length; i++) { | |
| 668 range = range.union(ranges[phi.inputs[i]]); | |
| 669 } | |
| 670 return range; | |
| 671 } | |
| 672 | |
| 673 Range visitConstant(HConstant hConstant) { | |
| 674 if (!hConstant.isInteger(compiler)) return info.newUnboundRange(); | |
| 675 ConstantValue constant = hConstant.constant; | |
| 676 NumConstantValue constantNum; | |
| 677 if (constant is DeferredConstantValue) { | |
| 678 constantNum = constant.referenced; | |
| 679 } else { | |
| 680 constantNum = constant; | |
| 681 } | |
| 682 if (constantNum.isMinusZero) constantNum = new IntConstantValue(0); | |
| 683 Value value = info.newIntValue(constantNum.primitiveValue); | |
| 684 return info.newNormalizedRange(value, value); | |
| 685 } | |
| 686 | |
| 687 Range visitFieldGet(HFieldGet fieldGet) { | |
| 688 if (!fieldGet.isInteger(compiler)) return info.newUnboundRange(); | |
| 689 if (!fieldGet.receiver.isIndexablePrimitive(compiler)) { | |
| 690 return visitInstruction(fieldGet); | |
| 691 } | |
| 692 JavaScriptBackend backend = compiler.backend; | |
| 693 assert(fieldGet.element == backend.jsIndexableLength); | |
| 694 PositiveValue value = info.newPositiveValue(fieldGet); | |
| 695 // We know this range is above zero. To simplify the analysis, we | |
| 696 // put the zero value as the lower bound of this range. This | |
| 697 // allows to easily remove the second bound check in the following | |
| 698 // expression: a[1] + a[0]. | |
| 699 return info.newNormalizedRange(info.intZero, value); | |
| 700 } | |
| 701 | |
| 702 Range visitBoundsCheck(HBoundsCheck check) { | |
| 703 // Save the next instruction, in case the check gets removed. | |
| 704 HInstruction next = check.next; | |
| 705 Range indexRange = ranges[check.index]; | |
| 706 Range lengthRange = ranges[check.length]; | |
| 707 if (indexRange == null) { | |
| 708 indexRange = info.newUnboundRange(); | |
| 709 assert(!check.index.isInteger(compiler)); | |
| 710 } | |
| 711 assert(check.length.isInteger(compiler)); | |
| 712 | |
| 713 // Check if the index is strictly below the upper bound of the length | |
| 714 // range. | |
| 715 Value maxIndex = lengthRange.upper - info.intOne; | |
| 716 bool belowLength = maxIndex != const MaxIntValue() | |
| 717 && indexRange.upper.min(maxIndex) == indexRange.upper; | |
| 718 | |
| 719 // Check if the index is strictly below the lower bound of the length | |
| 720 // range. | |
| 721 belowLength = belowLength | |
| 722 || (indexRange.upper != lengthRange.lower | |
| 723 && indexRange.upper.min(lengthRange.lower) == indexRange.upper); | |
| 724 if (indexRange.isPositive && belowLength) { | |
| 725 check.block.rewrite(check, check.index); | |
| 726 check.block.remove(check); | |
| 727 } else if (indexRange.isNegative || lengthRange < indexRange) { | |
| 728 check.staticChecks = HBoundsCheck.ALWAYS_FALSE; | |
| 729 // The check is always false, and whatever instruction it | |
| 730 // dominates is dead code. | |
| 731 return indexRange; | |
| 732 } else if (indexRange.isPositive) { | |
| 733 check.staticChecks = HBoundsCheck.ALWAYS_ABOVE_ZERO; | |
| 734 } else if (belowLength) { | |
| 735 check.staticChecks = HBoundsCheck.ALWAYS_BELOW_LENGTH; | |
| 736 } | |
| 737 | |
| 738 if (indexRange.isPositive) { | |
| 739 // If the test passes, we know the lower bound of the length is | |
| 740 // greater or equal than the lower bound of the index. | |
| 741 Value low = lengthRange.lower.max(indexRange.lower); | |
| 742 if (low != const UnknownValue()) { | |
| 743 HInstruction instruction = | |
| 744 createRangeConversion(next, check.length); | |
| 745 ranges[instruction] = info.newNormalizedRange(low, lengthRange.upper); | |
| 746 } | |
| 747 } | |
| 748 | |
| 749 // Update the range of the index if using the maximum index | |
| 750 // narrows it. Use that new range for this instruction as well. | |
| 751 Range newIndexRange = indexRange.intersection( | |
| 752 info.newNormalizedRange(info.intZero, maxIndex)); | |
| 753 if (indexRange == newIndexRange) return indexRange; | |
| 754 // Explicitly attach the range information to the index instruction, | |
| 755 // which may be used by other instructions. Returning the new range will | |
| 756 // attach it to this instruction. | |
| 757 HInstruction instruction = createRangeConversion(next, check.index); | |
| 758 ranges[instruction] = newIndexRange; | |
| 759 return newIndexRange; | |
| 760 } | |
| 761 | |
| 762 Range visitRelational(HRelational relational) { | |
| 763 HInstruction right = relational.right; | |
| 764 HInstruction left = relational.left; | |
| 765 if (!left.isInteger(compiler)) return info.newUnboundRange(); | |
| 766 if (!right.isInteger(compiler)) return info.newUnboundRange(); | |
| 767 BinaryOperation operation = relational.operation(constantSystem); | |
| 768 Range rightRange = ranges[relational.right]; | |
| 769 Range leftRange = ranges[relational.left]; | |
| 770 | |
| 771 if (relational is HIdentity) { | |
| 772 handleEqualityCheck(relational); | |
| 773 } else if (operation.apply(leftRange, rightRange)) { | |
| 774 relational.block.rewrite( | |
| 775 relational, graph.addConstantBool(true, compiler)); | |
| 776 relational.block.remove(relational); | |
| 777 } else if (negateOperation(operation).apply(leftRange, rightRange)) { | |
| 778 relational.block.rewrite( | |
| 779 relational, graph.addConstantBool(false, compiler)); | |
| 780 relational.block.remove(relational); | |
| 781 } | |
| 782 return info.newUnboundRange(); | |
| 783 } | |
| 784 | |
| 785 void handleEqualityCheck(HRelational node) { | |
| 786 Range right = ranges[node.right]; | |
| 787 Range left = ranges[node.left]; | |
| 788 if (left.isSingleValue && right.isSingleValue && left == right) { | |
| 789 node.block.rewrite( | |
| 790 node, graph.addConstantBool(true, compiler)); | |
| 791 node.block.remove(node); | |
| 792 } | |
| 793 } | |
| 794 | |
| 795 Range handleInvokeModulo(HInvokeDynamicMethod invoke) { | |
| 796 HInstruction left = invoke.inputs[1]; | |
| 797 HInstruction right = invoke.inputs[2]; | |
| 798 Range divisor = ranges[right]; | |
| 799 if (divisor != null) { | |
| 800 // For Integer values we can be precise in the upper bound, | |
| 801 // so special case those. | |
| 802 if (left.isInteger(compiler) && right.isInteger(compiler)) { | |
| 803 if (divisor.isPositive) { | |
| 804 return info.newNormalizedRange(info.intZero, divisor.upper - | |
| 805 info.intOne); | |
| 806 } else if (divisor.isNegative) { | |
| 807 return info.newNormalizedRange(info.intZero, info.newNegateValue( | |
| 808 divisor.lower) - info.intOne); | |
| 809 } | |
| 810 } else if (left.isNumber(compiler) && right.isNumber(compiler)) { | |
| 811 if (divisor.isPositive) { | |
| 812 return info.newNormalizedRange(info.intZero, divisor.upper); | |
| 813 } else if (divisor.isNegative) { | |
| 814 return info.newNormalizedRange(info.intZero, info.newNegateValue( | |
| 815 divisor.lower)); | |
| 816 } | |
| 817 } | |
| 818 } | |
| 819 return info.newUnboundRange(); | |
| 820 } | |
| 821 | |
| 822 Range visitInvokeDynamicMethod(HInvokeDynamicMethod invoke) { | |
| 823 if ((invoke.inputs.length == 3) && (invoke.selector.name == "%")) | |
| 824 return handleInvokeModulo(invoke); | |
| 825 return super.visitInvokeDynamicMethod(invoke); | |
| 826 } | |
| 827 | |
| 828 Range handleBinaryOperation(HBinaryArithmetic instruction) { | |
| 829 if (!instruction.isInteger(compiler)) return info.newUnboundRange(); | |
| 830 return instruction.operation(constantSystem).apply( | |
| 831 ranges[instruction.left], ranges[instruction.right]); | |
| 832 } | |
| 833 | |
| 834 Range visitAdd(HAdd add) { | |
| 835 return handleBinaryOperation(add); | |
| 836 } | |
| 837 | |
| 838 Range visitSubtract(HSubtract sub) { | |
| 839 return handleBinaryOperation(sub); | |
| 840 } | |
| 841 | |
| 842 Range visitBitAnd(HBitAnd node) { | |
| 843 if (!node.isInteger(compiler)) return info.newUnboundRange(); | |
| 844 HInstruction right = node.right; | |
| 845 HInstruction left = node.left; | |
| 846 if (left.isInteger(compiler) && right.isInteger(compiler)) { | |
| 847 return ranges[left] & ranges[right]; | |
| 848 } | |
| 849 | |
| 850 Range tryComputeRange(HInstruction instruction) { | |
| 851 Range range = ranges[instruction]; | |
| 852 if (range.isPositive) { | |
| 853 return info.newNormalizedRange(info.intZero, range.upper); | |
| 854 } else if (range.isNegative) { | |
| 855 return info.newNormalizedRange(range.lower, info.intZero); | |
| 856 } | |
| 857 return info.newUnboundRange(); | |
| 858 } | |
| 859 | |
| 860 if (left.isInteger(compiler)) { | |
| 861 return tryComputeRange(left); | |
| 862 } else if (right.isInteger(compiler)) { | |
| 863 return tryComputeRange(right); | |
| 864 } | |
| 865 return info.newUnboundRange(); | |
| 866 } | |
| 867 | |
| 868 Range visitCheck(HCheck instruction) { | |
| 869 if (ranges[instruction.checkedInput] == null) { | |
| 870 return visitInstruction(instruction); | |
| 871 } | |
| 872 return ranges[instruction.checkedInput]; | |
| 873 } | |
| 874 | |
| 875 HInstruction createRangeConversion(HInstruction cursor, | |
| 876 HInstruction instruction) { | |
| 877 JavaScriptBackend backend = compiler.backend; | |
| 878 HRangeConversion newInstruction = | |
| 879 new HRangeConversion(instruction, backend.intType); | |
| 880 conversions.add(newInstruction); | |
| 881 cursor.block.addBefore(cursor, newInstruction); | |
| 882 // Update the users of the instruction dominated by [cursor] to | |
| 883 // use the new instruction, that has an narrower range. | |
| 884 instruction.replaceAllUsersDominatedBy(cursor, newInstruction); | |
| 885 return newInstruction; | |
| 886 } | |
| 887 | |
| 888 static BinaryOperation negateOperation(BinaryOperation operation) { | |
| 889 if (operation == const LessOperation()) { | |
| 890 return const GreaterEqualOperation(); | |
| 891 } else if (operation == const LessEqualOperation()) { | |
| 892 return const GreaterOperation(); | |
| 893 } else if (operation == const GreaterOperation()) { | |
| 894 return const LessEqualOperation(); | |
| 895 } else if (operation == const GreaterEqualOperation()) { | |
| 896 return const LessOperation(); | |
| 897 } else { | |
| 898 return null; | |
| 899 } | |
| 900 } | |
| 901 | |
| 902 static BinaryOperation flipOperation(BinaryOperation operation) { | |
| 903 if (operation == const LessOperation()) { | |
| 904 return const GreaterOperation(); | |
| 905 } else if (operation == const LessEqualOperation()) { | |
| 906 return const GreaterEqualOperation(); | |
| 907 } else if (operation == const GreaterOperation()) { | |
| 908 return const LessOperation(); | |
| 909 } else if (operation == const GreaterEqualOperation()) { | |
| 910 return const LessEqualOperation(); | |
| 911 } else { | |
| 912 return null; | |
| 913 } | |
| 914 } | |
| 915 | |
| 916 Range computeConstrainedRange(BinaryOperation operation, | |
| 917 Range leftRange, | |
| 918 Range rightRange) { | |
| 919 Range range; | |
| 920 if (operation == const LessOperation()) { | |
| 921 range = info.newNormalizedRange( | |
| 922 const MinIntValue(), rightRange.upper - info.intOne); | |
| 923 } else if (operation == const LessEqualOperation()) { | |
| 924 range = info.newNormalizedRange(const MinIntValue(), rightRange.upper); | |
| 925 } else if (operation == const GreaterOperation()) { | |
| 926 range = info.newNormalizedRange( | |
| 927 rightRange.lower + info.intOne, const MaxIntValue()); | |
| 928 } else if (operation == const GreaterEqualOperation()) { | |
| 929 range = info.newNormalizedRange(rightRange.lower, const MaxIntValue()); | |
| 930 } else { | |
| 931 range = info.newUnboundRange(); | |
| 932 } | |
| 933 return range.intersection(leftRange); | |
| 934 } | |
| 935 | |
| 936 Range visitConditionalBranch(HConditionalBranch branch) { | |
| 937 var condition = branch.condition; | |
| 938 // TODO(ngeoffray): Handle complex conditions. | |
| 939 if (condition is !HRelational) return info.newUnboundRange(); | |
| 940 if (condition is HIdentity) return info.newUnboundRange(); | |
| 941 HInstruction right = condition.right; | |
| 942 HInstruction left = condition.left; | |
| 943 if (!left.isInteger(compiler)) return info.newUnboundRange(); | |
| 944 if (!right.isInteger(compiler)) return info.newUnboundRange(); | |
| 945 | |
| 946 Range rightRange = ranges[right]; | |
| 947 Range leftRange = ranges[left]; | |
| 948 Operation operation = condition.operation(constantSystem); | |
| 949 Operation mirrorOp = flipOperation(operation); | |
| 950 // Only update the true branch if this block is the only | |
| 951 // predecessor. | |
| 952 if (branch.trueBranch.predecessors.length == 1) { | |
| 953 assert(branch.trueBranch.predecessors[0] == branch.block); | |
| 954 // Update the true branch to use narrower ranges for [left] and | |
| 955 // [right]. | |
| 956 Range range = computeConstrainedRange(operation, leftRange, rightRange); | |
| 957 if (leftRange != range) { | |
| 958 HInstruction instruction = | |
| 959 createRangeConversion(branch.trueBranch.first, left); | |
| 960 ranges[instruction] = range; | |
| 961 } | |
| 962 | |
| 963 range = computeConstrainedRange(mirrorOp, rightRange, leftRange); | |
| 964 if (rightRange != range) { | |
| 965 HInstruction instruction = | |
| 966 createRangeConversion(branch.trueBranch.first, right); | |
| 967 ranges[instruction] = range; | |
| 968 } | |
| 969 } | |
| 970 | |
| 971 // Only update the false branch if this block is the only | |
| 972 // predecessor. | |
| 973 if (branch.falseBranch.predecessors.length == 1) { | |
| 974 assert(branch.falseBranch.predecessors[0] == branch.block); | |
| 975 Operation reverse = negateOperation(operation); | |
| 976 Operation reversedMirror = flipOperation(reverse); | |
| 977 // Update the false branch to use narrower ranges for [left] and | |
| 978 // [right]. | |
| 979 Range range = computeConstrainedRange(reverse, leftRange, rightRange); | |
| 980 if (leftRange != range) { | |
| 981 HInstruction instruction = | |
| 982 createRangeConversion(branch.falseBranch.first, left); | |
| 983 ranges[instruction] = range; | |
| 984 } | |
| 985 | |
| 986 range = computeConstrainedRange(reversedMirror, rightRange, leftRange); | |
| 987 if (rightRange != range) { | |
| 988 HInstruction instruction = | |
| 989 createRangeConversion(branch.falseBranch.first, right); | |
| 990 ranges[instruction] = range; | |
| 991 } | |
| 992 } | |
| 993 | |
| 994 return info.newUnboundRange(); | |
| 995 } | |
| 996 | |
| 997 Range visitRangeConversion(HRangeConversion conversion) { | |
| 998 return ranges[conversion]; | |
| 999 } | |
| 1000 } | |
| 1001 | |
| 1002 /** | |
| 1003 * Tries to find a range for the update instruction of a loop phi. | |
| 1004 */ | |
| 1005 class LoopUpdateRecognizer extends HBaseVisitor { | |
| 1006 final Compiler compiler; | |
| 1007 final Map<HInstruction, Range> ranges; | |
| 1008 final ValueRangeInfo info; | |
| 1009 LoopUpdateRecognizer(this.compiler, this.ranges, this.info); | |
| 1010 | |
| 1011 Range run(HPhi loopPhi) { | |
| 1012 // Create a marker range for the loop phi, so that if the update | |
| 1013 // uses the loop phi, it has a range to use. | |
| 1014 ranges[loopPhi] = info.newMarkerRange(); | |
| 1015 Range updateRange = visit(loopPhi.inputs[1]); | |
| 1016 ranges[loopPhi] = null; | |
| 1017 if (updateRange == null) return null; | |
| 1018 Range startRange = ranges[loopPhi.inputs[0]]; | |
| 1019 // If the lower (respectively upper) value is the marker, we know | |
| 1020 // the loop does not change it, so we can just use the | |
| 1021 // [startRange]'s lower (upper) value. Otherwise the lower (upper) value | |
| 1022 // is the minimum of the [startRange]'s lower (upper) and the | |
| 1023 // [updateRange]'s lower (upper). | |
| 1024 Value low = updateRange.lower is MarkerValue | |
| 1025 ? startRange.lower | |
| 1026 : updateRange.lower.min(startRange.lower); | |
| 1027 Value up = updateRange.upper is MarkerValue | |
| 1028 ? startRange.upper | |
| 1029 : updateRange.upper.max(startRange.upper); | |
| 1030 return info.newNormalizedRange(low, up); | |
| 1031 } | |
| 1032 | |
| 1033 Range visit(HInstruction instruction) { | |
| 1034 if (!instruction.isInteger(compiler)) return null; | |
| 1035 if (ranges[instruction] != null) return ranges[instruction]; | |
| 1036 return instruction.accept(this); | |
| 1037 } | |
| 1038 | |
| 1039 Range visitPhi(HPhi phi) { | |
| 1040 // If the update of a loop phi involves another loop phi, we give | |
| 1041 // up. | |
| 1042 if (phi.block.isLoopHeader()) return null; | |
| 1043 Range phiRange; | |
| 1044 for (HInstruction input in phi.inputs) { | |
| 1045 Range inputRange = visit(input); | |
| 1046 if (inputRange == null) return null; | |
| 1047 if (phiRange == null) { | |
| 1048 phiRange = inputRange; | |
| 1049 } else { | |
| 1050 phiRange = phiRange.union(inputRange); | |
| 1051 } | |
| 1052 } | |
| 1053 return phiRange; | |
| 1054 } | |
| 1055 | |
| 1056 Range visitCheck(HCheck instruction) { | |
| 1057 return visit(instruction.checkedInput); | |
| 1058 } | |
| 1059 | |
| 1060 Range visitAdd(HAdd operation) { | |
| 1061 return handleBinaryOperation(operation); | |
| 1062 } | |
| 1063 | |
| 1064 Range visitSubtract(HSubtract operation) { | |
| 1065 return handleBinaryOperation(operation); | |
| 1066 } | |
| 1067 | |
| 1068 Range handleBinaryOperation(HBinaryArithmetic instruction) { | |
| 1069 Range leftRange = visit(instruction.left); | |
| 1070 Range rightRange = visit(instruction.right); | |
| 1071 if (leftRange == null || rightRange == null) return null; | |
| 1072 BinaryOperation operation = instruction.operation(info.constantSystem); | |
| 1073 return operation.apply(leftRange, rightRange); | |
| 1074 } | |
| 1075 } | |
| OLD | NEW |