Chromium Code Reviews| 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 /** | |
| 6 * A [Value] represents both symbolic values like the value of a | |
| 7 * parameter, or the length of an array, and concrete values, like | |
| 8 * constants. | |
| 9 */ | |
| 10 abstract class Value { | |
| 11 const Value(); | |
| 12 | |
| 13 Value operator +(Value other); | |
| 14 Value operator -(Value other); | |
| 15 Value operator &(Value other); | |
| 16 | |
| 17 Value min(Value other) { | |
| 18 if (this == other) return this; | |
| 19 if (other == const MinValue()) return other; | |
| 20 if (other == const MaxValue()) return this; | |
| 21 Value value = this - other; | |
| 22 if (value.isPositive()) return other; | |
| 23 if (value.isNegative()) return this; | |
| 24 return const UnknownValue(); | |
| 25 } | |
| 26 | |
| 27 Value max(Value other) { | |
| 28 if (this == other) return this; | |
| 29 if (other == const MinValue()) return this; | |
| 30 if (other == const MaxValue()) return other; | |
| 31 Value value = this - other; | |
| 32 if (value.isPositive()) return this; | |
| 33 if (value.isNegative()) return other; | |
| 34 return const UnknownValue(); | |
| 35 } | |
| 36 | |
| 37 bool isNegative() => false; | |
| 38 bool isPositive() => false; | |
| 39 bool isZero() => false; | |
| 40 } | |
| 41 | |
| 42 /** | |
| 43 * An [IntValue] contains a constant integer value. | |
| 44 */ | |
| 45 class IntValue extends Value { | |
| 46 final int value; | |
| 47 const IntValue(this.value); | |
| 48 | |
| 49 Value operator +(other) { | |
| 50 if (other is !IntValue) return other + this; | |
| 51 return new IntValue(value + other.value); | |
| 52 } | |
| 53 | |
| 54 Value operator -(other) { | |
| 55 if (other is !IntValue) return other - this; | |
| 56 return new IntValue(value - other.value); | |
| 57 } | |
| 58 | |
| 59 Value operator &(other) { | |
| 60 if (other is !IntValue) return this; | |
| 61 return new IntValue(value & other.value); | |
| 62 } | |
| 63 | |
| 64 Value min(other) { | |
| 65 if (other is !IntValue) return other.min(this); | |
| 66 return this.value < other.value ? this : other; | |
| 67 } | |
| 68 | |
| 69 Value max(other) { | |
| 70 if (other is !IntValue) return other.max(this); | |
| 71 return this.value < other.value ? other : this; | |
| 72 } | |
| 73 | |
| 74 bool operator ==(other) { | |
| 75 if (other is !IntValue) return false; | |
| 76 return this.value == other.value; | |
| 77 } | |
| 78 | |
| 79 String toString() => 'IntValue $value'; | |
| 80 bool isNegative() => value < 0; | |
| 81 bool isPositive() => value >= 0; | |
| 82 bool isZero() => value == 0; | |
| 83 } | |
| 84 | |
| 85 /** | |
| 86 * The [MaxValue] represents the maximum value an integer can have, | |
| 87 * which is currently +infinity. | |
| 88 */ | |
| 89 class MaxValue extends Value { | |
|
Søren Gjesse
2012/09/26 09:08:24
Should this maybe be called MaxIntValue and extend
ngeoffray
2012/09/26 09:33:26
Renaming is fine, but if it extends IntValue what
Søren Gjesse
2012/09/26 14:00:15
Another alternative is to have AbstractIntValue wi
ngeoffray
2012/09/27 13:22:02
Let's rename MaxValue and MinValue to MaxIntValue
| |
| 90 const MaxValue(); | |
| 91 Value operator +(Value other) => this; | |
| 92 Value operator -(Value other) => this; | |
| 93 Value operator &(Value other) { | |
| 94 if (other.isPositive()) return other; | |
| 95 if (other.isNegative()) return const IntValue(0); | |
| 96 return this; | |
| 97 } | |
| 98 Value min(Value other) => other; | |
| 99 Value max(Value other) => this; | |
| 100 String toString() => 'Max'; | |
| 101 bool isNegative() => false; | |
| 102 bool isPositive() => true; | |
| 103 } | |
| 104 | |
| 105 /** | |
| 106 * The [MinValue] represents the minimum value an integer can have, | |
| 107 * which is currently -infinity. | |
| 108 */ | |
| 109 class MinValue extends Value { | |
|
Søren Gjesse
2012/09/26 09:08:24
MinIntValue?
| |
| 110 const MinValue(); | |
| 111 Value operator +(Value other) => this; | |
| 112 Value operator -(Value other) => this; | |
| 113 Value operator &(Value other) { | |
| 114 if (other.isPositive()) return const IntValue(0); | |
| 115 if (other.isNegative()) return other; | |
| 116 return this; | |
| 117 } | |
| 118 Value min(Value other) => this; | |
| 119 Value max(Value other) => other; | |
| 120 String toString() => 'Min'; | |
| 121 bool isNegative() => true; | |
| 122 bool isPositive() => false; | |
| 123 } | |
| 124 | |
| 125 /** | |
| 126 * The [UnknownValue] is the sentinel in our analysis to mark an | |
| 127 * operation that could not be done because of too much complexity. | |
| 128 */ | |
| 129 class UnknownValue extends Value { | |
| 130 const UnknownValue(); | |
| 131 Value operator +(Value other) => const UnknownValue(); | |
| 132 Value operator -(Value other) => const UnknownValue(); | |
| 133 Value operator &(Value other) => const UnknownValue(); | |
| 134 Value min(Value other) => const UnknownValue(); | |
| 135 Value max(Value other) => const UnknownValue(); | |
| 136 bool isNegative() => false; | |
| 137 bool isPositive() => false; | |
| 138 String toString() => 'Unknown'; | |
| 139 } | |
| 140 | |
| 141 /** | |
| 142 * A symbolic value representing an [HInstruction]. | |
| 143 */ | |
| 144 class InstructionValue extends Value { | |
| 145 final HInstruction instruction; | |
| 146 InstructionValue(this.instruction); | |
| 147 | |
| 148 bool operator ==(other) { | |
| 149 if (other is !InstructionValue) return false; | |
| 150 return this.instruction == other.instruction; | |
| 151 } | |
| 152 | |
| 153 Value operator +(Value other) { | |
| 154 if (other.isZero()) return this; | |
| 155 return new OperationValue(this, other, const AddOperation()); | |
| 156 } | |
| 157 | |
| 158 Value operator -(Value other) { | |
| 159 if (other.isZero()) return this; | |
| 160 if (this == other) return const IntValue(0); | |
| 161 return new OperationValue(this, other, const SubtractOperation()); | |
| 162 } | |
| 163 | |
| 164 Value operator &(Value other) { | |
| 165 if (other is IntValue) return other & this; | |
| 166 return this; | |
| 167 } | |
| 168 | |
| 169 bool isNegative() => false; | |
| 170 bool isPositive() => false; | |
| 171 | |
| 172 String toString() => 'Instruction: $instruction'; | |
| 173 } | |
| 174 | |
| 175 /** | |
| 176 * Special value for instructions that represent the length of an | |
| 177 * array. The difference with an [InstructionValue] is that we know | |
| 178 * the value is positive. | |
| 179 */ | |
| 180 class LengthValue extends InstructionValue { | |
| 181 LengthValue(HInstruction instruction) : super(instruction); | |
| 182 bool isPositive() => true; | |
| 183 String toString() => 'Length: $instruction'; | |
| 184 } | |
| 185 | |
| 186 /** | |
| 187 * Represents a binary operation on two [Value], where the operation | |
| 188 * did not yield a canonical value. | |
| 189 */ | |
| 190 class OperationValue extends Value { | |
| 191 final Value left; | |
| 192 final Value right; | |
| 193 final Operation operation; | |
| 194 OperationValue(this.left, this.right, this.operation); | |
| 195 | |
| 196 bool operator ==(other) { | |
| 197 if (other is !OperationValue) return false; | |
| 198 return left == other.left | |
| 199 && right == other.right | |
| 200 && operation == other.operation; | |
| 201 } | |
| 202 | |
| 203 Value operator +(Value other) => const UnknownValue(); | |
| 204 Value operator &(Value other) => const UnknownValue(); | |
| 205 | |
| 206 Value operator -(Value other) { | |
|
Søren Gjesse
2012/09/26 09:08:24
Could you please explain how this works?
ngeoffray
2012/09/26 09:33:26
Done.
| |
| 207 Value value = left - other; | |
| 208 if (value != const UnknownValue() && value is! OperationValue) { | |
| 209 return operation.apply(value, right); | |
| 210 } | |
| 211 value = right - other; | |
| 212 if (value != const UnknownValue() && value is! OperationValue) { | |
| 213 return operation.apply(left, value); | |
| 214 } | |
| 215 return const UnknownValue(); | |
| 216 } | |
| 217 | |
| 218 bool isNegative() => false; | |
| 219 bool isPositive() => false; | |
| 220 String toString() => '$left ${operation.name} $right'; | |
| 221 } | |
| 222 | |
| 223 /** | |
| 224 * A [Range] represents the possible integer values an instruction | |
| 225 * can have, from its [lower] bound to its [upper] bound, both | |
| 226 * included. | |
| 227 */ | |
| 228 class Range { | |
| 229 final Value lower; | |
| 230 final Value upper; | |
| 231 const Range([this.lower = const MinValue(), this.upper = const MaxValue()]); | |
| 232 | |
| 233 /** | |
| 234 * Checks if the range has UnknownValue as bounds, and returns a | |
| 235 * range that does not have any. | |
| 236 */ | |
| 237 Range normalize() { | |
| 238 if (lower != const UnknownValue() && upper != const UnknownValue()) { | |
| 239 return this; | |
| 240 } | |
| 241 Value low = lower == const UnknownValue() ? const MinValue() : lower; | |
| 242 Value up = upper == const UnknownValue() ? const MaxValue() : upper; | |
| 243 return new Range(low, up); | |
| 244 } | |
| 245 | |
| 246 Range union(Range other) { | |
| 247 Range range = new Range(lower.min(other.lower), upper.max(other.upper)); | |
|
Søren Gjesse
2012/09/26 09:08:24
Maybe add Range.normalize constructor.
ngeoffray
2012/09/26 09:33:26
Good point. Done.
| |
| 248 return range.normalize(); | |
| 249 } | |
| 250 | |
| 251 intersection(Range other) { | |
| 252 Range range = new Range(lower.max(other.lower), upper.min(other.upper)); | |
| 253 return range.normalize(); | |
| 254 } | |
| 255 | |
| 256 Range operator +(Range other) { | |
| 257 Range range = new Range(lower + other.lower, upper + other.upper); | |
| 258 return range.normalize(); | |
| 259 } | |
| 260 | |
| 261 Range operator -(Range other) { | |
| 262 Range range = new Range(lower - other.lower, upper - other.upper); | |
| 263 return range.normalize(); | |
| 264 } | |
| 265 | |
| 266 Range operator &(Range other) { | |
| 267 Range range = new Range(lower & other.lower, upper & other.upper); | |
| 268 return range.normalize(); | |
| 269 } | |
| 270 | |
| 271 bool operator ==(other) { | |
| 272 if (other is! Range) return false; | |
| 273 return other.lower == lower && other.upper == upper; | |
| 274 } | |
| 275 | |
| 276 bool isLessThan(Range other) { | |
| 277 return upper != other.lower && upper.min(other.lower) == upper; | |
| 278 } | |
| 279 | |
| 280 bool isNegative() => upper.isNegative(); | |
| 281 bool isPositive() => lower.isPositive(); | |
| 282 | |
| 283 String toString() => '[$lower, $upper]'; | |
| 284 } | |
| 285 | |
| 286 /** | |
| 287 * Visits the graph in dominator order, and computes value ranges for | |
| 288 * integer instructions. While visiting the graph, this phase also | |
| 289 * removes unnecessary bounds checks, and comparisons that are proven | |
| 290 * to be true or false. | |
| 291 */ | |
| 292 class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase { | |
| 293 String get name => 'SSA value range builder'; | |
| 294 | |
| 295 /** | |
| 296 * List of [HRangeConversion] instructions created by the phase. We | |
| 297 * save them here in order to remove them once the phase is done. | |
| 298 */ | |
| 299 final List<HRangeConversion> conversions = <HRangeConversion>[]; | |
| 300 | |
| 301 /** | |
| 302 * Value ranges for integer instructions. This map gets populated by | |
| 303 * the dominator tree visit. | |
| 304 */ | |
| 305 final Map<HInstruction, Range> ranges = new Map<HInstruction, Range>(); | |
| 306 | |
| 307 final ConstantSystem constantSystem; | |
| 308 final HTypeMap types; | |
| 309 WorkItem work; | |
| 310 HGraph graph; | |
| 311 | |
| 312 SsaValueRangeAnalyzer(this.constantSystem, this.types, WorkItem this.work); | |
| 313 | |
| 314 void visitGraph(HGraph graph) { | |
| 315 this.graph = graph; | |
| 316 visitDominatorTree(graph); | |
| 317 // We remove the range conversions after visiting the graph so | |
| 318 // that the graph does not get polluted with these instructions | |
| 319 // only necessary for this phase. | |
| 320 removeRangeConversion(); | |
| 321 } | |
| 322 | |
| 323 void removeRangeConversion() { | |
| 324 conversions.forEach((HRangeConversion instruction) { | |
| 325 instruction.block.rewrite(instruction, instruction.inputs[0]);; | |
| 326 instruction.block.remove(instruction); | |
| 327 }); | |
| 328 } | |
| 329 | |
| 330 void visitBasicBlock(HBasicBlock block) { | |
| 331 | |
| 332 void visit(HInstruction instruction) { | |
| 333 Range range = instruction.accept(this); | |
| 334 if (instruction.isInteger(types)) { | |
| 335 assert(range != null); | |
| 336 ranges[instruction] = range; | |
| 337 } | |
| 338 } | |
| 339 | |
| 340 block.forEachPhi(visit); | |
| 341 block.forEachInstruction(visit); | |
| 342 } | |
| 343 | |
| 344 Range visitInstruction(HInstruction instruction) { | |
| 345 return const Range(const MinValue(), const MaxValue()); | |
|
Søren Gjesse
2012/09/26 09:08:24
What is the difference between returning null and
ngeoffray
2012/09/26 09:33:26
It's to make sure an instruction that has type int
Søren Gjesse
2012/09/26 14:00:15
I think returning the [min,max] range instead of n
| |
| 346 } | |
| 347 | |
| 348 Range visitParameterValue(HParameterValue parameter) { | |
| 349 if (!parameter.isInteger(types)) return null; | |
| 350 Value value = new InstructionValue(parameter); | |
| 351 return new Range(value, value); | |
| 352 } | |
| 353 | |
| 354 Range visitPhi(HPhi phi) { | |
| 355 if (!phi.isInteger(types)) return null; | |
| 356 if (phi.block.isLoopHeader()) { | |
| 357 Range range = tryInferLoopPhiRange(phi); | |
| 358 if (range == null) return visitInstruction(phi); | |
| 359 return range; | |
| 360 } | |
| 361 | |
| 362 Range range = ranges[phi.inputs[0]]; | |
| 363 for (int i = 1; i < phi.inputs.length; i++) { | |
| 364 range = range.union(ranges[phi.inputs[i]]); | |
| 365 } | |
| 366 return range; | |
| 367 } | |
| 368 | |
| 369 Range tryInferLoopPhiRange(HPhi phi) { | |
| 370 HInstruction update = phi.inputs[1]; | |
| 371 return update.accept(new LoopUpdateRecognizer(phi, ranges, types)); | |
| 372 } | |
| 373 | |
| 374 Range visitConstant(HConstant constant) { | |
| 375 if (!constant.isInteger(types)) return null; | |
| 376 Value value = new IntValue(constant.constant.value); | |
| 377 return new Range(value, value); | |
| 378 } | |
| 379 | |
| 380 Range visitInvokeInterceptor(HInvokeInterceptor interceptor) { | |
| 381 if (!interceptor.isInteger(types)) return null; | |
| 382 if (!interceptor.isLengthGetterOnStringOrArray(types)) { | |
| 383 return visitInstruction(interceptor); | |
| 384 } | |
| 385 LengthValue value = new LengthValue(interceptor); | |
| 386 return new Range(value, value); | |
| 387 } | |
| 388 | |
| 389 bool handleBoundsCheck(HBoundsCheck check) { | |
| 390 Range indexRange = ranges[check.index]; | |
| 391 Range lengthRange = ranges[check.length]; | |
| 392 Value maxIndex = lengthRange.upper - const IntValue(1); | |
| 393 bool belowLength = maxIndex != const MaxValue() | |
| 394 && indexRange.upper.min(maxIndex) == indexRange.upper; | |
| 395 if (indexRange.isPositive() && belowLength) { | |
| 396 check.block.rewrite(check, check.index); | |
| 397 check.block.remove(check); | |
| 398 return true; | |
| 399 } else if (indexRange.isNegative() || lengthRange.isLessThan(indexRange)) { | |
| 400 check.staticChecks = HBoundsCheck.ALWAYS_FALSE; | |
| 401 } else if (indexRange.isPositive()) { | |
| 402 check.staticChecks = HBoundsCheck.ALWAYS_ABOVE_ZERO; | |
| 403 } else if (belowLength) { | |
| 404 check.staticChecks = HBoundsCheck.ALWAYS_BELOW_LENGTH; | |
| 405 } | |
| 406 return false; | |
| 407 } | |
| 408 | |
| 409 Range visitBoundsCheck(HBoundsCheck check) { | |
| 410 HInstruction next = check.next; | |
| 411 Range indexRange = ranges[check.index]; | |
| 412 Range lengthRange = ranges[check.length]; | |
| 413 if (handleBoundsCheck(check)) return indexRange; | |
| 414 // Update the range of the index. | |
| 415 Range newIndexRange = indexRange.intersection(lengthRange); | |
| 416 if (indexRange == newIndexRange) return indexRange; | |
| 417 HInstruction instruction = createRangeConversion(check.next, check.index); | |
| 418 ranges[instruction] = newIndexRange; | |
| 419 return newIndexRange; | |
| 420 } | |
| 421 | |
| 422 Range visitLess(HLess less) { | |
| 423 HInstruction right = less.right; | |
| 424 HInstruction left = less.left; | |
| 425 if (!left.isInteger(types)) return null; | |
| 426 if (!right.isInteger(types)) return null; | |
| 427 if (ranges[left].isLessThan(ranges[right])) { | |
| 428 less.block.rewrite(less, graph.addConstantBool(true, constantSystem)); | |
| 429 less.block.remove(less); | |
| 430 return null; | |
| 431 } | |
| 432 if (ranges[right].isLessThan(ranges[left])) { | |
| 433 less.block.rewrite(less, graph.addConstantBool(false, constantSystem)); | |
| 434 less.block.remove(less); | |
| 435 return null; | |
| 436 } | |
|
Søren Gjesse
2012/09/26 09:08:24
Missing explicit return.
ngeoffray
2012/09/26 09:33:26
Done.
| |
| 437 } | |
| 438 | |
| 439 Range handleBinaryOperation(HBinaryArithmetic instruction) { | |
| 440 if (!instruction.isInteger(types)) return null; | |
| 441 return instruction.operation(constantSystem).apply( | |
| 442 ranges[instruction.left], ranges[instruction.right]); | |
| 443 } | |
| 444 | |
| 445 Range visitAdd(HAdd add) { | |
| 446 return handleBinaryOperation(add); | |
| 447 } | |
| 448 | |
| 449 Range visitSubtract(HSubtract sub) { | |
| 450 return handleBinaryOperation(sub); | |
| 451 } | |
| 452 | |
| 453 Range visitBitAnd(HBitAnd node) { | |
| 454 if (!node.isInteger(types)) return null; | |
| 455 HInstruction right = node.right; | |
| 456 HInstruction left = node.left; | |
| 457 if (left.isInteger(types) && right.isInteger(types)) { | |
| 458 return ranges[left] & ranges[right]; | |
| 459 } | |
| 460 | |
| 461 Range tryComputeRange(HInstruction instruction) { | |
| 462 Range range = ranges[instruction]; | |
| 463 if (range.isPositive()) { | |
| 464 return new Range(const IntValue(0), range.upper); | |
| 465 } else if (range.isNegative()) { | |
| 466 return new Range(range.lower, const IntValue(0)); | |
| 467 } | |
| 468 return visitInstruction(node); | |
| 469 } | |
| 470 | |
| 471 if (left.isInteger(types)) { | |
| 472 return tryComputeRange(left); | |
| 473 } else if (right.isInteger(types)) { | |
| 474 return tryComputeRange(right); | |
| 475 } | |
| 476 return visitInstruction(node); | |
| 477 } | |
| 478 | |
| 479 Range visitCheck(HCheck instruction) { | |
| 480 if (ranges[instruction.checkedInput] == null) { | |
| 481 return visitInstruction(instruction); | |
| 482 } | |
| 483 return ranges[instruction.checkedInput]; | |
| 484 } | |
| 485 | |
| 486 HInstruction createRangeConversion(HInstruction cursor, | |
| 487 HInstruction instruction) { | |
| 488 HRangeConversion newInstruction = new HRangeConversion(instruction); | |
| 489 conversions.add(newInstruction); | |
| 490 cursor.block.addBefore(cursor, newInstruction); | |
| 491 // Update the users of the instruction dominated by [cursor] to | |
| 492 // use the new instruction, that has an narrower range. | |
| 493 Set<HInstruction> dominatedUsers = instruction.dominatedUsers(cursor); | |
| 494 for (HInstruction user in dominatedUsers) { | |
| 495 user.changeUse(instruction, newInstruction); | |
| 496 } | |
| 497 return newInstruction; | |
| 498 } | |
| 499 | |
| 500 Range visitConditionalBranch(HConditionalBranch branch) { | |
| 501 var condition = branch.condition; | |
| 502 // TODO(ngeoffray): Handle more condition kinds. | |
| 503 if (condition is !HLess) return null; | |
| 504 HInstruction right = condition.right; | |
| 505 HInstruction left = condition.left; | |
| 506 if (!left.isInteger(types)) return null; | |
| 507 if (!right.isInteger(types)) return null; | |
| 508 | |
| 509 // Update the true branch to use a narrower range for [left]. | |
| 510 // TODO(ngeoffray): Also do it for [right]. | |
| 511 HInstruction instruction = | |
| 512 createRangeConversion(branch.trueBranch.first, left); | |
| 513 Range range = new Range( | |
| 514 const MinValue(), ranges[right].upper - const IntValue(1)); | |
| 515 range = range.intersection(ranges[left]); | |
| 516 ranges[instruction] = range; | |
| 517 | |
| 518 // Update the false branch to use a narrower range for [left]. | |
| 519 // TODO(ngeoffray): Also do it for [right]. | |
| 520 instruction = createRangeConversion(branch.falseBranch.first, left); | |
| 521 range = new Range(ranges[right].lower, const MaxValue()); | |
| 522 range = range.intersection(ranges[left]); | |
| 523 ranges[instruction] = range; | |
| 524 | |
| 525 return null; | |
| 526 } | |
| 527 | |
| 528 Range visitRangeConversion(HRangeConversion conversion) { | |
| 529 return ranges[conversion]; | |
| 530 } | |
| 531 } | |
| 532 | |
| 533 /** | |
| 534 * Recognizes a number of patterns in a loop update instruction and | |
| 535 * tries to infer a range for the loop phi. | |
| 536 */ | |
| 537 class LoopUpdateRecognizer extends HBaseVisitor { | |
| 538 final HPhi loopPhi; | |
| 539 final Map<HInstruction, Range> ranges; | |
| 540 final HTypeMap types; | |
| 541 LoopUpdateRecognizer(this.loopPhi, this.ranges, this.types); | |
| 542 | |
| 543 Range visitAdd(HAdd operation) { | |
| 544 Range range = getRangeForRecognizableOperation(operation); | |
| 545 if (range == null) return null; | |
| 546 Range initial = ranges[loopPhi.inputs[0]]; | |
| 547 if (range.isPositive()) { | |
| 548 return new Range(initial.lower, const MaxValue()); | |
| 549 } else if (range.isNegative()) { | |
| 550 return new Range(const MinValue(), initial.upper); | |
| 551 } | |
|
Søren Gjesse
2012/09/26 09:08:24
Missing explicit "return null" here.
ngeoffray
2012/09/26 09:33:26
Done.
| |
| 552 } | |
| 553 | |
| 554 Range visitSubtract(HSubtract operation) { | |
| 555 Range range = getRangeForRecognizableOperation(operation); | |
| 556 if (range == null) return null; | |
| 557 Range initial = ranges[loopPhi.inputs[0]]; | |
| 558 if (range.isPositive()) { | |
| 559 return new Range(const MinValue(), initial.upper); | |
| 560 } else if (range.isNegative()) { | |
| 561 return new Range(initial.lower, const MaxValue()); | |
| 562 } | |
| 563 return null; | |
| 564 } | |
| 565 | |
| 566 Range visitPhi(HPhi phi) { | |
| 567 // If one of the inputs is the loop phi, then we're only | |
| 568 // interested in the other input: a loop phi feeding itself means | |
| 569 // it is not being updated. | |
| 570 if (unwrap(phi.inputs[0]) == loopPhi) return phi.inputs[1].accept(this); | |
| 571 if (unwrap(phi.inputs[1]) == loopPhi) return phi.inputs[0].accept(this); | |
| 572 assert(phi.inputs.length == 2); | |
|
Søren Gjesse
2012/09/26 09:08:24
Ditto.
ngeoffray
2012/09/26 09:33:26
Done.
| |
| 573 } | |
| 574 | |
| 575 Range getRangeForRecognizableOperation(HBinaryArithmetic operation) { | |
| 576 if (!operation.left.isInteger(types)) return null; | |
| 577 if (!operation.right.isInteger(types)) return null; | |
| 578 HInstruction left = unwrap(operation.left); | |
| 579 HInstruction right = unwrap(operation.right); | |
| 580 // We only recognize operations that operate on the loop phi. | |
| 581 bool isLeftLoopPhi = (left == loopPhi); | |
| 582 bool isRightLoopPhi = (right == loopPhi); | |
| 583 if (!isLeftLoopPhi && !isRightLoopPhi) return null; | |
| 584 | |
| 585 var other = isLeftLoopPhi ? right : left; | |
| 586 // If the analysis already computed range for the update, use it. | |
| 587 if (ranges[other] != null) return ranges[other]; | |
| 588 | |
| 589 // We currently only handle constants in updates if the | |
| 590 // update does not have a range. | |
| 591 if (other.isConstant()) { | |
| 592 Value value = new IntValue(other.constant.value); | |
| 593 return new Range(value, value); | |
| 594 } | |
| 595 return null; | |
| 596 } | |
| 597 | |
| 598 /** | |
| 599 * [HCheck] instructions may check the loop phi. Since we only | |
| 600 * recognize updated on the loop phi, we must [unwrap] the [HCheck] | |
|
Søren Gjesse
2012/09/26 09:08:24
updated -> updates
ngeoffray
2012/09/26 09:33:26
Done.
| |
| 601 * instruction to check if it references the loop phi. | |
| 602 */ | |
| 603 HInstruction unwrap(instruction) { | |
| 604 if (instruction is HCheck) return unwrap(instruction.checkedInput); | |
| 605 // [HPhi] might have two different [HCheck] instructions as | |
| 606 // inputs, checking the same instruction. | |
| 607 if (instruction is HPhi && !instruction.block.isLoopHeader()) { | |
| 608 HInstruction result = unwrap(instruction.inputs[0]); | |
| 609 for (int i = 1; i < instruction.inputs.length; i++) { | |
| 610 if (result != unwrap(instruction.inputs[i])) return instruction; | |
| 611 } | |
| 612 return result; | |
| 613 } | |
| 614 return instruction; | |
| 615 } | |
| 616 } | |
| OLD | NEW |