[pypy-svn] r6218 - pypy/branch/pypy-genc/translator

arigo at codespeak.net arigo at codespeak.net
Mon Aug 30 12:55:25 CEST 2004


Author: arigo
Date: Mon Aug 30 12:55:25 2004
New Revision: 6218

Added:
   pypy/branch/pypy-genc/translator/typer.py
Modified:
   pypy/branch/pypy-genc/translator/genc.h
   pypy/branch/pypy-genc/translator/genc.py
   pypy/branch/pypy-genc/translator/simplify.py
Log:
Generate typed C code.


Modified: pypy/branch/pypy-genc/translator/genc.h
==============================================================================
--- pypy/branch/pypy-genc/translator/genc.h	(original)
+++ pypy/branch/pypy-genc/translator/genc.h	Mon Aug 30 12:55:25 2004
@@ -4,27 +4,64 @@
 
 #include <Python.h>
 
-#define INCREF(x)  Py_INCREF(x);
-#define DECREF(x)  Py_DECREF(x);
 
-#define CASE(x,y)           if (PyObject_RichCompareBool(x,y,Py_EQ))
-#define ELSE(x,y)           assert(PyObject_RichCompareBool(x,y,Py_EQ));
+/*** operations on ints ***/
 
-#define OP_LT(x,y,r,err)    if (!(r=PyObject_RichCompare(x,y,Py_LT))) goto err;
-#define OP_LE(x,y,r,err)    if (!(r=PyObject_RichCompare(x,y,Py_LT))) goto err;
-#define OP_EQ(x,y,r,err)    if (!(r=PyObject_RichCompare(x,y,Py_EQ))) goto err;
-#define OP_NE(x,y,r,err)    if (!(r=PyObject_RichCompare(x,y,Py_NE))) goto err;
-#define OP_GT(x,y,r,err)    if (!(r=PyObject_RichCompare(x,y,Py_GT))) goto err;
-#define OP_GE(x,y,r,err)    if (!(r=PyObject_RichCompare(x,y,Py_GE))) goto err;
-
-#define OP_IS_TRUE(x,r,err) switch (PyObject_IsTrue(x)) {		 \
-				case 0: r=Py_False; Py_INCREF(r); break; \
-				case -1: goto err;			 \
-				default: r=Py_True; Py_INCREF(r); }
-
-#define OP_ADD(x,y,r,err)         if (!(r=PyNumber_Add(x,y)))        goto err;
-#define OP_MOD(x,y,r,err)         if (!(r=PyNumber_Remainder(x,y)))  goto err;
-#define OP_INPLACE_ADD(x,y,r,err) if (!(r=PyNumber_InPlaceAdd(x,y))) goto err;
+#define OP_LT_iii(x,y,r)           r = x < y;
+#define OP_LE_iii(x,y,r)           r = x <= y;
+#define OP_EQ_iii(x,y,r)           r = x == y;
+#define OP_NE_iii(x,y,r)           r = x != y;
+#define OP_GT_iii(x,y,r)           r = x > y;
+#define OP_GE_iii(x,y,r)           r = x >= y;
+
+#define OP_IS_TRUE_ii(x,r)         r = !(!x);
+
+#define OP_ADD_iii(x,y,r)          r = x + y;
+#define OP_MOD_iii(x,y,r)          r = x % y;
+#define OP_INPLACE_ADD_iii(x,y,r)  r = x + y;
+
+
+/*** generic operations on PyObjects ***/
+
+#define op_richcmp_ooi(x,y,r,err,dir)   \
+                       if ((r=PyObject_RichCompareBool(x,y,dir))<0) goto err;
+#define OP_LT_ooi(x,y,r,err)  op_richcmp_ooi(x,y,r,err, Py_LT)
+#define OP_LE_ooi(x,y,r,err)  op_richcmp_ooi(x,y,r,err, Py_LE)
+#define OP_EQ_ooi(x,y,r,err)  op_richcmp_ooi(x,y,r,err, Py_EQ)
+#define OP_NE_ooi(x,y,r,err)  op_richcmp_ooi(x,y,r,err, Py_NE)
+#define OP_GT_ooi(x,y,r,err)  op_richcmp_ooi(x,y,r,err, Py_GT)
+#define OP_GE_ooi(x,y,r,err)  op_richcmp_ooi(x,y,r,err, Py_GE)
+
+#define OP_IS_TRUE_oi(x,r,err)      if ((r=PyObject_IsTrue(x))<0)      goto err;
+#define OP_ADD_ooo(x,y,r,err)       if (!(r=PyNumber_Add(x,y)))        goto err;
+#define OP_MOD_ooo(x,y,r,err)       if (!(r=PyNumber_Remainder(x,y)))  goto err;
+#define OP_INPLACE_ADD_ooo(x,y,r,err) if(!(r=PyNumber_InPlaceAdd(x,y)))goto err;
+
+
+/*** conversions ***/
+
+#define convert_io(x,r,err)   if (!(r=PyInt_FromLong(x))) goto err;
+
+
+/*** tests ***/
+
+#define caseFalse_i(n, err)    if (n) goto err;
+#define caseTrue_i(n, err)     if (!n) goto err;
+#define case0_i(n, err)        if (n != 0) goto err;
+#define case1_i(n, err)        if (n != 1) goto err;
+
+#define caseFalse_o(o, err)    if (!(PyInt_Check(o) && !PyInt_AS_LONG(o))) goto err;
+#define caseTrue_o(o, err)     if (!(PyInt_Check(o) && PyInt_AS_LONG(o))) goto err;
+#define case0_o(o, err) if (!(PyInt_Check(o) && PyInt_AS_LONG(o)==0)) goto err;
+#define case1_o(o, err) if (!(PyInt_Check(o) && PyInt_AS_LONG(o)==1)) goto err;
+
+
+/*** misc ***/
+
+#define return_i(n)             return n;
+#define return_o(o)             return o;
+#define incref_o(o)             Py_INCREF(o);
+#define decref_o(o)             Py_DECREF(o);
 
 
 /************************************************************/

