Browse Source

Convert LoopDetection into a block transform.

pull/728/merge
Daniel Grunwald 9 years ago
parent
commit
e871f7c05a
  1. 11
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 15
      ICSharpCode.Decompiler/FlowAnalysis/Dominance.cs
  3. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  4. 12
      ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
  5. 179
      ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowGraph.cs
  6. 3
      ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs
  7. 211
      ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
  8. 41
      ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs
  9. 12
      ICSharpCode.Decompiler/Tests/RoundtripAssembly.cs

11
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(),

15
ICSharpCode.Decompiler/FlowAnalysis/Dominance.cs

@ -116,21 +116,24 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// <summary>
/// Computes a BitSet where
/// <c>result[i] == true</c> 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".
/// <c>result[i] == true</c> 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 <c>cfg[i].UserIndex == i</c> for all i.
/// </summary>
public static BitSet MarkNodesWithReachableExits(IReadOnlyList<ControlFlowNode> 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

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -275,6 +275,7 @@
<Compile Include="Documentation\IdStringMemberReference.cs" />
<Compile Include="Documentation\IdStringProvider.cs" />
<Compile Include="Documentation\XmlDocumentationProvider.cs" />
<Compile Include="IL\ControlFlow\ControlFlowGraph.cs" />
<Compile Include="IL\Patterns\AnyNode.cs" />
<Compile Include="Util\UnicodeNewline.cs" />
<Compile Include="FlowAnalysis\ControlFlowNode.cs" />

12
ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs

@ -35,6 +35,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
public class ConditionDetection : IBlockTransform
{
BlockTransformContext context;
BlockContainer currentContainer;
/// <summary>
/// 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;
}
/// <summary>
/// Gets whether <c>potentialBranchInstruction</c> is a branch to a block
/// that is dominated by <c>cfgNode</c>.
/// If this function returns true, we replace the branch instruction with the block itself.
/// </summary>
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)

179
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
{
/// <summary>
/// Holds the control flow graph.
/// A separate graph is computed for each BlockContainer at the start of the block transforms
/// (before loop detection).
/// </summary>
public class ControlFlowGraph
{
readonly BlockContainer container;
/// <summary>
/// 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.
/// </summary>
public BlockContainer Container { get { return container; } }
/// <summary>
/// Nodes array, indexed by original block index.
///
/// Originally <c>cfg[i].UserData == container.Blocks[i]</c>,
/// but the ILAst blocks may be moved/reordered by transforms.
/// </summary>
internal readonly ControlFlowNode[] cfg;
/// <summary>
/// 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.
/// </summary>
readonly Dictionary<Block, ControlFlowNode> dict = new Dictionary<Block, ControlFlowNode>();
/// <summary>
/// nodeHasDirectExitOutOfContainer[i] == true iff cfg[i] directly contains a
/// branch/leave instruction leaving the <c>container</c>.
/// </summary>
readonly BitSet nodeHasDirectExitOutOfContainer;
/// <summary>
/// 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 <c>container</c>.
/// </summary>
readonly BitSet nodeHasReachableExit;
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// Gets the ControlFlowNode for the block.
///
/// Precondition: the block belonged to the <c>container</c> at the start of the block transforms
/// (when the control flow graph was created).
/// </summary>
public ControlFlowNode GetNode(Block block)
{
return dict[block];
}
/// <summary>
/// Returns true iff there is a control flow path from <c>node</c> to one of the following:
/// * branch or leave instruction leaving <c>this.Container</c>
/// * branch instruction within this container to another node that is not dominated by <c>node</c>.
///
/// If this function returns false, the only way control flow can leave the set of nodes
/// dominated by <c>node</c> is by executing a <c>return</c> or <c>throw</c> instruction.
/// </summary>
public bool HasReachableExit(ControlFlowNode node)
{
Debug.Assert(cfg[node.UserIndex] == node);
return nodeHasReachableExit[node.UserIndex];
}
/// <summary>
/// Gets whether the control flow node directly contains a branch/leave instruction
/// exiting the container.
/// </summary>
public bool HasDirectExitOutOfContainer(ControlFlowNode node)
{
Debug.Assert(cfg[node.UserIndex] == node);
return nodeHasDirectExitOutOfContainer[node.UserIndex];
}
}
}

