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."""
 """Computes dominator trees for control-flow graphs."""
 
 
 from collections import defaultdict
 from collections import defaultdict
+import modelverse_jit.cfg_ir as cfg_ir
 
 
 def sort_postorder(entry_point):
 def sort_postorder(entry_point):
     """Produces a postorder traversal of the graph with the given block as 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)
     __sort_postorder_step(entry_point)
     return results
     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):
 def intersect_immediate_dominators(block1, block2, idoms, postorder_nums):
     """Computes the intersection of the given blocks' immediate dominators."""
     """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):
 def get_immediate_dominators(entry_point):
     """Computes the immediate dominators of the control-flow graph defined by the given entry
     """Computes the immediate dominators of the control-flow graph defined by the given entry
        point."""
        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 = {}
     idoms = {}
     postorder_sort = sort_postorder(entry_point)
     postorder_sort = sort_postorder(entry_point)
     postorder_nums = {}
     postorder_nums = {}
@@ -92,41 +113,3 @@ def get_immediate_dominators(entry_point):
 def get_dominator_tree(entry_point):
 def get_dominator_tree(entry_point):
     """Constructs the dominator tree for the control-flow graph defined by the given entry point."""
     """Constructs the dominator tree for the control-flow graph defined by the given entry point."""
     return DominatorTree(get_immediate_dominators(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."""
 """Defines control flow graph IR data structures."""
 
 
+from collections import defaultdict
+
 # Let's just agree to disagree on map vs list comprehensions, pylint.
 # Let's just agree to disagree on map vs list comprehensions, pylint.
 # pylint: disable=I0011,W0141
 # 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."""
     """Gets the value of the given literal value or definition with an underlying literal."""
     return apply_to_value(get_literal_value, def_or_value)
     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_ir as cfg_ir
 import modelverse_jit.cfg_dominators as cfg_dominators
 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):
 def is_empty_block(block):
     """Tests if the given block contains no parameters or definitions."""
     """Tests if the given block contains no parameters or definitions."""
     return len(block.parameters) == 0 and len(block.definitions) == 0
     return len(block.parameters) == 0 and len(block.definitions) == 0
@@ -71,21 +41,15 @@ def optimize_flow(block):
                 branch.arguments = new_branch.arguments
                 branch.arguments = new_branch.arguments
                 changed = True
                 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):
 def optimize_graph_flow(entry_point):
     """Optimizes all flow instructions in the graph defined by the given 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)
         optimize_flow(block)
 
 
 def merge_blocks(entry_point):
 def merge_blocks(entry_point):
     """Merges blocks which have exactly one predecessor with said predecessor, if the
     """Merges blocks which have exactly one predecessor with said predecessor, if the
        predecessor has a jump flow instruction."""
        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())
     queue = list(predecessor_map.keys())
     def __do_merge(source, target):
     def __do_merge(source, target):
         for target_param, branch_arg in zip(target.parameters, source.flow.branch.arguments):
         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.
     # from a declare-local.
     local_checks = defaultdict(set)
     local_checks = defaultdict(set)
     local_defs = 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:
         for definition in block.definitions:
             if cfg_ir.is_value_def(definition, cfg_ir.CheckLocalExists):
             if cfg_ir.is_value_def(definition, cfg_ir.CheckLocalExists):
                 local_checks[cfg_ir.get_def_variable(definition).node_id].add(definition)
                 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)
                 local_defs[cfg_ir.get_def_variable(definition).node_id].add(definition)
 
 
     dominator_tree = cfg_dominators.get_dominator_tree(entry_point)
     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 (variable, all_checks) in local_checks.items():
         for check in all_checks:
         for check in all_checks:
             is_reachable = False
             is_reachable = False
