[pypy-svn] r73881 - in pypy/branch/blackhole-improvement/pypy/jit: codewriter codewriter/test metainterp metainterp/test

arigo at codespeak.net arigo at codespeak.net
Mon Apr 19 10:51:03 CEST 2010


Author: arigo
Date: Mon Apr 19 10:51:01 2010
New Revision: 73881

Added:
   pypy/branch/blackhole-improvement/pypy/jit/codewriter/   (props changed)
   pypy/branch/blackhole-improvement/pypy/jit/codewriter/__init__.py   (contents, props changed)
   pypy/branch/blackhole-improvement/pypy/jit/codewriter/flatten.py   (contents, props changed)
   pypy/branch/blackhole-improvement/pypy/jit/codewriter/format.py   (contents, props changed)
   pypy/branch/blackhole-improvement/pypy/jit/codewriter/regalloc.py   (contents, props changed)
   pypy/branch/blackhole-improvement/pypy/jit/codewriter/support.py
      - copied unchanged from r73730, pypy/branch/blackhole-improvement/pypy/jit/metainterp/support.py
   pypy/branch/blackhole-improvement/pypy/jit/codewriter/test/   (props changed)
   pypy/branch/blackhole-improvement/pypy/jit/codewriter/test/__init__.py   (contents, props changed)
   pypy/branch/blackhole-improvement/pypy/jit/codewriter/test/test_flatten.py   (contents, props changed)
   pypy/branch/blackhole-improvement/pypy/jit/codewriter/test/test_format.py   (contents, props changed)
   pypy/branch/blackhole-improvement/pypy/jit/codewriter/test/test_regalloc.py   (contents, props changed)
Removed:
   pypy/branch/blackhole-improvement/pypy/jit/metainterp/codewriter2.py
   pypy/branch/blackhole-improvement/pypy/jit/metainterp/support.py
   pypy/branch/blackhole-improvement/pypy/jit/metainterp/test/test_codewriter2.py
Log:
Start splitting codewriter.py into multiple files that live in
their own directory.


Added: pypy/branch/blackhole-improvement/pypy/jit/codewriter/__init__.py
==============================================================================

