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