
9 changed files with 301 additions and 184 deletions
-
11ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
-
15ICSharpCode.Decompiler/FlowAnalysis/Dominance.cs
-
1ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
-
12ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
-
179ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowGraph.cs
-
3ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs
-
211ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
-
41ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs
-
12ICSharpCode.Decompiler/Tests/RoundtripAssembly.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]; |
|||
} |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue