diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
index ced0883cc..f799ef1a4 100644
--- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
+++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
@@ -67,7 +67,16 @@ namespace ICSharpCode.Decompiler.CSharp
// is already collapsed into stloc(V, ...).
new RemoveDeadVariableInit(),
new SwitchDetection(),
- new LoopDetection(),
+ new BlockILTransform { // per-block transforms
+ PostOrderTransforms = {
+ // Even though it's a post-order block-transform as most other transforms,
+ // let's keep LoopDetection separate for now until there's a compelling
+ // reason to combine it with the other block transforms.
+ // If we ran loop detection after some if structures are already detected,
+ // we might make our life introducing good exit points more difficult.
+ new LoopDetection()
+ }
+ },
new BlockILTransform { // per-block transforms
PostOrderTransforms = {
//new UseExitPoints(),
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/Dominance.cs b/ICSharpCode.Decompiler/FlowAnalysis/Dominance.cs
index 48eae66cf..a6b91e50f 100644
--- a/ICSharpCode.Decompiler/FlowAnalysis/Dominance.cs
+++ b/ICSharpCode.Decompiler/FlowAnalysis/Dominance.cs
@@ -116,21 +116,24 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
///
/// Computes a BitSet where
- /// result[i] == true iff cfg[i] is reachable and there is some node that is reachable from cfg[i] but not dominated by cfg[i].
- /// This is similar to "does cfg[i] have a non-empty dominance frontier", except that it uses non-strict dominance
- /// where the definition of dominance frontiers uses "strictly dominates".
+ /// result[i] == true iff cfg[i] is reachable and there is some node that is
+ /// reachable from cfg[i] but not dominated by cfg[i].
+ ///
+ /// This is similar to "does cfg[i] have a non-empty dominance frontier?",
+ /// except that it uses non-strict dominance where the definition of dominance frontiers
+ /// uses "strictly dominates".
///
/// Precondition:
/// Dominance was computed for cfg and cfg[i].UserIndex == i for all i.
///
- public static BitSet MarkNodesWithReachableExits(IReadOnlyList cfg)
+ public static BitSet MarkNodesWithReachableExits(ControlFlowNode[] cfg)
{
#if DEBUG
- for (int i = 0; i < cfg.Count; i++) {
+ for (int i = 0; i < cfg.Length; i++) {
Debug.Assert(cfg[i].UserIndex == i);
}
#endif
- BitSet nonEmpty = new BitSet(cfg.Count);
+ BitSet nonEmpty = new BitSet(cfg.Length);
foreach (var j in cfg) {
// If j is a join-point (more than one incoming edge):
// `j.IsReachable && j.ImmediateDominator == null` is the root node, which counts as an extra incoming edge
diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
index 053577c15..b64221f27 100644
--- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
+++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
@@ -275,6 +275,7 @@
+
diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
index eed2d8f5c..ee84fda70 100644
--- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
+++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
@@ -35,6 +35,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
public class ConditionDetection : IBlockTransform
{
BlockTransformContext context;
+ BlockContainer currentContainer;
///
/// Builds structured control flow for the block associated with the control flow node.
@@ -46,6 +47,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
public void Run(Block block, BlockTransformContext context)
{
this.context = context;
+ this.currentContainer = (BlockContainer)block.Parent;
// We only embed blocks into this block if they aren't referenced anywhere else,
// so those blocks are dominated by this block.
@@ -236,14 +238,20 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false;
}
+ ///
+ /// Gets whether potentialBranchInstruction is a branch to a block
+ /// that is dominated by cfgNode.
+ /// If this function returns true, we replace the branch instruction with the block itself.
+ ///
bool IsUsableBranchToChild(ControlFlowNode cfgNode, ILInstruction potentialBranchInstruction)
{
Branch br = potentialBranchInstruction as Branch;
if (br == null)
return false;
var targetBlock = br.TargetBlock;
- return targetBlock.Parent == context.Container && cfgNode.Dominates(context.GetNode(targetBlock))
- && targetBlock.IncomingEdgeCount == 1 && targetBlock.FinalInstruction.OpCode == OpCode.Nop;
+ return targetBlock.Parent == currentContainer
+ && targetBlock.IncomingEdgeCount == 1 && targetBlock.FinalInstruction.OpCode == OpCode.Nop
+ && cfgNode.Dominates(context.ControlFlowGraph.GetNode(targetBlock));
}
private void HandleSwitchInstruction(ControlFlowNode cfgNode, Block block, SwitchInstruction sw, ref ILInstruction exitInst)
diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowGraph.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowGraph.cs
new file mode 100644
index 000000000..d0a488f29
--- /dev/null
+++ b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowGraph.cs
@@ -0,0 +1,179 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using ICSharpCode.Decompiler.FlowAnalysis;
+using ICSharpCode.Decompiler.Util;
+
+namespace ICSharpCode.Decompiler.IL.ControlFlow
+{
+ ///
+ /// Holds the control flow graph.
+ /// A separate graph is computed for each BlockContainer at the start of the block transforms
+ /// (before loop detection).
+ ///
+ public class ControlFlowGraph
+ {
+ readonly BlockContainer container;
+
+ ///
+ /// The container for which the ControlFlowGraph was created.
+ ///
+ /// This may differ from the container currently holding a block,
+ /// because a transform could have moved the block since the CFG was created.
+ ///
+ public BlockContainer Container { get { return container; } }
+
+ ///
+ /// Nodes array, indexed by original block index.
+ ///
+ /// Originally cfg[i].UserData == container.Blocks[i],
+ /// but the ILAst blocks may be moved/reordered by transforms.
+ ///
+ internal readonly ControlFlowNode[] cfg;
+
+ ///
+ /// Dictionary from Block to ControlFlowNode.
+ /// Unlike the cfg array, this can be used to discover control flow nodes even after
+ /// blocks were moved/reordered by transforms.
+ ///
+ readonly Dictionary dict = new Dictionary();
+
+ ///
+ /// nodeHasDirectExitOutOfContainer[i] == true iff cfg[i] directly contains a
+ /// branch/leave instruction leaving the container.
+ ///
+ readonly BitSet nodeHasDirectExitOutOfContainer;
+
+ ///
+ /// nodeHasReachableExit[i] == true iff there is a path from cfg[i] to a node not dominated by cfg[i],
+ /// or if there is a path from cfg[i] to a branch/leave instruction leaving the container.
+ ///
+ readonly BitSet nodeHasReachableExit;
+
+ ///
+ /// Constructs a control flow graph for the blocks in the given block container.
+ ///
+ /// Return statements, exceptions, or branches leaving the block container are not
+ /// modeled by the control flow graph.
+ ///
+ public ControlFlowGraph(BlockContainer container, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ this.container = container;
+ this.cfg = new ControlFlowNode[container.Blocks.Count];
+ this.nodeHasDirectExitOutOfContainer = new BitSet(cfg.Length);
+ for (int i = 0; i < cfg.Length; i++) {
+ Block block = container.Blocks[i];
+ cfg[i] = new ControlFlowNode { UserIndex = i, UserData = block };
+ dict.Add(block, cfg[i]);
+ }
+
+ CreateEdges(cancellationToken);
+ Dominance.ComputeDominance(cfg[0], cancellationToken);
+ this.nodeHasReachableExit = Dominance.MarkNodesWithReachableExits(cfg);
+ this.nodeHasReachableExit.UnionWith(FindNodesWithExitsOutOfContainer());
+ }
+
+ void CreateEdges(CancellationToken cancellationToken)
+ {
+ for (int i = 0; i < container.Blocks.Count; i++) {
+ cancellationToken.ThrowIfCancellationRequested();
+ var block = container.Blocks[i];
+ var sourceNode = cfg[i];
+ foreach (var node in block.Descendants) {
+ if (node is Branch branch) {
+ if (branch.TargetBlock.Parent == container) {
+ sourceNode.AddEdgeTo(cfg[container.Blocks.IndexOf(branch.TargetBlock)]);
+ } else if (branch.TargetBlock.IsDescendantOf(container)) {
+ // Internal control flow within a nested container.
+ } else {
+ // Branch out of this container into a parent container.
+ // Like return statements and exceptional exits,
+ // we ignore this for the CFG and the dominance calculation.
+ // However, it's relevant for HasReachableExit().
+ nodeHasDirectExitOutOfContainer.Set(i);
+ }
+ } else if (node is Leave leave && !leave.TargetContainer.IsDescendantOf(block)) {
+ // Leave instructions (like other exits out of the container)
+ // are ignored for the CFG and dominance,
+ // but is relevant for HasReachableExit().
+ nodeHasDirectExitOutOfContainer.Set(i);
+ }
+ }
+ }
+ }
+
+ BitSet FindNodesWithExitsOutOfContainer()
+ {
+ // Also mark the nodes that exit the block container altogether.
+ // Invariant: leaving[n.UserIndex] == true implies leaving[n.ImmediateDominator.UserIndex] == true
+ var leaving = new BitSet(cfg.Length);
+ foreach (var node in cfg) {
+ if (leaving[node.UserIndex])
+ continue;
+ if (nodeHasDirectExitOutOfContainer[node.UserIndex]) {
+ for (ControlFlowNode p = node; p != null; p = p.ImmediateDominator) {
+ if (leaving[p.UserIndex]) {
+ // we can stop marking when we've reached an already-marked node
+ break;
+ }
+ leaving.Set(p.UserIndex);
+ }
+ }
+ }
+ return leaving;
+ }
+
+ bool LeavesCurrentBlockContainer(Block block)
+ {
+ foreach (var node in block.Descendants) {
+ if (node is Branch branch && !branch.TargetBlock.IsDescendantOf(container)) {
+ // control flow that isn't internal to the block container
+ return true;
+ }
+ if (node is Leave leave && !leave.TargetContainer.IsDescendantOf(block)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Gets the ControlFlowNode for the block.
+ ///
+ /// Precondition: the block belonged to the container at the start of the block transforms
+ /// (when the control flow graph was created).
+ ///
+ public ControlFlowNode GetNode(Block block)
+ {
+ return dict[block];
+ }
+
+ ///
+ /// Returns true iff there is a control flow path from node to one of the following:
+ /// * branch or leave instruction leaving this.Container
+ /// * branch instruction within this container to another node that is not dominated by node.
+ ///
+ /// If this function returns false, the only way control flow can leave the set of nodes
+ /// dominated by node is by executing a return or throw instruction.
+ ///
+ public bool HasReachableExit(ControlFlowNode node)
+ {
+ Debug.Assert(cfg[node.UserIndex] == node);
+ return nodeHasReachableExit[node.UserIndex];
+ }
+
+ ///
+ /// Gets whether the control flow node directly contains a branch/leave instruction
+ /// exiting the container.
+ ///
+ public bool HasDirectExitOutOfContainer(ControlFlowNode node)
+ {
+ Debug.Assert(cfg[node.UserIndex] == node);
+ return nodeHasDirectExitOutOfContainer[node.UserIndex];
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs
index cfa8599cf..689d8aa4e 100644
--- a/ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs
+++ b/ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs
@@ -190,6 +190,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
+ /*
///
/// Like DetectExitPoints, but only uses existing exit points
/// (by replacing compatible instructions with Leave),
@@ -202,6 +203,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
public void Run(Block block, BlockTransformContext context)
{
+ Debug.Assert(block.Parent == context.Container);
currentContainer = context.Container;
exitPoint = DetectExitPoints.GetExit(context.Container);
Visit(block);
@@ -227,4 +229,5 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
}
+ */
}
diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
index ed3b5b5ca..e214c081a 100644
--- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
+++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
@@ -35,113 +35,44 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// don't include more instructions than strictly necessary.
/// * Loop detection should run after the 'return block' is duplicated (ControlFlowSimplification).
///
- public class LoopDetection : IILTransform
+ public class LoopDetection : IBlockTransform
{
- #region Construct Control Flow Graph
- ///
- /// Constructs a control flow graph for the blocks in the given block container.
- /// The graph nodes will have the same indices as the blocks in the block container.
- /// Return statements, exceptions, or branches leaving the block container are not
- /// modeled by the control flow graph.
- ///
- internal static ControlFlowNode[] BuildCFG(BlockContainer bc)
- {
- ControlFlowNode[] nodes = new ControlFlowNode[bc.Blocks.Count];
- for (int i = 0; i < bc.Blocks.Count; i++) {
- nodes[i] = new ControlFlowNode { UserIndex = i, UserData = bc.Blocks[i] };
- }
-
- // Create edges:
- for (int i = 0; i < bc.Blocks.Count; i++) {
- var block = bc.Blocks[i];
- var sourceNode = nodes[i];
- foreach (var branch in block.Descendants.OfType()) {
- if (branch.TargetBlock.Parent == bc) {
- sourceNode.AddEdgeTo(nodes[bc.Blocks.IndexOf(branch.TargetBlock)]);
- } else {
- // Note: edges into different block containers are ignored:
- // Either they point to a nested block container in the source block,
- // in which case we can ignore them for control flow purposes;
- // or they jump to a parent block container, in which case they act
- // like a return statement or exceptional exit.
- }
- }
- }
-
- return nodes;
- }
- #endregion
-
- ///
- /// Run loop detection for all block containers in the function (including nested lambda functions).
- ///
- public void Run(ILFunction function, ILTransformContext context)
- {
- foreach (var blockContainer in function.Descendants.OfType()) {
- Run(blockContainer, context);
- }
- }
-
- ILTransformContext context;
-
- ControlFlowNode[] cfg;
-
- ///
- /// nodeHasReachableExit[i] == true iff there is a path from cfg[i] to a node not dominated by cfg[i],
- /// or if there is a path from cfg[i] to a branch/leave instruction leaving the currentBlockContainer.
- ///
- BitSet nodeHasReachableExit;
-
- ///
- /// nodeHasDirectExitOutOfContainer[i] == true iff cfg[i] directly contains a branch/leave instruction leaving the currentBlockContainer.
- ///
- BitSet nodeHasDirectExitOutOfContainer;
+ BlockTransformContext context;
/// Block container corresponding to the current cfg.
BlockContainer currentBlockContainer;
///
- /// Run loop detection for blocks in the block container.
+ /// Check whether 'block' is a loop head; and construct a loop instruction
+ /// (nested BlockContainer) if it is.
///
- public void Run(BlockContainer blockContainer, ILTransformContext context)
+ public void Run(Block block, BlockTransformContext context)
{
this.context = context;
- this.currentBlockContainer = blockContainer;
- this.cfg = BuildCFG(blockContainer);
- this.nodeHasReachableExit = null; // will be computed on-demand
- this.nodeHasDirectExitOutOfContainer = null; // will be computed on-demand
-
- var entryPoint = cfg[0];
- Dominance.ComputeDominance(entryPoint, context.CancellationToken);
- FindLoops(entryPoint);
-
- this.cfg = null;
- this.nodeHasReachableExit = null;
- this.nodeHasDirectExitOutOfContainer = null;
- this.currentBlockContainer = null;
- this.context = null;
- }
-
- ///
- /// Recurse into the dominator tree and find back edges/natural loops.
- ///
- ///
- /// 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.
- ///
- /// Preconditions:
- /// * dominance was computed for h
- /// * all blocks in the dominator subtree starting at h are in the same BlockContainer
- /// * the visited flag is set to false
- ///
- void FindLoops(ControlFlowNode h)
- {
+ // 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.
+
+ 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 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();
loop.Add(h);
@@ -158,21 +89,36 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// 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 nested loops
+ node.Visited = false; // reset visited flag so that we can find outer loops
Debug.Assert(h.Dominates(node), "The loop body must be dominated by the loop head");
}
ConstructLoop(loop, exitPoint);
}
+ }
+
+ ///
+ /// Recurse into the dominator tree and find back edges/natural loops.
+ ///
+ ///
+ ///
+ /// Preconditions:
+ /// * dominance was computed for h
+ /// * all blocks in the dominator subtree starting at h are in the same BlockContainer
+ /// * the visited flag is set to false
+ ///
+ void FindLoops(ControlFlowNode h)
+ {
// Recurse into the dominator tree to find other possible loop heads
foreach (var child in h.DominatorTreeChildren) {
FindLoops(child);
}
+
}
#region ExtendLoop
@@ -259,7 +205,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
///
void ExtendLoop(ControlFlowNode loopHead, List loop, out ControlFlowNode exitPoint)
{
- ComputeNodesWithReachableExits();
exitPoint = FindExitPoint(loopHead, loop);
Debug.Assert(!loop.Contains(exitPoint), "Cannot pick an exit point that is part of the natural loop");
if (exitPoint != null) {
@@ -279,55 +224,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
- void ComputeNodesWithReachableExits()
- {
- if (nodeHasReachableExit != null)
- return;
- nodeHasReachableExit = Dominance.MarkNodesWithReachableExits(cfg);
- nodeHasDirectExitOutOfContainer = new BitSet(cfg.Length);
- // Also mark the nodes that exit the block container altogether.
- // Invariant: leaving[n.UserIndex] == true implies leaving[n.ImmediateDominator.UserIndex] == true
- var leaving = new BitSet(cfg.Length);
- foreach (var node in cfg) {
- if (leaving[node.UserIndex])
- continue;
- if (LeavesCurrentBlockContainer((Block)node.UserData)) {
- nodeHasDirectExitOutOfContainer.Set(node.UserIndex);
- for (ControlFlowNode p = node; p != null; p = p.ImmediateDominator) {
- if (leaving[p.UserIndex]) {
- // we can stop marking when we've reached an already-marked node
- break;
- }
- leaving.Set(p.UserIndex);
- }
- }
- }
- nodeHasReachableExit.UnionWith(leaving);
- }
-
- bool LeavesCurrentBlockContainer(Block block)
- {
- foreach (var branch in block.Descendants.OfType()) {
- if (!branch.TargetBlock.IsDescendantOf(currentBlockContainer)) {
- // control flow that isn't internal to the block container
- return true;
- }
- }
- foreach (var leave in block.Descendants.OfType()) {
- if (!leave.TargetContainer.IsDescendantOf(block)) {
- return true;
- }
- }
- return false;
- }
-
///
/// Finds a suitable single exit point for the specified loop.
///
/// This method must not write to the Visited flags on the CFG.
ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList naturalLoop)
{
- if (!nodeHasReachableExit[loopHead.UserIndex]) {
+ if (!context.ControlFlowGraph.HasReachableExit(loopHead)) {
// 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.
@@ -341,6 +244,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// 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);
//ControlFlowNode.ExportGraph(cfg).Show("cfg");
//ControlFlowNode.ExportGraph(revCfg).Show("rev");
@@ -382,7 +286,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
foreach (var child in node.DominatorTreeChildren) {
codeAmount += PickExitPoint(child, ref exitPoint, ref exitPointCodeAmount);
}
- if (codeAmount > exitPointCodeAmount && !nodeHasReachableExit[node.UserIndex]) {
+ if (codeAmount > exitPointCodeAmount && !context.ControlFlowGraph.HasReachableExit(node)) {
// dominanceFrontier(node) is empty
// -> there are no nodes n so that `node` dominates a predecessor of n but not n itself
// -> there is no control flow out of `node` back into the loop, so it's usable as exit point
@@ -394,7 +298,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
ControlFlowNode[] PrepareReverseCFG(ControlFlowNode loopHead)
{
- ControlFlowNode[] cfg = this.cfg;
+ 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 };
@@ -412,7 +316,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
exitNode.AddEdgeTo(rev[i]);
}
}
- if (nodeHasDirectExitOutOfContainer[i]) {
+ if (context.ControlFlowGraph.HasDirectExitOutOfContainer(cfg[i])) {
exitNode.AddEdgeTo(rev[i]);
}
}
@@ -487,6 +391,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
///
void ConstructLoop(List loop, ControlFlowNode exitPoint)
{
+ exitPoint = null; // TODO
Block oldEntryPoint = (Block)loop[0].UserData;
Block exitTargetBlock = (Block)exitPoint?.UserData;
@@ -507,13 +412,23 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// and thus cannot be the target of branch instructions outside the loop.
for (int i = 1; i < loop.Count; i++) {
Block block = (Block)loop[i].UserData;
- Debug.Assert(block.ChildIndex != 0);
- var oldParent = ((BlockContainer)block.Parent);
- int oldChildIndex = block.ChildIndex;
- loopContainer.Blocks.Add(block);
- oldParent.Blocks.SwapRemoveAt(oldChildIndex);
+ // some blocks might already be in use by nested loops that were detected earlier;
+ // don't move those (they'll be implicitly moved when the block containing the
+ // nested loop container is moved).
+ if (block.Parent == currentBlockContainer) {
+ Debug.Assert(block.ChildIndex != 0);
+ int oldChildIndex = block.ChildIndex;
+ loopContainer.Blocks.Add(block);
+ currentBlockContainer.Blocks.SwapRemoveAt(oldChildIndex);
+ }
+ }
+ for (int i = 1; i < loop.Count; i++) {
+ // Verify that we moved all loop blocks into the loop container.
+ // If we wanted to move any blocks already in use by a nested loop,
+ // this means we check that the whole nested loop got moved.
+ Block block = (Block)loop[i].UserData;
+ Debug.Assert(block.IsDescendantOf(loopContainer));
}
-
// Rewrite branches within the loop from oldEntryPoint to newEntryPoint:
foreach (var branch in loopContainer.Descendants.OfType()) {
if (branch.TargetBlock == oldEntryPoint) {
diff --git a/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs
index a4c027173..fb62f81f2 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs
@@ -14,7 +14,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
///
- /// Parameter class holding various arguments for .
+ /// Parameter class holding various arguments for .
///
public class BlockTransformContext : ILTransformContext
{
@@ -23,11 +23,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
///
public ILFunction Function { get; set; }
- ///
- /// The container containing the block currently being processed.
- ///
- public BlockContainer Container { get; set; }
-
///
/// The block to process.
///
@@ -39,22 +34,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms
///
/// The control flow node corresponding to the block being processed.
///
+ ///
+ /// Identical to ControlFlowGraph.GetNode(Block).
+ /// Note: the control flow graph is not up-to-date, but was created at the start of the
+ /// block transforms (before loop detection).
+ ///
public ControlFlowNode ControlFlowNode { get; set; }
- internal readonly Dictionary cfg = new Dictionary();
-
- public BlockTransformContext(ILTransformContext context) : base(context)
- {
- }
-
///
- /// Gets the ControlFlowNode for the block.
- /// Precondition: the block belonged to the Container at the start of the block transforms
- /// (when the control flow graph was created).
+ /// Gets the control flow graph.
+ ///
+ /// Note: the control flow graph is not up-to-date, but was created at the start of the
+ /// block transforms (before loop detection).
///
- public ControlFlowNode GetNode(Block block)
+ public ControlFlowGraph ControlFlowGraph { get; set; }
+
+ public BlockTransformContext(ILTransformContext context) : base(context)
{
- return cfg[block];
}
}
@@ -73,15 +69,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
blockContext.Function = function;
foreach (var container in function.Descendants.OfType().ToList()) {
context.CancellationToken.ThrowIfCancellationRequested();
- var cfg = LoopDetection.BuildCFG(container);
- Dominance.ComputeDominance(cfg[0], context.CancellationToken);
-
- blockContext.Container = container;
- blockContext.cfg.Clear();
- for (int i = 0; i < cfg.Length; i++) {
- blockContext.cfg.Add(container.Blocks[i], cfg[i]);
- }
- VisitBlock(cfg[0], blockContext);
+ blockContext.ControlFlowGraph = new ControlFlowGraph(container, context.CancellationToken);
+ VisitBlock(blockContext.ControlFlowGraph.GetNode(container.EntryPoint), blockContext);
// TODO: handle unreachable code?
}
}
diff --git a/ICSharpCode.Decompiler/Tests/RoundtripAssembly.cs b/ICSharpCode.Decompiler/Tests/RoundtripAssembly.cs
index 737efba64..35919b2ec 100644
--- a/ICSharpCode.Decompiler/Tests/RoundtripAssembly.cs
+++ b/ICSharpCode.Decompiler/Tests/RoundtripAssembly.cs
@@ -21,6 +21,7 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
+using System.Threading;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.Tests.Helpers;
using Microsoft.Win32;
@@ -152,7 +153,16 @@ namespace ICSharpCode.Decompiler.Tests
{
Directory.CreateDirectory(dir);
foreach (string subdir in Directory.EnumerateDirectories(dir)) {
- Directory.Delete(subdir, true);
+ for (int attempt = 0; ; attempt++) {
+ try {
+ Directory.Delete(subdir, true);
+ break;
+ } catch (IOException) {
+ if (attempt >= 10)
+ throw;
+ Thread.Sleep(100);
+ }
+ }
}
foreach (string file in Directory.EnumerateFiles(dir)) {
File.Delete(file);