Browse Source

Move some CFG algorithms into cfg_ir

jonathanvdc 8 years ago
parent
commit
d4eeb6885c

+ 41 - 58
kernel/modelverse_jit/cfg_dominators.py

@@ -1,6 +1,7 @@
 """Computes dominator trees for control-flow graphs."""
 
 from collections import defaultdict
+import modelverse_jit.cfg_ir as cfg_ir
 
 def sort_postorder(entry_point):
     """Produces a postorder traversal of the graph with the given block as entry point."""
@@ -19,23 +20,47 @@ def sort_postorder(entry_point):
     __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
+class DominatorTree(object):
+    """A data structure that represents a dominator tree."""
+    def __init__(self, immediate_dominators):
+        self.immediate_dominators = immediate_dominators
+        self.dominator_sets = {}
 
-        processed.add(block)
-        for branch in block.flow.branches():
-            target_block = branch.block
-            results[target_block].add(block)
-            __find_predecessors_step(target_block)
+    def get_immediate_dominator(self, block):
+        """Gets the given block's immediate dominator."""
+        return self.immediate_dominators[block]
 
-    __find_predecessors_step(entry_point)
-    return results
+    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_block(self, dominator, dominated):
+        """Tests if the first block dominates the second."""
+        return dominator in self.get_dominators(dominated)
+
+    def dominates_instruction(self, dominator, dominated):
+        """Tests if the first instruction dominates the second."""
+        dominator_block = dominator.block
+        dominated_block = dominated.block
+        if dominator_block == dominated_block:
+            return dominator.definition_index < dominated.definition_index
+        else:
+            return self.dominates_block(dominator_block, dominated_block)
+
+# The algorithms below are 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)
 
 def intersect_immediate_dominators(block1, block2, idoms, postorder_nums):
     """Computes the intersection of the given blocks' immediate dominators."""
@@ -52,11 +77,7 @@ def intersect_immediate_dominators(block1, block2, idoms, postorder_nums):
 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)
+    predecessor_map = cfg_ir.get_all_predecessor_blocks(entry_point)
     idoms = {}
     postorder_sort = sort_postorder(entry_point)
     postorder_nums = {}
@@ -92,41 +113,3 @@ def get_immediate_dominators(entry_point):
 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_block(self, dominator, dominated):
-        """Tests if the first block dominates the second."""
-        return dominator in self.get_dominators(dominated)
-
-    def dominates_instruction(self, dominator, dominated):
-        """Tests if the first instruction dominates the second."""
-        dominator_block = dominator.block
-        dominated_block = dominated.block
-        if dominator_block == dominated_block:
-            return dominator.definition_index < dominated.definition_index
-        else:
-            return self.dominates_block(dominator_block, dominated_block)

+ 55 - 0
kernel/modelverse_jit/cfg_ir.py

@@ -1,5 +1,7 @@
 """Defines control flow graph IR data structures."""
 
+from collections import defaultdict
+
 # Let's just agree to disagree on map vs list comprehensions, pylint.
 # pylint: disable=I0011,W0141
 
@@ -665,3 +667,56 @@ def get_literal_def_value(def_or_value):
     """Gets the value of the given literal value or definition with an underlying literal."""
     return apply_to_value(get_literal_value, def_or_value)
 
+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 get_directly_reachable_blocks(block):
+    """Gets the set of all blocks that can be reached by taking a single branch from the
+       given block."""
+    return [branch.block for branch in block.flow.branches()]
+
+def get_reachable_blocks(entry_point):
+    """Constructs the set of all reachable vertices from the given block."""
+    # This is a simple O(n^2) algorithm. Maybe a faster algorithm is more appropriate here.
+    def __add_block_children(block, results):
+        for child in get_directly_reachable_blocks(block):
+            if child not in results:
+                results.add(child)
+                __add_block_children(child, results)
+
+        return results
+
+    return __add_block_children(entry_point, set())
+
+def get_all_reachable_blocks(entry_point):
+    """Constructs the set of all reachable vertices, for every block that is
+       reachable from the given entry point."""
+    # This is a simple O(n^3) algorithm. Maybe a faster algorithm is more appropriate here.
+    results = {}
+    all_blocks = get_reachable_blocks(entry_point)
+    results[entry_point] = all_blocks
+    for block in all_blocks:
+        if block not in results:
+            results[block] = get_reachable_blocks(block)
+    return results
+
+def get_all_blocks(entry_point):
+    """Gets all basic blocks in the control-flow graph defined by the given entry point."""
+    yield entry_point
+    for block in get_reachable_blocks(entry_point):
+        yield block

