Prechádzať zdrojové kódy

Create preliminary relooper implementation

jonathanvdc 8 rokov pred
rodič
commit
8b35e71fe7

+ 4 - 3
kernel/modelverse_jit/bytecode_to_cfg.py

@@ -10,7 +10,8 @@ class AnalysisState(object):
         self.counter = cfg_ir.SharedCounter()
         self.analyzed_instructions = set()
         self.loop_instructions = {}
-        self.current_block = cfg_ir.BasicBlock(self.counter)
+        self.entry_point = cfg_ir.BasicBlock(self.counter)
+        self.current_block = self.entry_point
 
     def analyze(self, instruction):
         """Analyzes the given instruction as a basic block."""
@@ -179,7 +180,7 @@ class AnalysisState(object):
 
     def analyze_break(self, instruction):
         """Analyzes a 'break' instruction."""
-        if instruction.loop in self.loop_instructions:
+        if instruction.loop not in self.loop_instructions:
             raise jit_runtime.JitCompilationFailedException(
                 "'break' instruction targets a 'while' loop that has not been defined yet.")
 
@@ -188,7 +189,7 @@ class AnalysisState(object):
 
     def analyze_continue(self, instruction):
         """Analyzes a 'continue' instruction."""
-        if instruction.loop in self.loop_instructions:
+        if instruction.loop not in self.loop_instructions:
             raise jit_runtime.JitCompilationFailedException(
                 "'continue' instruction targets a 'while' loop that has not been defined yet.")
 

+ 1 - 2
kernel/modelverse_jit/cfg_ir.py

@@ -25,8 +25,7 @@ class BasicBlock(object):
 
     def append_parameter(self, parameter):
         """Appends a parameter to this basic block."""
-        assert isinstance(parameter, BlockParameter)
-        result = Definition(self.counter.next_value(), parameter)
+        result = self.create_definition(parameter)
         self.parameters.append(result)
         return result
 

+ 33 - 0
kernel/modelverse_jit/cfg_optimization.py

@@ -0,0 +1,33 @@
+"""Optimizes and analyzes CFG-IR."""
+
+import modelverse_jit.cfg_ir as cfg_ir
+
+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

+ 218 - 0
kernel/modelverse_jit/cfg_to_tree.py

