/* * $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.compiler.ir; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.jnode.vm.bytecode.BytecodeFlags; import org.jnode.vm.bytecode.BytecodeVisitorSupport; import org.jnode.vm.classmgr.VmByteCode; import org.jnode.vm.classmgr.VmInterpretedExceptionHandler; import org.jnode.vm.classmgr.VmMethod; /** * @author Madhu Siddalingaiah */ public class IRBasicBlockFinder<T> extends BytecodeVisitorSupport implements Comparator<IRBasicBlock> { private boolean nextIsStartOfBB; private IRBasicBlock currentBlock; private byte[] opcodeFlags; private byte[] branchFlags; private final ArrayList<IRBasicBlock<T>> blocks = new ArrayList<IRBasicBlock<T>>(); private final List<int[]> branchTargets = new ArrayList<int[]>(); private VmByteCode byteCode; private static final byte CONDITIONAL_BRANCH = 1; private static final byte UNCONDITIONAL_BRANCH = 2; /** * Create all determined basic blocks * * @return the blocks. */ public IRBasicBlock<T>[] createBasicBlocks() { // Sort the blocks on start PC Collections.sort(blocks, this); // Create the array final IRBasicBlock<T>[] list = (IRBasicBlock<T>[]) blocks.toArray(new IRBasicBlock[blocks.size()]); // Set the EndPC's and flags final byte[] opcodeFlags = this.opcodeFlags; final byte[] branchFlags = this.branchFlags; 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 boolean nextIsSuccessor = true; if ((branchFlags[i] & UNCONDITIONAL_BRANCH) != 0) { nextIsSuccessor = false; } i++; while ((i < len) && (!isStartOfBB(i))) { if ((branchFlags[i] & UNCONDITIONAL_BRANCH) != 0) { nextIsSuccessor = false; } i++; } // the BB final IRBasicBlock<T> bb = list[bbIndex++]; if (nextIsSuccessor && bbIndex < list.length) { bb.addSuccessor(list[bbIndex]); } if (bb.getStartPC() != start) { throw new AssertionError("bb.getStartPC() != start"); } bb.setEndPC(i); bb.setStartOfExceptionHandler(isStartOfException(start)); i--; } } // TODO this is O(n^2), but it works... for (int[] entry : branchTargets) { final int from = entry[0]; final int to = entry[1]; IRBasicBlock<T> pred = findBB(list, from); IRBasicBlock<T> succ = findBB(list, to); if (pred == null || succ == null) { throw new AssertionError("unable to find BB!"); } pred.addSuccessor(succ); } if (bbIndex != list.length) { throw new AssertionError("bbIndex != list.length"); } for (int i = 0; i < byteCode.getNoExceptionHandlers(); i++) { VmInterpretedExceptionHandler eh = byteCode.getExceptionHandler(i); IRBasicBlock<T> handlerBB = findBB(list, eh.getHandlerPC()); IRBasicBlock<T> tryBB = findBB(list, eh.getStartPC()); for (IRBasicBlock bb : tryBB.getPredecessors()) { bb.addSuccessor(handlerBB); } } return list; } private IRBasicBlock<T> findBB(IRBasicBlock<T>[] blocks, int address) { for (IRBasicBlock<T> b : blocks) { if (b.contains(address)) { return b; } } return null; } public void startMethod(VmMethod method) { final VmByteCode bc = method.getBytecode(); byteCode = bc; final int length = bc.getLength(); opcodeFlags = new byte[length]; branchFlags = new byte[length]; // The first instruction is always the start of a BB. this.currentBlock = startBB(0); currentBlock.setStackOffset(bc.getNoLocals()); // The exception handler also start a basic block for (int i = 0; i < bc.getNoExceptionHandlers(); i++) { VmInterpretedExceptionHandler eh = bc.getExceptionHandler(i); IRBasicBlock tryBlock = startTryBlock(eh.getStartPC()); // IRBasicBlock endTryBlock = startTryBlockEnd(eh.getEndPC()); IRBasicBlock catchBlock = startException(eh.getHandlerPC()); } } public void endMethod() { VmByteCode bc = byteCode; // TODO add catch blocks to try successors for (int i = 0; i < bc.getNoExceptionHandlers(); i++) { VmInterpretedExceptionHandler eh = bc.getExceptionHandler(i); } } public void visit_ifeq(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_ifne(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_iflt(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_ifge(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_ifgt(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_ifle(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_if_icmpeq(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_if_icmpne(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_if_icmplt(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_if_icmpge(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_if_icmpgt(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_if_icmple(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_if_acmpeq(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_if_acmpne(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_goto(int address) { addBranch(address, UNCONDITIONAL_BRANCH); } public void visit_jsr(int address) { // TODO Not sure about this, the next block I believe it NOT a // direct successor. This will have to be tested. //addBranch(address); } public void visit_tableswitch(int defValue, int lowValue, int highValue, int[] addresses) { for (int address : addresses) { // Next block could be successor, e.g. switch could fall through addBranch(address, CONDITIONAL_BRANCH); } // Same for default case addBranch(defValue, CONDITIONAL_BRANCH); } public void visit_lookupswitch(int defValue, int[] matchValues, int[] addresses) { for (int address : addresses) { // Next block could be successor, e.g. switch could fall through addBranch(address, CONDITIONAL_BRANCH); } // Same for default case addBranch(defValue, CONDITIONAL_BRANCH); } public void visit_ifnull(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_ifnonnull(int address) { addBranch(address, CONDITIONAL_BRANCH); } public void visit_athrow() { endBB(UNCONDITIONAL_BRANCH); } public void visit_areturn() { endBB(UNCONDITIONAL_BRANCH); } public void visit_dreturn() { endBB(UNCONDITIONAL_BRANCH); } public void visit_freturn() { endBB(UNCONDITIONAL_BRANCH); } public void visit_ireturn() { endBB(UNCONDITIONAL_BRANCH); } public void visit_lreturn() { endBB(UNCONDITIONAL_BRANCH); } public void visit_ret(int index) { // Not sure about this either, this needs testing endBB(UNCONDITIONAL_BRANCH); } public void visit_return() { endBB(UNCONDITIONAL_BRANCH); } /** * Add branching information (to the given target) to the basic blocks information. * * @param target */ private final void addBranch(int target, byte flags) { IRBasicBlock pred = this.currentBlock; IRBasicBlock succ = startBB(target); branchTargets.add(new int[]{getInstructionAddress(), target}); endBB(flags); } /** * Mark the start of a basic block * * @param address */ private final IRBasicBlock<T> startBB(int address) { IRBasicBlock<T> next = null; if ((opcodeFlags[address] & BytecodeFlags.F_START_OF_BASICBLOCK) == 0) { opcodeFlags[address] |= BytecodeFlags.F_START_OF_BASICBLOCK; next = new IRBasicBlock<T>(address); blocks.add(next); } else { for (IRBasicBlock<T> bb : blocks) { if (bb.getStartPC() == address) { next = bb; break; } } } return next; } /** * Mark the end of a basic block */ private final void endBB(byte flags) { nextIsStartOfBB = true; int address = getInstructionAddress(); branchFlags[address] |= flags; } public void startInstruction(int address) { super.startInstruction(address); opcodeFlags[address] |= BytecodeFlags.F_START_OF_INSTRUCTION; if (nextIsStartOfBB || isStartOfBB(address)) { this.currentBlock = startBB(address); nextIsStartOfBB = false; } } private final boolean isStartOfBB(int address) { return ((opcodeFlags[address] & BytecodeFlags.F_START_OF_BASICBLOCK) != 0); } private final boolean isStartOfException(int address) { return ((opcodeFlags[address] & BytecodeFlags.F_START_OF_EXCEPTIONHANDLER) != 0); } /** * Mark the start of a exception handler * * @param address */ private final IRBasicBlock startException(int address) { opcodeFlags[address] |= BytecodeFlags.F_START_OF_EXCEPTIONHANDLER; return startBB(address); } /** * Mark the start of a try-catch block * * @param address */ private final IRBasicBlock startTryBlock(int address) { opcodeFlags[address] |= BytecodeFlags.F_START_OF_TRYBLOCK; return startBB(address); } /** * Mark the end of a try-catch block * * @param address */ private final IRBasicBlock startTryBlockEnd(int address) { opcodeFlags[address] |= BytecodeFlags.F_START_OF_TRYBLOCKEND; return startBB(address); } public int compare(IRBasicBlock o1, IRBasicBlock o2) { final int sp1 = ((IRBasicBlock) o1).getStartPC(); final int sp2 = ((IRBasicBlock) o2).getStartPC(); return sp1 - sp2; } }