blob: 73b9f1d2c83ad2865ebb98283aba475afe880aed [file] [log] [blame]
/*
* Copyright (c) 2009, 2015, 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.java;
import static org.graalvm.compiler.bytecode.Bytecodes.ALOAD;
import static org.graalvm.compiler.bytecode.Bytecodes.ALOAD_0;
import static org.graalvm.compiler.bytecode.Bytecodes.ALOAD_1;
import static org.graalvm.compiler.bytecode.Bytecodes.ALOAD_2;
import static org.graalvm.compiler.bytecode.Bytecodes.ALOAD_3;
import static org.graalvm.compiler.bytecode.Bytecodes.ASTORE;
import static org.graalvm.compiler.bytecode.Bytecodes.ASTORE_0;
import static org.graalvm.compiler.bytecode.Bytecodes.ASTORE_1;
import static org.graalvm.compiler.bytecode.Bytecodes.ASTORE_2;
import static org.graalvm.compiler.bytecode.Bytecodes.ASTORE_3;
import static org.graalvm.compiler.bytecode.Bytecodes.DLOAD;
import static org.graalvm.compiler.bytecode.Bytecodes.DLOAD_0;
import static org.graalvm.compiler.bytecode.Bytecodes.DLOAD_1;
import static org.graalvm.compiler.bytecode.Bytecodes.DLOAD_2;
import static org.graalvm.compiler.bytecode.Bytecodes.DLOAD_3;
import static org.graalvm.compiler.bytecode.Bytecodes.DSTORE;
import static org.graalvm.compiler.bytecode.Bytecodes.DSTORE_0;
import static org.graalvm.compiler.bytecode.Bytecodes.DSTORE_1;
import static org.graalvm.compiler.bytecode.Bytecodes.DSTORE_2;
import static org.graalvm.compiler.bytecode.Bytecodes.DSTORE_3;
import static org.graalvm.compiler.bytecode.Bytecodes.FLOAD;
import static org.graalvm.compiler.bytecode.Bytecodes.FLOAD_0;
import static org.graalvm.compiler.bytecode.Bytecodes.FLOAD_1;
import static org.graalvm.compiler.bytecode.Bytecodes.FLOAD_2;
import static org.graalvm.compiler.bytecode.Bytecodes.FLOAD_3;
import static org.graalvm.compiler.bytecode.Bytecodes.FSTORE;
import static org.graalvm.compiler.bytecode.Bytecodes.FSTORE_0;
import static org.graalvm.compiler.bytecode.Bytecodes.FSTORE_1;
import static org.graalvm.compiler.bytecode.Bytecodes.FSTORE_2;
import static org.graalvm.compiler.bytecode.Bytecodes.FSTORE_3;
import static org.graalvm.compiler.bytecode.Bytecodes.IINC;
import static org.graalvm.compiler.bytecode.Bytecodes.ILOAD;
import static org.graalvm.compiler.bytecode.Bytecodes.ILOAD_0;
import static org.graalvm.compiler.bytecode.Bytecodes.ILOAD_1;
import static org.graalvm.compiler.bytecode.Bytecodes.ILOAD_2;
import static org.graalvm.compiler.bytecode.Bytecodes.ILOAD_3;
import static org.graalvm.compiler.bytecode.Bytecodes.ISTORE;
import static org.graalvm.compiler.bytecode.Bytecodes.ISTORE_0;
import static org.graalvm.compiler.bytecode.Bytecodes.ISTORE_1;
import static org.graalvm.compiler.bytecode.Bytecodes.ISTORE_2;
import static org.graalvm.compiler.bytecode.Bytecodes.ISTORE_3;
import static org.graalvm.compiler.bytecode.Bytecodes.LLOAD;
import static org.graalvm.compiler.bytecode.Bytecodes.LLOAD_0;
import static org.graalvm.compiler.bytecode.Bytecodes.LLOAD_1;
import static org.graalvm.compiler.bytecode.Bytecodes.LLOAD_2;
import static org.graalvm.compiler.bytecode.Bytecodes.LLOAD_3;
import static org.graalvm.compiler.bytecode.Bytecodes.LSTORE;
import static org.graalvm.compiler.bytecode.Bytecodes.LSTORE_0;
import static org.graalvm.compiler.bytecode.Bytecodes.LSTORE_1;
import static org.graalvm.compiler.bytecode.Bytecodes.LSTORE_2;
import static org.graalvm.compiler.bytecode.Bytecodes.LSTORE_3;
import static org.graalvm.compiler.bytecode.Bytecodes.RET;
import org.graalvm.compiler.bytecode.BytecodeStream;
import org.graalvm.compiler.debug.Debug;
import org.graalvm.compiler.java.BciBlockMapping.BciBlock;
/**
* Encapsulates the liveness calculation, so that subclasses for locals ≤ 64 and locals > 64
* can be implemented.
*/
public abstract class LocalLiveness {
protected final BciBlock[] blocks;
public static LocalLiveness compute(BytecodeStream stream, BciBlock[] blocks, int maxLocals, int loopCount) {
LocalLiveness liveness = maxLocals <= 64 ? new SmallLocalLiveness(blocks, maxLocals, loopCount) : new LargeLocalLiveness(blocks, maxLocals, loopCount);
liveness.computeLiveness(stream);
return liveness;
}
protected LocalLiveness(BciBlock[] blocks) {
this.blocks = blocks;
}
void computeLiveness(BytecodeStream stream) {
for (BciBlock block : blocks) {
computeLocalLiveness(stream, block);
}
boolean changed;
int iteration = 0;
do {
assert traceIteration(iteration);
changed = false;
for (int i = blocks.length - 1; i >= 0; i--) {
BciBlock block = blocks[i];
int blockID = block.getId();
assert traceStart(block, blockID);
boolean blockChanged = (iteration == 0);
if (block.getSuccessorCount() > 0) {
int oldCardinality = liveOutCardinality(blockID);
for (BciBlock sux : block.getSuccessors()) {
assert traceSuccessor(sux);
propagateLiveness(blockID, sux.getId());
}
blockChanged |= (oldCardinality != liveOutCardinality(blockID));
}
if (blockChanged) {
updateLiveness(blockID);
assert traceEnd(block, blockID);
}
changed |= blockChanged;
}
iteration++;
} while (changed);
}
private static boolean traceIteration(int iteration) {
Debug.log("Iteration %d", iteration);
return true;
}
private boolean traceEnd(BciBlock block, int blockID) {
if (Debug.isLogEnabled()) {
Debug.logv(" end B%d [%d, %d] in: %s out: %s gen: %s kill: %s", block.getId(), block.startBci, block.endBci, debugLiveIn(blockID), debugLiveOut(blockID), debugLiveGen(blockID),
debugLiveKill(blockID));
}
return true;
}
private boolean traceSuccessor(BciBlock sux) {
if (Debug.isLogEnabled()) {
Debug.log(" Successor B%d: %s", sux.getId(), debugLiveIn(sux.getId()));
}
return true;
}
private boolean traceStart(BciBlock block, int blockID) {
if (Debug.isLogEnabled()) {
Debug.logv(" start B%d [%d, %d] in: %s out: %s gen: %s kill: %s", block.getId(), block.startBci, block.endBci, debugLiveIn(blockID), debugLiveOut(blockID), debugLiveGen(blockID),
debugLiveKill(blockID));
}
return true;
}
/**
* Returns whether the local is live at the beginning of the given block.
*/
public abstract boolean localIsLiveIn(BciBlock block, int local);
/**
* Returns whether the local is set in the given loop.
*/
public abstract boolean localIsChangedInLoop(int loopId, int local);
/**
* Returns whether the local is live at the end of the given block.
*/
public abstract boolean localIsLiveOut(BciBlock block, int local);
/**
* Returns a string representation of the liveIn values of the given block.
*/
protected abstract String debugLiveIn(int blockID);
/**
* Returns a string representation of the liveOut values of the given block.
*/
protected abstract String debugLiveOut(int blockID);
/**
* Returns a string representation of the liveGen values of the given block.
*/
protected abstract String debugLiveGen(int blockID);
/**
* Returns a string representation of the liveKill values of the given block.
*/
protected abstract String debugLiveKill(int blockID);
/**
* Returns the number of live locals at the end of the given block.
*/
protected abstract int liveOutCardinality(int blockID);
/**
* Adds all locals the are in the liveIn of the successor to the liveOut of the block.
*/
protected abstract void propagateLiveness(int blockID, int successorID);
/**
* Calculates a new liveIn for the given block from liveOut, liveKill and liveGen.
*/
protected abstract void updateLiveness(int blockID);
/**
* Adds the local to liveGen if it wasn't already killed in this block.
*/
protected abstract void loadOne(int blockID, int local);
/**
* Add this local to liveKill if it wasn't already generated in this block.
*/
protected abstract void storeOne(int blockID, int local);
private void computeLocalLiveness(BytecodeStream stream, BciBlock block) {
if (block.startBci < 0 || block.endBci < 0) {
return;
}
int blockID = block.getId();
int localIndex;
stream.setBCI(block.startBci);
while (stream.currentBCI() <= block.endBci) {
switch (stream.currentBC()) {
case LLOAD:
case DLOAD:
loadTwo(blockID, stream.readLocalIndex());
break;
case LLOAD_0:
case DLOAD_0:
loadTwo(blockID, 0);
break;
case LLOAD_1:
case DLOAD_1:
loadTwo(blockID, 1);
break;
case LLOAD_2:
case DLOAD_2:
loadTwo(blockID, 2);
break;
case LLOAD_3:
case DLOAD_3:
loadTwo(blockID, 3);
break;
case IINC:
localIndex = stream.readLocalIndex();
loadOne(blockID, localIndex);
storeOne(blockID, localIndex);
break;
case ILOAD:
case FLOAD:
case ALOAD:
case RET:
loadOne(blockID, stream.readLocalIndex());
break;
case ILOAD_0:
case FLOAD_0:
case ALOAD_0:
loadOne(blockID, 0);
break;
case ILOAD_1:
case FLOAD_1:
case ALOAD_1:
loadOne(blockID, 1);
break;
case ILOAD_2:
case FLOAD_2:
case ALOAD_2:
loadOne(blockID, 2);
break;
case ILOAD_3:
case FLOAD_3:
case ALOAD_3:
loadOne(blockID, 3);
break;
case LSTORE:
case DSTORE:
storeTwo(blockID, stream.readLocalIndex());
break;
case LSTORE_0:
case DSTORE_0:
storeTwo(blockID, 0);
break;
case LSTORE_1:
case DSTORE_1:
storeTwo(blockID, 1);
break;
case LSTORE_2:
case DSTORE_2:
storeTwo(blockID, 2);
break;
case LSTORE_3:
case DSTORE_3:
storeTwo(blockID, 3);
break;
case ISTORE:
case FSTORE:
case ASTORE:
storeOne(blockID, stream.readLocalIndex());
break;
case ISTORE_0:
case FSTORE_0:
case ASTORE_0:
storeOne(blockID, 0);
break;
case ISTORE_1:
case FSTORE_1:
case ASTORE_1:
storeOne(blockID, 1);
break;
case ISTORE_2:
case FSTORE_2:
case ASTORE_2:
storeOne(blockID, 2);
break;
case ISTORE_3:
case FSTORE_3:
case ASTORE_3:
storeOne(blockID, 3);
break;
}
stream.next();
}
}
private void loadTwo(int blockID, int local) {
loadOne(blockID, local);
loadOne(blockID, local + 1);
}
private void storeTwo(int blockID, int local) {
storeOne(blockID, local);
storeOne(blockID, local + 1);
}
}