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