Added: pypy/branch/blackhole-improvement/pypy/jit/codewriter/flatten.py
==============================================================================
--- (empty file)
+++ pypy/branch/blackhole-improvement/pypy/jit/codewriter/flatten.py	Mon Apr 19 10:51:01 2010
@@ -0,0 +1,169 @@
+from pypy.objspace.flow.model import Variable
+
+
+class SSARepr(object):
+    def __init__(self):
+        self.insns = []
+
+class Label(object):
+    def __init__(self, name):
+        self.name = name
+    def __repr__(self):
+        return "Label(%r)" % (self.name, )
+    def __eq__(self, other):
+        return isinstance(other, Label) and other.name == self.name
+
+class TLabel(object):
+    def __init__(self, name):
+        self.name = name
+    def __repr__(self):
+        return "TLabel(%r)" % (self.name, )
+    def __eq__(self, other):
+        return isinstance(other, TLabel) and other.name == self.name
+
+class Register(object):
+    def __init__(self, index):
+        self.index = index
+
+# ____________________________________________________________
+
+def flatten_graph(graph, regalloc):
+    """Flatten the graph into an SSARepr, with already-computed register
+    allocations."""
+    flattener = GraphFlattener(graph, regalloc)
+    flattener.enforce_input_args()
+    flattener.generate_ssa_form()
+    return flattener.assembler
+
+
+class GraphFlattener(object):
+
+    def __init__(self, graph, regalloc):
+        self.graph = graph
+        self.regalloc = regalloc
+        self.registers = {}
+
+    def enforce_input_args(self):
+        inputargs = self.graph.startblock.inputargs
+        for i in range(len(inputargs)):
+            col = self.regalloc.getcolor(inputargs[i])
+            if col != i:
+                assert col > i
+                self.regalloc.swapcolors(i, col)
+        for i in range(len(inputargs)):
+            assert self.regalloc.getcolor(inputargs[i]) == i
+
+    def generate_ssa_form(self):
+        self.assembler = SSARepr()
+        self.seen_blocks = {}
+        self.make_bytecode_block(self.graph.startblock)
+
+    def make_bytecode_block(self, block):
+        if block.exits == ():
+            self.make_return(block.inputargs)
+            return
+        if block in self.seen_blocks:
+            self.emitline("goto", TLabel(block))
+            return
+        # inserting a goto not necessary, falling through
+        self.seen_blocks[block] = True
+        self.emitline(Label(block))
+        #
+        operations = block.operations
+        for i, op in enumerate(operations):
+            self.serialize_op(op)
+        #
+        self.insert_exits(block)
+
+    def make_return(self, args):
+        if len(args) == 1:
+            # return from function
+            self.emitline("int_return", self.getcolor(args[0]))
+        elif len(args) == 2:
+            # exception block, raising an exception from a function
+            xxx
+        else:
+            raise Exception("?")
+
+    def make_link(self, link):
+        if link.target.exits == ():
+            self.make_return(link.args)
+            return
+        self.insert_renamings(link)
+        self.make_bytecode_block(link.target)
+
+    def insert_exits(self, block):
+        if len(block.exits) == 1:
+            link = block.exits[0]
+            assert link.exitcase is None
+            self.make_link(link)
+        else:
+            assert len(block.exits) == 2
+            linkfalse, linktrue = block.exits
+            if linkfalse.llexitcase == True:
+                linkfalse, linktrue = linktrue, linkfalse
+            #
+            self.emitline('goto_if_not', TLabel(linkfalse),
+                          self.getcolor(block.exitswitch))
+            # true path:
+            self.make_link(linktrue)
+            # false path:
+            self.emitline(Label(linkfalse))
+            self.make_link(linkfalse)
+
+    def optimize_goto_if_not(self, block):
+        xxxxxxx
+        if not self.optimize:
+            raise CannotOptimize
+        v = block.exitswitch
+        for link in block.exits:
+            if v in link.args:
+                raise CannotOptimize   # variable escapes to next block
+        for op in block.operations[::-1]:
+            if v in op.args:
+                raise CannotOptimize   # variable is also used in cur block
+            if v is op.result:
+                if op.opname not in ('int_lt', 'int_le', 'int_eq', 'int_ne',
+                                     'int_gt', 'int_ge'):
+                    raise CannotOptimize    # not a supported operation
+                killop = (op.opname,) + tuple(op.args) + (v,)
+                self.assembler.insns.remove(killop)
+                return 'goto_if_not_' + op.opname, op.args
+        raise CannotOptimize   # variable is not produced in cur block
+
+    def insert_renamings(self, link):
+        renamings_from = []
+        renamings_to = []
+        lst = [(v, self.getcolor(link.target.inputargs[i]))
+               for i, v in enumerate(link.args)]
+        lst.sort(key=lambda(v, w): w.index)
+        for v, w in lst:
+            if isinstance(v, Variable):
+                v = self.getcolor(v)
+                if v == w:
+                    continue
+            renamings_from.append(v)
+            renamings_to.append(w)
+        if renamings_from:
+            self.emitline('int_rename', renamings_from, renamings_to)
+
+    def emitline(self, *line):
+        self.assembler.insns.append(line)
+
+    def serialize_op(self, op):
+        args = []
+        for v in op.args:
+            if isinstance(v, Variable):
+                v = self.getcolor(v)
+            args.append(v)
+        if op.result is not None:
+            args.append(self.getcolor(op.result))
+        self.emitline(op.opname, *args)
+
+    def getcolor(self, v):
+        col = self.regalloc.getcolor(v)
+        try:
+            r = self.registers[col]
+        except KeyError:
+            r = self.registers[col] = Register(col)
+        return r

Added: pypy/branch/blackhole-improvement/pypy/jit/codewriter/format.py
==============================================================================
--- (empty file)
+++ pypy/branch/blackhole-improvement/pypy/jit/codewriter/format.py	Mon Apr 19 10:51:01 2010
@@ -0,0 +1,46 @@
+import py
+from pypy.objspace.flow.model import Constant
+from pypy.jit.codewriter.flatten import SSARepr, Label, TLabel, Register
+
+
+def format_assembler(ssarepr):
+    """For testing: format a SSARepr as a multiline string."""
+    from cStringIO import StringIO
+    seen = {}
+    #
+    def repr(x):
+        if isinstance(x, Register):
+            return '%i' + str(x.index)
+        elif isinstance(x, Constant):
+            return '$' + str(x.value)
+        elif isinstance(x, TLabel):
+            return getlabelname(x)
+        elif isinstance(x, list):
+            return '[%s]' % ', '.join(map(repr, x))
+        else:
+            return `x`   # ?
+    #
+    seenlabels = {}
+    for asm in ssarepr.insns:
+        for x in asm:
+            if isinstance(x, TLabel):
+                seenlabels[x.name] = -1
+    labelcount = [0]
+    def getlabelname(lbl):
+        if seenlabels[lbl.name] == -1:
+            labelcount[0] += 1
+            seenlabels[lbl.name] = labelcount[0]
+        return 'L%d' % seenlabels[lbl.name]
+    #
+    output = StringIO()
+    for asm in ssarepr.insns:
+        if isinstance(asm[0], Label):
+            if asm[0].name in seenlabels:
+                print >> output, '%s:' % getlabelname(asm[0])
+        else:
+            print >> output, asm[0],
+            if len(asm) > 1:
+                print >> output, ', '.join(map(repr, asm[1:]))
+            else:
+                print >> output
+    return output.getvalue()