3
ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs

@ -190,6 +190,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
/*
/// <summary>
/// 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
}
}
}
*/
}

211
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).
/// </remarks>
public class LoopDetection : IILTransform
public class LoopDetection : IBlockTransform
{
#region Construct Control Flow Graph
/// <summary>
/// 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.
/// </summary>
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<Branch>()) {
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
/// <summary>
/// Run loop detection for all block containers in the function (including nested lambda functions).
/// </summary>
public void Run(ILFunction function, ILTransformContext context)
{
foreach (var blockContainer in function.Descendants.OfType<BlockContainer>()) {
Run(blockContainer, context);
}
}
ILTransformContext context;
ControlFlowNode[] cfg;
/// <summary>
/// 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.
/// </summary>
BitSet nodeHasReachableExit;
/// <summary>
/// nodeHasDirectExitOutOfContainer[i] == true iff cfg[i] directly contains a branch/leave instruction leaving the currentBlockContainer.
/// </summary>
BitSet nodeHasDirectExitOutOfContainer;
BlockTransformContext context;
/// <summary>Block container corresponding to the current cfg.</summary>
BlockContainer currentBlockContainer;
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// Recurse into the dominator tree and find back edges/natural loops.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
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<ControlFlowNode> 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<ControlFlowNode>();
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);
}
}
/// <summary>
/// Recurse into the dominator tree and find back edges/natural loops.
/// </summary>
/// <remarks>
///
/// 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
/// </remarks>
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
/// </remarks>
void ExtendLoop(ControlFlowNode loopHead, List<ControlFlowNode> 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<Branch>()) {
if (!branch.TargetBlock.IsDescendantOf(currentBlockContainer)) {
// control flow that isn't internal to the block container
return true;
}
}
foreach (var leave in block.Descendants.OfType<Leave>()) {
if (!leave.TargetContainer.IsDescendantOf(block)) {
return true;
}
}
return false;
}
/// <summary>
/// Finds a suitable single exit point for the specified loop.
/// </summary>
/// <remarks>This method must not write to the Visited flags on the CFG.</remarks>
ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList<ControlFlowNode> 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
/// </summary>
void ConstructLoop(List<ControlFlowNode> 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<Branch>()) {
if (branch.TargetBlock == oldEntryPoint) {

41
ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs

@ -14,7 +14,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
/// <summary>
/// Parameter class holding various arguments for <see cref="IBlockTransform.Run(ILFunction, BlockTransformContext)"/>.
/// Parameter class holding various arguments for <see cref="IBlockTransform.Run"/>.
/// </summary>
public class BlockTransformContext : ILTransformContext
{
@ -23,11 +23,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary>
public ILFunction Function { get; set; }
/// <summary>
/// The container containing the block currently being processed.
/// </summary>
public BlockContainer Container { get; set; }
/// <summary>
/// The block to process.
/// </summary>
@ -39,22 +34,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// The control flow node corresponding to the block being processed.
/// </summary>
/// <remarks>
/// Identical to <c>ControlFlowGraph.GetNode(Block)</c>.
/// Note: the control flow graph is not up-to-date, but was created at the start of the
/// block transforms (before loop detection).
/// </remarks>
public ControlFlowNode ControlFlowNode { get; set; }
internal readonly Dictionary<Block, ControlFlowNode> cfg = new Dictionary<Block, ControlFlowNode>();
public BlockTransformContext(ILTransformContext context) : base(context)
{
}
/// <summary>
/// Gets the ControlFlowNode for the block.
/// Precondition: the block belonged to the <c>Container</c> 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).
/// </summary>
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<BlockContainer>().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?
}
}

12
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);

Loading…
Cancel
Save