[pypy-svn] r6729 - pypy/trunk/src/pypy/translator

arigo at codespeak.net arigo at codespeak.net
Fri Sep 24 21:43:52 CEST 2004


Author: arigo
Date: Fri Sep 24 21:43:52 2004
New Revision: 6729

Modified:
   pypy/trunk/src/pypy/translator/transform.py
Log:
A long function with no associated test.  Ugh.

This is an attempt to solve the problems related to generating C code for
'list += otherlist' expressions.  This involves generating a loop with
potentially conversion operations inside.  Where should the loop come from?  
It is tricky to generate its C code manually (e.g. writing a for(...) directly
in the C source from genc_op.py) because the copy-and-convert may fail, and
explicit control flow in the C code written by genc_op.py doesn't mix well
with typer.py's efforts to Py_DECREF things correctly after errors.  Indeed,
typer.py assumes that the control flow is exactly as specified by the control
flow graph (duh).

So this tries to attack the problem earlier: transform.py will detect the +=
operation and replace it with new control flow graph blocks that do, in a
loop, the getting-items-from-list2-and-appending-them-to-list1 dance.

This looks good, actually: for ease of code generation as well as for
optimizations, transforming the control flow graph in various ways will help.  
Maybe we should have utility functions to make this easier...  Currently it's
pretty verbose to manipulate control flow graphs.

There is no test for transform.py.  But it's not obvious how to test it...

All that remain to make the tests pass again should be to support in genc
these new introduced SpaceOperations (growlist, fastappend).



Modified: pypy/trunk/src/pypy/translator/transform.py
==============================================================================
--- pypy/trunk/src/pypy/translator/transform.py	(original)
+++ pypy/trunk/src/pypy/translator/transform.py	Fri Sep 24 21:43:52 2004
@@ -6,7 +6,9 @@
 
 import types
 from pypy.objspace.flow.model import SpaceOperation
+from pypy.objspace.flow.model import Variable, Constant, Block, Link
 from pypy.translator.annrpython import CannotSimplify
+from pypy.annotation import model as annmodel
 
 # XXX: Lots of duplicated codes. Fix this!
 
@@ -171,10 +173,135 @@
             if block.inputargs[i] not in read_vars:
                 del block.inputargs[i]
 
+# expands the += operation between lists into a basic block loop.
+#    a = inplace_add(b, c)
+# becomes the following graph:
+#
+#  clen = len(c)
+#  growlist(b, clen)     # ensure there is enough space for clen new items
+#        |
+#        |  (pass all variables to next block, plus i=0)
+#        V
+#  ,--> z = lt(i, clen)
+#  |    exitswitch(z):
+#  |     |          |        False
+#  |     | True     `------------------>  ...sequel...
+#  |     V
+#  |    x = getitem(c, i)
+#  |    fastappend(b, x)
+#  |    i1 = add(i, 1)
+#  |     |
+#  `-----'  (pass all variables, with i=i1)
+#
+def transform_listextend(self):
+    allblocks = list(self.annotated)
+    for block in allblocks:
+        for j in range(len(block.operations)):
+            op = block.operations[j]
+            if op.opname != 'inplace_add':
+                continue
+            a = op.result
+            b, c = op.args
+            s_list = self.bindings.get(b)
+            if not isinstance(s_list, annmodel.SomeList):
+                continue
+
+            # new variables
+            clen  = Variable()
+            i     = Variable()
+            i1    = Variable()
+            z     = Variable()
+            x     = Variable()
+            dummy = Variable()
+            self.setbinding(clen,  annmodel.SomeInteger(nonneg=True))
+            self.setbinding(i,     annmodel.SomeInteger(nonneg=True))
+            self.setbinding(i1,    annmodel.SomeInteger(nonneg=True))
+            self.setbinding(z,     annmodel.SomeBool())
+            self.setbinding(x,     s_list.s_item)
+            self.setbinding(dummy, annmodel.SomeImpossibleValue())
+
+            sequel_operations = block.operations[j+1:]
+            sequel_exitswitch = block.exitswitch
+            sequel_exits      = block.exits
+
+            del block.operations[j:]
+            block.operations += [
+                SpaceOperation('len', [c], clen),
+                SpaceOperation('growlist', [b, clen], dummy),
+                ]
+            block.exitswitch = None
+            allvars = block.getvariables()
+
+            condition_block = Block(allvars+[i])
+            condition_block.operations += [
+                SpaceOperation('lt', [i, clen], z),
+                ]
+            condition_block.exitswitch = z
+
+            loopbody_block = Block(allvars+[i])
+            loopbody_block.operations += [
+                SpaceOperation('getitem', [c, i], x),
+                SpaceOperation('fastappend', [b, x], dummy),
+                SpaceOperation('add', [i, Constant(1)], i1),
+                ]
+
+            sequel_block = Block(allvars+[a])
+            sequel_block.operations = sequel_operations
+            sequel_block.exitswitch = sequel_exitswitch
+
+            # link the blocks together
+            block.recloseblock(
+                Link(allvars+[Constant(0)], condition_block),
+                )
+            condition_block.closeblock(
+                Link(allvars+[i],           loopbody_block,  exitcase=True),
+                Link(allvars+[b],           sequel_block,    exitcase=False),
+                )
+            loopbody_block.closeblock(
+                Link(allvars+[i1],          condition_block),
+                )
+            sequel_block.closeblock(*sequel_exits)
+
+            # now rename the variables -- so far all blocks use the
+            # same variables, which is forbidden
+            renamevariables(self, condition_block)
+            renamevariables(self, loopbody_block)
+            renamevariables(self, sequel_block)
+
+            allblocks.append(sequel_block)
+            break
+
+def renamevariables(self, block):
+    """Utility to rename the variables in a block to fresh variables.
+    The annotations are carried over from the old to the new vars."""
+    varmap = {}
+    block.inputargs = [varmap.setdefault(a, Variable())
+                       for a in block.inputargs]
+    operations = []
+    for op in block.operations:
+        result = varmap.setdefault(op.result, Variable())
+        args = [varmap.get(a, a) for a in op.args]
+        op = SpaceOperation(op.opname, args, result)
+        operations.append(op)
+    block.operations = operations
+    block.exitswitch = varmap.get(block.exitswitch, block.exitswitch)
+    exits = []
+    for exit in block.exits:
+        args = [varmap.get(a, a) for a in exit.args]
+        exits.append(Link(args, exit.target, exit.exitcase))
+    block.recloseblock(*exits)
+    # carry over the annotations
+    for a1, a2 in varmap.items():
+        if a1 in self.bindings:
+            self.setbinding(a2, self.bindings[a1])
+    self.annotated[block] = True
+
+
 def transform_graph(ann):
     """Apply set of transformations available."""
     transform_allocate(ann)
     transform_slice(ann)
+    transform_listextend(ann)
     # do this last, after the previous transformations had a
     # chance to remove dependency on certain variables
     transform_dead_op_vars(ann)



More information about the Pypy-commit mailing list