/* * FlowBlock (c) 1998 Jochen Hoenicke * * You may distribute under the terms of the GNU General Public License. * * IN NO EVENT SHALL JOCHEN HOENICKE BE LIABLE TO ANY PARTY FOR DIRECT, * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF JOCHEN HOENICKE * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * JOCHEN HOENICKE SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" * BASIS, AND JOCHEN HOENICKE HAS NO OBLIGATION TO PROVIDE MAINTENANCE, * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * $Id$ */ package jode.flow; import java.util.*; import jode.TabbedPrintWriter; import jode.Expression; /** * A flow block is the structure of which the flow graph consists. A * flow block contains structured code together with some conditional * or unconditional jumps to the head of other flow blocks. * * We do a T1/T2 analysis to combine all flow blocks to a single. If * the graph isn't reducible that doesn't work, but java can only * produce reducible flow graphs. */ public class FlowBlock { static FlowBlock END_OF_METHOD = new FlowBlock(Integer.MAX_VALUE, 0, new EmptyBlock()); static { END_OF_METHOD.label = "END_OF_METHOD"; } /** * The starting address of this flow block. This is mainly used * to produce the source code in code order. */ int addr; /** * The length of the structured block, only needed at the beginning. */ int length; /** * The outermost structructed block in this flow block. */ StructuredBlock block; /** * The last modified structured block. */ StructuredBlock lastModified; /** * All Jumps that this flow block contains. The objects may be * null, if they were marked as deleted. */ Vector successors; /** * This is a vector of flow blocks, which reference this block. * Only if this vector contains exactly one element, it can be * moved into the preceding flow block. */ Vector predecessors; /** * The default constructor. Creates a new flowblock containing * only the given structured block. */ public FlowBlock(int addr, int length, StructuredBlock block) { this.addr = addr; this.length = length; this.block = block; lastModified = block; predecessors = new Vector(); // filled in later successors = new Vector(); block.setFlowBlock(this); block.fillSuccessors(successors); } public int getNextAddr() { return addr+length; } /** * This method optimizes the jumps to successor. * Returns the new appendBlock, it may have changed. */ public StructuredBlock optimizeJumps(FlowBlock successor, StructuredBlock appendBlock) { Enumeration enum = successors.elements(); next_jump: while (enum.hasMoreElements()) { Jump jump = (Jump) enum.nextElement(); if (jump == null || jump.destination != successor) continue next_jump; same_jump: while(true) { /* if the jump is the jump of the appendBlock, skip it. */ if (jump.prev == appendBlock) continue next_jump; /* Note: jump.prev.outer != null, since appendBlock is * an outer block of jump.prev */ /* remove all jumps to the successor which have the successor * as getNextFlowBlock(). */ if (jump.prev.outer.getNextFlowBlock(jump.prev) == successor) { jump.prev.removeJump(); continue next_jump; } /* replace all conditional jumps to the successor, which * are followed by a block which has the end of the block * as normal successor, with "if (not condition) block". */ if (jump.prev instanceof EmptyBlock && jump.prev.outer instanceof ConditionalBlock) { StructuredBlock prev = jump.prev; ConditionalBlock cb = (ConditionalBlock) prev.outer; jode.Instruction instr = cb.getInstruction(); if ((cb == appendBlock || cb.outer.getNextFlowBlock(cb) == successor /*XXX jumpMayBeChanged()??? */) && instr instanceof jode.Expression) { cb.setInstruction(((jode.Expression)instr).negate()); prev.removeJump(); prev.moveJump(cb); continue next_jump; } /* cb.outer is not null, * since appendBlock outers cb */ if (cb.outer instanceof SequentialBlock && cb.outer.getSubBlocks()[0] == cb && (cb.outer.getNextFlowBlock() == successor || cb.outer.jumpMayBeChanged()) && instr instanceof jode.Expression) { SequentialBlock sequBlock = (SequentialBlock) cb.outer; IfThenElseBlock newIfBlock = new IfThenElseBlock(((jode.Expression)instr).negate()); newIfBlock.replace(sequBlock); newIfBlock.setThenBlock(sequBlock.getSubBlocks()[1]); newIfBlock.moveJump(sequBlock); if (appendBlock == sequBlock) appendBlock = newIfBlock; if (newIfBlock.getNextFlowBlock() != successor && newIfBlock != appendBlock) { newIfBlock.moveJump(prev); continue same_jump; } else { prev.removeJump(); continue next_jump; } } } /* if there are jumps in an if-then block, which * have as normal successor the end of the if-then block, and * the if-then block is followed by a single block, then replace * the if-then block with a if-then-else block and remove the * unconditional jump. */ StructuredBlock elseBlock = jump.prev.outer.getNextBlock(jump.prev); if (elseBlock != null && elseBlock.outer != null && elseBlock.outer instanceof SequentialBlock && elseBlock.outer.getSubBlocks()[0] instanceof IfThenElseBlock && (elseBlock.outer.getNextFlowBlock() == successor || elseBlock.outer.jumpMayBeChanged())) { IfThenElseBlock ifBlock = (IfThenElseBlock)elseBlock.outer.getSubBlocks()[0]; if (ifBlock.getSubBlocks().length == 1) { elseBlock.outer.removeJump(); ifBlock.replace(elseBlock.outer); if (appendBlock == elseBlock.outer) appendBlock = ifBlock; ifBlock.moveJump(jump.prev); ifBlock.setElseBlock(elseBlock); continue same_jump; } } /* if the successor is the dummy return instruction, replace all * jumps with a return. */ if (successor == END_OF_METHOD) { SequentialBlock sequBlock = new SequentialBlock(); StructuredBlock prevBlock = jump.prev; prevBlock.removeJump(); sequBlock.replace(prevBlock); sequBlock.setFirst(prevBlock); sequBlock.setSecond(new ReturnBlock()); continue next_jump; } /* If this is a conditional jump, the first instruction of * a while and the condition of the while is true, use * the condition as while condition. */ if (jump.prev instanceof EmptyBlock && jump.prev.outer instanceof ConditionalBlock && jump.prev.outer.jump == null) { StructuredBlock prev = jump.prev; ConditionalBlock cb = (ConditionalBlock) prev.outer; jode.Instruction instr = cb.getInstruction(); /* This is the first instruction in a while block */ if (cb.outer instanceof SequentialBlock && cb.outer.getSubBlocks()[0] == cb && cb.outer.outer instanceof LoopBlock) { LoopBlock loopBlock = (LoopBlock) cb.outer.outer; if (loopBlock.getCondition() == LoopBlock.TRUE && loopBlock.getType() != LoopBlock.DOWHILE && loopBlock.getNextFlowBlock() == successor && instr instanceof Expression) { prev.removeJump(); loopBlock.setCondition(((Expression)instr).negate()); if (cb.outer.jump != null) { if (cb.outer.getSubBlocks()[1].jump != null) cb.outer.removeJump(); else cb.outer.getSubBlocks()[1].moveJump(cb.outer); } cb.outer.getSubBlocks()[1].replace(cb.outer); /* cb and cb.outer are not used any more */ /* Note that cb.outer != appendBlock because * appendBlock contains loopBlock */ continue next_jump; } } /* Now the same for the empty loop. In this case there is * no sequential block. */ if (cb.outer instanceof LoopBlock) { LoopBlock loopBlock = (LoopBlock) cb.outer; if (loopBlock.getCondition() == LoopBlock.TRUE && loopBlock.getType() != LoopBlock.DOWHILE && loopBlock.getNextFlowBlock() == successor && instr instanceof Expression) { prev.removeJump(); loopBlock.setCondition(((Expression)instr).negate()); EmptyBlock empty = new EmptyBlock(); empty.replace(cb); /* cb is not used any more */ continue next_jump; } } } /* if there are jumps in a while block or switch block and the * while/switch block is followed by a jump to successor or has * successor as getNextFlowBlock(), replace jump with break to * the innermost such while/switch block. * * If the switch block hasn't been breaked before we could * take some heuristics and add a jump after the switch to * succesor, so that the above succeeds. */ int breaklevel = 0; for (StructuredBlock surrounder = jump.prev.outer; surrounder != null && surrounder != appendBlock.outer; surrounder = surrounder.outer) { if (surrounder instanceof BreakableBlock) { breaklevel++; if (surrounder.getNextFlowBlock() == successor || surrounder.jumpMayBeChanged()) { SequentialBlock sequBlock = new SequentialBlock(); StructuredBlock prevBlock = jump.prev; if (surrounder.getNextFlowBlock() != successor) surrounder.moveJump(prevBlock); else prevBlock.removeJump(); sequBlock.replace(prevBlock); sequBlock.setFirst(prevBlock); sequBlock.setSecond (new BreakBlock((BreakableBlock) surrounder, breaklevel > 1)); continue next_jump; } } } continue next_jump; } } return appendBlock; } /** * Updates the in/out-Vectors of the structured block of the * successing flow block simultanous to a T1 transformation. * @param successor The flow block which is unified with this flow * block. */ void updateInOut (FlowBlock successor, boolean t1Transformation) { /* First get the out vectors of all jumps to successor and * calculate the intersection. */ VariableSet allOuts = new VariableSet(); VariableSet intersectOut = null; Enumeration enum = successors.elements(); while (enum.hasMoreElements()) { Jump jump = (Jump) enum.nextElement(); if (jump == null || jump.destination != successor) continue; allOuts.union(jump.prev.out); if (intersectOut == null) intersectOut = jump.prev.out; else intersectOut = intersectOut.intersect(jump.prev.out); } /* Now work on each block of the successor */ Stack todo = new Stack(); todo.push(successor.block); while (!todo.empty()) { StructuredBlock block = (StructuredBlock) todo.pop(); StructuredBlock[] subBlocks = block.getSubBlocks(); for (int i=0; i| * throw local_n; | * finally: <-----------------------' * astore_n * ... * return_n * * * flow-block * finally-block * ---> try-header * finally { * ---> first-finally-instruction * * A synchronized block uses a similar technique: * * local_x = monitor object; * monitorenter local_x * try-header any * |- syncronized block * | ... * | every jump to outside is preceded by jsr monexit ---, * | ... | * |- monitorexit local_x | * | jump after this block (without jsr monexit) | * `- catch any (local_n) | * monitorexit local_x | * throw local_n | * monexit: <-----------------------------------------------' * astore_n * monitorexit local_x * return_n */ /** * Search for an apropriate successor. * @return the successor with smallest address * or null if there isn't a successor at all. */ public FlowBlock getSuccessor() { /* search successor with smallest addr. */ Enumeration enum = successors.elements(); FlowBlock succ = null; while (enum.hasMoreElements()) { Jump jump = (Jump) enum.nextElement(); if (jump == null) continue; FlowBlock fb = jump.destination; if (succ == null || fb.addr < succ.addr) { succ = fb; } } return succ; } public void checkConsistent() { if (block.outer != null || block.flowBlock != this) { throw new RuntimeException("Inconsistency"); } block.checkConsistent(); Enumeration enum = successors.elements(); while (enum.hasMoreElements()) { Jump jump = (Jump) enum.nextElement(); if (jump == null) continue; if (jump.prev.flowBlock != this || jump.prev.jump != jump) throw new RuntimeException("Inconsistency"); StructuredBlock sb = jump.prev; while (sb != block) { if (sb.outer == null) throw new RuntimeException("Inconsistency"); StructuredBlock[] blocks = sb.outer.getSubBlocks(); int i; for (i=0; i 1)); } if (doWhileFalse != null) { doWhileFalse.replace(appendBlock); doWhileFalse.setBody(appendBlock); } /* Believe it or not: Now the rule, that the first part of a * SequentialBlock shouldn't be another SequentialBlock is * fulfilled.

* * This isn't easy to prove, it has a lot to do with the * transformation in optimizeJump and the fact that * appendBlock was the innermost Block containing all jumps * and lastModified. */ /* Set last modified to correct value. */ lastModified = succ.lastModified; /* Set addr+length to (semi-)correct value */ if (succ.addr < addr) addr = succ.addr; length += succ.length; /* T1 transformation succeeded */ try { TabbedPrintWriter writer = new TabbedPrintWriter(System.err, " "); writer.tab(); System.err.println("T1 succeeded:"); block.dumpSource(writer); checkConsistent(); } catch (java.io.IOException ex) {} return true; } public boolean doT2() { /* If there are no jumps to the beginning of this flow block * or if this block has other predecessors which weren't * considered yet, return false. The second condition make * sure that the while isn't created up to the first continue. */ if (!predecessors.contains(this) /* || complicated second condition XXX */ ) return false; try { TabbedPrintWriter writer = new TabbedPrintWriter(System.err, " "); writer.tab(); System.err.println("doing T2 analysis on: "+getLabel()); block.dumpSource(writer); checkConsistent(); } catch (java.io.IOException ex) {} /* Update the in/out-Vectors now */ updateInOut(this, false); /* If there is only one jump to the beginning and it is the * last jump and (there is a do/while(0) block surrounding * everything but the last instruction, or the last * instruction is a increase/decrease statement), replace the * do/while(0) with a for(;;last_instr) resp. create a new one * and replace breaks to do/while with continue to for. */ /* XXX implement above */ /* XXX condition for do/while(cond) blocks */ { /* Otherwise: */ /* create a new while(true) block. */ StructuredBlock bodyBlock = block; /* Prepare the unification of the blocks: Make sure that * bodyBlock has a jump. */ if (bodyBlock.jump == null) { Jump jump = new Jump(this); bodyBlock.setJump(jump); successors.addElement(jump); } LoopBlock whileBlock = new LoopBlock(LoopBlock.WHILE, LoopBlock.TRUE); whileBlock.replace(bodyBlock); whileBlock.setBody(bodyBlock); /* Try to eliminate as many jumps as possible. */ bodyBlock = optimizeJumps(this, bodyBlock); /* Now remove the jump of block if it points to this. */ if (bodyBlock.jump != null && bodyBlock.jump.destination == this) bodyBlock.removeJump(); /* if there are further jumps to this, replace every jump with a * continue to while block and return true. */ Enumeration enum = successors.elements(); while (enum.hasMoreElements()) { Jump jump = (Jump) enum.nextElement(); if (jump == null || jump.destination != this) continue; int continuelevel = 1; for (StructuredBlock surrounder = jump.prev.outer; surrounder != null; surrounder = surrounder.outer) { if (surrounder instanceof LoopBlock) { continuelevel++; } } SequentialBlock sequBlock = new SequentialBlock(); StructuredBlock prevBlock = jump.prev; prevBlock.removeJump(); sequBlock.replace(prevBlock); sequBlock.setFirst(prevBlock); sequBlock.setSecond(new ContinueBlock(whileBlock, continuelevel > 1)); } lastModified = whileBlock; } /* remove ourself from the predecessor list. */ predecessors.removeElement(this); /* T2 analysis succeeded */ try { TabbedPrintWriter writer = new TabbedPrintWriter(System.err, " "); writer.tab(); System.err.println("T2 succeded:"); block.dumpSource(writer); checkConsistent(); } catch (java.io.IOException ex) {} return true; } /** * Resolves the destinations of all jumps. */ public void resolveJumps(FlowBlock[] instr) { Enumeration enum = successors.elements(); while (enum.hasMoreElements()) { Jump jump = (Jump) enum.nextElement(); if (jump.destAddr == -1) jump.destination = END_OF_METHOD; else jump.destination = instr[jump.destAddr]; if (!jump.destination.predecessors.contains(this)) jump.destination.predecessors.addElement(this); } } public void removeSuccessor(Jump jump) { successors.setElementAt(null, successors.indexOf(jump)); } /** * Print the source code for this structured block. This handles * everything that is unique for all structured blocks and calls * dumpInstruction afterwards. * @param writer The tabbed print writer, where we print to. */ public void dumpSource(TabbedPrintWriter writer) throws java.io.IOException { if (label != null) { writer.untab(); writer.println(label+":"); writer.tab(); } block.dumpSource(writer); FlowBlock succ = getSuccessor(); if (succ != null) succ.dumpSource(writer); } /** * The serial number for labels. */ static int serialno = 0; /** * The label of this instruction, or null if it needs no label. */ String label = null; /** * Returns the label of this block and creates a new label, if * there wasn't a label previously. */ public String getLabel() { if (label == null) label = "flow_"+(serialno++)+"_"; return label; } }