1 // Copyright (c) 2016, the R8 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 package com.android.tools.r8.ir.regalloc; 5 6 import static com.android.tools.r8.dex.Constants.U16BIT_MAX; 7 import static com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator.NO_REGISTER; 8 9 import com.android.tools.r8.dex.Constants; 10 import com.android.tools.r8.ir.code.MoveType; 11 import com.android.tools.r8.ir.code.Value; 12 import com.android.tools.r8.utils.CfgPrinter; 13 import java.util.ArrayList; 14 import java.util.Iterator; 15 import java.util.List; 16 import java.util.TreeSet; 17 18 public class LiveIntervals { 19 20 private final Value value; 21 private LiveIntervals nextConsecutive; 22 private LiveIntervals previousConsecutive; 23 private LiveIntervals splitParent; 24 private List<LiveIntervals> splitChildren = new ArrayList<>(); 25 private List<LiveRange> ranges = new ArrayList<>(); 26 private TreeSet<LiveIntervalsUse> uses = new TreeSet<>(); 27 private int numberOfConsecutiveRegisters = -1; 28 private int register = NO_REGISTER; 29 private LiveIntervals hint; 30 private boolean spilled = false; 31 32 // Only registers up to and including the registerLimit are allowed for this interval. 33 private int registerLimit = U16BIT_MAX; 34 35 // Max register used for any of the non-spilled splits for these live intervals or for any of the 36 // live intervals that this live interval is connected to by phi moves. This is used to 37 // conservatively determine if it is safe to use rematerialization for this value. 38 private int maxNonSpilledRegister = NO_REGISTER; 39 LiveIntervals(Value value)40 LiveIntervals(Value value) { 41 this.value = value; 42 splitParent = this; 43 value.setLiveIntervals(this); 44 } 45 LiveIntervals(LiveIntervals splitParent)46 LiveIntervals(LiveIntervals splitParent) { 47 this.splitParent = splitParent; 48 value = splitParent.value; 49 } 50 toInstructionPosition(int position)51 private int toInstructionPosition(int position) { 52 return position % 2 == 0 ? position : position + 1; 53 } 54 toGapPosition(int position)55 private int toGapPosition(int position) { 56 return position % 2 == 1 ? position : position - 1; 57 } 58 getType()59 public MoveType getType() { 60 return value.outType(); 61 } 62 getValue()63 public Value getValue() { 64 return value; 65 } 66 requiredRegisters()67 public int requiredRegisters() { 68 return getType() == MoveType.WIDE ? 2 : 1; 69 } 70 setHint(LiveIntervals intervals)71 public void setHint(LiveIntervals intervals) { 72 hint = intervals; 73 } 74 getHint()75 public LiveIntervals getHint() { 76 return hint; 77 } 78 setSpilled(boolean value)79 public void setSpilled(boolean value) { 80 spilled = value; 81 } 82 isSpilled()83 public boolean isSpilled() { 84 return spilled; 85 } 86 isRematerializable(LinearScanRegisterAllocator registerAllocator)87 public boolean isRematerializable(LinearScanRegisterAllocator registerAllocator) { 88 if (!value.isConstant()) { 89 return false; 90 } 91 // If one of the non-spilled splits uses a register that is higher than U8BIT_MAX we cannot 92 // rematerialize it using a ConstNumber instruction and we use spill moves instead of 93 // rematerialization. We use this check both before and after we have computed the set 94 // of unused registers. We therefore have to be careful to use the same max number for 95 // these computations. We use the unadjusted real register number to make sure that 96 // isRematerializable for the same intervals does not change from one phase of 97 // compilation to the next. 98 int max = registerAllocator.unadjustedRealRegisterFromAllocated(getMaxNonSpilledRegister()); 99 return max < Constants.U8BIT_MAX; 100 } 101 isSpilledAndRematerializable(LinearScanRegisterAllocator allocator)102 public boolean isSpilledAndRematerializable(LinearScanRegisterAllocator allocator) { 103 return isSpilled() && isRematerializable(allocator); 104 } 105 link(LiveIntervals next)106 public void link(LiveIntervals next) { 107 assert numberOfConsecutiveRegisters == -1; 108 nextConsecutive = next; 109 next.previousConsecutive = this; 110 } 111 isLinked()112 public boolean isLinked() { 113 return splitParent.previousConsecutive != null || splitParent.nextConsecutive != null; 114 } 115 isArgumentInterval()116 public boolean isArgumentInterval() { 117 // TODO(ager): This is pretty indirect. We might want to have a more direct indication. 118 LiveIntervals current = splitParent; 119 while (current.previousConsecutive != null) { 120 current = current.previousConsecutive; 121 } 122 return current.ranges.get(0).isInfinite(); 123 } 124 125 getStartOfConsecutive()126 public LiveIntervals getStartOfConsecutive() { 127 LiveIntervals current = this; 128 while (current.previousConsecutive != null) { 129 current = current.previousConsecutive; 130 } 131 return current; 132 } 133 getNextConsecutive()134 public LiveIntervals getNextConsecutive() { 135 return nextConsecutive; 136 } 137 getPreviousConsecutive()138 public LiveIntervals getPreviousConsecutive() { 139 return previousConsecutive; 140 } 141 numberOfConsecutiveRegisters()142 public int numberOfConsecutiveRegisters() { 143 LiveIntervals start = getStartOfConsecutive(); 144 if (start.numberOfConsecutiveRegisters != -1) { 145 assert start.numberOfConsecutiveRegisters == computeNumberOfConsecutiveRegisters(); 146 return start.numberOfConsecutiveRegisters; 147 } 148 return computeNumberOfConsecutiveRegisters(); 149 } 150 computeNumberOfConsecutiveRegisters()151 private int computeNumberOfConsecutiveRegisters() { 152 LiveIntervals start = getStartOfConsecutive(); 153 int result = 0; 154 for (LiveIntervals current = start; 155 current != null; 156 current = current.nextConsecutive) { 157 result += current.requiredRegisters(); 158 } 159 start.numberOfConsecutiveRegisters = result; 160 return result; 161 } 162 hasSplits()163 public boolean hasSplits() { 164 return splitChildren.size() != 0; 165 } 166 getSplitChildren()167 public List<LiveIntervals> getSplitChildren() { 168 return splitChildren; 169 } 170 getSplitParent()171 public LiveIntervals getSplitParent() { 172 return splitParent; 173 } 174 175 /** 176 * Add a live range to the intervals. 177 * 178 * @param range the range to add 179 */ addRange(LiveRange range)180 public void addRange(LiveRange range) { 181 boolean added = tryAddRange(range); 182 assert added; 183 } 184 tryAddRange(LiveRange range)185 private boolean tryAddRange(LiveRange range) { 186 if (ranges.size() > 0) { 187 LiveRange lastRange = ranges.get(ranges.size() - 1); 188 if (lastRange.isInfinite()) { 189 return false; 190 } 191 int rangeStartInstructionPosition = toInstructionPosition(range.start); 192 int lastRangeEndInstructionPosition = toInstructionPosition(lastRange.end); 193 if (lastRangeEndInstructionPosition > rangeStartInstructionPosition) { 194 return false; 195 } 196 if (lastRangeEndInstructionPosition == rangeStartInstructionPosition) { 197 lastRange.end = range.end; 198 return true; 199 } 200 } 201 ranges.add(range); 202 return true; 203 } 204 205 /** 206 * Record a use for this interval. 207 */ addUse(LiveIntervalsUse use)208 public void addUse(LiveIntervalsUse use) { 209 uses.add(use); 210 updateRegisterConstraint(use.getLimit()); 211 } 212 updateRegisterConstraint(int constraint)213 public void updateRegisterConstraint(int constraint) { 214 registerLimit = Math.min(registerLimit, constraint); 215 } 216 getUses()217 public TreeSet<LiveIntervalsUse> getUses() { 218 return uses; 219 } 220 getRanges()221 public List<LiveRange> getRanges() { 222 return ranges; 223 } 224 getStart()225 public int getStart() { 226 assert !ranges.isEmpty(); 227 return ranges.get(0).start; 228 } 229 getEnd()230 public int getEnd() { 231 assert !ranges.isEmpty(); 232 return ranges.get(ranges.size() - 1).end; 233 } 234 getRegister()235 public int getRegister() { 236 return register; 237 } 238 getRegisterLimit()239 public int getRegisterLimit() { 240 return registerLimit; 241 } 242 setRegister(int n)243 public void setRegister(int n) { 244 assert register == NO_REGISTER || register == n; 245 register = n; 246 } 247 computeMaxNonSpilledRegister()248 private int computeMaxNonSpilledRegister() { 249 assert splitParent == this; 250 assert maxNonSpilledRegister == NO_REGISTER; 251 if (!isSpilled()) { 252 maxNonSpilledRegister = getRegister(); 253 } 254 for (LiveIntervals child : splitChildren) { 255 if (!child.isSpilled()) { 256 maxNonSpilledRegister = Math.max(maxNonSpilledRegister, child.getRegister()); 257 } 258 } 259 return maxNonSpilledRegister; 260 } 261 setMaxNonSpilledRegister(int i)262 public void setMaxNonSpilledRegister(int i) { 263 assert i >= splitParent.maxNonSpilledRegister; 264 splitParent.maxNonSpilledRegister = i; 265 } 266 getMaxNonSpilledRegister()267 public int getMaxNonSpilledRegister() { 268 if (splitParent.maxNonSpilledRegister != NO_REGISTER) { 269 return splitParent.maxNonSpilledRegister; 270 } 271 return splitParent.computeMaxNonSpilledRegister(); 272 } 273 usesRegister(int n)274 public boolean usesRegister(int n) { 275 if (register == n || (getType() == MoveType.WIDE && register + 1 == n)) { 276 return true; 277 } 278 return false; 279 } 280 clearRegisterAssignment()281 public void clearRegisterAssignment() { 282 register = NO_REGISTER; 283 hint = null; 284 } 285 overlapsPosition(int position)286 public boolean overlapsPosition(int position) { 287 for (LiveRange range : ranges) { 288 if (range.start > position) { 289 // Ranges are sorted. When a range starts after position there is no overlap. 290 return false; 291 } 292 if (position < range.end) { 293 return true; 294 } 295 } 296 return false; 297 } 298 overlaps(LiveIntervals other)299 public boolean overlaps(LiveIntervals other) { 300 return nextOverlap(other) != -1; 301 } 302 nextOverlap(LiveIntervals other)303 public int nextOverlap(LiveIntervals other) { 304 Iterator<LiveRange> it = other.ranges.iterator(); 305 LiveRange otherRange = it.next(); 306 for (LiveRange range : ranges) { 307 while (otherRange.end <= range.start) { 308 if (!it.hasNext()) { 309 return -1; 310 } 311 otherRange = it.next(); 312 } 313 if (otherRange.start < range.end) { 314 return otherRange.start; 315 } 316 } 317 return -1; 318 } 319 firstUseAfter(int unhandledStart)320 public int firstUseAfter(int unhandledStart) { 321 for (LiveIntervalsUse use : uses) { 322 if (use.getPosition() >= unhandledStart) { 323 return use.getPosition(); 324 } 325 } 326 return Integer.MAX_VALUE; 327 } 328 getFirstUse()329 public int getFirstUse() { 330 return uses.first().getPosition(); 331 } 332 firstUseWithConstraint()333 public LiveIntervalsUse firstUseWithConstraint() { 334 for (LiveIntervalsUse use : uses) { 335 if (use.hasConstraint()) { 336 return use; 337 } 338 } 339 return null; 340 } 341 splitBefore(int start)342 public LiveIntervals splitBefore(int start) { 343 if (toInstructionPosition(start) == toInstructionPosition(getStart())) { 344 assert uses.size() == 0 || getFirstUse() != start; 345 register = NO_REGISTER; 346 return this; 347 } 348 start = toGapPosition(start); 349 LiveIntervals splitChild = new LiveIntervals(splitParent); 350 splitParent.splitChildren.add(splitChild); 351 List<LiveRange> beforeSplit = new ArrayList<>(); 352 List<LiveRange> afterSplit = new ArrayList<>(); 353 if (start == getEnd()) { 354 beforeSplit = ranges; 355 afterSplit.add(new LiveRange(start, start)); 356 } else { 357 int rangeToSplitIndex = 0; 358 for (; rangeToSplitIndex < ranges.size(); rangeToSplitIndex++) { 359 LiveRange range = ranges.get(rangeToSplitIndex); 360 if (range.start <= start && range.end > start) { 361 break; 362 } 363 if (range.start > start) { 364 break; 365 } 366 } 367 LiveRange rangeToSplit = ranges.get(rangeToSplitIndex); 368 beforeSplit.addAll(ranges.subList(0, rangeToSplitIndex)); 369 if (rangeToSplit.start < start) { 370 beforeSplit.add(new LiveRange(rangeToSplit.start, start)); 371 afterSplit.add(new LiveRange(start, rangeToSplit.end)); 372 } else { 373 afterSplit.add(rangeToSplit); 374 } 375 afterSplit.addAll(ranges.subList(rangeToSplitIndex + 1, ranges.size())); 376 } 377 splitChild.ranges = afterSplit; 378 ranges = beforeSplit; 379 while (!uses.isEmpty() && uses.last().getPosition() >= start) { 380 splitChild.addUse(uses.pollLast()); 381 } 382 // Recompute limit after having removed uses from this interval. 383 recomputeLimit(); 384 assert !ranges.isEmpty(); 385 assert !splitChild.ranges.isEmpty(); 386 return splitChild; 387 } 388 recomputeLimit()389 private void recomputeLimit() { 390 registerLimit = U16BIT_MAX; 391 for (LiveIntervalsUse use : uses) { 392 updateRegisterConstraint(use.getLimit()); 393 } 394 } 395 getSplitCovering(int instructionNumber)396 public LiveIntervals getSplitCovering(int instructionNumber) { 397 assert getSplitParent() == this; 398 // Check if this interval itself is covering the instruction. 399 if (getStart() <= instructionNumber && getEnd() > instructionNumber) { 400 return this; 401 } 402 // If the instruction number is not in this intervals range, we go through all split children. 403 // If we do not find a child that contains the instruction number we return the interval 404 // whose end is the instruction number. This is needed when transitioning values across 405 // control-flow boundaries. 406 LiveIntervals matchingEnd = getEnd() == instructionNumber ? this : null; 407 for (LiveIntervals splitChild : splitChildren) { 408 if (splitChild.getStart() <= instructionNumber && splitChild.getEnd() > instructionNumber) { 409 return splitChild; 410 } 411 if (splitChild.getEnd() == instructionNumber) { 412 matchingEnd = splitChild; 413 } 414 } 415 if (matchingEnd != null) { 416 return matchingEnd; 417 } 418 assert false : "Couldn't find split covering instruction position."; 419 return null; 420 } 421 isConstantNumberInterval()422 public boolean isConstantNumberInterval() { 423 return value.definition != null && value.isConstant(); 424 } 425 numberOfUsesWithConstraint()426 public int numberOfUsesWithConstraint() { 427 int count = 0; 428 for (LiveIntervalsUse use : getUses()) { 429 if (use.hasConstraint()) { 430 count++; 431 } 432 } 433 return count; 434 } 435 436 @Override toString()437 public String toString() { 438 StringBuilder builder = new StringBuilder(); 439 builder.append("(cons "); 440 // Use the field here to avoid toString to have side effects. 441 builder.append(numberOfConsecutiveRegisters); 442 builder.append("): "); 443 for (LiveRange range : getRanges()) { 444 builder.append(range); 445 builder.append(" "); 446 } 447 builder.append("\n"); 448 return builder.toString(); 449 } 450 toAscciArtString()451 public String toAscciArtString() { 452 StringBuilder builder = new StringBuilder(); 453 int current = 0; 454 for (LiveRange range : getRanges()) { 455 if (range.isInfinite()) { 456 builder.append("--- infinite ---..."); 457 break; 458 } 459 for (; current < range.start; current++) { 460 builder.append(" "); 461 } 462 for (; current < range.end; current++) { 463 builder.append("-"); 464 } 465 } 466 return builder.toString(); 467 } 468 print(CfgPrinter printer, int number, int parentNumber)469 public void print(CfgPrinter printer, int number, int parentNumber) { 470 printer.append(number * 10000 + register) // range number 471 .sp().append("object") // range type 472 .sp().append(parentNumber * 10000 + getSplitParent().getRegister()) // split parent 473 .sp().append(-1); // hint 474 for (LiveRange range : getRanges()) { 475 printer.sp().append(range.toString()); 476 } 477 for (LiveIntervalsUse use : getUses()) { 478 printer.sp().append(use.getPosition()).sp().append("M"); 479 } 480 printer.append(" \"\"").ln(); 481 int delta = 0; 482 for (LiveIntervals splitChild : splitChildren) { 483 delta += 10000; 484 splitChild.print(printer, number + delta, number); 485 } 486 } 487 } 488