001/* 002 * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.lir.alloc.trace; 024 025import static jdk.internal.jvmci.code.ValueUtil.*; 026 027import java.util.*; 028 029import jdk.internal.jvmci.code.*; 030import jdk.internal.jvmci.common.*; 031import jdk.internal.jvmci.meta.*; 032 033import com.oracle.graal.debug.*; 034import com.oracle.graal.lir.*; 035import com.oracle.graal.lir.alloc.lsra.*; 036import com.oracle.graal.lir.framemap.*; 037import com.oracle.graal.lir.gen.*; 038import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory; 039 040/** 041 */ 042class TraceGlobalMoveResolver { 043 044 private int insertIdx; 045 private LIRInsertionBuffer insertionBuffer; // buffer where moves are inserted 046 047 private final List<Value> mappingFrom; 048 private final List<AllocatableValue> mappingTo; 049 private final int[] registerBlocked; 050 private static final int STACK_SLOT_IN_CALLER_FRAME_IDX = -1; 051 private int[] stackBlocked; 052 private final int firstVirtualStackIndex; 053 private final SpillMoveFactory spillMoveFactory; 054 private final FrameMapBuilder frameMapBuilder; 055 056 private void setValueBlocked(Value location, int direction) { 057 assert direction == 1 || direction == -1 : "out of bounds"; 058 if (isStackSlotValue(location)) { 059 int stackIdx = getStackArrayIndex(asStackSlotValue(location)); 060 if (stackIdx == STACK_SLOT_IN_CALLER_FRAME_IDX) { 061 // incoming stack arguments can be ignored 062 return; 063 } 064 if (stackIdx >= stackBlocked.length) { 065 stackBlocked = Arrays.copyOf(stackBlocked, stackIdx + 1); 066 } 067 stackBlocked[stackIdx] += direction; 068 } else { 069 assert direction == 1 || direction == -1 : "out of bounds"; 070 if (isRegister(location)) { 071 registerBlocked[asRegister(location).number] += direction; 072 } else { 073 throw JVMCIError.shouldNotReachHere("unhandled value " + location); 074 } 075 } 076 } 077 078 private int valueBlocked(Value location) { 079 if (isStackSlotValue(location)) { 080 int stackIdx = getStackArrayIndex(asStackSlotValue(location)); 081 if (stackIdx == STACK_SLOT_IN_CALLER_FRAME_IDX) { 082 // incoming stack arguments are always blocked (aka they can not be written) 083 return 1; 084 } 085 if (stackIdx >= stackBlocked.length) { 086 return 0; 087 } 088 return stackBlocked[stackIdx]; 089 } 090 if (isRegister(location)) { 091 return registerBlocked[asRegister(location).number]; 092 } 093 throw JVMCIError.shouldNotReachHere("unhandled value " + location); 094 } 095 096 private static boolean areMultipleReadsAllowed() { 097 return true; 098 } 099 100 private boolean hasMappings() { 101 return mappingFrom.size() > 0; 102 } 103 104 private SpillMoveFactory getSpillMoveFactory() { 105 return spillMoveFactory; 106 } 107 108 private Register[] getRegisters() { 109 return frameMapBuilder.getRegisterConfig().getAllocatableRegisters(); 110 } 111 112 public TraceGlobalMoveResolver(LIRGenerationResult res, SpillMoveFactory spillMoveFactory, Architecture arch) { 113 114 this.mappingFrom = new ArrayList<>(8); 115 this.mappingTo = new ArrayList<>(8); 116 this.insertIdx = -1; 117 this.insertionBuffer = new LIRInsertionBuffer(); 118 119 this.frameMapBuilder = res.getFrameMapBuilder(); 120 this.spillMoveFactory = spillMoveFactory; 121 this.registerBlocked = new int[arch.getRegisters().length]; 122 123 FrameMapBuilderTool frameMapBuilderTool = (FrameMapBuilderTool) frameMapBuilder; 124 this.stackBlocked = new int[frameMapBuilderTool.getNumberOfStackSlots()]; 125 126 FrameMap frameMap = frameMapBuilderTool.getFrameMap(); 127 this.firstVirtualStackIndex = !frameMap.frameNeedsAllocating() ? 0 : frameMap.currentFrameSize() + 1; 128 } 129 130 private boolean checkEmpty() { 131 for (int i = 0; i < stackBlocked.length; i++) { 132 assert stackBlocked[i] == 0 : "stack map must be empty before and after processing"; 133 } 134 assert mappingFrom.size() == 0 && mappingTo.size() == 0 : "list must be empty before and after processing"; 135 for (int i = 0; i < getRegisters().length; i++) { 136 assert registerBlocked[i] == 0 : "register map must be empty before and after processing"; 137 } 138 return true; 139 } 140 141 private boolean verifyBeforeResolve() { 142 assert mappingFrom.size() == mappingTo.size() : "length must be equal"; 143 assert insertIdx != -1 : "insert position not set"; 144 145 int i; 146 int j; 147 if (!areMultipleReadsAllowed()) { 148 for (i = 0; i < mappingFrom.size(); i++) { 149 for (j = i + 1; j < mappingFrom.size(); j++) { 150 assert mappingFrom.get(i) == null || mappingFrom.get(i) != mappingFrom.get(j) : "cannot read from same interval twice"; 151 } 152 } 153 } 154 155 for (i = 0; i < mappingTo.size(); i++) { 156 for (j = i + 1; j < mappingTo.size(); j++) { 157 assert mappingTo.get(i) != mappingTo.get(j) : "cannot write to same interval twice"; 158 } 159 } 160 161 for (i = 0; i < mappingTo.size(); i++) { 162 Value to = mappingTo.get(i); 163 assert !isStackSlotValue(to) || getStackArrayIndex(asStackSlotValue(to)) != STACK_SLOT_IN_CALLER_FRAME_IDX : "Cannot move to in argument: " + to; 164 } 165 166 HashSet<Value> usedRegs = new HashSet<>(); 167 if (!areMultipleReadsAllowed()) { 168 for (i = 0; i < mappingFrom.size(); i++) { 169 Value from = mappingFrom.get(i); 170 if (from != null && !isIllegal(from)) { 171 boolean unique = usedRegs.add(from); 172 assert unique : "cannot read from same register twice"; 173 } 174 } 175 } 176 177 usedRegs.clear(); 178 for (i = 0; i < mappingTo.size(); i++) { 179 Value to = mappingTo.get(i); 180 if (isIllegal(to)) { 181 // After insertion the location may become illegal, so don't check it since multiple 182 // intervals might be illegal. 183 continue; 184 } 185 boolean unique = usedRegs.add(to); 186 assert unique : "cannot write to same register twice"; 187 } 188 189 return true; 190 } 191 192 // mark assignedReg and assignedRegHi of the interval as blocked 193 private void block(Value location) { 194 if (mightBeBlocked(location)) { 195 assert areMultipleReadsAllowed() || valueBlocked(location) == 0 : "location already marked as used: " + location; 196 setValueBlocked(location, 1); 197 Debug.log("block %s", location); 198 } 199 } 200 201 // mark assignedReg and assignedRegHi of the interval as unblocked 202 private void unblock(Value location) { 203 if (mightBeBlocked(location)) { 204 assert valueBlocked(location) > 0 : "location already marked as unused: " + location; 205 setValueBlocked(location, -1); 206 Debug.log("unblock %s", location); 207 } 208 } 209 210 /** 211 * Checks if the {@linkplain Interval#location() location} of {@code to} is not blocked or is 212 * only blocked by {@code from}. 213 */ 214 private boolean safeToProcessMove(Value fromLocation, Value toLocation) { 215 if (mightBeBlocked(toLocation)) { 216 if ((valueBlocked(toLocation) > 1 || (valueBlocked(toLocation) == 1 && !isMoveToSelf(fromLocation, toLocation)))) { 217 return false; 218 } 219 } 220 221 return true; 222 } 223 224 public static boolean isMoveToSelf(Value from, Value to) { 225 assert to != null; 226 if (to.equals(from)) { 227 return true; 228 } 229 if (from != null && isRegister(from) && isRegister(to) && asRegister(from).equals(asRegister(to))) { 230 assert LIRKind.verifyMoveKinds(to.getLIRKind(), from.getLIRKind()) : String.format("Same register but Kind mismatch %s <- %s", to, from); 231 return true; 232 } 233 return false; 234 } 235 236 private static boolean mightBeBlocked(Value location) { 237 return isRegister(location) || isStackSlotValue(location); 238 } 239 240 private void createInsertionBuffer(List<LIRInstruction> list) { 241 assert !insertionBuffer.initialized() : "overwriting existing buffer"; 242 insertionBuffer.init(list); 243 } 244 245 private void appendInsertionBuffer() { 246 if (insertionBuffer.initialized()) { 247 insertionBuffer.finish(); 248 } 249 assert !insertionBuffer.initialized() : "must be uninitialized now"; 250 251 insertIdx = -1; 252 } 253 254 private void insertMove(Value fromOperand, AllocatableValue toOperand) { 255 assert !fromOperand.equals(toOperand) : "from and to are equal: " + fromOperand + " vs. " + toOperand; 256 assert LIRKind.verifyMoveKinds(fromOperand.getLIRKind(), fromOperand.getLIRKind()) : "move between different types"; 257 assert insertIdx != -1 : "must setup insert position first"; 258 259 insertionBuffer.append(insertIdx, createMove(fromOperand, toOperand)); 260 261 if (Debug.isLogEnabled()) { 262 Debug.log("insert move from %s to %s at %d", fromOperand, toOperand, insertIdx); 263 } 264 } 265 266 /** 267 * @param fromOpr {@link Interval#operand operand} of the {@code from} interval 268 * @param toOpr {@link Interval#operand operand} of the {@code to} interval 269 */ 270 private LIRInstruction createMove(Value fromOpr, AllocatableValue toOpr) { 271 if (isStackSlotValue(toOpr) && isStackSlotValue(fromOpr)) { 272 return getSpillMoveFactory().createStackMove(toOpr, fromOpr); 273 } 274 return getSpillMoveFactory().createMove(toOpr, fromOpr); 275 } 276 277 private void resolveMappings() { 278 try (Indent indent = Debug.logAndIndent("resolveMapping")) { 279 assert verifyBeforeResolve(); 280 if (Debug.isLogEnabled()) { 281 printMapping(); 282 } 283 284 // Block all registers that are used as input operands of a move. 285 // When a register is blocked, no move to this register is emitted. 286 // This is necessary for detecting cycles in moves. 287 for (int i = mappingFrom.size() - 1; i >= 0; i--) { 288 Value from = mappingFrom.get(i); 289 block(from); 290 } 291 292 int spillCandidate = -1; 293 while (mappingFrom.size() > 0) { 294 boolean processedInterval = false; 295 296 for (int i = mappingFrom.size() - 1; i >= 0; i--) { 297 Value fromInterval = mappingFrom.get(i); 298 AllocatableValue toInterval = mappingTo.get(i); 299 300 Value fromLocation = fromInterval; 301 AllocatableValue toLocation = toInterval; 302 if (safeToProcessMove(fromLocation, toLocation)) { 303 // this interval can be processed because target is free 304 insertMove(fromLocation, toLocation); 305 unblock(fromLocation); 306 mappingFrom.remove(i); 307 mappingTo.remove(i); 308 309 processedInterval = true; 310 } else if (fromInterval != null && isRegister(fromLocation)) { 311 // this interval cannot be processed now because target is not free 312 // it starts in a register, so it is a possible candidate for spilling 313 spillCandidate = i; 314 } 315 } 316 317 if (!processedInterval) { 318 breakCycle(spillCandidate); 319 } 320 } 321 } 322 323 // check that all intervals have been processed 324 assert checkEmpty(); 325 } 326 327 private void breakCycle(int spillCandidate) { 328 // no move could be processed because there is a cycle in the move list 329 // (e.g. r1 . r2, r2 . r1), so one interval must be spilled to memory 330 assert spillCandidate != -1 : "no interval in register for spilling found"; 331 332 // create a new spill interval and assign a stack slot to it 333 Value from = mappingFrom.get(spillCandidate); 334 try (Indent indent = Debug.logAndIndent("BreakCycle: %s", from)) { 335 StackSlotValue spillSlot = frameMapBuilder.allocateSpillSlot(from.getLIRKind()); 336 if (Debug.isLogEnabled()) { 337 Debug.log("created new slot for spilling: %s", spillSlot); 338 } 339 // insert a move from register to stack and update the mapping 340 insertMove(from, spillSlot); 341 block(spillSlot); 342 mappingFrom.set(spillCandidate, spillSlot); 343 unblock(from); 344 } 345 } 346 347 private void printMapping() { 348 try (Indent indent = Debug.logAndIndent("Mapping")) { 349 for (int i = mappingFrom.size() - 1; i >= 0; i--) { 350 Debug.log("move %s <- %s", mappingTo.get(i), mappingFrom.get(i)); 351 } 352 } 353 } 354 355 public void setInsertPosition(List<LIRInstruction> insertList, int insertIdx) { 356 assert this.insertIdx == -1 : "use moveInsertPosition instead of setInsertPosition when data already set"; 357 358 createInsertionBuffer(insertList); 359 this.insertIdx = insertIdx; 360 } 361 362 public void addMapping(Value from, AllocatableValue to) { 363 if (Debug.isLogEnabled()) { 364 Debug.log("add move mapping from %s to %s", from, to); 365 } 366 367 assert !from.equals(to) : "from and to interval equal: " + from; 368 assert LIRKind.verifyMoveKinds(to.getLIRKind(), from.getLIRKind()) : String.format("Kind mismatch: %s vs. %s, from=%s, to=%s", from.getLIRKind(), to.getLIRKind(), from, to); 369 mappingFrom.add(from); 370 mappingTo.add(to); 371 } 372 373 public void resolveAndAppendMoves() { 374 if (hasMappings()) { 375 resolveMappings(); 376 } 377 appendInsertionBuffer(); 378 } 379 380 private int getStackArrayIndex(StackSlotValue stackSlotValue) { 381 if (isStackSlot(stackSlotValue)) { 382 return getStackArrayIndex(asStackSlot(stackSlotValue)); 383 } 384 if (isVirtualStackSlot(stackSlotValue)) { 385 return getStackArrayIndex(asVirtualStackSlot(stackSlotValue)); 386 } 387 throw JVMCIError.shouldNotReachHere("Unhandled StackSlotValue: " + stackSlotValue); 388 } 389 390 private int getStackArrayIndex(StackSlot stackSlot) { 391 int stackIdx; 392 if (stackSlot.isInCallerFrame()) { 393 // incoming stack arguments can be ignored 394 stackIdx = STACK_SLOT_IN_CALLER_FRAME_IDX; 395 } else { 396 assert stackSlot.getRawAddFrameSize() : "Unexpected stack slot: " + stackSlot; 397 int offset = -stackSlot.getRawOffset(); 398 assert 0 <= offset && offset < firstVirtualStackIndex : String.format("Wrong stack slot offset: %d (first virtual stack slot index: %d", offset, firstVirtualStackIndex); 399 stackIdx = offset; 400 } 401 return stackIdx; 402 } 403 404 private int getStackArrayIndex(VirtualStackSlot virtualStackSlot) { 405 return firstVirtualStackIndex + virtualStackSlot.getId(); 406 } 407 408}