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;
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;
private final BitSet[] liveIns;
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();
}
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);
}
@SuppressWarnings("try")
private void computeLiveness() {
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));
}
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);
}
}
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));
}
}
}
}
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();
AbstractBlockBase<?> startBlock = getLIR().getControlFlowGraph().getStartBlock();
if (getLiveIn(startBlock).cardinality() != 0) {
throw new GraalError("liveIn set of first block must be empty: " + getLiveIn(startBlock));
}
}
@SuppressWarnings("try")
public void finish() {
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) {
assert getLiveIn(block).isEmpty() : "liveIn for start block is not empty? " + getLiveIn(block);
liveInArray = livenessInfoBuilder.emptySet;
} else {
BitSet predLiveOut = getLiveOut(block.getPredecessors()[0]);
liveInArray = predLiveOut.isEmpty() ? livenessInfoBuilder.emptySet : bitSetToIntArray(predLiveOut);
}
livenessInfoBuilder.setIncoming(block, liveInArray);
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);
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);
}
}
}