+ 10 - 46
kernel/modelverse_jit/cfg_optimization.py

@@ -4,36 +4,6 @@ from collections import defaultdict
 import modelverse_jit.cfg_ir as cfg_ir
 import modelverse_jit.cfg_dominators as cfg_dominators
 
-def get_directly_reachable_blocks(block):
-    """Gets the set of all blocks that can be reached by taking a single branch from the
-       given block."""
-    return [branch.block for branch in block.flow.branches()]
-
-def get_reachable_blocks(entry_point):
-    """Constructs the set of all reachable vertices from the given block."""
-    # This is a simple O(n^2) algorithm. Maybe a faster algorithm is more appropriate here.
-    def __add_block_children(block, results):
-        for child in get_directly_reachable_blocks(block):
-            if child not in results:
-                results.add(child)
-                __add_block_children(child, results)
-
-        return results
-
-    return __add_block_children(entry_point, set())
-
-def get_all_reachable_blocks(entry_point):
-    """Constructs the set of all reachable vertices, for every block that is
-       reachable from the given entry point."""
-    # This is a simple O(n^3) algorithm. Maybe a faster algorithm is more appropriate here.
-    results = {}
-    all_blocks = get_reachable_blocks(entry_point)
-    results[entry_point] = all_blocks
-    for block in all_blocks:
-        if block not in results:
-            results[block] = get_reachable_blocks(block)
-    return results
-
 def is_empty_block(block):
     """Tests if the given block contains no parameters or definitions."""
     return len(block.parameters) == 0 and len(block.definitions) == 0
@@ -71,21 +41,15 @@ def optimize_flow(block):
                 branch.arguments = new_branch.arguments
                 changed = True
 
-def get_all_blocks(entry_point):
-    """Gets all basic blocks in the control-flow graph defined by the given entry point."""
-    yield entry_point
-    for block in get_reachable_blocks(entry_point):
-        yield block
-
 def optimize_graph_flow(entry_point):
     """Optimizes all flow instructions in the graph defined by the given entry point."""
-    for block in get_all_blocks(entry_point):
+    for block in cfg_ir.get_all_blocks(entry_point):
         optimize_flow(block)
 
 def merge_blocks(entry_point):
     """Merges blocks which have exactly one predecessor with said predecessor, if the
        predecessor has a jump flow instruction."""
-    predecessor_map = cfg_dominators.get_all_predecessor_blocks(entry_point)
+    predecessor_map = cfg_ir.get_all_predecessor_blocks(entry_point)
     queue = list(predecessor_map.keys())
     def __do_merge(source, target):
         for target_param, branch_arg in zip(target.parameters, source.flow.branch.arguments):
@@ -116,7 +80,7 @@ def elide_local_checks(entry_point):
     # from a declare-local.
     local_checks = defaultdict(set)
     local_defs = defaultdict(set)
-    for block in get_all_blocks(entry_point):
+    for block in cfg_ir.get_all_blocks(entry_point):
         for definition in block.definitions:
             if cfg_ir.is_value_def(definition, cfg_ir.CheckLocalExists):
                 local_checks[cfg_ir.get_def_variable(definition).node_id].add(definition)
@@ -124,7 +88,7 @@ def elide_local_checks(entry_point):
                 local_defs[cfg_ir.get_def_variable(definition).node_id].add(definition)
 
     dominator_tree = cfg_dominators.get_dominator_tree(entry_point)
-    reachable_blocks = get_all_reachable_blocks(entry_point)
+    reachable_blocks = cfg_ir.get_all_reachable_blocks(entry_point)
     for (variable, all_checks) in local_checks.items():
         for check in all_checks:
             is_reachable = False
@@ -146,7 +110,7 @@ def eliminate_unused_definitions(entry_point):
        given entry point."""
     def_dependencies = {}
     root_defs = set()
-    for block in get_all_blocks(entry_point):
+    for block in cfg_ir.get_all_blocks(entry_point):
         for definition in block.definitions:
             def_dependencies[definition] = set(
                 [dep for dep in definition.get_all_dependencies()
@@ -192,7 +156,7 @@ def eliminate_trivial_phis(entry_point):
     """Eliminates trivial block parameters, i.e., block parameters which are really
        aliases."""
     phi_values = defaultdict(set)
-    all_blocks = list(get_all_blocks(entry_point))
+    all_blocks = list(cfg_ir.get_all_blocks(entry_point))
     for block in all_blocks:
         for branch in block.flow.branches():
             for phi, arg in zip(branch.block.parameters, branch.arguments):
@@ -217,7 +181,7 @@ def eliminate_trivial_phis(entry_point):
 def erase_parameters(entry_point, parameters_to_erase):
     """Erases all arguments for the given set of parameters, and then takes out the
        parameters themselves."""
-    for block in get_all_blocks(entry_point):
+    for block in cfg_ir.get_all_blocks(entry_point):
         for branch in block.flow.branches():
             new_arg_list = []
             for parameter, arg in zip(branch.block.parameters, branch.arguments):
@@ -296,7 +260,7 @@ def optimize_calls(entry_point, jit):
        given entry point."""
     called_globals = set()
     global_exists_defs = defaultdict(list)
