Ejemplo n.º 1
0
 /// <summary>
 /// Finds a suitable single exit point for the specified loop.
 /// </summary>
 /// <remarks>This method must not write to the Visited flags on the CFG.</remarks>
 ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList <ControlFlowNode> naturalLoop)
 {
     if (!context.ControlFlowGraph.HasReachableExit(loopHead))
     {
         // Case 1:
         // There are no nodes n so that loopHead dominates a predecessor of n but not n itself
         // -> we could build a loop with zero exit points.
         ControlFlowNode exitPoint         = null;
         int             exitPointILOffset = -1;
         foreach (var node in loopHead.DominatorTreeChildren)
         {
             PickExitPoint(node, ref exitPoint, ref exitPointILOffset);
         }
         return(exitPoint);
     }
     else
     {
         // Case 2:
         // We need to pick our exit point so that all paths from the loop head
         // to the reachable exits run through that exit point.
         var cfg    = context.ControlFlowGraph.cfg;
         var revCfg = PrepareReverseCFG(loopHead);
         //ControlFlowNode.ExportGraph(cfg).Show("cfg");
         //ControlFlowNode.ExportGraph(revCfg).Show("rev");
         ControlFlowNode commonAncestor = revCfg[loopHead.UserIndex];
         Debug.Assert(commonAncestor.IsReachable);
         foreach (ControlFlowNode cfgNode in naturalLoop)
         {
             ControlFlowNode revNode = revCfg[cfgNode.UserIndex];
             if (revNode.IsReachable)
             {
                 commonAncestor = Dominance.FindCommonDominator(commonAncestor, revNode);
             }
         }
         ControlFlowNode exitPoint;
         while (commonAncestor.UserIndex >= 0)
         {
             exitPoint = cfg[commonAncestor.UserIndex];
             Debug.Assert(exitPoint.Visited == naturalLoop.Contains(exitPoint));
             if (exitPoint.Visited)
             {
                 commonAncestor = commonAncestor.ImmediateDominator;
                 continue;
             }
             else
             {
                 return(exitPoint);
             }
         }
         // least common dominator is the artificial exit node
         return(null);
     }
 }
