示例#1
0
        static HashSet <ControlFlowNode> FindLoopContent(HashSet <ControlFlowNode> scope, ControlFlowNode head)
        {
            var viaBackEdges = head.Predecessors.Where(p => head.Dominates(p));
            HashSet <ControlFlowNode> agenda = new HashSet <ControlFlowNode>(viaBackEdges);
            HashSet <ControlFlowNode> result = new HashSet <ControlFlowNode>();

            while (agenda.Count > 0)
            {
                ControlFlowNode addNode = agenda.First();
                agenda.Remove(addNode);

                if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode))
                {
                    foreach (var predecessor in addNode.Predecessors)
                    {
                        agenda.Add(predecessor);
                    }
                }
            }
            if (scope.Contains(head))
            {
                result.Add(head);
            }

            return(result);
        }
示例#2
0
        static HashSet <ControlFlowNode> FindLoopContent(HashSet <ControlFlowNode> scope, ControlFlowNode head)
        {
            HashSet <ControlFlowNode> agenda = new HashSet <ControlFlowNode>();

            for (int i = 0; i < head.Incoming.Count; i++)
            {
                var p = head.Incoming[i].Source;
                if (head.Dominates(p))
                {
                    agenda.Add(p);
                }
            }
            HashSet <ControlFlowNode> result = new HashSet <ControlFlowNode>();

            while (agenda.Count > 0)
            {
                ControlFlowNode addNode = agenda.First();
                agenda.Remove(addNode);

                if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode))
                {
                    for (int i = 0; i < addNode.Incoming.Count; i++)
                    {
                        agenda.Add(addNode.Incoming[i].Source);
                    }
                }
            }
            if (scope.Contains(head))
            {
                result.Add(head);
            }

            return(result);
        }
示例#3
0
            private static ControlFlowNode FindContinue(ControlFlowNode loopHead)
            {
                // potential continue target
                var pred = loopHead.Predecessors.OnlyOrDefault(p => p != loopHead && loopHead.Dominates(p));

                if (pred == null)
                {
                    return(loopHead);
                }

                // match for loop increment block
                if (pred.Successors.Count == 1)
                {
                    if (HighLevelLoopTransform.MatchIncrementBlock((Block)pred.UserData, out var target) && target == loopHead.UserData)
                    {
                        return(pred);
                    }
                }

                // match do-while condition
                if (pred.Successors.Count <= 2)
                {
                    if (HighLevelLoopTransform.MatchDoWhileConditionBlock((Block)pred.UserData, out var t1, out var t2) &&
                        (t1 == loopHead.UserData || t2 == loopHead.UserData))
                    {
                        return(pred);
                    }
                }

                return(loopHead);
            }
示例#4
0
        /// <summary>
        /// Validates an exit point.
        ///
        /// An exit point is invalid iff there is a node reachable from the exit point that
        /// is dominated by the loop head, but not by the exit point.
        /// (i.e. this method returns false iff the exit point's dominance frontier contains
        /// a node dominated by the loop head. but we implement this the slow way because
        /// we don't have dominance frontiers precomputed)
        /// </summary>
        /// <remarks>
        /// We need this because it's possible that there's a return block (thus reverse-unreachable node ignored by post-dominance)
        /// that is reachable both directly from the loop, and from the exit point.
        /// </remarks>
        bool ValidateExitPoint(ControlFlowNode loopHead, ControlFlowNode exitPoint)
        {
            var cfg = context.ControlFlowGraph;

            return(IsValid(exitPoint));

            bool IsValid(ControlFlowNode node)
            {
                if (!cfg.HasReachableExit(node))
                {
                    // Optimization: if the dominance frontier is empty, we don't need
                    // to check every node.
                    return(true);
                }
                foreach (var succ in node.Successors)
                {
                    if (loopHead != succ && loopHead.Dominates(succ) && !exitPoint.Dominates(succ))
                    {
                        return(false);
                    }
                }
                foreach (var child in node.DominatorTreeChildren)
                {
                    if (!IsValid(child))
                    {
                        return(false);
                    }
                }
                return(true);
            }
        }
