|
@@ -0,0 +1,123 @@
|
|
|
+"""Computes dominator trees for control-flow graphs."""
|
|
|
+
|
|
|
+from collections import defaultdict
|
|
|
+
|
|
|
+def sort_postorder(entry_point):
|
|
|
+ """Produces a postorder traversal of the graph with the given block as entry point."""
|
|
|
+ processed = set()
|
|
|
+ results = []
|
|
|
+ def __sort_postorder_step(block):
|
|
|
+ if block in processed:
|
|
|
+ return
|
|
|
+
|
|
|
+ processed.add(block)
|
|
|
+ for branch in block.flow.branches():
|
|
|
+ __sort_postorder_step(branch.block)
|
|
|
+
|
|
|
+ results.append(block)
|
|
|
+
|
|
|
+ __sort_postorder_step(entry_point)
|
|
|
+ return results
|
|
|
+
|
|
|
+def get_all_predecessor_blocks(entry_point):
|
|
|
+ """Creates a mapping of blocks to their direct predecessors for every block in the control-flow
|
|
|
+ graph defined by the given entry point."""
|
|
|
+ results = defaultdict(set)
|
|
|
+ processed = set()
|
|
|
+ def __find_predecessors_step(block):
|
|
|
+ if block in processed:
|
|
|
+ return
|
|
|
+
|
|
|
+ processed.add(block)
|
|
|
+ for branch in block.flow.branches():
|
|
|
+ target_block = branch.block
|
|
|
+ results[target_block].add(block)
|
|
|
+ __find_predecessors_step(target_block)
|
|
|
+
|
|
|
+ __find_predecessors_step(entry_point)
|
|
|
+ return results
|
|
|
+
|
|
|
+def intersect_immediate_dominators(block1, block2, idoms, postorder_nums):
|
|
|
+ """Computes the intersection of the given blocks' immediate dominators."""
|
|
|
+ finger1 = block1
|
|
|
+ finger2 = block2
|
|
|
+ while finger1 != finger2:
|
|
|
+ while postorder_nums[finger1] < postorder_nums[finger2]:
|
|
|
+ finger1 = idoms[finger1]
|
|
|
+
|
|
|
+ while postorder_nums[finger2] < postorder_nums[finger1]:
|
|
|
+ finger2 = idoms[finger2]
|
|
|
+ return finger1
|
|
|
+
|
|
|
+def get_immediate_dominators(entry_point):
|
|
|
+ """Computes the immediate dominators of the control-flow graph defined by the given entry
|
|
|
+ point."""
|
|
|
+ # Based on "A Simple, Fast Dominance Algorithm" by
|
|
|
+ # Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy
|
|
|
+ # (http://www.cs.rice.edu/~keith/Embed/dom.pdf)
|
|
|
+
|
|
|
+ predecessor_map = get_all_predecessor_blocks(entry_point)
|
|
|
+ idoms = {}
|
|
|
+ postorder_sort = sort_postorder(entry_point)
|
|
|
+ postorder_nums = {}
|
|
|
+ for i, elem in enumerate(postorder_sort):
|
|
|
+ postorder_nums[elem] = i
|
|
|
+ idoms[elem] = None
|
|
|
+
|
|
|
+ idoms[entry_point] = entry_point
|
|
|
+
|
|
|
+ changed = True
|
|
|
+ while changed:
|
|
|
+ changed = False
|
|
|
+ for block in reversed(postorder_sort):
|
|
|
+ if block == entry_point:
|
|
|
+ continue
|
|
|
+
|
|
|
+ new_idom = None
|
|
|
+ for pred in predecessor_map[entry_point]:
|
|
|
+ if pred not in postorder_nums:
|
|
|
+ continue
|
|
|
+
|
|
|
+ if new_idom is None:
|
|
|
+ new_idom = pred
|
|
|
+ elif idoms[pred] is not None:
|
|
|
+ new_idom = intersect_immediate_dominators(pred, new_idom, idoms, postorder_nums)
|
|
|
+
|
|
|
+ if idoms[block] is not new_idom:
|
|
|
+ idoms[block] = new_idom
|
|
|
+ changed = True
|
|
|
+
|
|
|
+ return idoms
|
|
|
+
|
|
|
+def get_dominator_tree(entry_point):
|
|
|
+ """Constructs the dominator tree for the control-flow graph defined by the given entry point."""
|
|
|
+ return DominatorTree(get_immediate_dominators(entry_point))
|
|
|
+
|
|
|
+class DominatorTree(object):
|
|
|
+ """A data structure that represents a dominator tree."""
|
|
|
+ def __init__(self, immediate_dominators):
|
|
|
+ self.immediate_dominators = immediate_dominators
|
|
|
+ self.dominator_sets = {}
|
|
|
+
|
|
|
+ def get_immediate_dominator(self, block):
|
|
|
+ """Gets the given block's immediate dominator."""
|
|
|
+ return self.immediate_dominators[block]
|
|
|
+
|
|
|
+ def get_dominators(self, block):
|
|
|
+ """Gets the given block's set of all dominators."""
|
|
|
+ if block in self.dominator_sets:
|
|
|
+ return self.dominator_sets
|
|
|
+ else:
|
|
|
+ idom = self.get_immediate_dominator(block)
|
|
|
+ if idom == block:
|
|
|
+ results = set([block])
|
|
|
+ else:
|
|
|
+ results = set(self.get_dominators(idom))
|
|
|
+ results.add(block)
|
|
|
+
|
|
|
+ self.dominator_sets[block] = results
|
|
|
+ return results
|
|
|
+
|
|
|
+ def dominates(self, dominator, dominated):
|
|
|
+ """Tests if the first block dominates the second."""
|
|
|
+ return dominator in self.get_dominators(dominated)
|