Browse Source

Fix #861: Improved loop exit point heuristic.

pull/881/merge
Daniel Grunwald 8 years ago
parent
commit
a6c6ddcea1
  1. 36
      ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs

36
ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs

@ -120,7 +120,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} }
} }
#region ExtendLoop #region ExtendLoop
/// <summary> /// <summary>
/// Given a natural loop, add additional CFG nodes to the loop in order /// Given a natural loop, add additional CFG nodes to the loop in order
@ -174,17 +174,16 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// (exception: the loop head itself must always be in-loop) /// (exception: the loop head itself must always be in-loop)
/// ///
/// There are two different cases we need to consider: /// There are two different cases we need to consider:
/// a) There are no exits reachable at all from the loop head. /// 1) There are no exits reachable at all from the loop head.
/// -> it is possible to create a loop with zero exit points by adding all nodes /// -> it is possible to create a loop with zero exit points by adding all nodes
/// dominated by the loop to the loop. /// dominated by the loop to the loop.
/// -> the only way to exit the loop is by "return;" or "throw;" /// -> the only way to exit the loop is by "return;" or "throw;"
/// b) There are some exits reachable from the loop head. /// 2) There are some exits reachable from the loop head.
/// ///
/// In case 1, we can pick a single exit point freely by picking any node that has no reachable exits /// In case 1, we can pick a single exit point freely by picking any node that has no reachable exits
/// (other than the loop head). /// (other than the loop head).
/// All nodes dominated by the exit point are out-of-loop, all other nodes are in-loop. /// All nodes dominated by the exit point are out-of-loop, all other nodes are in-loop.
/// Maximizing the amount of code in the out-of-loop partition is thus simple: sum up the amount of code /// See PickExitPoint() for the heuristic that picks the exit point in this case.
/// over the dominator tree and pick the node with the maximum amount of code.
/// ///
/// In case 2, we need to pick our exit point so that all paths from the loop head /// In 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. /// to the reachable exits run through that exit point.
@ -235,9 +234,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// There are no nodes n so that loopHead dominates a predecessor of n but not n itself // 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. // -> we could build a loop with zero exit points.
ControlFlowNode exitPoint = null; ControlFlowNode exitPoint = null;
int exitPointCodeAmount = -1; int exitPointILOffset = -1;
foreach (var node in loopHead.DominatorTreeChildren) { foreach (var node in loopHead.DominatorTreeChildren) {
PickExitPoint(node, ref exitPoint, ref exitPointCodeAmount); PickExitPoint(node, ref exitPoint, ref exitPointILOffset);
} }
return exitPoint; return exitPoint;
} else { } else {
@ -275,18 +274,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <summary> /// <summary>
/// Pick exit point by picking any node that has no reachable exits. /// Pick exit point by picking any node that has no reachable exits.
/// ///
/// Maximizing the amount of code in the out-of-loop partition is thus simple: sum up the amount of code /// In the common case where the code was compiled with a compiler that emits IL code
/// over the dominator tree and pick the node with the maximum amount of code. /// in source order (like the C# compiler), we can find the "real" exit point
/// by simply picking the block with the highest IL offset.
/// So let's do that instead of maximizing amount of code.
/// </summary> /// </summary>
/// <returns>Code amount in <paramref name="node"/> and its dominated nodes.</returns> /// <returns>Code amount in <paramref name="node"/> and its dominated nodes.</returns>
/// <remarks>This method must not write to the Visited flags on the CFG.</remarks> /// <remarks>This method must not write to the Visited flags on the CFG.</remarks>
int PickExitPoint(ControlFlowNode node, ref ControlFlowNode exitPoint, ref int exitPointCodeAmount) void PickExitPoint(ControlFlowNode node, ref ControlFlowNode exitPoint, ref int exitPointILOffset)
{ {
int codeAmount = ((Block)node.UserData).Children.Count; Block block = (Block)node.UserData;
foreach (var child in node.DominatorTreeChildren) { if (block.ILRange.Start > exitPointILOffset
codeAmount += PickExitPoint(child, ref exitPoint, ref exitPointCodeAmount);
}
if (codeAmount > exitPointCodeAmount
&& !context.ControlFlowGraph.HasReachableExit(node) && !context.ControlFlowGraph.HasReachableExit(node)
&& ((Block)node.UserData).Parent == currentBlockContainer) && ((Block)node.UserData).Parent == currentBlockContainer)
{ {
@ -301,9 +299,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// that prevents us from finding a nice exit for the inner loops, causing // that prevents us from finding a nice exit for the inner loops, causing
// unnecessary gotos. // unnecessary gotos.
exitPoint = node; exitPoint = node;
exitPointCodeAmount = codeAmount; exitPointILOffset = block.ILRange.Start;
return; // don't visit children, they are likely to have even later IL offsets and we'd end up
// moving almost all of the code into the loop.
}
foreach (var child in node.DominatorTreeChildren) {
PickExitPoint(child, ref exitPoint, ref exitPointILOffset);
} }
return codeAmount;
} }
ControlFlowNode[] PrepareReverseCFG(ControlFlowNode loopHead) ControlFlowNode[] PrepareReverseCFG(ControlFlowNode loopHead)

Loading…
Cancel
Save