You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
6.1 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. using ICSharpCode.Decompiler.FlowAnalysis;
  9. using ICSharpCode.Decompiler.Util;
  10. namespace ICSharpCode.Decompiler.IL.ControlFlow
  11. {
  12. /// <summary>
  13. /// Holds the control flow graph.
  14. /// A separate graph is computed for each BlockContainer at the start of the block transforms
  15. /// (before loop detection).
  16. /// </summary>
  17. public class ControlFlowGraph
  18. {
  19. readonly BlockContainer container;
  20. /// <summary>
  21. /// The container for which the ControlFlowGraph was created.
  22. ///
  23. /// This may differ from the container currently holding a block,
  24. /// because a transform could have moved the block since the CFG was created.
  25. /// </summary>
  26. public BlockContainer Container { get { return container; } }
  27. /// <summary>
  28. /// Nodes array, indexed by original block index.
  29. ///
  30. /// Originally <c>cfg[i].UserData == container.Blocks[i]</c>,
  31. /// but the ILAst blocks may be moved/reordered by transforms.
  32. /// </summary>
  33. internal readonly ControlFlowNode[] cfg;
  34. /// <summary>
  35. /// Dictionary from Block to ControlFlowNode.
  36. /// Unlike the cfg array, this can be used to discover control flow nodes even after
  37. /// blocks were moved/reordered by transforms.
  38. /// </summary>
  39. readonly Dictionary<Block, ControlFlowNode> dict = new Dictionary<Block, ControlFlowNode>();
  40. /// <summary>
  41. /// nodeHasDirectExitOutOfContainer[i] == true iff cfg[i] directly contains a
  42. /// branch/leave instruction leaving the <c>container</c>.
  43. /// </summary>
  44. readonly BitSet nodeHasDirectExitOutOfContainer;
  45. /// <summary>
  46. /// nodeHasReachableExit[i] == true iff there is a path from cfg[i] to a node not dominated by cfg[i],
  47. /// or if there is a path from cfg[i] to a branch/leave instruction leaving the <c>container</c>.
  48. /// </summary>
  49. readonly BitSet nodeHasReachableExit;
  50. /// <summary>
  51. /// Constructs a control flow graph for the blocks in the given block container.
  52. ///
  53. /// Return statements, exceptions, or branches leaving the block container are not
  54. /// modeled by the control flow graph.
  55. /// </summary>
  56. public ControlFlowGraph(BlockContainer container, CancellationToken cancellationToken = default(CancellationToken))
  57. {
  58. this.container = container;
  59. this.cfg = new ControlFlowNode[container.Blocks.Count];
  60. this.nodeHasDirectExitOutOfContainer = new BitSet(cfg.Length);
  61. for (int i = 0; i < cfg.Length; i++) {
  62. Block block = container.Blocks[i];
  63. cfg[i] = new ControlFlowNode { UserIndex = i, UserData = block };
  64. dict.Add(block, cfg[i]);
  65. }
  66. CreateEdges(cancellationToken);
  67. Dominance.ComputeDominance(cfg[0], cancellationToken);
  68. this.nodeHasReachableExit = Dominance.MarkNodesWithReachableExits(cfg);
  69. this.nodeHasReachableExit.UnionWith(FindNodesWithExitsOutOfContainer());
  70. }
  71. void CreateEdges(CancellationToken cancellationToken)
  72. {
  73. for (int i = 0; i < container.Blocks.Count; i++) {
  74. cancellationToken.ThrowIfCancellationRequested();
  75. var block = container.Blocks[i];
  76. var sourceNode = cfg[i];
  77. foreach (var node in block.Descendants) {
  78. if (node is Branch branch) {
  79. if (branch.TargetBlock.Parent == container) {
  80. sourceNode.AddEdgeTo(cfg[container.Blocks.IndexOf(branch.TargetBlock)]);
  81. } else if (branch.TargetBlock.IsDescendantOf(container)) {
  82. // Internal control flow within a nested container.
  83. } else {
  84. // Branch out of this container into a parent container.
  85. // Like return statements and exceptional exits,
  86. // we ignore this for the CFG and the dominance calculation.
  87. // However, it's relevant for HasReachableExit().
  88. nodeHasDirectExitOutOfContainer.Set(i);
  89. }
  90. } else if (node is Leave leave && !leave.TargetContainer.IsDescendantOf(block)) {
  91. // Leave instructions (like other exits out of the container)
  92. // are ignored for the CFG and dominance,
  93. // but is relevant for HasReachableExit().
  94. // However, a 'leave' that exits the whole function represents a return,
  95. // and is not considered a reachable exit.
  96. if (!leave.IsLeavingFunction) {
  97. nodeHasDirectExitOutOfContainer.Set(i);
  98. }
  99. }
  100. }
  101. }
  102. }
  103. BitSet FindNodesWithExitsOutOfContainer()
  104. {
  105. // Also mark the nodes that exit the block container altogether.
  106. // Invariant: leaving[n.UserIndex] == true implies leaving[n.ImmediateDominator.UserIndex] == true
  107. var leaving = new BitSet(cfg.Length);
  108. foreach (var node in cfg) {
  109. if (leaving[node.UserIndex])
  110. continue;
  111. if (nodeHasDirectExitOutOfContainer[node.UserIndex]) {
  112. for (ControlFlowNode p = node; p != null; p = p.ImmediateDominator) {
  113. if (leaving[p.UserIndex]) {
  114. // we can stop marking when we've reached an already-marked node
  115. break;
  116. }
  117. leaving.Set(p.UserIndex);
  118. }
  119. }
  120. }
  121. return leaving;
  122. }
  123. /// <summary>
  124. /// Gets the ControlFlowNode for the block.
  125. ///
  126. /// Precondition: the block belonged to the <c>container</c> at the start of the block transforms
  127. /// (when the control flow graph was created).
  128. /// </summary>
  129. public ControlFlowNode GetNode(Block block)
  130. {
  131. return dict[block];
  132. }
  133. /// <summary>
  134. /// Returns true iff there is a control flow path from <c>node</c> to one of the following:
  135. /// * branch or leave instruction leaving <c>this.Container</c>
  136. /// * branch instruction within this container to another node that is not dominated by <c>node</c>.
  137. ///
  138. /// If this function returns false, the only way control flow can leave the set of nodes
  139. /// dominated by <c>node</c> is by executing a <c>return</c> or <c>throw</c> instruction.
  140. /// </summary>
  141. public bool HasReachableExit(ControlFlowNode node)
  142. {
  143. Debug.Assert(cfg[node.UserIndex] == node);
  144. return nodeHasReachableExit[node.UserIndex];
  145. }
  146. /// <summary>
  147. /// Gets whether the control flow node directly contains a branch/leave instruction
  148. /// exiting the container.
  149. /// </summary>
  150. public bool HasDirectExitOutOfContainer(ControlFlowNode node)
  151. {
  152. Debug.Assert(cfg[node.UserIndex] == node);
  153. return nodeHasDirectExitOutOfContainer[node.UserIndex];
  154. }
  155. }
  156. }