internal ControlFlowGraph(ControlFlowNode[] nodes) { this.nodes = new ReadOnlyCollection<ControlFlowNode>(nodes); Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint); Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit); Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit); }
static void FindLoopContents(HashSet<ControlFlowNode> nodes, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode addNode) { if (nodes.Contains(addNode) && loopHead.Dominates(addNode) && loopContents.Add(addNode)) { foreach (var edge in addNode.Incoming) { FindLoopContents(nodes, loopContents, loopHead, edge.Source); } } }
ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel) { cached_ControlFlowGraph.Nodes.Clear(); int index = 0; var cfNodes = cached_ControlFlowGraph.Nodes; ControlFlowNode entryPoint = new ControlFlowNode(index++, 0, ControlFlowNodeType.EntryPoint); cfNodes.Add(entryPoint); ControlFlowNode regularExit = new ControlFlowNode(index++, null, ControlFlowNodeType.RegularExit); cfNodes.Add(regularExit); ControlFlowNode exceptionalExit = new ControlFlowNode(index++, null, ControlFlowNodeType.ExceptionalExit); cfNodes.Add(exceptionalExit); // Create graph nodes labelToCfNode.Clear(); Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>(); List<ILLabel> listLabels = null; foreach(ILBasicBlock node in nodes) { ControlFlowNode cfNode = new ControlFlowNode(index++, null, ControlFlowNodeType.Normal); cfNodes.Add(cfNode); astNodeToCfNode[node] = cfNode; cfNode.UserData = node; // Find all contained labels foreach(ILLabel label in node.GetSelfAndChildrenRecursive<ILLabel>(listLabels ?? (listLabels = new List<ILLabel>()))) { labelToCfNode[label] = cfNode; } } // Entry endge ControlFlowNode entryNode = labelToCfNode[entryLabel]; ControlFlowEdge entryEdge = new ControlFlowEdge(entryPoint, entryNode, JumpType.Normal); entryPoint.Outgoing.Add(entryEdge); entryNode.Incoming.Add(entryEdge); // Create edges List<ILExpression> listExpressions = null; foreach(ILBasicBlock node in nodes) { ControlFlowNode source = astNodeToCfNode[node]; // Find all branches foreach(ILLabel target in node.GetSelfAndChildrenRecursive<ILExpression>(listExpressions ?? (listExpressions = new List<ILExpression>()), e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { ControlFlowNode destination; // Labels which are out of out scope will not be in the collection // Insert self edge only if we are sure we are a loop if (labelToCfNode.TryGetValue(target, out destination) && (destination != source || target == node.Body.FirstOrDefault())) { ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); source.Outgoing.Add(edge); destination.Incoming.Add(edge); } } } return cached_ControlFlowGraph; }
private ControlFlowGraphBuilder(MethodBody methodBody) { this.methodBody = methodBody; offsets = methodBody.Instructions.Select(i => i.Offset).ToArray(); hasIncomingJumps = new bool[methodBody.Instructions.Count]; entryPoint = new ControlFlowNode(0, 0, ControlFlowNodeType.EntryPoint); nodes.Add(entryPoint); regularExit = new ControlFlowNode(1, -1, ControlFlowNodeType.RegularExit); nodes.Add(regularExit); exceptionalExit = new ControlFlowNode(2, -1, ControlFlowNodeType.ExceptionalExit); nodes.Add(exceptionalExit); Debug.Assert(nodes.Count == 3); }
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; }
static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> nodes, ControlFlowNode head) { var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors); HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(exitNodes); HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>(); while(agenda.Count > 0) { ControlFlowNode addNode = agenda.First(); agenda.Remove(addNode); if (nodes.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { foreach (var predecessor in addNode.Predecessors) { agenda.Add(predecessor); } } } if (nodes.Contains(head)) result.Add(head); return result; }
/// <summary> /// Gets whether <c>this</c> dominates <paramref name="node"/>. /// </summary> public bool Dominates(ControlFlowNode node) { // TODO: this can be made O(1) by numbering the dominator tree ControlFlowNode tmp = node; while (tmp != null) { if (tmp == this) return true; tmp = tmp.ImmediateDominator; } return false; }
internal ControlFlowNode(int blockIndex, ExceptionHandler exceptionHandler, ControlFlowNode endFinallyOrFaultNode) { BlockIndex = blockIndex; NodeType = endFinallyOrFaultNode != null ? ControlFlowNodeType.FinallyOrFaultHandler : ControlFlowNodeType.CatchHandler; ExceptionHandler = exceptionHandler; EndFinallyOrFaultNode = endFinallyOrFaultNode; Debug.Assert((exceptionHandler.HandlerType == ExceptionHandlerType.Finally || exceptionHandler.HandlerType == ExceptionHandlerType.Fault) == (endFinallyOrFaultNode != null)); Offset = exceptionHandler.HandlerStart.Offset; }
ControlFlowNode FindCommonDominator(ControlFlowNode b1, ControlFlowNode b2) { // Here we could use the postorder numbers to get rid of the hashset, see "A Simple, Fast Dominance Algorithm" FindCommonDominator_path1.Clear(); while (b1 != null && FindCommonDominator_path1.Add(b1)) b1 = b1.ImmediateDominator; while (b2 != null) { if (FindCommonDominator_path1.Contains(b2)) return b2; else b2 = b2.ImmediateDominator; } throw new Exception("No common dominator found!"); }
void CreateEdge(ControlFlowNode fromNode, ControlFlowNode toNode, JumpType type) { ControlFlowEdge edge = new ControlFlowEdge(fromNode, toNode, type); fromNode.Outgoing.Add(edge); toNode.Incoming.Add(edge); }
ControlFlowNode GetNew(ControlFlowNode oldNode) { if (oldNode == end) return newEnd; ControlFlowNode newNode; if (oldToNew.TryGetValue(oldNode, out newNode)) return newNode; return oldNode; }
void CollectNodes(ControlFlowNode node) { if (node == end || node == newEnd) throw new InvalidOperationException("unexpected cycle involving finally construct"); if (!oldToNew.ContainsKey(node)) { int newBlockIndex = builder.nodes.Count; ControlFlowNode copy; switch (node.NodeType) { case ControlFlowNodeType.Normal: copy = new ControlFlowNode(newBlockIndex, node.Start, node.End); break; case ControlFlowNodeType.FinallyOrFaultHandler: copy = new ControlFlowNode(newBlockIndex, node.ExceptionHandler, node.EndFinallyOrFaultNode); break; default: // other nodes shouldn't occur when copying finally blocks throw new NotSupportedException(node.NodeType.ToString()); } copy.CopyFrom = node; builder.nodes.Add(copy); oldToNew.Add(node, copy); if (node != start) { foreach (ControlFlowNode n in node.Predecessors) { CollectNodes(n); } } } }
/// <summary> /// Creates a copy of all nodes pointing to 'end' and replaces those references with references to 'newEnd'. /// Nodes pointing to the copied node are copied recursively to update those references, too. /// This recursion stops at 'start'. The modified version of start is returned. /// </summary> ControlFlowNode CopyFinallySubGraph(ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd) { return new CopyFinallySubGraphLogic(this, start, end, newEnd).CopyFinallySubGraph(); }
ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel) { int index = 0; List<ControlFlowNode> cfNodes = new List<ControlFlowNode>(); ControlFlowNode entryPoint = new ControlFlowNode(index++, 0, ControlFlowNodeType.EntryPoint); cfNodes.Add(entryPoint); ControlFlowNode regularExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.RegularExit); cfNodes.Add(regularExit); ControlFlowNode exceptionalExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.ExceptionalExit); cfNodes.Add(exceptionalExit); // Create graph nodes labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>(); foreach(ILNode node in nodes) { ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal); cfNodes.Add(cfNode); astNodeToCfNode[node] = cfNode; cfNode.UserData = node; // Find all contained labels foreach(ILLabel label in node.GetSelfAndChildrenRecursive<ILLabel>()) { labelToCfNode[label] = cfNode; } } // Entry endge ControlFlowNode entryNode = labelToCfNode[entryLabel]; ControlFlowEdge entryEdge = new ControlFlowEdge(entryPoint, entryNode, JumpType.Normal); entryPoint.Outgoing.Add(entryEdge); entryNode.Incoming.Add(entryEdge); // Create edges foreach(ILNode node in nodes) { ControlFlowNode source = astNodeToCfNode[node]; // Find all branches foreach(ILExpression child in node.GetSelfAndChildrenRecursive<ILExpression>()) { IEnumerable<ILLabel> targets = child.GetBranchTargets(); if (targets != null) { foreach(ILLabel target in targets) { ControlFlowNode destination; // Labels which are out of out scope will not be int the collection if (labelToCfNode.TryGetValue(target, out destination) && destination != source) { ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); source.Outgoing.Add(edge); destination.Incoming.Add(edge); } } } } } return new ControlFlowGraph(cfNodes.ToArray()); }
List<ILNode> FindLoops(HashSet<ControlFlowNode> scope, ControlFlowNode entryPoint, bool excludeEntryPoint) { List<ILNode> result = new List<ILNode>(); Queue<ControlFlowNode> agenda = new Queue<ControlFlowNode>(); agenda.Enqueue(entryPoint); while(agenda.Count > 0) { ControlFlowNode node = agenda.Dequeue(); if (scope.Contains(node) && node.DominanceFrontier.Contains(node) && (node != entryPoint || !excludeEntryPoint)) { HashSet<ControlFlowNode> loopContents = FindDominatedNodes(scope, node); ILWhileLoop loop = new ILWhileLoop(); ILCondition cond; HashSet<ControlFlowNode> condNodes; ILLabel condLabel; if (TryMatchCondition(loopContents, new ControlFlowNode[]{}, node, out cond, out condNodes, out condLabel)) { loopContents.ExceptWith(condNodes); scope.ExceptWith(condNodes); // Use loop to implement condition loop.Condition = cond.Condition; loop.PreLoopLabel = condLabel; loop.PostLoopGoto = cond.FalseBlock.EntryGoto; loop.BodyBlock = new ILBlock() { EntryGoto = cond.TrueBlock.EntryGoto }; } else { // Give the block some explicit entry point ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextBlockIndex++) }; loop.BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, entryLabel) }; ((ILBasicBlock)node.UserData).Body.Insert(0, entryLabel); } loop.BodyBlock.Body = FindLoops(loopContents, node, true); // Move the content into loop block scope.ExceptWith(loopContents); result.Add(loop); } // 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); } return result; }
List<ILNode> FindLoops(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, bool excludeEntryPoint) { List<ILNode> result = new List<ILNode>(); Queue<ControlFlowNode> agenda = new Queue<ControlFlowNode>(); agenda.Enqueue(entryPoint); while(agenda.Count > 0) { ControlFlowNode node = agenda.Dequeue(); if (nodes.Contains(node) && node.DominanceFrontier.Contains(node) && (node != entryPoint || !excludeEntryPoint)) { HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>(); FindLoopContents(nodes, loopContents, node, node); // Move the content into loop block nodes.ExceptWith(loopContents); ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextBlockIndex++) }; ((ILBlock)node.UserData).Body.Insert(0, entryLabel); result.Add(new ILLoop() { ContentBlock = new ILBlock(FindLoops(loopContents, node, true)) { EntryPoint = entryLabel } }); } // 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 nodes) { result.Add((ILNode)node.UserData); } return result; }
List<ILNode> FindConditions(HashSet<ControlFlowNode> nodes, ControlFlowNode entryNode) { List<ILNode> result = new List<ILNode>(); Queue<ControlFlowNode> agenda = new Queue<ControlFlowNode>(); agenda.Enqueue(entryNode); while(agenda.Count > 0) { ControlFlowNode node = agenda.Dequeue(); // Find a block that represents a simple condition if (nodes.Contains(node)) { ILMoveableBlock block = node.UserData as ILMoveableBlock; if (block != null && block.Body.Count == 3) { ILLabel label = block.Body[0] as ILLabel; ILExpression condBranch = block.Body[1] as ILExpression; ILExpression statBranch = block.Body[2] as ILExpression; // Switch if (label != null && condBranch != null && condBranch.Operand is ILLabel[] && condBranch.Arguments.Count > 0 && statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0) { ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch }; // Replace the two branches with a conditional structure - this preserves the node label block.Body.Remove(condBranch); block.Body.Remove(statBranch); block.Body.Add(ilSwitch); ControlFlowNode statTarget = null; labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget); // Pull in the conditional code HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); if (statTarget != null) frontiers.UnionWith(statTarget.DominanceFrontier); foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) { ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); if (condTarget != null) frontiers.UnionWith(condTarget.DominanceFrontier); } foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) { ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); ILBlock caseBlock = new ILBlock() { EntryPoint = condLabel }; if (condTarget != null && !frontiers.Contains(condTarget)) { HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget); nodes.ExceptWith(content); caseBlock.Body.AddRange(FindConditions(content, condTarget)); } ilSwitch.CaseBlocks.Add(caseBlock); } // The labels will not be used - kill them condBranch.Operand = null; result.Add(block); nodes.Remove(node); } // Two-way branch if (label != null && condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0 && statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0) { ControlFlowNode statTarget = null; labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget); ControlFlowNode condTarget = null; labelToCfNode.TryGetValue((ILLabel)condBranch.Operand, out condTarget); ILCondition condition = new ILCondition() { Condition = condBranch, TrueBlock = new ILBlock() { EntryPoint = (ILLabel)condBranch.Operand }, FalseBlock = new ILBlock() { EntryPoint = (ILLabel)statBranch.Operand } }; // Replace the two branches with a conditional structure - this preserves the node label block.Body.Remove(condBranch); block.Body.Remove(statBranch); block.Body.Add(condition); // Pull in the conditional code HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); if (statTarget != null) frontiers.UnionWith(statTarget.DominanceFrontier); if (condTarget != null) frontiers.UnionWith(condTarget.DominanceFrontier); if (condTarget != null && !frontiers.Contains(condTarget)) { HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget); nodes.ExceptWith(content); condition.TrueBlock.Body.AddRange(FindConditions(content, condTarget)); } if (statTarget != null && !frontiers.Contains(statTarget)) { HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, statTarget); nodes.ExceptWith(content); condition.FalseBlock.Body.AddRange(FindConditions(content, statTarget)); } // The label will not be used - kill it condBranch.Operand = null; result.Add(block); nodes.Remove(node); } } // Add the node now so that we have good ordering if (nodes.Contains(node)) { result.Add((ILNode)node.UserData); nodes.Remove(node); } } // 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 nodes) { result.Add((ILNode)node.UserData); } return result; }
internal SsaBlock(ControlFlowNode node) { this.NodeType = node.NodeType; this.BlockIndex = node.BlockIndex; }
void CreateNodes() { // Step 2a: find basic blocks and create nodes for them for (int i = 0; i < methodBody.Instructions.Count; i++) { Instruction blockStart = methodBody.Instructions[i]; ExceptionHandler blockStartEH = FindInnermostExceptionHandler(blockStart.Offset); // try and see how big we can make that block: for (; i + 1 < methodBody.Instructions.Count; i++) { Instruction inst = methodBody.Instructions[i]; if (IsBranch(inst.OpCode) || CanThrowException(inst.OpCode)) break; if (hasIncomingJumps[i + 1]) break; if (inst.Next != null) { // ensure that blocks never contain instructions from different try blocks ExceptionHandler instEH = FindInnermostExceptionHandler(inst.Next.Offset); if (instEH != blockStartEH) break; } } nodes.Add(new ControlFlowNode(nodes.Count, blockStart, methodBody.Instructions[i])); } // Step 2b: Create special nodes for the exception handling constructs foreach (ExceptionHandler handler in methodBody.ExceptionHandlers) { if (handler.HandlerType == ExceptionHandlerType.Filter) throw new NotSupportedException(); ControlFlowNode endFinallyOrFaultNode = null; if (handler.HandlerType == ExceptionHandlerType.Finally || handler.HandlerType == ExceptionHandlerType.Fault) { endFinallyOrFaultNode = new ControlFlowNode(nodes.Count, handler.HandlerEnd.Offset, ControlFlowNodeType.EndFinallyOrFault); nodes.Add(endFinallyOrFaultNode); } nodes.Add(new ControlFlowNode(nodes.Count, handler, endFinallyOrFaultNode)); } }
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; }
ControlFlowNode FindParentExceptionHandlerNode(ControlFlowNode exceptionHandler) { Debug.Assert(exceptionHandler.NodeType == ControlFlowNodeType.CatchHandler || exceptionHandler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler); int offset = exceptionHandler.ExceptionHandler.TryStart.Offset; for (int i = exceptionHandler.BlockIndex + 1; i < nodes.Count; i++) { ExceptionHandler h = nodes[i].ExceptionHandler; if (h != null && h.TryStart.Offset <= offset && offset < h.TryEnd.Offset) return nodes[i]; } return exceptionalExit; }
List<ILNode> FindConditions(HashSet<ControlFlowNode> scope, ControlFlowNode entryNode) { List<ILNode> result = new List<ILNode>(); // Do not modify entry data scope = new HashSet<ControlFlowNode>(scope); Stack<ControlFlowNode> agenda = new Stack<ControlFlowNode>(); agenda.Push(entryNode); while(agenda.Count > 0) { ControlFlowNode node = agenda.Pop(); // Find a block that represents a simple condition if (scope.Contains(node)) { ILBasicBlock block = (ILBasicBlock)node.UserData; { // Switch ILLabel[] caseLabels; ILExpression switchArg; ILLabel fallLabel; if (block.MatchLastAndBr(ILCode.Switch, out caseLabels, out switchArg, out fallLabel)) { // Replace the switch code with ILSwitch ILSwitch ilSwitch = new ILSwitch() { Condition = switchArg }; block.Body.RemoveTail(ILCode.Switch, ILCode.Br); block.Body.Add(ilSwitch); block.Body.Add(new ILExpression(ILCode.Br, fallLabel)); result.Add(block); // Remove the item so that it is not picked up as content scope.RemoveOrThrow(node); // Find the switch offset int addValue = 0; List<ILExpression> subArgs; if (ilSwitch.Condition.Match(ILCode.Sub, out subArgs) && subArgs[1].Match(ILCode.Ldc_I4, out addValue)) { ilSwitch.Condition = subArgs[0]; } // Pull in code of cases ControlFlowNode fallTarget = null; labelToCfNode.TryGetValue(fallLabel, out fallTarget); HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); if (fallTarget != null) frontiers.UnionWith(fallTarget.DominanceFrontier.Except(new [] { fallTarget })); foreach(ILLabel condLabel in caseLabels) { ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); if (condTarget != null) frontiers.UnionWith(condTarget.DominanceFrontier.Except(new [] { condTarget })); } for (int i = 0; i < caseLabels.Length; i++) { ILLabel condLabel = caseLabels[i]; // Find or create new case block ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.FirstOrDefault(b => b.EntryGoto.Operand == condLabel); if (caseBlock == null) { caseBlock = new ILSwitch.CaseBlock() { Values = new List<int>(), EntryGoto = new ILExpression(ILCode.Br, condLabel) }; ilSwitch.CaseBlocks.Add(caseBlock); ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); if (condTarget != null && !frontiers.Contains(condTarget)) { HashSet<ControlFlowNode> content = FindDominatedNodes(scope, condTarget); scope.ExceptWith(content); caseBlock.Body.AddRange(FindConditions(content, condTarget)); // Add explicit break which should not be used by default, but the goto removal might decide to use it caseBlock.Body.Add(new ILBasicBlock() { Body = { new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) }, new ILExpression(ILCode.LoopOrSwitchBreak, null) } }); } } caseBlock.Values.Add(i + addValue); } // Heuristis to determine if we want to use fallthough as default case if (fallTarget != null && !frontiers.Contains(fallTarget)) { HashSet<ControlFlowNode> content = FindDominatedNodes(scope, fallTarget); if (content.Any()) { var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) }; ilSwitch.CaseBlocks.Add(caseBlock); block.Body.RemoveTail(ILCode.Br); scope.ExceptWith(content); caseBlock.Body.AddRange(FindConditions(content, fallTarget)); // Add explicit break which should not be used by default, but the goto removal might decide to use it caseBlock.Body.Add(new ILBasicBlock() { Body = { new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) }, new ILExpression(ILCode.LoopOrSwitchBreak, null) } }); } } } // Two-way branch ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; if(block.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { // Swap bodies since that seems to be the usual C# order ILLabel temp = trueLabel; trueLabel = falseLabel; falseLabel = temp; condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); // Convert the brtrue to ILCondition ILCondition ilCond = new ILCondition() { Condition = condExpr, TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) }, FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) } }; block.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); block.Body.Add(ilCond); result.Add(block); // Remove the item immediately so that it is not picked up as content scope.RemoveOrThrow(node); ControlFlowNode trueTarget = null; labelToCfNode.TryGetValue(trueLabel, out trueTarget); ControlFlowNode falseTarget = null; labelToCfNode.TryGetValue(falseLabel, out falseTarget); // Pull in the conditional code if (trueTarget != null && HasSingleEdgeEnteringBlock(trueTarget)) { HashSet<ControlFlowNode> content = FindDominatedNodes(scope, trueTarget); scope.ExceptWith(content); ilCond.TrueBlock.Body.AddRange(FindConditions(content, trueTarget)); } if (falseTarget != null && HasSingleEdgeEnteringBlock(falseTarget)) { HashSet<ControlFlowNode> content = FindDominatedNodes(scope, falseTarget); scope.ExceptWith(content); ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget)); } } } // Add the node now so that we have good ordering if (scope.Contains(node)) { result.Add((ILNode)node.UserData); scope.Remove(node); } } // depth-first traversal of dominator tree for (int i = node.DominatorTreeChildren.Count - 1; i >= 0; i--) { agenda.Push(node.DominatorTreeChildren[i]); } } // Add whatever is left foreach(var node in scope) { result.Add((ILNode)node.UserData); } return result; }
public CopyFinallySubGraphLogic(ControlFlowGraphBuilder builder, ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd) { this.builder = builder; this.start = start; this.end = end; this.newEnd = newEnd; }
List<ILNode> FindConditions(HashSet<ControlFlowNode> scope, ControlFlowNode entryNode) { List<ILNode> result = new List<ILNode>(); HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(); agenda.Add(entryNode); while(agenda.Any()) { ControlFlowNode node = agenda.First(); // Attempt for a good order while(agenda.Contains(node.ImmediateDominator)) { node = node.ImmediateDominator; } agenda.Remove(node); // Find a block that represents a simple condition if (scope.Contains(node)) { ILBasicBlock block = node.UserData as ILBasicBlock; if (block != null && block.Body.Count == 1) { ILExpression condBranch = block.Body[0] as ILExpression; // Switch if (condBranch != null && condBranch.Operand is ILLabel[] && condBranch.Arguments.Count > 0) { ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch, DefaultGoto = block.FallthoughGoto }; ControlFlowNode fallTarget = null; labelToCfNode.TryGetValue((ILLabel)block.FallthoughGoto.Operand, out fallTarget); HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); if (fallTarget != null) frontiers.UnionWith(fallTarget.DominanceFrontier); foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) { ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); if (condTarget != null) frontiers.UnionWith(condTarget.DominanceFrontier); } foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) { ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); ILBlock caseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, condLabel) }; if (condTarget != null && !frontiers.Contains(condTarget)) { HashSet<ControlFlowNode> content = FindDominatedNodes(scope, condTarget); scope.ExceptWith(content); caseBlock.Body.AddRange(FindConditions(content, condTarget)); } ilSwitch.CaseBlocks.Add(caseBlock); } // The labels will not be used - kill them condBranch.Operand = null; result.Add(new ILBasicBlock() { EntryLabel = block.EntryLabel, // Keep the entry label Body = { ilSwitch } }); scope.Remove(node); } // Two-way branch ILCondition ilCond; HashSet<ControlFlowNode> matchedNodes; ILLabel condEntryLabel; if (TryMatchCondition(scope, new ControlFlowNode[] {}, node, out ilCond, out matchedNodes, out condEntryLabel)) { // The branch labels will not be used - kill them foreach(ILExpression expr in ilCond.Condition.GetSelfAndChildrenRecursive<ILExpression>()) { if (expr.GetBranchTargets().Any()) { expr.Operand = null; } } ControlFlowNode trueTarget = null; labelToCfNode.TryGetValue((ILLabel)ilCond.TrueBlock.EntryGoto.Operand, out trueTarget); ControlFlowNode falseTarget = null; labelToCfNode.TryGetValue((ILLabel)ilCond.FalseBlock.EntryGoto.Operand, out falseTarget); // Pull in the conditional code HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); if (trueTarget != null) frontiers.UnionWith(trueTarget.DominanceFrontier); if (falseTarget != null) frontiers.UnionWith(falseTarget.DominanceFrontier); if (trueTarget != null && !frontiers.Contains(trueTarget)) { HashSet<ControlFlowNode> content = FindDominatedNodes(scope, trueTarget); scope.ExceptWith(content); ilCond.TrueBlock.Body.AddRange(FindConditions(content, trueTarget)); } if (falseTarget != null && !frontiers.Contains(falseTarget)) { HashSet<ControlFlowNode> content = FindDominatedNodes(scope, falseTarget); scope.ExceptWith(content); ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget)); } result.Add(new ILBasicBlock() { EntryLabel = condEntryLabel, // Keep the entry label Body = { ilCond } }); scope.ExceptWith(matchedNodes); } } // Add the node now so that we have good ordering if (scope.Contains(node)) { result.Add((ILNode)node.UserData); scope.Remove(node); } } // Using the dominator tree should ensure we find the the widest loop first foreach(var child in node.DominatorTreeChildren) { agenda.Add(child); } } // Add whatever is left foreach(var node in scope) { result.Add((ILNode)node.UserData); } return result; }
void ReconstructEdges(ControlFlowNode oldNode, ControlFlowNode newNode) { foreach (ControlFlowEdge oldEdge in oldNode.Outgoing) { builder.CreateEdge(newNode, GetNew(oldEdge.Target), oldEdge.Type); } }
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; }
void CreateEdge(ControlFlowNode fromNode, Instruction toInstruction, JumpType type) { CreateEdge(fromNode, nodes.Single(n => n.Start == toInstruction), type); }
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; }
static bool HasSingleEdgeEnteringBlock(ControlFlowNode node) { return node.Incoming.Count(edge => !node.Dominates(edge.Source)) == 1; }
bool TryMatchCondition(HashSet<ControlFlowNode> scope, IEnumerable<ControlFlowNode> scopeExcept, ControlFlowNode head, out ILCondition condition, out HashSet<ControlFlowNode> matchedNodes, out ILLabel entryLabel) { condition = null; matchedNodes = null; entryLabel = null; if (!scope.Contains(head) || scopeExcept.Contains(head)) return false; ILBasicBlock basicBlock = head.UserData as ILBasicBlock; if (basicBlock == null || basicBlock.Body.Count != 1) return false; ILExpression condBranch = basicBlock.Body[0] as ILExpression; if (condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0) { // We have found a two-way condition condition = new ILCondition() { Condition = condBranch, TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, condBranch.Operand) }, FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, basicBlock.FallthoughGoto.Operand) } }; // We are done with the node so "remove" it from scope scopeExcept = scopeExcept.Union(new[] {head}); matchedNodes = new HashSet<ControlFlowNode>() { head }; entryLabel = basicBlock.EntryLabel; // Optimize short-circut expressions while(true) { // Consider condition.TrueBlock { ILLabel nextLabel = (ILLabel)condition.TrueBlock.EntryGoto.Operand; ControlFlowNode nextTarget; labelToCfNode.TryGetValue(nextLabel, out nextTarget); ILCondition nextCond; HashSet<ControlFlowNode> nextMatchedNodes; ILLabel nextEnteryLabel; if (nextTarget != null && TryMatchCondition(scope, scopeExcept, nextTarget, out nextCond, out nextMatchedNodes, out nextEnteryLabel) && labelRefCount[nextEnteryLabel] == 1) { if (condition.FalseBlock.EntryGoto.Operand == nextCond.FalseBlock.EntryGoto.Operand) { condition.Condition = new ILExpression(ILCode.LogicAnd, null, condition.Condition, nextCond.Condition); condition.TrueBlock = nextCond.TrueBlock; condition.FalseBlock = nextCond.FalseBlock; scopeExcept = scopeExcept.Union(nextMatchedNodes); matchedNodes.UnionWith(nextMatchedNodes); continue; } if (condition.FalseBlock.EntryGoto.Operand == nextCond.TrueBlock.EntryGoto.Operand) { condition.Condition = new ILExpression(ILCode.LogicOr, null, new ILExpression(ILCode.LogicNot, null, condition.Condition), nextCond.Condition); condition.TrueBlock = nextCond.TrueBlock; condition.FalseBlock = nextCond.FalseBlock; scopeExcept = scopeExcept.Union(nextMatchedNodes); matchedNodes.UnionWith(nextMatchedNodes); continue; } } } // Consider condition.FalseBlock { ILLabel nextLabel = (ILLabel)condition.FalseBlock.EntryGoto.Operand; ControlFlowNode nextTarget; labelToCfNode.TryGetValue(nextLabel, out nextTarget); ILCondition nextCond; HashSet<ControlFlowNode> nextMatchedNodes; ILLabel nextEnteryLabel; if (nextTarget != null && TryMatchCondition(scope, scopeExcept, nextTarget, out nextCond, out nextMatchedNodes, out nextEnteryLabel) && labelRefCount[nextEnteryLabel] == 1) { if (condition.TrueBlock.EntryGoto.Operand == nextCond.FalseBlock.EntryGoto.Operand) { condition.Condition = new ILExpression(ILCode.LogicAnd, null, new ILExpression(ILCode.LogicNot, null, condition.Condition), nextCond.Condition); condition.TrueBlock = nextCond.TrueBlock; condition.FalseBlock = nextCond.FalseBlock; scopeExcept = scopeExcept.Union(nextMatchedNodes); matchedNodes.UnionWith(nextMatchedNodes); continue; } if (condition.TrueBlock.EntryGoto.Operand == nextCond.TrueBlock.EntryGoto.Operand) { condition.Condition = new ILExpression(ILCode.LogicOr, null, condition.Condition, nextCond.Condition); condition.TrueBlock = nextCond.TrueBlock; condition.FalseBlock = nextCond.FalseBlock; scopeExcept = scopeExcept.Union(nextMatchedNodes); matchedNodes.UnionWith(nextMatchedNodes); continue; } } } break; } return true; } return false; }