@@ -146,7 +110,7 @@ def eliminate_unused_definitions(entry_point):
        given entry point."""
        given entry point."""
     def_dependencies = {}
     def_dependencies = {}
     root_defs = set()
     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:
         for definition in block.definitions:
             def_dependencies[definition] = set(
             def_dependencies[definition] = set(
                 [dep for dep in definition.get_all_dependencies()
                 [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
     """Eliminates trivial block parameters, i.e., block parameters which are really
        aliases."""
        aliases."""
     phi_values = defaultdict(set)
     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 block in all_blocks:
         for branch in block.flow.branches():
         for branch in block.flow.branches():
             for phi, arg in zip(branch.block.parameters, branch.arguments):
             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):
 def erase_parameters(entry_point, parameters_to_erase):
     """Erases all arguments for the given set of parameters, and then takes out the
     """Erases all arguments for the given set of parameters, and then takes out the
        parameters themselves."""
        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():
         for branch in block.flow.branches():
             new_arg_list = []
             new_arg_list = []
             for parameter, arg in zip(branch.block.parameters, branch.arguments):
             for parameter, arg in zip(branch.block.parameters, branch.arguments):
@@ -296,7 +260,7 @@ def optimize_calls(entry_point, jit):
        given entry point."""
        given entry point."""
     called_globals = set()
     called_globals = set()
     global_exists_defs = defaultdict(list)
     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 block in all_blocks:
         for definition in block.definitions:
         for definition in block.definitions:
             checked_global = get_checked_global(definition)
             checked_global = get_checked_global(definition)
@@ -311,7 +275,7 @@ def optimize_calls(entry_point, jit):
 
 
 def simplify_values(entry_point):
 def simplify_values(entry_point):
     """Simplifies values in the control-flow graph defined by the given 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:
         for definition in block.definitions:
             def_val = cfg_ir.get_def_value(definition)
             def_val = cfg_ir.get_def_value(definition)
             if isinstance(def_val, cfg_ir.Read):
             if isinstance(def_val, cfg_ir.Read):
@@ -328,7 +292,7 @@ def simplify_values(entry_point):
 
 
 def inline_constants(entry_point):
 def inline_constants(entry_point):
     """Replaces reads of constant nodes by the literals they contain."""
     """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:
         for definition in block.definitions:
             def_val = cfg_ir.get_def_value(definition)
             def_val = cfg_ir.get_def_value(definition)
             if isinstance(def_val, cfg_ir.Read):
             if isinstance(def_val, cfg_ir.Read):

+ 4 - 5
kernel/modelverse_jit/cfg_to_tree.py

@@ -2,7 +2,6 @@
 
 
 from collections import defaultdict
 from collections import defaultdict
 import modelverse_jit.cfg_ir as cfg_ir
 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.tree_ir as tree_ir
 import modelverse_jit.runtime as jit_runtime
 import modelverse_jit.runtime as jit_runtime
 import modelverse_jit.bytecode_to_tree as bytecode_to_tree
 import modelverse_jit.bytecode_to_tree as bytecode_to_tree
@@ -28,7 +27,7 @@ class FlowGraphComponent(object):
     def get_directly_reachable_blocks(self, block):
     def get_directly_reachable_blocks(self, block):
         """Gets all blocks in this component that are reachable from the given block by taking
         """Gets all blocks in this component that are reachable from the given block by taking
            exactly one branch."""
            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):
     def can_reach(self, source_block, target_block):
         """Tests if the first block can reach the second."""
         """Tests if the first block can reach the second."""
@@ -42,7 +41,7 @@ class FlowGraphComponent(object):
 
 
 def create_graph_component(entry_point):
 def create_graph_component(entry_point):
     """Creates a flow graph component from the given 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 = set(reachable[entry_point])
     all_blocks.add(entry_point)
     all_blocks.add(entry_point)
     return FlowGraphComponent([entry_point], all_blocks, reachable)
     return FlowGraphComponent([entry_point], all_blocks, reachable)
@@ -182,7 +181,7 @@ def solipsize(graph_component, loop, entry_blocks, next_blocks):
         graph_component.entry_blocks,
         graph_component.entry_blocks,
         all_blocks,
         all_blocks,
         {
         {
-            block : cfg_optimization.get_reachable_blocks(block)
+            block : cfg_ir.get_reachable_blocks(block)
             for block in all_blocks
             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
     """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 used only once and are consumed in the same basic block in which they
        are defined."""
        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.
     # Find all definition users for each definition.
     def_users = defaultdict(set)
     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(
                 '\n'.join(
                     map(
                     map(
                         str,
                         str,
-                        cfg_optimization.get_all_reachable_blocks(
+                        cfg_ir.get_all_reachable_blocks(
                             bytecode_analyzer.entry_point)))))
                             bytecode_analyzer.entry_point)))))
     raise primitive_functions.PrimitiveFinished(
     raise primitive_functions.PrimitiveFinished(
         create_bare_function(
         create_bare_function(