1 /* 2 * Copyright (c) 2015, 2018, 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.stackslotalloc; 26 27 import static org.graalvm.compiler.lir.LIRValueUtil.asVirtualStackSlot; 28 import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot; 29 30 import java.util.ArrayDeque; 31 import java.util.ArrayList; 32 import java.util.BitSet; 33 import java.util.Deque; 34 import java.util.EnumSet; 35 36 import jdk.internal.vm.compiler.collections.EconomicSet; 37 import jdk.internal.vm.compiler.collections.Equivalence; 38 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; 39 import org.graalvm.compiler.core.common.cfg.BlockMap; 40 import org.graalvm.compiler.debug.CounterKey; 41 import org.graalvm.compiler.debug.DebugContext; 42 import org.graalvm.compiler.debug.Indent; 43 import org.graalvm.compiler.lir.InstructionValueConsumer; 44 import org.graalvm.compiler.lir.InstructionValueProcedure; 45 import org.graalvm.compiler.lir.LIR; 46 import org.graalvm.compiler.lir.LIRInstruction; 47 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag; 48 import org.graalvm.compiler.lir.LIRInstruction.OperandMode; 49 import org.graalvm.compiler.lir.VirtualStackSlot; 50 51 import jdk.vm.ci.meta.Value; 52 53 /** 54 * Calculates the stack intervals using a worklist-based backwards data-flow analysis. 55 */ 56 final class FixPointIntervalBuilder { 57 private final BlockMap<BitSet> liveInMap; 58 private final BlockMap<BitSet> liveOutMap; 59 private final LIR lir; 60 private final int maxOpId; 61 private final StackInterval[] stackSlotMap; 62 private final EconomicSet<LIRInstruction> usePos; 63 64 /** 65 * The number of allocated stack slots. 66 */ 67 private static final CounterKey uninitializedSlots = DebugContext.counter("StackSlotAllocator[uninitializedSlots]"); 68 FixPointIntervalBuilder(LIR lir, StackInterval[] stackSlotMap, int maxOpId)69 FixPointIntervalBuilder(LIR lir, StackInterval[] stackSlotMap, int maxOpId) { 70 this.lir = lir; 71 this.stackSlotMap = stackSlotMap; 72 this.maxOpId = maxOpId; 73 liveInMap = new BlockMap<>(lir.getControlFlowGraph()); 74 liveOutMap = new BlockMap<>(lir.getControlFlowGraph()); 75 this.usePos = EconomicSet.create(Equivalence.IDENTITY); 76 } 77 78 /** 79 * Builds the lifetime intervals for {@link VirtualStackSlot virtual stack slots}, sets up 80 * {@link #stackSlotMap} and returns a set of use positions, i.e. instructions that contain 81 * virtual stack slots. 82 */ build()83 EconomicSet<LIRInstruction> build() { 84 Deque<AbstractBlockBase<?>> worklist = new ArrayDeque<>(); 85 AbstractBlockBase<?>[] blocks = lir.getControlFlowGraph().getBlocks(); 86 for (int i = blocks.length - 1; i >= 0; i--) { 87 worklist.add(blocks[i]); 88 } 89 for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) { 90 liveInMap.put(block, new BitSet(stackSlotMap.length)); 91 } 92 while (!worklist.isEmpty()) { 93 AbstractBlockBase<?> block = worklist.poll(); 94 processBlock(block, worklist); 95 } 96 return usePos; 97 } 98 99 /** 100 * Merge outSet with in-set of successors. 101 */ updateOutBlock(AbstractBlockBase<?> block)102 private boolean updateOutBlock(AbstractBlockBase<?> block) { 103 BitSet union = new BitSet(stackSlotMap.length); 104 for (AbstractBlockBase<?> succ : block.getSuccessors()) { 105 union.or(liveInMap.get(succ)); 106 } 107 BitSet outSet = liveOutMap.get(block); 108 // check if changed 109 if (outSet == null || !union.equals(outSet)) { 110 liveOutMap.put(block, union); 111 return true; 112 } 113 return false; 114 } 115 116 @SuppressWarnings("try") processBlock(AbstractBlockBase<?> block, Deque<AbstractBlockBase<?>> worklist)117 private void processBlock(AbstractBlockBase<?> block, Deque<AbstractBlockBase<?>> worklist) { 118 DebugContext debug = lir.getDebug(); 119 if (updateOutBlock(block)) { 120 try (Indent indent = debug.logAndIndent("handle block %s", block)) { 121 ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block); 122 // get out set and mark intervals 123 BitSet outSet = liveOutMap.get(block); 124 markOutInterval(outSet, getBlockEnd(instructions)); 125 printLiveSet("liveOut", outSet); 126 127 // process instructions 128 BlockClosure closure = new BlockClosure((BitSet) outSet.clone()); 129 for (int i = instructions.size() - 1; i >= 0; i--) { 130 LIRInstruction inst = instructions.get(i); 131 closure.processInstructionBottomUp(inst); 132 } 133 134 // add predecessors to work list 135 for (AbstractBlockBase<?> b : block.getPredecessors()) { 136 worklist.add(b); 137 } 138 // set in set and mark intervals 139 BitSet inSet = closure.getCurrentSet(); 140 liveInMap.put(block, inSet); 141 markInInterval(inSet, getBlockBegin(instructions)); 142 printLiveSet("liveIn", inSet); 143 } 144 } 145 } 146 147 @SuppressWarnings("try") printLiveSet(String label, BitSet liveSet)148 private void printLiveSet(String label, BitSet liveSet) { 149 DebugContext debug = lir.getDebug(); 150 if (debug.isLogEnabled()) { 151 try (Indent indent = debug.logAndIndent(label)) { 152 debug.log("%s", liveSetToString(liveSet)); 153 } 154 } 155 } 156 liveSetToString(BitSet liveSet)157 private String liveSetToString(BitSet liveSet) { 158 StringBuilder sb = new StringBuilder(); 159 for (int i = liveSet.nextSetBit(0); i >= 0; i = liveSet.nextSetBit(i + 1)) { 160 StackInterval interval = getIntervalFromStackId(i); 161 sb.append(interval.getOperand()).append(" "); 162 } 163 return sb.toString(); 164 } 165 markOutInterval(BitSet outSet, int blockEndOpId)166 private void markOutInterval(BitSet outSet, int blockEndOpId) { 167 DebugContext debug = lir.getDebug(); 168 for (int i = outSet.nextSetBit(0); i >= 0; i = outSet.nextSetBit(i + 1)) { 169 StackInterval interval = getIntervalFromStackId(i); 170 debug.log("mark live operand: %s", interval.getOperand()); 171 interval.addTo(blockEndOpId); 172 } 173 } 174 markInInterval(BitSet inSet, int blockFirstOpId)175 private void markInInterval(BitSet inSet, int blockFirstOpId) { 176 DebugContext debug = lir.getDebug(); 177 for (int i = inSet.nextSetBit(0); i >= 0; i = inSet.nextSetBit(i + 1)) { 178 StackInterval interval = getIntervalFromStackId(i); 179 debug.log("mark live operand: %s", interval.getOperand()); 180 interval.addFrom(blockFirstOpId); 181 } 182 } 183 184 private final class BlockClosure { 185 private final BitSet currentSet; 186 BlockClosure(BitSet set)187 private BlockClosure(BitSet set) { 188 currentSet = set; 189 } 190 getCurrentSet()191 private BitSet getCurrentSet() { 192 return currentSet; 193 } 194 195 /** 196 * Process all values of an instruction bottom-up, i.e. definitions before usages. Values 197 * that start or end at the current operation are not included. 198 */ 199 @SuppressWarnings("try") processInstructionBottomUp(LIRInstruction op)200 private void processInstructionBottomUp(LIRInstruction op) { 201 DebugContext debug = lir.getDebug(); 202 try (Indent indent = debug.logAndIndent("handle op %d, %s", op.id(), op)) { 203 // kills 204 op.visitEachTemp(defConsumer); 205 op.visitEachOutput(defConsumer); 206 207 // gen - values that are considered alive for this state 208 op.visitEachAlive(useConsumer); 209 op.visitEachState(useConsumer); 210 // mark locations 211 // gen 212 op.visitEachInput(useConsumer); 213 } 214 } 215 216 InstructionValueConsumer useConsumer = new InstructionValueConsumer() { 217 @Override 218 public void visitValue(LIRInstruction inst, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) { 219 if (isVirtualStackSlot(operand)) { 220 DebugContext debug = lir.getDebug(); 221 VirtualStackSlot vslot = asVirtualStackSlot(operand); 222 addUse(vslot, inst, flags); 223 addRegisterHint(inst, vslot, mode, flags, false); 224 usePos.add(inst); 225 debug.log("set operand: %s", operand); 226 currentSet.set(vslot.getId()); 227 } 228 } 229 }; 230 231 InstructionValueConsumer defConsumer = new InstructionValueConsumer() { 232 @Override 233 public void visitValue(LIRInstruction inst, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) { 234 if (isVirtualStackSlot(operand)) { 235 DebugContext debug = lir.getDebug(); 236 VirtualStackSlot vslot = asVirtualStackSlot(operand); 237 addDef(vslot, inst); 238 addRegisterHint(inst, vslot, mode, flags, true); 239 usePos.add(inst); 240 debug.log("clear operand: %s", operand); 241 currentSet.clear(vslot.getId()); 242 } 243 244 } 245 }; 246 addUse(VirtualStackSlot stackSlot, LIRInstruction inst, EnumSet<OperandFlag> flags)247 private void addUse(VirtualStackSlot stackSlot, LIRInstruction inst, EnumSet<OperandFlag> flags) { 248 StackInterval interval = getOrCreateInterval(stackSlot); 249 if (flags.contains(OperandFlag.UNINITIALIZED)) { 250 // Stack slot is marked uninitialized so we have to assume it is live all 251 // the time. 252 DebugContext debug = lir.getDebug(); 253 if (debug.isCountEnabled() && !(interval.from() == 0 && interval.to() == maxOpId)) { 254 uninitializedSlots.increment(debug); 255 } 256 interval.addFrom(0); 257 interval.addTo(maxOpId); 258 } else { 259 interval.addTo(inst.id()); 260 } 261 } 262 addDef(VirtualStackSlot stackSlot, LIRInstruction inst)263 private void addDef(VirtualStackSlot stackSlot, LIRInstruction inst) { 264 StackInterval interval = getOrCreateInterval(stackSlot); 265 interval.addFrom(inst.id()); 266 } 267 addRegisterHint(final LIRInstruction op, VirtualStackSlot targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef)268 void addRegisterHint(final LIRInstruction op, VirtualStackSlot targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef) { 269 if (flags.contains(OperandFlag.HINT)) { 270 InstructionValueProcedure proc = new InstructionValueProcedure() { 271 @Override 272 public Value doValue(LIRInstruction instruction, Value registerHint, OperandMode vaueMode, EnumSet<OperandFlag> valueFlags) { 273 if (isVirtualStackSlot(registerHint)) { 274 StackInterval from = getOrCreateInterval((VirtualStackSlot) registerHint); 275 StackInterval to = getOrCreateInterval(targetValue); 276 277 // hints always point from def to use 278 if (hintAtDef) { 279 to.setLocationHint(from); 280 } else { 281 from.setLocationHint(to); 282 } 283 DebugContext debug = lir.getDebug(); 284 if (debug.isLogEnabled()) { 285 debug.log("operation %s at opId %d: added hint from interval %s to %s", op, op.id(), from, to); 286 } 287 288 return registerHint; 289 } 290 return null; 291 } 292 }; 293 op.forEachRegisterHint(targetValue, mode, proc); 294 } 295 } 296 297 } 298 get(VirtualStackSlot stackSlot)299 private StackInterval get(VirtualStackSlot stackSlot) { 300 return stackSlotMap[stackSlot.getId()]; 301 } 302 put(VirtualStackSlot stackSlot, StackInterval interval)303 private void put(VirtualStackSlot stackSlot, StackInterval interval) { 304 stackSlotMap[stackSlot.getId()] = interval; 305 } 306 getOrCreateInterval(VirtualStackSlot stackSlot)307 private StackInterval getOrCreateInterval(VirtualStackSlot stackSlot) { 308 StackInterval interval = get(stackSlot); 309 if (interval == null) { 310 interval = new StackInterval(stackSlot, stackSlot.getValueKind()); 311 put(stackSlot, interval); 312 } 313 return interval; 314 } 315 getIntervalFromStackId(int id)316 private StackInterval getIntervalFromStackId(int id) { 317 return stackSlotMap[id]; 318 } 319 getBlockBegin(ArrayList<LIRInstruction> instructions)320 private static int getBlockBegin(ArrayList<LIRInstruction> instructions) { 321 return instructions.get(0).id(); 322 } 323 getBlockEnd(ArrayList<LIRInstruction> instructions)324 private static int getBlockEnd(ArrayList<LIRInstruction> instructions) { 325 return instructions.get(instructions.size() - 1).id() + 1; 326 } 327 328 } 329