示例#5
0
        ControlFlowNode[] PrepareReverseCFG(ControlFlowNode loopHead, bool treatBackEdgesAsExits)
        {
            ControlFlowNode[] cfg = context.ControlFlowGraph.cfg;
            ControlFlowNode[] rev = new ControlFlowNode[cfg.Length + 1];
            for (int i = 0; i < cfg.Length; i++)
            {
                rev[i] = new ControlFlowNode {
                    UserIndex = i, UserData = cfg[i].UserData
                };
            }
            ControlFlowNode exitNode = new ControlFlowNode {
                UserIndex = -1
            };

            rev[cfg.Length] = exitNode;
            for (int i = 0; i < cfg.Length; i++)
            {
                if (!loopHead.Dominates(cfg[i]))
                {
                    continue;
                }
                // Add reverse edges for all edges in cfg
                foreach (var succ in cfg[i].Successors)
                {
                    if (loopHead.Dominates(succ) && (!treatBackEdgesAsExits || loopHead != succ))
                    {
                        rev[succ.UserIndex].AddEdgeTo(rev[i]);
                    }
                    else
                    {
                        exitNode.AddEdgeTo(rev[i]);
                    }
                }
                if (context.ControlFlowGraph.HasDirectExitOutOfContainer(cfg[i]))
                {
                    exitNode.AddEdgeTo(rev[i]);
                }
            }
            Dominance.ComputeDominance(exitNode, context.CancellationToken);
            return(rev);
        }
示例#6
0
        /// <summary>
        /// Gets whether <c>potentialBranchInstruction</c> is a branch to a block
        /// that is dominated by <c>cfgNode</c>.
        /// If this function returns true, we replace the branch instruction with the block itself.
        /// </summary>
        bool IsUsableBranchToChild(ControlFlowNode cfgNode, ILInstruction potentialBranchInstruction)
        {
            Branch br = potentialBranchInstruction as Branch;

            if (br == null)
            {
                return(false);
            }
            var targetBlock = br.TargetBlock;

            return(targetBlock.Parent == currentContainer &&
                   targetBlock.IncomingEdgeCount == 1 && targetBlock.FinalInstruction.OpCode == OpCode.Nop &&
                   cfgNode.Dominates(context.ControlFlowGraph.GetNode(targetBlock)));
        }
示例#7
0
        /// <summary>
        /// Gets whether <c>potentialBranchInstruction</c> is a branch to a block that is dominated by <c>cfgNode</c>.
        /// If this function returns true, we replace the branch instruction with the block itself.
        /// </summary>
        private bool CanInline(ILInstruction exitInst)
        {
            if (exitInst is Branch branch &&
                branch.TargetBlock.Parent == currentContainer &&
                branch.TargetBlock.IncomingEdgeCount == 1)
            {
                // if the incoming edge count is 1, then this must be the sole branch, and dominance is already ensured
                Debug.Assert(cfgNode.Dominates(context.ControlFlowGraph.GetNode(branch.TargetBlock)));
                // can't have "final instructions" in control flow blocks
                Debug.Assert(branch.TargetBlock.FinalInstruction is Nop);
                return(true);
            }

            return(false);
        }
示例#8
0
        static HashSet <ControlFlowNode> FindDominatedNodes(HashSet <ControlFlowNode> scope, ControlFlowNode head)
        {
            HashSet <ControlFlowNode> agenda = new HashSet <ControlFlowNode>();
            HashSet <ControlFlowNode> result = new HashSet <ControlFlowNode>();

            agenda.Add(head);

            while (agenda.Count > 0)
            {
                ControlFlowNode addNode = agenda.First();
                agenda.Remove(addNode);

                if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode))
                {
                    foreach (var successor in addNode.Successors)
                    {
                        agenda.Add(successor);
                    }
                }
            }

            return(result);
        }
