[pypy-commit] pypy generator-in-rpython: Write and test pieces of the final solution
arigo
noreply at buildbot.pypy.org
Mon Dec 19 18:23:01 CET 2011
Author: Armin Rigo <arigo at tunes.org>
Branch: generator-in-rpython
Changeset: r50713:6fa43287fca0
Date: 2011-12-19 17:13 +0100
http://bitbucket.org/pypy/pypy/changeset/6fa43287fca0/
Log: Write and test pieces of the final solution
diff --git a/pypy/translator/generator.py b/pypy/translator/generator.py
new file mode 100644
--- /dev/null
+++ b/pypy/translator/generator.py
@@ -0,0 +1,136 @@
+from pypy.objspace.flow.model import Block, Link, SpaceOperation, checkgraph
+from pypy.objspace.flow.model import Variable, Constant, FunctionGraph
+from pypy.translator.unsimplify import insert_empty_startblock
+from pypy.translator.unsimplify import split_block
+from pypy.translator.simplify import eliminate_empty_blocks
+
+
+class AbstractPosition(object):
+ _immutable_ = True
+ _attrs_ = ()
+
+
+def replace_graph_with_bootstrap(graph, graph_of_body, Entry):
+ #
+ class GeneratorIterator(object):
+ graph = graph_of_body
+ def __init__(self, entry):
+ self.current = entry
+ GeneratorIterator.Entry = Entry
+ #
+ newblock = Block(graph.startblock.inputargs)
+ v_generator = Variable('generator')
+ v_entry = Variable('entry')
+ newblock.operations.append(
+ SpaceOperation('simple_call', [Constant(Entry)], v_entry))
+ assert len(graph.startblock.inputargs) == len(Entry.varnames)
+ for v, name in zip(graph.startblock.inputargs, Entry.varnames):
+ newblock.operations.append(
+ SpaceOperation('setattr', [v_entry, Constant(name), v],
+ Variable()))
+ newblock.operations.append(
+ SpaceOperation('simple_call', [Constant(GeneratorIterator), v_entry],
+ v_generator))
+ newblock.closeblock(Link([v_generator], graph.returnblock))
+ graph.startblock = newblock
+ return GeneratorIterator
+
+def get_variable_names(variables):
+ seen = set()
+ result = []
+ for v in variables:
+ name = v._name.strip('_')
+ while name in seen:
+ name += '_'
+ result.append('g_' + name)
+ seen.add(name)
+ return result
+
+def _insert_reads(block, varnames):
+ assert len(varnames) == len(block.inputargs)
+ v_entry1 = Variable('entry')
+ for i, name in enumerate(varnames):
+ block.operations.insert(i,
+ SpaceOperation('getattr', [v_entry1, Constant(name)],
+ block.inputargs[i]))
+ block.inputargs = [v_entry1]
+
+def tweak_generator_body_graph(graph):
+ assert graph.startblock.operations[0].opname == 'generator_mark'
+ graph.startblock.operations.pop(0)
+ #
+ entryvarnames = get_variable_names(graph.startblock.inputargs)
+ insert_empty_startblock(None, graph)
+ _insert_reads(graph.startblock, entryvarnames)
+ #
+ class Entry(AbstractPosition):
+ block = graph.startblock
+ varnames = entryvarnames
+ mappings = [Entry]
+ #
+ for block in list(graph.iterblocks()):
+ for exit in block.exits:
+ if exit.target is graph.returnblock:
+ exit.args = [Constant(StopIteration),
+ Constant(StopIteration())]
+ exit.target = graph.exceptblock
+ for index in range(len(block.operations)-1, -1, -1):
+ op = block.operations[index]
+ if op.opname == 'yield':
+ [v_yielded_value] = op.args
+ del block.operations[index]
+ newlink = split_block(None, block, index)
+ newblock = newlink.target
+ #
+ class Resume(AbstractPosition):
+ block = newblock
+ Resume.__name__ = 'Resume%d' % len(mappings)
+ mappings.append(Resume)
+ varnames = get_variable_names(newlink.args)
+ #
+ _insert_reads(newblock, varnames)
+ #
+ v_resume = Variable('resume')
+ block.operations.append(
+ SpaceOperation('simple_call', [Constant(Resume)],
+ v_resume))
+ for i, name in enumerate(varnames):
+ block.operations.append(
+ SpaceOperation('setattr', [v_resume, Constant(name),
+ newlink.args[i]],
+ Variable()))
+ v_pair = Variable('pair')
+ block.operations.append(
+ SpaceOperation('newtuple', [v_resume, v_yielded_value],
+ v_pair))
+ newlink.args = [v_pair]
+ newlink.target = graph.returnblock
+ #
+ regular_entry_block = Block([Variable('entry')])
+ block = regular_entry_block
+ for Resume in mappings:
+ v_check = Variable()
+ block.operations.append(
+ SpaceOperation('simple_call', [Constant(isinstance),
+ block.inputargs[0],
+ Constant(Resume)],
+ v_check))
+ block.exitswitch = v_check
+ link1 = Link([block.inputargs[0]], Resume.block)
+ link1.exitcase = True
+ nextblock = Block([Variable('entry')])
+ link2 = Link([block.inputargs[0]], nextblock)
+ link2.exitcase = False
+ block.closeblock(link1, link2)
+ block = nextblock
+ block.closeblock(Link([Constant(AssertionError),
+ Constant(AssertionError("bad generator class"))],
+ graph.exceptblock))
+ graph.startblock = regular_entry_block
+ checkgraph(graph)
+ eliminate_empty_blocks(graph)
+ try:
+ graph.func._always_inline_ = True
+ except AttributeError:
+ pass
+ return Entry
diff --git a/pypy/translator/test/test_generator.py b/pypy/translator/test/test_generator.py
--- a/pypy/translator/test/test_generator.py
+++ b/pypy/translator/test/test_generator.py
@@ -4,6 +4,7 @@
from pypy.translator.translator import TranslationContext
from pypy.translator.generator import replace_graph_with_bootstrap
from pypy.translator.generator import get_variable_names
+from pypy.translator.generator import tweak_generator_body_graph
# ____________________________________________________________
@@ -70,66 +71,37 @@
space = FlowObjSpace()
graph = space.build_flow(func)
assert graph.startblock.operations[0].opname == 'generator_mark'
- replace_graph_with_bootstrap(graph, 'newgraph')
+ class Entry:
+ varnames = ['g_n', 'g_x', 'g_y', 'g_z']
+ replace_graph_with_bootstrap(graph, 'newgraph', Entry)
if option.view:
graph.show()
block = graph.startblock
ops = block.operations
- assert ops[0].opname == 'call' # e = Entry1()
- assert ops[1].opname == 'setattr' # e.g_n = n
+ assert ops[0].opname == 'simple_call' # e = Entry1()
+ assert ops[1].opname == 'setattr' # e.g_n = n
assert ops[1].args[1].value == 'g_n'
- assert ops[2].opname == 'setattr' # e.g_x = x
+ assert ops[2].opname == 'setattr' # e.g_x = x
assert ops[2].args[1].value == 'g_x'
- assert ops[3].opname == 'setattr' # e.g_y = y
+ assert ops[3].opname == 'setattr' # e.g_y = y
assert ops[3].args[1].value == 'g_y'
- assert ops[4].opname == 'setattr' # e.g_z = z
+ assert ops[4].opname == 'setattr' # e.g_z = z
assert ops[4].args[1].value == 'g_z'
- assert ops[5].opname == 'call' # g = GeneratorIterator(e)
+ assert ops[5].opname == 'simple_call' # g = GeneratorIterator(e)
assert ops[5].args[1] == ops[0].result
assert len(ops) == 6
assert len(block.exits) == 1
assert block.exits[0].target is graph.returnblock
- def test_make_generator_body_graph(self):
+ def test_tweak_generator_body_graph(self):
def f(n, x, y, z):
z *= 10
- yield n
+ yield n + 1
z -= 10
#
- def f__next(generator):
- n = generator.n_0
- x = generator.x_0
- y = generator.y_0
- z = generator.z_0
- e = generator.current
- generator.current = None
- if isinstance(e, "some class"):
- xxx
- #
space = FlowObjSpace()
- graph = space.build_flow(func)
- newgraph = make_generator_body_graph(graph)
- assert len(newgraph.startblock.inputargs) == 1
- [v_generator] = newgraph.startblock.inputargs
- ops = newgraph.startblock.operations
- assert ops[0].opname == 'getattr' # n = g.n_0
- assert ops[0].args[0] == v_generator
- assert ops[0].args[1].value.startswith('n_')
- assert ops[1].opname == 'getattr' # x = g.x_0
- assert ops[1].args[0] == v_generator
- assert ops[1].args[1].value.startswith('x_')
- assert ops[2].opname == 'getattr' # y = g.y_0
- assert ops[2].args[0] == v_generator
- assert ops[2].args[1].value.startswith('y_')
- assert ops[3].opname == 'getattr' # z = g.z_0
- assert ops[3].args[0] == v_generator
- assert ops[3].args[1].value.startswith('z_')
- assert ops[4].opname == 'getattr' # e = g.current
- assert ops[4].args[0] == v_generator
- assert ops[4].args[1].value == 'current'
- assert ops[5].opname == 'setattr' # g.current = None
- assert ops[5].args[0] == v_generator
- assert ops[5].args[1].value == 'current'
- assert ops[6].opname == 'call' # isinstance(e, Yield1)
- assert ops[6].args[0].value == isinstance
- assert len(ops) == 7
+ graph = space.build_flow(f)
+ tweak_generator_body_graph(graph)
+ if option.view:
+ graph.show()
+ # XXX how to test directly that the graph is correct? :-(
More information about the pypy-commit
mailing list