Added: pypy/branch/blackhole-improvement/pypy/jit/codewriter/regalloc.py
==============================================================================
--- (empty file)
+++ pypy/branch/blackhole-improvement/pypy/jit/codewriter/regalloc.py	Mon Apr 19 10:51:01 2010
@@ -0,0 +1,100 @@
+import sys
+from pypy.objspace.flow.model import Variable
+from pypy.tool.algo.color import DependencyGraph
+from pypy.tool.algo.unionfind import UnionFind
+
+
+def perform_register_allocation(graph):
+    regalloc = RegAllocator(graph)
+    regalloc.make_dependencies()
+    regalloc.coalesce_variables()
+    regalloc.find_node_coloring()
+    return regalloc
+
+
+class RegAllocator(object):
+    DEBUG_REGALLOC = False
+
+    def __init__(self, graph):
+        self.graph = graph
+
+    def make_dependencies(self):
+        dg = DependencyGraph()
+        for block in self.graph.iterblocks():
+            # Compute die_at = {Variable: index_of_operation_with_last_usage}
+            die_at = dict.fromkeys(block.inputargs, 0)
+            for i, op in enumerate(block.operations):
+                for v in op.args:
+                    if isinstance(v, Variable):
+                        die_at[v] = i
+                if op.result is not None:
+                    die_at[op.result] = i
+            die_at.pop(block.exitswitch, None)
+            for link in block.exits:
+                for v in link.args:
+                    die_at.pop(v, None)
+            # Add the variables of this block to the dependency graph
+            for i, v in enumerate(block.inputargs):
+                dg.add_node(v)
+                for j in range(i):
+                    dg.add_edge(block.inputargs[j], v)
+            livevars = set(block.inputargs)
+            die_at = [(value, key) for (key, value) in die_at.items()]
+            die_at.sort()
+            die_at.append((sys.maxint,))
+            die_index = 0
+            for i, op in enumerate(block.operations):
+                while die_at[die_index][0] == i:
+                    livevars.remove(die_at[die_index][1])
+                    die_index += 1
+                if op.result is not None:
+                    livevars.add(op.result)
+                    dg.add_node(op.result)
+                    for v in livevars:
+                        dg.add_edge(v, op.result)
+        self._depgraph = dg
+
+    def coalesce_variables(self):
+        uf = UnionFind()
+        dg = self._depgraph
+        pendingblocks = list(self.graph.iterblocks())
+        while pendingblocks:
+            block = pendingblocks.pop()
+            # Aggressively try to coalesce each source variable with its
+            # target.  We start from the end of the graph instead of
+            # from the beginning.  This is a bit arbitrary, but the idea
+            # is that the end of the graph runs typically more often
+            # than the start, given that we resume execution from the
+            # middle during blackholing.
+            for link in block.exits:
+                for i, v in enumerate(link.args):
+                    if isinstance(v, Variable):
+                        w = link.target.inputargs[i]
+                        v0 = uf.find_rep(v)
+                        w0 = uf.find_rep(w)
+                        if v0 is not w0 and v0 not in dg.neighbours[w0]:
+                            _, rep, _ = uf.union(v0, w0)
+                            if rep is v0:
+                                dg.coalesce(w0, v0)
+                            else:
+                                assert rep is w0
+                                dg.coalesce(v0, w0)
+        self._unionfind = uf
+
+    def find_node_coloring(self):
+        self._coloring = self._depgraph.find_node_coloring()
+        if self.DEBUG_REGALLOC:
+            for block in self.graph.iterblocks():
+                print block
+                for v in block.getvariables():
+                    print '\t', v, '\t', self.getcolor(v)
+
+    def getcolor(self, v):
+        return self._coloring[self._unionfind.find_rep(v)]
+
+    def swapcolors(self, col1, col2):
+        for key, value in self._coloring.items():
+            if value == col1:
+                self._coloring[key] = col2
+            elif value == col2:
+                self._coloring[key] = col1