示例#9
0
        static HashSet <ControlFlowNode> FindDominatedNodes(HashSet <ControlFlowNode> scope, ControlFlowNode head)
        {
            HashSet <ControlFlowNode> agenda = new HashSet <ControlFlowNode>();
            HashSet <ControlFlowNode> result = new HashSet <ControlFlowNode>();

            agenda.Add(head);

            while (agenda.Count > 0)
            {
                ControlFlowNode addNode = agenda.First();
                agenda.Remove(addNode);

                if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode))
                {
                    for (int i = 0; i < addNode.Outgoing.Count; i++)
                    {
                        agenda.Add(addNode.Outgoing[i].Target);
                    }
                }
            }

            return(result);
        }
示例#10
0
        List <ILNode> FindLoops(HashSet <ControlFlowNode> scope, ControlFlowNode entryPoint, bool excludeEntryPoint)
        {
            List <ILNode> result = new List <ILNode>();

            // Do not modify entry data
            scope = new HashSet <ControlFlowNode>(scope);

            Queue <ControlFlowNode> agenda = new Queue <ControlFlowNode>();

            agenda.Enqueue(entryPoint);
            while (agenda.Count > 0)
            {
                ControlFlowNode node = agenda.Dequeue();

                // If the node is a loop header
                if (scope.Contains(node) &&
                    node.DominanceFrontier.Contains(node) &&
                    (node != entryPoint || !excludeEntryPoint))
                {
                    HashSet <ControlFlowNode> loopContents = FindLoopContent(scope, node);

                    // If the first expression is a loop condition
                    ILBasicBlock basicBlock = (ILBasicBlock)node.UserData;
                    ILExpression condExpr   = null;
                    ILLabel      trueLabel;
                    ILLabel      falseLabel;
                    // It has to be just brtrue - any preceding code would introduce goto
                    error.Assert(!basicBlock.MatchLastAndBr(GMCode.Bt, out trueLabel, out condExpr, out falseLabel),
                                 "Unrolled loop");
                    if (basicBlock.MatchLastAndBr(GMCode.Bt, out trueLabel, out condExpr, out falseLabel) ||
                        basicBlock.MatchLastAndBr(GMCode.Pushenv, out falseLabel, out condExpr, out trueLabel) || // built it the same way from the dissasembler, this needs inverted
                        basicBlock.MatchLastAndBr(GMCode.Repeat, out trueLabel, out condExpr, out falseLabel))    // repeate loop is built like a while
                    {
                        GMCode          loopType = (basicBlock.Body.ElementAt(basicBlock.Body.Count - 2) as ILExpression).Code;
                        ControlFlowNode trueTarget;
                        labelToCfNode.TryGetValue(trueLabel, out trueTarget);
                        ControlFlowNode falseTarget;
                        labelToCfNode.TryGetValue(falseLabel, out falseTarget);

                        // If one point inside the loop and the other outside
                        if ((!loopContents.Contains(trueTarget) && loopContents.Contains(falseTarget)) ||
                            (loopContents.Contains(trueTarget) && !loopContents.Contains(falseTarget)))
                        {
                            loopContents.RemoveOrThrow(node);
                            scope.RemoveOrThrow(node);

                            bool mustNegate = false;
                            if (loopContents.Contains(falseTarget) || falseTarget == node)
                            {
                                // Negate the condition
                                mustNegate = true;
                                //  condExpr = new ILExpression(GMCode.Not, null, condExpr);
                                ILLabel tmp = trueLabel;
                                trueLabel  = falseLabel;
                                falseLabel = tmp;
                            }
                            // HACK

                            ControlFlowNode postLoopTarget;
                            labelToCfNode.TryGetValue(falseLabel, out postLoopTarget);
                            if (postLoopTarget != null)
                            {
                                // Pull more nodes into the loop
                                HashSet <ControlFlowNode> postLoopContents = FindDominatedNodes(scope, postLoopTarget);
                                var pullIn = scope.Except(postLoopContents).Where(n => node.Dominates(n));
                                loopContents.UnionWith(pullIn);
                            }


                            //Debug.Assert(false);
                            Debug.Assert(condExpr != null);
                            switch (loopType)
                            {
                            case GMCode.Pushenv:
                                basicBlock.Body.RemoveTail(GMCode.Pushenv, GMCode.B);
                                basicBlock.Body.Add(new ILWithStatement()
                                {
                                    Condition = condExpr,     // we never negate
                                    Body      = new ILBlock()
                                    {
                                        EntryGoto = new ILExpression(GMCode.B, trueLabel),
                                        Body      = FindLoops(loopContents, node, false)
                                    }
                                });
                                break;

                            case GMCode.Bt:
                                basicBlock.Body.RemoveTail(GMCode.Bt, GMCode.B);
                                basicBlock.Body.Add(new ILWhileLoop()
                                {
                                    Condition = mustNegate ? condExpr.NegateCondition() : condExpr,
                                    Body      = new ILBlock()
                                    {
                                        EntryGoto = new ILExpression(GMCode.B, trueLabel),
                                        Body      = FindLoops(loopContents, node, false)
                                    }
                                });

                                break;

                            case GMCode.Repeat:
                                basicBlock.Body.RemoveTail(GMCode.Repeat, GMCode.B);
                                basicBlock.Body.Add(new ILRepeat()
                                {
                                    Condition = condExpr,     // we never negate
                                    Body      = new ILBlock()
                                    {
                                        EntryGoto = new ILExpression(GMCode.B, trueLabel),
                                        Body      = FindLoops(loopContents, node, false)
                                    }
                                });

                                break;
                            }
                            basicBlock.Body.Add(new ILExpression(GMCode.B, falseLabel));


                            result.Add(basicBlock);

                            scope.ExceptWith(loopContents);
                        }
                    }

                    // Fallback method: while(true)
                    if (scope.Contains(node))
                    {
                        Debug.Assert(false);
                        result.Add(new ILBasicBlock()
                        {
                            Body = new List <ILNode>()
                            {
                                ILLabel.Generate("Loop"),
                                new ILWhileLoop()
                                {
                                    Condition = new ILExpression(GMCode.Constant, new ILValue(true)),
                                    Body      = new ILBlock()
                                    {
                                        EntryGoto = new ILExpression(GMCode.B, (ILLabel)basicBlock.Body.First()),
                                        Body      = FindLoops(loopContents, node, true)
                                    }
                                },
                            },
                        });

                        scope.ExceptWith(loopContents);
                    }
                }

                // Using the dominator tree should ensure we find the the widest loop first
                foreach (var child in node.DominatorTreeChildren)
                {
                    agenda.Enqueue(child);
                }
            }

            // Add whatever is left
            foreach (var node in scope)
            {
                result.Add((ILNode)node.UserData);
            }
            scope.Clear();

            return(result);
        }