@@ -0,0 +1,218 @@
+"""Lowers CFG-IR to tree-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
+
+# The CFG deconstruction code here is based on the relooper algorithm
+# as detailed in https://github.com/kripken/Relooper/blob/master/paper.pdf
+
+class FlowGraphComponent(object):
+    """Represents a control-flow graph component."""
+    def __init__(self, entry_blocks, blocks, reachable):
+        self.entry_blocks = entry_blocks
+        self.blocks = blocks
+        self.reachable = reachable
+
+    def get_entry_reachable_blocks(self):
+        """Gets the set of all blocks that are reachable from the entry points."""
+        return set.union(*[self.get_reachable_blocks(block) for block in self.entry_blocks])
+
+    def get_reachable_blocks(self, block):
+        """Gets all blocks in this component that are reachable from the given block."""
+        return set.intersection(self.reachable[block], self.blocks)
+
+    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)
+
+    def can_reach(self, source_block, target_block):
+        """Tests if the first block can reach the second."""
+        return target_block in self.reachable[source_block] and target_block in self.blocks
+
+    def sub_component(self, new_entry_points):
+        """Creates a sub-component of this component, with the given new entry points."""
+        result = FlowGraphComponent(new_entry_points, self.blocks, self.reachable)
+        result.blocks = result.get_entry_reachable_blocks()
+        return result
+
+class SimpleBlock(object):
+    """A 'simple' block in the relooper algorithm."""
+    def __init__(self, body, next_block):
+        self.body = body
+        self.next_block = next_block
+
+class LoopBlock(object):
+    """A 'loop' block in the relooper algorithm."""
+    def __init__(self, inner, next_block):
+        self.inner = inner
+        self.next_block = next_block
+
+class MultipleBlock(object):
+    """A 'multiple' block in the relooper algorithm."""
+    def __init__(self, handled_blocks, next_block):
+        self.handled_blocks = handled_blocks
+        self.next_block = next_block
+
+class ContinueFlow(cfg_ir.FlowInstruction):
+    """Represents a control flow instruction which continues to the next loop iteration."""
+    def __init__(self, loop):
+        cfg_ir.FlowInstruction.__init__(self)
+        self.loop = loop
+
+    def get_dependencies(self):
+        """Gets all definitions and instructions on which this instruction depends."""
+        return []
+
+    def branches(self):
+        """Gets a list of basic blocks targeted by this flow instruction."""
+        return []
+
+    def __str__(self):
+        return 'continue'
+
+class BreakFlow(cfg_ir.FlowInstruction):
+    """Represents a control flow instruction which breaks out of a loop."""
+    def __init__(self, loop):
+        cfg_ir.FlowInstruction.__init__(self)
+        self.loop = loop
+
+    def get_dependencies(self):
+        """Gets all definitions and instructions on which this instruction depends."""
+        return []
+
+    def branches(self):
+        """Gets a list of basic blocks targeted by this flow instruction."""
+        return []
+
+    def __str__(self):
+        return 'break'
+
+def solipsize(graph_component, loop, entry_blocks, next_blocks):
+    """Replaces branches to entry blocks and next blocks by jumps to 'continue' and 'break'
+       blocks, respectively."""
+    all_blocks = set()
+    reachable_from_inner = set()
+    for block in graph_component.blocks:
+        all_blocks.add(block)
+        for branch in block.flow.branches():
+            if branch.block in entry_blocks:
+                # Create a 'continue' block, and point to that.
+                continue_block = cfg_ir.BasicBlock(block.counter)
+                for param in branch.block.parameters:
+                    continue_block.append_parameter(param)
+
+                continue_block.flow = ContinueFlow(loop)
+                branch.block = continue_block
+                all_blocks.add(continue_block)
+            elif branch.block in next_blocks:
+                # Create a 'break' block, and point to that.
+                break_block = cfg_ir.BasicBlock(block.counter)
+                for param in branch.block.parameters:
+                    break_block.append_parameter(param)
+
+                break_block.flow = BreakFlow(loop)
+                branch.block = break_block
+                all_blocks.add(break_block)
+                reachable_from_inner.add(branch.block)
+
+    return reachable_from_inner, FlowGraphComponent(
+        graph_component.entry_blocks,
+        all_blocks,
+        {
+            block : cfg_optimization.get_reachable_blocks(block)
+            for block in all_blocks
+        })
+
+def to_relooper_loop(graph_component):
+    """Converts the given graph component to a relooper 'loop'."""
+    entry_blocks = graph_component.entry_blocks
+    result = LoopBlock(None, None)
+    inner_blocks = []
+    next_blocks = []
+    for block in graph_component.blocks:
+        if any([graph_component.can_reach(block, ep) for ep in entry_blocks]):
+            inner_blocks.append(block)
+        else:
+            next_blocks.append(block)
+
+    inner_component = FlowGraphComponent(
+        entry_blocks, inner_blocks, graph_component.reachable)
+    reachable_from_inner, inner_component = solipsize(
+        inner_component, result, entry_blocks, next_blocks)
+    result.inner = reloop(inner_component)
+
+    next_component = FlowGraphComponent(
+        reachable_from_inner, next_blocks, graph_component.reachable)
+    result.next_block = reloop(next_component)
+
+    return result
+
+def to_relooper_multiple_or_loop(graph_component):
+    """Converts the given graph component to a relooper 'multiple' or 'loop'."""
+    # From the Emscripten paper:
+    #
+    # If we have more than one entry, try to create a Multiple block:
+    # For each entry, find all the labels it reaches that
+    # cannot be reached by any other entry. If at least one entry
+    # has such labels, return a Multiple block, whose Handled
+    # blocks are blocks for those labels (and whose entries are
+    # those labels), and whose Next block is all the rest. Entries
+    # for the next block are entries that did not become part of
+    # the Handled blocks, and also labels that can be reached
+    # from the Handled blocks.
+    entry_blocks = graph_component.entry_blocks
+    if len(entry_blocks) <= 1:
+        return to_relooper_loop(graph_component)
+
+    entry_reachables = {ep : graph_component.get_reachable_blocks(ep) for ep in entry_blocks}
+    exclusive_entries = {}
+    for entry in entry_blocks:
+        exclusive_blocks = set(entry_reachables[entry])
+        for other_entry in entry_blocks:
+            if other_entry != entry:
+                exclusive_blocks.difference_update(entry_reachables[other_entry])
+        if len(exclusive_blocks) > 0:
+            exclusive_entries[entry] = exclusive_blocks
+
+    if len(exclusive_entries) == 0:
+        return to_relooper_loop(graph_component)
+
+    next_entries = set(graph_component.entry_blocks)
+    for block_set in exclusive_entries.values():
+        for elem in block_set:
+            directly_reachable = graph_component.get_directly_reachable_blocks(elem)
+            directly_reachable.remove(elem)
+            next_entries.update(directly_reachable)
+
+    next_entries.difference_update(exclusive_entries.keys())
+
+    result = MultipleBlock({}, None)
+    for entry, exclusive_blocks in exclusive_entries.items():
+        other_blocks = set(graph_component.blocks)
+        other_blocks.difference_update(exclusive_blocks)
+        result.handled_blocks[entry] = reloop(
+            solipsize(graph_component, result, set([entry]), other_blocks))
+
+    result.next_block = reloop(graph_component.sub_component(next_entries))
+
+    return result
+
+def reloop(graph_component):
+    """Applies the relooper algorithm to the given graph component."""
+    reachable_set = graph_component.get_entry_reachable_blocks()
+    entry_blocks = graph_component.entry_blocks
+    if len(entry_blocks) == 1 and entry_blocks[0] not in reachable_set:
+        graph_component.blocks.remove(entry_blocks[0])
+        return SimpleBlock(
+            entry_blocks[0],
+            reloop(
+                FlowGraphComponent(
+                    graph_component.get_directly_reachable_blocks(entry_blocks[0]),
+                    graph_component.blocks,
+                    graph_component.reachable)))
+    elif all([block in reachable_set for block in entry_blocks]):
+        return to_relooper_loop(graph_component)
+    else:
+        return to_relooper_multiple_or_loop(graph_component)