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.lsra; 024 025import static java.lang.String.*; 026import static jdk.internal.jvmci.code.ValueUtil.*; 027 028import java.util.*; 029 030import jdk.internal.jvmci.code.*; 031import jdk.internal.jvmci.common.*; 032import com.oracle.graal.debug.*; 033import jdk.internal.jvmci.meta.*; 034 035import com.oracle.graal.lir.*; 036 037/** 038 */ 039public class MoveResolver { 040 041 private final LinearScan allocator; 042 043 private int insertIdx; 044 private LIRInsertionBuffer insertionBuffer; // buffer where moves are inserted 045 046 private final List<Interval> mappingFrom; 047 private final List<Value> mappingFromOpr; 048 private final List<Interval> mappingTo; 049 private boolean multipleReadsAllowed; 050 private final int[] registerBlocked; 051 052 protected void setValueBlocked(Value location, int direction) { 053 assert direction == 1 || direction == -1 : "out of bounds"; 054 if (isRegister(location)) { 055 registerBlocked[asRegister(location).number] += direction; 056 } else { 057 throw JVMCIError.shouldNotReachHere("unhandled value " + location); 058 } 059 } 060 061 protected Interval getMappingFrom(int i) { 062 return mappingFrom.get(i); 063 } 064 065 protected int mappingFromSize() { 066 return mappingFrom.size(); 067 } 068 069 protected int valueBlocked(Value location) { 070 if (isRegister(location)) { 071 return registerBlocked[asRegister(location).number]; 072 } 073 throw JVMCIError.shouldNotReachHere("unhandled value " + location); 074 } 075 076 void setMultipleReadsAllowed() { 077 multipleReadsAllowed = true; 078 } 079 080 protected boolean areMultipleReadsAllowed() { 081 return multipleReadsAllowed; 082 } 083 084 boolean hasMappings() { 085 return mappingFrom.size() > 0; 086 } 087 088 protected LinearScan getAllocator() { 089 return allocator; 090 } 091 092 protected MoveResolver(LinearScan allocator) { 093 094 this.allocator = allocator; 095 this.multipleReadsAllowed = false; 096 this.mappingFrom = new ArrayList<>(8); 097 this.mappingFromOpr = new ArrayList<>(8); 098 this.mappingTo = new ArrayList<>(8); 099 this.insertIdx = -1; 100 this.insertionBuffer = new LIRInsertionBuffer(); 101 this.registerBlocked = new int[allocator.getRegisters().length]; 102 } 103 104 protected boolean checkEmpty() { 105 assert mappingFrom.size() == 0 && mappingFromOpr.size() == 0 && mappingTo.size() == 0 : "list must be empty before and after processing"; 106 for (int i = 0; i < getAllocator().getRegisters().length; i++) { 107 assert registerBlocked[i] == 0 : "register map must be empty before and after processing"; 108 } 109 checkMultipleReads(); 110 return true; 111 } 112 113 protected void checkMultipleReads() { 114 assert !areMultipleReadsAllowed() : "must have default value"; 115 } 116 117 private boolean verifyBeforeResolve() { 118 assert mappingFrom.size() == mappingFromOpr.size() : "length must be equal"; 119 assert mappingFrom.size() == mappingTo.size() : "length must be equal"; 120 assert insertIdx != -1 : "insert position not set"; 121 122 int i; 123 int j; 124 if (!areMultipleReadsAllowed()) { 125 for (i = 0; i < mappingFrom.size(); i++) { 126 for (j = i + 1; j < mappingFrom.size(); j++) { 127 assert mappingFrom.get(i) == null || mappingFrom.get(i) != mappingFrom.get(j) : "cannot read from same interval twice"; 128 } 129 } 130 } 131 132 for (i = 0; i < mappingTo.size(); i++) { 133 for (j = i + 1; j < mappingTo.size(); j++) { 134 assert mappingTo.get(i) != mappingTo.get(j) : "cannot write to same interval twice"; 135 } 136 } 137 138 HashSet<Value> usedRegs = new HashSet<>(); 139 if (!areMultipleReadsAllowed()) { 140 for (i = 0; i < mappingFrom.size(); i++) { 141 Interval interval = mappingFrom.get(i); 142 if (interval != null && !isIllegal(interval.location())) { 143 boolean unique = usedRegs.add(interval.location()); 144 assert unique : "cannot read from same register twice"; 145 } 146 } 147 } 148 149 usedRegs.clear(); 150 for (i = 0; i < mappingTo.size(); i++) { 151 Interval interval = mappingTo.get(i); 152 if (isIllegal(interval.location())) { 153 // After insertion the location may become illegal, so don't check it since multiple 154 // intervals might be illegal. 155 continue; 156 } 157 boolean unique = usedRegs.add(interval.location()); 158 assert unique : "cannot write to same register twice"; 159 } 160 161 verifyStackSlotMapping(); 162 163 return true; 164 } 165 166 protected void verifyStackSlotMapping() { 167 HashSet<Value> usedRegs = new HashSet<>(); 168 for (int i = 0; i < mappingFrom.size(); i++) { 169 Interval interval = mappingFrom.get(i); 170 if (interval != null && !isRegister(interval.location())) { 171 usedRegs.add(interval.location()); 172 } 173 } 174 for (int i = 0; i < mappingTo.size(); i++) { 175 Interval interval = mappingTo.get(i); 176 assert !usedRegs.contains(interval.location()) || checkIntervalLocation(mappingFrom.get(i), interval, mappingFromOpr.get(i)) : "stack slots used in mappingFrom must be disjoint to mappingTo"; 177 } 178 } 179 180 private static boolean checkIntervalLocation(Interval from, Interval to, Value fromOpr) { 181 if (from == null) { 182 return fromOpr != null; 183 } else { 184 return to.location().equals(from.location()); 185 } 186 } 187 188 // mark assignedReg and assignedRegHi of the interval as blocked 189 private void blockRegisters(Interval interval) { 190 Value location = interval.location(); 191 if (mightBeBlocked(location)) { 192 assert areMultipleReadsAllowed() || valueBlocked(location) == 0 : "location already marked as used: " + location; 193 int direction = 1; 194 setValueBlocked(location, direction); 195 Debug.log("block %s", location); 196 } 197 } 198 199 // mark assignedReg and assignedRegHi of the interval as unblocked 200 private void unblockRegisters(Interval interval) { 201 Value location = interval.location(); 202 if (mightBeBlocked(location)) { 203 assert valueBlocked(location) > 0 : "location already marked as unused: " + location; 204 setValueBlocked(location, -1); 205 Debug.log("unblock %s", location); 206 } 207 } 208 209 /** 210 * Checks if the {@linkplain Interval#location() location} of {@code to} is not blocked or is 211 * only blocked by {@code from}. 212 */ 213 private boolean safeToProcessMove(Interval from, Interval to) { 214 Value fromReg = from != null ? from.location() : null; 215 216 Value location = to.location(); 217 if (mightBeBlocked(location)) { 218 if ((valueBlocked(location) > 1 || (valueBlocked(location) == 1 && !isMoveToSelf(fromReg, location)))) { 219 return false; 220 } 221 } 222 223 return true; 224 } 225 226 protected boolean isMoveToSelf(Value from, Value to) { 227 assert to != null; 228 if (to.equals(from)) { 229 return true; 230 } 231 if (from != null && isRegister(from) && isRegister(to) && asRegister(from).equals(asRegister(to))) { 232 assert LIRKind.verifyMoveKinds(to.getLIRKind(), from.getLIRKind()) : String.format("Same register but Kind mismatch %s <- %s", to, from); 233 return true; 234 } 235 return false; 236 } 237 238 protected boolean mightBeBlocked(Value location) { 239 return isRegister(location); 240 } 241 242 private void createInsertionBuffer(List<LIRInstruction> list) { 243 assert !insertionBuffer.initialized() : "overwriting existing buffer"; 244 insertionBuffer.init(list); 245 } 246 247 private void appendInsertionBuffer() { 248 if (insertionBuffer.initialized()) { 249 insertionBuffer.finish(); 250 } 251 assert !insertionBuffer.initialized() : "must be uninitialized now"; 252 253 insertIdx = -1; 254 } 255 256 private void insertMove(Interval fromInterval, Interval toInterval) { 257 assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; 258 assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind()) : "move between different types"; 259 assert insertIdx != -1 : "must setup insert position first"; 260 261 insertionBuffer.append(insertIdx, createMove(fromInterval.operand, toInterval.operand, fromInterval.location(), toInterval.location())); 262 263 if (Debug.isLogEnabled()) { 264 Debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx); 265 } 266 } 267 268 /** 269 * @param fromOpr {@link Interval#operand operand} of the {@code from} interval 270 * @param toOpr {@link Interval#operand operand} of the {@code to} interval 271 * @param fromLocation {@link Interval#location() location} of the {@code to} interval 272 * @param toLocation {@link Interval#location() location} of the {@code to} interval 273 */ 274 protected LIRInstruction createMove(AllocatableValue fromOpr, AllocatableValue toOpr, AllocatableValue fromLocation, AllocatableValue toLocation) { 275 return getAllocator().getSpillMoveFactory().createMove(toOpr, fromOpr); 276 } 277 278 private void insertMove(Value fromOpr, Interval toInterval) { 279 assert LIRKind.verifyMoveKinds(toInterval.kind(), fromOpr.getLIRKind()) : format("move between different types %s %s", fromOpr.getLIRKind(), toInterval.kind()); 280 assert insertIdx != -1 : "must setup insert position first"; 281 282 AllocatableValue toOpr = toInterval.operand; 283 LIRInstruction move = getAllocator().getSpillMoveFactory().createMove(toOpr, fromOpr); 284 insertionBuffer.append(insertIdx, move); 285 286 if (Debug.isLogEnabled()) { 287 Debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx); 288 } 289 } 290 291 private void resolveMappings() { 292 try (Indent indent = Debug.logAndIndent("resolveMapping")) { 293 assert verifyBeforeResolve(); 294 if (Debug.isLogEnabled()) { 295 printMapping(); 296 } 297 298 // Block all registers that are used as input operands of a move. 299 // When a register is blocked, no move to this register is emitted. 300 // This is necessary for detecting cycles in moves. 301 int i; 302 for (i = mappingFrom.size() - 1; i >= 0; i--) { 303 Interval fromInterval = mappingFrom.get(i); 304 if (fromInterval != null) { 305 blockRegisters(fromInterval); 306 } 307 } 308 309 int spillCandidate = -1; 310 while (mappingFrom.size() > 0) { 311 boolean processedInterval = false; 312 313 for (i = mappingFrom.size() - 1; i >= 0; i--) { 314 Interval fromInterval = mappingFrom.get(i); 315 Interval toInterval = mappingTo.get(i); 316 317 if (safeToProcessMove(fromInterval, toInterval)) { 318 // this interval can be processed because target is free 319 if (fromInterval != null) { 320 insertMove(fromInterval, toInterval); 321 unblockRegisters(fromInterval); 322 } else { 323 insertMove(mappingFromOpr.get(i), toInterval); 324 } 325 mappingFrom.remove(i); 326 mappingFromOpr.remove(i); 327 mappingTo.remove(i); 328 329 processedInterval = true; 330 } else if (fromInterval != null && isRegister(fromInterval.location())) { 331 // this interval cannot be processed now because target is not free 332 // it starts in a register, so it is a possible candidate for spilling 333 spillCandidate = i; 334 } 335 } 336 337 if (!processedInterval) { 338 breakCycle(spillCandidate); 339 } 340 } 341 } 342 343 // reset to default value 344 multipleReadsAllowed = false; 345 346 // check that all intervals have been processed 347 assert checkEmpty(); 348 } 349 350 protected void breakCycle(int spillCandidate) { 351 // no move could be processed because there is a cycle in the move list 352 // (e.g. r1 . r2, r2 . r1), so one interval must be spilled to memory 353 assert spillCandidate != -1 : "no interval in register for spilling found"; 354 355 // create a new spill interval and assign a stack slot to it 356 Interval fromInterval = mappingFrom.get(spillCandidate); 357 // do not allocate a new spill slot for temporary interval, but 358 // use spill slot assigned to fromInterval. Otherwise moves from 359 // one stack slot to another can happen (not allowed by LIRAssembler 360 StackSlotValue spillSlot = fromInterval.spillSlot(); 361 if (spillSlot == null) { 362 spillSlot = getAllocator().getFrameMapBuilder().allocateSpillSlot(fromInterval.kind()); 363 fromInterval.setSpillSlot(spillSlot); 364 } 365 spillInterval(spillCandidate, fromInterval, spillSlot); 366 } 367 368 protected void spillInterval(int spillCandidate, Interval fromInterval, StackSlotValue spillSlot) { 369 assert mappingFrom.get(spillCandidate).equals(fromInterval); 370 Interval spillInterval = getAllocator().createDerivedInterval(fromInterval); 371 spillInterval.setKind(fromInterval.kind()); 372 373 // add a dummy range because real position is difficult to calculate 374 // Note: this range is a special case when the integrity of the allocation is 375 // checked 376 spillInterval.addRange(1, 2); 377 378 spillInterval.assignLocation(spillSlot); 379 380 if (Debug.isLogEnabled()) { 381 Debug.log("created new Interval for spilling: %s", spillInterval); 382 } 383 blockRegisters(spillInterval); 384 385 // insert a move from register to stack and update the mapping 386 insertMove(fromInterval, spillInterval); 387 mappingFrom.set(spillCandidate, spillInterval); 388 unblockRegisters(fromInterval); 389 } 390 391 private void printMapping() { 392 try (Indent indent = Debug.logAndIndent("Mapping")) { 393 for (int i = mappingFrom.size() - 1; i >= 0; i--) { 394 Interval fromInterval = mappingFrom.get(i); 395 Interval toInterval = mappingTo.get(i); 396 Value from; 397 Value to = toInterval.location(); 398 if (fromInterval == null) { 399 from = mappingFromOpr.get(i); 400 } else { 401 from = fromInterval.location(); 402 } 403 Debug.log("move %s <- %s", from, to); 404 } 405 } 406 } 407 408 void setInsertPosition(List<LIRInstruction> insertList, int insertIdx) { 409 assert this.insertIdx == -1 : "use moveInsertPosition instead of setInsertPosition when data already set"; 410 411 createInsertionBuffer(insertList); 412 this.insertIdx = insertIdx; 413 } 414 415 void moveInsertPosition(List<LIRInstruction> newInsertList, int newInsertIdx) { 416 if (insertionBuffer.lirList() != null && (insertionBuffer.lirList() != newInsertList || this.insertIdx != newInsertIdx)) { 417 // insert position changed . resolve current mappings 418 resolveMappings(); 419 } 420 421 if (insertionBuffer.lirList() != newInsertList) { 422 // block changed . append insertionBuffer because it is 423 // bound to a specific block and create a new insertionBuffer 424 appendInsertionBuffer(); 425 createInsertionBuffer(newInsertList); 426 } 427 428 this.insertIdx = newInsertIdx; 429 } 430 431 public void addMapping(Interval fromInterval, Interval toInterval) { 432 433 if (isIllegal(toInterval.location()) && toInterval.canMaterialize()) { 434 if (Debug.isLogEnabled()) { 435 Debug.log("no store to rematerializable interval %s needed", toInterval); 436 } 437 return; 438 } 439 if (isIllegal(fromInterval.location()) && fromInterval.canMaterialize()) { 440 // Instead of a reload, re-materialize the value 441 Value rematValue = fromInterval.getMaterializedValue(); 442 addMapping(rematValue, toInterval); 443 return; 444 } 445 if (Debug.isLogEnabled()) { 446 Debug.log("add move mapping from %s to %s", fromInterval, toInterval); 447 } 448 449 assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; 450 assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind()) : String.format("Kind mismatch: %s vs. %s, from=%s, to=%s", fromInterval.kind(), toInterval.kind(), fromInterval, 451 toInterval); 452 mappingFrom.add(fromInterval); 453 mappingFromOpr.add(Value.ILLEGAL); 454 mappingTo.add(toInterval); 455 } 456 457 public void addMapping(Value fromOpr, Interval toInterval) { 458 if (Debug.isLogEnabled()) { 459 Debug.log("add move mapping from %s to %s", fromOpr, toInterval); 460 } 461 462 assert isConstant(fromOpr) : "only for constants"; 463 464 mappingFrom.add(null); 465 mappingFromOpr.add(fromOpr); 466 mappingTo.add(toInterval); 467 } 468 469 void resolveAndAppendMoves() { 470 if (hasMappings()) { 471 resolveMappings(); 472 } 473 appendInsertionBuffer(); 474 } 475}