1 /* 2 * Copyright (c) 2009, 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 jdk.vm.ci.code.ValueUtil.asRegister; 26 import static jdk.vm.ci.code.ValueUtil.isIllegal; 27 import static jdk.vm.ci.code.ValueUtil.isRegister; 28 29 import java.util.ArrayList; 30 31 import org.graalvm.compiler.core.common.LIRKind; 32 import org.graalvm.compiler.debug.Debug; 33 import org.graalvm.compiler.debug.DebugCounter; 34 import org.graalvm.compiler.debug.GraalError; 35 import org.graalvm.compiler.debug.Indent; 36 import org.graalvm.compiler.lir.LIRInsertionBuffer; 37 import org.graalvm.compiler.lir.LIRInstruction; 38 import org.graalvm.compiler.lir.LIRValueUtil; 39 import org.graalvm.compiler.lir.gen.LIRGenerationResult; 40 import org.graalvm.util.Equivalence; 41 import org.graalvm.util.EconomicSet; 42 43 import jdk.vm.ci.meta.AllocatableValue; 44 import jdk.vm.ci.meta.Constant; 45 import jdk.vm.ci.meta.Value; 46 47 /** 48 */ 49 public class MoveResolver { 50 51 private static final DebugCounter cycleBreakingSlotsAllocated = Debug.counter("LSRA[cycleBreakingSlotsAllocated]"); 52 53 private final LinearScan allocator; 54 55 private int insertIdx; 56 private LIRInsertionBuffer insertionBuffer; // buffer where moves are inserted 57 58 private final ArrayList<Interval> mappingFrom; 59 private final ArrayList<Constant> mappingFromOpr; 60 private final ArrayList<Interval> mappingTo; 61 private boolean multipleReadsAllowed; 62 private final int[] registerBlocked; 63 64 private final LIRGenerationResult res; 65 66 protected void setValueBlocked(Value location, int direction) { 67 assert direction == 1 || direction == -1 : "out of bounds"; 68 if (isRegister(location)) { 69 registerBlocked[asRegister(location).number] += direction; 70 } else { 71 throw GraalError.shouldNotReachHere("unhandled value " + location); 72 } 73 } 74 75 protected Interval getMappingFrom(int i) { 76 return mappingFrom.get(i); 77 } 78 79 protected int mappingFromSize() { 80 return mappingFrom.size(); 81 } 82 83 protected int valueBlocked(Value location) { 84 if (isRegister(location)) { 85 return registerBlocked[asRegister(location).number]; 86 } 87 throw GraalError.shouldNotReachHere("unhandled value " + location); 88 } 89 90 void setMultipleReadsAllowed() { 91 multipleReadsAllowed = true; 92 } 93 94 protected boolean areMultipleReadsAllowed() { 95 return multipleReadsAllowed; 96 } 97 98 boolean hasMappings() { 99 return mappingFrom.size() > 0; 100 } 101 102 protected LinearScan getAllocator() { 103 return allocator; 104 } 105 106 protected MoveResolver(LinearScan allocator) { 107 this.allocator = allocator; 108 this.multipleReadsAllowed = false; 109 this.mappingFrom = new ArrayList<>(8); 110 this.mappingFromOpr = new ArrayList<>(8); 111 this.mappingTo = new ArrayList<>(8); 112 this.insertIdx = -1; 113 this.insertionBuffer = new LIRInsertionBuffer(); 114 this.registerBlocked = new int[allocator.getRegisters().size()]; 115 this.res = allocator.getLIRGenerationResult(); 116 } 117 118 protected boolean checkEmpty() { 119 assert mappingFrom.size() == 0 && mappingFromOpr.size() == 0 && mappingTo.size() == 0 : "list must be empty before and after processing"; 120 for (int i = 0; i < getAllocator().getRegisters().size(); i++) { 121 assert registerBlocked[i] == 0 : "register map must be empty before and after processing"; 122 } 123 checkMultipleReads(); 124 return true; 125 } 126 127 protected void checkMultipleReads() { 128 assert !areMultipleReadsAllowed() : "must have default value"; 129 } 130 131 private boolean verifyBeforeResolve() { 132 assert mappingFrom.size() == mappingFromOpr.size() : "length must be equal"; 133 assert mappingFrom.size() == mappingTo.size() : "length must be equal"; 134 assert insertIdx != -1 : "insert position not set"; 135 136 int i; 137 int j; 138 if (!areMultipleReadsAllowed()) { 139 for (i = 0; i < mappingFrom.size(); i++) { 140 for (j = i + 1; j < mappingFrom.size(); j++) { 141 assert mappingFrom.get(i) == null || mappingFrom.get(i) != mappingFrom.get(j) : "cannot read from same interval twice"; 142 } 143 } 144 } 145 146 for (i = 0; i < mappingTo.size(); i++) { 147 for (j = i + 1; j < mappingTo.size(); j++) { 148 assert mappingTo.get(i) != mappingTo.get(j) : "cannot write to same interval twice"; 149 } 150 } 151 152 EconomicSet<Value> usedRegs = EconomicSet.create(Equivalence.DEFAULT); 153 if (!areMultipleReadsAllowed()) { 154 for (i = 0; i < mappingFrom.size(); i++) { 155 Interval interval = mappingFrom.get(i); 156 if (interval != null && !isIllegal(interval.location())) { 157 boolean unique = usedRegs.add(interval.location()); 158 assert unique : "cannot read from same register twice"; 159 } 160 } 161 } 162 163 usedRegs.clear(); 164 for (i = 0; i < mappingTo.size(); i++) { 165 Interval interval = mappingTo.get(i); 166 if (isIllegal(interval.location())) { 167 // After insertion the location may become illegal, so don't check it since multiple 168 // intervals might be illegal. 169 continue; 170 } 171 boolean unique = usedRegs.add(interval.location()); 172 assert unique : "cannot write to same register twice"; 173 } 174 175 verifyStackSlotMapping(); 176 177 return true; 178 } 179 180 protected void verifyStackSlotMapping() { 181 EconomicSet<Value> usedRegs = EconomicSet.create(Equivalence.DEFAULT); 182 for (int i = 0; i < mappingFrom.size(); i++) { 183 Interval interval = mappingFrom.get(i); 184 if (interval != null && !isRegister(interval.location())) { 185 usedRegs.add(interval.location()); 186 } 187 } 188 for (int i = 0; i < mappingTo.size(); i++) { 189 Interval interval = mappingTo.get(i); 190 assert !usedRegs.contains(interval.location()) || 191 checkIntervalLocation(mappingFrom.get(i), interval, mappingFromOpr.get(i)) : "stack slots used in mappingFrom must be disjoint to mappingTo"; 192 } 193 } 194 195 private static boolean checkIntervalLocation(Interval from, Interval to, Constant fromOpr) { 196 if (from == null) { 197 return fromOpr != null; 198 } else { 199 return to.location().equals(from.location()); 200 } 201 } 202 203 // mark assignedReg and assignedRegHi of the interval as blocked 204 private void blockRegisters(Interval interval) { 205 Value location = interval.location(); 206 if (mightBeBlocked(location)) { 207 assert areMultipleReadsAllowed() || valueBlocked(location) == 0 : "location already marked as used: " + location; 208 int direction = 1; 209 setValueBlocked(location, direction); 210 Debug.log("block %s", location); 211 } 212 } 213 214 // mark assignedReg and assignedRegHi of the interval as unblocked 215 private void unblockRegisters(Interval interval) { 216 Value location = interval.location(); 217 if (mightBeBlocked(location)) { 218 assert valueBlocked(location) > 0 : "location already marked as unused: " + location; 219 setValueBlocked(location, -1); 220 Debug.log("unblock %s", location); 221 } 222 } 223 224 /** 225 * Checks if the {@linkplain Interval#location() location} of {@code to} is not blocked or is 226 * only blocked by {@code from}. 227 */ 228 private boolean safeToProcessMove(Interval from, Interval to) { 229 Value fromReg = from != null ? from.location() : null; 230 231 Value location = to.location(); 232 if (mightBeBlocked(location)) { 233 if ((valueBlocked(location) > 1 || (valueBlocked(location) == 1 && !isMoveToSelf(fromReg, location)))) { 234 return false; 235 } 236 } 237 238 return true; 239 } 240 241 protected boolean isMoveToSelf(Value from, Value to) { 242 assert to != null; 243 if (to.equals(from)) { 244 return true; 245 } 246 if (from != null && isRegister(from) && isRegister(to) && asRegister(from).equals(asRegister(to))) { 247 assert LIRKind.verifyMoveKinds(to.getValueKind(), from.getValueKind(), allocator.getRegisterAllocationConfig()) : String.format("Same register but Kind mismatch %s <- %s", to, from); 248 return true; 249 } 250 return false; 251 } 252 253 protected boolean mightBeBlocked(Value location) { 254 return isRegister(location); 255 } 256 257 private void createInsertionBuffer(ArrayList<LIRInstruction> list) { 258 assert !insertionBuffer.initialized() : "overwriting existing buffer"; 259 insertionBuffer.init(list); 260 } 261 262 private void appendInsertionBuffer() { 263 if (insertionBuffer.initialized()) { 264 insertionBuffer.finish(); 265 } 266 assert !insertionBuffer.initialized() : "must be uninitialized now"; 267 268 insertIdx = -1; 269 } 270 271 private LIRInstruction insertMove(Interval fromInterval, Interval toInterval) { 272 assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; 273 assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind(), allocator.getRegisterAllocationConfig()) : "move between different types"; 274 assert insertIdx != -1 : "must setup insert position first"; 275 276 LIRInstruction move = createMove(fromInterval.operand, toInterval.operand, fromInterval.location(), toInterval.location()); 277 insertionBuffer.append(insertIdx, move); 278 279 if (Debug.isLogEnabled()) { 280 Debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx); 281 } 282 return move; 283 } 284 285 /** 286 * @param fromOpr {@link Interval#operand operand} of the {@code from} interval 287 * @param toOpr {@link Interval#operand operand} of the {@code to} interval 288 * @param fromLocation {@link Interval#location() location} of the {@code to} interval 289 * @param toLocation {@link Interval#location() location} of the {@code to} interval 290 */ 291 protected LIRInstruction createMove(AllocatableValue fromOpr, AllocatableValue toOpr, AllocatableValue fromLocation, AllocatableValue toLocation) { 292 return getAllocator().getSpillMoveFactory().createMove(toOpr, fromOpr); 293 } 294 295 private LIRInstruction insertMove(Constant fromOpr, Interval toInterval) { 296 assert insertIdx != -1 : "must setup insert position first"; 297 298 AllocatableValue toOpr = toInterval.operand; 299 LIRInstruction move = getAllocator().getSpillMoveFactory().createLoad(toOpr, fromOpr); 300 insertionBuffer.append(insertIdx, move); 301 302 if (Debug.isLogEnabled()) { 303 Debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx); 304 } 305 return move; 306 } 307 308 @SuppressWarnings("try") 309 private void resolveMappings() { 310 try (Indent indent = Debug.logAndIndent("resolveMapping")) { 311 assert verifyBeforeResolve(); 312 if (Debug.isLogEnabled()) { 313 printMapping(); 314 } 315 316 // Block all registers that are used as input operands of a move. 317 // When a register is blocked, no move to this register is emitted. 318 // This is necessary for detecting cycles in moves. 319 int i; 320 for (i = mappingFrom.size() - 1; i >= 0; i--) { 321 Interval fromInterval = mappingFrom.get(i); 322 if (fromInterval != null) { 323 blockRegisters(fromInterval); 324 } 325 } 326 327 ArrayList<AllocatableValue> busySpillSlots = null; 328 while (mappingFrom.size() > 0) { 329 boolean processedInterval = false; 330 331 int spillCandidate = -1; 332 for (i = mappingFrom.size() - 1; i >= 0; i--) { 333 Interval fromInterval = mappingFrom.get(i); 334 Interval toInterval = mappingTo.get(i); 335 336 if (safeToProcessMove(fromInterval, toInterval)) { 337 // this interval can be processed because target is free 338 final LIRInstruction move; 339 if (fromInterval != null) { 340 move = insertMove(fromInterval, toInterval); 341 unblockRegisters(fromInterval); 342 } else { 343 move = insertMove(mappingFromOpr.get(i), toInterval); 344 } 345 move.setComment(res, "MoveResolver resolve mapping"); 346 if (LIRValueUtil.isStackSlotValue(toInterval.location())) { 347 if (busySpillSlots == null) { 348 busySpillSlots = new ArrayList<>(2); 349 } 350 busySpillSlots.add(toInterval.location()); 351 } 352 mappingFrom.remove(i); 353 mappingFromOpr.remove(i); 354 mappingTo.remove(i); 355 356 processedInterval = true; 357 } else if (fromInterval != null && isRegister(fromInterval.location()) && 358 (busySpillSlots == null || !busySpillSlots.contains(fromInterval.spillSlot()))) { 359 // this interval cannot be processed now because target is not free 360 // it starts in a register, so it is a possible candidate for spilling 361 spillCandidate = i; 362 } 363 } 364 365 if (!processedInterval) { 366 breakCycle(spillCandidate); 367 } 368 } 369 } 370 371 // reset to default value 372 multipleReadsAllowed = false; 373 374 // check that all intervals have been processed 375 assert checkEmpty(); 376 } 377 378 protected void breakCycle(int spillCandidate) { 379 // no move could be processed because there is a cycle in the move list 380 // (e.g. r1 . r2, r2 . r1), so one interval must be spilled to memory 381 assert spillCandidate != -1 : "no interval in register for spilling found"; 382 383 // create a new spill interval and assign a stack slot to it 384 Interval fromInterval = mappingFrom.get(spillCandidate); 385 // do not allocate a new spill slot for temporary interval, but 386 // use spill slot assigned to fromInterval. Otherwise moves from 387 // one stack slot to another can happen (not allowed by LIRAssembler 388 AllocatableValue spillSlot = fromInterval.spillSlot(); 389 if (spillSlot == null) { 390 spillSlot = getAllocator().getFrameMapBuilder().allocateSpillSlot(fromInterval.kind()); 391 fromInterval.setSpillSlot(spillSlot); 392 cycleBreakingSlotsAllocated.increment(); 393 } 394 spillInterval(spillCandidate, fromInterval, spillSlot); 395 } 396 397 protected void spillInterval(int spillCandidate, Interval fromInterval, AllocatableValue spillSlot) { 398 assert mappingFrom.get(spillCandidate).equals(fromInterval); 399 Interval spillInterval = getAllocator().createDerivedInterval(fromInterval); 400 spillInterval.setKind(fromInterval.kind()); 401 402 // add a dummy range because real position is difficult to calculate 403 // Note: this range is a special case when the integrity of the allocation is 404 // checked 405 spillInterval.addRange(1, 2); 406 407 spillInterval.assignLocation(spillSlot); 408 409 if (Debug.isLogEnabled()) { 410 Debug.log("created new Interval for spilling: %s", spillInterval); 411 } 412 blockRegisters(spillInterval); 413 414 // insert a move from register to stack and update the mapping 415 LIRInstruction move = insertMove(fromInterval, spillInterval); 416 mappingFrom.set(spillCandidate, spillInterval); 417 unblockRegisters(fromInterval); 418 move.setComment(res, "MoveResolver break cycle"); 419 } 420 421 @SuppressWarnings("try") 422 private void printMapping() { 423 try (Indent indent = Debug.logAndIndent("Mapping")) { 424 for (int i = mappingFrom.size() - 1; i >= 0; i--) { 425 Interval fromInterval = mappingFrom.get(i); 426 Interval toInterval = mappingTo.get(i); 427 String from; 428 Value to = toInterval.location(); 429 if (fromInterval == null) { 430 from = mappingFromOpr.get(i).toString(); 431 } else { 432 from = fromInterval.location().toString(); 433 } 434 Debug.log("move %s <- %s", from, to); 435 } 436 } 437 } 438 439 void setInsertPosition(ArrayList<LIRInstruction> insertList, int insertIdx) { 440 assert this.insertIdx == -1 : "use moveInsertPosition instead of setInsertPosition when data already set"; 441 442 createInsertionBuffer(insertList); 443 this.insertIdx = insertIdx; 444 } 445 446 void moveInsertPosition(ArrayList<LIRInstruction> newInsertList, int newInsertIdx) { 447 if (insertionBuffer.lirList() != null && (insertionBuffer.lirList() != newInsertList || this.insertIdx != newInsertIdx)) { 448 // insert position changed . resolve current mappings 449 resolveMappings(); 450 } 451 452 if (insertionBuffer.lirList() != newInsertList) { 453 // block changed . append insertionBuffer because it is 454 // bound to a specific block and create a new insertionBuffer 455 appendInsertionBuffer(); 456 createInsertionBuffer(newInsertList); 457 } 458 459 this.insertIdx = newInsertIdx; 460 } 461 462 public void addMapping(Interval fromInterval, Interval toInterval) { 463 464 if (isIllegal(toInterval.location()) && toInterval.canMaterialize()) { 465 if (Debug.isLogEnabled()) { 466 Debug.log("no store to rematerializable interval %s needed", toInterval); 467 } 468 return; 469 } 470 if (isIllegal(fromInterval.location()) && fromInterval.canMaterialize()) { 471 // Instead of a reload, re-materialize the value 472 Constant rematValue = fromInterval.getMaterializedValue(); 473 addMapping(rematValue, toInterval); 474 return; 475 } 476 if (Debug.isLogEnabled()) { 477 Debug.log("add move mapping from %s to %s", fromInterval, toInterval); 478 } 479 480 assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; 481 assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind(), allocator.getRegisterAllocationConfig()) : String.format("Kind mismatch: %s vs. %s, from=%s, to=%s", fromInterval.kind(), 482 toInterval.kind(), fromInterval, toInterval); 483 mappingFrom.add(fromInterval); 484 mappingFromOpr.add(null); 485 mappingTo.add(toInterval); 486 } 487 488 public void addMapping(Constant fromOpr, Interval toInterval) { 489 if (Debug.isLogEnabled()) { 490 Debug.log("add move mapping from %s to %s", fromOpr, toInterval); 491 } 492 493 mappingFrom.add(null); 494 mappingFromOpr.add(fromOpr); 495 mappingTo.add(toInterval); 496 } 497 498 void resolveAndAppendMoves() { 499 if (hasMappings()) { 500 resolveMappings(); 501 } 502 appendInsertionBuffer(); 503 } 504 }