Added: pypy/branch/blackhole-improvement/pypy/jit/codewriter/test/__init__.py
==============================================================================

Added: pypy/branch/blackhole-improvement/pypy/jit/codewriter/test/test_flatten.py
==============================================================================
--- (empty file)
+++ pypy/branch/blackhole-improvement/pypy/jit/codewriter/test/test_flatten.py	Mon Apr 19 10:51:01 2010
@@ -0,0 +1,58 @@
+import py
+from pypy.jit.codewriter import support
+from pypy.jit.codewriter.flatten import flatten_graph
+from pypy.jit.codewriter.format import format_assembler
+
+
+class FakeRegAlloc:
+    # a RegAllocator that answers "0, 1, 2, 3, 4..." for the colors
+    def __init__(self):
+        self.seen = {}
+        self.num_colors = 0
+    def getcolor(self, v):
+        if v not in self.seen:
+            self.seen[v] = self.num_colors
+            self.num_colors += 1
+        return self.seen[v]
+
+
+class TestFlatten:
+
+    def make_graphs(self, func, values, type_system='lltype'):
+        self.rtyper = support.annotate(func, values, type_system=type_system)
+        return self.rtyper.annotator.translator.graphs
+
+    def encoding_test(self, func, args, expected, optimize=True):
+        graphs = self.make_graphs(func, args)
+        ssarepr = flatten_graph(graphs[0], FakeRegAlloc())
+        asm = format_assembler(ssarepr)
+        expected = str(py.code.Source(expected)).strip() + '\n'
+        assert asm == expected
+
+    def test_simple(self):
+        def f(n):
+            return n + 10
+        self.encoding_test(f, [5], """
+            int_add %i0, $10, %i1
+            int_return %i1
+        """)
+
+    def test_loop(self):
+        def f(a, b):
+            while a > 0:
+                b += a
+                a -= 1
+            return b
+        self.encoding_test(f, [5, 6], """
+            int_rename [%i0, %i1], [%i2, %i3]
+            L1:
+            int_gt %i2, $0, %i4
+            goto_if_not L2, %i4
+            int_rename [%i2, %i3], [%i5, %i6]
+            int_add %i6, %i5, %i7
+            int_sub %i5, $1, %i8
+            int_rename [%i8, %i7], [%i2, %i3]
+            goto L1
+            L2:
+            int_return %i3
+        """)

Added: pypy/branch/blackhole-improvement/pypy/jit/codewriter/test/test_format.py
==============================================================================
--- (empty file)
+++ pypy/branch/blackhole-improvement/pypy/jit/codewriter/test/test_format.py	Mon Apr 19 10:51:01 2010
@@ -0,0 +1,47 @@
+import py
+from pypy.objspace.flow.model import Constant
+from pypy.jit.codewriter.format import format_assembler
+from pypy.jit.codewriter.flatten import Label, TLabel, SSARepr, Register
+
+
+def test_format_assembler_simple():
+    ssarepr = SSARepr()
+    i0, i1, i2 = Register(0), Register(1), Register(2)
+    ssarepr.insns = [
+        ('livevars', [i0, i1]),
+        ('int_add', i0, i1, i2),
+        ('int_return', i2),
+        ]
+    asm = format_assembler(ssarepr)
+    expected = """
+        livevars [%i0, %i1]
+        int_add %i0, %i1, %i2
+        int_return %i2
+    """
+    assert asm == str(py.code.Source(expected)).strip() + '\n'
+
+def test_format_assembler_loop():
+    ssarepr = SSARepr()
+    i0, i1 = Register(0), Register(1)
+    ssarepr.insns = [
+        ('livevars', [i0, i1]),
+        (Label('L1'),),
+        ('goto_if_not_int_gt', TLabel('L2'), i0, Constant(0)),
+        ('int_add', i1, i0, i1),
+        ('int_sub', i0, Constant(1), i0),
+        ('goto', TLabel('L1')),
+        (Label('L2'),),
+        ('int_return', i1),
+        ]
+    asm = format_assembler(ssarepr)
+    expected = """
+        livevars [%i0, %i1]
+        L1:
+        goto_if_not_int_gt L2, %i0, $0
+        int_add %i1, %i0, %i1
+        int_sub %i0, $1, %i0
+        goto L1
+        L2:
+        int_return %i1
+    """
+    assert asm == str(py.code.Source(expected)).strip() + '\n'