示例#11
0
 static bool HasSingleEdgeEnteringBlock(ControlFlowNode node)
 {
     return(node.Incoming.Count(edge => !node.Dominates(edge.Source)) == 1);
 }
示例#12
0
        List <ILNode> FindLoops(HashSet <ControlFlowNode> scope, ControlFlowNode entryPoint, bool excludeEntryPoint)
        {
            List <ILNode> result = new List <ILNode>();

            // Do not modify entry data
            scope = new HashSet <ControlFlowNode>(scope);

            Queue <ControlFlowNode> agenda = new Queue <ControlFlowNode>();

            agenda.Enqueue(entryPoint);
            while (agenda.Count > 0)
            {
                ControlFlowNode node = agenda.Dequeue();

                // If the node is a loop header
                if (scope.Contains(node) &&
                    node.DominanceFrontier.Contains(node) &&
                    (node != entryPoint || !excludeEntryPoint))
                {
                    HashSet <ControlFlowNode> loopContents = FindLoopContent(scope, node);

                    // If the first expression is a loop condition
                    ILBasicBlock basicBlock = (ILBasicBlock)node.UserData;
                    ILExpression condExpr;
                    ILLabel      trueLabel;
                    ILLabel      falseLabel;
                    // It has to be just brtrue - any preceding code would introduce goto
                    if (basicBlock.MatchSingleAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel))
                    {
                        ControlFlowNode trueTarget;
                        labelToCfNode.TryGetValue(trueLabel, out trueTarget);
                        ControlFlowNode falseTarget;
                        labelToCfNode.TryGetValue(falseLabel, out falseTarget);

                        // If one point inside the loop and the other outside
                        if ((!loopContents.Contains(trueTarget) && loopContents.Contains(falseTarget)) ||
                            (loopContents.Contains(trueTarget) && !loopContents.Contains(falseTarget)))
                        {
                            loopContents.RemoveOrThrow(node);
                            scope.RemoveOrThrow(node);

                            // If false means enter the loop
                            if (loopContents.Contains(falseTarget) || falseTarget == node)
                            {
                                // Negate the condition
                                condExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
                                ILLabel tmp = trueLabel;
                                trueLabel  = falseLabel;
                                falseLabel = tmp;
                            }

                            ControlFlowNode postLoopTarget;
                            labelToCfNode.TryGetValue(falseLabel, out postLoopTarget);
                            if (postLoopTarget != null)
                            {
                                // Pull more nodes into the loop
                                HashSet <ControlFlowNode> postLoopContents = FindDominatedNodes(scope, postLoopTarget);
                                var pullIn = scope.Except(postLoopContents).Where(n => node.Dominates(n));
                                loopContents.UnionWith(pullIn);
                            }

                            // Use loop to implement the brtrue
                            basicBlock.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
                            basicBlock.Body.Add(new ILWhileLoop()
                            {
                                Condition = condExpr,
                                BodyBlock = new ILBlock()
                                {
                                    EntryGoto = new ILExpression(ILCode.Br, trueLabel),
                                    Body      = FindLoops(loopContents, node, false)
                                }
                            });
                            basicBlock.Body.Add(new ILExpression(ILCode.Br, falseLabel));
                            result.Add(basicBlock);

                            scope.ExceptWith(loopContents);
                        }
                    }

                    // Fallback method: while(true)
                    if (scope.Contains(node))
                    {
                        result.Add(new ILBasicBlock()
                        {
                            Body = new List <ILNode>()
                            {
                                new ILLabel()
                                {
                                    Name = "Loop_" + (nextLabelIndex++)
                                },
                                new ILWhileLoop()
                                {
                                    BodyBlock = new ILBlock()
                                    {
                                        EntryGoto = new ILExpression(ILCode.Br, (ILLabel)basicBlock.Body.First()),
                                        Body      = FindLoops(loopContents, node, true)
                                    }
                                },
                            },
                        });

                        scope.ExceptWith(loopContents);
                    }
                }

                // Using the dominator tree should ensure we find the the widest loop first
                foreach (var child in node.DominatorTreeChildren)
                {
                    agenda.Enqueue(child);
                }
            }

            // Add whatever is left
            foreach (var node in scope)
            {
                result.Add((ILNode)node.UserData);
            }
            scope.Clear();

            return(result);
        }
