/* * This file is part of JOP, the Java Optimized Processor * see <http://www.jopdesign.com/> * * Copyright (C) 2008-2011, Benedikt Huber (benedikt.huber@gmail.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.jopdesign.common.graphutils; import org.jgrapht.DirectedGraph; import org.jgrapht.VertexFactory; import org.jgrapht.graph.DefaultDirectedGraph; import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.DirectedSubgraph; import org.jgrapht.graph.SimpleDirectedGraph; import org.jgrapht.traverse.AbstractGraphIterator; import org.jgrapht.traverse.TopologicalOrderIterator; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.Stack; import java.util.TreeSet; import java.util.Vector; /** * Compute the loop coloring of a graph, i.e. for each node * the set of loops (identified by targets of back-edges) it * belongs to. Based on {@link TopOrder} and {@link Dominators}. * * @param <V> node type * @param <E> edge type */ public class LoopColoring<V, E> { private enum SimpleVisitColor { WHITE, GREY } private class LoopColorIterator extends AbstractGraphIterator<V, E> { private Stack<V> stack; private V hol; private Map<V, SimpleVisitColor> visited; LoopColorIterator(V headOfLoop, Collection<E> backEdges) { this.hol = headOfLoop; this.stack = new Stack<V>(); for (E edge : backEdges) { stack.push(graph.getEdgeSource(edge)); } this.visited = new HashMap<V, SimpleVisitColor>(); } public boolean hasNext() { return !stack.empty(); } public V next() { V nextV = stack.pop(); visited.put(nextV, SimpleVisitColor.GREY); // push all non-visited predecessors on the stack, if the node != hol if (!nextV.equals(hol)) { for (E preEdge : graph.incomingEdgesOf(nextV)) { V pre = graph.getEdgeSource(preEdge); if (visited.containsKey(pre)) continue; stack.push(pre); visited.put(pre, SimpleVisitColor.WHITE); } } return nextV; } } private DirectedGraph<V, E> graph; private TopOrder<V, E> topOrder; private Map<V, Set<V>> loopColors; private Map<V, List<E>> backEdgesByHOL; private Map<V, List<E>> exitEdges; private SimpleDirectedGraph<V, DefaultEdge> loopNestForest; private Map<E, IterationBranchLabel<V>> iterationBranchEdges; private Set<E> backEdges; public LoopColoring(DirectedGraph<V, E> graph, TopOrder<V, E> topOrder, V exit) { this.graph = graph; this.topOrder = topOrder; } public Map<V, Set<V>> getLoopColors() { if (loopColors == null) analyse(); return loopColors; } private void analyse() { loopColors = new HashMap<V, Set<V>>(); for (V v : graph.vertexSet()) loopColors.put(v, new TreeSet<V>()); /* Step 1: Group backedges by Head-Of-Loop */ backEdgesByHOL = new HashMap<V, List<E>>(); for (E backedge : this.topOrder.getBackEdges()) { V hol = graph.getEdgeTarget(backedge); List<E> endVxs = backEdgesByHOL.get(hol); if (endVxs == null) endVxs = new ArrayList<E>(); endVxs.add(backedge); backEdgesByHOL.put(hol, endVxs); } /* Step 2: For every (hol,endVertices) pair, perform a DFS starting at endVertices, * on the RCFG with all outgoing edges of hol removed. For this purpose, we provide * a special iterator. */ for (Entry<V, List<E>> loop : backEdgesByHOL.entrySet()) { V hol = loop.getKey(); LoopColorIterator iter = new LoopColorIterator(hol, loop.getValue()); while (iter.hasNext()) { loopColors.get(iter.next()).add(hol); } } computeExitEdges(); } private void computeExitEdges() { exitEdges = new HashMap<V, List<E>>(); /* For each edge, compute the set difference of source color and target color */ for (E e : graph.edgeSet()) { V src = graph.getEdgeSource(e); V target = graph.getEdgeTarget(e); Set<V> exitSet = new HashSet<V>(this.loopColors.get(src)); exitSet.removeAll(this.loopColors.get(target)); for (V loop : exitSet) { List<E> exits = this.exitEdges.get(loop); if (exits == null) { exits = new ArrayList<E>(); this.exitEdges.put(loop, exits); } exits.add(e); } } } /** * A loop-nest DAG has an edge from loop A to loop B, if B is an inner loop of A. * * @return The loop-nest DAG of the graph */ public SimpleDirectedGraph<V, DefaultEdge> getLoopNestDAG() { if (loopNestForest != null) return loopNestForest; analyse(); loopNestForest = new SimpleDirectedGraph<V, DefaultEdge>(DefaultEdge.class); for (V hol : backEdgesByHOL.keySet()) { loopNestForest.addVertex(hol); } for (V hol : backEdgesByHOL.keySet()) { Set<V> outerLoops = this.loopColors.get(hol); for (V outerLoop : outerLoops) { if (outerLoop.equals(hol)) continue; loopNestForest.addEdge(outerLoop, hol); } } return loopNestForest; } /** * @param hol the loop of which to compute the ancestor * @param dist the distance n * @return the n-th outer loop * <p>Given all predecessors of the loop hol in the loop nest forest, we want the one * where {@code |successors \cap loopcolor(hol)| = dist}.</p> * <p>Example: Assume hol = loop 4, and the following loop nest DAG<pre> * 1->2,3,4,5,6 for(1: ) * 2->3,4,5,6 for(2: ) * 3->4,5,6 for(3: ) * 4->5 for(4: ) * for(5: ) * colors(4) = {1,2,4,5} for(6:) * </pre> * For 3, we have {@code |{4,5,6} \cap {1,2,3,4}| = 1}, so 3 is the ancestor with distance 1 * For 1, we have {@code |{2,3,4,5,6} \cap {1,2,3,4}| = 3}, so 1 is the ancestor with distance 3 */ public V getLoopAncestor(V hol, int dist) { if(dist == 0) return hol; V ancestor = null; Set<V> holColors = loopColors.get(hol); SimpleDirectedGraph<V, DefaultEdge> loopNestForest = getLoopNestDAG(); for(DefaultEdge incoming : loopNestForest.incomingEdgesOf(hol)) { V pred = loopNestForest.getEdgeSource(incoming); loopNestForest.outgoingEdgesOf(pred); int intersectSize = 0; for(DefaultEdge predOutgoing : loopNestForest.outgoingEdgesOf(pred)) { V predSucc = loopNestForest.getEdgeTarget(predOutgoing); if(holColors.contains(predSucc)) intersectSize+=1; } if(intersectSize == dist) { if(ancestor != null) { throw new AssertionError("malformed loop nest DAG: more than one direct ancestor"); } else { ancestor = pred; } } } return ancestor; } /** * <p>Return a traversal of the graph in 'flow' order, a topological order * where back edges and the loop header are treated in a special way.</p> * </p> * * @return The traversal as list of nodes */ public List<V> getFlowTraversal() { List<V> flowTraversal = new ArrayList<V>(); TopologicalOrderIterator<V, DefaultEdge> iter = new TopologicalOrderIterator<V, DefaultEdge>(getFlowTraversalGraph()); while (iter.hasNext()) { flowTraversal.add(iter.next()); } return flowTraversal; } /** * topological order, where back edges are replaced by exit loop edg * * @return */ public DirectedGraph<V, DefaultEdge> getFlowTraversalGraph() { DirectedGraph<V, DefaultEdge> travGraph = new DefaultDirectedGraph<V, DefaultEdge>(DefaultEdge.class); for (V v : graph.vertexSet()) { travGraph.addVertex(v); } for (E e : graph.edgeSet()) { V src = graph.getEdgeSource(e); V target = graph.getEdgeTarget(e); if (isBackEdge(e)) { for (E exitEdge : getExitEdgesOf(target)) { V newTarget = graph.getEdgeTarget(exitEdge); travGraph.addEdge(src, newTarget); } } else { travGraph.addEdge(src, target); } } return travGraph; } /** * A node is an Head of loop, if it is the target of a back-edge * * @return */ public Set<V> getHeadOfLoops() { if (backEdgesByHOL == null) analyse(); return backEdgesByHOL.keySet(); } /** * Get nodes belonging to some head of loop * <p>Complexity: {@code O(|V|) * (map-get + set-member)}</p> * * @param hol the head of loop * @return a set of nodes belonging to that loop */ public Set<V> getNodesOfLoop(V hol) { Set<V> nodes = new HashSet<V>(); for (V node : graph.vertexSet()) { Set<V> col = getLoopColor(node); if (col != null && col.contains(hol)) nodes.add(node); } return nodes; } /** * Get set of back edges * * @return */ public Set<E> getBackEdges() { if (backEdges != null) return backEdges; backEdges = new HashSet<E>(); if (backEdgesByHOL == null) analyse(); for (List<E> edge : backEdgesByHOL.values()) { backEdges.addAll(edge); } return backEdges; } /** * Get source of back edges, grouped by head of loop * * @return a map from head-of-loop nodes to back-edge source vertices. */ public Map<V, List<E>> getBackEdgesByHOL() { if (backEdgesByHOL == null) analyse(); return backEdgesByHOL; } public List<E> getBackEdgesTo(V hol) { return backEdgesByHOL.get(hol); } /** * test whether the given edge is a "back-edge" * * @param edge the edge to test * @return */ public boolean isBackEdge(E edge) { return getBackEdges().contains(edge); } /** * An edge E is an <i>exit edge</i> of an loop, if the source of E * is part of the loop, but its target is not. * * @return a map from head-of-loop nodes to that loop's exit edges */ public Map<V, List<E>> getExitEdges() { if (this.exitEdges == null) analyse(); return this.exitEdges; } public Set<V> getLoopEntrySet(E edge) { /* no loops */ if (getHeadOfLoops().isEmpty()) return new HashSet<V>(); Set<V> setSource = getLoopColor(graph.getEdgeSource(edge)); Set<V> setTarget = new TreeSet<V>(getLoopColor(graph.getEdgeTarget(edge))); setTarget.removeAll(setSource); return setTarget; } public Collection<E> getExitEdgesOf(V hol) { return getExitEdges().get(hol); } public Set<V> getLoopExitSet(E edge) { /* no loops */ if (getHeadOfLoops().isEmpty()) return new HashSet<V>(); Set<V> setSource = new TreeSet<V>(getLoopColor(graph.getEdgeSource(edge))); Set<V> setTarget = getLoopColor(graph.getEdgeTarget(edge)); setSource.removeAll(setTarget); return setSource; } public Set<V> getLoopColor(V node) { if (this.getHeadOfLoops().isEmpty()) return new HashSet<V>(); Set<V> color = getLoopColors().get(node); if (color == null) { /* Unreachable Code */ return new HashSet<V>(); } return color; } public static class IterationBranchLabel<V> extends Pair<Set<V>, Set<V>> { private static final long serialVersionUID = 1L; public IterationBranchLabel(Set<V> fst, Set<V> snd) { super(fst, snd); } public Set<V> getContinues() { return first(); } public Set<V> getExits() { return second(); } @SuppressWarnings({"AccessingNonPublicFieldOfAnotherObject"}) public void mergeLabel(IterationBranchLabel<V> other) { this.first.addAll(other.first); this.second.addAll(other.second); } public boolean isEmpty() { return this.first.isEmpty() && this.second.isEmpty(); } } /** * Return a map from edges to iteration branch labels <code>(continue n, break N)</code> * <p> * Iteration branches are nodes s.t. following different edges leads to different * looping behavior. * An edge is marked as <code>continue n</code> when following this edge inevitably leads * to another iteration of loop <code>n</code>. * An edges is marked as <code>break n</code> when following this edge inevitably leads to * leaving loop <code>n</code> * </p> * <p>Nodes are traversed in reverse topological order of the graph without backedges</p> * * @return a map from edges to iteration branch labels */ public Map<E, IterationBranchLabel<V>> getIterationBranchEdges() { if (iterationBranchEdges != null) return iterationBranchEdges; Set<V> hols = this.getHeadOfLoops(); iterationBranchEdges = new HashMap<E, IterationBranchLabel<V>>(); Map<V, IterationBranchLabel<V>> nodeLabels = new HashMap<V, IterationBranchLabel<V>>(); if (hols.isEmpty()) return iterationBranchEdges; List<V> rTopTrav = this.topOrder.getTopologicalTraversal(); Collections.reverse(rTopTrav); for (V source : rTopTrav) { /* mark edges */ if (loopColors.get(source).isEmpty()) continue; IterationBranchLabel<V> first = null; boolean isIterationBranch = false; for (E edge : graph.outgoingEdgesOf(source)) { V target = graph.getEdgeTarget(edge); Set<V> contLoop = new HashSet<V>(); if (hols.contains(target)) { if (backEdgesByHOL.get(target).contains(edge)) { contLoop.add(target); } } Set<V> breakLoops = new HashSet<V>(loopColors.get(source)); breakLoops.removeAll(loopColors.get(target)); IterationBranchLabel<V> key = new IterationBranchLabel<V>(contLoop, breakLoops); if (nodeLabels.containsKey(target)) { key.mergeLabel(nodeLabels.get(target)); } if (!key.isEmpty()) { iterationBranchEdges.put(edge, key); } if (first == null) first = key; else if (!key.equals(first)) isIterationBranch = true; } /* TODO: A better implementation would use postdominators */ if (!isIterationBranch && first != null) { nodeLabels.put(source, first); for (E edge : graph.outgoingEdgesOf(source)) { iterationBranchEdges.remove(edge); } } } return iterationBranchEdges; } /** * return an acyclic subgraph which only contains nodes from the given loop, * and no back-edges at all * * @param hol * @return */ public DirectedGraph<V, E> getLinearSubgraph(V hol) { Set<V> nodesOfInterest; if (hol == null) nodesOfInterest = this.graph.vertexSet(); else nodesOfInterest = this.getNodesOfLoop(hol); Set<E> edgesOfInterest = new HashSet<E>(); for (E edge : this.graph.edgeSet()) { if (this.getBackEdges().contains(edge)) continue; V src = graph.getEdgeSource(edge); if (!nodesOfInterest.contains(src)) continue; V target = graph.getEdgeTarget(edge); if (!nodesOfInterest.contains(target)) continue; edgesOfInterest.add(edge); } return new DirectedSubgraph<V, E>(graph, nodesOfInterest, edgesOfInterest); } /* EXPERIMENTAL - NOT USED follows */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* * To unpeel a loop, we create a copy of the subgraph of all colored nodes. * Then we redirect the ingoing edges of the head-of-loop to the copy of head-of-loop, * and the back edges in the copied subgraph to the old head of loop. * * We need to provide callbacks when copying vertices/edges */ /** * TODO: move unpeel loops * Unpeel the loop given by the headOfLoop vertex * - modifies graph (adds new vertices via factory) * - updates loopColors * - invalidates topOrder,doms, loopNestForest, headOfLoops */ public void unpeelLoop(V headOfLoop, VertexFactory<V> factory) { /* invalidate */ this.topOrder = null; this.loopNestForest = null; this.backEdgesByHOL = null; /* step 1: find all vertices involved in the loop */ Set<V> coloredVertices = new TreeSet<V>(); for (V v : this.graph.vertexSet()) { if (this.loopColors.get(v).contains(headOfLoop)) { coloredVertices.add(v); } } /* step 2: create a copy of the subgraph, backedges linking to the original HOL */ Map<V, V> vertexCopies = new HashMap<V, V>(); Set<V> copiedVertices = new TreeSet<V>(); for (V loopVertex : coloredVertices) { V copy = factory.createVertex(); vertexCopies.put(loopVertex, copy); copiedVertices.add(copy); graph.addVertex(copy); // The copied vertex has the same loop color as the old one, except for the // given head of loop Set<V> loopColorCopy = new TreeSet<V>(); loopColorCopy.addAll(this.loopColors.get(loopVertex)); loopColorCopy.remove(headOfLoop); loopColors.put(copy, loopColorCopy); } for (V loopVertex : coloredVertices) { for (E outgoing : graph.outgoingEdgesOf(loopVertex)) { V target = graph.getEdgeTarget(outgoing); V srcCopy = vertexCopies.get(loopVertex); // V -> HOL ==> V' -> HOL if (target.equals(headOfLoop)) { graph.addEdge(srcCopy, target); } // V -> W | W \notin LoopSugraph ==> V' -> W else if (!coloredVertices.contains(target)) { graph.addEdge(srcCopy, target); } // V -> W | W \in LoopSubgraph ==> V' -> W' else { graph.addEdge(srcCopy, vertexCopies.get(target)); } } } /* Step 3: Update headOfLoops, loopColors */ /* Step 4: Redirect ingoing, not backlink edges to HOL to copy-of-HOL */ V headOfLoopCopy = vertexCopies.get(headOfLoop); Vector<E> incomingEdges = new Vector<E>(); incomingEdges.addAll(graph.incomingEdgesOf(headOfLoop)); for (E incoming : incomingEdges) { V src = graph.getEdgeSource(incoming); if (!coloredVertices.contains(src) && !copiedVertices.contains(src)) { graph.removeEdge(incoming); graph.addEdge(src, headOfLoopCopy); } } } } ///* headers last */ //DirectedGraph<V,DefaultEdge> travGraph = new DefaultDirectedGraph<V,DefaultEdge>(DefaultEdge.class); //for(V v : graph.vertexSet()) { // travGraph.addVertex(v); //} //Set<V> hols = this.getHeadOfLoops(); //for(E e: graph.edgeSet()) { // V src = graph.getEdgeSource(e); // V target = graph.getEdgeTarget(e); // if(hols.contains(src)) { // V hol = src; // if(! getLoopColor(target).contains(hol)) { // travGraph.addEdge(src,target); // continue; // } // /* Special case: src is hol, target is part of loop */ // Stack<V> todo = new Stack<V>(); // Vector<V> newPreds = new Vector<V>(); // todo.add(hol); // while(! todo.empty()) { // V pred = todo.pop(); // if(hols.contains(pred)) { // for(E toHOL : graph.incomingEdgesOf(pred)) { // V predPred = graph.getEdgeSource(toHOL); // if(getLoopColor(predPred).contains(pred)) continue; // todo.push(predPred); // } // } else { // newPreds.add(pred); /* not a hol */ // } // } // for(V newPred : newPreds) { // travGraph.addEdge(newPred,target); // } // } else { // travGraph.addEdge(src,target); // } //} //for(V hol : hols) { // for(E exitEdge : this.exitEdges.get(hol)) { // V exit = graph.getEdgeTarget(exitEdge); // travGraph.addEdge(hol, exit); // } //} //return travGraph;