static ControlFlowGraph ReduceBreak(ControlFlowGraph graph, CfgLoop loop, LoopPart part) { // Func<string> vis = () => graph.ToVis().AddPointer("part", part.Index).ToString(); // For VS Code debug visualisation // Break = needs to exit the loop to be a break if (!part.Break) { return(graph); } // Outside entry = non-structured code, so give up if (part.OutsideEntry) { return(graph); } var children = graph.Children(part.Index); if (children.Length != 2) { throw new ControlFlowGraphException($"Break at {part.Index} has unexpected child count ({children.Length})", graph); } bool isfirstChildInLoop = loop.Body.Any(x => x.Index == children[0]) || children[0] == loop.Header.Index; var exitTarget = isfirstChildInLoop ? children[1] : children[0]; // Add LoopSuccessor edge if this is the last link to the MainExit. var remainingLoopChildren = loop.Body .Where(x => x.Index != part.Index) .Aggregate( (IEnumerable <int>)graph.Children(loop.Header.Index), (current, x) => current.Union(graph.Children(x.Index))); if (loop.MainExit.HasValue && !remainingLoopChildren.Contains(loop.MainExit.Value)) { graph = graph.AddEdge(loop.Header.Index, loop.MainExit.Value, CfgEdge.LoopSuccessor); } if (exitTarget != loop.MainExit) { var targetChildren = graph.Children(exitTarget); if (targetChildren.Length != 1 || targetChildren[0] != loop.MainExit) { return(graph); } var condition = graph.Nodes[part.Index]; if (graph.GetEdgeLabel(part.Index, exitTarget) == CfgEdge.False) { condition = Emit.Negation(condition); } var ifNode = Emit.If(condition, Emit.Seq(graph.Nodes[exitTarget], Emit.Break())); return(graph .RemoveNode(exitTarget) .ReplaceNode(part.Index, ifNode)); } return(ReplaceLoopBranch(graph, part.Index, exitTarget, Emit.Break())); }
public static (ControlFlowGraph, string) Decompile(ControlFlowGraph graph) { if (graph == null) { throw new ArgumentNullException(nameof(graph)); } foreach (var index in graph.GetDfsPostOrder()) { var children = graph.Children(index); if (children.Length != 1 || children[0] == index) { continue; } // If the node is the target of a back-edge then leave it alone: it's probably an empty loop-header node if (graph.GetBackEdges().Any(x => x.end == index)) { continue; } int child = children[0]; // Func<string> vis = () => graph.ToVis().AddPointer("index", index).AddPointer("child", child).ToString(); // For VS Code debug visualisation var childsParents = graph.Parents(child); var grandChildren = graph.Children(child); if (childsParents.Length != 1 || grandChildren.Length > 1) { continue; // Is a jump target from somewhere else as well - can't combine } if (grandChildren.Length == 1 && (grandChildren[0] == index || grandChildren[0] == child)) { continue; // Loops around, not a sequence } var node = graph.Nodes[index]; var childNode = graph.Nodes[child]; var updated = graph .RemoveNode(child) .ReplaceNode(index, Emit.Seq(node, childNode)); foreach (var grandChild in grandChildren) { updated = updated.AddEdge(index, grandChild, graph.GetEdgeLabel(child, grandChild)); } return(updated, $"Reduce sequence (node {index}, child {child})"); } return(graph, null); }
public static (ControlFlowGraph, string) Decompile(ControlFlowGraph graph) { if (graph == null) { throw new ArgumentNullException(nameof(graph)); } foreach (var head in graph.GetDfsPostOrder()) { var(trueChild, falseChild) = graph.GetBinaryChildren(head); if (!trueChild.HasValue || !falseChild.HasValue) { continue; } int after = -1; var then = -1; // Func<string> vis = () => graph.ToVis().AddPointer("head", head).AddPointer("after", after).AddPointer("then", then).ToString(); // For VS Code debug visualisation var parents0 = graph.Parents(trueChild.Value); var parents1 = graph.Parents(falseChild.Value); if (parents0.Length == 1) { var grandChildren = graph.Children(trueChild.Value); if (grandChildren.Length == 1 && grandChildren[0] == falseChild.Value) { then = trueChild.Value; after = falseChild.Value; } } else if (parents1.Length == 1) { var grandChildren = graph.Children(falseChild.Value); if (grandChildren.Length == 1 && grandChildren[0] == trueChild.Value) { then = falseChild.Value; after = trueChild.Value; } } if (after == -1 || then == -1 || after == head) { continue; } var condition = then == falseChild.Value ? Emit.Negation(graph.Nodes[head]) : graph.Nodes[head]; var newNode = Emit.If(condition, graph.Nodes[then]); return(graph.RemoveNode(then).ReplaceNode(head, newNode), Description); } return(graph, null); }
static ControlFlowGraph ReduceContinue(ControlFlowGraph graph, CfgLoop loop, LoopPart part) { // Func<string> vis = () => graph.ToVis().AddPointer("index", index).AddPointer("part", part.Index).ToString(); // For VS Code debug visualisation // Outside entry = non-structured code, so give up // Tail = the tail of a loop always continues by default, don't need a statement - the top-level loop reduction will handle it. // Header = continuing header should be handled by simple loop rule // !Continue = if it's not a jump back to the header, it's not a continue // Break = shouldn't be both a continue and a break if (part.OutsideEntry || part.Tail || part.Header || !part.Continue || part.Break) { return(graph); } var children = graph.Children(part.Index); switch (children.Length) { case 1: { var seq = Emit.Seq(graph.Nodes[part.Index], Emit.Continue()); var tailIndex = loop.Body.First(x => x.Tail).Index; return(graph .ReplaceNode(part.Index, seq) .RemoveEdge(part.Index, loop.Header.Index) .AddEdge(part.Index, tailIndex, CfgEdge.True)); } case 2: return(ReplaceLoopBranch(graph, part.Index, loop.Header.Index, Emit.Continue())); default: throw new ControlFlowGraphException($"Continue at {part.Index} has unexpected child count ({children.Length})", graph); } }
public static (ControlFlowGraph, string) Decompile(ControlFlowGraph graph) { if (graph == null) { throw new ArgumentNullException(nameof(graph)); } var simpleLoopIndices = from index in graph.GetDfsPostOrder() let children = graph.Children(index) where children.Length == 2 && children.Contains(index) select index; foreach (var index in simpleLoopIndices) { // Func<string> vis = () => graph.ToVis().AddPointer("index", index).ToString(); // For VS Code debug visualisation // Add an empty header node, which will put it into the form expected by the general-case loop reducer int?successor = null; foreach (var child in graph.Children(index).Where(x => graph.GetEdgeLabel(index, x) == CfgEdge.LoopSuccessor)) { successor = child; } graph = graph.InsertBefore(index, Emit.Empty(), out var newHeaderIndex); if (successor.HasValue) { graph = graph .RemoveEdge(index, successor.Value) .AddEdge(newHeaderIndex, successor.Value, CfgEdge.LoopSuccessor); } return(graph, "Add empty header for single-node loop"); } return(graph, null); }
public static CfgLoop GetLoopInformation(ControlFlowGraph graph, List <int> nodes) { if (nodes == null) { throw new ArgumentNullException(nameof(nodes)); } if (nodes.Count == 0) { throw new ArgumentException("Empty loop provided to GetLoopInformation", nameof(nodes)); } var body = new List <LoopPart>(); var header = new LoopPart(nodes[0], true); var exits = new HashSet <int>(); // Determine if header can break out of the loop foreach (int child in graph.Children(nodes[0])) { if (nodes.Contains(child)) { continue; } CfgEdge edgeLabel = graph.GetEdgeLabel(nodes[0], child); if (edgeLabel == CfgEdge.LoopSuccessor) // Loop successor pseudo-edges don't count for break-detection { continue; } header = new LoopPart(header.Index, true, Break: true, Negated: edgeLabel == CfgEdge.True); exits.Add(child); } for (int i = 1; i < nodes.Count; i++) { var node = nodes[i]; bool isContinue = false; bool isBreak = false; bool isTail = true; bool negated = false; foreach (int child in graph.Children(node)) { // Func<string> vis = () => ToVis().AddPointer("i", node).AddPointer("child", child).ToString(); // For VS Code debug visualisation if (child == header.Index) // Jump to header = possible continue { isContinue = true; } else if (nodes.Contains(child)) { isTail = false; } else { negated = graph.GetEdgeLabel(node, child) == CfgEdge.False; isBreak = true; exits.Add(child); } } bool hasOutsideEntry = Enumerable.Any(graph.Parents(node), x => !nodes.Contains(x)); body.Add(new LoopPart(node, false, isTail, isBreak, isContinue, hasOutsideEntry, negated)); } var postDom = graph.GetPostDominatorTree(); int?mainExit = postDom.ImmediateDominator(header.Index); while (mainExit.HasValue && body.Any(x => !postDom.Dominates(mainExit.Value, x.Index))) { mainExit = postDom.ImmediateDominator(mainExit.Value); } if (body.Count(x => x.Tail) > 1) // Only allow one tail, pick one of the nodes with the longest path from the header. { var longestPaths = new Dictionary <int, int>(); for (int i = 0; i < body.Count; i++) { var part = body[i]; if (!part.Tail) { continue; } var paths = graph.GetAllReachingPaths(header.Index, part.Index); longestPaths[i] = paths.Select(x => x.Count).Max(); } var longestDistance = longestPaths.Values.Max(); var winner = longestPaths.First(x => x.Value == longestDistance).Key; foreach (var kvp in longestPaths) { if (kvp.Key == winner) { continue; } var part = body[kvp.Key]; body[kvp.Key] = new LoopPart(part.Index, part.Header, false, part.Break, part.Continue, part.OutsideEntry, part.Negated); } } return(new CfgLoop(header, body.ToImmutableList(), exits.ToImmutableList(), mainExit)); }
public static (ControlFlowGraph, string) Decompile(ControlFlowGraph graph) { if (graph == null) { throw new ArgumentNullException(nameof(graph)); } var loops = GetLoops(graph); // Add an empty node for the header so the header will never be a continue / break. Then we can structure // all loops as infinite loops (the most general case) and use a separate step to identify while / do loops. foreach (var index in graph.GetDfsPostOrder()) { foreach (var loop in loops) { if (loop.Header.Index != index) { continue; } var headerNode = graph.Nodes[loop.Header.Index]; if (headerNode is EmptyNode) { continue; } int?successor = null; foreach (var child in graph.Children(loop.Header.Index).Where(x => graph.GetEdgeLabel(loop.Header.Index, x) == CfgEdge.LoopSuccessor)) { successor = child; } graph = graph.InsertBefore(loop.Header.Index, Emit.Empty(), out var newHeaderIndex); if (successor.HasValue) { graph = graph .RemoveEdge(loop.Header.Index, successor.Value) .AddEdge(newHeaderIndex, successor.Value, CfgEdge.LoopSuccessor); } return(graph, "Add empty node for loop header"); } } foreach (var index in graph.GetDfsPostOrder()) { foreach (var loop in loops) { if (loop.Header.Index != index) // Handle non-header exits and back edges (post-order iteration means these will be handled before the header) { var part = loop.Body.FirstOrDefault(x => x.Index == index); if (part == null) { continue; } // Func<string> vis = () => graph.ToVis().AddPointer("index", index).AddPointer("part", part.Index).ToString(); // For VS Code debug visualisation var updated = ReduceContinue(graph, loop, part); if (updated != graph) { return(updated, "Reduce continue"); } updated = ReduceBreak(graph, loop, part); if (updated != graph) { return(updated, "Reduce break"); } } else // Handle header { if (loop.Body.Count != 1 || loop.Body[0].OutsideEntry) // TODO: Add a separate reducer to turn outside entries into gotos + labels { continue; } int tail = loop.Body[0].Index; var loopNode = Emit.Loop(Emit.Seq(graph.Nodes[index], graph.Nodes[tail])); return(graph .RemoveNode(tail) .ReplaceNode(index, loopNode), "Reduce generic loop"); } } } return(graph, null); }
public static (ControlFlowGraph, string) Decompile(ControlFlowGraph graph) { if (graph == null) { throw new ArgumentNullException(nameof(graph)); } foreach (var head in graph.GetDfsPostOrder()) { var(trueChild, falseChild) = graph.GetBinaryChildren(head); if (!trueChild.HasValue || !falseChild.HasValue) { continue; } var left = trueChild.Value; var right = falseChild.Value; // Func<string> vis = () => graph.ToVis().AddPointer("head", head).AddPointer("after", after).AddPointer("left", left).AddPointer("right", right).ToString(); // For VS Code debug visualisation var leftParents = graph.Parents(left); var rightParents = graph.Parents(right); var leftChildren = graph.Children(left); var rightChildren = graph.Children(right); if (leftParents.Length != 1 || rightParents.Length != 1) // Branches of an if can't be jump destinations from elsewhere { continue; } bool isRegularIfThenElse = leftChildren.Length == 1 && rightChildren.Length == 1 && leftChildren[0] == rightChildren[0]; bool isTerminalIfThenElse = leftChildren.Length == 0 && rightChildren.Length == 0; if (!isRegularIfThenElse && !isTerminalIfThenElse) { continue; } var after = isRegularIfThenElse ? leftChildren[0] : -1; if (after == head) { continue; } var newNode = Emit.IfElse( graph.Nodes[head], graph.Nodes[left], graph.Nodes[right]); var updated = graph; if (isRegularIfThenElse) { updated = updated.AddEdge(head, after, CfgEdge.True); } return(updated .RemoveNode(left) .RemoveNode(right) .ReplaceNode(head, newNode), Description); } return(graph, null); }