[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