예제 #1
0
파일: BlockSorter.cs 프로젝트: lanicon/Echo
        // -------------------------
        // Implementation rationale
        // -------------------------
        //
        // Two key observations are:
        //
        // - Topological orderings of nodes for directed acyclic graphs (DAGs) are orderings that "respect" dominance
        //   without actually needing to compute the entire dominator tree. Nodes sorted by dominance are easier
        //   to read, since they resemble structured flow more. If node A dominates B, it would mean that in the
        //   resulting ordering, node A will appear before node B, as it would appear in "normal" programs.
        //   For cyclic graphs we can simply ignore back-edges to turn the input graph into a DAG.
        //
        // - Paths constructed by fallthrough edges in the control flow graph cannot be broken up into smaller
        //   paths in the resulting node sequence without changing the code of the basic block (e.g. inserting a goto).
        //   This puts constraints on what kind of topological orderings we can construct.
        //
        // In this implementation, we create a "view" on the input control flow graph, that contracts nodes in a single
        // path induced by fallthrough edges into a single node. It is safe to put the nodes of this new graph in any
        // ordering without invalidating the requirements for fallthrough edges, since the new nodes never have outgoing
        // fallthrough edges by construction. The exception to this rule is the entry point node, which always has to
        // start at the beginning of the sequence. Therefore, doing a topological sorting starting at this entry point
        // node results in a valid sequence of basic blocks that is reasonably readable.
        //
        // There is still some form of "non-determinism" in the algorithm, as neighbours of each node are still somewhat
        // arbitrarily ordered.  As a result, an exit point block (e.g. a block with a return) might still end up
        // somewhere in the middle of the sequence, which can be somewhat counter-intuitive.

        /// <summary>
        /// Determines an ordering of nodes in the control flow graph in such a way that the basic blocks can be
        /// concatenated together in sequence, and still result in a valid execution of the original program.
        /// </summary>
        /// <param name="cfg">The control flow graph to pull the nodes from.</param>
        /// <typeparam name="TInstruction">The type of instructions stored in the graph.</typeparam>
        /// <returns>The ordering.</returns>
        public static IEnumerable <ControlFlowNode <TInstruction> > SortNodes <TInstruction>(
            this ControlFlowGraph <TInstruction> cfg)
        {
            var pathsView = DetermineUnbreakablePaths(cfg);
            var sorter    = new TopologicalSorter <ControlFlowNode <TInstruction> >(pathsView.GetImpliedNeighbours, true);

            return(sorter
                   .GetTopologicalSorting(cfg.Entrypoint)
                   .Reverse()
                   .SelectMany(n => pathsView.GetUnbreakablePath(n)));
        }
예제 #2
0
        /// <summary>
        /// Collects all dependency nodes recursively, and sorts them in a topological order such that the final collection
        /// of nodes can be executed sequentially.
        /// </summary>
        /// <param name="node">The node to find all dependencies for.</param>
        /// <param name="flags">Flags that influence the behaviour of the algorithm.</param>
        /// <typeparam name="T">The type of contents that each node contains.</typeparam>
        /// <returns>The topological ordering of all dependencies of the node.</returns>
        /// <exception cref="CyclicDependencyException">Occurs when there is a cyclic dependency in the graph.</exception>
        public static IEnumerable <DataFlowNode <T> > GetOrderedDependencies <T>(this DataFlowNode <T> node, DependencyCollectionFlags flags)
        {
            try
            {
                var topologicalSorting = new TopologicalSorter <DataFlowNode <T> >(GetSortedOutgoingEdges);
                return(topologicalSorting.GetTopologicalSorting(node));
            }
            catch (CycleDetectedException ex)
            {
                throw new CyclicDependencyException("Cyclic dependency was detected.", ex);
            }

            IReadOnlyList <DataFlowNode <T> > GetSortedOutgoingEdges(DataFlowNode <T> n)
            {
                var result = new List <DataFlowNode <T> >();

                // Prioritize stack dependencies over variable dependencies.
                if ((flags & DependencyCollectionFlags.IncludeStackDependencies) != 0)
                {
                    foreach (var dependency in n.StackDependencies)
                    {
                        if (dependency.HasKnownDataSources)
                        {
                            result.Add(dependency.First().Node);
                        }
                    }
                }

                if ((flags & DependencyCollectionFlags.IncludeVariableDependencies) != 0)
                {
                    foreach (var entry in n.VariableDependencies)
                    {
                        if (entry.Value.HasKnownDataSources)
                        {
                            result.Add(entry.Value.First().Node);
                        }
                    }
                }

                return(result);
            }
        }
예제 #3
0
        /// <summary>
        /// Constructs the tree of scopes and basic blocks based on the provided control flow graph.
        /// </summary>
        /// <param name="cfg">The control flow graph.</param>
        /// <returns>The root scope.</returns>
        public ScopeBlock <TInstruction> ConstructBlocks(ControlFlowGraph <TInstruction> cfg)
        {
            // Sort the nodes in topological order, where we ignore cyclic dependencies.
            var sorter = new TopologicalSorter <ControlFlowNode <TInstruction> >(ChildrenLister)
            {
                IgnoreCycles = true
            };

            var sorting = sorter
                          .GetTopologicalSorting(cfg.Entrypoint)
                          .Reverse()
#if DEBUG
                          .ToArray()
#endif
            ;

            // We maintain a stack of scope information. Every time we enter a new region, we enter a new scope,
            // and similarly, we leave a scope when we leave a region.

            var rootScope  = new ScopeBlock <TInstruction>();
            var scopeStack = new IndexableStack <ScopeInfo>();

            scopeStack.Push(new ScopeInfo(cfg, rootScope));

            // Add the nodes in the order of the sorting.
            foreach (var node in sorting)
            {
                var currentScope = scopeStack.Peek();
                if (node.ParentRegion != currentScope.Region)
                {
                    UpdateScopeStack(node, scopeStack);
                    currentScope = scopeStack.Peek();
                }

                currentScope.AddBlock(node.Contents);
            }

            return(rootScope);
        }