示例#13
0
        private void DetectSwitchBody(Block block, SwitchInstruction switchInst)
        {
            Debug.Assert(block.Instructions.Last() == switchInst);
            ControlFlowNode h = context.ControlFlowNode;             // CFG node for our switch head

            Debug.Assert(h.UserData == block);
            Debug.Assert(!TreeTraversal.PreOrder(h, n => n.DominatorTreeChildren).Any(n => n.Visited));

            var nodesInSwitch = new List <ControlFlowNode>();

            nodesInSwitch.Add(h);
            h.Visited = true;
            ExtendLoop(h, nodesInSwitch, out var exitPoint, isSwitch: true);
            if (exitPoint != null && exitPoint.Predecessors.Count == 1 && !context.ControlFlowGraph.HasReachableExit(exitPoint))
            {
                // If the exit point is reachable from just one single "break;",
                // it's better to move the code into the switch.
                // (unlike loops which should not be nested unless necessary,
                //  nesting switches makes it clearer in which cases a piece of code is reachable)
                nodesInSwitch.AddRange(TreeTraversal.PreOrder(exitPoint, p => p.DominatorTreeChildren));
                exitPoint = null;
            }

            context.Step("Create BlockContainer for switch", switchInst);
            // Sort blocks in the loop in reverse post-order to make the output look a bit nicer.
            // (if the loop doesn't contain nested loops, this is a topological sort)
            nodesInSwitch.Sort((a, b) => b.PostOrderNumber.CompareTo(a.PostOrderNumber));
            Debug.Assert(nodesInSwitch[0] == h);
            foreach (var node in nodesInSwitch)
            {
                node.Visited = false;                 // reset visited flag so that we can find outer loops
                Debug.Assert(h.Dominates(node) || !node.IsReachable, "The switch body must be dominated by the switch head");
            }

            BlockContainer switchContainer = new BlockContainer();
            Block          newEntryPoint   = new Block();

            newEntryPoint.ILRange = switchInst.ILRange;
            switchContainer.Blocks.Add(newEntryPoint);
            newEntryPoint.Instructions.Add(switchInst);
            block.Instructions[block.Instructions.Count - 1] = switchContainer;

            Block exitTargetBlock = (Block)exitPoint?.UserData;

            if (exitTargetBlock != null)
            {
                block.Instructions.Add(new Branch(exitTargetBlock));
            }

            MoveBlocksIntoContainer(nodesInSwitch, switchContainer);

            // Rewrite branches within the loop from oldEntryPoint to newEntryPoint:
            foreach (var branch in switchContainer.Descendants.OfType <Branch>())
            {
                if (branch.TargetBlock == exitTargetBlock)
                {
                    branch.ReplaceWith(new Leave(switchContainer)
                    {
                        ILRange = branch.ILRange
                    });
                }
            }
        }
