cfg_dominators.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. """Computes dominator trees for control-flow graphs."""
  2. from collections import defaultdict
  3. def sort_postorder(entry_point):
  4. """Produces a postorder traversal of the graph with the given block as entry point."""
  5. processed = set()
  6. results = []
  7. def __sort_postorder_step(block):
  8. if block in processed:
  9. return
  10. processed.add(block)
  11. for branch in block.flow.branches():
  12. __sort_postorder_step(branch.block)
  13. results.append(block)
  14. __sort_postorder_step(entry_point)
  15. return results
  16. def get_all_predecessor_blocks(entry_point):
  17. """Creates a mapping of blocks to their direct predecessors for every block in the control-flow
  18. graph defined by the given entry point."""
  19. results = defaultdict(set)
  20. processed = set()
  21. def __find_predecessors_step(block):
  22. if block in processed:
  23. return
  24. processed.add(block)
  25. for branch in block.flow.branches():
  26. target_block = branch.block
  27. results[target_block].add(block)
  28. __find_predecessors_step(target_block)
  29. __find_predecessors_step(entry_point)
  30. return results
  31. def intersect_immediate_dominators(block1, block2, idoms, postorder_nums):
  32. """Computes the intersection of the given blocks' immediate dominators."""
  33. finger1 = block1
  34. finger2 = block2
  35. while finger1 != finger2:
  36. while postorder_nums[finger1] < postorder_nums[finger2]:
  37. finger1 = idoms[finger1]
  38. while postorder_nums[finger2] < postorder_nums[finger1]:
  39. finger2 = idoms[finger2]
  40. return finger1
  41. def get_immediate_dominators(entry_point):
  42. """Computes the immediate dominators of the control-flow graph defined by the given entry
  43. point."""
  44. # Based on "A Simple, Fast Dominance Algorithm" by
  45. # Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy
  46. # (http://www.cs.rice.edu/~keith/Embed/dom.pdf)
  47. predecessor_map = get_all_predecessor_blocks(entry_point)
  48. idoms = {}
  49. postorder_sort = sort_postorder(entry_point)
  50. postorder_nums = {}
  51. for i, elem in enumerate(postorder_sort):
  52. postorder_nums[elem] = i
  53. idoms[elem] = None
  54. idoms[entry_point] = entry_point
  55. changed = True
  56. while changed:
  57. changed = False
  58. for block in reversed(postorder_sort):
  59. if block == entry_point:
  60. continue
  61. new_idom = None
  62. for pred in predecessor_map[entry_point]:
  63. if pred not in postorder_nums:
  64. continue
  65. if new_idom is None:
  66. new_idom = pred
  67. elif idoms[pred] is not None:
  68. new_idom = intersect_immediate_dominators(pred, new_idom, idoms, postorder_nums)
  69. if idoms[block] is not new_idom:
  70. idoms[block] = new_idom
  71. changed = True
  72. return idoms
  73. def get_dominator_tree(entry_point):
  74. """Constructs the dominator tree for the control-flow graph defined by the given entry point."""
  75. return DominatorTree(get_immediate_dominators(entry_point))
  76. class DominatorTree(object):
  77. """A data structure that represents a dominator tree."""
  78. def __init__(self, immediate_dominators):
  79. self.immediate_dominators = immediate_dominators
  80. self.dominator_sets = {}
  81. def get_immediate_dominator(self, block):
  82. """Gets the given block's immediate dominator."""
  83. return self.immediate_dominators[block]
  84. def get_dominators(self, block):
  85. """Gets the given block's set of all dominators."""
  86. if block in self.dominator_sets:
  87. return self.dominator_sets
  88. else:
  89. idom = self.get_immediate_dominator(block)
  90. if idom == block:
  91. results = set([block])
  92. else:
  93. results = set(self.get_dominators(idom))
  94. results.add(block)
  95. self.dominator_sets[block] = results
  96. return results
  97. def dominates(self, dominator, dominated):
  98. """Tests if the first block dominates the second."""
  99. return dominator in self.get_dominators(dominated)