blob: 017ad9295fb052fae2e532b4b6b805bb811432f6 [file] [log] [blame]
/*
* Copyright (c) 2016, 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;
import static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.EnumSet;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.core.common.cfg.Loop;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.debug.Indent;
import org.graalvm.compiler.lir.InstructionValueConsumer;
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.Variable;
import org.graalvm.compiler.lir.gen.LIRGenerationResult;
import org.graalvm.compiler.lir.phases.AllocationPhase;
import org.graalvm.compiler.lir.ssa.SSAUtil;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.meta.Value;
/**
* Constructs {@link GlobalLivenessInfo global liveness information}.
*/
public final class GlobalLivenessAnalysisPhase extends AllocationPhase {
@Override
protected void run(TargetDescription target, LIRGenerationResult lirGenRes, AllocationContext context) {
assert SSAUtil.verifySSAForm(lirGenRes.getLIR());
Analyser ssiBuilder = new Analyser(lirGenRes.getLIR());
ssiBuilder.build();
ssiBuilder.finish();
GlobalLivenessInfo livenessInfo = ssiBuilder.getLivenessInfo();
assert livenessInfo.verify(lirGenRes.getLIR());
context.contextAdd(livenessInfo);
}
private final class Analyser {
private static final int LOG_LEVEL = DebugContext.INFO_LEVEL;
/**
* Bit map specifying which operands are live upon entry to this block. These are values
* used in this block or any of its successors where such value are not defined in this
* block. The bit index of an operand is its {@linkplain #operandNumber operand number}.
*/
private final BitSet[] liveIns;
/**
* Bit map specifying which operands are live upon exit from this block. These are values
* used in a successor block that are either defined in this block or were live upon entry
* to this block. The bit index of an operand is its {@linkplain #operandNumber operand
* number}.
*/
private final BitSet[] liveOuts;
private final AbstractBlockBase<?>[] blocks;
private final Value[] operands;
private final LIR lir;
private final GlobalLivenessInfo.Builder livenessInfoBuilder;
Analyser(LIR lir) {
int numBlocks = lir.getControlFlowGraph().getBlocks().length;
this.liveIns = new BitSet[numBlocks];
this.liveOuts = new BitSet[numBlocks];
this.blocks = lir.getControlFlowGraph().getBlocks();
this.lir = lir;
this.operands = new Value[lir.numVariables()];
this.livenessInfoBuilder = new GlobalLivenessInfo.Builder(lir);
}
private BitSet getLiveIn(final AbstractBlockBase<?> block) {
return liveIns[block.getId()];
}
private BitSet getLiveOut(final AbstractBlockBase<?> block) {
return liveOuts[block.getId()];
}
private void setLiveIn(final AbstractBlockBase<?> block, final BitSet liveIn) {
liveIns[block.getId()] = liveIn;
}
private void setLiveOut(final AbstractBlockBase<?> block, final BitSet liveOut) {
liveOuts[block.getId()] = liveOut;
}
private void buildIntern() {
computeLiveness();
}
/**
* Gets the size of the {@link #liveIns} and {@link #liveOuts} sets for a basic block.
*/
private int liveSetSize() {
return lir.numVariables();
}
private int operandNumber(Value operand) {
if (isVariable(operand)) {
return asVariable(operand).index;
}
throw GraalError.shouldNotReachHere("Can only handle Variables: " + operand);
}
/**
* Computes live sets for each block.
*/
@SuppressWarnings("try")
private void computeLiveness() {
// iterate all blocks
DebugContext debug = lir.getDebug();
for (int i = blocks.length - 1; i >= 0; i--) {
final AbstractBlockBase<?> block = blocks[i];
try (Indent indent = debug.logAndIndent(LOG_LEVEL, "compute local live sets for block %s", block)) {
final BitSet liveIn = mergeLiveSets(block);
setLiveOut(block, (BitSet) liveIn.clone());
InstructionValueConsumer useConsumer = new InstructionValueConsumer() {
@Override
public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
processUse(liveIn, operand);
}
};
InstructionValueConsumer defConsumer = new InstructionValueConsumer() {
@Override
public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
processDef(liveIn, op, operand);
}
};
if (debug.isLogEnabled()) {
debug.log(LOG_LEVEL, "liveOut B%d %s", block.getId(), getLiveOut(block));
}
// iterate all instructions of the block
ArrayList<LIRInstruction> instructions = getLIR().getLIRforBlock(block);
for (int j = instructions.size() - 1; j >= 0; j--) {
final LIRInstruction op = instructions.get(j);
try (Indent indent2 = debug.logAndIndent(LOG_LEVEL, "handle op %d: %s", op.id(), op)) {
op.visitEachOutput(defConsumer);
op.visitEachTemp(defConsumer);
op.visitEachState(useConsumer);
op.visitEachAlive(useConsumer);
op.visitEachInput(useConsumer);
}
} // end of instruction iteration
setLiveIn(block, liveIn);
if (block.isLoopHeader()) {
handleLoopHeader(block.getLoop(), liveIn);
}
if (debug.isLogEnabled()) {
debug.log(LOG_LEVEL, "liveIn B%d %s", block.getId(), getLiveIn(block));
}
}
} // end of block iteration
}
/**
* All variables live at the beginning of a loop are live throughout the loop.
*/
private void handleLoopHeader(Loop<?> loop, BitSet live) {
for (AbstractBlockBase<?> block : loop.getBlocks()) {
getLiveIn(block).or(live);
getLiveOut(block).or(live);
}
}
private BitSet mergeLiveSets(final AbstractBlockBase<?> block) {
assert block != null;
final BitSet liveOut = new BitSet(liveSetSize());
for (AbstractBlockBase<?> successor : block.getSuccessors()) {
BitSet succLiveIn = getLiveIn(successor);
if (succLiveIn != null) {
liveOut.or(succLiveIn);
} else {
assert successor.isLoopHeader() : "Successor of " + block + " not yet processed and not loop header: " + successor;
}
}
return liveOut;
}
private void processUse(final BitSet liveGen, Value operand) {
if (isVariable(operand)) {
int operandNum = operandNumber(operand);
liveGen.set(operandNum);
DebugContext debug = lir.getDebug();
if (debug.isLogEnabled()) {
debug.log(LOG_LEVEL, "liveGen for operand %d(%s)", operandNum, operand);
}
}
}
private void processDef(final BitSet liveGen, LIRInstruction op, Value operand) {
if (isVariable(operand)) {
recordVariable(op, asVariable(operand));
int operandNum = operandNumber(operand);
if (operands[operandNum] == null) {
operands[operandNum] = operand;
}
liveGen.clear(operandNum);
DebugContext debug = lir.getDebug();
if (debug.isLogEnabled()) {
debug.log(LOG_LEVEL, "liveKill for operand %d(%s)", operandNum, operand);
}
}
}
private LIR getLIR() {
return lir;
}
public void build() {
buildIntern();
// check that the liveIn set of the first block is empty
AbstractBlockBase<?> startBlock = getLIR().getControlFlowGraph().getStartBlock();
if (getLiveIn(startBlock).cardinality() != 0) {
// bailout if this occurs in product mode.
throw new GraalError("liveIn set of first block must be empty: " + getLiveIn(startBlock));
}
}
@SuppressWarnings("try")
public void finish() {
// iterate all blocks in reverse order
for (AbstractBlockBase<?> block : (AbstractBlockBase<?>[]) lir.getControlFlowGraph().getBlocks()) {
try (Indent indent = lir.getDebug().logAndIndent(LOG_LEVEL, "Finish Block %s", block)) {
buildIncoming(block);
buildOutgoing(block);
}
}
}
public GlobalLivenessInfo getLivenessInfo() {
assert livenessInfoBuilder != null : "No liveness info collected";
return livenessInfoBuilder.createLivenessInfo();
}
private void buildIncoming(AbstractBlockBase<?> block) {
if (!GlobalLivenessInfo.storesIncoming(block)) {
assert block.getPredecessorCount() == 1;
assert GlobalLivenessInfo.storesOutgoing(block.getPredecessors()[0]) : "No incoming liveness info: " + block;
return;
}
final int[] liveInArray;
if (block.getPredecessorCount() == 0) {
// start block
assert getLiveIn(block).isEmpty() : "liveIn for start block is not empty? " + getLiveIn(block);
liveInArray = livenessInfoBuilder.emptySet;
} else {
/*
* Collect live out of predecessors since there might be values not used in this
* block which might cause out/in mismatch. Per construction the live sets of all
* predecessors are equal.
*/
BitSet predLiveOut = getLiveOut(block.getPredecessors()[0]);
liveInArray = predLiveOut.isEmpty() ? livenessInfoBuilder.emptySet : bitSetToIntArray(predLiveOut);
}
livenessInfoBuilder.setIncoming(block, liveInArray);
// reuse the same array for outgoing variables in predecessors
for (AbstractBlockBase<?> pred : block.getPredecessors()) {
livenessInfoBuilder.setOutgoing(pred, liveInArray);
}
}
private void buildOutgoing(AbstractBlockBase<?> block) {
BitSet liveOut = getLiveOut(block);
if (!GlobalLivenessInfo.storesOutgoing(block)) {
assert GlobalLivenessInfo.storesOutgoing(block) || block.getSuccessorCount() == 1;
assert GlobalLivenessInfo.storesOutgoing(block) || GlobalLivenessInfo.storesIncoming(block.getSuccessors()[0]) : "No outgoing liveness info: " + block;
return;
}
int[] liveOutArray = liveOut.isEmpty() ? livenessInfoBuilder.emptySet : bitSetToIntArray(liveOut);
livenessInfoBuilder.setOutgoing(block, liveOutArray);
// reuse the same array for incoming variables in successors
for (AbstractBlockBase<?> succ : block.getSuccessors()) {
livenessInfoBuilder.setIncoming(succ, liveOutArray);
}
}
private int[] bitSetToIntArray(BitSet live) {
int[] vars = new int[live.cardinality()];
int cnt = 0;
for (int i = live.nextSetBit(0); i >= 0; i = live.nextSetBit(i + 1), cnt++) {
vars[cnt] = i;
}
return vars;
}
private void recordVariable(LIRInstruction op, Variable var) {
livenessInfoBuilder.addVariable(op, var);
}
}
}