//----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //----------------------------------------------------------------------------- using System; using System.Collections.Generic; using Microsoft.SpecSharp.Collections; using Microsoft.Contracts; using System.Text; // for StringBuilder using System.Diagnostics.Contracts; namespace Graphing { internal static class Util { private static string/*!*/ ListToString(IEnumerable xs) { Contract.Ensures(Contract.Result() != null); StringBuilder sb = new StringBuilder(); sb.Append("["); bool first = true; foreach (T/*!*/ x in xs) { Contract.Assert(x != null); if (!first) sb.Append(", "); sb.Append(x.ToString()); first = false; } sb.Append("]"); return sb.ToString(); } public static string/*!*/ MapToString(Dictionary> d) { Contract.Ensures(Contract.Result() != null); StringBuilder sb = new StringBuilder(); sb.Append("{"); bool first = true; foreach (KeyValuePair> de in d) { if (!first) sb.Append(", "); sb.Append(cce.NonNull(de.Key).ToString()); sb.Append("~>"); sb.Append(ListToString(de.Value)); first = false; } sb.Append("}"); return sb.ToString(); } } // own struct to represent possibly undefined values, because Mono does // not like arrays with element type T! or T? public struct Maybe { private T Value; public bool IsSet; // initialised with false by the default ctor public T Val { get { Contract.Assume(IsSet); return Value; } set { Value = value; IsSet = true; } } public void UnSet() { IsSet = false; } } internal class DomRelation { // doms maps (unique) node numbers to the node numbers of the immediate dominator // to use it on Nodes, one needs the two way mapping between nodes and their numbers. private int[] doms; // 0 is unused: means undefined // here are the two mappings private Maybe[] postOrderNumberToNode; private Dictionary nodeToPostOrderNumber; private int sourceNum; // (number for) root of the graph private Node source; // root of the graph private Graph graph; private Dictionary> immediateDominatorMap; [NotDelayed] internal DomRelation(Graph g, Node source) { this.graph = g; // slot 0 not used: nodes are numbered from 1 to n so zero // can represent undefined. this.source = source; //:base(); this.NewComputeDominators(); } public Dictionary> ImmediateDominatorMap { get { Contract.Assume(this.immediateDominatorMap != null); return this.immediateDominatorMap; } } public bool DominatedBy(Node dominee, Node dominator) { Contract.Assume(this.nodeToPostOrderNumber != null); Contract.Assume(this.doms != null); int domineeNum = this.nodeToPostOrderNumber[dominee]; int dominatorNum = this.nodeToPostOrderNumber[dominator]; if (domineeNum == dominatorNum) return true; int currentNodeNum = this.doms[domineeNum]; do { if (currentNodeNum == dominatorNum) return true; currentNodeNum = this.doms[currentNodeNum]; } while (currentNodeNum != this.sourceNum); return false; } private Dictionary> domMap = null; [Pure] public override string ToString() { Contract.Assume(this.doms != null); int[] localDoms = this.doms; Contract.Assume(this.postOrderNumberToNode != null); if (domMap == null) { domMap = new Dictionary>(); for (int i = 1; i < localDoms.Length; i++) { // 0 slot is not used int domineeNum = i; int currentNodeNum = domineeNum; List dominators = new List(); while (currentNodeNum != this.sourceNum) { dominators.Add(this.postOrderNumberToNode[currentNodeNum].Val); currentNodeNum = this.doms[currentNodeNum]; } dominators.Add(this.postOrderNumberToNode[this.sourceNum].Val); domMap.Add(this.postOrderNumberToNode[i].Val, dominators); } } StringBuilder sb = new StringBuilder(); sb.Append("{"); bool first = true; foreach (KeyValuePair> de in domMap) { if (!first) sb.Append(", "); sb.Append(cce.NonNull(de.Key).ToString()); sb.Append("~>"); sb.Append(ListToString(de.Value)); first = false; } sb.Append("}"); return sb.ToString(); } private void PrintIntArray(int[] xs) { Console.Write("["); for (int i = 0; i < xs.Length; i++) { if (0 < i) Console.Write(", "); Console.Write(xs[i]); } Console.WriteLine("]"); } public void PrintList(IEnumerable xs) { Console.Write("["); int i = 0; foreach (T/*!*/ x in xs) { Contract.Assert(x != null); if (0 < i) Console.Write(", "); Console.Write(x.ToString()); i++; } Console.WriteLine("]"); } public string/*!*/ ListToString(IEnumerable xs) { Contract.Ensures(Contract.Result() != null); StringBuilder sb = new StringBuilder(); sb.Append("["); bool first = true; foreach (T/*!*/ x in xs) { Contract.Assert(x != null); if (!first) sb.Append(", "); sb.Append(x.ToString()); first = false; } sb.Append("]"); return sb.ToString(); } // Keith D. Cooper, Timothy J. Harvey, Ken Kennedy, "A Simple, Fast Dominance Algorithm ", Software Practice and Experience, 2001. // http://citeseer.ist.psu.edu/cooper01simple.html private void NewComputeDominators() { int n = this.graph.Nodes.Count; this.postOrderNumberToNode = new Maybe[n + 1]; this.nodeToPostOrderNumber = new Dictionary(); Dictionary visited = new Dictionary(n); int currentNumber = 1; Contract.Assume(this.source != null); this.PostOrderVisit(this.source, visited, ref currentNumber); this.sourceNum = this.nodeToPostOrderNumber[source]; // for (int i = 1; i <= n; i++){ Console.WriteLine(postOrderNumberToNode[i]); } this.doms = new int[n + 1]; // 0 is unused: means undefined Node start_node = this.source; this.doms[this.nodeToPostOrderNumber[start_node]] = this.nodeToPostOrderNumber[start_node]; bool changed = true; // PrintIntArray(doms); while (changed) { changed = false; // for all nodes, b, in reverse postorder (except start_node) for (int nodeNum = n - 1; 1 <= nodeNum; nodeNum--) { Node b = this.postOrderNumberToNode[nodeNum].Val; IEnumerable predecessors = this.graph.Predecessors(b); // find a predecessor (i.e., a higher number) for which // the doms array has been set int new_idom = 0; int first_processed_predecessor = 0; #region new_idom <- number of first (processed) predecessor of b (pick one) foreach (Node p in predecessors) { if (this.doms[this.nodeToPostOrderNumber[p]] != 0) { int x = this.nodeToPostOrderNumber[p]; new_idom = x; first_processed_predecessor = x; break; } } #endregion #region for all other predecessors, p, of b foreach (Node p in predecessors) { if (this.nodeToPostOrderNumber[p] == first_processed_predecessor) { continue; } if (this.doms[this.nodeToPostOrderNumber[p]] != 0) new_idom = intersect(this.nodeToPostOrderNumber[p], new_idom, this.doms); } #endregion if (this.doms[this.nodeToPostOrderNumber[b]] != new_idom) { this.doms[this.nodeToPostOrderNumber[b]] = new_idom; changed = true; } } } #region Populate the Immediate Dominator Map int sourceNum = this.nodeToPostOrderNumber[this.source]; immediateDominatorMap = new Dictionary>(); for (int i = 1; i <= n; i++) { Node node = this.postOrderNumberToNode[i].Val; Node idomNode = this.postOrderNumberToNode[this.doms[i]].Val; if (i == sourceNum && this.doms[i] == sourceNum) { continue; } if (immediateDominatorMap.ContainsKey(idomNode)) { immediateDominatorMap[idomNode].Add(node); } else { List l = new List(); l.Add(node); immediateDominatorMap.Add(idomNode, l); } } #endregion } private int intersect(int b1, int b2, int[] doms) { int finger1 = b1; int finger2 = b2; while (finger1 != finger2) { while (finger1 < finger2) { finger1 = doms[finger1]; } while (finger2 < finger1) { finger2 = doms[finger2]; } } return finger1; } private void PostOrderVisit(Node/*!*/ n, Dictionary visited, ref int currentNumber) { Contract.Requires(n != null); if (visited.ContainsKey(n)) return; visited[n] = true; foreach (Node/*!*/ child in this.graph.Successors(n)) { Contract.Assert(child != null); PostOrderVisit(child, visited, ref currentNumber); } Contract.Assume(this.postOrderNumberToNode != null); Contract.Assume(this.nodeToPostOrderNumber != null); this.postOrderNumberToNode[currentNumber].Val = n; this.nodeToPostOrderNumber[n] = currentNumber; currentNumber++; return; } } public class Graph { private Set> es; private Set ns; private Node source; private bool reducible; private Set headers; private Map> backEdgeNodes; private Map, Set> naturalLoops; private DomRelation dominatorMap = null; private Dictionary> predCache = new Dictionary>(); private Dictionary> succCache = new Dictionary>(); private bool predComputed; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(es == null || Contract.ForAll(es, p => p.First != null && p.Second != null)); Contract.Invariant(naturalLoops == null || Contract.ForAll(naturalLoops.Keys, p => p.Second != null && p.First != null)); } private class PreHeader { Node/*!*/ myHeader; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(myHeader != null); } internal PreHeader(Node/*!*/ h) { Contract.Requires(h != null); myHeader = h; } [Pure] public override string/*!*/ ToString() { Contract.Ensures(Contract.Result() != null); return "#" + myHeader.ToString(); } } public Graph(Set> edges) { Contract.Requires(cce.NonNullElements(edges) && Contract.ForAll(edges, p => p.First != null && p.Second != null)); es = edges; // original A# //ns = Set{ x : in es } + Set{ y : in es }; // closest Spec# //ns = new Set{ Pair p in edges; p.First } + new Set{ Pair p in edges; p.Second }; // Set temp = new Set(); foreach (Pair p in edges) { Contract.Assert(p.First != null); temp.Add(p.First); Contract.Assert(p.Second != null); temp.Add(p.Second); } ns = temp; } public Graph() { es = new Set>(); ns = new Set(); } // BUGBUG: Set.ToString() should return a non-null string [Pure] public override string/*!*/ ToString() { return "" + es.ToString(); } public void AddSource(Node/*!*/ x) { Contract.Requires(x != null); // BUGBUG: This generates bad code in the compiler //ns += new Set{x}; ns.Add(x); source = x; } public void AddEdge(Node/*!*/ source, Node/*!*/ dest) { Contract.Requires(source != null); Contract.Requires(dest != null); //es += Set{}; //ns += Set{source, dest}; es.Add(new Pair(source, dest)); ns.Add(source); ns.Add(dest); predComputed = false; } public Set Nodes { get { return ns; } } public IEnumerable> Edges { get { Contract.Ensures(cce.NonNullElements(Contract.Result>>()) && Contract.ForAll(Contract.Result>>(), n => n.First != null && n.Second != null)); return es; } } public bool Edge(Node/*!*/ x, Node/*!*/ y) { Contract.Requires(x != null); Contract.Requires(y != null); // original A# // return in es; return es.Contains(new Pair(x, y)); } private void ComputePredSuccCaches() { if (predComputed) return; predComputed = true; predCache = new Dictionary>(); succCache = new Dictionary>(); foreach (Node n in Nodes) { predCache[n] = new Set(); succCache[n] = new Set(); } foreach (Pair p in Edges) { Contract.Assert(p.First != null); Contract.Assert(p.Second != null); Set tmp; tmp = predCache[p.Second]; tmp.Add(p.First); predCache[p.Second] = tmp; tmp = succCache[p.First]; tmp.Add(p.Second); succCache[p.First] = tmp; } } internal IEnumerable Predecessors(Node n) { // original A# //Set result = Set{ x : x in Nodes, Edge(x,n) }; ComputePredSuccCaches(); return predCache[n]; } internal IEnumerable Successors(Node n) { ComputePredSuccCaches(); return succCache[n]; } internal DomRelation /*Map>*/ DominatorMap { get { Contract.Assert(source != null); if (this.dominatorMap == null) { this.dominatorMap = new DomRelation(this, this.source); } return this.dominatorMap; } } public Dictionary> ImmediateDominatorMap { get { Contract.Assert(source != null); if (this.dominatorMap == null) { this.dominatorMap = new DomRelation(this, this.source); } return this.dominatorMap.ImmediateDominatorMap; } } public List ImmediatelyDominatedBy(Node/*!*/ n) { Contract.Requires(n != null); List dominees; this.ImmediateDominatorMap.TryGetValue(n, out dominees); return dominees == null ? new List() : dominees; } public IEnumerable TopologicalSort() { bool acyclic; List sortedList; this.TarjanTopSort(out acyclic, out sortedList); return acyclic ? sortedList : new List(); } // From Tarjan 1972 public void TarjanTopSort(out bool acyclic, out List sortedNodes) { int n = this.Nodes.Count; if (n == 0) { acyclic = true; sortedNodes = new List(); return; } int[] incomingEdges = new int[n]; // need an arbitrary numbering for the nodes to use as indices into // the arrays used within this algorithm Dictionary nodeToNumber = new Dictionary(n); Maybe[] numberToNode = new Maybe[n]; int counter = 0; foreach (Node node in this.Nodes) { numberToNode[counter].Val = node; nodeToNumber[node] = counter; counter++; } foreach (Pair e in this.Edges) { Contract.Assert(e.First != null); Contract.Assert(e.Second != null); Node/*!*/ target = e.Second; incomingEdges[nodeToNumber[target]]++; } List sorted = new List(); int sortedIndex = 0; while (sortedIndex < n) { // find a root (i.e., its index) int rootIndex = -1; for (int i = 0; i < n; i++) { if (incomingEdges[i] == 0) { rootIndex = i; break; } } if (rootIndex == -1) { acyclic = false; sortedNodes = new List(); return; } // mark root so it won't be used again incomingEdges[rootIndex] = -1; Node root = numberToNode[rootIndex].Val; sorted.Add(root); ++sortedIndex; foreach (Node s in this.Successors(root)) { incomingEdges[nodeToNumber[s]]--; } } acyclic = true; sortedNodes = sorted; return; } private IEnumerable OldTopologicalSort() { Pair> result = this.TopSort(); return result.First ? result.Second : (IEnumerable)new Seq(); } // From AsmL distribution example private Pair> TopSort() { Seq S = new Seq(); Set V = this.Nodes; Set X = new Set(); foreach (Node/*!*/ n in V) { Contract.Assert(n != null); X.Add(n); } bool change = true; while (change) // invariant: X = V - S { change = false; if (X.Count > 0) { foreach (Node/*!*/ n in X) { Contract.Assert(n != null); // see if n has any incoming edges from any other node in X bool inDegreeZero = true; foreach (Node/*!*/ u in X) { Contract.Assert(u != null); if (this.Edge(u, n)) { inDegreeZero = false; break; // no point looking further } } if (inDegreeZero) { S.Add(n); X.Remove(n); change = true; break; // might as well go back and start looking through X from the beginning } } // Then we made it all the way through X without finding a source node if (!change) { return new Pair>(false, new Seq()); } } } return new Pair>(true, S); } public static bool Acyclic(Graph g, Node source) { bool acyclic; List sortedList; g.TarjanTopSort(out acyclic, out sortedList); return acyclic; } // // [Dragon, pp. 670--671] // returns map D s.t. d in D(n) iff d dom n // static private Map> OldComputeDominators(Graph g, Node/*!*/ source) { Contract.Requires(source != null); Contract.Assert(g.source != null); Set N = g.Nodes; Set nonSourceNodes = N - new Set(source); Map> D = new Map>(); D[source] = new Set(source); foreach (Node/*!*/ n in nonSourceNodes) { Contract.Assert(n != null); D[n] = N; } bool change = true; while (change) { change = false; foreach (Node/*!*/ n in nonSourceNodes) { Contract.Assert(n != null); // original A# //Set> allPreds = new Set>{ Node p in this.Predecessors(n) ; D[p] }; Set> allPreds = new Set>(); foreach (Node/*!*/ p in g.Predecessors(n)) { Contract.Assert(p != null); allPreds.Add(D[p]); } Set temp = new Set(n) + Set.BigIntersect(allPreds); if (temp != D[n]) { change = true; D[n] = temp; } } } return D; } // [Dragon, Fig. 10.15, p. 604. Algorithm for constructing the natural loop.] static Set NaturalLoop(Graph g, Pair backEdge) { Contract.Requires(backEdge.First != null && backEdge.Second != null); Node/*!*/ n = backEdge.First; Node/*!*/ d = backEdge.Second; Seq stack = new Seq(); Set loop = new Set(d); if (!n.Equals(d)) // then n is not in loop { loop.Add(n); stack = new Seq(n) + stack; // push n onto stack } while (stack.Count > 0) // not empty { Node m = stack.Head; stack = stack.Tail; // pop stack foreach (Node/*!*/ p in g.Predecessors(m)) { Contract.Assert(p != null); if (!(loop.Contains(p))) { loop.Add(p); stack = new Seq(p) + stack; // push p onto stack } } } return loop; } internal struct ReducibleResult { internal bool reducible; internal Set headers; internal Map> backEdgeNodes; internal Map, Set> naturalLoops; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Contract.ForAll(naturalLoops.Keys, p => p.First != null && p.Second != null)); } internal ReducibleResult(bool b, Set headers, Map> backEdgeNodes, Map, Set> naturalLoops) { Contract.Requires(naturalLoops == null || Contract.ForAll(naturalLoops.Keys, Key => Key.First != null && Key.Second != null)); this.reducible = b; this.headers = headers; this.backEdgeNodes = backEdgeNodes; this.naturalLoops = naturalLoops; } } // [Dragon, p. 606] static ReducibleResult ComputeReducible(Graph g, Node source) { // first, compute the dom relation DomRelation /*Map>*/ D = g.DominatorMap; return ComputeReducible(g, source, D); } // [Dragon, p. 606] static ReducibleResult ComputeReducible(Graph g, Node source, DomRelation/*!*/ DomRelation) { Contract.Requires(DomRelation != null); //Console.WriteLine("[" + DateTime.Now +"]: begin ComputeReducible"); IEnumerable> edges = g.Edges; Contract.Assert(Contract.ForAll(edges, n => n.First != null && n.Second != null)); Set> backEdges = new Set>(); Set> nonBackEdges = new Set>(); foreach (Pair e in edges) { Contract.Assert(e.First != null); Contract.Assert(e.Second != null); Node x = e.First; Node y = e.Second; // so there is an edge from x to y if (DomRelation.DominatedBy(x, y)) { // y dom x: which means y dominates x backEdges.Add(e); } else { nonBackEdges.Add(e); } } if (!Acyclic(new Graph(nonBackEdges), source)) { return new ReducibleResult(false, new Set(), new Map>(), new Map, Set>()); } else { // original A#: //Set headers = Set{ d : in backEdges }; Set headers = new Set(); foreach (Pair e in backEdges) { Contract.Assert(e.First != null); Contract.Assert(e.Second != null); headers.Add(e.Second); } // original A#: //Map> backEdgeNodes = Map{ h -> bs : h in headers, bs = Set{ b : in backEdges, x == h } }; Map> backEdgeNodes = new Map>(); foreach (Node/*!*/ h in headers) { Contract.Assert(h != null); Set bs = new Set(); foreach (Pair backedge in backEdges) { Contract.Assert(backedge.First != null); Contract.Assert(backedge.Second != null); if (backedge.Second.Equals(h)) { bs.Add(backedge.First); } } backEdgeNodes.Add(h, bs); } // original A#: //Map,Set> naturalLoops = Map{ e -> NaturalLoop(g,e) : e in backEdges }; Map, Set> naturalLoops = new Map, Set>(); foreach (Pair e in backEdges) { Contract.Assert(e.First != null && e.Second != null); naturalLoops.Add(e, NaturalLoop(g, e)); } //Console.WriteLine("[" + DateTime.Now +"]: end ComputeReducible"); return new ReducibleResult(true, headers, backEdgeNodes, naturalLoops); } } public bool Reducible { get { return reducible; } } public IEnumerable Headers { get { return headers; } } public IEnumerable BackEdgeNodes(Node/*!*/ h) { Contract.Requires(h != null); // original A#: //return h in backEdgeNodes ? backEdgeNodes[h] : null; return (backEdgeNodes.ContainsKey(h) ? backEdgeNodes[h] : (IEnumerable)new Seq()); } public IEnumerable NaturalLoops(Node/*!*/ header, Node/*!*/ backEdgeNode) { Contract.Requires(header != null); Contract.Requires(backEdgeNode != null); Pair e = new Pair(backEdgeNode, header); return naturalLoops.ContainsKey(e) ? naturalLoops[e] : (IEnumerable)new Seq(); } public void ComputeLoops() { ReducibleResult r = ComputeReducible(this, this.source); this.reducible = r.reducible; this.headers = r.headers; this.backEdgeNodes = r.backEdgeNodes; this.naturalLoops = r.naturalLoops; return; } public IEnumerable SortHeadersByDominance() { Graph dag = new Graph(); foreach (Node b in headers) { dag.AddSource(b); foreach (Node c in headers) { if (b.Equals(c)) continue; if (DominatorMap.DominatedBy(b, c)) { Debug.Assert(!DominatorMap.DominatedBy(c, b)); dag.AddEdge(b, c); } } } return dag.TopologicalSort(); } } // end: class Graph public class GraphProgram { static void TestGraph(T/*!*/ source, params Pair[] edges) { Contract.Requires(source != null); Contract.Requires(Contract.ForAll(edges, pair => pair.First != null && pair.Second != null)); Set> es = new Set>(); foreach (Pair e in edges) { Contract.Assert(e.First != null && e.Second != null); es.Add(e); } Graph g = new Graph(es); g.AddSource(source); Console.WriteLine("G = " + g); g.ComputeLoops(); Console.WriteLine("G's Dominator Map = " + g.DominatorMap); Console.WriteLine("G's Immediate Dominator Map = " + Util.MapToString(g.ImmediateDominatorMap)); Console.WriteLine("G is reducible: " + (g.Reducible ? "yes" : "no")); } static void Main(string[] args) //requires forall{string s in args; s != null}; { Console.WriteLine("Spec# says hello!"); // This generates bad IL -- need to fix a bug in the compiler //Graph g = new Graph(new Set>{ new Pair(1,2), new Pair(1,3), new Pair(2,3) }); Console.WriteLine(""); TestGraph('a', new Pair('a', 'b'), new Pair('a', 'c'), new Pair('b', 'c') ); Console.WriteLine(""); TestGraph('a', new Pair('a', 'b'), new Pair('a', 'c'), new Pair('b', 'd'), new Pair('c', 'e'), new Pair('c', 'f'), new Pair('d', 'e'), new Pair('e', 'd'), new Pair('e', 'f'), new Pair('f', 'e') ); Console.WriteLine(""); TestGraph('a', new Pair('a', 'b'), new Pair('a', 'c'), new Pair('b', 'c'), new Pair('c', 'b') ); Console.WriteLine(""); TestGraph(1, new Pair(1, 2), new Pair(1, 3), new Pair(2, 3) ); Console.WriteLine(""); TestGraph(1, new Pair(1, 2), new Pair(1, 3), new Pair(2, 3), new Pair(3, 2) ); Console.WriteLine(""); TestGraph(2, new Pair(2, 3), new Pair(2, 4), new Pair(3, 2) ); Console.WriteLine(""); TestGraph('a', new Pair('a', 'b'), new Pair('a', 'c'), new Pair('b', 'c'), new Pair('b', 'b') ); } } }