-    all_blocks = list(get_all_blocks(entry_point))
+    all_blocks = list(cfg_ir.get_all_blocks(entry_point))
     for block in all_blocks:
         for definition in block.definitions:
             checked_global = get_checked_global(definition)
@@ -311,7 +275,7 @@ def optimize_calls(entry_point, jit):
 
 def simplify_values(entry_point):
     """Simplifies values in the control-flow graph defined by the given entry point."""
-    for block in get_all_blocks(entry_point):
+    for block in cfg_ir.get_all_blocks(entry_point):
         for definition in block.definitions:
             def_val = cfg_ir.get_def_value(definition)
             if isinstance(def_val, cfg_ir.Read):
@@ -328,7 +292,7 @@ def simplify_values(entry_point):
 
 def inline_constants(entry_point):
     """Replaces reads of constant nodes by the literals they contain."""
-    for block in get_all_blocks(entry_point):
+    for block in cfg_ir.get_all_blocks(entry_point):
         for definition in block.definitions:
             def_val = cfg_ir.get_def_value(definition)
             if isinstance(def_val, cfg_ir.Read):

+ 4 - 5
kernel/modelverse_jit/cfg_to_tree.py

@@ -2,7 +2,6 @@
 
 from collections import defaultdict
 import modelverse_jit.cfg_ir as cfg_ir
-import modelverse_jit.cfg_optimization as cfg_optimization
 import modelverse_jit.tree_ir as tree_ir
 import modelverse_jit.runtime as jit_runtime
 import modelverse_jit.bytecode_to_tree as bytecode_to_tree
@@ -28,7 +27,7 @@ class FlowGraphComponent(object):
     def get_directly_reachable_blocks(self, block):
         """Gets all blocks in this component that are reachable from the given block by taking
            exactly one branch."""
-        return set.intersection(cfg_optimization.get_directly_reachable_blocks(block), self.blocks)
+        return set.intersection(cfg_ir.get_directly_reachable_blocks(block), self.blocks)
 
     def can_reach(self, source_block, target_block):
         """Tests if the first block can reach the second."""
@@ -42,7 +41,7 @@ class FlowGraphComponent(object):
 
 def create_graph_component(entry_point):
     """Creates a flow graph component from the given entry point."""
-    reachable = cfg_optimization.get_all_reachable_blocks(entry_point)
+    reachable = cfg_ir.get_all_reachable_blocks(entry_point)
     all_blocks = set(reachable[entry_point])
     all_blocks.add(entry_point)
     return FlowGraphComponent([entry_point], all_blocks, reachable)
@@ -182,7 +181,7 @@ def solipsize(graph_component, loop, entry_blocks, next_blocks):
         graph_component.entry_blocks,
         all_blocks,
         {
-            block : cfg_optimization.get_reachable_blocks(block)
+            block : cfg_ir.get_reachable_blocks(block)
             for block in all_blocks
         })
 
@@ -294,7 +293,7 @@ def find_inlinable_definitions(entry_point):
     """Computes a set of definitions which are eligible for inlining, i.e., they
        are used only once and are consumed in the same basic block in which they
        are defined."""
-    all_blocks = list(cfg_optimization.get_all_blocks(entry_point))
+    all_blocks = list(cfg_ir.get_all_blocks(entry_point))
 
     # Find all definition users for each definition.
     def_users = defaultdict(set)

+ 1 - 1
kernel/modelverse_jit/jit.py

@@ -652,7 +652,7 @@ def compile_function_body_fast(jit, function_name, body_id, _):
                 '\n'.join(
                     map(
                         str,
-                        cfg_optimization.get_all_reachable_blocks(
+                        cfg_ir.get_all_reachable_blocks(
                             bytecode_analyzer.entry_point)))))
     raise primitive_functions.PrimitiveFinished(
         create_bare_function(