コード例 #1
0
ファイル: ReduceLoops.cs プロジェクト: csinkers/ualbion
    public static CfgLoop GetLoopInformation(ControlFlowGraph graph, List <int> nodes)
    {
        if (nodes == null)
        {
            throw new ArgumentNullException(nameof(nodes));
        }
        if (nodes.Count == 0)
        {
            throw new ArgumentException("Empty loop provided to GetLoopInformation", nameof(nodes));
        }

        var body   = new List <LoopPart>();
        var header = new LoopPart(nodes[0], true);
        var exits  = new HashSet <int>();

        // Determine if header can break out of the loop
        foreach (int child in graph.Children(nodes[0]))
        {
            if (nodes.Contains(child))
            {
                continue;
            }

            CfgEdge edgeLabel = graph.GetEdgeLabel(nodes[0], child);
            if (edgeLabel == CfgEdge.LoopSuccessor) // Loop successor pseudo-edges don't count for break-detection
            {
                continue;
            }

            header = new LoopPart(header.Index, true, Break: true, Negated: edgeLabel == CfgEdge.True);
            exits.Add(child);
        }

        for (int i = 1; i < nodes.Count; i++)
        {
            var  node       = nodes[i];
            bool isContinue = false;
            bool isBreak    = false;
            bool isTail     = true;
            bool negated    = false;

            foreach (int child in graph.Children(node))
            {
                // Func<string> vis = () => ToVis().AddPointer("i", node).AddPointer("child", child).ToString(); // For VS Code debug visualisation

                if (child == header.Index) // Jump to header = possible continue
                {
                    isContinue = true;
                }
                else if (nodes.Contains(child))
                {
                    isTail = false;
                }
                else
                {
                    negated = graph.GetEdgeLabel(node, child) == CfgEdge.False;
                    isBreak = true;
                    exits.Add(child);
                }
            }

            bool hasOutsideEntry = Enumerable.Any(graph.Parents(node), x => !nodes.Contains(x));
            body.Add(new LoopPart(node, false, isTail, isBreak, isContinue, hasOutsideEntry, negated));
        }

        var postDom  = graph.GetPostDominatorTree();
        int?mainExit = postDom.ImmediateDominator(header.Index);

        while (mainExit.HasValue && body.Any(x => !postDom.Dominates(mainExit.Value, x.Index)))
        {
            mainExit = postDom.ImmediateDominator(mainExit.Value);
        }

        if (body.Count(x => x.Tail) > 1) // Only allow one tail, pick one of the nodes with the longest path from the header.
        {
            var longestPaths = new Dictionary <int, int>();
            for (int i = 0; i < body.Count; i++)
            {
                var part = body[i];
                if (!part.Tail)
                {
                    continue;
                }
                var paths = graph.GetAllReachingPaths(header.Index, part.Index);
                longestPaths[i] = paths.Select(x => x.Count).Max();
            }

            var longestDistance = longestPaths.Values.Max();
            var winner          = longestPaths.First(x => x.Value == longestDistance).Key;
            foreach (var kvp in longestPaths)
            {
                if (kvp.Key == winner)
                {
                    continue;
                }
                var part = body[kvp.Key];
                body[kvp.Key] = new LoopPart(part.Index, part.Header, false, part.Break, part.Continue, part.OutsideEntry, part.Negated);
            }
        }

        return(new CfgLoop(header, body.ToImmutableList(), exits.ToImmutableList(), mainExit));
    }