Ejemplo n.º 2
0
        /// <summary>
        /// Finds a suitable single exit point for the specified loop.
        /// </summary>
        /// <returns>
        /// 1) If a suitable exit point was found: the control flow block that should be reached when breaking from the loop
        /// 2) If the loop should not have any exit point (extend by all dominated blocks): NoExitPoint
        /// 3) otherwise (exit point unknown, heuristically extend loop): null
        /// </returns>
        /// <remarks>This method must not write to the Visited flags on the CFG.</remarks>
        ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList <ControlFlowNode> naturalLoop, bool treatBackEdgesAsExits)
        {
            bool hasReachableExit = context.ControlFlowGraph.HasReachableExit(loopHead);

            if (!hasReachableExit && treatBackEdgesAsExits)
            {
                // If we're analyzing the switch, there's no reachable exit, but the loopHead (=switchHead) block
                // is also a loop head, we consider the back-edge a reachable exit for the switch.
                hasReachableExit = loopHead.Predecessors.Any(p => loopHead.Dominates(p));
            }
            if (!hasReachableExit)
            {
                // Case 1:
                // There are no nodes n so that loopHead dominates a predecessor of n but not n itself
                // -> we could build a loop with zero exit points.
                if (IsPossibleForeachLoop((Block)loopHead.UserData, out var exitBranch))
                {
                    if (exitBranch != null)
                    {
                        // let's see if the target of the exit branch is a suitable exit point
                        var cfgNode = loopHead.Successors.FirstOrDefault(n => n.UserData == exitBranch.TargetBlock);
                        if (cfgNode != null && loopHead.Dominates(cfgNode) && !context.ControlFlowGraph.HasReachableExit(cfgNode))
                        {
                            return(cfgNode);
                        }
                    }
                    return(NoExitPoint);
                }
                ControlFlowNode exitPoint         = null;
                int             exitPointILOffset = -1;
                foreach (var node in loopHead.DominatorTreeChildren)
                {
                    PickExitPoint(node, ref exitPoint, ref exitPointILOffset);
                }
                return(exitPoint);
            }
            else
            {
                // Case 2:
                // We need to pick our exit point so that all paths from the loop head
                // to the reachable exits run through that exit point.
                var cfg    = context.ControlFlowGraph.cfg;
                var revCfg = PrepareReverseCFG(loopHead, treatBackEdgesAsExits);
                //ControlFlowNode.ExportGraph(cfg).Show("cfg");
                //ControlFlowNode.ExportGraph(revCfg).Show("rev");
                ControlFlowNode commonAncestor = revCfg[loopHead.UserIndex];
                Debug.Assert(commonAncestor.IsReachable);
                foreach (ControlFlowNode cfgNode in naturalLoop)
                {
                    ControlFlowNode revNode = revCfg[cfgNode.UserIndex];
                    if (revNode.IsReachable)
                    {
                        commonAncestor = Dominance.FindCommonDominator(commonAncestor, revNode);
                    }
                }
                // All paths from within the loop to a reachable exit run through 'commonAncestor'.
                // However, this doesn't mean that 'commonAncestor' is valid as an exit point.
                // We walk up the post-dominator tree until we've got a valid exit point:
                ControlFlowNode exitPoint;
                while (commonAncestor.UserIndex >= 0)
                {
                    exitPoint = cfg[commonAncestor.UserIndex];
                    Debug.Assert(exitPoint.Visited == naturalLoop.Contains(exitPoint));
                    // It's possible that 'commonAncestor' is itself part of the natural loop.
                    // If so, it's not a valid exit point.
                    if (!exitPoint.Visited && ValidateExitPoint(loopHead, exitPoint))
                    {
                        // we found an exit point
                        return(exitPoint);
                    }
                    commonAncestor = commonAncestor.ImmediateDominator;
                }
                // least common post-dominator is the artificial exit node
                return(null);
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Finds a suitable single exit point for the specified loop.
        /// </summary>
        /// <returns>
        /// 1) If a suitable exit point was found: the control flow block that should be reached when breaking from the loop
        /// 2) If the loop should not have any exit point (extend by all dominated blocks): NoExitPoint
        /// 3) otherwise (exit point unknown, heuristically extend loop): null
        /// </returns>
        /// <remarks>This method must not write to the Visited flags on the CFG.</remarks>
        internal ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList <ControlFlowNode> naturalLoop)
        {
            bool hasReachableExit = HasReachableExit(loopHead);

            if (!hasReachableExit)
            {
                // Case 1:
                // There are no nodes n so that loopHead dominates a predecessor of n but not n itself
                // -> we could build a loop with zero exit points.
                if (IsPossibleForeachLoop((Block)loopHead.UserData, out var exitBranch))
                {
                    if (exitBranch != null)
                    {
                        // let's see if the target of the exit branch is a suitable exit point
                        var cfgNode = loopHead.Successors.FirstOrDefault(n => n.UserData == exitBranch.TargetBlock);
                        if (cfgNode != null && loopHead.Dominates(cfgNode) && !context.ControlFlowGraph.HasReachableExit(cfgNode))
                        {
                            return(cfgNode);
                        }
                    }
                    return(NoExitPoint);
                }
                ControlFlowNode exitPoint         = null;
                int             exitPointILOffset = -1;
                ConsiderReturnAsExitPoint((Block)loopHead.UserData, ref exitPoint, ref exitPointILOffset);
                foreach (var node in loopHead.DominatorTreeChildren)
                {
                    PickExitPoint(node, ref exitPoint, ref exitPointILOffset);
                }
                return(exitPoint);
            }
            else
            {
                // Case 2:
                // We need to pick our exit point so that all paths from the loop head
                // to the reachable exits run through that exit point.
                var cfg    = context.ControlFlowGraph.cfg;
                var revCfg = PrepareReverseCFG(loopHead, out int exitNodeArity);
                //ControlFlowNode.ExportGraph(cfg).Show("cfg");
                //ControlFlowNode.ExportGraph(revCfg).Show("rev");
                ControlFlowNode commonAncestor = revCfg[loopHead.UserIndex];
                Debug.Assert(commonAncestor.IsReachable);
                foreach (ControlFlowNode cfgNode in naturalLoop)
                {
                    ControlFlowNode revNode = revCfg[cfgNode.UserIndex];
                    if (revNode.IsReachable)
                    {
                        commonAncestor = Dominance.FindCommonDominator(commonAncestor, revNode);
                    }
                }
                // All paths from within the loop to a reachable exit run through 'commonAncestor'.
                // However, this doesn't mean that 'commonAncestor' is valid as an exit point.
                // We walk up the post-dominator tree until we've got a valid exit point:
                ControlFlowNode exitPoint;
                while (commonAncestor.UserIndex >= 0)
                {
                    exitPoint = cfg[commonAncestor.UserIndex];
                    Debug.Assert(exitPoint.Visited == naturalLoop.Contains(exitPoint));
                    // It's possible that 'commonAncestor' is itself part of the natural loop.
                    // If so, it's not a valid exit point.
                    if (!exitPoint.Visited && ValidateExitPoint(loopHead, exitPoint))
                    {
                        // we found an exit point
                        return(exitPoint);
                    }
                    commonAncestor = commonAncestor.ImmediateDominator;
                }
                // least common post-dominator is the artificial exit node
                // This means we're in one of two cases:
                // * The loop might have multiple exit points.
                //     -> we should return null
                // * The loop has a single exit point that wasn't considered during post-dominance analysis.
                //        (which means the single exit isn't dominated by the loop head)
                //     -> we should return NoExitPoint so that all code dominated by the loop head is included into the loop
                if (exitNodeArity > 1)
                {
                    return(null);
                }

                // Exit node is on the very edge of the tree, and isn't important for determining inclusion
                // Still necessary for switch detection to insert correct leave statements
                if (exitNodeArity == 1 && isSwitch)
                {
                    return(loopContext.GetBreakTargets(loopHead).Distinct().Single());
                }

                // If exitNodeArity == 0, we should maybe look test if our exits out of the block container are all compatible?
                // but I don't think it hurts to have a bit too much code inside the loop in this rare case.
                return(NoExitPoint);
            }
        }