/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.compilers.opt.controlflow; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import org.jikesrvm.VM; import org.jikesrvm.compilers.opt.OperationNotImplementedException; import org.jikesrvm.compilers.opt.ir.BasicBlock; import org.jikesrvm.compilers.opt.ir.ControlFlowGraph; import org.jikesrvm.compilers.opt.ir.IR; import org.jikesrvm.compilers.opt.util.Stack; /** * Calculate dominators using Langauer and Tarjan's fastest algorithm. * TOPLAS 1(1), July 1979. This implementation uses path compression and * results in a O(e * alpha(e,n)) complexity, where e is the number of * edges in the CFG and n is the number of nodes. * <p> * Sources: TOPLAS article, Muchnick book * <p> * The current implementation (4/25/00) does not include the EXIT node * in any solution despite the fact that it is part of the CFG (it has * incoming edges). This is to be compatible with the old code. */ public class LTDominators extends Stack<BasicBlock> { static final boolean DEBUG = false; /** * Indicates whether we perform the algorithm over the CFG or * the reverse CFG, i.e., whether we are computing dominators or * post-dominators. */ private final boolean forward; /** * a counter for assigning DFS numbers */ protected int DFSCounter; /** * a mapping from DFS number to their basic blocks */ private BasicBlock[] vertex; /** * a convenient place to locate the cfg to avoid passing it internally */ private final ControlFlowGraph cfg; private Map<BasicBlock, LTDominatorInfo> ltDominators; private final IR ir; /** * The constructor, called by the perform method * @param ir the governing IR * @param forward Should we compute regular dominators, or post-dominators? */ LTDominators(IR ir, boolean forward) { this.ir = ir; cfg = ir.cfg; // save the cfg for easy access this.forward = forward; // save the forward flag } /** * The entry point for this phase * @param ir the IR * @param forward Should we compute regular dominators, or post-dominators? * @param unfactor Should we unfactor the CFG? */ public static void perform(IR ir, boolean forward, boolean unfactor) { if (ir.hasReachableExceptionHandlers()) { if (unfactor) { ir.unfactor(); } else { throw new OperationNotImplementedException("IR with exception handlers"); } } LTDominators dom = new LTDominators(ir, forward); ir.setLtDominators(dom); dom.analyze(ir); } /** * Compute approximate dominator/post dominator without unfactoring * exception handlers. Can only be used if what the client wants is * approximate domination (ie, if it doesn't actually have to be correct...) * @param ir the IR * @param forward Should we compute regular dominators, or post-dominators? */ public static void approximate(IR ir, boolean forward) { LTDominators dom = new LTDominators(ir, forward); ir.setLtDominators(dom); dom.analyze(ir); } protected void analyze(IR ir) { if (DEBUG) { System.out.println(" Here's the CFG for method: " + ir.method.getName() + "\n" + ir.cfg); } // Step 1: Perform a DFS numbering step1(); // Check to make sure all nodes were reached checkReachability(ir); // Step 2: the heart of the algorithm step2(); // Step 3: adjust immediate dominators of nodes whoe current version of // the immediate dominators differs from the nodes with the depth-first // number of the node's semidominator. step3(); if (DEBUG) { printResults(ir); } } /** * Checks that all nodes were reached. * * @param ir the governing IR */ private void checkReachability(IR ir) { if (!forward) { if (DFSCounter != cfg.numberOfNodes()) { VM.sysWriteln(" *** Warning ***\n CFG for method " + ir.method.getName() + " in class " + ir.method.getDeclaringClass() + " has unreachable nodes."); VM.sysWriteln(" Assuming pessimistic results in dominators computation\n" + " for unreachable nodes."); } } } /** * The goal of this step is to perform a DFS numbering on the CFG, * starting at the root. The exit node is not included. */ private void step1() { // allocate the vertex array, one element for each basic block, starting // at 1 int size = cfg.numberOfNodes() + 1; vertex = new BasicBlock[size]; DFSCounter = 0; if (DEBUG) { System.out.println("Initializing blocks:"); } int noRehashCapacity = (int) (size * 1.4f); ltDominators = new HashMap<BasicBlock, LTDominatorInfo>(noRehashCapacity); // Initialize each block with an empty set of predecessors and // a 0 for a semidominator for (Enumeration<BasicBlock> bbEnum = cfg.basicBlocks(); bbEnum.hasMoreElements();) { BasicBlock block = bbEnum.nextElement(); // We don't compute a result for the exit node in the forward direction if (!forward || !block.isExit()) { ltDominators.put(block, new LTDominatorInfo(block)); if (DEBUG) { printNextNodes(block); } } } DFS(); if (DEBUG) { System.out.println("DFSCounter: " + DFSCounter + ", CFG Nodes: " + cfg.numberOfNodes()); printDFSNumbers(); } } private void DFS() { DFS(getFirstNode()); } /** * Get the first node, either entry or exit * depending on which way we are viewing the graph * @return the entry node or exit node */ private BasicBlock getFirstNode() { if (forward) { return cfg.entry(); } else { return cfg.exit(); } } /** * Print the "next" nodes (either out or in) for the passed block * depending on which way we are viewing the graph * @param block the basic block of interest */ private void printNextNodes(BasicBlock block) { if (forward) { System.out.print(block + " Succs:"); } else { System.out.print(block + " Preds:"); } Enumeration<BasicBlock> e = getNextNodes(block); while (e.hasMoreElements()) { System.out.print(' '); System.out.print(e.nextElement()); } System.out.println(); } /** * @param block the basic block of interest * @return an enumeration of the "next" nodes (either out or in) for the * passed block depending on which way we are viewing the graph */ private Enumeration<BasicBlock> getNextNodes(BasicBlock block) { Enumeration<BasicBlock> bbEnum; if (forward) { bbEnum = block.getOut(); } else { bbEnum = block.getIn(); } return bbEnum; } /** * @param block the basic block of interest * @return an enumeration of the "prev" nodes (either in or out) for the * passed block depending on which way we are viewing the graph */ private Enumeration<BasicBlock> getPrevNodes(BasicBlock block) { Enumeration<BasicBlock> bbEnum; if (forward) { bbEnum = block.getIn(); } else { bbEnum = block.getOut(); } return bbEnum; } /** * The non-recursive depth-first numbering code called from Step 1. * The recursive version was too costly on the toba benchmark on Linux/IA32. * @param block the basic block to process */ protected void DFS(BasicBlock block) { // push node on to the emulated activation stack push(block); recurse: while (!empty()) { block = peek(); if (DEBUG) { System.out.println(" Processing (peek)" + block); } if (block == null) { if (DEBUG) { System.out.println(" Popping"); } pop(); // return continue; } // The current Dominance Frontier and SSA code assumes the exit // node will not be part of the (regular) dominator solution. // To avoid this from happening we screen it out here for forward CFG // // However, it really shouldn't be in the CFG, if it isn't a node! if (forward && block == cfg.exit()) { if (DEBUG) { System.out.println(" Popping"); } pop(); // return continue; } Enumeration<BasicBlock> e; e = LTDominatorInfo.getInfo(block, ir).getEnum(); if (e == null) { if (DEBUG) { System.out.println(" Initial processing of " + block); } DFSCounter++; LTDominatorInfo.getInfo(block, ir).setSemiDominator(DFSCounter); vertex[DFSCounter] = block; e = getNextNodes(block); } else { if (DEBUG) { System.out.println(" Resuming processing of " + block); } } while (e.hasMoreElements()) { BasicBlock next = e.nextElement(); if (DEBUG) { System.out.println(" Inspecting next node: " + next); } // We treat the exit node as not being in the CFG for forward direction if (forward && next.isExit()) { continue; // inner loop } if (getSemi(next) == 0) { LTDominatorInfo.getInfo(next, ir).setParent(block); // simulate a recursive call // save the enumeration state for resumption later LTDominatorInfo.getInfo(block, ir).setEnum(e); if (DEBUG) { System.out.println(" Pushing" + next); } push(next); continue recurse; } } // while more nexts // "Pop" from the emulated activiation stack if (DEBUG) { System.out.println(" Popping"); } pop(); } // while stack not empty loop } /** * This is the heart of the algorithm. See sources for details. */ private void step2() { if (DEBUG) { System.out.println(" ******* Beginning STEP 2 *******"); System.out.println(); } // Visit each node in reverse DFS order, except for the root, which // has number 1 // for i=n downto 2 for (int i = DFSCounter; i > 1; i--) { // block = vertex[i] BasicBlock block = vertex[i]; LTDominatorInfo blockInfo = LTDominatorInfo.getInfo(block, ir); if (DEBUG) { System.out.println(" Processing: " + block); System.out.println(); } // visit each predecessor Enumeration<BasicBlock> e = getPrevNodes(block); while (e.hasMoreElements()) { BasicBlock prev = e.nextElement(); if (DEBUG) { System.out.println(" Inspecting prev: " + prev); } BasicBlock u = EVAL(prev); // if semi(u) < semi(block) then semi(block) = semi(u) // u may be part of infinite loop and thus, is unreachable from the exit node. // In this case, it will have a semi value of 0. Thus, we screen for it here if (getSemi(u) != 0 && getSemi(u) < getSemi(block)) { blockInfo.setSemiDominator(getSemi(u)); } } // while prev // add "block" to bucket(vertex(semi(block))); LTDominatorInfo.getInfo(vertex[blockInfo.getSemiDominator()], ir). addToBucket(block); // LINK(parent(block), block) LINK(blockInfo.getParent(), block); // foreach block2 in bucket(parent(block)) do java.util.Iterator<BasicBlock> bucketEnum = LTDominatorInfo.getInfo(getParent(block), ir).getBucketIterator(); while (bucketEnum.hasNext()) { BasicBlock block2 = bucketEnum.next(); // u = EVAL(block2) BasicBlock u = EVAL(block2); // if semi(u) < semi(block2) then // dom(block2) = u // else // dom(block2) = parent(block) if (getSemi(u) < getSemi(block2)) { LTDominatorInfo.getInfo(block2, ir).setDominator(u); } else { LTDominatorInfo.getInfo(block2, ir).setDominator(getParent(block)); } } // while bucket has more elements } // for DFSCounter .. 1 } // method /** * This method inspects the passed block and returns the following: * <ul> * <li>block, if block is a root of a tree in the forest * <li>any vertex, u != r such that r is the root of the tree * containing block and semi(u) is minimum on the path r -> v, * otherwise * </ul> * <p> * * See TOPLAS 1(1), July 1979, p 128 for details. * * @param block the block to evaluate * @return the block as described above */ private BasicBlock EVAL(BasicBlock block) { if (DEBUG) { System.out.println(" Evaling " + block); } if (getAncestor(block) == null) { return getLabel(block); } else { compress(block); if (getSemi(getLabel(getAncestor(block))) >= getSemi(getLabel(block))) { return getLabel(block); } else { return getLabel(getAncestor(block)); } } } /** * This recursive method performs the path compression * @param block the block of interest */ private void compress(BasicBlock block) { if (getAncestor(getAncestor(block)) != null) { compress(getAncestor(block)); LTDominatorInfo blockInfo = LTDominatorInfo.getInfo(block, ir); if (getSemi(getLabel(getAncestor(block))) < getSemi(getLabel(block))) { blockInfo.setLabel(getLabel(getAncestor(block))); } blockInfo.setAncestor(getAncestor(getAncestor(block))); } } /** * Adds edge (block1, block2) to the forest maintained as an auxiliary * data structure. This implementation uses path compression and * results in a O(e * alpha(e,n)) complexity, where e is the number of * edges in the CFG and n is the number of nodes. * * @param block1 a basic block corresponding to the source of the new edge * @param block2 a basic block corresponding to the source of the new edge */ private void LINK(BasicBlock block1, BasicBlock block2) { if (DEBUG) { System.out.println(" Linking " + block1 + " with " + block2); } BasicBlock s = block2; while (getSemi(getLabel(block2)) < getSemi(getLabel(getChild(s)))) { if (getSize(s) + getSize(getChild(getChild(s))) >= 2 * getSize(getChild(s))) { LTDominatorInfo.getInfo(getChild(s), ir).setAncestor(s); LTDominatorInfo.getInfo(s, ir).setChild(getChild(getChild(s))); } else { LTDominatorInfo.getInfo(getChild(s), ir).setSize(getSize(s)); LTDominatorInfo.getInfo(s, ir).setAncestor(getChild(s)); s = getChild(s); } } LTDominatorInfo.getInfo(s, ir).setLabel(getLabel(block2)); LTDominatorInfo.getInfo(block1, ir).setSize(getSize(block1) + getSize(block2)); if (getSize(block1) < 2 * getSize(block2)) { BasicBlock tmp = s; s = getChild(block1); LTDominatorInfo.getInfo(block1, ir).setChild(tmp); } while (s != null) { LTDominatorInfo.getInfo(s, ir).setAncestor(block1); s = getChild(s); } if (DEBUG) { System.out.println(" .... done"); } } /** * This final step sets the final dominator information. */ private void step3() { // Visit each node in DFS order, except for the root, which has number 1 for (int i = 2; i <= DFSCounter; i++) { BasicBlock block = vertex[i]; // if dom(block) != vertex[semi(block)] if (getDom(block) != vertex[getSemi(block)]) { // dom(block) = dom(dom(block)) LTDominatorInfo.getInfo(block, ir).setDominator(getDom(getDom(block))); } } } // // The following methods are simple helper methods to increase the // readability of the code. // /** * @param block the block whose dominator is of interest * @return the dominator for the passed block */ private BasicBlock getDom(BasicBlock block) { return LTDominatorInfo.getInfo(block, ir).getDominator(); } /** * @param block the block whose parent is of interest * @return the parent for the passed block */ private BasicBlock getParent(BasicBlock block) { return LTDominatorInfo.getInfo(block, ir).getParent(); } /** * @param block the block whose ancestor is of interest * @return the ancestor for the passed block */ private BasicBlock getAncestor(BasicBlock block) { return LTDominatorInfo.getInfo(block, ir).getAncestor(); } /** * @param block the block whose label is of interest * @return the label for the passed block or {@code null} if the block is * {@code null} */ private BasicBlock getLabel(BasicBlock block) { if (block == null) { return null; } return LTDominatorInfo.getInfo(block, ir).getLabel(); } /** * @param block the block whose semidominator is of interest * @return the semidominator for the passed block */ private int getSemi(BasicBlock block) { if (block == null) { return 0; } return LTDominatorInfo.getInfo(block, ir).getSemiDominator(); } /** * @param block block the block whose size is of interest * @return the size of the block or 0 if the block is {@code null} */ private int getSize(BasicBlock block) { if (block == null) { return 0; } return LTDominatorInfo.getInfo(block, ir).getSize(); } /** * @param block the block whose child is of interest * @return the child node */ private BasicBlock getChild(BasicBlock block) { return LTDominatorInfo.getInfo(block, ir).getChild(); } /** * Print the nodes that dominate each basic block * @param ir the IR */ private void printResults(IR ir) { if (forward) { System.out.println("Results of dominators computation for method " + ir.method.getName()); System.out.println(); System.out.println(" Here's the CFG:"); System.out.println(ir.cfg); System.out.println(); System.out.println(); System.out.println(" Here's the Dominator Info:"); } else { System.out.println("Results of Post-Dominators computation for method " + ir.method.getName()); System.out.println(); System.out.println(" Here's the CFG:"); System.out.println(ir.cfg); System.out.println(); System.out.println(); System.out.println(" Here's the Post-Dominator Info:"); } for (Enumeration<BasicBlock> bbEnum = cfg.basicBlocks(); bbEnum.hasMoreElements();) { BasicBlock block = bbEnum.nextElement(); // We don't compute a result for the exit node for forward direction if (!forward || !block.isExit()) { System.out.println("Dominators of " + block + ":" + LTDominatorInfo.getInfo(block, ir).dominators(block, ir)); } } System.out.println('\n'); } /** * Print the result of the DFS numbering performed in Step 1 */ private void printDFSNumbers() { for (Enumeration<BasicBlock> bbEnum = cfg.basicBlocks(); bbEnum.hasMoreElements();) { BasicBlock block = bbEnum.nextElement(); // We don't compute a result for the exit node for forward direction if (forward && block.isExit()) { continue; } LTDominatorInfo info = ir.getLtDominators().getInfo(block); System.out.println(" " + block + " " + info); } // Visit each node in reverse DFS order, except for the root, which // has number 1 for (int i = 1; i <= DFSCounter; i++) { System.out.println(" Vertex: " + i + " " + vertex[i]); } } LTDominatorInfo getInfo(BasicBlock bb) { return ltDominators.get(bb); } }