/// <summary> /// Remove redundancies and add in-edge information /// </summary> private void Optimize(SortedList <ushort, CallGraphNode> nodes) { // Remove redundant jumps directly to the next block that is not rechable from any other for (int i = 0; i < nodes.Count; i++) { CallGraphNode node = nodes.Values[i]; if ((node.NodeType == NodeType.OneBranch) && (node.OutEdges.Count == 1) && (node.OutEdges[0].InEdges.Count == 1)) { ICallGraphNode targetNode = (ICallGraphNode)node.OutEdges[0]; // Remove the jump instruction at the end of this node node.Instructions.RemoveAt(node.Instructions.Count - 1); // Add all the instructions in the next node node.EndIP = targetNode.EndIP; foreach (IInstruction instruction in targetNode.Instructions) { node.Instructions.Add(instruction); } // Remove the target node nodes.Remove(targetNode.StartIP); node.OutEdges.Clear(); ((List <IGraphNode>)node.OutEdges).AddRange(targetNode.OutEdges); foreach (CallGraphNode n in targetNode.OutEdges) { n.InEdges.Remove(targetNode); n.InEdges.Add(node); } // This node now becomes the type of the target node node.NodeType = targetNode.NodeType; } } // First pass over block list removes redundant jumps of the form // (Un)Conditional-> Unconditional jump foreach (CallGraphNode node in nodes.Values) { if ((node.OutEdges.Count == 0) && ((node.NodeType == NodeType.OneBranch) || (node.NodeType == NodeType.TwoBranch))) { for (int i = 0; i < node.OutEdges.Count; i++) { CallGraphNode newTargetNode = RemoveJump((CallGraphNode)node.OutEdges[i]); node.OutEdges[i] = newTargetNode; newTargetNode.InEdges.Add(node); } } } // Next is a depth-first traversal merging any FallThrough node or // OneBranch node that fall through to a node with that as their only in-edge. MergeFallThrough(); // Remove redundant nodes created by the optimizations }
private void MakeNode(ushort targetIp, SortedList <ushort, CallGraphNode> nodes) { CallGraphNode newNode; if (!nodes.TryGetValue(targetIp, out newNode)) { newNode = new CallGraphNode(targetIp); nodes.Add(targetIp, newNode); } }
/// <summary> /// Finds the immediate dominator of each node in the graph /// This is based on the findImmedDom function of the Control.C file /// in the dcc source code which is in turn an adapted version /// of the dominators algorithm by Hecht and Ullman; finds /// immediate dominators only. /// Note graph should be reducible /// </summary> private void FindImmediateDominators() { for (int currentIndex = 0; currentIndex < _depthFirstSearchLast.Length; currentIndex++) { CallGraphNode currentNode = _depthFirstSearchLast[currentIndex]; for (int j = 0; j < currentNode.InEdges.Count; j++) { int predecessorIndex = currentNode.InEdges[j].DepthFirstSearchLastNumber; if (predecessorIndex < currentIndex) { currentNode.ImmediateDominatorNumber = CommonDominator(currentNode.ImmediateDominatorNumber, predecessorIndex); } } } }
/// <summary> /// Performs a depth first traversal of the call graph and labels each node with its /// postion in the traversal /// </summary> /// <param name="node"></param> /// <param name="firstNumber"></param> /// <param name="lastNumber"></param> private void DepthFirstTraverse(CallGraphNode node, ref int lastNumber) { node.Traversed = true; foreach (CallGraphNode childNode in node.OutEdges) { // Traverse the child node if it has not already been traversed if (!childNode.Traversed) { DepthFirstTraverse(childNode, ref lastNumber); } } node.DepthFirstSearchLastNumber = lastNumber; _depthFirstSearchLast[lastNumber--] = node; }
/// <summary> /// Recursive procedure to tag nodes that belong to the case described by /// the list l, head and tail (dfsLast index to first and exit node of the /// case). /// </summary> /// <param name="node"></param> /// <param name="l"></param> /// <param name="head"></param> /// <param name="tail"></param> private static void TagNodesInCase(CallGraphNode node, ICollection <IGraphNode> l, ICallGraphNode head, IGraphNode tail) { node.Traversed = true; if ((node != tail) && (node.Instructions.Count > 0) && (node.Instructions[node.Instructions.Count - 1].OpCode != OpCodeTable.Switch) && l.Contains(node)) { l.Add(node); node.CaseHead = head; for (int i = 0; i < node.OutEdges.Count; i++) { if (!((CallGraphNode)node.OutEdges[i]).Traversed) { TagNodesInCase((CallGraphNode)node.OutEdges[i], l, head, tail); } } } }
private static bool LinkContinuationNode(CallGraphNode node, SortedList <ushort, CallGraphNode> nodes) { int index = nodes.IndexOfValue(node) + 1; if (index == nodes.Count) { return(false); } IGraphNode targetNode = nodes.Values[index]; node.OutEdges.Add(targetNode); targetNode.InEdges.Add(node); return(true); }
private CallGraphNode SplitNode(ushort ip, NodeType type) { foreach (CallGraphNode node in _nodes) { if ((node.StartIP <= ip) && (node.EndIP >= ip)) { // Found the node CallGraphNode newNode = new CallGraphNode(ip, new List <IInstruction>()); newNode.NodeType = node.NodeType; node.NodeType = type; // Move the instructions int i = 0; while ((node.Instructions.Count > i) && (node.Instructions[i].IP < ip)) { i++; } while (node.Instructions.Count > i) { newNode.Instructions.Add(node.Instructions[i]); node.Instructions.RemoveAt(i); } // Move the out edges from the existing node to the new node foreach (CallGraphNode outNode in node.OutEdges) { newNode.OutEdges.Add(outNode); outNode.InEdges.Remove(node); outNode.InEdges.Add(newNode); } node.OutEdges.Clear(); // Link the two nodes node.OutEdges.Add(newNode); newNode.InEdges.Add(node); // Add the new node after the existing node into the node list _nodes.Insert(_nodes.IndexOf(node) + 1, newNode); return(node); } } throw new ApplicationException("Exception clause out of IP range"); }
private static void LinkNode(IGraphNode node, IDictionary <ushort, CallGraphNode> nodes, ushort targetIP) { CallGraphNode targetNode; if (nodes.TryGetValue(targetIP, out targetNode)) { node.OutEdges.Add(targetNode); targetNode.InEdges.Add(node); } else { // Create a new empty node at the IP IList <IInstruction> instructions = new List <IInstruction>(); instructions.Add(OpCodeTable.GetInstruction(OpCodes.Nop, targetIP, null)); CallGraphNode emptyNode = new CallGraphNode(instructions); nodes.Add(targetIP, emptyNode); node.OutEdges.Add(emptyNode); emptyNode.InEdges.Add(node); } }
private void TraverseExceptionTree(TryHandler tryHandler) { // Find the right node to add the try node before CallGraphNode tryNode = SplitNode((ushort)tryHandler.TryStartIp, tryHandler.Type); CallGraphNode handlerNode = SplitNode((ushort)tryHandler.HandlerStartIp, NodeType.FallThrough); CallGraphNode followNode = SplitNode((ushort)tryHandler.HandlerEndIp, NodeType.FallThrough); tryNode.HandlerNode = (ICallGraphNode)handlerNode.OutEdges[0]; tryNode.FollowNode = (ICallGraphNode)followNode.OutEdges[0]; // Split the children foreach (TryHandler childTryHandler in tryHandler.NestedTry.Values) { TraverseExceptionTree(childTryHandler); } foreach (TryHandler childTryHandler in tryHandler.NestedHandler.Values) { TraverseExceptionTree(childTryHandler); } }
/// <summary> /// If the node is just a jump then replace the node with its target /// </summary> /// <param name="node">The node that is just a jump instruction.</param> /// <returns>The node that the jump instruction jumped to.</returns> private static CallGraphNode RemoveJump(CallGraphNode node) { while ((node.NodeType == NodeType.OneBranch) && (node.EndIP - node.StartIP == 1)) { if (!node.Traversed) { node.Traversed = true; if (node.InEdges.Count == 1) { // Remove this node from the parents nodes out edges node.InEdges[0].OutEdges.Remove(node); // Remove this node from the child nodes in edges node.OutEdges[0].InEdges.Remove(node); } } node = (CallGraphNode)node.OutEdges[0]; } return(node); }
public ControlFlowGraph(NetMethodBody body, Module module, Type[] genericTypeArguments, Type[] genericMethodArguments) { // Convert the byte code into instruction objects ushort ip = 0; byte[] il = body.GetILAsByteArray(); List <IInstruction> instructions = new List <IInstruction>(); while (ip < il.Length) { instructions.Add(OpCodeTable.GetInstruction(il, ref ip, module, genericTypeArguments, genericMethodArguments)); } // Iterate the instructions building a list of call graph nodes SortedList <ushort, CallGraphNode> nodes = new SortedList <ushort, CallGraphNode>(); CallGraphNode node = new CallGraphNode(0); nodes.Add(0, node); int index = 0; IInstruction instruction = instructions[index]; do { Console.Write(instruction); // Create the nodes that this instruction branches to switch (instruction.OpCode.FlowControl) { case FlowControl.Branch: { if ((instruction.OpCode != OpCodes.Leave) && (instruction.OpCode != OpCodes.Leave_S)) { // Direct branch to a new instruction that is not a leave instruction from a try-catch block. // Create a call graph node for the target of the branch. MakeNode((ushort)instruction.Argument, nodes); Console.Write(" <-- Branch {0:x4}", instruction.Argument); } break; } case FlowControl.Cond_Branch: { if (instruction.OpCode.Value == OpCodes.Switch.Value) { // Conditional branch to n-blocks foreach (ushort switchTargetIp in (ushort[])instruction.Argument) { MakeNode(switchTargetIp, nodes); } } else { // Conditional branch to two blocks MakeNode((ushort)instruction.Argument, nodes); Console.WriteLine("Making " + instruction.Argument); } // Set the next instruction of a branch to also be a target MakeNode(instructions[index + 1].IP, nodes); Console.Write(" <-- If Node {0:x4}", instruction.Argument); break; } case FlowControl.Return: // Set the next instruction of a branch to also be a target Console.Write(" <-- Exit Node"); break; case FlowControl.Throw: // End of graph Console.Write(" <-- Throw Node"); break; case FlowControl.Break: case FlowControl.Call: case FlowControl.Meta: case FlowControl.Next: #pragma warning disable 612,618 case FlowControl.Phi: #pragma warning restore 612,618 // Add the continuation link //node.NodeType = NodeType.FallThrough; //MakeNode(ip, node, nodes); break; } Console.WriteLine(); // Get the next instruction index++; if (index < instructions.Count) { instruction = instructions[index]; // Find the node to add the next instruction to CallGraphNode nextNode; if (nodes.TryGetValue(instruction.IP, out nextNode)) { /*if (node.NodeType == NodeType.FallThrough) * { * Console.Write("added fall through link "); * node.OutEdges.Add(nextNode); * nextNode.InEdges.Add(node); * }*/ node = nextNode; Console.Write("New Node --> "); } } } while (index < instructions.Count); Console.WriteLine(); // Iterate the instructions a second time adding them to the correct nodes //CallGraphNode node; node = nodes[0]; for (index = 0; index < instructions.Count; index++) { instruction = instructions[index]; if (index > 0) { CallGraphNode nextNode; if (nodes.TryGetValue(instruction.IP, out nextNode)) { if (node.OutEdges.Count == 0) { Console.WriteLine("ff"); node.NodeType = NodeType.FallThrough; LinkNode(node, nextNode); } Console.Write("--" + node.OutEdges.Count + "--"); node = nextNode; Console.Write("new node "); } } Console.WriteLine(instruction); node.Instructions.Add(instruction); // Create the nodes that this instruction branches to switch (instruction.OpCode.FlowControl) { case FlowControl.Branch: { if ((instruction.OpCode != OpCodes.Leave) && (instruction.OpCode != OpCodes.Leave_S)) { // Direct branch to a new instruction that is not a leave instruction from a try-catch block. // Create a call graph node for the target of the branch. node.NodeType = NodeType.OneBranch; LinkNode(node, nodes[(ushort)instruction.Argument]); Console.Write(" <-- Branch {0:x4}", instruction.Argument); } break; } case FlowControl.Cond_Branch: { if (instruction.OpCode.Value == OpCodes.Switch.Value) { // Conditional branch to n-blocks node.NodeType = NodeType.MultiBranch; foreach (ushort switchTargetIp in (ushort[])instruction.Argument) { LinkNode(node, nodes[switchTargetIp]); } } else { // Conditional branch to two blocks node.NodeType = NodeType.TwoBranch; LinkNode(node, nodes[(ushort)instruction.Argument]); Console.Write("Linking " + instruction.Argument); } // Set the next instruction of a branch to also be a target LinkNode(node, nodes[instructions[index + 1].IP]); Console.Write(" <-- If Node {0:x4}", instruction.Argument); break; } case FlowControl.Return: // Set the next instruction of a branch to also be a target node.NodeType = NodeType.Exit; Console.Write(" <-- Exit Node"); break; case FlowControl.Throw: // End of graph node.NodeType = NodeType.Throw; Console.Write(" <-- Throw Node"); break; } } // Copy the nodes to a simple list _nodes.AddRange(nodes.Values); Console.WriteLine(); Console.WriteLine(_nodes.Count); foreach (CallGraphNode n in _nodes) { Console.WriteLine(n.StartIP + " " + n.NodeType + " " + n.OutEdges.Count); } CheckGraph(); // Add the exception information - build a sorted tree of clauses SortedList <int, TryHandler> clauses = new SortedList <int, TryHandler>(); foreach (ExceptionHandlingClause clause in body.ExceptionHandlingClauses) { Add(new TryHandler(clause), clauses); } foreach (TryHandler clause in clauses.Values) { TraverseExceptionTree(clause); } // Find the root node -- This fails when there are multiple try statements on the first instruction for (int i = 0; i < _nodes.Count; i++) { if (_nodes[i].StartIP == 0) { _rootNode = _nodes[i]; break; } } if (_rootNode == null) { throw new ApplicationException("Unable to find a root node"); } CheckGraph(); // Remove redundancies and add in-edge information Optimize(); CheckGraph(); // Visit the graph in depth first order and label the nodes _depthFirstSearchLast = new CallGraphNode[_nodes.Count]; int last = _nodes.Count - 1; DepthFirstTraverse(_rootNode, ref last); // Find the immediate dominators of each node FindImmediateDominators(); // Check the graph for reducibility FindDerivedSequence(); // Work out the graphs back edges DetermineBackEdges(); ResetTraversal(); StructureCases(); StructureLoops(); StructureIfs(); ResetTraversal(); }
private void LinkNode(CallGraphNode sourceNode, CallGraphNode targetNode) { // Link the nodes sourceNode.OutEdges.Add(targetNode); targetNode.InEdges.Add(sourceNode); }
/// <summary> /// Construct a control flow graph out of the method body /// </summary> /// <param name="instructions"></param> public ControlFlowGraph(SortedList <ushort, IInstruction> instructions) { // Iterate the list of instructions building a list of call graph nodes SortedList <ushort, CallGraphNode> nodes = new SortedList <ushort, CallGraphNode>(); List <IInstruction> nodeInstructions = new List <IInstruction>(); foreach (IInstruction instruction in instructions.Values) { if ((instruction.IsTarget) && (nodeInstructions.Count > 0)) { // This is the start of a block. Create the previous block CallGraphNode callGraphNode = new CallGraphNode(nodeInstructions); nodes.Add(callGraphNode.StartIP, callGraphNode); nodeInstructions = new List <IInstruction>(); } nodeInstructions.Add(instruction); } // Add the last node to the list if (nodeInstructions.Count > 0) { CallGraphNode callGraphNode = new CallGraphNode(nodeInstructions); nodes.Add(callGraphNode.StartIP, callGraphNode); } // Link the list of nodes for (int i = 0; i < nodes.Count; i++) { CallGraphNode node = nodes.Values[i]; // Examine the last instruction to determine how to link this node to others IInstruction instruction = node.Instructions[node.Instructions.Count - 1]; switch (instruction.OpCode.FlowControl) { case FlowControl.Branch: // Direct branch to a new block node.NodeType = NodeType.OneBranch; LinkNode(node, nodes, (ushort)instruction.Argument); break; case FlowControl.Cond_Branch: if (instruction.OpCode.Value == OpCodes.Switch.Value) { // Conditional branch to n-blocks node.NodeType = NodeType.MultiBranch; foreach (ushort switchTargetIp in (ushort[])instruction.Argument) { LinkNode(node, nodes, switchTargetIp); } } else { // Conditional branch to two blocks node.NodeType = NodeType.TwoBranch; LinkNode(node, nodes, (ushort)instruction.Argument); } // Add the continuation link LinkContinuationNode(node, nodes); break; case FlowControl.Return: node.NodeType = NodeType.Exit; break; case FlowControl.Throw: node.NodeType = NodeType.Throw; // End of graph break; case FlowControl.Break: case FlowControl.Call: case FlowControl.Meta: case FlowControl.Next: #pragma warning disable 612,618 case FlowControl.Phi: #pragma warning restore 612,618 // Add the continuation link node.NodeType = LinkContinuationNode(node, nodes) ? NodeType.FallThrough : NodeType.ExitBlock; break; default: throw new ApplicationException("Unexpected flow control type in instruction " + instruction.OpCode.Name); } } _rootNode = nodes.Values[0]; _nodes = nodes; CheckGraph(); // Remove redundancies and add in-edge information Optimize(nodes); IList <IGraphNode> ln = new List <IGraphNode>(); foreach (CallGraphNode n in _nodes.Values) { ln.Add(n); } PrintGraph(ln); CheckGraph(); // Visit the graph in depth first order and label the nodes _depthFirstSearchLast = new CallGraphNode[nodes.Count]; _depthFirstSearchFirst = new CallGraphNode[nodes.Count]; int first = 0; int last = nodes.Count - 1; DepthFirstTraverse(_rootNode, ref first, ref last); // Find the immediate dominators of each node FindImmediateDominators(); // Check the graph for reducibility FindDerivedSequence(); // Work out the graphs back edges DetermineBackEdges(); ResetTraversal(); StructureCases(); StructureLoops(); StructureIfs(); }