1 /* 2 * Copyright (c) 2015, 2016, 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.isConstantValue; 26 import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue; 27 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable; 28 import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot; 29 import static org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAshareSpillInformation; 30 import static jdk.vm.ci.code.ValueUtil.isIllegal; 31 import static jdk.vm.ci.code.ValueUtil.isRegister; 32 33 import java.util.Collections; 34 import java.util.EnumSet; 35 import java.util.List; 36 37 import org.graalvm.compiler.core.common.alloc.Trace; 38 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; 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.ConstantValue; 43 import org.graalvm.compiler.lir.InstructionValueProcedure; 44 import org.graalvm.compiler.lir.LIRFrameState; 45 import org.graalvm.compiler.lir.LIRInstruction; 46 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag; 47 import org.graalvm.compiler.lir.LIRInstruction.OperandMode; 48 import org.graalvm.compiler.lir.StandardOp; 49 import org.graalvm.compiler.lir.StandardOp.BlockEndOp; 50 import org.graalvm.compiler.lir.StandardOp.LabelOp; 51 import org.graalvm.compiler.lir.StandardOp.MoveOp; 52 import org.graalvm.compiler.lir.StandardOp.ValueMoveOp; 53 import org.graalvm.compiler.lir.Variable; 54 import org.graalvm.compiler.lir.alloc.trace.ShadowedRegisterValue; 55 import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan; 56 import org.graalvm.compiler.lir.gen.LIRGenerationResult; 57 import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory; 58 59 import jdk.vm.ci.code.RegisterValue; 60 import jdk.vm.ci.code.StackSlot; 61 import jdk.vm.ci.code.TargetDescription; 62 import jdk.vm.ci.meta.AllocatableValue; 63 import jdk.vm.ci.meta.Value; 64 65 /** 66 * Specialization of {@link org.graalvm.compiler.lir.alloc.lsra.LinearScanAssignLocationsPhase} that 67 * inserts {@link ShadowedRegisterValue}s to describe {@link RegisterValue}s that are also available 68 * on the {@link StackSlot stack}. 69 */ 70 final class TraceLinearScanAssignLocationsPhase extends TraceLinearScanAllocationPhase { 71 72 @Override 73 protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Trace trace, TraceLinearScanAllocationContext context) { 74 TraceLinearScan allocator = context.allocator; 75 MoveFactory spillMoveFactory = context.spillMoveFactory; 76 new Assigner(allocator, spillMoveFactory).assignLocations(); 77 } 78 79 private static final class Assigner { 80 private final TraceLinearScan allocator; 81 private final MoveFactory spillMoveFactory; 82 83 private Assigner(TraceLinearScan allocator, MoveFactory spillMoveFactory) { 84 this.allocator = allocator; 85 this.spillMoveFactory = spillMoveFactory; 86 } 87 88 /** 89 * Assigns the allocated location for an LIR instruction operand back into the instruction. 90 * 91 * @param op current {@link LIRInstruction} 92 * @param operand an LIR instruction operand 93 * @param mode the usage mode for {@code operand} by the instruction 94 * @return the location assigned for the operand 95 */ 96 private Value colorLirOperand(LIRInstruction op, Variable operand, OperandMode mode) { 97 int opId = op.id(); 98 TraceInterval interval = allocator.intervalFor(operand); 99 assert interval != null : "interval must exist"; 100 101 if (opId != -1) { 102 /* 103 * Operands are not changed when an interval is split during allocation, so search 104 * the right interval here. 105 */ 106 interval = allocator.splitChildAtOpId(interval, opId, mode); 107 } 108 109 if (isIllegal(interval.location()) && interval.canMaterialize()) { 110 if (op instanceof LabelOp) { 111 /* 112 * Spilled materialized value in a LabelOp (i.e. incoming): no need for move 113 * resolution so we can ignore it. 114 */ 115 return Value.ILLEGAL; 116 } 117 assert mode != OperandMode.DEF; 118 return new ConstantValue(interval.kind(), interval.getMaterializedValue()); 119 } 120 return interval.location(); 121 } 122 123 /** 124 * @param op 125 * @param operand 126 * @param valueMode 127 * @param flags 128 * @see InstructionValueProcedure#doValue(LIRInstruction, Value, OperandMode, EnumSet) 129 */ 130 private Value debugInfoProcedure(LIRInstruction op, Value operand, OperandMode valueMode, EnumSet<OperandFlag> flags) { 131 if (isVirtualStackSlot(operand)) { 132 return operand; 133 } 134 int tempOpId = op.id(); 135 OperandMode mode = OperandMode.USE; 136 AbstractBlockBase<?> block = allocator.blockForId(tempOpId); 137 if (block.getSuccessorCount() == 1 && tempOpId == allocator.getLastLirInstructionId(block)) { 138 /* 139 * Generating debug information for the last instruction of a block. If this 140 * instruction is a branch, spill moves are inserted before this branch and so the 141 * wrong operand would be returned (spill moves at block boundaries are not 142 * considered in the live ranges of intervals). 143 * 144 * Solution: use the first opId of the branch target block instead. 145 */ 146 final LIRInstruction instr = allocator.getLIR().getLIRforBlock(block).get(allocator.getLIR().getLIRforBlock(block).size() - 1); 147 if (instr instanceof StandardOp.JumpOp) { 148 throw GraalError.unimplemented("DebugInfo on jumps are not supported!"); 149 } 150 } 151 152 /* 153 * Get current location of operand. The operand must be live because debug information 154 * is considered when building the intervals if the interval is not live, 155 * colorLirOperand will cause an assert on failure. 156 */ 157 Value result = colorLirOperand(op, (Variable) operand, mode); 158 assert !allocator.hasCall(tempOpId) || isStackSlotValue(result) || isConstantValue(result) || !allocator.isCallerSave(result) : "cannot have caller-save register operands at calls"; 159 return result; 160 } 161 162 private void computeDebugInfo(final LIRInstruction op, LIRFrameState info) { 163 info.forEachState(op, this::debugInfoProcedure); 164 } 165 166 private void assignLocations(List<LIRInstruction> instructions) { 167 int numInst = instructions.size(); 168 boolean hasDead = false; 169 170 for (int j = 0; j < numInst; j++) { 171 final LIRInstruction op = instructions.get(j); 172 if (op == null) { 173 /* 174 * this can happen when spill-moves are removed in eliminateSpillMoves 175 */ 176 hasDead = true; 177 } else if (assignLocations(op, instructions, j)) { 178 hasDead = true; 179 } 180 } 181 182 if (hasDead) { 183 // Remove null values from the list. 184 instructions.removeAll(Collections.singleton(null)); 185 } 186 } 187 188 private final InstructionValueProcedure assignProc = new InstructionValueProcedure() { 189 @Override 190 public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) { 191 if (isVariable(value)) { 192 return colorLirOperand(instruction, (Variable) value, mode); 193 } 194 return value; 195 } 196 }; 197 198 /** 199 * Assigns the operand of an {@link LIRInstruction}. 200 * 201 * @param op The {@link LIRInstruction} that should be colored. 202 * @param j The index of {@code op} in the {@code instructions} list. 203 * @param instructions The instructions of the current block. 204 * @return {@code true} if the instruction was deleted. 205 */ 206 private boolean assignLocations(LIRInstruction op, List<LIRInstruction> instructions, int j) { 207 assert op != null && instructions.get(j) == op; 208 if (TraceRAshareSpillInformation.getValue()) { 209 if (op instanceof BlockEndOp) { 210 ((BlockEndOp) op).forEachOutgoingValue(colorOutgoingIncomingValues); 211 } else if (op instanceof LabelOp) { 212 ((LabelOp) op).forEachIncomingValue(colorOutgoingIncomingValues); 213 } 214 } 215 216 // remove useless moves 217 if (op instanceof MoveOp) { 218 AllocatableValue result = ((MoveOp) op).getResult(); 219 if (isVariable(result) && allocator.isMaterialized(result, op.id(), OperandMode.DEF)) { 220 /* 221 * This happens if a materializable interval is originally not spilled but then 222 * kicked out in LinearScanWalker.splitForSpilling(). When kicking out such an 223 * interval this move operation was already generated. 224 */ 225 instructions.set(j, null); 226 return true; 227 } 228 } 229 230 op.forEachInput(assignProc); 231 op.forEachAlive(assignProc); 232 op.forEachTemp(assignProc); 233 op.forEachOutput(assignProc); 234 235 // compute reference map and debug information 236 op.forEachState((inst, state) -> computeDebugInfo(inst, state)); 237 238 // remove useless moves 239 if (op instanceof ValueMoveOp) { 240 ValueMoveOp move = (ValueMoveOp) op; 241 if (move.getInput().equals(move.getResult())) { 242 instructions.set(j, null); 243 return true; 244 } 245 if (isStackSlotValue(move.getInput()) && isStackSlotValue(move.getResult())) { 246 // rewrite stack to stack moves 247 instructions.set(j, spillMoveFactory.createStackMove(move.getResult(), move.getInput())); 248 return true; 249 } 250 } 251 return false; 252 } 253 254 @SuppressWarnings("try") 255 private void assignLocations() { 256 try (Indent indent = Debug.logAndIndent("assign locations")) { 257 for (AbstractBlockBase<?> block : allocator.sortedBlocks()) { 258 try (Indent indent2 = Debug.logAndIndent("assign locations in block B%d", block.getId())) { 259 assignLocations(allocator.getLIR().getLIRforBlock(block)); 260 } 261 } 262 } 263 } 264 265 private final InstructionValueProcedure colorOutgoingIncomingValues = new InstructionValueProcedure() { 266 267 @Override 268 public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) { 269 if (isVariable(value)) { 270 TraceInterval interval = allocator.intervalFor(value); 271 assert interval != null : "interval must exist"; 272 interval = allocator.splitChildAtOpId(interval, instruction.id(), mode); 273 274 if (interval.inMemoryAt(instruction.id()) && isRegister(interval.location())) { 275 return new ShadowedRegisterValue((RegisterValue) interval.location(), interval.spillSlot()); 276 } 277 } 278 return value; 279 } 280 }; 281 } 282 }