1 /* 2 * Copyright (c) 2015, 2017, 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 jdk.vm.ci.code.ValueUtil.isIllegal; 26 import static jdk.vm.ci.code.ValueUtil.isRegister; 27 import static org.graalvm.compiler.lir.LIRValueUtil.asVariable; 28 import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue; 29 import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue; 30 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable; 31 import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot; 32 33 import java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.EnumSet; 36 37 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig; 38 import org.graalvm.compiler.core.common.alloc.Trace; 39 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult; 40 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; 41 import org.graalvm.compiler.debug.DebugContext; 42 import org.graalvm.compiler.debug.GraalError; 43 import org.graalvm.compiler.debug.Indent; 44 import org.graalvm.compiler.lir.ConstantValue; 45 import org.graalvm.compiler.lir.InstructionValueProcedure; 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.StandardOp; 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.GlobalLivenessInfo; 55 import org.graalvm.compiler.lir.alloc.trace.ShadowedRegisterValue; 56 import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan; 57 import org.graalvm.compiler.lir.gen.LIRGenerationResult; 58 import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory; 59 60 import jdk.vm.ci.code.RegisterValue; 61 import jdk.vm.ci.code.StackSlot; 62 import jdk.vm.ci.code.TargetDescription; 63 import jdk.vm.ci.meta.AllocatableValue; 64 import jdk.vm.ci.meta.Value; 65 66 /** 67 * Specialization of {@link org.graalvm.compiler.lir.alloc.lsra.LinearScanAssignLocationsPhase} that 68 * inserts {@link ShadowedRegisterValue}s to describe {@link RegisterValue}s that are also available 69 * on the {@link StackSlot stack}. 70 */ 71 final class TraceLinearScanAssignLocationsPhase extends TraceLinearScanAllocationPhase { 72 73 @Override 74 protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Trace trace, MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig, 75 TraceBuilderResult traceBuilderResult, TraceLinearScan allocator) { 76 new Assigner(allocator, spillMoveFactory).assign(); 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 return getLocation(op, interval, mode); 110 } 111 112 private Value getLocation(LIRInstruction op, TraceInterval interval, OperandMode mode) { 113 if (isIllegal(interval.location()) && interval.canMaterialize()) { 114 if (op instanceof LabelOp) { 115 /* 116 * Spilled materialized value in a LabelOp (i.e. incoming): no need for move 117 * resolution so we can ignore it. 118 */ 119 return Value.ILLEGAL; 120 } 121 assert mode != OperandMode.DEF; 122 return new ConstantValue(allocator.getKind(interval), interval.getMaterializedValue()); 123 } 124 return interval.location(); 125 } 126 127 /** 128 * @param op 129 * @param operand 130 * @param valueMode 131 * @param flags 132 * @see InstructionValueProcedure#doValue(LIRInstruction, Value, OperandMode, EnumSet) 133 */ 134 private Value debugInfoProcedure(LIRInstruction op, Value operand, OperandMode valueMode, EnumSet<OperandFlag> flags) { 135 if (isVirtualStackSlot(operand)) { 136 return operand; 137 } 138 int tempOpId = op.id(); 139 OperandMode mode = OperandMode.USE; 140 AbstractBlockBase<?> block = allocator.blockForId(tempOpId); 141 if (block.getSuccessorCount() == 1 && tempOpId == allocator.getLastLirInstructionId(block)) { 142 /* 143 * Generating debug information for the last instruction of a block. If this 144 * instruction is a branch, spill moves are inserted before this branch and so the 145 * wrong operand would be returned (spill moves at block boundaries are not 146 * considered in the live ranges of intervals). 147 * 148 * Solution: use the first opId of the branch target block instead. 149 */ 150 final LIRInstruction instr = allocator.getLIR().getLIRforBlock(block).get(allocator.getLIR().getLIRforBlock(block).size() - 1); 151 if (instr instanceof StandardOp.JumpOp) { 152 throw GraalError.unimplemented("DebugInfo on jumps are not supported!"); 153 } 154 } 155 156 /* 157 * Get current location of operand. The operand must be live because debug information 158 * is considered when building the intervals if the interval is not live, 159 * colorLirOperand will cause an assert on failure. 160 */ 161 Value result = colorLirOperand(op, (Variable) operand, mode); 162 assert !allocator.hasCall(tempOpId) || isStackSlotValue(result) || isConstantValue(result) || !allocator.isCallerSave(result) : "cannot have caller-save register operands at calls"; 163 return result; 164 } 165 166 @SuppressWarnings("try") 167 private void assignBlock(AbstractBlockBase<?> block) { 168 DebugContext debug = allocator.getDebug(); 169 try (Indent indent2 = debug.logAndIndent("assign locations in block B%d", block.getId())) { 170 ArrayList<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block); 171 handleBlockBegin(block, instructions); 172 int numInst = instructions.size(); 173 boolean hasDead = false; 174 175 for (int j = 0; j < numInst; j++) { 176 final LIRInstruction op = instructions.get(j); 177 if (op == null) { 178 /* 179 * this can happen when spill-moves are removed in eliminateSpillMoves 180 */ 181 hasDead = true; 182 } else if (assignLocations(op, instructions, j)) { 183 hasDead = true; 184 } 185 } 186 handleBlockEnd(block, instructions); 187 188 if (hasDead) { 189 // Remove null values from the list. 190 instructions.removeAll(Collections.singleton(null)); 191 } 192 } 193 } 194 195 private void handleBlockBegin(AbstractBlockBase<?> block, ArrayList<LIRInstruction> instructions) { 196 if (allocator.hasInterTracePredecessor(block)) { 197 /* Only materialize the locations array if there is an incoming inter-trace edge. */ 198 assert instructions.equals(allocator.getLIR().getLIRforBlock(block)); 199 GlobalLivenessInfo li = allocator.getGlobalLivenessInfo(); 200 LIRInstruction instruction = instructions.get(0); 201 OperandMode mode = OperandMode.DEF; 202 int[] live = li.getBlockIn(block); 203 Value[] values = calculateBlockBoundaryValues(instruction, live, mode); 204 li.setInLocations(block, values); 205 } 206 } 207 208 private void handleBlockEnd(AbstractBlockBase<?> block, ArrayList<LIRInstruction> instructions) { 209 if (allocator.hasInterTraceSuccessor(block)) { 210 /* Only materialize the locations array if there is an outgoing inter-trace edge. */ 211 assert instructions.equals(allocator.getLIR().getLIRforBlock(block)); 212 GlobalLivenessInfo li = allocator.getGlobalLivenessInfo(); 213 LIRInstruction instruction = instructions.get(instructions.size() - 1); 214 OperandMode mode = OperandMode.USE; 215 int[] live = li.getBlockOut(block); 216 Value[] values = calculateBlockBoundaryValues(instruction, live, mode); 217 li.setOutLocations(block, values); 218 } 219 } 220 221 private Value[] calculateBlockBoundaryValues(LIRInstruction instruction, int[] live, OperandMode mode) { 222 Value[] values = new Value[live.length]; 223 for (int i = 0; i < live.length; i++) { 224 TraceInterval interval = allocator.intervalFor(live[i]); 225 Value val = valueAtBlockBoundary(instruction, interval, mode); 226 values[i] = val; 227 } 228 return values; 229 } 230 231 private Value valueAtBlockBoundary(LIRInstruction instruction, TraceInterval interval, OperandMode mode) { 232 if (mode == OperandMode.DEF && interval == null) { 233 // not needed in this trace 234 return Value.ILLEGAL; 235 } 236 assert interval != null : "interval must exist"; 237 TraceInterval splitInterval = interval.getSplitChildAtOpIdOrNull(instruction.id(), mode); 238 if (splitInterval == null) { 239 assert mode == OperandMode.DEF : String.format("Not split child at %d for interval %s", instruction.id(), interval); 240 // not needed in this branch 241 return Value.ILLEGAL; 242 } 243 244 if (splitInterval.inMemoryAt(instruction.id()) && isRegister(splitInterval.location())) { 245 return new ShadowedRegisterValue((RegisterValue) splitInterval.location(), splitInterval.spillSlot()); 246 } 247 return getLocation(instruction, splitInterval, mode); 248 } 249 250 private final InstructionValueProcedure assignProc = new InstructionValueProcedure() { 251 @Override 252 public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) { 253 if (isVariable(value)) { 254 return colorLirOperand(instruction, (Variable) value, mode); 255 } 256 return value; 257 } 258 }; 259 private final InstructionValueProcedure debugInfoValueProc = new InstructionValueProcedure() { 260 @Override 261 public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) { 262 return debugInfoProcedure(instruction, value, mode, flags); 263 } 264 }; 265 266 /** 267 * Assigns the operand of an {@link LIRInstruction}. 268 * 269 * @param op The {@link LIRInstruction} that should be colored. 270 * @param j The index of {@code op} in the {@code instructions} list. 271 * @param instructions The instructions of the current block. 272 * @return {@code true} if the instruction was deleted. 273 */ 274 private boolean assignLocations(LIRInstruction op, ArrayList<LIRInstruction> instructions, int j) { 275 assert op != null && instructions.get(j) == op; 276 277 // remove useless moves 278 if (MoveOp.isMoveOp(op)) { 279 AllocatableValue result = MoveOp.asMoveOp(op).getResult(); 280 if (isVariable(result) && allocator.isMaterialized(asVariable(result), op.id(), OperandMode.DEF)) { 281 /* 282 * This happens if a materializable interval is originally not spilled but then 283 * kicked out in LinearScanWalker.splitForSpilling(). When kicking out such an 284 * interval this move operation was already generated. 285 */ 286 instructions.set(j, null); 287 return true; 288 } 289 } 290 291 op.forEachInput(assignProc); 292 op.forEachAlive(assignProc); 293 op.forEachTemp(assignProc); 294 op.forEachOutput(assignProc); 295 296 // compute reference map and debug information 297 op.forEachState(debugInfoValueProc); 298 299 // remove useless moves 300 if (ValueMoveOp.isValueMoveOp(op)) { 301 ValueMoveOp move = ValueMoveOp.asValueMoveOp(op); 302 if (move.getInput().equals(move.getResult())) { 303 instructions.set(j, null); 304 return true; 305 } 306 if (isStackSlotValue(move.getInput()) && isStackSlotValue(move.getResult())) { 307 // rewrite stack to stack moves 308 instructions.set(j, spillMoveFactory.createStackMove(move.getResult(), move.getInput())); 309 } 310 } 311 return false; 312 } 313 314 @SuppressWarnings("try") 315 private void assign() { 316 try (Indent indent = allocator.getDebug().logAndIndent("assign locations")) { 317 for (AbstractBlockBase<?> block : allocator.sortedBlocks()) { 318 assignBlock(block); 319 } 320 } 321 } 322 } 323 }