blob: 3c5a975ac04e778bf0fd5ef00d9085732f3c5c58 [file] [log] [blame]
/*
* Copyright (c) 2015, 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.stackslotalloc;
import static org.graalvm.compiler.lir.LIRValueUtil.asVirtualStackSlot;
import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Deque;
import java.util.EnumSet;
import jdk.internal.vm.compiler.collections.EconomicSet;
import jdk.internal.vm.compiler.collections.Equivalence;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.core.common.cfg.BlockMap;
import org.graalvm.compiler.debug.CounterKey;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.Indent;
import org.graalvm.compiler.lir.InstructionValueConsumer;
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.VirtualStackSlot;
import jdk.vm.ci.meta.Value;
/**
* Calculates the stack intervals using a worklist-based backwards data-flow analysis.
*/
final class FixPointIntervalBuilder {
private final BlockMap<BitSet> liveInMap;
private final BlockMap<BitSet> liveOutMap;
private final LIR lir;
private final int maxOpId;
private final StackInterval[] stackSlotMap;
private final EconomicSet<LIRInstruction> usePos;
/**
* The number of allocated stack slots.
*/
private static final CounterKey uninitializedSlots = DebugContext.counter("StackSlotAllocator[uninitializedSlots]");
FixPointIntervalBuilder(LIR lir, StackInterval[] stackSlotMap, int maxOpId) {
this.lir = lir;
this.stackSlotMap = stackSlotMap;
this.maxOpId = maxOpId;
liveInMap = new BlockMap<>(lir.getControlFlowGraph());
liveOutMap = new BlockMap<>(lir.getControlFlowGraph());
this.usePos = EconomicSet.create(Equivalence.IDENTITY);
}
/**
* Builds the lifetime intervals for {@link VirtualStackSlot virtual stack slots}, sets up
* {@link #stackSlotMap} and returns a set of use positions, i.e. instructions that contain
* virtual stack slots.
*/
EconomicSet<LIRInstruction> build() {
Deque<AbstractBlockBase<?>> worklist = new ArrayDeque<>();
AbstractBlockBase<?>[] blocks = lir.getControlFlowGraph().getBlocks();
for (int i = blocks.length - 1; i >= 0; i--) {
worklist.add(blocks[i]);
}
for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) {
liveInMap.put(block, new BitSet(stackSlotMap.length));
}
while (!worklist.isEmpty()) {
AbstractBlockBase<?> block = worklist.poll();
processBlock(block, worklist);
}
return usePos;
}
/**
* Merge outSet with in-set of successors.
*/
private boolean updateOutBlock(AbstractBlockBase<?> block) {
BitSet union = new BitSet(stackSlotMap.length);
for (AbstractBlockBase<?> succ : block.getSuccessors()) {
union.or(liveInMap.get(succ));
}
BitSet outSet = liveOutMap.get(block);
// check if changed
if (outSet == null || !union.equals(outSet)) {
liveOutMap.put(block, union);
return true;
}
return false;
}
@SuppressWarnings("try")
private void processBlock(AbstractBlockBase<?> block, Deque<AbstractBlockBase<?>> worklist) {
DebugContext debug = lir.getDebug();
if (updateOutBlock(block)) {
try (Indent indent = debug.logAndIndent("handle block %s", block)) {
ArrayList<LIRInstruction> instructions = lir.getLIRforBlock(block);
// get out set and mark intervals
BitSet outSet = liveOutMap.get(block);
markOutInterval(outSet, getBlockEnd(instructions));
printLiveSet("liveOut", outSet);
// process instructions
BlockClosure closure = new BlockClosure((BitSet) outSet.clone());
for (int i = instructions.size() - 1; i >= 0; i--) {
LIRInstruction inst = instructions.get(i);
closure.processInstructionBottomUp(inst);
}
// add predecessors to work list
for (AbstractBlockBase<?> b : block.getPredecessors()) {
worklist.add(b);
}
// set in set and mark intervals
BitSet inSet = closure.getCurrentSet();
liveInMap.put(block, inSet);
markInInterval(inSet, getBlockBegin(instructions));
printLiveSet("liveIn", inSet);
}
}
}
@SuppressWarnings("try")
private void printLiveSet(String label, BitSet liveSet) {
DebugContext debug = lir.getDebug();
if (debug.isLogEnabled()) {
try (Indent indent = debug.logAndIndent(label)) {
debug.log("%s", liveSetToString(liveSet));
}
}
}
private String liveSetToString(BitSet liveSet) {
StringBuilder sb = new StringBuilder();
for (int i = liveSet.nextSetBit(0); i >= 0; i = liveSet.nextSetBit(i + 1)) {
StackInterval interval = getIntervalFromStackId(i);
sb.append(interval.getOperand()).append(" ");
}
return sb.toString();
}
private void markOutInterval(BitSet outSet, int blockEndOpId) {
DebugContext debug = lir.getDebug();
for (int i = outSet.nextSetBit(0); i >= 0; i = outSet.nextSetBit(i + 1)) {
StackInterval interval = getIntervalFromStackId(i);
debug.log("mark live operand: %s", interval.getOperand());
interval.addTo(blockEndOpId);
}
}
private void markInInterval(BitSet inSet, int blockFirstOpId) {
DebugContext debug = lir.getDebug();
for (int i = inSet.nextSetBit(0); i >= 0; i = inSet.nextSetBit(i + 1)) {
StackInterval interval = getIntervalFromStackId(i);
debug.log("mark live operand: %s", interval.getOperand());
interval.addFrom(blockFirstOpId);
}
}
private final class BlockClosure {
private final BitSet currentSet;
private BlockClosure(BitSet set) {
currentSet = set;
}
private BitSet getCurrentSet() {
return currentSet;
}
/**
* Process all values of an instruction bottom-up, i.e. definitions before usages. Values
* that start or end at the current operation are not included.
*/
@SuppressWarnings("try")
private void processInstructionBottomUp(LIRInstruction op) {
DebugContext debug = lir.getDebug();
try (Indent indent = debug.logAndIndent("handle op %d, %s", op.id(), op)) {
// kills
op.visitEachTemp(defConsumer);
op.visitEachOutput(defConsumer);
// gen - values that are considered alive for this state
op.visitEachAlive(useConsumer);
op.visitEachState(useConsumer);
// mark locations
// gen
op.visitEachInput(useConsumer);
}
}
InstructionValueConsumer useConsumer = new InstructionValueConsumer() {
@Override
public void visitValue(LIRInstruction inst, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
if (isVirtualStackSlot(operand)) {
DebugContext debug = lir.getDebug();
VirtualStackSlot vslot = asVirtualStackSlot(operand);
addUse(vslot, inst, flags);
addRegisterHint(inst, vslot, mode, flags, false);
usePos.add(inst);
debug.log("set operand: %s", operand);
currentSet.set(vslot.getId());
}
}
};
InstructionValueConsumer defConsumer = new InstructionValueConsumer() {
@Override
public void visitValue(LIRInstruction inst, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
if (isVirtualStackSlot(operand)) {
DebugContext debug = lir.getDebug();
VirtualStackSlot vslot = asVirtualStackSlot(operand);
addDef(vslot, inst);
addRegisterHint(inst, vslot, mode, flags, true);
usePos.add(inst);
debug.log("clear operand: %s", operand);
currentSet.clear(vslot.getId());
}
}
};
private void addUse(VirtualStackSlot stackSlot, LIRInstruction inst, EnumSet<OperandFlag> flags) {
StackInterval interval = getOrCreateInterval(stackSlot);
if (flags.contains(OperandFlag.UNINITIALIZED)) {
// Stack slot is marked uninitialized so we have to assume it is live all
// the time.
DebugContext debug = lir.getDebug();
if (debug.isCountEnabled() && !(interval.from() == 0 && interval.to() == maxOpId)) {
uninitializedSlots.increment(debug);
}
interval.addFrom(0);
interval.addTo(maxOpId);
} else {
interval.addTo(inst.id());
}
}
private void addDef(VirtualStackSlot stackSlot, LIRInstruction inst) {
StackInterval interval = getOrCreateInterval(stackSlot);
interval.addFrom(inst.id());
}
void addRegisterHint(final LIRInstruction op, VirtualStackSlot targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef) {
if (flags.contains(OperandFlag.HINT)) {
InstructionValueProcedure proc = new InstructionValueProcedure() {
@Override
public Value doValue(LIRInstruction instruction, Value registerHint, OperandMode vaueMode, EnumSet<OperandFlag> valueFlags) {
if (isVirtualStackSlot(registerHint)) {
StackInterval from = getOrCreateInterval((VirtualStackSlot) registerHint);
StackInterval to = getOrCreateInterval(targetValue);
// hints always point from def to use
if (hintAtDef) {
to.setLocationHint(from);
} else {
from.setLocationHint(to);
}
DebugContext debug = lir.getDebug();
if (debug.isLogEnabled()) {
debug.log("operation %s at opId %d: added hint from interval %s to %s", op, op.id(), from, to);
}
return registerHint;
}
return null;
}
};
op.forEachRegisterHint(targetValue, mode, proc);
}
}
}
private StackInterval get(VirtualStackSlot stackSlot) {
return stackSlotMap[stackSlot.getId()];
}
private void put(VirtualStackSlot stackSlot, StackInterval interval) {
stackSlotMap[stackSlot.getId()] = interval;
}
private StackInterval getOrCreateInterval(VirtualStackSlot stackSlot) {
StackInterval interval = get(stackSlot);
if (interval == null) {
interval = new StackInterval(stackSlot, stackSlot.getValueKind());
put(stackSlot, interval);
}
return interval;
}
private StackInterval getIntervalFromStackId(int id) {
return stackSlotMap[id];
}
private static int getBlockBegin(ArrayList<LIRInstruction> instructions) {
return instructions.get(0).id();
}
private static int getBlockEnd(ArrayList<LIRInstruction> instructions) {
return instructions.get(instructions.size() - 1).id() + 1;
}
}