Added: pypy/branch/blackhole-improvement/pypy/jit/codewriter/test/test_regalloc.py
==============================================================================
--- (empty file)
+++ pypy/branch/blackhole-improvement/pypy/jit/codewriter/test/test_regalloc.py	Mon Apr 19 10:51:01 2010
@@ -0,0 +1,111 @@
+import py
+from pypy.jit.codewriter import support
+from pypy.jit.codewriter.regalloc import perform_register_allocation
+from pypy.jit.codewriter.flatten import flatten_graph
+from pypy.jit.codewriter.format import format_assembler
+
+
+class TestRegAlloc:
+
+    def make_graphs(self, func, values, type_system='lltype'):
+        self.rtyper = support.annotate(func, values, type_system=type_system)
+        return self.rtyper.annotator.translator.graphs
+
+    def check_assembler(self, graph, expected):
+        regalloc = perform_register_allocation(graph)
+        ssarepr = flatten_graph(graph, regalloc)
+        asm = format_assembler(ssarepr)
+        assert asm == str(py.code.Source(expected).strip()) + '\n'
+
+    def test_regalloc_simple(self):
+        def f(a, b):
+            return a + b
+        graph = self.make_graphs(f, [5, 6])[0]
+        regalloc = perform_register_allocation(graph)
+        va, vb = graph.startblock.inputargs
+        vc = graph.startblock.operations[0].result
+        assert regalloc.getcolor(va) == 0
+        assert regalloc.getcolor(vb) == 1
+        assert regalloc.getcolor(vc) == 0
+
+    def test_regalloc_loop(self):
+        def f(a, b):
+            while a > 0:
+                b += a
+                a -= 1
+            return b
+        graph = self.make_graphs(f, [5, 6])[0]
+        self.check_assembler(graph, """
+            L1:
+            int_gt %i0, $0, %i2
+            goto_if_not L2, %i2
+            int_add %i1, %i0, %i1
+            int_sub %i0, $1, %i0
+            goto L1
+            L2:
+            int_return %i1
+        """)
+
+    def test_regalloc_loop_swap(self):
+        def f(a, b):
+            while a > 0:
+                a, b = b, a
+            return b
+        graph = self.make_graphs(f, [5, 6])[0]
+        self.check_assembler(graph, """
+            L1:
+            int_gt %i0, $0, %i2
+            goto_if_not L2, %i2
+            int_rename [%i1, %i0], [%i0, %i1]
+            goto L1
+            L2:
+            int_return %i1
+        """)
+
+    def test_regalloc_loop_constant(self):
+        def f(a, b):
+            while a > 0:
+                a, b = b, 2
+            return b
+        graph = self.make_graphs(f, [5, 6])[0]
+        self.check_assembler(graph, """
+            L1:
+            int_gt %i0, $0, %i0
+            goto_if_not L2, %i0
+            int_rename [%i1, $2], [%i0, %i1]
+            goto L1
+            L2:
+            int_return %i1
+        """)
+
+    def test_regalloc_cycle(self):
+        def f(a, b, c):
+            while a > 0:
+                a, b, c = b, c, a
+            return b
+        graph = self.make_graphs(f, [5, 6, 7])[0]
+        self.check_assembler(graph, """
+            L1:
+            int_gt %i0, $0, %i3
+            goto_if_not L2, %i3
+            int_rename [%i1, %i2, %i0], [%i0, %i1, %i2]
+            goto L1
+            L2:
+            int_return %i1
+        """)
+
+    def test_regalloc_same_as_var(self):
+        def f(a, b, c):
+            while a > 0:
+                b = c
+            return b
+        graph = self.make_graphs(f, [5, 6, 7])[0]
+        self.check_assembler(graph, """
+            L1:
+            int_gt %i0, $0, %i3
+            goto_if_not L2, %i3
+            int_rename [%i2], [%i1]
+            goto L1
+            L2:
+            int_return %i1
+        """)



More information about the Pypy-commit mailing list