1 /*
   2  * Copyright (c) 2016, 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.bu;
  24 
  25 import static jdk.vm.ci.code.ValueUtil.asAllocatableValue;
  26 import static jdk.vm.ci.code.ValueUtil.asRegister;
  27 import static jdk.vm.ci.code.ValueUtil.isIllegal;
  28 import static jdk.vm.ci.code.ValueUtil.isRegister;
  29 import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
  30 import static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
  31 import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue;
  32 import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
  33 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
  34 
  35 import java.util.ArrayList;
  36 import java.util.BitSet;
  37 import java.util.Collections;
  38 import java.util.EnumSet;
  39 
  40 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
  41 import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig.AllocatableRegisters;
  42 import org.graalvm.compiler.core.common.alloc.Trace;
  43 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
  44 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
  45 import org.graalvm.compiler.debug.Debug;
  46 import org.graalvm.compiler.debug.Debug.Scope;
  47 import org.graalvm.compiler.debug.Indent;
  48 import org.graalvm.compiler.lir.InstructionValueProcedure;
  49 import org.graalvm.compiler.lir.LIR;
  50 import org.graalvm.compiler.lir.LIRInstruction;
  51 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
  52 import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
  53 import org.graalvm.compiler.lir.LIRValueUtil;
  54 import org.graalvm.compiler.lir.RedundantMoveElimination;
  55 import org.graalvm.compiler.lir.StandardOp;
  56 import org.graalvm.compiler.lir.StandardOp.BlockEndOp;
  57 import org.graalvm.compiler.lir.StandardOp.JumpOp;
  58 import org.graalvm.compiler.lir.StandardOp.LabelOp;
  59 import org.graalvm.compiler.lir.Variable;
  60 import org.graalvm.compiler.lir.VirtualStackSlot;
  61 import org.graalvm.compiler.lir.alloc.OutOfRegistersException;
  62 import org.graalvm.compiler.lir.alloc.trace.GlobalLivenessInfo;
  63 import org.graalvm.compiler.lir.alloc.trace.TraceAllocationPhase;
  64 import org.graalvm.compiler.lir.alloc.trace.TraceAllocationPhase.TraceAllocationContext;
  65 import org.graalvm.compiler.lir.alloc.trace.TraceGlobalMoveResolutionPhase;
  66 import org.graalvm.compiler.lir.alloc.trace.TraceGlobalMoveResolver;
  67 import org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase;
  68 import org.graalvm.compiler.lir.gen.LIRGenerationResult;
  69 import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory;
  70 import org.graalvm.compiler.lir.ssa.SSAUtil;
  71 
  72 import jdk.vm.ci.code.Register;
  73 import jdk.vm.ci.code.RegisterArray;
  74 import jdk.vm.ci.code.RegisterAttributes;
  75 import jdk.vm.ci.code.RegisterValue;
  76 import jdk.vm.ci.code.TargetDescription;
  77 import jdk.vm.ci.common.JVMCIError;
  78 import jdk.vm.ci.meta.AllocatableValue;
  79 import jdk.vm.ci.meta.PlatformKind;
  80 import jdk.vm.ci.meta.Value;
  81 
  82 /**
  83  * Allocates registers within a trace in a greedy, bottom-up fashion. The liveness information is
  84  * computed on the fly as the instructions are traversed instead of computing it in a separate pass.
  85  * The goal of this allocator is to provide a simple and fast algorithm for situations where code
  86  * quality is not the primary target.
  87  *
  88  * This implementation does not (yet) exploit hinting information and might introduce multiple spill
  89  * moves to the same stack slot (which are likely to be remove by {@link RedundantMoveElimination}.
  90  *
  91  * The current implementation cannot deal with {@link AbstractBlockBase blocks} with edges to
  92  * compiled exception handlers since it might introduce spill code after the {@link LIRInstruction
  93  * instruction} that triggers the exception.
  94  */
  95 public final class BottomUpAllocator extends TraceAllocationPhase<TraceAllocationContext> {
  96     private final TargetDescription target;
  97     private final LIRGenerationResult lirGenRes;
  98     private final MoveFactory spillMoveFactory;
  99     private final RegisterAllocationConfig registerAllocationConfig;
 100     private final RegisterArray callerSaveRegs;
 101     private final RegisterAttributes[] registerAttributes;
 102     private final BitSet allocatedBlocks;
 103     private final TraceBuilderResult resultTraces;
 104     private final TraceGlobalMoveResolver moveResolver;
 105 
 106     /**
 107      * Maps from {@link Variable#index} to a spill stack slot. If
 108      * {@linkplain org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase.Options#TraceRACacheStackSlots
 109      * enabled} a {@link Variable} is always assigned to the same stack slot.
 110      */
 111     private final AllocatableValue[] stackSlots;
 112 
 113     private final ArrayList<LIRInstruction> insertInstructionsBefore;
 114     private final ArrayList<LIRInstruction> insertInstructionsAfter;
 115     private final boolean neverSpillConstants;
 116 
 117     private final GlobalLivenessInfo livenessInfo;
 118 
 119     public BottomUpAllocator(TargetDescription target, LIRGenerationResult lirGenRes, MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig,
 120                     AllocatableValue[] cachedStackSlots, TraceBuilderResult resultTraces, boolean neverSpillConstant, GlobalLivenessInfo livenessInfo) {
 121         this.target = target;
 122         this.lirGenRes = lirGenRes;
 123         this.spillMoveFactory = spillMoveFactory;
 124         this.registerAllocationConfig = registerAllocationConfig;
 125         this.callerSaveRegs = registerAllocationConfig.getRegisterConfig().getCallerSaveRegisters();
 126         this.registerAttributes = registerAllocationConfig.getRegisterConfig().getAttributesMap();
 127         this.allocatedBlocks = new BitSet(lirGenRes.getLIR().getControlFlowGraph().getBlocks().length);
 128         this.resultTraces = resultTraces;
 129         this.moveResolver = new TraceGlobalMoveResolver(lirGenRes, spillMoveFactory, registerAllocationConfig, target.arch);
 130         this.neverSpillConstants = neverSpillConstant;
 131         this.livenessInfo = livenessInfo;
 132 
 133         this.insertInstructionsBefore = new ArrayList<>(4);
 134         this.insertInstructionsAfter = new ArrayList<>(4);
 135 
 136         if (TraceRegisterAllocationPhase.Options.TraceRACacheStackSlots.getValue(lirGenRes.getLIR().getOptions())) {
 137             this.stackSlots = cachedStackSlots;
 138         } else {
 139             this.stackSlots = new AllocatableValue[lirGenRes.getLIR().numVariables()];
 140         }
 141 
 142     }
 143 
 144     private LIR getLIR() {
 145         return lirGenRes.getLIR();
 146     }
 147 
 148     /**
 149      * Gets an object describing the attributes of a given register according to this register
 150      * configuration.
 151      */
 152     private RegisterAttributes attributes(Register reg) {
 153         return registerAttributes[reg.number];
 154     }
 155 
 156     /**
 157      * Returns a new spill slot or a cached entry if there is already one for the variable.
 158      */
 159     private AllocatableValue allocateSpillSlot(Variable var) {
 160         int variableIndex = var.index;
 161         AllocatableValue cachedStackSlot = stackSlots[variableIndex];
 162         if (cachedStackSlot != null) {
 163             TraceRegisterAllocationPhase.globalStackSlots.increment();
 164             assert cachedStackSlot.getValueKind().equals(var.getValueKind()) : "CachedStackSlot: kind mismatch? " + var.getValueKind() + " vs. " + cachedStackSlot.getValueKind();
 165             return cachedStackSlot;
 166         }
 167         VirtualStackSlot slot = lirGenRes.getFrameMapBuilder().allocateSpillSlot(var.getValueKind());
 168         stackSlots[variableIndex] = slot;
 169         TraceRegisterAllocationPhase.allocatedStackSlots.increment();
 170         return slot;
 171     }
 172 
 173     @Override
 174     protected void run(@SuppressWarnings("hiding") TargetDescription target, @SuppressWarnings("hiding") LIRGenerationResult lirGenRes, Trace trace, TraceAllocationContext context) {
 175         allocate(trace);
 176     }
 177 
 178     private void allocate(Trace trace) {
 179         if (neverSpillConstants) {
 180             throw JVMCIError.unimplemented("NeverSpillConstant not supported!");
 181         }
 182         new Allocator().allocateTrace(trace);
 183         assert verify(trace);
 184     }
 185 
 186     private boolean verify(Trace trace) {
 187         for (AbstractBlockBase<?> block : trace.getBlocks()) {
 188             assert LIR.verifyBlock(lirGenRes.getLIR(), block);
 189         }
 190         return true;
 191     }
 192 
 193     private static boolean requiresRegisters(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
 194         assert isVariable(value) : "Not a variable " + value;
 195         if (instruction instanceof LabelOp) {
 196             // phi and incoming values do not require a register
 197             return false;
 198         }
 199         if (mode == OperandMode.DEF || mode == OperandMode.TEMP) {
 200             return true;
 201         }
 202         return !flags.contains(OperandFlag.STACK);
 203     }
 204 
 205     private void resolveFindInsertPos(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock) {
 206         LIR lir = lirGenRes.getLIR();
 207         if (fromBlock.getSuccessorCount() <= 1) {
 208             if (Debug.isLogEnabled()) {
 209                 Debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId());
 210             }
 211 
 212             ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(fromBlock);
 213             LIRInstruction instr = instructions.get(instructions.size() - 1);
 214             if (instr instanceof StandardOp.JumpOp) {
 215                 // insert moves before branch
 216                 moveResolver.setInsertPosition(instructions, instructions.size() - 1);
 217             } else {
 218                 moveResolver.setInsertPosition(instructions, instructions.size());
 219             }
 220 
 221         } else {
 222             if (Debug.isLogEnabled()) {
 223                 Debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId());
 224             }
 225 
 226             if (DetailedAsserts.getValue(getLIR().getOptions())) {
 227                 assert lir.getLIRforBlock(fromBlock).get(0) instanceof StandardOp.LabelOp : "block does not start with a label";
 228 
 229                 /*
 230                  * Because the number of predecessor edges matches the number of successor edges,
 231                  * blocks which are reached by switch statements may have be more than one
 232                  * predecessor but it will be guaranteed that all predecessors will be the same.
 233                  */
 234                 for (AbstractBlockBase<?> predecessor : toBlock.getPredecessors()) {
 235                     assert fromBlock == predecessor : "all critical edges must be broken";
 236                 }
 237             }
 238 
 239             moveResolver.setInsertPosition(lir.getLIRforBlock(toBlock), 1);
 240         }
 241     }
 242 
 243     private final class Allocator {
 244 
 245         /**
 246          * Maps from {@linkplain Register#number register} to the current {@linkplain Variable
 247          * variable}.
 248          */
 249         private final AllocatableValue[] currentRegisterMapping;
 250         /**
 251          * Maps from {@linkplain Variable#index variable} to its current location.
 252          */
 253         private final AllocatableValue[] locations;
 254 
 255         private final int[] lastRegisterUsage;
 256         private final int[] lastRegisterKill;
 257 
 258         private ArrayList<LIRInstruction> currentInstructions;
 259         private int currentInstructionIndex;
 260         private int currentOpId;
 261 
 262         private Allocator() {
 263             RegisterArray registers = target.arch.getRegisters();
 264             int numRegs = registers.size();
 265             int numVar = getLIR().numVariables();
 266             currentRegisterMapping = new AllocatableValue[numRegs];
 267             lastRegisterUsage = new int[numRegs];
 268             lastRegisterKill = new int[numRegs];
 269             locations = new AllocatableValue[numVar];
 270             // we start at offset 2 to distinguish if from the default value
 271             currentOpId = 2;
 272         }
 273 
 274         private void setCurrentValue(Register reg, AllocatableValue val) {
 275             currentRegisterMapping[reg.number] = val;
 276         }
 277 
 278         private AllocatableValue getCurrentValue(Register reg) {
 279             return currentRegisterMapping[reg.number];
 280         }
 281 
 282         private int getLastRegisterUsage(Register reg) {
 283             return lastRegisterUsage[reg.number];
 284         }
 285 
 286         private void setLastRegisterUsage(Register reg, int pos) {
 287             Debug.log("Register %s last used %d", reg, pos);
 288             lastRegisterUsage[reg.number] = pos;
 289         }
 290 
 291         private int getLastRegisterKill(Register reg) {
 292             return lastRegisterKill[reg.number];
 293         }
 294 
 295         private void setLastRegisterKill(Register reg, int pos) {
 296             Debug.log("Register %s killed %d", reg, pos);
 297             lastRegisterKill[reg.number] = pos;
 298         }
 299 
 300         private void setCurrentLocation(Variable var, AllocatableValue location) {
 301             locations[var.index] = location;
 302         }
 303 
 304         private AllocatableValue getCurrentLocation(Variable var) {
 305             return locations[var.index];
 306         }
 307 
 308         private void insertSpillMoveBefore(AllocatableValue dst, Value src) {
 309             LIRInstruction move = spillMoveFactory.createMove(dst, src);
 310             insertInstructionsBefore.add(move);
 311             move.setComment(lirGenRes, "BottomUp: spill move before");
 312             Debug.log("insert before %s", move);
 313         }
 314 
 315         private void insertSpillMoveAfter(AllocatableValue dst, Value src) {
 316             LIRInstruction inst = currentInstructions.get(currentInstructionIndex);
 317             if (!(inst instanceof BlockEndOp)) {
 318                 LIRInstruction move = spillMoveFactory.createMove(dst, src);
 319                 insertInstructionsAfter.add(move);
 320                 move.setComment(lirGenRes, "BottomUp: spill move after");
 321                 Debug.log("insert after %s", move);
 322             } else {
 323                 Debug.log("Block end op. No from %s to %s necessary.", src, dst);
 324                 requireResolution = true;
 325             }
 326         }
 327 
 328         private void insertInstructions() {
 329             // TODO (je) this is can probably be improved
 330             currentInstructions.ensureCapacity(currentInstructions.size() + insertInstructionsBefore.size() + insertInstructionsAfter.size());
 331             LIRInstruction inst = currentInstructions.get(currentInstructionIndex);
 332             // insert after
 333             if (insertInstructionsAfter.size() != 0) {
 334                 Collections.reverse(insertInstructionsAfter);
 335                 assert !(inst instanceof BlockEndOp) : "Cannot insert instruction after the block end op: " + inst;
 336                 currentInstructions.addAll(currentInstructionIndex + 1, insertInstructionsAfter);
 337                 insertInstructionsAfter.clear();
 338             }
 339             // insert before
 340             if (insertInstructionsBefore.size() != 0) {
 341                 assert !(inst instanceof LabelOp) : "Cannot insert instruction before the label op: " + inst;
 342                 currentInstructions.addAll(currentInstructionIndex, insertInstructionsBefore);
 343                 insertInstructionsBefore.clear();
 344             }
 345         }
 346 
 347         @SuppressWarnings("try")
 348         private void allocateTrace(Trace trace) {
 349             try (Scope s = Debug.scope("BottomUpAllocator", trace.getBlocks()); Indent indent = Debug.logAndIndent("%s (Trace%d)", trace, trace.getId())) {
 350                 AbstractBlockBase<?>[] blocks = trace.getBlocks();
 351                 int lastBlockIdx = blocks.length - 1;
 352                 AbstractBlockBase<?> successorBlock = blocks[lastBlockIdx];
 353                 // handle last block
 354                 allocateBlock(successorBlock);
 355                 // handle remaining blocks
 356                 for (int i = lastBlockIdx - 1; i >= 0; i--) {
 357                     AbstractBlockBase<?> block = blocks[i];
 358                     // handle PHIs
 359                     resolvePhis(block, successorBlock);
 360                     boolean needResolution = allocateBlock(block);
 361 
 362                     if (needResolution) {
 363                         // resolve local data flow
 364                         resolveIntraTraceEdge(block, successorBlock);
 365                     }
 366                     successorBlock = block;
 367                 }
 368                 resolveLoopBackEdge(trace);
 369             } catch (Throwable e) {
 370                 throw Debug.handle(e);
 371             }
 372         }
 373 
 374         private final ArrayList<LIRInstruction> phiResolutionMoves = new ArrayList<>();
 375 
 376         /**
 377          * Resolve phi values, i.e., set the current location of values in the predecessors block
 378          * (which is not yet allocated) to the location of the variable defined by the phi in the
 379          * successor (which is already allocated). For constant inputs we insert moves.
 380          */
 381         private void resolvePhis(AbstractBlockBase<?> from, AbstractBlockBase<?> to) {
 382             if (SSAUtil.isMerge(to)) {
 383                 JumpOp jump = SSAUtil.phiOut(getLIR(), from);
 384                 LabelOp label = SSAUtil.phiIn(getLIR(), to);
 385 
 386                 assert phiResolutionMoves.isEmpty();
 387 
 388                 for (int i = 0; i < label.getPhiSize(); i++) {
 389                     visitPhiValuePair(jump.getOutgoingValue(i), label.getIncomingValue(i));
 390                 }
 391                 if (!phiResolutionMoves.isEmpty()) {
 392                     ArrayList<LIRInstruction> instructions = getLIR().getLIRforBlock(from);
 393                     instructions.addAll(instructions.size() - 1, phiResolutionMoves);
 394                     phiResolutionMoves.clear();
 395                 }
 396             }
 397         }
 398 
 399         private void visitPhiValuePair(Value phiOut, Value phiIn) {
 400             assert isStackSlotValue(phiIn) || isRegister(phiIn) : "PHI defined values is not a register or stack slot: " + phiIn;
 401             AllocatableValue in = asAllocatableValue(phiIn);
 402 
 403             AllocatableValue dest = isRegister(in) ? getCurrentValue(asRegister(in)) : in;
 404             final LIRInstruction move;
 405             if (isConstantValue(phiOut)) {
 406                 // insert move from constant
 407                 move = spillMoveFactory.createLoad(dest, LIRValueUtil.asConstant(phiOut));
 408             } else {
 409                 assert isVariable(phiOut) : "Not a variable or constant: " + phiOut;
 410                 // insert move from variable
 411                 move = spillMoveFactory.createMove(dest, asVariable(phiOut));
 412             }
 413             Debug.log("Inserting load %s", move);
 414             move.setComment(lirGenRes, "BottomUp: phi resolution");
 415             phiResolutionMoves.add(move);
 416         }
 417 
 418         private boolean requireResolution;
 419 
 420         /**
 421          * Intra-trace edges, i.e., edge where both, the source and the target block are in the same
 422          * trace, are either
 423          * <ul>
 424          * <li><em>immediate forward edges</em>, i.e., an edge from {@code i}th block of the trace
 425          * to the {@code (i+1)}th block, or
 426          * <li>a <em>loop back-edge</em> from the last block of the trace to the loop header.
 427          * </ul>
 428          * This property is guaranteed due to splitting of <em>critical edge</em>.
 429          *
 430          * Since forward edges are handled locally during bottom-up allocation we only need to check
 431          * for the second case.
 432          */
 433         private void resolveLoopBackEdge(Trace trace) {
 434             AbstractBlockBase<?>[] blocks = trace.getBlocks();
 435             AbstractBlockBase<?> endBlock = blocks[blocks.length - 1];
 436             if (endBlock.isLoopEnd()) {
 437                 assert endBlock.getSuccessorCount() == 1;
 438                 AbstractBlockBase<?> targetBlock = endBlock.getSuccessors()[0];
 439                 assert targetBlock.isLoopHeader() : String.format("Successor %s or loop end %s is not a loop header?", targetBlock, endBlock);
 440                 if (resultTraces.getTraceForBlock(targetBlock).equals(trace)) {
 441                     resolveLoopBackEdge(endBlock, targetBlock);
 442                 }
 443             }
 444         }
 445 
 446         private void resolveLoopBackEdge(AbstractBlockBase<?> from, AbstractBlockBase<?> to) {
 447             assert resultTraces.getTraceForBlock(from).equals(resultTraces.getTraceForBlock(to)) : "Not on the same trace? " + from + " -> " + to;
 448             resolveFindInsertPos(from, to);
 449             LIR lir = getLIR();
 450 
 451             if (SSAUtil.isMerge(to)) {
 452                 JumpOp blockEnd = SSAUtil.phiOut(lir, from);
 453                 LabelOp label = SSAUtil.phiIn(lir, to);
 454 
 455                 for (int i = 0; i < label.getPhiSize(); i++) {
 456                     Value incomingValue = label.getIncomingValue(i);
 457                     Value outgoingValue = blockEnd.getOutgoingValue(i);
 458                     resolveValuePair(incomingValue, outgoingValue);
 459                 }
 460             }
 461             resolveTraceEdge(from, to);
 462             moveResolver.resolveAndAppendMoves();
 463         }
 464 
 465         private void resolveIntraTraceEdge(AbstractBlockBase<?> from, AbstractBlockBase<?> to) {
 466             assert resultTraces.getTraceForBlock(from).equals(resultTraces.getTraceForBlock(to)) : "Not on the same trace? " + from + " -> " + to;
 467             resolveFindInsertPos(from, to);
 468             resolveTraceEdge(from, to);
 469             moveResolver.resolveAndAppendMoves();
 470         }
 471 
 472         private void resolveTraceEdge(AbstractBlockBase<?> from, AbstractBlockBase<?> to) {
 473             Value[] out = livenessInfo.getOutLocation(from);
 474             Value[] in = livenessInfo.getInLocation(to);
 475 
 476             assert out != null;
 477             assert in != null;
 478             assert out.length == in.length;
 479 
 480             for (int i = 0; i < out.length; i++) {
 481                 Value incomingValue = in[i];
 482                 Value outgoingValue = out[i];
 483                 resolveValuePair(incomingValue, outgoingValue);
 484             }
 485         }
 486 
 487         private void resolveValuePair(Value incomingValue, Value outgoingValue) {
 488             if (!isIllegal(incomingValue) && !TraceGlobalMoveResolver.isMoveToSelf(outgoingValue, incomingValue)) {
 489                 TraceGlobalMoveResolutionPhase.addMapping(moveResolver, outgoingValue, incomingValue);
 490             }
 491         }
 492 
 493         /**
 494          * @return {@code true} if the block requires data-flow resolution.
 495          */
 496         @SuppressWarnings("try")
 497         private boolean allocateBlock(AbstractBlockBase<?> block) {
 498             // might be set in insertSpillMoveAfter
 499             requireResolution = false;
 500 
 501             try (Indent indent = Debug.logAndIndent("handle block %s", block)) {
 502                 currentInstructions = getLIR().getLIRforBlock(block);
 503                 for (currentInstructionIndex = currentInstructions.size() - 1; currentInstructionIndex >= 0; currentInstructionIndex--) {
 504                     LIRInstruction inst = currentInstructions.get(currentInstructionIndex);
 505                     if (inst != null) {
 506                         inst.setId(currentOpId);
 507                         allocateInstruction(inst, block);
 508                     }
 509                 }
 510                 allocatedBlocks.set(block.getId());
 511             }
 512             return requireResolution;
 513         }
 514 
 515         @SuppressWarnings("try")
 516         private void allocateInstruction(LIRInstruction op, AbstractBlockBase<?> block) {
 517             assert op != null && op.id() == currentOpId;
 518             try (Indent indent = Debug.logAndIndent("handle inst: %d: %s", op.id(), op)) {
 519                 try (Indent indent1 = Debug.logAndIndent("output pos")) {
 520                     // spill caller saved registers
 521                     if (op.destroysCallerSavedRegisters()) {
 522                         spillCallerSavedRegisters();
 523                     }
 524 
 525                     // fixed
 526                     op.forEachOutput(allocFixedRegisterProcedure);
 527                     op.forEachTemp(allocFixedRegisterProcedure);
 528                     op.forEachAlive(allocFixedRegisterProcedure);
 529                     // variable
 530                     op.forEachOutput(allocRegisterProcedure);
 531                     op.forEachTemp(allocRegisterProcedure);
 532                     op.forEachAlive(allocRegisterProcedure);
 533                     /* state do never require a register */
 534                     // op.forEachState(allocRegisterProcedure);
 535 
 536                     // should have
 537                     op.forEachTemp(allocStackOrRegisterProcedure);
 538                     op.forEachOutput(allocStackOrRegisterProcedure);
 539                     if (op instanceof LabelOp) {
 540                         processIncoming(block, op);
 541                     }
 542                 }
 543                 try (Indent indent1 = Debug.logAndIndent("input pos")) {
 544 
 545                     currentOpId++;
 546 
 547                     // fixed
 548                     op.forEachInput(allocFixedRegisterProcedure);
 549                     // variable
 550                     op.forEachInput(allocRegisterProcedure);
 551 
 552                     op.forEachAlive(allocStackOrRegisterProcedure);
 553                     if (op instanceof BlockEndOp) {
 554                         processOutgoing(block, op);
 555                     }
 556                     op.forEachState(allocStackOrRegisterProcedure);
 557                     op.forEachInput(allocStackOrRegisterProcedure);
 558                 }
 559 
 560                 // insert spill/load instructions
 561                 insertInstructions();
 562                 currentOpId++;
 563             }
 564         }
 565 
 566         private void processIncoming(AbstractBlockBase<?> block, LIRInstruction instruction) {
 567             int[] vars = livenessInfo.getBlockIn(block);
 568             Value[] locs = new Value[vars.length];
 569             for (int i = 0; i < vars.length; i++) {
 570                 int varNum = vars[i];
 571                 if (varNum >= 0) {
 572                     locs[i] = allocStackOrRegister(instruction, livenessInfo.getVariable(varNum), OperandMode.DEF, LabelOp.incomingFlags);
 573                 }
 574             }
 575             livenessInfo.setInLocations(block, locs);
 576         }
 577 
 578         private void processOutgoing(AbstractBlockBase<?> block, LIRInstruction instruction) {
 579             int[] vars = livenessInfo.getBlockOut(block);
 580             Value[] locs = new Value[vars.length];
 581             for (int i = 0; i < vars.length; i++) {
 582                 locs[i] = allocStackOrRegister(instruction, livenessInfo.getVariable(vars[i]), OperandMode.ALIVE, JumpOp.outgoingFlags);
 583             }
 584             livenessInfo.setOutLocations(block, locs);
 585         }
 586 
 587         private void spillCallerSavedRegisters() {
 588             for (Register reg : callerSaveRegs) {
 589                 if (attributes(reg).isAllocatable()) {
 590                     evacuateRegisterAndSpill(reg);
 591                     assert checkRegisterUsage(reg);
 592                     setLastRegisterUsage(reg, currentOpId);
 593                 }
 594             }
 595             if (Debug.isLogEnabled()) {
 596                 Debug.log("operation destroys all caller-save registers");
 597             }
 598         }
 599 
 600         private final InstructionValueProcedure allocFixedRegisterProcedure = new InstructionValueProcedure() {
 601             @Override
 602             public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
 603                 return allocFixedRegister(value);
 604             }
 605         };
 606         private final InstructionValueProcedure allocRegisterProcedure = new InstructionValueProcedure() {
 607             @Override
 608             public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
 609                 return allocRegister(instruction, value, mode, flags);
 610             }
 611         };
 612         private final InstructionValueProcedure allocStackOrRegisterProcedure = new InstructionValueProcedure() {
 613             @Override
 614             public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
 615                 return allocStackOrRegister(instruction, value, mode, flags);
 616             }
 617         };
 618 
 619         private Value allocFixedRegister(Value value) {
 620             if (isRegister(value)) {
 621                 Register reg = asRegister(value);
 622                 assert checkRegisterUsage(reg);
 623                 evacuateRegisterAndSpill(reg);
 624                 setRegisterUsage(reg, asAllocatableValue(value));
 625             }
 626             return value;
 627         }
 628 
 629         private Value allocRegister(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
 630             if (isVariable(value) && requiresRegisters(instruction, value, mode, flags)) {
 631                 Variable var = asVariable(value);
 632                 // check if available
 633                 AllocatableValue currentLocation = getCurrentLocation(var);
 634                 if (currentLocation == null) {
 635                     // nothing yet assigned
 636                     return allocRegister(var, mode);
 637                 }
 638                 // already a location assigned
 639                 if (isRegister(currentLocation)) {
 640                     // register assigned -> nothing todo
 641                     setLastRegisterUsage(asRegister(currentLocation), currentOpId);
 642                     return currentLocation;
 643                 }
 644                 assert isStackSlotValue(currentLocation);
 645                 // stackSlot assigned but need register -> spill
 646                 Value allocatedRegister = allocRegister(var, mode);
 647                 if (mode == OperandMode.USE) {
 648                     // input might be destroyed at the def position
 649                     // but it must be available before the instruction
 650                     insertSpillMoveBefore(currentLocation, allocatedRegister);
 651                 } else {
 652                     insertSpillMoveAfter(currentLocation, allocatedRegister);
 653                 }
 654                 return allocatedRegister;
 655             }
 656             return value;
 657         }
 658 
 659         private Value allocRegister(Variable var, OperandMode mode) {
 660             PlatformKind platformKind = var.getPlatformKind();
 661             Register freeRegister = findFreeRegister(platformKind, mode);
 662             if (freeRegister == null) {
 663                 // no free register found, looking for a blocked one
 664                 freeRegister = findLockedRegister(platformKind, mode);
 665                 if (freeRegister == null) {
 666                     throw new OutOfRegistersException("TraceRA[BottomUp]: no register found");
 667                 }
 668             }
 669             // found a register
 670             setRegisterUsage(freeRegister, var);
 671             RegisterValue registerValue = freeRegister.asValue(var.getValueKind());
 672             setCurrentLocation(var, registerValue);
 673             Debug.log("AllocateRegister[%5s] %s for %s", mode, freeRegister, var);
 674             return registerValue;
 675         }
 676 
 677         private Value allocStackOrRegister(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
 678             if (isRegister(value)) {
 679                 if ((mode == OperandMode.DEF || mode == OperandMode.TEMP) && !(instruction instanceof LabelOp)) {
 680                     freeRegister(asRegister(value));
 681                 }
 682                 return value;
 683             }
 684             if (isVariable(value)) {
 685                 assert !requiresRegisters(instruction, value, mode, flags) : "Should have a register already: " + value;
 686                 Variable var = asVariable(value);
 687                 // check if available
 688                 AllocatableValue currentLocation = getCurrentLocation(var);
 689                 if (currentLocation != null) {
 690                     // already a location assigned -> nothing todo
 691                     if (isRegister(currentLocation)) {
 692                         Register reg = asRegister(currentLocation);
 693                         if (mode == OperandMode.ALIVE && killedAtDef(reg)) {
 694                             AllocatableValue spillSlot = allocateSpillSlot(var);
 695                             insertSpillMoveBefore(spillSlot, currentLocation);
 696                             Debug.log("AllocateStackOrReg[%5s] temporary use %s for %s since current location %s is destroyed at def", mode, spillSlot, var, currentLocation);
 697                             return spillSlot;
 698                         }
 699                         // update register usage
 700                         setLastRegisterUsage(reg, currentOpId);
 701                     }
 702                     Debug.log(3, "AllocateStackOrReg[%5s] %s already in %s", mode, var, currentLocation);
 703                     return currentLocation;
 704                 }
 705                 // no location available
 706                 PlatformKind platformKind = var.getPlatformKind();
 707                 Register freeRegister = findFreeRegister(platformKind, mode);
 708                 if (freeRegister == null) {
 709                     // no free register available -> either spill current or free a register
 710                     AllocatableValue spillSlot = allocateSpillSlot(var);
 711                     setCurrentLocation(var, spillSlot);
 712                     return spillSlot;
 713                 }
 714                 assert freeRegister != null;
 715                 // found a register
 716                 setRegisterUsage(freeRegister, var);
 717                 RegisterValue registerValue = freeRegister.asValue(var.getValueKind());
 718                 setCurrentLocation(var, registerValue);
 719                 Debug.log("AllocateStackOrReg[%5s] %s for %s", mode, freeRegister, var);
 720                 return registerValue;
 721             }
 722             return value;
 723         }
 724 
 725         private boolean killedAtDef(Register reg) {
 726             return getLastRegisterKill(reg) == currentOpId - 1;
 727         }
 728 
 729         /**
 730          * Searches for a free register.
 731          */
 732         @SuppressWarnings("try")
 733         private Register findFreeRegister(PlatformKind kind, OperandMode mode) {
 734             AllocatableRegisters allocatableRegisters = registerAllocationConfig.getAllocatableRegisters(kind);
 735             Register[] availableRegs = allocatableRegisters.allocatableRegisters;
 736             for (Register reg : availableRegs) {
 737                 AllocatableValue currentVal = getCurrentValue(reg);
 738                 if (currentVal == null && !isCurrentlyUsed(reg, mode)) {
 739                     return reg;
 740                 }
 741             }
 742             if (Debug.isLogEnabled()) {
 743                 try (Indent i = Debug.logAndIndent("All Registers occupied:")) {
 744                     for (Register reg : availableRegs) {
 745                         Debug.log("%6s: last used %4d %s", reg, getLastRegisterUsage(reg), getCurrentValue(reg));
 746                     }
 747                 }
 748             }
 749             return null;
 750         }
 751 
 752         /**
 753          * Searches for a occupied register to spill.
 754          */
 755         @SuppressWarnings("try")
 756         private Register findLockedRegister(PlatformKind kind, OperandMode mode) {
 757             AllocatableRegisters allocatableRegisters = registerAllocationConfig.getAllocatableRegisters(kind);
 758             Register[] availableRegs = allocatableRegisters.allocatableRegisters;
 759             // TODO (je): better strategies for spilling
 760             // TODO (je): we need to ensure that we do not use the register in the current
 761             // instruction!
 762             Register lockedReg = null;
 763             for (Register reg : availableRegs) {
 764                 if (!isCurrentlyUsed(reg, mode) && !isActiveFixedRegister(reg)) {
 765                     lockedReg = reg;
 766                     break;
 767                 }
 768             }
 769             if (lockedReg == null) {
 770                 return null;
 771             }
 772             evacuateRegisterAndSpill(lockedReg);
 773             return lockedReg;
 774         }
 775 
 776         private boolean isActiveFixedRegister(Register reg) {
 777             return isRegister(getCurrentValue(reg));
 778         }
 779 
 780         private boolean isCurrentlyUsed(Register reg, OperandMode mode) {
 781             int lastRegUsage = getLastRegisterUsage(reg);
 782             if (lastRegUsage == currentOpId) {
 783                 return true;
 784             }
 785             return mode == OperandMode.ALIVE && lastRegUsage == (currentOpId & ~1);
 786         }
 787 
 788         private void freeRegister(Register reg) {
 789             AllocatableValue val = getCurrentValue(reg);
 790             setCurrentValue(reg, null);
 791             setLastRegisterKill(reg, currentOpId);
 792             if (val != null && isVariable(val)) {
 793                 Variable var = asVariable(val);
 794                 setCurrentLocation(var, null);
 795                 Debug.log("Free Registers %s (was %s)", reg, var);
 796             } else {
 797                 Debug.log("Free Registers %s", reg);
 798             }
 799         }
 800 
 801         private void setRegisterUsage(Register reg, AllocatableValue currentValue) {
 802             assert checkRegisterUsage(reg);
 803             setCurrentValue(reg, currentValue);
 804             setLastRegisterUsage(reg, currentOpId);
 805         }
 806 
 807         private boolean checkRegisterUsage(Register reg) {
 808             AllocatableValue currentValue = getCurrentValue(reg);
 809             assert getLastRegisterUsage(reg) < currentOpId || currentValue == null || isRegister(currentValue) && asRegister(currentValue).equals(reg) : String.format("Register %s is occupied", reg);
 810             return true;
 811         }
 812 
 813         /**
 814          * Frees a registers and spill the variable that is currently occupying it.
 815          *
 816          * @return The value that currently occupies the register or {@code null} if there is none.
 817          */
 818         private AllocatableValue evacuateRegisterAndSpill(Register reg) {
 819             AllocatableValue val = evacuateRegister(reg);
 820             spillVariable(val, reg);
 821             return val;
 822         }
 823 
 824         /**
 825          * Frees a registers. The variable that is currently occupying it is <em>not</em> spilled.
 826          *
 827          * @return The value that currently occupies the register or {@code null} if there is none.
 828          */
 829         private AllocatableValue evacuateRegister(Register reg) {
 830             AllocatableValue val = getCurrentValue(reg);
 831             if (val == null) {
 832                 return null;
 833             }
 834             setCurrentValue(reg, null);
 835             return val;
 836         }
 837 
 838         private void spillVariable(AllocatableValue val, Register reg) {
 839             if (val != null && isVariable(val)) {
 840                 Variable var = asVariable(val);
 841                 Debug.log("Spill Variable %s from %s", var, reg);
 842                 // insert reload
 843                 AllocatableValue spillSlot = allocateSpillSlot(var);
 844                 setCurrentLocation(var, spillSlot);
 845                 insertSpillMoveAfter(reg.asValue(var.getValueKind()), spillSlot);
 846             }
 847         }
 848     }
 849 }