示例#14
0
        /// <summary>
        /// Check whether 'block' is a loop head; and construct a loop instruction
        /// (nested BlockContainer) if it is.
        /// </summary>
        public void Run(Block block, BlockTransformContext context)
        {
            this.context = context;
            // LoopDetection runs early enough so that block should still
            // be in the original container at this point.
            Debug.Assert(block.Parent == context.ControlFlowGraph.Container);
            this.currentBlockContainer = context.ControlFlowGraph.Container;

            // Because this is a post-order block transform, we can assume that
            // any nested loops within this loop have already been constructed.

            if (block.Instructions.Last() is SwitchInstruction switchInst)
            {
                // Switch instructions support "break;" just like loops
                DetectSwitchBody(block, switchInst);
            }

            ControlFlowNode h = context.ControlFlowNode;             // CFG node for our potential loop head

            Debug.Assert(h.UserData == block);
            Debug.Assert(!TreeTraversal.PreOrder(h, n => n.DominatorTreeChildren).Any(n => n.Visited));

            List <ControlFlowNode> loop = null;

            foreach (var t in h.Predecessors)
            {
                if (h.Dominates(t))
                {
                    // h->t is a back edge, and h is a loop header
                    // Add the natural loop of t->h to the loop.

                    // Definitions:
                    // * A back edge is an edge t->h so that h dominates t.
                    // * The natural loop of the back edge is the smallest set of nodes
                    //   that includes the back edge and has no predecessors outside the set
                    //   except for the predecessor of the header.

                    if (loop == null)
                    {
                        loop = new List <ControlFlowNode>();
                        loop.Add(h);
                        // Mark loop header as visited so that the pre-order traversal
                        // stops at the loop header.
                        h.Visited = true;
                    }
                    t.TraversePreOrder(n => n.Predecessors, loop.Add);
                }
            }
            if (loop != null)
            {
                var headBlock = (Block)h.UserData;
                context.Step($"Construct loop with head {headBlock.Label}", headBlock);
                // loop now is the union of all natural loops with loop head h.
                // Try to extend the loop to reduce the number of exit points:
                ExtendLoop(h, loop, out var exitPoint);

                // Sort blocks in the loop in reverse post-order to make the output look a bit nicer.
                // (if the loop doesn't contain nested loops, this is a topological sort)
                loop.Sort((a, b) => b.PostOrderNumber.CompareTo(a.PostOrderNumber));
                Debug.Assert(loop[0] == h);
                foreach (var node in loop)
                {
                    node.Visited = false;                     // reset visited flag so that we can find outer loops
                    Debug.Assert(h.Dominates(node) || !node.IsReachable, "The loop body must be dominated by the loop head");
                }
                ConstructLoop(loop, exitPoint);
            }
        }
