/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.bytecode;
import java.util.TreeMap;
import org.jnode.bootlog.BootLogInstance;
import org.jnode.vm.JvmType;
import org.jnode.vm.classmgr.VmByteCode;
import org.jnode.vm.classmgr.VmInterpretedExceptionHandler;
import org.jnode.vm.classmgr.VmMethod;
/**
* Bytecode visitor, used to determine the start addresses of basic blocks.
*
* @author Levente S\u00e1ntha
*/
public class DeadBlockFinder extends BytecodeVisitorSupport implements BytecodeFlags {
private static final boolean debug = false;
private final TreeMap<Integer, BasicBlock> blocks = new TreeMap<Integer, BasicBlock>();
private byte[] opcodeFlags;
private boolean nextIsStartOfBB;
private boolean nextFollowsTypeStack;
private boolean nextIsRetTarget;
private int curAddress;
private BasicBlock current;
/**
* Create all determined basic blocks
*
* @return the basic blocks.
*/
public BasicBlock[] createBasicBlocks() {
// Create the array
final BasicBlock[] list = blocks.values().toArray(new BasicBlock[blocks.size()]);
// Set the EndPC's and flags
final byte[] opcodeFlags = this.opcodeFlags;
final int len = opcodeFlags.length;
int bbIndex = 0;
for (int i = 0; i < len; i++) {
if (isStartOfBB(i)) {
final int start = i;
// Find the end of the BB
i++;
while ((i < len) && (!isStartOfBB(i))) {
i++;
}
// the BB
final BasicBlock bb = list[bbIndex++];
if (bb.getStartPC() != start) {
throw new AssertionError("bb.getStartPC() != start");
}
bb.setEndPC(i);
bb.setStartOfExceptionHandler(isStartOfException(start));
i--;
}
}
if (bbIndex != list.length) {
throw new AssertionError("bbIndex != list.length");
}
return list;
}
/**
* Get the per-opcode bytecode flags.
*
* @return byte[]
*/
public final byte[] getOpcodeFlags() {
return opcodeFlags;
}
/**
* @param method
* @see BytecodeVisitor#startMethod(org.jnode.vm.classmgr.VmMethod)
*/
public void startMethod(VmMethod method) {
final VmByteCode bc = method.getBytecode();
final int length = bc.getLength();
opcodeFlags = new byte[length];
// The first instruction is always the start of a BB.
startBB(0, true);
// The exception handler also start a basic block
final TypeStack ehTStack = new TypeStack();
ehTStack.push(JvmType.REFERENCE);
for (int i = 0; i < bc.getNoExceptionHandlers(); i++) {
VmInterpretedExceptionHandler eh = bc.getExceptionHandler(i);
startTryBlock(eh.getStartPC());
startTryBlockEnd(eh.getEndPC());
startException(eh.getHandlerPC(), ehTStack);
}
}
/**
* @param address
* @see BytecodeVisitor#startInstruction(int)
*/
public void startInstruction(int address) {
if (debug) {
BootLogInstance.get().debug("#" + address);
}
curAddress = address;
super.startInstruction(address);
opcodeFlags[address] |= F_START_OF_INSTRUCTION;
boolean next_is_rt = nextIsRetTarget;
if (nextIsRetTarget) {
opcodeFlags[address] |= F_RET_TARGET;
nextIsRetTarget = false;
}
boolean next_is_bb = nextIsStartOfBB;
if (nextIsStartOfBB) {
if (debug) BootLogInstance.get().debug("\tnextIsStartOfBB\t" + nextFollowsTypeStack);
startBB(address, nextFollowsTypeStack);
nextIsStartOfBB = false;
nextFollowsTypeStack = true;
}
if (isStartOfBB(address)) {
if (!next_is_bb) {
BasicBlock bb = blocks.get(address);
bb.addEntryBlock(current);
current = bb;
} else {
current = blocks.get(address);
}
if (next_is_rt) {
current.setRetTarget(true);
}
if (debug) BootLogInstance.get().debug("\tcurrent\t" + current);
}
if (debug) {
BootLogInstance.get().debug("#" + address);
}
}
/**
* Mark the start of a basic block
*
* @param address
*/
private void startBB(int address, boolean setTypeStack) {
if ((opcodeFlags[address] & F_START_OF_BASICBLOCK) == 0) {
opcodeFlags[address] |= F_START_OF_BASICBLOCK;
final BasicBlock bb = new BasicBlock(address);
blocks.put(address, bb);
if (setTypeStack) {
bb.addEntryBlock(current);
}
} else if (setTypeStack) {
final BasicBlock bb = blocks.get(address);
// Add entry block
bb.addEntryBlock(current);
}
}
/**
* Mark the end of a basic block
*/
private void endBB(boolean nextFollowsTypeStack) {
this.nextIsStartOfBB = true;
this.nextFollowsTypeStack = nextFollowsTypeStack;
}
/**
* @param address
* @see BytecodeVisitor#visit_ifeq(int)
*/
public void visit_ifeq(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_ifne(int)
*/
public void visit_ifne(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_iflt(int)
*/
public void visit_iflt(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_ifge(int)
*/
public void visit_ifge(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_ifgt(int)
*/
public void visit_ifgt(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_ifle(int)
*/
public void visit_ifle(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_if_icmpeq(int)
*/
public void visit_if_icmpeq(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_if_icmpne(int)
*/
public void visit_if_icmpne(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_if_icmplt(int)
*/
public void visit_if_icmplt(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_if_icmpge(int)
*/
public void visit_if_icmpge(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_if_icmpgt(int)
*/
public void visit_if_icmpgt(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_if_icmple(int)
*/
public void visit_if_icmple(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_if_acmpeq(int)
*/
public void visit_if_acmpeq(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_if_acmpne(int)
*/
public void visit_if_acmpne(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_goto(int)
*/
public void visit_goto(int address) {
// No change
addBranch(address, false);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_jsr(int)
*/
public void visit_jsr(int address) {
addBranch(address, false);
nextIsRetTarget = true;
condYieldPoint(address);
}
/**
* @param defValue
* @param lowValue
* @param highValue
* @param addresses
* @see BytecodeVisitor#visit_tableswitch(int, int, int, int[])
*/
public void visit_tableswitch(int defValue, int lowValue, int highValue, int[] addresses) {
for (int address : addresses) {
addBranch(address, true);
condYieldPoint(address);
}
addBranch(defValue, false);
condYieldPoint(defValue);
}
/**
* @param defValue
* @param matchValues
* @param addresses
* @see BytecodeVisitor#visit_lookupswitch(int, int[], int[])
*/
public void visit_lookupswitch(int defValue, int[] matchValues, int[] addresses) {
for (int address : addresses) {
addBranch(address, true);
condYieldPoint(address);
}
addBranch(defValue, false);
condYieldPoint(defValue);
}
/**
* @param address
* @see BytecodeVisitor#visit_ifnull(int)
*/
public void visit_ifnull(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @param address
* @see BytecodeVisitor#visit_ifnonnull(int)
*/
public void visit_ifnonnull(int address) {
addBranch(address, true);
condYieldPoint(address);
}
/**
* @see BytecodeVisitor#visit_athrow()
*/
public void visit_athrow() {
endBB(false);
// Reference is actually pushed on the stack, but that is handled
// by the startException blocks.
}
/**
* @see BytecodeVisitor#visit_areturn()
*/
public void visit_areturn() {
endBB(false);
}
/**
* @see BytecodeVisitor#visit_dreturn()
*/
public void visit_dreturn() {
endBB(false);
}
/**
* @see BytecodeVisitor#visit_freturn()
*/
public void visit_freturn() {
endBB(false);
}
/**
* @see BytecodeVisitor#visit_ireturn()
*/
public void visit_ireturn() {
if (debug) {
BootLogInstance.get().debug("ireturn at " + curAddress);
}
endBB(false);
}
/**
* @see BytecodeVisitor#visit_lreturn()
*/
public void visit_lreturn() {
endBB(false);
}
/**
* @param index
* @see BytecodeVisitor#visit_ret(int)
*/
public void visit_ret(int index) {
// No change
endBB(false);
}
/**
* @see BytecodeVisitor#visit_return()
*/
public void visit_return() {
// No change
endBB(false);
}
/**
* Add branching information (to the given target) to the basic blocks information.
*
* @param target
*/
private void addBranch(int target, boolean conditional) {
startBB(target, true);
endBB(conditional);
}
private boolean isStartOfBB(int address) {
return ((opcodeFlags[address] & F_START_OF_BASICBLOCK) != 0);
}
private boolean isStartOfException(int address) {
return ((opcodeFlags[address] & F_START_OF_EXCEPTIONHANDLER) != 0);
}
/**
* Mark the start of a exception handler
*
* @param address
*/
private void startException(int address, TypeStack tstack) {
opcodeFlags[address] |= F_START_OF_EXCEPTIONHANDLER;
//System.out.println("startException: " + tstack);
startBB(address, true);
}
/**
* Mark the start of a try-catch block
*
* @param address
*/
private void startTryBlock(int address) {
opcodeFlags[address] |= F_START_OF_TRYBLOCK;
//startBB(address, false, null);
}
/**
* Mark the end of a try-catch block
*
* @param address
*/
private void startTryBlockEnd(int address) {
opcodeFlags[address] |= F_START_OF_TRYBLOCKEND;
//startBB(address, false, null);
}
/**
* Mark a conditional yieldpoint.
*/
private void condYieldPoint(int target) {
if (target < curAddress) {
opcodeFlags[curAddress] |= F_YIELDPOINT;
}
}
}