--- /dev/null 2017-01-22 10:16:57.869617664 -0800 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/bu/BottomUpAllocator.java 2017-02-15 17:05:36.518957859 -0800 @@ -0,0 +1,749 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.lir.alloc.trace.bu; + +import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts; +import static org.graalvm.compiler.lir.LIRValueUtil.asVariable; +import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue; +import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue; +import static org.graalvm.compiler.lir.LIRValueUtil.isVariable; +import static jdk.vm.ci.code.ValueUtil.asAllocatableValue; +import static jdk.vm.ci.code.ValueUtil.asRegister; +import static jdk.vm.ci.code.ValueUtil.isIllegal; +import static jdk.vm.ci.code.ValueUtil.isRegister; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; + +import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig; +import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig.AllocatableRegisters; +import org.graalvm.compiler.core.common.alloc.Trace; +import org.graalvm.compiler.core.common.alloc.TraceBuilderResult; +import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; +import org.graalvm.compiler.debug.Debug; +import org.graalvm.compiler.debug.Debug.Scope; +import org.graalvm.compiler.debug.Indent; +import org.graalvm.compiler.lir.InstructionValueProcedure; +import org.graalvm.compiler.lir.LIR; +import org.graalvm.compiler.lir.LIRInstruction; +import org.graalvm.compiler.lir.LIRInstruction.OperandFlag; +import org.graalvm.compiler.lir.LIRInstruction.OperandMode; +import org.graalvm.compiler.lir.LIRValueUtil; +import org.graalvm.compiler.lir.RedundantMoveElimination; +import org.graalvm.compiler.lir.StandardOp; +import org.graalvm.compiler.lir.StandardOp.BlockEndOp; +import org.graalvm.compiler.lir.StandardOp.LabelOp; +import org.graalvm.compiler.lir.Variable; +import org.graalvm.compiler.lir.VirtualStackSlot; +import org.graalvm.compiler.lir.alloc.OutOfRegistersException; +import org.graalvm.compiler.lir.alloc.trace.TraceAllocationPhase; +import org.graalvm.compiler.lir.alloc.trace.TraceAllocationPhase.TraceAllocationContext; +import org.graalvm.compiler.lir.alloc.trace.TraceGlobalMoveResolutionPhase; +import org.graalvm.compiler.lir.alloc.trace.TraceGlobalMoveResolver; +import org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase; +import org.graalvm.compiler.lir.gen.LIRGenerationResult; +import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory; +import org.graalvm.compiler.lir.ssa.SSAUtil; +import org.graalvm.compiler.lir.ssa.SSAUtil.PhiValueVisitor; +import org.graalvm.compiler.lir.ssi.SSIUtil; + +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.RegisterArray; +import jdk.vm.ci.code.RegisterAttributes; +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.meta.AllocatableValue; +import jdk.vm.ci.meta.PlatformKind; +import jdk.vm.ci.meta.Value; + +/** + * Allocates registers within a trace in a greedy, bottom-up fashion. The liveness information is + * computed on the fly as the instructions are traversed instead of computing it in a separate pass. + * The goal of this allocator is to provide a simple and fast algorithm for situations where code + * quality is not the primary target. + * + * This implementation does not (yet) exploit hinting information and might introduce multiple spill + * moves to the same stack slot (which are likely to be remove by {@link RedundantMoveElimination}. + * + * The current implementation cannot deal with {@link AbstractBlockBase blocks} with edges to + * compiled exception handlers since it might introduce spill code after the {@link LIRInstruction + * instruction} that triggers the exception. + */ +public final class BottomUpAllocator extends TraceAllocationPhase { + private final TargetDescription target; + private final LIRGenerationResult lirGenRes; + private final MoveFactory spillMoveFactory; + private final RegisterAllocationConfig registerAllocationConfig; + private final RegisterArray callerSaveRegs; + private final RegisterAttributes[] registerAttributes; + private final BitSet allocatedBlocks; + private final TraceBuilderResult resultTraces; + private final TraceGlobalMoveResolver moveResolver; + + /** + * Maps from {@link Variable#index} to a spill stack slot. If + * {@linkplain org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase.Options#TraceRACacheStackSlots + * enabled} a {@link Variable} is always assigned to the same stack slot. + */ + private final AllocatableValue[] stackSlots; + + private final ArrayList insertInstructionsBefore; + private final ArrayList insertInstructionsAfter; + private final boolean neverSpillConstants; + + public BottomUpAllocator(TargetDescription target, LIRGenerationResult lirGenRes, MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig, + AllocatableValue[] cachedStackSlots, TraceBuilderResult resultTraces, boolean neverSpillConstant) { + this.target = target; + this.lirGenRes = lirGenRes; + this.spillMoveFactory = spillMoveFactory; + this.registerAllocationConfig = registerAllocationConfig; + this.callerSaveRegs = registerAllocationConfig.getRegisterConfig().getCallerSaveRegisters(); + this.registerAttributes = registerAllocationConfig.getRegisterConfig().getAttributesMap(); + this.allocatedBlocks = new BitSet(lirGenRes.getLIR().getControlFlowGraph().getBlocks().length); + this.resultTraces = resultTraces; + this.moveResolver = new TraceGlobalMoveResolver(lirGenRes, spillMoveFactory, target.arch); + this.neverSpillConstants = neverSpillConstant; + + this.insertInstructionsBefore = new ArrayList<>(4); + this.insertInstructionsAfter = new ArrayList<>(4); + + if (TraceRegisterAllocationPhase.Options.TraceRACacheStackSlots.getValue()) { + this.stackSlots = cachedStackSlots; + } else { + this.stackSlots = new AllocatableValue[lirGenRes.getLIR().numVariables()]; + } + + } + + private LIR getLIR() { + return lirGenRes.getLIR(); + } + + /** + * Gets an object describing the attributes of a given register according to this register + * configuration. + */ + private RegisterAttributes attributes(Register reg) { + return registerAttributes[reg.number]; + } + + /** + * Returns a new spill slot or a cached entry if there is already one for the variable. + */ + private AllocatableValue allocateSpillSlot(Variable var) { + int variableIndex = var.index; + AllocatableValue cachedStackSlot = stackSlots[variableIndex]; + if (cachedStackSlot != null) { + TraceRegisterAllocationPhase.globalStackSlots.increment(); + assert cachedStackSlot.getValueKind().equals(var.getValueKind()) : "CachedStackSlot: kind mismatch? " + var.getValueKind() + " vs. " + cachedStackSlot.getValueKind(); + return cachedStackSlot; + } + VirtualStackSlot slot = lirGenRes.getFrameMapBuilder().allocateSpillSlot(var.getValueKind()); + stackSlots[variableIndex] = slot; + TraceRegisterAllocationPhase.allocatedStackSlots.increment(); + return slot; + } + + private final PhiValueVisitor resolveLoopBackEdgeVisitor = (Value in, Value out) -> { + resolveBackEdge(in, out); + }; + + private void resolveBackEdge(Value in, Value out) { + if (!isIllegal(in) && !TraceGlobalMoveResolver.isMoveToSelf(out, in)) { + TraceGlobalMoveResolutionPhase.addMapping(moveResolver, out, in); + } + } + + @Override + protected void run(@SuppressWarnings("hiding") TargetDescription target, @SuppressWarnings("hiding") LIRGenerationResult lirGenRes, Trace trace, TraceAllocationContext context) { + allocate(trace); + } + + private void allocate(Trace trace) { + if (neverSpillConstants) { + throw JVMCIError.unimplemented("NeverSpillConstant not supported!"); + } + new Allocator().allocateTrace(trace); + assert verify(trace); + } + + private boolean verify(Trace trace) { + for (AbstractBlockBase block : trace.getBlocks()) { + assert LIR.verifyBlock(lirGenRes.getLIR(), block); + } + return true; + } + + private static boolean requiresRegisters(LIRInstruction instruction, Value value, OperandMode mode, EnumSet flags) { + assert isVariable(value) : "Not a variable " + value; + if (instruction instanceof LabelOp) { + // phi and incoming values do not require a register + return false; + } + if (mode == OperandMode.DEF || mode == OperandMode.TEMP) { + return true; + } + return !flags.contains(OperandFlag.STACK); + } + + private void resolveFindInsertPos(AbstractBlockBase fromBlock, AbstractBlockBase toBlock) { + LIR lir = lirGenRes.getLIR(); + if (fromBlock.getSuccessorCount() <= 1) { + if (Debug.isLogEnabled()) { + Debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId()); + } + + List instructions = lir.getLIRforBlock(fromBlock); + LIRInstruction instr = instructions.get(instructions.size() - 1); + if (instr instanceof StandardOp.JumpOp) { + // insert moves before branch + moveResolver.setInsertPosition(instructions, instructions.size() - 1); + } else { + moveResolver.setInsertPosition(instructions, instructions.size()); + } + + } else { + if (Debug.isLogEnabled()) { + Debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId()); + } + + if (DetailedAsserts.getValue()) { + assert lir.getLIRforBlock(fromBlock).get(0) instanceof StandardOp.LabelOp : "block does not start with a label"; + + /* + * Because the number of predecessor edges matches the number of successor edges, + * blocks which are reached by switch statements may have be more than one + * predecessor but it will be guaranteed that all predecessors will be the same. + */ + for (AbstractBlockBase predecessor : toBlock.getPredecessors()) { + assert fromBlock == predecessor : "all critical edges must be broken"; + } + } + + moveResolver.setInsertPosition(lir.getLIRforBlock(toBlock), 1); + } + } + + private final class Allocator { + + /** + * Maps from {@linkplain Register#number register} to the current {@linkplain Variable + * variable}. + */ + private final AllocatableValue[] currentRegisterMapping; + /** + * Maps from {@linkplain Variable#index variable} to its current location. + */ + private final AllocatableValue[] locations; + + private final int[] lastRegisterUsage; + private final int[] lastRegisterKill; + + private ArrayList currentInstructions; + private int currentInstructionIndex; + private int currentOpId; + + private Allocator() { + RegisterArray registers = target.arch.getRegisters(); + int numRegs = registers.size(); + int numVar = getLIR().numVariables(); + currentRegisterMapping = new AllocatableValue[numRegs]; + lastRegisterUsage = new int[numRegs]; + lastRegisterKill = new int[numRegs]; + locations = new AllocatableValue[numVar]; + // we start at offset 2 to distinguish if from the default value + currentOpId = 2; + } + + private void setCurrentValue(Register reg, AllocatableValue val) { + currentRegisterMapping[reg.number] = val; + } + + private AllocatableValue getCurrentValue(Register reg) { + return currentRegisterMapping[reg.number]; + } + + private int getLastRegisterUsage(Register reg) { + return lastRegisterUsage[reg.number]; + } + + private void setLastRegisterUsage(Register reg, int pos) { + Debug.log("Register %s last used %d", reg, pos); + lastRegisterUsage[reg.number] = pos; + } + + private int getLastRegisterKill(Register reg) { + return lastRegisterKill[reg.number]; + } + + private void setLastRegisterKill(Register reg, int pos) { + Debug.log("Register %s killed %d", reg, pos); + lastRegisterKill[reg.number] = pos; + } + + private void setCurrentLocation(Variable var, AllocatableValue location) { + locations[var.index] = location; + } + + private AllocatableValue getCurrentLocation(Variable var) { + return locations[var.index]; + } + + private void insertSpillMoveBefore(AllocatableValue dst, Value src) { + LIRInstruction move = spillMoveFactory.createMove(dst, src); + insertInstructionsBefore.add(move); + Debug.log("insert before %s", move); + } + + private void insertSpillMoveAfter(AllocatableValue dst, Value src) { + LIRInstruction inst = currentInstructions.get(currentInstructionIndex); + if (!(inst instanceof BlockEndOp)) { + LIRInstruction move = spillMoveFactory.createMove(dst, src); + insertInstructionsAfter.add(move); + Debug.log("insert after %s", move); + } else { + Debug.log("Block end op. No from %s to %s necessary.", src, dst); + } + } + + private void insertInstructions() { + // TODO (je) this is can probably be improved + currentInstructions.ensureCapacity(currentInstructions.size() + insertInstructionsBefore.size() + insertInstructionsAfter.size()); + LIRInstruction inst = currentInstructions.get(currentInstructionIndex); + // insert after + if (insertInstructionsAfter.size() != 0) { + Collections.reverse(insertInstructionsAfter); + assert !(inst instanceof BlockEndOp) : "Cannot insert instruction after the block end op: " + inst; + currentInstructions.addAll(currentInstructionIndex + 1, insertInstructionsAfter); + insertInstructionsAfter.clear(); + } + // insert before + if (insertInstructionsBefore.size() != 0) { + assert !(inst instanceof LabelOp) : "Cannot insert instruction before the label op: " + inst; + currentInstructions.addAll(currentInstructionIndex, insertInstructionsBefore); + insertInstructionsBefore.clear(); + } + } + + @SuppressWarnings("try") + private void allocateTrace(Trace trace) { + try (Scope s = Debug.scope("BottomUpAllocator", trace.getBlocks()); Indent indent = Debug.logAndIndent("%s (Trace%d)", trace, trace.getId())) { + AbstractBlockBase successorBlock = null; + for (int i = trace.getBlocks().length - 1; i >= 0; i--) { + AbstractBlockBase block = trace.getBlocks()[i]; + // handle PHIs + if (successorBlock != null) { + resolvePhis(successorBlock, block); + } + allocateBlock(block); + successorBlock = block; + } + resolveLocalDataFlow(trace); + } catch (Throwable e) { + throw Debug.handle(e); + } + } + + /** + * Resolve phi values, i.e. set the current location of values in the predecessors block + * (which is not yet allocated) to the location of the variable defined by the phi in the + * successor (which is already allocated). For constant inputs we insert moves. + */ + private void resolvePhis(AbstractBlockBase successorBlock, AbstractBlockBase block) { + // Note that we are only visiting PHI values, not transient SSI values. + phiVisitor.loads.clear(); + SSAUtil.forEachPhiValuePair(getLIR(), successorBlock, block, phiVisitor); + if (phiVisitor.loads.size() > 0) { + ArrayList instructions = getLIR().getLIRforBlock(block); + instructions.addAll(instructions.size() - 1, phiVisitor.loads); + } + } + + private final PhiVisitor phiVisitor = new PhiVisitor(); + + private final class PhiVisitor implements PhiValueVisitor { + + private final ArrayList loads = new ArrayList<>(); + + @Override + public void visit(Value phiIn, Value phiOut) { + assert isStackSlotValue(phiIn) || isRegister(phiIn) : "PHI defined values is not a register or stack slot: " + phiIn; + AllocatableValue in = asAllocatableValue(phiIn); + + AllocatableValue dest = isRegister(in) ? getCurrentValue(asRegister(in)) : in; + final LIRInstruction load; + if (isConstantValue(phiOut)) { + // insert move from constant + load = spillMoveFactory.createLoad(dest, LIRValueUtil.asConstant(phiOut)); + } else { + assert isVariable(phiOut) : "Not a variable or constant: " + phiOut; + // insert move from variable + load = spillMoveFactory.createMove(dest, asVariable(phiOut)); + } + Debug.log("Inserting load %s", load); + loads.add(load); + return; + } + } + + private void resolveLocalDataFlow(Trace trace) { + for (AbstractBlockBase block : trace.getBlocks()) { + for (AbstractBlockBase pred : block.getPredecessors()) { + if (resultTraces.getTraceForBlock(pred).equals(trace)) { + resolveFindInsertPos(pred, block); + SSIUtil.forEachValuePair(getLIR(), block, pred, resolveLoopBackEdgeVisitor); + moveResolver.resolveAndAppendMoves(); + } + } + } + } + + @SuppressWarnings("try") + private void allocateBlock(AbstractBlockBase block) { + try (Indent indent = Debug.logAndIndent("handle block %s", block)) { + currentInstructions = getLIR().getLIRforBlock(block); + for (currentInstructionIndex = currentInstructions.size() - 1; currentInstructionIndex >= 0; currentInstructionIndex--) { + LIRInstruction inst = currentInstructions.get(currentInstructionIndex); + if (inst != null) { + inst.setId(currentOpId); + allocateInstruction(inst); + } + } + allocatedBlocks.set(block.getId()); + } + } + + @SuppressWarnings("try") + private void allocateInstruction(LIRInstruction op) { + assert op != null && op.id() == currentOpId; + try (Indent indent = Debug.logAndIndent("handle inst: %d: %s", op.id(), op)) { + try (Indent indent1 = Debug.logAndIndent("output pos")) { + // spill caller saved registers + if (op.destroysCallerSavedRegisters()) { + spillCallerSavedRegisters(); + } + + // fixed + op.forEachOutput(allocFixedRegisterProcedure); + op.forEachTemp(allocFixedRegisterProcedure); + op.forEachAlive(allocFixedRegisterProcedure); + // variable + op.forEachOutput(allocRegisterProcedure); + op.forEachTemp(allocRegisterProcedure); + op.forEachAlive(allocRegisterProcedure); + /* state do never require a register */ + // op.forEachState(allocRegisterProcedure); + + // should have + op.forEachTemp(allocStackOrRegisterProcedure); + op.forEachOutput(allocStackOrRegisterProcedure); + } + try (Indent indent1 = Debug.logAndIndent("input pos")) { + + currentOpId++; + + // fixed + op.forEachInput(allocFixedRegisterProcedure); + // variable + op.forEachInput(allocRegisterProcedure); + + op.forEachAlive(allocStackOrRegisterProcedure); + op.forEachState(allocStackOrRegisterProcedure); + op.forEachInput(allocStackOrRegisterProcedure); + } + + // insert spill/load instructions + insertInstructions(); + currentOpId++; + } + } + + private void spillCallerSavedRegisters() { + for (Register reg : callerSaveRegs) { + if (attributes(reg).isAllocatable()) { + evacuateRegisterAndSpill(reg); + assert checkRegisterUsage(reg); + evacuateRegisterAndSpill(reg); + // setCurrentValue(reg, reg.asValue()); + setLastRegisterUsage(reg, currentOpId); + } + } + if (Debug.isLogEnabled()) { + Debug.log("operation destroys all caller-save registers"); + } + } + + private final InstructionValueProcedure allocFixedRegisterProcedure = new InstructionValueProcedure() { + @Override + public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet flags) { + return allocFixedRegister(value); + } + }; + private final InstructionValueProcedure allocRegisterProcedure = new InstructionValueProcedure() { + @Override + public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet flags) { + return allocRegister(instruction, value, mode, flags); + } + }; + private final InstructionValueProcedure allocStackOrRegisterProcedure = new InstructionValueProcedure() { + @Override + public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet flags) { + return allocStackOrRegister(instruction, value, mode, flags); + } + }; + + private Value allocFixedRegister(Value value) { + if (isRegister(value)) { + Register reg = asRegister(value); + assert checkRegisterUsage(reg); + evacuateRegisterAndSpill(reg); + setRegisterUsage(reg, asAllocatableValue(value)); + } + return value; + } + + private Value allocRegister(LIRInstruction instruction, Value value, OperandMode mode, EnumSet flags) { + if (isVariable(value) && requiresRegisters(instruction, value, mode, flags)) { + Variable var = asVariable(value); + // check if available + AllocatableValue currentLocation = getCurrentLocation(var); + if (currentLocation == null) { + // nothing yet assigned + return allocRegister(var, mode); + } + // already a location assigned + if (isRegister(currentLocation)) { + // register assigned -> nothing todo + setLastRegisterUsage(asRegister(currentLocation), currentOpId); + return currentLocation; + } + assert isStackSlotValue(currentLocation); + // stackSlot assigned but need register -> spill + Value allocatedRegister = allocRegister(var, mode); + if (mode == OperandMode.USE) { + // input might be destroyed at the def position + // but it must be available before the instruction + insertSpillMoveBefore(currentLocation, allocatedRegister); + } else { + insertSpillMoveAfter(currentLocation, allocatedRegister); + } + return allocatedRegister; + } + return value; + } + + private Value allocRegister(Variable var, OperandMode mode) { + PlatformKind platformKind = var.getPlatformKind(); + Register freeRegister = findFreeRegister(platformKind, mode); + if (freeRegister == null) { + // no free register found, looking for a blocked one + freeRegister = findLockedRegister(platformKind, mode); + if (freeRegister == null) { + throw new OutOfRegistersException("TraceRA[BottomUp]: no register found"); + } + } + // found a register + setRegisterUsage(freeRegister, var); + RegisterValue registerValue = freeRegister.asValue(var.getValueKind()); + setCurrentLocation(var, registerValue); + Debug.log("AllocateRegister[%5s] %s for %s", mode, freeRegister, var); + return registerValue; + } + + private Value allocStackOrRegister(LIRInstruction instruction, Value value, OperandMode mode, EnumSet flags) { + if (isRegister(value)) { + if ((mode == OperandMode.DEF || mode == OperandMode.TEMP) && !(instruction instanceof LabelOp)) { + freeRegister(asRegister(value)); + } + return value; + } + if (isVariable(value)) { + assert !requiresRegisters(instruction, value, mode, flags) : "Should have a register already: " + value; + Variable var = asVariable(value); + // check if available + AllocatableValue currentLocation = getCurrentLocation(var); + if (currentLocation != null) { + // already a location assigned -> nothing todo + if (isRegister(currentLocation)) { + Register reg = asRegister(currentLocation); + if (mode == OperandMode.ALIVE && killedAtDef(reg)) { + AllocatableValue spillSlot = allocateSpillSlot(var); + insertSpillMoveBefore(spillSlot, currentLocation); + Debug.log("AllocateStackOrReg[%5s] temporary use %s for %s since current location %s is destroyed at def", mode, spillSlot, var, currentLocation); + return spillSlot; + } + // update register usage + setLastRegisterUsage(reg, currentOpId); + } + Debug.log(3, "AllocateStackOrReg[%5s] %s already in %s", mode, var, currentLocation); + return currentLocation; + } + // no location available + PlatformKind platformKind = var.getPlatformKind(); + Register freeRegister = findFreeRegister(platformKind, mode); + if (freeRegister == null) { + // no free register available -> either spill current or free a register + AllocatableValue spillSlot = allocateSpillSlot(var); + setCurrentLocation(var, spillSlot); + return spillSlot; + } + assert freeRegister != null; + // found a register + setRegisterUsage(freeRegister, var); + RegisterValue registerValue = freeRegister.asValue(var.getValueKind()); + setCurrentLocation(var, registerValue); + Debug.log("AllocateStackOrReg[%5s] %s for %s", mode, freeRegister, var); + return registerValue; + } + return value; + } + + private boolean killedAtDef(Register reg) { + return getLastRegisterKill(reg) == currentOpId - 1; + } + + /** + * Searches for a free register. + */ + @SuppressWarnings("try") + private Register findFreeRegister(PlatformKind kind, OperandMode mode) { + AllocatableRegisters allocatableRegisters = registerAllocationConfig.getAllocatableRegisters(kind); + Register[] availableRegs = allocatableRegisters.allocatableRegisters; + for (Register reg : availableRegs) { + AllocatableValue currentVal = getCurrentValue(reg); + if (currentVal == null && !isCurrentlyUsed(reg, mode)) { + return reg; + } + } + if (Debug.isLogEnabled()) { + try (Indent i = Debug.logAndIndent("All Registers occupied:")) { + for (Register reg : availableRegs) { + Debug.log("%6s: last used %4d %s", reg, getLastRegisterUsage(reg), getCurrentValue(reg)); + } + } + } + return null; + } + + /** + * Searches for a occupied register to spill. + */ + @SuppressWarnings("try") + private Register findLockedRegister(PlatformKind kind, OperandMode mode) { + AllocatableRegisters allocatableRegisters = registerAllocationConfig.getAllocatableRegisters(kind); + Register[] availableRegs = allocatableRegisters.allocatableRegisters; + // TODO (je): better strategies for spilling + // TODO (je): we need to ensure that we do not use the register in the current + // instruction! + Register lockedReg = null; + for (Register reg : availableRegs) { + if (!isCurrentlyUsed(reg, mode) && !isActiveFixedRegister(reg)) { + lockedReg = reg; + break; + } + } + if (lockedReg == null) { + return null; + } + evacuateRegisterAndSpill(lockedReg); + return lockedReg; + } + + private boolean isActiveFixedRegister(Register reg) { + return isRegister(getCurrentValue(reg)); + } + + private boolean isCurrentlyUsed(Register reg, OperandMode mode) { + int lastRegUsage = getLastRegisterUsage(reg); + if (lastRegUsage == currentOpId) { + return true; + } + return mode == OperandMode.ALIVE && lastRegUsage == (currentOpId & ~1); + } + + private void freeRegister(Register reg) { + AllocatableValue val = getCurrentValue(reg); + setCurrentValue(reg, null); + setLastRegisterKill(reg, currentOpId); + if (val != null && isVariable(val)) { + Variable var = asVariable(val); + setCurrentLocation(var, null); + Debug.log("Free Registers %s (was %s)", reg, var); + } else { + Debug.log("Free Registers %s", reg); + } + } + + private void setRegisterUsage(Register reg, AllocatableValue currentValue) { + assert checkRegisterUsage(reg); + setCurrentValue(reg, currentValue); + setLastRegisterUsage(reg, currentOpId); + } + + private boolean checkRegisterUsage(Register reg) { + AllocatableValue currentValue = getCurrentValue(reg); + assert getLastRegisterUsage(reg) < currentOpId || currentValue == null || isRegister(currentValue) && asRegister(currentValue).equals(reg) : String.format("Register %s is occupied", reg); + return true; + } + + /** + * Frees a registers and spill the variable that is currently occupying it. + * + * @return The value that currently occupies the register or {@code null} if there is none. + */ + private AllocatableValue evacuateRegisterAndSpill(Register reg) { + AllocatableValue val = evacuateRegister(reg); + spillVariable(val, reg); + return val; + } + + /** + * Frees a registers. The variable that is currently occupying it is not spilled. + * + * @return The value that currently occupies the register or {@code null} if there is none. + */ + private AllocatableValue evacuateRegister(Register reg) { + AllocatableValue val = getCurrentValue(reg); + if (val == null) { + return null; + } + setCurrentValue(reg, null); + return val; + } + + private void spillVariable(AllocatableValue val, Register reg) { + if (val != null && isVariable(val)) { + Variable var = asVariable(val); + Debug.log("Spill Variable %s from %s", var, reg); + // insert reload + AllocatableValue spillSlot = allocateSpillSlot(var); + setCurrentLocation(var, spillSlot); + insertSpillMoveAfter(reg.asValue(var.getValueKind()), spillSlot); + } + } + } +}