blob: eaf2f3a1d1ccc87e596a5e8e41749b977628d1da [file] [log] [blame]
/*
* 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<TraceAllocationContext> {
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<LIRInstruction> insertInstructionsBefore;
private final ArrayList<LIRInstruction> 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<OperandFlag> 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<LIRInstruction> 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<LIRInstruction> 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<LIRInstruction> 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<LIRInstruction> 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<OperandFlag> flags) {
return allocFixedRegister(value);
}
};
private final InstructionValueProcedure allocRegisterProcedure = new InstructionValueProcedure() {
@Override
public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
return allocRegister(instruction, value, mode, flags);
}
};
private final InstructionValueProcedure allocStackOrRegisterProcedure = new InstructionValueProcedure() {
@Override
public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> 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<OperandFlag> 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<OperandFlag> 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 <em>not</em> 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);
}
}
}
}