1 /* 2 * Copyright (c) 2015, 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 24 25 package org.graalvm.compiler.lir.alloc.lsra; 26 27 import static org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph.commonDominator; 28 import static org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph.dominates; 29 import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue; 30 31 import java.util.Iterator; 32 33 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; 34 import org.graalvm.compiler.debug.CounterKey; 35 import org.graalvm.compiler.debug.DebugContext; 36 import org.graalvm.compiler.debug.Indent; 37 import org.graalvm.compiler.lir.LIRInsertionBuffer; 38 import org.graalvm.compiler.lir.LIRInstruction; 39 import org.graalvm.compiler.lir.LIRInstruction.OperandMode; 40 import org.graalvm.compiler.lir.alloc.lsra.Interval.SpillState; 41 import org.graalvm.compiler.lir.gen.LIRGenerationResult; 42 import org.graalvm.compiler.lir.phases.AllocationPhase.AllocationContext; 43 44 import jdk.vm.ci.code.TargetDescription; 45 import jdk.vm.ci.meta.AllocatableValue; 46 47 public final class LinearScanOptimizeSpillPositionPhase extends LinearScanAllocationPhase { 48 49 private static final CounterKey betterSpillPos = DebugContext.counter("BetterSpillPosition"); 50 private static final CounterKey betterSpillPosWithLowerProbability = DebugContext.counter("BetterSpillPositionWithLowerProbability"); 51 52 private final LinearScan allocator; 53 private DebugContext debug; 54 55 LinearScanOptimizeSpillPositionPhase(LinearScan allocator) { 56 this.allocator = allocator; 57 this.debug = allocator.getDebug(); 58 } 59 60 @Override 61 protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) { 62 optimizeSpillPosition(lirGenRes); 63 allocator.printIntervals("After optimize spill position"); 64 } 65 66 @SuppressWarnings("try") 67 private void optimizeSpillPosition(LIRGenerationResult res) { 68 try (Indent indent0 = debug.logAndIndent("OptimizeSpillPositions")) { 69 LIRInsertionBuffer[] insertionBuffers = new LIRInsertionBuffer[allocator.getLIR().linearScanOrder().length]; 70 for (Interval interval : allocator.intervals()) { 71 optimizeInterval(insertionBuffers, interval, res); 72 } 73 for (LIRInsertionBuffer insertionBuffer : insertionBuffers) { 74 if (insertionBuffer != null) { 75 assert insertionBuffer.initialized() : "Insertion buffer is nonnull but not initialized!"; 76 insertionBuffer.finish(); 77 } 78 } 79 } 80 } 81 82 @SuppressWarnings("try") 83 private void optimizeInterval(LIRInsertionBuffer[] insertionBuffers, Interval interval, LIRGenerationResult res) { 84 if (interval == null || !interval.isSplitParent() || interval.spillState() != SpillState.SpillInDominator) { 85 return; 86 } 87 AbstractBlockBase<?> defBlock = allocator.blockForId(interval.spillDefinitionPos()); 88 AbstractBlockBase<?> spillBlock = null; 89 Interval firstSpillChild = null; 90 try (Indent indent = debug.logAndIndent("interval %s (%s)", interval, defBlock)) { 91 for (Interval splitChild : interval.getSplitChildren()) { 92 if (isStackSlotValue(splitChild.location())) { 93 if (firstSpillChild == null || splitChild.from() < firstSpillChild.from()) { 94 firstSpillChild = splitChild; 95 } else { 96 assert firstSpillChild.from() < splitChild.from(); 97 } 98 // iterate all blocks where the interval has use positions 99 for (AbstractBlockBase<?> splitBlock : blocksForInterval(splitChild)) { 100 if (dominates(defBlock, splitBlock)) { 101 debug.log("Split interval %s, block %s", splitChild, splitBlock); 102 if (spillBlock == null) { 103 spillBlock = splitBlock; 104 } else { 105 spillBlock = commonDominator(spillBlock, splitBlock); 106 assert spillBlock != null; 107 } 108 } 109 } 110 } 111 } 112 if (spillBlock == null) { 113 debug.log("not spill interval found"); 114 // no spill interval 115 interval.setSpillState(SpillState.StoreAtDefinition); 116 return; 117 } 118 debug.log(DebugContext.VERBOSE_LEVEL, "Spill block candidate (initial): %s", spillBlock); 119 // move out of loops 120 if (defBlock.getLoopDepth() < spillBlock.getLoopDepth()) { 121 spillBlock = moveSpillOutOfLoop(defBlock, spillBlock); 122 } 123 debug.log(DebugContext.VERBOSE_LEVEL, "Spill block candidate (after loop optimizaton): %s", spillBlock); 124 125 /* 126 * The spill block is the begin of the first split child (aka the value is on the 127 * stack). 128 * 129 * The problem is that if spill block has more than one predecessor, the values at the 130 * end of the predecessors might differ. Therefore, we would need a spill move in all 131 * predecessors. To avoid this we spill in the dominator. 132 */ 133 assert firstSpillChild != null; 134 if (!defBlock.equals(spillBlock) && spillBlock.equals(allocator.blockForId(firstSpillChild.from()))) { 135 AbstractBlockBase<?> dom = spillBlock.getDominator(); 136 if (debug.isLogEnabled()) { 137 debug.log("Spill block (%s) is the beginning of a spill child -> use dominator (%s)", spillBlock, dom); 138 } 139 spillBlock = dom; 140 } 141 if (defBlock.equals(spillBlock)) { 142 debug.log(DebugContext.VERBOSE_LEVEL, "Definition is the best choice: %s", defBlock); 143 // definition is the best choice 144 interval.setSpillState(SpillState.StoreAtDefinition); 145 return; 146 } 147 assert dominates(defBlock, spillBlock); 148 betterSpillPos.increment(debug); 149 if (debug.isLogEnabled()) { 150 debug.log("Better spill position found (Block %s)", spillBlock); 151 } 152 153 if (defBlock.probability() <= spillBlock.probability()) { 154 debug.log(DebugContext.VERBOSE_LEVEL, "Definition has lower probability %s (%f) is lower than spill block %s (%f)", defBlock, defBlock.probability(), spillBlock, 155 spillBlock.probability()); 156 // better spill block has the same probability -> do nothing 157 interval.setSpillState(SpillState.StoreAtDefinition); 158 return; 159 } 160 161 LIRInsertionBuffer insertionBuffer = insertionBuffers[spillBlock.getId()]; 162 if (insertionBuffer == null) { 163 insertionBuffer = new LIRInsertionBuffer(); 164 insertionBuffers[spillBlock.getId()] = insertionBuffer; 165 insertionBuffer.init(allocator.getLIR().getLIRforBlock(spillBlock)); 166 } 167 int spillOpId = allocator.getFirstLirInstructionId(spillBlock); 168 // insert spill move 169 AllocatableValue fromLocation = interval.getSplitChildAtOpId(spillOpId, OperandMode.DEF, allocator).location(); 170 AllocatableValue toLocation = LinearScan.canonicalSpillOpr(interval); 171 LIRInstruction move = allocator.getSpillMoveFactory().createMove(toLocation, fromLocation); 172 move.setComment(res, "LSRAOptimizeSpillPos: optimize spill pos"); 173 debug.log(DebugContext.VERBOSE_LEVEL, "Insert spill move %s", move); 174 move.setId(LinearScan.DOMINATOR_SPILL_MOVE_ID); 175 /* 176 * We can use the insertion buffer directly because we always insert at position 1. 177 */ 178 insertionBuffer.append(1, move); 179 180 betterSpillPosWithLowerProbability.increment(debug); 181 interval.setSpillDefinitionPos(spillOpId); 182 } 183 } 184 185 /** 186 * Iterate over all {@link AbstractBlockBase blocks} of an interval. 187 */ 188 private class IntervalBlockIterator implements Iterator<AbstractBlockBase<?>> { 189 190 Range range; 191 AbstractBlockBase<?> block; 192 193 IntervalBlockIterator(Interval interval) { 194 range = interval.first(); 195 block = allocator.blockForId(range.from); 196 } 197 198 @Override 199 public AbstractBlockBase<?> next() { 200 AbstractBlockBase<?> currentBlock = block; 201 int nextBlockIndex = block.getLinearScanNumber() + 1; 202 if (nextBlockIndex < allocator.sortedBlocks().length) { 203 block = allocator.sortedBlocks()[nextBlockIndex]; 204 if (range.to <= allocator.getFirstLirInstructionId(block)) { 205 range = range.next; 206 if (range.isEndMarker()) { 207 block = null; 208 } else { 209 block = allocator.blockForId(range.from); 210 } 211 } 212 } else { 213 block = null; 214 } 215 return currentBlock; 216 } 217 218 @Override 219 public boolean hasNext() { 220 return block != null; 221 } 222 } 223 224 private Iterable<AbstractBlockBase<?>> blocksForInterval(Interval interval) { 225 return new Iterable<AbstractBlockBase<?>>() { 226 @Override 227 public Iterator<AbstractBlockBase<?>> iterator() { 228 return new IntervalBlockIterator(interval); 229 } 230 }; 231 } 232 233 private static AbstractBlockBase<?> moveSpillOutOfLoop(AbstractBlockBase<?> defBlock, AbstractBlockBase<?> spillBlock) { 234 int defLoopDepth = defBlock.getLoopDepth(); 235 for (AbstractBlockBase<?> block = spillBlock.getDominator(); !defBlock.equals(block); block = block.getDominator()) { 236 assert block != null : "spill block not dominated by definition block?"; 237 if (block.getLoopDepth() <= defLoopDepth) { 238 assert block.getLoopDepth() == defLoopDepth : "Cannot spill an interval outside of the loop where it is defined!"; 239 return block; 240 } 241 } 242 return defBlock; 243 } 244 }