[pypy-commit] pypy framestate: Set up infrastructure for bytecode-level flow graphs

rlamy noreply at buildbot.pypy.org
Mon Nov 24 17:29:45 CET 2014


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: framestate
Changeset: r74676:a699440d109d
Date: 2014-05-10 05:28 +0100
http://bitbucket.org/pypy/pypy/changeset/a699440d109d/

Log:	Set up infrastructure for bytecode-level flow graphs

	Create classes BytecodeGraph and BytecodeBlock

diff --git a/rpython/flowspace/bytecode.py b/rpython/flowspace/bytecode.py
--- a/rpython/flowspace/bytecode.py
+++ b/rpython/flowspace/bytecode.py
@@ -59,13 +59,25 @@
         self.build_flow()
 
     def build_flow(self):
-        next_pos = pos = 0
         contents, offsets, jumps = bc_reader.disassemble(self)
-        self.contents = zip(offsets, contents)
-        self.pos_index = dict((offset, i) for i, offset in enumerate(offsets))
-        # add end marker
-        self.contents.append((len(self.co_code), None))
+        pos_map = dict([(pos, i) for i, pos in enumerate(offsets)])
+        cuts = sorted([pos_map[n] + 1 for n in jumps.keys()] +
+                [pos_map[n] for n in jumps.values()])
+        pendingblocks = [SimpleBlock(contents[i:j])
+                for i, j in zip([0] + cuts, cuts + [len(self.co_code)])]
 
+        graph = self.graph = BytecodeGraph(pendingblocks[0])
+        graph.pendingblocks = pendingblocks
+        for block in pendingblocks:
+            for i, op in enumerate(block.operations):
+                graph.pos_index[op.offset] = block, i
+        graph.next_pos = dict([(offsets[i], offsets[i+1])
+            for i in range(len(offsets) - 1)])
+        graph.next_pos[offsets[-1]] = len(self.co_code)
+        while graph.pendingblocks:
+            block = graph.next_block()
+            for i, op in enumerate(block.operations):
+                op.bc_flow(block, graph)
 
     @classmethod
     def _from_code(cls, code):
@@ -88,9 +100,9 @@
         return bool(self.co_flags & CO_GENERATOR)
 
     def read(self, offset):
-        i = self.pos_index[offset]
-        op = self.contents[i][1]
-        next_offset = self.contents[i+1][0]
+        block, i = self.graph.pos_index[offset]
+        op = block[i]
+        next_offset = self.graph.next_pos[offset]
         return next_offset, op
 
 
@@ -166,6 +178,61 @@
 
 bc_reader = BytecodeReader(host_bytecode_spec.method_names)
 
+class BytecodeGraph(object):
+    def __init__(self, startblock):
+        self.entry = EntryBlock()
+        self.entry.set_exits([startblock])
+        self.pos_index = {}
+        self.pendingblocks = [startblock]
+
+    def next_block(self):
+        return self.pendingblocks.pop()
+
+
+class BytecodeBlock(object):
+    """Base class for opcode blocks"""
+    def __init__(self):
+        self.parents = set()
+        self._exits = []
+
+    def __getitem__(self, i):
+        return self.operations[i]
+
+    def add_exit(self, exit):
+        self._exits.append(exit)
+        exit.parents.add(self)
+
+    def set_exits(self, exits):
+        for old_exit in self._exits:
+            old_exit.parents.remove(self)
+        self._exits = exits
+        for new_exit in exits:
+            new_exit.parents.add(self)
+
+    def change_exit(self, old_exit, new_exit):
+        self._exits = [new_exit if exit is old_exit else exit
+                for exit in self._exits]
+        old_exit.parents.remove(self)
+        new_exit.parents.add(self)
+
+    @property
+    def startpos(self):
+        return self.operations[0].offset
+
+
+class EntryBlock(BytecodeBlock):
+    """A fake block to represent the beginning of a code object"""
+
+class SimpleBlock(BytecodeBlock):
+    """A block with a single exit."""
+    def __init__(self, operations, exit=None):
+        BytecodeBlock.__init__(self)
+        self.operations = operations
+        if exit:
+            self.set_exits([exit])
+
+
+OPNAMES = host_bytecode_spec.method_names
 
 class BCInstruction(object):
     """
@@ -183,6 +250,9 @@
     def eval(self, ctx):
         pass
 
+    def bc_flow(self, block, graph):
+        pass
+
     def has_jump(self):
         return self.num in opcode.hasjrel or self.num in opcode.hasjabs
 


More information about the pypy-commit mailing list