1 /* 2 * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.lir.alloc.trace.lsra; 24 25 import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue; 26 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable; 27 import static jdk.vm.ci.code.CodeUtil.isOdd; 28 import static jdk.vm.ci.code.ValueUtil.asRegister; 29 import static jdk.vm.ci.code.ValueUtil.isRegister; 30 31 import java.util.ArrayList; 32 import java.util.Arrays; 33 import java.util.BitSet; 34 import java.util.List; 35 36 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig.AllocatableRegisters; 37 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; 38 import org.graalvm.compiler.core.common.util.Util; 39 import org.graalvm.compiler.debug.Debug; 40 import org.graalvm.compiler.debug.GraalError; 41 import org.graalvm.compiler.debug.Indent; 42 import org.graalvm.compiler.lir.LIRInstruction; 43 import org.graalvm.compiler.lir.StandardOp.BlockEndOp; 44 import org.graalvm.compiler.lir.StandardOp.LabelOp; 45 import org.graalvm.compiler.lir.StandardOp.ValueMoveOp; 46 import org.graalvm.compiler.lir.alloc.OutOfRegistersException; 47 import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.RegisterPriority; 48 import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.SpillState; 49 import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan; 50 51 import jdk.vm.ci.code.Register; 52 import jdk.vm.ci.meta.Value; 53 54 /** 55 */ 56 final class TraceLinearScanWalker extends TraceIntervalWalker { 57 58 private Register[] availableRegs; 59 60 private final int[] usePos; 61 private final int[] blockPos; 62 private final BitSet isInMemory; 63 64 private List<TraceInterval>[] spillIntervals; 65 66 private TraceLocalMoveResolver moveResolver; // for ordering spill moves 67 68 private int minReg; 69 70 private int maxReg; 71 72 /** 73 * Only 10% of the lists in {@link #spillIntervals} are actually used. But when they are used, 74 * they can grow quite long. The maximum length observed was 45 (all numbers taken from a 75 * bootstrap run of Graal). Therefore, we initialize {@link #spillIntervals} with this marker 76 * value, and allocate a "real" list only on demand in {@link #setUsePos}. 77 */ 78 private static final List<TraceInterval> EMPTY_LIST = new ArrayList<>(0); 79 80 // accessors mapped to same functions in class LinearScan 81 private int blockCount() { 82 return allocator.blockCount(); 83 } 84 85 private AbstractBlockBase<?> blockAt(int idx) { 86 return allocator.blockAt(idx); 87 } 88 89 @SuppressWarnings("unused") 90 private AbstractBlockBase<?> blockOfOpWithId(int opId) { 91 return allocator.blockForId(opId); 92 } 93 94 TraceLinearScanWalker(TraceLinearScan allocator, FixedInterval unhandledFixedFirst, TraceInterval unhandledAnyFirst) { 95 super(allocator, unhandledFixedFirst, unhandledAnyFirst); 96 97 moveResolver = allocator.createMoveResolver(); 98 int numRegs = allocator.getRegisters().size(); 99 spillIntervals = Util.uncheckedCast(new List<?>[numRegs]); 100 for (int i = 0; i < numRegs; i++) { 101 spillIntervals[i] = EMPTY_LIST; 102 } 103 usePos = new int[numRegs]; 104 blockPos = new int[numRegs]; 105 isInMemory = new BitSet(numRegs); 106 } 107 108 private void initUseLists(boolean onlyProcessUsePos) { 109 for (Register register : availableRegs) { 110 int i = register.number; 111 usePos[i] = Integer.MAX_VALUE; 112 113 if (!onlyProcessUsePos) { 114 blockPos[i] = Integer.MAX_VALUE; 115 spillIntervals[i].clear(); 116 isInMemory.clear(i); 117 } 118 } 119 } 120 121 private int maxRegisterNumber() { 122 return maxReg; 123 } 124 125 private int minRegisterNumber() { 126 return minReg; 127 } 128 129 private boolean isRegisterInRange(int reg) { 130 return reg >= minRegisterNumber() && reg <= maxRegisterNumber(); 131 } 132 133 private void excludeFromUse(IntervalHint i) { 134 Value location = i.location(); 135 int i1 = asRegister(location).number; 136 if (isRegisterInRange(i1)) { 137 usePos[i1] = 0; 138 } 139 } 140 141 private void setUsePos(TraceInterval interval, int usePos, boolean onlyProcessUsePos) { 142 if (usePos != -1) { 143 assert usePos != 0 : "must use excludeFromUse to set usePos to 0"; 144 int i = asRegister(interval.location()).number; 145 if (isRegisterInRange(i)) { 146 if (this.usePos[i] > usePos) { 147 this.usePos[i] = usePos; 148 } 149 if (!onlyProcessUsePos) { 150 List<TraceInterval> list = spillIntervals[i]; 151 if (list == EMPTY_LIST) { 152 list = new ArrayList<>(2); 153 spillIntervals[i] = list; 154 } 155 list.add(interval); 156 // set is in memory flag 157 if (interval.inMemoryAt(currentPosition)) { 158 isInMemory.set(i); 159 } 160 } 161 } 162 } 163 } 164 165 private void setUsePos(FixedInterval interval, int usePos, boolean onlyProcessUsePos) { 166 assert onlyProcessUsePos; 167 if (usePos != -1) { 168 assert usePos != 0 : "must use excludeFromUse to set usePos to 0"; 169 int i = asRegister(interval.location()).number; 170 if (isRegisterInRange(i)) { 171 if (this.usePos[i] > usePos) { 172 this.usePos[i] = usePos; 173 } 174 } 175 } 176 } 177 178 private void setBlockPos(IntervalHint i, int blockPos) { 179 if (blockPos != -1) { 180 int reg = asRegister(i.location()).number; 181 if (isRegisterInRange(reg)) { 182 if (this.blockPos[reg] > blockPos) { 183 this.blockPos[reg] = blockPos; 184 } 185 if (usePos[reg] > blockPos) { 186 usePos[reg] = blockPos; 187 } 188 } 189 } 190 } 191 192 private void freeExcludeActiveFixed() { 193 FixedInterval interval = activeFixedList.getFixed(); 194 while (interval != FixedInterval.EndMarker) { 195 assert isRegister(interval.location()) : "active interval must have a register assigned"; 196 excludeFromUse(interval); 197 interval = interval.next; 198 } 199 } 200 201 private void freeExcludeActiveAny() { 202 TraceInterval interval = activeAnyList.getAny(); 203 while (interval != TraceInterval.EndMarker) { 204 assert isRegister(interval.location()) : "active interval must have a register assigned"; 205 excludeFromUse(interval); 206 interval = interval.next; 207 } 208 } 209 210 private void freeCollectInactiveFixed(TraceInterval current) { 211 FixedInterval interval = inactiveFixedList.getFixed(); 212 while (interval != FixedInterval.EndMarker) { 213 if (current.to() <= interval.from()) { 214 assert interval.intersectsAt(current) == -1 : "must not intersect"; 215 setUsePos(interval, interval.from(), true); 216 } else { 217 setUsePos(interval, interval.currentIntersectsAt(current), true); 218 } 219 interval = interval.next; 220 } 221 } 222 223 private void spillExcludeActiveFixed() { 224 FixedInterval interval = activeFixedList.getFixed(); 225 while (interval != FixedInterval.EndMarker) { 226 excludeFromUse(interval); 227 interval = interval.next; 228 } 229 } 230 231 private void spillBlockInactiveFixed(TraceInterval current) { 232 FixedInterval interval = inactiveFixedList.getFixed(); 233 while (interval != FixedInterval.EndMarker) { 234 if (current.to() > interval.currentFrom()) { 235 setBlockPos(interval, interval.currentIntersectsAt(current)); 236 } else { 237 assert interval.currentIntersectsAt(current) == -1 : "invalid optimization: intervals intersect"; 238 } 239 240 interval = interval.next; 241 } 242 } 243 244 private void spillCollectActiveAny(RegisterPriority registerPriority) { 245 TraceInterval interval = activeAnyList.getAny(); 246 while (interval != TraceInterval.EndMarker) { 247 setUsePos(interval, Math.min(interval.nextUsage(registerPriority, currentPosition), interval.to()), false); 248 interval = interval.next; 249 } 250 } 251 252 @SuppressWarnings("unused") 253 private int insertIdAtBasicBlockBoundary(int opId) { 254 assert allocator.isBlockBegin(opId) : "Not a block begin: " + opId; 255 assert allocator.instructionForId(opId) instanceof LabelOp; 256 assert allocator.instructionForId(opId - 2) instanceof BlockEndOp; 257 258 AbstractBlockBase<?> toBlock = allocator.blockForId(opId); 259 AbstractBlockBase<?> fromBlock = allocator.blockForId(opId - 2); 260 261 if (fromBlock.getSuccessorCount() == 1) { 262 // insert move in predecessor 263 return opId - 2; 264 } 265 assert toBlock.getPredecessorCount() == 1 : String.format("Critical Edge? %s->%s", fromBlock, toBlock); 266 // insert move in successor 267 return opId + 2; 268 } 269 270 private void insertMove(int operandId, TraceInterval srcIt, TraceInterval dstIt) { 271 // output all moves here. When source and target are equal, the move is 272 // optimized away later in assignRegNums 273 274 int opId = (operandId + 1) & ~1; 275 AbstractBlockBase<?> opBlock = allocator.blockForId(opId); 276 assert opId > 0 && allocator.blockForId(opId - 2) == opBlock : "cannot insert move at block boundary"; 277 278 // calculate index of instruction inside instruction list of current block 279 // the minimal index (for a block with no spill moves) can be calculated because the 280 // numbering of instructions is known. 281 // When the block already contains spill moves, the index must be increased until the 282 // correct index is reached. 283 List<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(opBlock); 284 int index = (opId - instructions.get(0).id()) >> 1; 285 assert instructions.get(index).id() <= opId : "error in calculation"; 286 287 while (instructions.get(index).id() != opId) { 288 index++; 289 assert 0 <= index && index < instructions.size() : "index out of bounds"; 290 } 291 assert 1 <= index && index < instructions.size() : "index out of bounds"; 292 assert instructions.get(index).id() == opId : "error in calculation"; 293 294 // insert new instruction before instruction at position index 295 moveResolver.moveInsertPosition(instructions, index); 296 moveResolver.addMapping(srcIt, dstIt); 297 } 298 299 private int findOptimalSplitPos(AbstractBlockBase<?> minBlock, AbstractBlockBase<?> maxBlock, int maxSplitPos) { 300 int fromBlockNr = minBlock.getLinearScanNumber(); 301 int toBlockNr = maxBlock.getLinearScanNumber(); 302 303 assert 0 <= fromBlockNr && fromBlockNr < blockCount() : "out of range"; 304 assert 0 <= toBlockNr && toBlockNr < blockCount() : "out of range"; 305 assert fromBlockNr < toBlockNr : "must cross block boundary"; 306 307 // Try to split at end of maxBlock. If this would be after 308 // maxSplitPos, then use the begin of maxBlock 309 int optimalSplitPos = allocator.getLastLirInstructionId(maxBlock) + 2; 310 if (optimalSplitPos > maxSplitPos) { 311 optimalSplitPos = allocator.getFirstLirInstructionId(maxBlock); 312 } 313 314 // minimal block probability 315 double minProbability = maxBlock.probability(); 316 for (int i = toBlockNr - 1; i >= fromBlockNr; i--) { 317 AbstractBlockBase<?> cur = blockAt(i); 318 319 if (cur.probability() < minProbability) { 320 // Block with lower probability found. Split at the end of this block. 321 minProbability = cur.probability(); 322 optimalSplitPos = allocator.getLastLirInstructionId(cur) + 2; 323 } 324 } 325 assert optimalSplitPos > allocator.maxOpId() || allocator.isBlockBegin(optimalSplitPos) : "algorithm must move split pos to block boundary"; 326 327 return optimalSplitPos; 328 } 329 330 @SuppressWarnings({"unused"}) 331 private int findOptimalSplitPos(TraceInterval interval, int minSplitPos, int maxSplitPos, boolean doLoopOptimization) { 332 int optimalSplitPos = findOptimalSplitPos0(minSplitPos, maxSplitPos); 333 if (Debug.isLogEnabled()) { 334 Debug.log("optimal split position: %d", optimalSplitPos); 335 } 336 return optimalSplitPos; 337 } 338 339 private int findOptimalSplitPos0(int minSplitPos, int maxSplitPos) { 340 if (minSplitPos == maxSplitPos) { 341 // trivial case, no optimization of split position possible 342 if (Debug.isLogEnabled()) { 343 Debug.log("min-pos and max-pos are equal, no optimization possible"); 344 } 345 return minSplitPos; 346 347 } 348 assert minSplitPos < maxSplitPos : "must be true then"; 349 assert minSplitPos > 0 : "cannot access minSplitPos - 1 otherwise"; 350 351 // reason for using minSplitPos - 1: when the minimal split pos is exactly at the 352 // beginning of a block, then minSplitPos is also a possible split position. 353 // Use the block before as minBlock, because then minBlock.lastLirInstructionId() + 2 == 354 // minSplitPos 355 AbstractBlockBase<?> minBlock = allocator.blockForId(minSplitPos - 1); 356 357 // reason for using maxSplitPos - 1: otherwise there would be an assert on failure 358 // when an interval ends at the end of the last block of the method 359 // (in this case, maxSplitPos == allocator().maxLirOpId() + 2, and there is no 360 // block at this opId) 361 AbstractBlockBase<?> maxBlock = allocator.blockForId(maxSplitPos - 1); 362 363 assert minBlock.getLinearScanNumber() <= maxBlock.getLinearScanNumber() : "invalid order"; 364 if (minBlock == maxBlock) { 365 // split position cannot be moved to block boundary : so split as late as possible 366 if (Debug.isLogEnabled()) { 367 Debug.log("cannot move split pos to block boundary because minPos and maxPos are in same block"); 368 } 369 return maxSplitPos; 370 371 } 372 // seach optimal block boundary between minSplitPos and maxSplitPos 373 if (Debug.isLogEnabled()) { 374 Debug.log("moving split pos to optimal block boundary between block B%d and B%d", minBlock.getId(), maxBlock.getId()); 375 } 376 377 return findOptimalSplitPos(minBlock, maxBlock, maxSplitPos); 378 } 379 380 // split an interval at the optimal position between minSplitPos and 381 // maxSplitPos in two parts: 382 // 1) the left part has already a location assigned 383 // 2) the right part is sorted into to the unhandled-list 384 @SuppressWarnings("try") 385 private void splitBeforeUsage(TraceInterval interval, int minSplitPos, int maxSplitPos) { 386 387 try (Indent indent = Debug.logAndIndent("splitting interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) { 388 389 assert interval.from() < minSplitPos : "cannot split at start of interval"; 390 assert currentPosition < minSplitPos : "cannot split before current position"; 391 assert minSplitPos <= maxSplitPos : "invalid order"; 392 assert maxSplitPos <= interval.to() : "cannot split after end of interval"; 393 394 final int optimalSplitPos = findOptimalSplitPos(interval, minSplitPos, maxSplitPos, true); 395 396 if (optimalSplitPos == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) { 397 // the split position would be just before the end of the interval 398 // . no split at all necessary 399 if (Debug.isLogEnabled()) { 400 Debug.log("no split necessary because optimal split position is at end of interval"); 401 } 402 return; 403 } 404 // must calculate this before the actual split is performed and before split position is 405 // moved to odd opId 406 final int optimalSplitPosFinal; 407 boolean blockBegin = allocator.isBlockBegin(optimalSplitPos); 408 if (blockBegin) { 409 assert (optimalSplitPos & 1) == 0 : "Block begins must be even: " + optimalSplitPos; 410 // move position after the label (odd optId) 411 optimalSplitPosFinal = optimalSplitPos + 1; 412 } else { 413 // move position before actual instruction (odd opId) 414 optimalSplitPosFinal = (optimalSplitPos - 1) | 1; 415 } 416 417 // TODO( je) better define what min split pos max split pos mean. 418 assert minSplitPos <= optimalSplitPosFinal && optimalSplitPosFinal <= maxSplitPos || minSplitPos == maxSplitPos && optimalSplitPosFinal == minSplitPos - 1 : "out of range"; 419 assert optimalSplitPosFinal <= interval.to() : "cannot split after end of interval"; 420 assert optimalSplitPosFinal > interval.from() : "cannot split at start of interval"; 421 422 if (Debug.isLogEnabled()) { 423 Debug.log("splitting at position %d", optimalSplitPosFinal); 424 } 425 assert optimalSplitPosFinal > currentPosition : "Can not split interval " + interval + " at current position: " + currentPosition; 426 427 // was: 428 // assert isBlockBegin || ((optimalSplitPos1 & 1) == 1) : 429 // "split pos must be odd when not on block boundary"; 430 // assert !isBlockBegin || ((optimalSplitPos1 & 1) == 0) : 431 // "split pos must be even on block boundary"; 432 assert (optimalSplitPosFinal & 1) == 1 : "split pos must be odd"; 433 434 // TODO (je) duplicate code. try to fold 435 if (optimalSplitPosFinal == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) { 436 // the split position would be just before the end of the interval 437 // . no split at all necessary 438 if (Debug.isLogEnabled()) { 439 Debug.log("no split necessary because optimal split position is at end of interval"); 440 } 441 return; 442 } 443 TraceInterval splitPart = interval.split(optimalSplitPosFinal, allocator); 444 445 boolean moveNecessary = true; 446 splitPart.setInsertMoveWhenActivated(moveNecessary); 447 448 assert splitPart.from() >= currentPosition : "cannot append new interval before current walk position"; 449 unhandledAnyList.addToListSortedByStartAndUsePositions(splitPart); 450 451 if (Debug.isLogEnabled()) { 452 Debug.log("left interval %s: %s", moveNecessary ? " " : "", interval.logString()); 453 Debug.log("right interval %s: %s", moveNecessary ? "(move)" : "", splitPart.logString()); 454 } 455 } 456 } 457 458 // split an interval at the optimal position between minSplitPos and 459 // maxSplitPos in two parts: 460 // 1) the left part has already a location assigned 461 // 2) the right part is always on the stack and therefore ignored in further processing 462 @SuppressWarnings("try") 463 private void splitForSpilling(TraceInterval interval) { 464 // calculate allowed range of splitting position 465 int maxSplitPos = currentPosition; 466 int previousUsage = interval.previousUsage(RegisterPriority.ShouldHaveRegister, maxSplitPos); 467 if (previousUsage == currentPosition) { 468 /* 469 * If there is a usage with ShouldHaveRegister priority at the current position fall 470 * back to MustHaveRegister priority. This only happens if register priority was 471 * downgraded to MustHaveRegister in #allocLockedRegister. 472 */ 473 previousUsage = interval.previousUsage(RegisterPriority.MustHaveRegister, maxSplitPos); 474 } 475 int minSplitPos = Math.max(previousUsage + 1, interval.from()); 476 477 try (Indent indent = Debug.logAndIndent("splitting and spilling interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) { 478 479 assert interval.from() <= minSplitPos : "cannot split before start of interval"; 480 assert minSplitPos <= maxSplitPos : "invalid order"; 481 assert maxSplitPos < interval.to() : "cannot split at end end of interval"; 482 assert currentPosition < interval.to() : "interval must not end before current position"; 483 484 if (minSplitPos == interval.from()) { 485 // the whole interval is never used, so spill it entirely to memory 486 487 try (Indent indent2 = Debug.logAndIndent("spilling entire interval because split pos is at beginning of interval (use positions: %d)", interval.numUsePos())) { 488 489 assert interval.firstUsage(RegisterPriority.MustHaveRegister) > currentPosition : String.format("interval %s must not have use position before currentPosition %d", interval, 490 currentPosition); 491 492 allocator.assignSpillSlot(interval); 493 handleSpillSlot(interval); 494 changeSpillState(interval, minSplitPos); 495 496 // Also kick parent intervals out of register to memory when they have no use 497 // position. This avoids short interval in register surrounded by intervals in 498 // memory . avoid useless moves from memory to register and back 499 TraceInterval parent = interval; 500 while (parent != null && parent.isSplitChild()) { 501 parent = parent.getSplitChildBeforeOpId(parent.from()); 502 503 if (isRegister(parent.location())) { 504 if (parent.firstUsage(RegisterPriority.ShouldHaveRegister) == Integer.MAX_VALUE) { 505 // parent is never used, so kick it out of its assigned register 506 if (Debug.isLogEnabled()) { 507 Debug.log("kicking out interval %d out of its register because it is never used", parent.operandNumber); 508 } 509 allocator.assignSpillSlot(parent); 510 handleSpillSlot(parent); 511 } else { 512 // do not go further back because the register is actually used by 513 // the interval 514 parent = null; 515 } 516 } 517 } 518 } 519 520 } else { 521 // search optimal split pos, split interval and spill only the right hand part 522 int optimalSplitPos = findOptimalSplitPos(interval, minSplitPos, maxSplitPos, false); 523 524 assert minSplitPos <= optimalSplitPos && optimalSplitPos <= maxSplitPos : "out of range"; 525 assert optimalSplitPos < interval.to() : "cannot split at end of interval"; 526 assert optimalSplitPos >= interval.from() : "cannot split before start of interval"; 527 528 if (!allocator.isBlockBegin(optimalSplitPos)) { 529 // move position before actual instruction (odd opId) 530 optimalSplitPos = (optimalSplitPos - 1) | 1; 531 } 532 533 try (Indent indent2 = Debug.logAndIndent("splitting at position %d", optimalSplitPos)) { 534 assert allocator.isBlockBegin(optimalSplitPos) || ((optimalSplitPos & 1) == 1) : "split pos must be odd when not on block boundary"; 535 assert !allocator.isBlockBegin(optimalSplitPos) || ((optimalSplitPos & 1) == 0) : "split pos must be even on block boundary"; 536 537 TraceInterval spilledPart = interval.split(optimalSplitPos, allocator); 538 allocator.assignSpillSlot(spilledPart); 539 handleSpillSlot(spilledPart); 540 changeSpillState(spilledPart, optimalSplitPos); 541 542 if (!allocator.isBlockBegin(optimalSplitPos)) { 543 if (Debug.isLogEnabled()) { 544 Debug.log("inserting move from interval %s to %s", interval, spilledPart); 545 } 546 insertMove(optimalSplitPos, interval, spilledPart); 547 } else { 548 if (Debug.isLogEnabled()) { 549 Debug.log("no need to insert move. done by data-flow resolution"); 550 } 551 } 552 553 // the currentSplitChild is needed later when moves are inserted for reloading 554 assert spilledPart.currentSplitChild() == interval : "overwriting wrong currentSplitChild"; 555 spilledPart.makeCurrentSplitChild(); 556 557 if (Debug.isLogEnabled()) { 558 Debug.log("left interval: %s", interval.logString()); 559 Debug.log("spilled interval : %s", spilledPart.logString()); 560 } 561 } 562 } 563 } 564 } 565 566 /** 567 * Change spill state of an interval. 568 * 569 * Note: called during register allocation. 570 * 571 * @param spillPos position of the spill 572 */ 573 private void changeSpillState(TraceInterval interval, int spillPos) { 574 if (TraceLinearScanPhase.Options.LIROptTraceRAEliminateSpillMoves.getValue()) { 575 switch (interval.spillState()) { 576 case NoSpillStore: 577 final int minSpillPos = interval.spillDefinitionPos(); 578 final int maxSpillPost = spillPos; 579 580 final int optimalSpillPos = findOptimalSpillPos(minSpillPos, maxSpillPost); 581 582 // assert !allocator.isBlockBegin(optimalSpillPos); 583 assert !allocator.isBlockEnd(optimalSpillPos); 584 assert (optimalSpillPos & 1) == 0 : "Spill pos must be even"; 585 586 interval.setSpillDefinitionPos(optimalSpillPos); 587 interval.setSpillState(SpillState.SpillStore); 588 break; 589 case SpillStore: 590 case StartInMemory: 591 case NoOptimization: 592 case NoDefinitionFound: 593 // nothing to do 594 break; 595 596 default: 597 throw GraalError.shouldNotReachHere("other states not allowed at this time"); 598 } 599 } else { 600 interval.setSpillState(SpillState.NoOptimization); 601 } 602 } 603 604 /** 605 * @param minSpillPos minimal spill position 606 * @param maxSpillPos maximal spill position 607 */ 608 private int findOptimalSpillPos(int minSpillPos, int maxSpillPos) { 609 int optimalSpillPos = findOptimalSpillPos0(minSpillPos, maxSpillPos) & (~1); 610 if (Debug.isLogEnabled()) { 611 Debug.log("optimal spill position: %d", optimalSpillPos); 612 } 613 return optimalSpillPos; 614 } 615 616 private int findOptimalSpillPos0(int minSpillPos, int maxSpillPos) { 617 if (minSpillPos == maxSpillPos) { 618 // trivial case, no optimization of split position possible 619 if (Debug.isLogEnabled()) { 620 Debug.log("min-pos and max-pos are equal, no optimization possible"); 621 } 622 return minSpillPos; 623 624 } 625 assert minSpillPos < maxSpillPos : "must be true then"; 626 assert minSpillPos >= 0 : "cannot access minSplitPos - 1 otherwise"; 627 628 AbstractBlockBase<?> minBlock = allocator.blockForId(minSpillPos); 629 AbstractBlockBase<?> maxBlock = allocator.blockForId(maxSpillPos); 630 631 assert minBlock.getLinearScanNumber() <= maxBlock.getLinearScanNumber() : "invalid order"; 632 if (minBlock == maxBlock) { 633 // split position cannot be moved to block boundary : so split as late as possible 634 if (Debug.isLogEnabled()) { 635 Debug.log("cannot move split pos to block boundary because minPos and maxPos are in same block"); 636 } 637 return maxSpillPos; 638 639 } 640 // search optimal block boundary between minSplitPos and maxSplitPos 641 if (Debug.isLogEnabled()) { 642 Debug.log("moving split pos to optimal block boundary between block B%d and B%d", minBlock.getId(), maxBlock.getId()); 643 } 644 645 // currently using the same heuristic as for splitting 646 return findOptimalSpillPos(minBlock, maxBlock, maxSpillPos); 647 } 648 649 private int findOptimalSpillPos(AbstractBlockBase<?> minBlock, AbstractBlockBase<?> maxBlock, int maxSplitPos) { 650 int fromBlockNr = minBlock.getLinearScanNumber(); 651 int toBlockNr = maxBlock.getLinearScanNumber(); 652 653 assert 0 <= fromBlockNr && fromBlockNr < blockCount() : "out of range"; 654 assert 0 <= toBlockNr && toBlockNr < blockCount() : "out of range"; 655 assert fromBlockNr < toBlockNr : "must cross block boundary"; 656 657 /* 658 * Try to split at end of maxBlock. If this would be after maxSplitPos, then use the begin 659 * of maxBlock. We use last instruction -2 because we want to insert the move before the 660 * block end op. 661 */ 662 int optimalSplitPos = allocator.getLastLirInstructionId(maxBlock) - 2; 663 if (optimalSplitPos > maxSplitPos) { 664 optimalSplitPos = allocator.getFirstLirInstructionId(maxBlock); 665 } 666 667 // minimal block probability 668 double minProbability = maxBlock.probability(); 669 for (int i = toBlockNr - 1; i >= fromBlockNr; i--) { 670 AbstractBlockBase<?> cur = blockAt(i); 671 672 if (cur.probability() < minProbability) { 673 // Block with lower probability found. Split at the end of this block. 674 minProbability = cur.probability(); 675 optimalSplitPos = allocator.getLastLirInstructionId(cur) - 2; 676 } 677 } 678 assert optimalSplitPos > allocator.maxOpId() || allocator.isBlockBegin(optimalSplitPos) || allocator.isBlockEnd(optimalSplitPos + 2) : "algorithm must move split pos to block boundary"; 679 680 return optimalSplitPos; 681 } 682 683 /** 684 * This is called for every interval that is assigned to a stack slot. 685 */ 686 private static void handleSpillSlot(TraceInterval interval) { 687 assert interval.location() != null && (interval.canMaterialize() || isStackSlotValue(interval.location())) : "interval not assigned to a stack slot " + interval; 688 // Do nothing. Stack slots are not processed in this implementation. 689 } 690 691 private void splitStackInterval(TraceInterval interval) { 692 int minSplitPos = currentPosition + 1; 693 int maxSplitPos = Math.min(interval.firstUsage(RegisterPriority.ShouldHaveRegister), interval.to()); 694 695 splitBeforeUsage(interval, minSplitPos, maxSplitPos); 696 } 697 698 private void splitWhenPartialRegisterAvailable(TraceInterval interval, int registerAvailableUntil) { 699 int minSplitPos = Math.max(interval.previousUsage(RegisterPriority.ShouldHaveRegister, registerAvailableUntil), interval.from() + 1); 700 splitBeforeUsage(interval, minSplitPos, registerAvailableUntil); 701 } 702 703 private void splitAndSpillInterval(TraceInterval interval) { 704 int currentPos = currentPosition; 705 /* 706 * Search the position where the interval must have a register and split at the optimal 707 * position before. The new created part is added to the unhandled list and will get a 708 * register when it is activated. 709 */ 710 int minSplitPos = currentPos + 1; 711 int maxSplitPos = interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos); 712 713 if (maxSplitPos <= interval.to()) { 714 splitBeforeUsage(interval, minSplitPos, maxSplitPos); 715 } else { 716 Debug.log("No more usage, no need to split: %s", interval); 717 } 718 719 assert interval.nextUsage(RegisterPriority.MustHaveRegister, currentPos) == Integer.MAX_VALUE : "the remaining part is spilled to stack and therefore has no register"; 720 splitForSpilling(interval); 721 } 722 723 @SuppressWarnings("try") 724 private boolean allocFreeRegister(TraceInterval interval) { 725 try (Indent indent = Debug.logAndIndent("trying to find free register for %s", interval)) { 726 727 initUseLists(true); 728 freeExcludeActiveFixed(); 729 freeCollectInactiveFixed(interval); 730 freeExcludeActiveAny(); 731 // freeCollectUnhandled(fixedKind, cur); 732 733 // usePos contains the start of the next interval that has this register assigned 734 // (either as a fixed register or a normal allocated register in the past) 735 // only intervals overlapping with cur are processed, non-overlapping invervals can be 736 // ignored safely 737 if (Debug.isLogEnabled()) { 738 // Enable this logging to see all register states 739 try (Indent indent2 = Debug.logAndIndent("state of registers:")) { 740 for (Register register : availableRegs) { 741 int i = register.number; 742 Debug.log("reg %d (%s): usePos: %d", register.number, register, usePos[i]); 743 } 744 } 745 } 746 747 Register hint = null; 748 IntervalHint locationHint = interval.locationHint(true); 749 if (locationHint != null && locationHint.location() != null && isRegister(locationHint.location())) { 750 hint = asRegister(locationHint.location()); 751 if (Debug.isLogEnabled()) { 752 Debug.log("hint register %3d (%4s) from interval %s", hint.number, hint, locationHint); 753 } 754 } 755 assert interval.location() == null : "register already assigned to interval"; 756 757 // the register must be free at least until this position 758 int regNeededUntil = interval.from() + 1; 759 int intervalTo = interval.to(); 760 761 boolean needSplit = false; 762 int splitPos = -1; 763 764 Register reg = null; 765 Register minFullReg = null; 766 Register maxPartialReg = null; 767 768 for (Register availableReg : availableRegs) { 769 int number = availableReg.number; 770 if (usePos[number] >= intervalTo) { 771 // this register is free for the full interval 772 if (minFullReg == null || availableReg.equals(hint) || (usePos[number] < usePos[minFullReg.number] && !minFullReg.equals(hint))) { 773 minFullReg = availableReg; 774 } 775 } else if (usePos[number] > regNeededUntil) { 776 // this register is at least free until regNeededUntil 777 if (maxPartialReg == null || availableReg.equals(hint) || (usePos[number] > usePos[maxPartialReg.number] && !maxPartialReg.equals(hint))) { 778 maxPartialReg = availableReg; 779 } 780 } 781 } 782 783 if (minFullReg != null) { 784 reg = minFullReg; 785 } else if (maxPartialReg != null) { 786 needSplit = true; 787 reg = maxPartialReg; 788 } else { 789 return false; 790 } 791 792 splitPos = usePos[reg.number]; 793 interval.assignLocation(reg.asValue(interval.kind())); 794 if (Debug.isLogEnabled()) { 795 Debug.log("selected register %d (%s)", reg.number, reg); 796 } 797 798 assert splitPos > 0 : "invalid splitPos"; 799 if (needSplit) { 800 // register not available for full interval, so split it 801 splitWhenPartialRegisterAvailable(interval, splitPos); 802 } 803 // only return true if interval is completely assigned 804 return true; 805 } 806 } 807 808 private void splitAndSpillIntersectingIntervals(Register reg) { 809 assert reg != null : "no register assigned"; 810 811 for (int i = 0; i < spillIntervals[reg.number].size(); i++) { 812 TraceInterval interval = spillIntervals[reg.number].get(i); 813 removeFromList(interval); 814 splitAndSpillInterval(interval); 815 } 816 } 817 818 // Split an Interval and spill it to memory so that cur can be placed in a register 819 @SuppressWarnings("try") 820 private void allocLockedRegister(TraceInterval interval) { 821 try (Indent indent = Debug.logAndIndent("alloc locked register: need to split and spill to get register for %s", interval)) { 822 823 // the register must be free at least until this position 824 int firstUsage = interval.firstUsage(RegisterPriority.MustHaveRegister); 825 int firstShouldHaveUsage = interval.firstUsage(RegisterPriority.ShouldHaveRegister); 826 int regNeededUntil = Math.min(firstUsage, interval.from() + 1); 827 int intervalTo = interval.to(); 828 assert regNeededUntil >= 0 && regNeededUntil < Integer.MAX_VALUE : "interval has no use"; 829 830 Register reg; 831 Register ignore; 832 /* 833 * In the common case we don't spill registers that have _any_ use position that is 834 * closer than the next use of the current interval, but if we can't spill the current 835 * interval we weaken this strategy and also allow spilling of intervals that have a 836 * non-mandatory requirements (no MustHaveRegister use position). 837 */ 838 for (RegisterPriority registerPriority = RegisterPriority.LiveAtLoopEnd; true; registerPriority = RegisterPriority.MustHaveRegister) { 839 // collect current usage of registers 840 initUseLists(false); 841 spillExcludeActiveFixed(); 842 // spillBlockUnhandledFixed(cur); 843 spillBlockInactiveFixed(interval); 844 spillCollectActiveAny(registerPriority); 845 if (Debug.isLogEnabled()) { 846 printRegisterState(); 847 } 848 849 reg = null; 850 ignore = interval.location() != null && isRegister(interval.location()) ? asRegister(interval.location()) : null; 851 852 for (Register availableReg : availableRegs) { 853 int number = availableReg.number; 854 if (availableReg.equals(ignore)) { 855 // this register must be ignored 856 } else if (usePos[number] > regNeededUntil) { 857 /* 858 * If the use position is the same, prefer registers (active intervals) 859 * where the value is already on the stack. 860 */ 861 if (reg == null || (usePos[number] > usePos[reg.number]) || (usePos[number] == usePos[reg.number] && (!isInMemory.get(reg.number) && isInMemory.get(number)))) { 862 reg = availableReg; 863 } 864 } 865 } 866 867 if (Debug.isLogEnabled()) { 868 Debug.log("Register Selected: %s", reg); 869 } 870 871 int regUsePos = (reg == null ? 0 : usePos[reg.number]); 872 if (regUsePos <= firstShouldHaveUsage) { 873 /* Check if there is another interval that is already in memory. */ 874 if (reg == null || interval.inMemoryAt(currentPosition) || !isInMemory.get(reg.number)) { 875 if (Debug.isLogEnabled()) { 876 Debug.log("able to spill current interval. firstUsage(register): %d, usePos: %d", firstUsage, regUsePos); 877 } 878 879 if (firstUsage <= interval.from() + 1) { 880 if (registerPriority.equals(RegisterPriority.LiveAtLoopEnd)) { 881 /* 882 * Tool of last resort: we can not spill the current interval so we 883 * try to spill an active interval that has a usage but do not 884 * require a register. 885 */ 886 Debug.log("retry with register priority must have register"); 887 continue; 888 } 889 String description = "cannot spill interval (" + interval + ") that is used in first instruction (possible reason: no register found) firstUsage=" + firstUsage + 890 ", interval.from()=" + interval.from() + "; already used candidates: " + Arrays.toString(availableRegs); 891 /* 892 * assign a reasonable register and do a bailout in product mode to 893 * avoid errors 894 */ 895 allocator.assignSpillSlot(interval); 896 if (Debug.isDumpEnabled(Debug.INFO_LOG_LEVEL)) { 897 dumpLIRAndIntervals(description); 898 } 899 throw new OutOfRegistersException("LinearScan: no register found", description); 900 } 901 902 splitAndSpillInterval(interval); 903 return; 904 } 905 } 906 // common case: break out of the loop 907 break; 908 } 909 910 boolean needSplit = blockPos[reg.number] <= intervalTo; 911 912 int splitPos = blockPos[reg.number]; 913 914 if (Debug.isLogEnabled()) { 915 Debug.log("decided to use register %d", reg.number); 916 } 917 assert splitPos > 0 : "invalid splitPos"; 918 assert needSplit || splitPos > interval.from() : "splitting interval at from"; 919 920 interval.assignLocation(reg.asValue(interval.kind())); 921 if (needSplit) { 922 // register not available for full interval : so split it 923 splitWhenPartialRegisterAvailable(interval, splitPos); 924 } 925 926 // perform splitting and spilling for all affected intervals 927 splitAndSpillIntersectingIntervals(reg); 928 return; 929 } 930 } 931 932 protected void dumpLIRAndIntervals(String description) { 933 Debug.dump(Debug.INFO_LOG_LEVEL, allocator.getLIR(), description); 934 allocator.printIntervals(description); 935 } 936 937 @SuppressWarnings("try") 938 private void printRegisterState() { 939 try (Indent indent2 = Debug.logAndIndent("state of registers:")) { 940 for (Register reg : availableRegs) { 941 int i = reg.number; 942 try (Indent indent3 = Debug.logAndIndent("reg %d: usePos: %d, blockPos: %d, inMemory: %b, intervals: ", i, usePos[i], blockPos[i], isInMemory.get(i))) { 943 for (int j = 0; j < spillIntervals[i].size(); j++) { 944 Debug.log("%s", spillIntervals[i].get(j)); 945 } 946 } 947 } 948 } 949 } 950 951 private boolean noAllocationPossible(TraceInterval interval) { 952 if (allocator.callKillsRegisters()) { 953 // fast calculation of intervals that can never get a register because the 954 // the next instruction is a call that blocks all registers 955 // Note: this only works if a call kills all registers 956 957 // check if this interval is the result of a split operation 958 // (an interval got a register until this position) 959 int pos = interval.from(); 960 if (isOdd(pos)) { 961 // the current instruction is a call that blocks all registers 962 if (pos < allocator.maxOpId() && allocator.hasCall(pos + 1) && interval.to() > pos + 1) { 963 if (Debug.isLogEnabled()) { 964 Debug.log("free register cannot be available because all registers blocked by following call"); 965 } 966 967 // safety check that there is really no register available 968 assert !allocFreeRegister(interval) : "found a register for this interval"; 969 return true; 970 } 971 } 972 } 973 return false; 974 } 975 976 private void initVarsForAlloc(TraceInterval interval) { 977 AllocatableRegisters allocatableRegisters = allocator.getRegisterAllocationConfig().getAllocatableRegisters(interval.kind().getPlatformKind()); 978 availableRegs = allocatableRegisters.allocatableRegisters; 979 minReg = allocatableRegisters.minRegisterNumber; 980 maxReg = allocatableRegisters.maxRegisterNumber; 981 } 982 983 private static boolean isMove(LIRInstruction op, TraceInterval from, TraceInterval to) { 984 if (op instanceof ValueMoveOp) { 985 ValueMoveOp move = (ValueMoveOp) op; 986 if (isVariable(move.getInput()) && isVariable(move.getResult())) { 987 return move.getInput() != null && move.getInput().equals(from.operand) && move.getResult() != null && move.getResult().equals(to.operand); 988 } 989 } 990 return false; 991 } 992 993 // optimization (especially for phi functions of nested loops): 994 // assign same spill slot to non-intersecting intervals 995 private void combineSpilledIntervals(TraceInterval interval) { 996 if (interval.isSplitChild()) { 997 // optimization is only suitable for split parents 998 return; 999 } 1000 1001 IntervalHint locationHint = interval.locationHint(false); 1002 if (locationHint == null || !(locationHint instanceof TraceInterval)) { 1003 return; 1004 } 1005 TraceInterval registerHint = (TraceInterval) locationHint; 1006 assert registerHint.isSplitParent() : "register hint must be split parent"; 1007 1008 if (interval.spillState() != SpillState.NoOptimization || registerHint.spillState() != SpillState.NoOptimization) { 1009 // combining the stack slots for intervals where spill move optimization is applied 1010 // is not benefitial and would cause problems 1011 return; 1012 } 1013 1014 int beginPos = interval.from(); 1015 int endPos = interval.to(); 1016 if (endPos > allocator.maxOpId() || isOdd(beginPos) || isOdd(endPos)) { 1017 // safety check that lirOpWithId is allowed 1018 return; 1019 } 1020 1021 if (!isMove(allocator.instructionForId(beginPos), registerHint, interval) || !isMove(allocator.instructionForId(endPos), interval, registerHint)) { 1022 // cur and registerHint are not connected with two moves 1023 return; 1024 } 1025 1026 TraceInterval beginHint = registerHint.getSplitChildAtOpId(beginPos, LIRInstruction.OperandMode.USE); 1027 TraceInterval endHint = registerHint.getSplitChildAtOpId(endPos, LIRInstruction.OperandMode.DEF); 1028 if (beginHint == endHint || beginHint.to() != beginPos || endHint.from() != endPos) { 1029 // registerHint must be split : otherwise the re-writing of use positions does not work 1030 return; 1031 } 1032 1033 assert beginHint.location() != null : "must have register assigned"; 1034 assert endHint.location() == null : "must not have register assigned"; 1035 assert interval.firstUsage(RegisterPriority.MustHaveRegister) == beginPos : "must have use position at begin of interval because of move"; 1036 assert endHint.firstUsage(RegisterPriority.MustHaveRegister) == endPos : "must have use position at begin of interval because of move"; 1037 1038 if (isRegister(beginHint.location())) { 1039 // registerHint is not spilled at beginPos : so it would not be benefitial to 1040 // immediately spill cur 1041 return; 1042 } 1043 assert registerHint.spillSlot() != null : "must be set when part of interval was spilled"; 1044 1045 // modify intervals such that cur gets the same stack slot as registerHint 1046 // delete use positions to prevent the intervals to get a register at beginning 1047 interval.setSpillSlot(registerHint.spillSlot()); 1048 interval.removeFirstUsePos(); 1049 endHint.removeFirstUsePos(); 1050 } 1051 1052 // allocate a physical register or memory location to an interval 1053 @Override 1054 @SuppressWarnings("try") 1055 protected boolean activateCurrent(TraceInterval interval) { 1056 if (Debug.isLogEnabled()) { 1057 logCurrentStatus(); 1058 } 1059 boolean result = true; 1060 1061 try (Indent indent = Debug.logAndIndent("activating interval %s, splitParent: %d", interval, interval.splitParent().operandNumber)) { 1062 1063 final Value operand = interval.operand; 1064 if (interval.location() != null && isStackSlotValue(interval.location())) { 1065 // activating an interval that has a stack slot assigned . split it at first use 1066 // position 1067 // used for method parameters 1068 if (Debug.isLogEnabled()) { 1069 Debug.log("interval has spill slot assigned (method parameter) . split it before first use"); 1070 } 1071 splitStackInterval(interval); 1072 result = false; 1073 1074 } else { 1075 if (interval.location() == null) { 1076 // interval has not assigned register . normal allocation 1077 // (this is the normal case for most intervals) 1078 if (Debug.isLogEnabled()) { 1079 Debug.log("normal allocation of register"); 1080 } 1081 1082 // assign same spill slot to non-intersecting intervals 1083 combineSpilledIntervals(interval); 1084 1085 initVarsForAlloc(interval); 1086 if (noAllocationPossible(interval) || !allocFreeRegister(interval)) { 1087 // no empty register available. 1088 // split and spill another interval so that this interval gets a register 1089 allocLockedRegister(interval); 1090 } 1091 1092 // spilled intervals need not be move to active-list 1093 if (!isRegister(interval.location())) { 1094 result = false; 1095 } 1096 } 1097 } 1098 1099 // load spilled values that become active from stack slot to register 1100 if (interval.insertMoveWhenActivated()) { 1101 assert interval.isSplitChild(); 1102 assert interval.currentSplitChild() != null; 1103 assert !interval.currentSplitChild().operand.equals(operand) : "cannot insert move between same interval"; 1104 if (Debug.isLogEnabled()) { 1105 Debug.log("Inserting move from interval %d to %d because insertMoveWhenActivated is set", interval.currentSplitChild().operandNumber, interval.operandNumber); 1106 } 1107 1108 insertMove(interval.from(), interval.currentSplitChild(), interval); 1109 } 1110 interval.makeCurrentSplitChild(); 1111 1112 } 1113 1114 return result; // true = interval is moved to active list 1115 } 1116 1117 void finishAllocation() { 1118 // must be called when all intervals are allocated 1119 moveResolver.resolveAndAppendMoves(); 1120 } 1121 }