示例#15
0
 /// <summary>
 /// Lists all potential targets for break; statements from a domination tree,
 /// assuming the domination tree must be exited via either break; or continue;
 ///
 /// First list all nodes in the dominator tree (excluding continue nodes)
 /// Then return the all successors not contained within said tree.
 ///
 /// Note that node will be returned once for each outgoing edge.
 /// Labelled continue statements (depth > 1) are counted as break targets
 /// </summary>
 internal IEnumerable <ControlFlowNode> GetBreakTargets(ControlFlowNode dominator) =>
 TreeTraversal.PreOrder(dominator, n => n.DominatorTreeChildren.Where(c => !MatchContinue(c)))
 .SelectMany(n => n.Successors)
 .Where(n => !dominator.Dominates(n) && !MatchContinue(n, 1));
示例#16
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);
            }
        }
示例#17
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);
            }
        }
示例#18
0
        /// <summary>
        /// Constructs a new control flow graph.
        /// Each node cfg[i] has a corresponding node rev[i].
        /// Edges are only created for nodes dominated by loopHead, and are in reverse from their direction
        /// in the primary CFG.
        /// An artificial exit node is used for edges that leave the set of nodes dominated by loopHead,
        /// or that leave the block Container.
        /// </summary>
        /// <param name="loopHead">Entry point of the loop.</param>
        /// <param name="exitNodeArity">out: The number of different CFG nodes.
        /// Possible values:
        ///  0 = no CFG nodes used as exit nodes (although edges leaving the block container might still be exits);
        ///  1 = a single CFG node (not dominated by loopHead) was used as an exit node;
        ///  2 = more than one CFG node (not dominated by loopHead) was used as an exit node.
        /// </param>
        /// <returns></returns>
        ControlFlowNode[] PrepareReverseCFG(ControlFlowNode loopHead, out int exitNodeArity)
        {
            ControlFlowNode[] cfg = context.ControlFlowGraph.cfg;
            ControlFlowNode[] rev = new ControlFlowNode[cfg.Length + 1];
            for (int i = 0; i < cfg.Length; i++)
            {
                rev[i] = new ControlFlowNode {
                    UserIndex = i, UserData = cfg[i].UserData
                };
            }
            ControlFlowNode nodeTreatedAsExitNode           = null;
            bool            multipleNodesTreatedAsExitNodes = false;
            ControlFlowNode exitNode = new ControlFlowNode {
                UserIndex = -1
            };

            rev[cfg.Length] = exitNode;
            for (int i = 0; i < cfg.Length; i++)
            {
                if (!loopHead.Dominates(cfg[i]) || isSwitch && cfg[i] != loopHead && loopContext.MatchContinue(cfg[i]))
                {
                    continue;
                }

                // Add reverse edges for all edges in cfg
                foreach (var succ in cfg[i].Successors)
                {
                    // edges to outer loops still count as exits (labelled continue not implemented)
                    if (isSwitch && loopContext.MatchContinue(succ, 1))
                    {
                        continue;
                    }

                    if (loopHead.Dominates(succ))
                    {
                        rev[succ.UserIndex].AddEdgeTo(rev[i]);
                    }
                    else
                    {
                        if (nodeTreatedAsExitNode == null)
                        {
                            nodeTreatedAsExitNode = succ;
                        }
                        if (nodeTreatedAsExitNode != succ)
                        {
                            multipleNodesTreatedAsExitNodes = true;
                        }
                        exitNode.AddEdgeTo(rev[i]);
                    }
                }
                if (context.ControlFlowGraph.HasDirectExitOutOfContainer(cfg[i]))
                {
                    exitNode.AddEdgeTo(rev[i]);
                }
            }
            if (multipleNodesTreatedAsExitNodes)
            {
                exitNodeArity = 2;                 // more than 1
            }
            else if (nodeTreatedAsExitNode != null)
            {
                exitNodeArity = 1;
            }
            else
            {
                exitNodeArity = 0;
            }
            Dominance.ComputeDominance(exitNode, context.CancellationToken);
            return(rev);
        }