Modified: pypy/branch/pypy-genc/translator/genc.py
==============================================================================
--- pypy/branch/pypy-genc/translator/genc.py	(original)
+++ pypy/branch/pypy-genc/translator/genc.py	Mon Aug 30 12:55:25 2004
@@ -2,10 +2,112 @@
 Generate a C source file from the flowmodel.
 
 """
-import autopath, os
+import autopath, os, re
 from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant
 from pypy.objspace.flow.model import Block, Link, traverse
-import inspect
+from pypy.translator.typer import LLFunction, LLOp
+from pypy.annotation import model as annmodel
+
+
+class CRepr:
+    "A possible representation of a flow-graph variable as C-level variables."
+
+    def __init__(self, impl, error_retval, parse_code=None):
+        self.impl = impl   # list [(C type, prefix for variable name)]
+        self.error_retval = error_retval  # return value signalling an error
+        self.parse_code = parse_code  # character(s) for PyArg_ParseTuple()
+
+    def __repr__(self):
+        if hasattr(self, 'const'):
+            return '<C const %s>' % (self.const,)
+        else:
+            return '<C: %s>' % ' '.join(self.impl)
+
+
+class CTypeSet:
+    "A (small) set of C types that typer.LLFunction can manipulate."
+
+    R_VOID   = CRepr([],             error_retval='-1')
+    R_INT    = CRepr(['int'],        error_retval='-1',   parse_code='i')
+    R_OBJECT = CRepr(['PyObject*'],  error_retval='NULL', parse_code='O')
+
+    REPR_BY_CODE = {
+        'i': R_INT,
+        'o': R_OBJECT,
+        }
+
+    def __init__(self, bindings):
+        self.bindings = bindings
+        self.r_constants = {}
+        self.lloperations = {'convert': {}, 'release': {}}
+        self.parse_operation_templates()
+
+    # __________ methods required by LLFunction __________
+    #
+    # Here, we assume that every high-level type has a canonical representation
+    # so a high-level type is just a CRepr.
+
+    def gethltype(self, var):
+        if isinstance(var, Variable):
+            s_value = self.bindings.get(var) or annmodel.SomeObject()
+            if s_value.is_constant():
+                return self.constant_representation(s_value.const)
+            if issubclass(s_value.knowntype, int):
+                return self.R_INT
+            # fall-back
+            return self.R_OBJECT
+        if isinstance(var, Constant):
+            return self.constant_representation(var.value)
+        raise TypeError, var
+
+    def represent(self, hltype):
+        return hltype.impl
+
+    # ____________________________________________________________
+
+    def constant_representation(self, value):
+        key = type(value), value   # to avoid mixing for example 0 and 0.0
+        try:
+            return self.r_constants[key]
+        except KeyError:
+            # a constant doesn't need any C variable to be encoded
+            r = self.r_constants[key] = CRepr([], '-1')
+            # but to convert it to something more general like an int or
+            # a PyObject* we need to revive its value, which is done by
+            # new conversion operations that we define now
+            conv = self.lloperations['convert']
+            if isinstance(value, int):
+                # can convert the constant to a C int
+                r.const = str(int(value))
+                llname = '%s = ' + r.const + ';'
+                conv[r, self.R_INT] = llname, False
+                # can convert the constant to a PyObject*
+                llname = 'convert_io(' + r.const + ', %s, %s)'
+                conv[r, self.R_OBJECT] = llname, True
+            else:
+                pass # XXX not implemented
+            return r
+
+    def parse_operation_templates(self):
+        # parse the genc.h header to figure out which macros are implemented
+        codes = ''.join(self.REPR_BY_CODE.keys())
+        pattern = r"#define ([A-Za-z_][0-9A-Za-z_]*)_([%s]*)[(](.*?)[)]" % codes
+        rexp = re.compile(pattern)
+        for line in C_HEADER.split('\n'):
+            match = rexp.match(line)
+            if match:
+                opname, typecodes, formalargs = match.groups()
+                llname = '%s_%s' % (opname, typecodes)
+                sig = tuple([self.REPR_BY_CODE[code] for code in typecodes])
+                can_fail = formalargs.replace(' ','').endswith(',err')
+                ops = self.lloperations.setdefault(opname, {})
+                assert sig not in ops, llname
+                l = ['%s'] * (len(typecodes) + can_fail)
+                llname = llname + '(' + ', '.join(l) + ')'
+                ops.setdefault(sig, (llname, can_fail))
+
+
+# ____________________________________________________________
 
 
 class GenC:
@@ -14,9 +116,19 @@
         self.f = f
         self.translator = translator
         self.modname = modname or translator.functions[0].__name__
-        self.annotator = translator.annotator
-        self.funcname = self.choose_func_names()
-        self.constname = self.choose_const_names()
+        if translator.annotator:
+            bindings = translator.annotator.bindings.copy()
+            # force the entry point's return type to be 'PyObject*'
+            try:
+                entrypoint = translator.functions[0]
+                del bindings[translator.flowgraphs[entrypoint].getreturnvar()]
+            except KeyError:
+                pass
+        else:
+            bindings = {}
+        self.typeset = CTypeSet(bindings)
+        self.llfunctions = {}
+        self.build_llfunctions()
         self.gen_source()
 
     def gen_source(self):
@@ -26,7 +138,7 @@
             'exported': self.translator.functions[0].__name__,
             }
         # header
-        print >> f, C_HEADER % info
+        print >> f, C_HEADER
 
         # forward declarations
         print >> f, '/* forward declarations */'
@@ -34,14 +146,6 @@
             print >> f, 'static %s;' % self.cfunction_header(func)
         print >> f
 
-        # constants
-        print >> f, '/* constants */'
-        consts = self.constname.values()
-        consts.sort()
-        for name in consts:
-            print >> f, 'static PyObject* %s;' % name
-        print >> f
-
         # function implementation
         print >> f, C_SEP
         print >> f
@@ -55,178 +159,98 @@
         self.gen_entrypoint(self.translator.functions[0])
         print >> f, C_ENTRYPOINT_FOOTER % info
 
-        # initialization of constants
-        print >> f, 'static void build_constants(void)'
-        print >> f, '{'
-        self.gen_constants()
-        print >> f, '}'
-
         # footer
         print >> f, C_METHOD_TABLE % info
         print >> f, C_FOOTER % info
 
 
-    def choose_func_names(self):
-        result = {}
+    def build_llfunctions(self):
         n = 0
         for func in self.translator.functions:
-            assert func not in result, '%r duplicate' % (func,)
-            result[func] = '%s__%d' % (func.func_name, n)
+            assert func not in self.llfunctions, '%r duplicate' % (func,)
+            self.llfunctions[func] = LLFunction(
+                typeset = self.typeset,
+                name    = '%s__%d' % (func.func_name, n),
+                graph   = self.translator.flowgraphs[func])
             n += 1
-        return result
-
-    def choose_const_names(self):
-        result = {}
-        def see(value):
-            if value not in result:
-                if isinstance(value, int):
-                    name = ('kint_%d' % value).replace('-','minus')
-                else:
-                    name = 'k%04d' % len(result)
-                result[value] = name
-        def visit(node):
-            if isinstance(node, Block):
-                for c in node.getconstants():
-                    see(c.value)
-            elif isinstance(node, Link):
-                see(node.exitcase)
-        for graph in self.translator.flowgraphs.itervalues():
-            traverse(visit, graph)
-        return result
-
-    def var_or_const_name(self, v):
-        if isinstance(v, Variable):
-            return v.name
-        else:
-            return self.constname[v.value]
 
     def cfunction_header(self, func):
-        graph = self.translator.flowgraphs[func]
-        args = graph.getargs()
-        _eval = self.var_or_const_name
-        returntypename = 'PyObject*'
-        l = ['PyObject* %s' % a for a in args]
-        return '%s %s(%s)' % (returntypename,
-                              self.funcname[func],
+        llfunc = self.llfunctions[func]
+        llargs, rettype = llfunc.ll_header()
+        l = ['%s %s' % (a.type, a.name) for a in llargs]
+        return '%s %s(%s)' % (rettype,
+                              llfunc.name,
                               ', '.join(l))
 
     def gen_entrypoint(self, func):
         f = self.f
-        graph = self.translator.flowgraphs[func]
-        args = graph.getargs()
-        l = ['"' + 'O'*len(args) + '"']
+        llfunc = self.llfunctions[func]
+        llargs, rettype = llfunc.ll_header()
+        assert rettype == 'PyObject*', rettype
+        l = []
         l2 = []
-        for i in range(len(args)):
-            print >> f, '\tPyObject* a%d;' % i
-            l.append('&a%d' % i)
-            l2.append('a%d' % i)
+        for a in llargs:
+            print >> f, '\t%s %s;' % (a.type, a.name)
+            l.append('&' + a.name)
+            l2.append(a.name)
+        formatstr = []
+        for v in llfunc.graph.getargs():
+            hltype = self.typeset.gethltype(v)
+            assert hltype.parse_code, (
+                "entry point arg %s has unsupported type %s" % (v, hltype))
+            formatstr.append(hltype.parse_code)
+        l.insert(0, '"' + ''.join(formatstr) + '"')
         print >> f, '\tif (!PyArg_ParseTuple(args, %s))' % ', '.join(l)
         print >> f, '\t\treturn NULL;'
-        print >> f, '\treturn %s(%s);' % (self.funcname[func],
-                                          ', '.join(l2))
-
-    def gen_constants(self):
-        f = self.f
-        consts = [(name, value) for value, name in self.constname.items()]
-        consts.sort()
-        for name, value in consts:
-            if isinstance(value, int):
-                print >> f, '\t%s = PyInt_FromLong(%d);' % (name, value)
-            elif value is None:
-                print >> f, '\t%s = Py_None;' % name
-            else:
-                raise ValueError, value
+        print >> f, '\treturn %s(%s);' % (llfunc.name, ', '.join(l2))
 
     def gen_cfunction(self, func):
         f = self.f
-        graph = self.translator.flowgraphs[func]
-        _eval = self.var_or_const_name
-        allblocks = []
-        blockname = {}
-        def visit(n):
-            if isinstance(n, Block):
-                allblocks.append(n)
-                blockname[n] = 'block%d' % len(blockname)
-        traverse(visit, graph)
-        
-        # header
+        llfunc = self.llfunctions[func]
+
+        # print header
         print >> f, self.cfunction_header(func)
         print >> f, '{'
 
-        # local variables
-        input_args = {}
-        for var in graph.getargs():
-            input_args[var] = True
-        for block in allblocks:
-            for var in block.getvariables():
-                if var not in input_args:
-                    print >> f, '\tPyObject* %s;' % var
-        print >> f
-
-        # body
-        for block in allblocks:
-            print >> f, '  %s:' % blockname[block]
-            to_release = block.inputargs[:]
-            errlabels = {}
-            
-            # basic block operations
-            for op in block.operations:
-                l = [_eval(a) for a in op.args]
-                n = len(to_release)
-                errlabel = '%s_err%d' % (blockname[block], n)
-                errlabels[n] = errlabel
-                to_release.append(op.result)
-                print >> f, '\tOP_%s(%s, %s, %s)' % (op.opname.upper(),
-                                                     ', '.join(l),
-                                                     op.result,
-                                                     errlabel)
-
-            # exits
-            if block.exits:
-                macros = ['CASE'] * (len(block.exits)-1) + ['ELSE']
-                for macro, exit in zip(macros, block.exits):
-                    if len(block.exits) == 1:
-                        indent = '\t'
-                        close = ''
-                    else:
-                        print >> f, '\t%s(%s, %s) {' % (
-                            macro, _eval(block.exitswitch),
-                            self.constname[exit.exitcase])
-                        indent = '\t\t'
-                        close = '\t}'
-                    sourceargs = exit.args
-                    targetargs = exit.target.inputargs
-                    assert len(sourceargs) == len(targetargs)
-                    for n in range(len(to_release)-1, -1, -1):
-                        var = to_release[n]
-                        if var not in sourceargs:
-                            print >> f, '%sDECREF(%s)' % (indent, var)
-                    for s,t in zip(sourceargs, targetargs):
-                        sname = _eval(s)
-                        if isinstance(s, Constant):
-                            print >> f, '%sINCREF(%s)' % (indent, sname)
-                        print >> f, '%s%s = %s;' % (indent, t, sname)
-                    print >> f, '%sgoto %s;' % (indent,
-                                                blockname[exit.target])
-                    if close:
-                        print >> f, close
-            elif hasattr(block, 'exc_type'):
-                xxx_raise
-            else:
-                print >> f, '\treturn %s;' % _eval(block.inputargs[0])
+        # generate the body of the function
+        body = list(llfunc.ll_body())
 
-            # error paths
-            reachable = False
-            for n in range(len(to_release)-1, -1, -1):
-                if reachable:
-                    print >> f, '\tDECREF(%s)' % to_release[n]
-                if n in errlabels:
-                    print >> f, '   %s:' % errlabels[n]
-                    reachable = True
-            if reachable:
-                print >> f, '\treturn NULL;'
-            print >> f
+        # collect and print all the local variables from the body
+        llargs, rettype = llfunc.ll_header()
+        locals = llargs[:]
+        for line in body:
+            if isinstance(line, LLOp):
+                locals += line.args
+        seen = {}
+        for a in llargs:
+            seen[a] = True
+        for a in locals:
+            if a not in seen:
+                print >> f, '\t%s %s;' % (a.type, a.name)
+                seen[a] = True
+        print >> f
 
+        # print the body
+        for line in body:
+            if isinstance(line, LLOp):
+                args = [a.name for a in line.args]
+                if line.errtarget:
+                    args.append(line.errtarget)
+                s = line.name
+                if s == 'move':
+                    print >> f, '\t%s = %s;' % (args[1], args[0])
+                elif s == 'goto':
+                    print >> f, '\tgoto %s;' % tuple(args)
+                elif s == 'returnerror':
+                    v = llfunc.graph.getreturnvar()
+                    hltype = self.typeset.gethltype(v)
+                    print >> f, '\treturn %s;' % hltype.error_retval
+                else:  # common case of operations
+                    print >> f, '\t' + s % tuple(args)
+            elif line:  # label
+                print >> f, '   %s:' % line
+            else:  # empty line
+                print >> f
         print >> f, '}'
 
 
@@ -238,26 +262,20 @@
 
 C_ENTRYPOINT_HEADER = '''
 static PyObject* c_%(exported)s(PyObject* self, PyObject* args)
-{
-'''
+{'''
 
-C_ENTRYPOINT_FOOTER = '''
-}
-'''
+C_ENTRYPOINT_FOOTER = '''}'''
 
 C_METHOD_TABLE = '''
 static PyMethodDef %(modname)sMethods[] = {
 \t{"%(exported)s", (PyCFunction)c_%(exported)s, METH_VARARGS},
 \t{NULL, NULL}
-};
-'''
+};'''
 
 C_FOOTER = '''
 void init%(modname)s(void)
 {
 \tPy_InitModule("%(modname)s", %(modname)sMethods);
-\tbuild_constants();
-}
-'''
+}'''
 
 # ____________________________________________________________

Modified: pypy/branch/pypy-genc/translator/simplify.py
==============================================================================
--- pypy/branch/pypy-genc/translator/simplify.py	(original)
+++ pypy/branch/pypy-genc/translator/simplify.py	Mon Aug 30 12:55:25 2004
@@ -85,3 +85,19 @@
     remove_implicit_exceptions(graph)
     join_blocks(graph)
     return graph
+
+
+def remove_direct_loops(graph):
+    """This is useful for code generators: it ensures that no link has
+    common input and output variables, which could occur if a block's exit
+    points back directly to the same block.  It allows code generators to be
+    simpler because they don't have to worry about overwriting input
+    variables when generating a sequence of assignments."""
+    def visit(link):
+        if isinstance(link, Link) and link.prevblock is link.target:
+            # insert an empty block with fresh variables.
+            intermediate = [Variable() for a in link.args]
+            b = Block(intermediate)
+            b.closeblock(Link(intermediate, link.target))
+            link.target = b
+    traverse(visit, graph)

Added: pypy/branch/pypy-genc/translator/typer.py
==============================================================================
--- (empty file)
+++ pypy/branch/pypy-genc/translator/typer.py	Mon Aug 30 12:55:25 2004
@@ -0,0 +1,352 @@
+"""
+Graph-to-low-level transformer for C/Assembler code generators.
+"""
+
+from __future__ import generators
+from pypy.objspace.flow.model import Constant, Variable, Block, Link, traverse
+from pypy.translator.simplify import remove_direct_loops
+
+
+class LLVar:
+    "A variable in the low-level language."
+    def __init__(self, type, name):
+        self.type = type   # low-level type in any format, e.g. a C type name
+        self.name = name
+
+class LLOp:
+    "A low-level operation."
+    def __init__(self, name, args, errtarget=None):
+        self.name = name    # low-level operation name
+        self.args = args    # list of LLVars
+        self.errtarget = errtarget  # label to jump to in case of error
+
+
+# ____________________________________________________________
+#
+# below, a 'typeset' is an object with the following methods/attributes:
+#
+#   def gethltype(self, var_or_const)
+#       Return the high-level type of a var or const.
+#
+#   def represent(self, hltype)
+#       Return a list of LLTypes that together implement the high-level type.
+#
+#   lloperations = {
+#       'opname': {
+#           (tuple-of-hltypes): (low-level-name, flag-can-fail),
+#           ... },
+#       ...}
+#       This dict contains the known signatures of each space operation.
+#       Special opnames:
+#         'convert' x y : convert some x to y
+#         'incref'    z : get a new ref to the object z
+#         'decref'    z : release (or decref) the object z
+#         'caseXXX'   z : fails (i.e. jump to errlabel) if z is not XXX
+#         'return'    z : z is the return value of the function
+#
+#       Low-level-only operation names:
+#         'goto'        : fails unconditionally (i.e. jump to errlabel)
+#         'move'    x y : raw copy of the LLVar x to the LLVar y
+#         'returnerror' : return an error code from the function
+# ____________________________________________________________
+
+
+class LLFunction:
+    "A low-level version of a function from a control flow graph."
+
+    def __init__(self, typeset, name, graph):
+        remove_direct_loops(graph)
+        self.name = name
+        self.graph = graph
+        self.gethltype    = typeset.gethltype
+        self.represent    = typeset.represent
+        self.lloperations = typeset.lloperations
+        self.hltypes = {}
+        self.llreprs = {}
+
+    def ll_header(self):
+        """
+        Get the low-level representation of the header.
+        """
+        llrepr = []
+        for v in self.graph.getargs():
+            self.makevar(v)
+            llrepr += self.llreprs[v]
+        v = self.graph.getreturnvar()
+        self.makevar(v)
+        llret = self.llreprs[v]
+        if len(llret) == 0:
+            retlltype = None
+        elif len(llret) == 1:
+            retlltype = llret[0].type
+        else:
+            XXX("to do")
+        return llrepr, retlltype
+
+    def ll_body(self):
+        """
+        Get the body by flattening and low-level-izing the flow graph.
+        Enumerates low-level operations: LLOps with labels inbetween (strings).
+        """
+        self.blockname = {}
+        self.release_root = ReleaseNode(None, LLOp('returnerror', []), None)
+        allblocks = []
+        
+        # collect all blocks
+        def visit(block):
+            if isinstance(block, Block):
+                allblocks.append(block)
+                self.blockname[block] = 'block%d' % len(self.blockname)
+                for v in block.inputargs:
+                    self.makevar(v)
+        traverse(visit, self.graph)
+
+        # generate an incref for each input argument
+        for v in self.graph.getargs():
+            sig = (self.hltypes[v],)
+            try:
+                llname, can_fail = self.lloperations['incref'][sig]
+            except KeyError:
+                continue   # ignore types with no particular incref operation
+            assert not can_fail
+            yield LLOp(llname, self.llreprs[v])
+
+        # generate the body of each block
+        for block in allblocks:
+            for op in self.generate_block(block):
+                yield op
+            yield ''   # empty line
+
+        # generate the code to handle errors
+        for op in self.release_root.error_code():
+            yield op
+
+    def generate_block(self, block):
+        "Generate the operations for one basic block."
+        self.to_release = self.release_root
+        for v in block.inputargs:
+            self.mark_release(v)
+        # entry point
+        self.blockops = [self.blockname[block]]   # label
+        # basic block operations
+        for op in block.operations:
+            self.operation('OP_' + op.opname.upper(), list(op.args), op.result)
+        # exits
+        if block.exits:
+            for exit in block.exits[:-1]:
+                # generate | caseXXX v elselabel
+                #          |   copy output vars to next block's input vars
+                #          |   jump to next block
+                #          | elselabel:
+                elselabel = '%s_not%s' % (self.blockname[block], exit.exitcase)
+                self.operation('case%s' % exit.exitcase,
+                               [block.exitswitch],
+                               errlabel = elselabel)
+                self.goto(exit)
+                self.blockops.append(elselabel)
+            # for the last exit, generate only the jump to next block
+            exit = block.exits[-1]
+            self.goto(exit)
+
+        elif hasattr(block, 'exc_type'):
+            XXX("to do")
+        else:
+            self.operation('return', block.inputargs)
+        return self.blockops
+
+    # __________ Type checking and conversion routines __________
+
+    def makevar(self, v, hltype=None):
+        "Record v in self.hltypes and self.llreprs."
+        if v in self.llreprs:
+            return
+        if hltype is None:
+            hltype = self.gethltype(v)
+        llrepr = []
+        lltypes = self.represent(hltype)
+        for i, lltype in zip(range(len(lltypes)), lltypes):
+            if i:
+                suffix = '_%d' % i
+            else:
+                suffix = ''
+            llrepr.append(LLVar(lltype, v.name + suffix))
+        self.hltypes[v] = hltype
+        self.llreprs[v] = llrepr
+
+    def mark_release(self, v):
+        sig = (self.hltypes[v],)
+        try:
+            llname, can_fail = self.lloperations['decref'][sig]
+        except KeyError:
+            return   # ignore types with no particular decref operation
+        assert not can_fail
+        llop = LLOp(llname, self.llreprs[v])
+        # make a new node for the release tree
+        self.to_release = ReleaseNode(v, llop, self.to_release)
+
+    def convert_from(self, hltype):
+        # enumerate all types that hltype can be converted to
+        for frm, to in self.lloperations['convert']:
+            if frm == hltype:
+                yield to
+
+    def convert_to(self, hltype):
+        # enumerate all types that can be converted to hltype
+        for frm, to in self.lloperations['convert']:
+            if to == hltype:
+                yield frm
+
+    def operation(self, opname, args, result=None, errlabel=None):
+        "Helper to build the LLOps for a single high-level operation."
+        # get the hltypes of the input arguments
+        for v in args:
+            self.makevar(v)
+        args_t = [self.hltypes[v] for v in args]
+        directions = [self.convert_from] * len(args)
+        # append the hltype of the result
+        if result:
+            self.makevar(result)
+            args_t.append(self.hltypes[result])
+            directions.append(self.convert_to)
+        # enumerate possible signatures until we get a match
+        llsigs = self.lloperations[opname]
+        for sig in variants(tuple(args_t), directions):
+            if sig in llsigs:
+                llname, can_fail = llsigs[sig]
+                break
+        else:
+            raise TypingError, (opname, args_t)
+        # convert input args to temporary variables
+        llargs = []
+        for v, v_t, s_t in zip(args, args_t, sig):
+            if v_t != s_t:
+                tmp = Variable()
+                self.makevar(tmp, hltype=s_t)
+                self.operation('convert', [v], tmp)
+                v = tmp
+            llargs += self.llreprs[v]
+        # generate an error label if the operation can fail
+        if can_fail and errlabel is None:
+            errlabel = self.to_release.getlabel()
+        # case-by-case analysis of the result variable
+        if result:
+            if args_t[-1] == sig[-1]:
+                # the result has the correct type
+                llargs += self.llreprs[result]
+                self.blockops.append(LLOp(llname, llargs, errlabel))
+                self.mark_release(result)
+            else:
+                # the result has to be converted
+                tmp = Variable()
+                self.makevar(tmp, hltype=sig[-1])
+                llargs += self.llreprs[tmp]
+                self.blockops.append(LLOp(llname, llargs, errlabel))
+                self.mark_release(tmp)
+                self.operation('convert', [tmp], result)
+        else:
+            # no result variable
+            self.blockops.append(LLOp(llname, llargs, errlabel))
+
+    def goto(self, exit):
+        # generate the exit.args -> target.inputargs copying operations
+        to_release_copy = self.to_release
+        try:
+            # copy the data, performing any necessary conversion
+            # See also remove_direct_loops() for why we don't worry about
+            # the order of the operations
+            dontrelease = {}
+            for v, w in zip(exit.args, exit.target.inputargs):
+                self.makevar(v)
+                if self.hltypes[v] == self.hltypes[w]:  # fast case
+                    for x, y in zip(self.llreprs[v], self.llreprs[w]):
+                        self.blockops.append(LLOp('move', [x, y]))
+                    dontrelease[v] = True
+                else:
+                    self.operation('convert', [v], w)
+                    dontrelease[w] = True
+            # then release all the variables that go out of scope minus the
+            # ones that are carried over to the next block
+            for node in self.to_release.getbranch():
+                if node.var not in dontrelease:
+                    self.blockops.append(node.release_operation)
+            # finally jump to the target block
+            self.blockops.append(LLOp('goto', [], self.blockname[exit.target]))
+        finally:
+            self.to_release = to_release_copy
+            # after a call to goto() we are back to generating ops for
+            # other cases, so we restore the previous self.to_release.
+
+# ____________________________________________________________
+
+
+class TypingError(Exception):
+    pass
+
+
+# In a function, all the variables that have to released can be organized
+# in a tree in which each node is a variable: whenever this variable has
+# to be released, its parent in the tree has to be release too, and its
+# parent's parent and so on up to the root.
+class ReleaseNode:
+    accessible = False
+    label = None
+    counter = 0
+    
+    def __init__(self, var, release_operation, parent):
+        self.var = var
+        self.release_operation = release_operation
+        self.parent = parent
+        self.accessible_children = []
+
+    def mark_accessible(self):
+        if not self.accessible:
+            self.accessible = True
+            if self.parent:
+                self.parent.accessible_children.append(self)
+                self.parent.mark_accessible()
+
+    def nextlabel(self):
+        while self.parent:
+            self = self.parent
+        self.counter += 1
+        return 'err%d' % self.counter
+
+    def getlabel(self):
+        if self.label is None:
+            self.mark_accessible()
+            self.label = self.nextlabel()
+        return self.label
+
+    def getbranch(self):
+        while self.parent:
+            yield self
+            self = self.parent
+
+    def error_code(self):
+        N = len(self.accessible_children)
+        for i in range(N):
+            if i > 0:
+                yield LLOp('goto', [], self.getlabel())
+            node = self.accessible_children[~i]
+            for op in node.error_code():
+                yield op
+        if self.label:
+            yield self.label
+        elif not N:
+            return
+        yield self.release_operation
+
+
+def variants(args_t, directions):
+    # enumerate all variants of the given signature of hltypes
+    # XXX this can become quadratically slow for large programs because
+    # XXX it enumerates all conversions of the result from a constant value
+    if len(args_t):
+        for sig in variants(args_t[:-1], directions[:-1]):
+            yield sig + args_t[-1:]
+        choices_for_last_arg = list(directions[-1](args_t[-1]))
+        for sig in variants(args_t[:-1], directions[:-1]):
+            for last_arg in choices_for_last_arg:
+                yield sig + (last_arg,)
+    else:
+        yield ()



More information about the Pypy-commit mailing list