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

arigo at codespeak.net arigo at codespeak.net
Thu Sep 9 21:16:08 CEST 2004


Author: arigo
Date: Thu Sep  9 21:16:06 2004
New Revision: 6372

Added:
   pypy/trunk/src/pypy/translator/genc_repr.py
Modified:
   pypy/trunk/src/pypy/translator/classtyper.py
   pypy/trunk/src/pypy/translator/genc.py
   pypy/trunk/src/pypy/translator/genc_op.py
   pypy/trunk/src/pypy/translator/genc_typeset.py
   pypy/trunk/src/pypy/translator/typer.py
Log:
Urgh, another huge check-in.  This one introduces classes for various
kinds of representations, and put them to the new genc_repr.py.  This is a
clean-up of the conversion mess.  Now the representation classes,
subclassing CRepr, provide a convert_to() method called when a value of
some type (i.e. with some repr) needs to be converted to a value of some
other, given type.

To support this model, the operations have a "cost" and typer.py will
select, among the variants of an operation, the one with the less cost
(including the cost of conversions).  The operation() method is (a bit)  
simpler now.

genc_typeset.py is much more readable now: the three huge functions have
been split up in genc_repr.py and in a set of build_OP_XXX() methods.  
Automatic chaining of conversion is gone again.


Modified: pypy/trunk/src/pypy/translator/classtyper.py
==============================================================================
--- pypy/trunk/src/pypy/translator/classtyper.py	(original)
+++ pypy/trunk/src/pypy/translator/classtyper.py	Thu Sep  9 21:16:06 2004
@@ -8,6 +8,7 @@
 from pypy.objspace.flow.model import FunctionGraph, Block, Link
 from pypy.annotation import model as annmodel
 from pypy.translator.typer import LLTyper, LLFunction
+from pypy.translator.genc_repr import R_OBJECT
 
 r_ends_in_underscore_digit = re.compile(r'_\d+$')
 
@@ -54,7 +55,7 @@
                        for attr, s_value in cdef.attrs.items()]
 
         self.pyobj_fields = [  # XXX this should not be necessary
-            fld.name for fld in self.fields if fld.hltype is typeset.R_OBJECT]
+            fld.name for fld in self.fields if fld.hltype == R_OBJECT]
         self.s_instance = annmodel.SomeInstance(self.cdef)
 
     def get_management_functions(self):

Modified: pypy/trunk/src/pypy/translator/genc.py
==============================================================================
--- pypy/trunk/src/pypy/translator/genc.py	(original)
+++ pypy/trunk/src/pypy/translator/genc.py	Thu Sep  9 21:16:06 2004
@@ -10,6 +10,7 @@
 from pypy.translator.classtyper import LLClass
 from pypy.translator.genc_typeset import CTypeSet
 from pypy.translator.genc_op import ERROR_RETVAL
+from pypy.translator.genc_repr import R_INT, R_OBJECT
 
 # ____________________________________________________________
 
@@ -267,9 +268,9 @@
         print >> f, self.C_MEMBERLIST_HEADER % info
         # XXX write member definitions for member with well-known types only
         for fld in llclass.fields:
-            if fld.hltype is self.typeset.R_OBJECT:
+            if fld.hltype == R_OBJECT:
                 t = 'T_OBJECT_EX'
-            elif fld.hltype is self.typeset.R_INT:
+            elif fld.hltype == R_INT:
                 t = 'T_INT'
             else:
                 continue   # ignored

Modified: pypy/trunk/src/pypy/translator/genc_op.py
==============================================================================
--- pypy/trunk/src/pypy/translator/genc_op.py	(original)
+++ pypy/trunk/src/pypy/translator/genc_op.py	Thu Sep  9 21:16:06 2004
@@ -14,6 +14,7 @@
 
 class LoC(LLOp):
     # base class for LLOps that produce C code.
+    cost = 2
 
     def write(self):
         "Default write method, delegating to writestr()."
@@ -53,16 +54,19 @@
     "A standard operation is one defined by a macro in genc.h."
     can_fail = PARAMETER
     llname   = PARAMETER
+    cost     = PARAMETER
     def writestr(self, *args):
         return self.llname + '(' + ', '.join(args) + ')'
 
 class LoKnownAnswer(LoOptimized):
     known_answer = PARAMETER
+    cost         = 0
     def optimize(self, typer):
         return self.known_answer
 
 class LoNewList(LoC):
     can_fail = True
+    cost     = 3
     def writestr(self, *stuff):
         content = stuff[:-2]
         result = stuff[-2]
@@ -76,6 +80,7 @@
 
 class LoCallFunction(LoC):
     can_fail = True
+    cost     = 3
     def writestr(self, func, *stuff):
         args = stuff[:-2]
         result = stuff[-2]
@@ -102,11 +107,11 @@
 class LoConvertTupleItem(LoOptimized):
     source_r = PARAMETER   # tuple-of-hltypes, one per item of the input tuple
     target_r = PARAMETER   # tuple-of-hltypes, one per item of the output tuple
-    index    = PARAMETER   # index of the item to convert
+    cost     = PARAMETER
 
     def optimize(self, typer):
         # replace this complex conversion by the simpler conversion of
-        # only the indexth item
+        # only the items that changed
         llinputs = []
         pos = 0
         for r in self.source_r:
@@ -121,7 +126,7 @@
 
         llrepr = []     # answer
         for i in range(len(self.source_r)):
-            if i == self.index:
+            if self.source_r[i] != self.target_r[i]:
                 # convert this item
                 llrepr += typer.convert(self.source_r[i], llinputs[i],
                                         self.target_r[i], lloutputs[i])
@@ -132,6 +137,7 @@
 
 class LoNewTuple(LoC):
     can_fail = True
+    cost     = 3
 
     def writestr(self, *stuff):
         args   = stuff[:-2]
@@ -145,10 +151,10 @@
         return '\n'.join(ls)
 
 class LoConvertChain(LoOptimized):
-    r_from         = PARAMETER
-    r_middle       = PARAMETER
-    r_to           = PARAMETER
-    convert_length = PARAMETER
+    r_from   = PARAMETER
+    r_middle = PARAMETER
+    r_to     = PARAMETER
+    cost     = PARAMETER
 
     def optimize(self, typer):
         half = len(self.r_from.impl)
@@ -158,17 +164,30 @@
         middle = typer.convert(self.r_from, input, self.r_middle)
         return typer.convert(self.r_middle, middle, self.r_to, output)
 
+class LoDummyResult(LoC):
+    cost = 1
+    def write(self):
+        ls = []
+        for a in self.args:
+            if a.type == 'PyObject*':
+                ls.append('%s = Py_None; Py_INCREF(%s); /* dummy */' % (
+                    a.name, a.name))
+        return '\n'.join(ls)
+
 # ____________________________________________________________
 
 class LoMove(LoC):
+    cost = 1
     def writestr(self, x, y):
         return '%s = %s;' % (y, x)
 
 class LoGoto(LoC):
+    cost = 0
     def write(self):
         return 'goto %s;' % self.errtarget
 
 class LoCopy(LoOptimized):
+    cost = 0
     def optimize(self, typer):
         # the result's llvars is equal to the input's llvars.
         assert len(self.args) % 2 == 0
@@ -177,6 +196,7 @@
 
 class LoDoSomethingWithRef(LoC):
     do_what = PARAMETER
+    cost    = 1
     def write(self):
         ls = []
         for a in self.args:
@@ -189,6 +209,7 @@
 LoXDecref = LoDoSomethingWithRef.With(do_what = 'Py_XDECREF')
 
 class LoComment(LoC):
+    cost = 0
     def write(self):
         s = self.errtarget
         s = s.replace('/*', '/+')
@@ -232,6 +253,7 @@
                 ERROR_CHECK[retvar.type], err))
 
 class LoReturn(LoC):
+    cost = 1
     def write(self):
         if not self.args:
             return 'return 0;'

Added: pypy/trunk/src/pypy/translator/genc_repr.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/translator/genc_repr.py	Thu Sep  9 21:16:06 2004
@@ -0,0 +1,216 @@
+"""
+High-level types and how variables of that type are represented by a
+series of low-level variables.
+"""
+
+import types, __builtin__
+from pypy.translator.typer import CannotConvert, LLConst
+from pypy.translator import genc_op
+
+
+class CRepr:
+    "A possible representation of a flow-graph variable as C-level variables."
+    parse_code = None    # character(s) for PyArg_ParseTuple()
+
+    def __repr__(self):
+        return '%s(%s)' % (self.__class__.__name__, ' + '.join(self.impl))
+
+    def convert_to(self, target, typeset):
+        raise CannotConvert
+
+
+class CAtomicRepr(CRepr):
+    "A simple C type."
+
+    def __init__(self, impl, parse_code=None):
+        self.impl = impl
+        self.parse_code = parse_code
+
+
+class CUndefined(CRepr):
+    "An undefined value. (singleton class)"
+    impl = []
+    
+    def convert_to(self, target, typeset):
+        return genc_op.LoDummyResult
+
+
+class CConstant(CRepr):
+    "A constant value."
+    impl = []
+
+    def __init__(self, value):
+        self.value = value
+
+    def __repr__(self):
+        return 'CConstant(%r)' % (self.value,)
+
+    def convert_to_pyobj(self, initexpr, globalname):
+        # helper for subclasses
+        return genc_op.LoKnownAnswer.With(
+            known_answer = [LLConst('PyObject*', globalname, initexpr,
+                                    to_declare = bool(initexpr))],
+            )
+
+class CConstantInt(CConstant):
+    def convert_to(self, target, typeset):
+        if target == R_INT:
+            # can convert the constant to a C int
+            return genc_op.LoKnownAnswer.With(
+                known_answer = [LLConst('int', '%d' % self.value)],
+                )
+        elif target == R_OBJECT:
+            # can convert the constant to a PyObject*
+            if self.value >= 0:
+                name = 'g_IntObj_%d' % self.value
+            else:
+                name = 'g_IntObj_minus%d' % abs(self.value)
+            return self.convert_to_pyobj(
+                'PyInt_FromLong(%d)' % self.value,
+                name)
+        else:
+            raise CannotConvert
+
+class CConstantStr(CConstant):
+    def convert_to(self, target, typeset):
+        if target == R_OBJECT:
+            # can convert the constant to a PyObject*
+            return self.convert_to_pyobj(
+                'PyString_FromStringAndSize(%s, %d)' % (c_str(self.value),
+                                                        len(self.value)),
+                'g_StrObj_%s' % manglestr(self.value))
+        else:
+            raise CannotConvert
+
+class CConstantNone(CConstant):
+    def convert_to(self, target, typeset):
+        if target == R_OBJECT:
+            # can convert the constant to a PyObject*
+            return self.convert_to_pyobj(None, 'Py_None')
+        else:
+            raise CannotConvert
+
+class CConstantBuiltin(CConstant):   # a constant from the __builtin__ module
+    def convert_to(self, target, typeset):
+        if target == R_OBJECT:
+            # can load the constant as a PyObject* from the builtins
+            fn = self.value
+            if getattr(__builtin__, fn.__name__, None) is not fn:
+                raise CannotConvert   # unless it doesn't appear to be there
+            return self.convert_to_pyobj(
+                'PyMapping_GetItemString(PyEval_GetBuiltins(), %s)' % (
+                    c_str(fn.__name__)),
+                'g_Builtin_%s' % manglestr(fn.__name__))
+        else:
+            raise CannotConvert
+
+
+class CTuple(CRepr):
+    "An unpacked tuple.  Represents all its items independently."
+
+    def __init__(self, items_r):
+        self.items_r = tuple(items_r)
+        self.impl = []
+        for r in items_r:
+            self.impl += r.impl
+
+    def __repr__(self):
+        return 'CTuple(%s)' % ', '.join([repr(r) for r in self.items_r])
+
+    def convert_to(self, target, typeset):
+        if isinstance(target, CTuple):
+            if len(self.items_r) != len(target.items_r):
+                raise CannotConvert
+            # convert each tuple item that needs to be converted
+            cost = 0
+            for r1, r2 in zip(self.items_r, target.items_r):
+                if r1 != r2:
+                    cost += typeset.getconversion(r1, r2).cost
+            return genc_op.LoConvertTupleItem.With(
+                source_r = self.items_r,
+                target_r = target.items_r,
+                cost     = cost,
+                )
+        elif target == R_OBJECT:
+            # to convert a tuple to a PyTupleObject* we first need to
+            # make sure that all items are PyObject*.
+            intermediate_r = (R_OBJECT,) * len(self.items_r)
+            if self.items_r == intermediate_r:
+                return genc_op.LoNewTuple   # already the case
+            else:
+                r_middle = tuple_representation(intermediate_r)
+                cost1 = typeset.getconversion(self, r_middle).cost
+                return genc_op.LoConvertChain.With(
+                    r_from   = self,
+                    r_middle = r_middle,
+                    r_to     = target,
+                    cost     = cost1 + genc_op.LoNewTuple.cost,
+                    )
+        else:
+            raise CannotConvert
+
+# ____________________________________________________________
+#
+# Predefined CReprs and caches for building more
+
+R_VOID      = CAtomicRepr([])
+R_INT       = CAtomicRepr(['int'],       parse_code='i')
+R_OBJECT    = CAtomicRepr(['PyObject*'], parse_code='O')
+R_UNDEFINED = CUndefined()
+
+R_TUPLE_CACHE    = {}
+R_CONSTANT_CACHE = {}
+
+def tuple_representation(items_r):
+    items_r = tuple(items_r)
+    try:
+        return R_TUPLE_CACHE[items_r]
+    except KeyError:
+        rt   = R_TUPLE_CACHE[items_r] = CTuple(items_r)
+        return rt
+
+CONST_TYPES = {
+    int:                       CConstantInt,
+    str:                       CConstantStr,
+    types.NoneType:            CConstantNone,
+    types.BuiltinFunctionType: CConstantBuiltin,
+    }
+
+def constant_representation(value):
+    key = type(value), value   # to avoid mixing for example 0 and 0.0
+    try:
+        return R_CONSTANT_CACHE[key]
+    except KeyError:
+        if isinstance(value, tuple):
+            # tuples have their own representation and
+            # don't need a fully constant representation
+            items_r = [constant_representation(x) for x in value]
+            return tuple_representation(items_r)
+        for cls in type(value).__mro__:
+            if cls in CONST_TYPES:
+                cls = CONST_TYPES[cls]
+                break
+        else:
+            cls = CConstant
+        r = R_CONSTANT_CACHE[key] = cls(value)
+        return r
+
+
+def c_str(s):
+    "Return the C expression for the string 's'."
+    s = repr(s)
+    if s.startswith("'"):
+        s = '"' + s[1:-1].replace('"', r'\"') + '"'
+    return s
+
+def manglestr(s):
+    "Return an identifier name unique for the string 's'."
+    l = []
+    for c in s:
+        if not ('a' <= c <= 'z' or 'A' <= c <= 'Z' or '0' <= c <= '9'):
+            if c == '_':
+                c = '__'
+            else:
+                c = '_%02x' % ord(c)
+        l.append(c)
+    return ''.join(l)

Modified: pypy/trunk/src/pypy/translator/genc_typeset.py
==============================================================================
--- pypy/trunk/src/pypy/translator/genc_typeset.py	(original)
+++ pypy/trunk/src/pypy/translator/genc_typeset.py	Thu Sep  9 21:16:06 2004
@@ -1,30 +1,18 @@
-import re, types, __builtin__
+from __future__ import generators
+import re
 from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant
 from pypy.annotation import model as annmodel
-from pypy.translator.typer import LLConst
 from pypy.translator import genc_op
-
-
-class CRepr:
-    "A possible representation of a flow-graph variable as C-level variables."
-
-    def __init__(self, impl, parse_code=None, name=None):
-        self.impl = impl   # list [(C type, prefix for variable name)]
-        self.parse_code = parse_code  # character(s) for PyArg_ParseTuple()
-        self.name = name or '<C %s>' % ' + '.join(self.impl)
-
-    def __repr__(self):
-        return self.name
+from pypy.translator.typer import CannotConvert
+from pypy.translator.genc_repr import R_VOID, R_INT, R_OBJECT, R_UNDEFINED
+from pypy.translator.genc_repr import tuple_representation
+from pypy.translator.genc_repr import constant_representation, CConstant
+from pypy.translator.genc_repr import c_str, manglestr
 
 
 class CTypeSet:
     "A (small) set of C types that typer.LLFunction can manipulate."
 
-    R_VOID     = CRepr([])
-    R_INT      = CRepr(['int'],       parse_code='i')
-    R_OBJECT   = CRepr(['PyObject*'], parse_code='O')
-    #R_DONTCARE = CRepr([])   # for uninitialized variables
-
     REPR_BY_CODE = {
         'v': R_VOID,
         'i': R_INT,
@@ -45,9 +33,9 @@
     def __init__(self, genc, bindings):
         self.genc = genc
         self.bindings = bindings
-        self.r_constants = {}
-        self.r_tuples = {}
-        self.lloperations = {'convert': {}}
+        self.conversion_cache = {}
+        self.conversion_errors = {}
+        self.lloperations = {'convert': self.conversion_cache}
         self.parse_operation_templates()
 
     # __________ methods required by LLFunction __________
@@ -60,187 +48,130 @@
             var = self.bindings.get(var) or annmodel.SomeObject()
         if isinstance(var, annmodel.SomeObject):
             if var.is_constant():
-                return self.constant_representation(var.const)
+                return constant_representation(var.const)
             if issubclass(var.knowntype, int):
-                return self.R_INT
+                return R_INT
             if isinstance(var, annmodel.SomeImpossibleValue):
-                return self.R_VOID
+                return R_VOID
             if isinstance(var, annmodel.SomeTuple):
                 items_r = [self.gethltype(s_item) for s_item in var.items]
-                return self.tuple_representation(items_r)
+                return tuple_representation(items_r)
             # fall-back
-            return self.R_OBJECT
-        #if isinstance(var, UndefinedConstant):
-        #    return self.R_DONTCARE
+            return R_OBJECT
+        if isinstance(var, UndefinedConstant):
+            return R_UNDEFINED
         if isinstance(var, Constant):
-            return self.constant_representation(var.value)
+            return constant_representation(var.value)
         raise TypeError, var
 
     def represent(self, hltype):
         return hltype.impl
 
+    def getconversion(self, hltype1, hltype2):
+        sig = hltype1, hltype2
+        if sig in self.conversion_errors:
+            raise CannotConvert   # shortcut
+        try:
+            return self.conversion_cache[sig]
+        except KeyError:
+            try:
+                llopcls = hltype1.convert_to(hltype2, self)
+                self.conversion_cache[sig] = llopcls
+                return llopcls
+            except CannotConvert:
+                self.conversion_errors[sig] = True
+                raise
+
     def typingerror(self, opname, hltypes):
-        # build operations with a variable number of arguments on demand
-        if opname == 'OP_NEWLIST':
-            opnewlist = self.lloperations.setdefault('OP_NEWLIST', {})
-            sig = (self.R_OBJECT,) * len(hltypes)
-            if sig in opnewlist:
-                return False
-            opnewlist[sig] = genc_op.LoNewList
-            return True   # retry
-        if opname == 'OP_NEWTUPLE':
-            opnewtuple = self.lloperations.setdefault('OP_NEWTUPLE', {})
-            rt = self.tuple_representation(hltypes[:-1])
-            sig = tuple(hltypes[:-1]) + (rt,)
-            if sig in opnewtuple:
-                return False
-            opnewtuple[sig] = genc_op.LoCopy
-            # Note that we can use LoCopy to virtually build a tuple because
-            # the tuple representation 'rt' is just the collection of all the
-            # representations for the input args.
-            return True   # retry
-        if opname == 'OP_SIMPLE_CALL' and hltypes:
-            opsimplecall = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
-            sig = (self.R_OBJECT,) * len(hltypes)
-            if sig in opsimplecall:
-                return False
-            opsimplecall[sig] = genc_op.LoCallFunction
-            return True   # retry
-        return False
+        # build operations on demand, e.g. if they take a variable number
+        # of arguments or depend on a constant value (e.g. the class being
+        # instantiated).
+        try:
+            builder = getattr(self, 'build_' + opname)
+        except AttributeError:
+            return False
+
+        progress = False
+        for newsig, newop in builder(hltypes):
+            llops = self.lloperations.setdefault(opname, {})
+            if newsig not in llops:
+                llops[newsig] = newop
+                progress = True
+        return progress
 
     # ____________________________________________________________
 
-    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:
-            if isinstance(value, tuple):
-                # tuples have their own representation and
-                # don't need a fully constant representation
-                items_r = [self.constant_representation(x) for x in value]
-                return self.tuple_representation(items_r)
-            # a constant doesn't need any C variable to be encoded
-            r = self.r_constants[key] = CRepr([], name='<const %r>' % (value,))
-            r.const = value
-
-            # 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
-            if isinstance(value, int):
-                # can convert the constant to a PyObject*
-                if value >= 0:
-                    name = 'g_IntObj_%d' % value
-                else:
-                    name = 'g_IntObj_minus%d' % abs(value)
-                self.can_convert_to_pyobj(r, 'PyInt_FromLong(%d)' % value,
-                                          name)
-                # can convert the constant to a C int
-                self.register_conv(r, self.R_INT, genc_op.LoKnownAnswer.With(
-                    known_answer = [LLConst(self.R_INT, '%d' % value)],
-                    ))
-            elif isinstance(value, str):
-                # can convert the constant to a PyObject*
-                self.can_convert_to_pyobj(r,
-                    'PyString_FromStringAndSize(%s, %d)' % (c_str(value),
-                                                            len(value)),
-                    'g_StrObj_%s' % manglestr(value))
-            elif value is None:
-                # can convert the constant to Py_None
-                self.can_convert_to_pyobj(r, None, 'Py_None')
-            elif callable(value) and value in self.genc.llfunctions:
-                # another Python function: can be called with OP_SIMPLE_CALL
-                llfunc = self.genc.llfunctions[value]
-                ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
+    def build_OP_NEWLIST(self, hltypes):
+        # LoNewList can build a list of any length from PyObject* args.
+        sig = (R_OBJECT,) * len(hltypes)
+        yield sig, genc_op.LoNewList
+
+    def build_OP_NEWTUPLE(self, hltypes):
+        # We can use LoCopy to virtually build a tuple because
+        # the tuple representation 'rt' is just the collection of all the
+        # representations for the input args.
+        rt = tuple_representation(hltypes[:-1])
+        sig = tuple(hltypes[:-1]) + (rt,)
+        yield sig, genc_op.LoCopy
+
+    def build_OP_SIMPLE_CALL(self, hltypes):
+        if not hltypes:
+            return
+        # We can call the function using PyObject_CallFunction(), if
+        # it can be converted to PyObject*.
+        sig = (R_OBJECT,) * len(hltypes)
+        yield sig, genc_op.LoCallFunction
+        # But not all variables holding pointers to function can nicely
+        # be converted to PyObject*.  We might be calling a well-known
+        # (constant) function:
+        r = hltypes[0]
+        if isinstance(r, CConstant):
+            fn = r.value
+            if not callable(fn):
+                return
+            # Calling another user-defined generated function
+            if fn in self.genc.llfunctions:
+                llfunc = self.genc.llfunctions[fn]
                 sig = [r]
                 for v in llfunc.graph.getargs():
                     sig.append(self.gethltype(v))
                 hltype = self.gethltype(llfunc.graph.getreturnvar())
                 sig.append(hltype)
-                ops[tuple(sig)] = genc_op.LoCallPyFunction.With(
+                yield tuple(sig), genc_op.LoCallPyFunction.With(
                     llfunc = llfunc,
                     hlrettype = hltype,
                     )
-            elif (isinstance(value, types.BuiltinFunctionType) and
-                  value is getattr(__builtin__, value.__name__, None)):
-                # a function from __builtin__: can convert to PyObject*
-                self.can_convert_to_pyobj(r,
-                    'PyMapping_GetItemString(PyEval_GetBuiltins(), %s)' % (
-                    c_str(value.__name__)),
-                    'g_Builtin_%s' % manglestr(value.__name__))
-                # if the function is defined in genc.h, import its definition
-                # by copying the operation CALL_xxx to OP_SIMPLE_CALL with
-                # a first argument which is the constant function xxx.
-                opname = 'CALL_' + value.__name__
-                if opname in self.lloperations:
-                    ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
-                    for sig, llopcls in self.lloperations[opname].items():
-                        sig = (r,) + sig
-                        ops[sig] = llopcls
-            elif (isinstance(value, (type, types.ClassType)) and
-                  value in self.genc.llclasses):
-                # a user-defined class
-                ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
+            # Instantiating a user-defined class
+            if fn in self.genc.llclasses:
                 # XXX do __init__
-                sig = (r, self.R_OBJECT)
-                ops[sig] = genc_op.LoInstantiate.With(
-                    llclass = self.genc.llclasses[value],
+                sig = (r, R_OBJECT)   # R_OBJECT is the result of the op
+                yield sig, genc_op.LoInstantiate.With(
+                    llclass = self.genc.llclasses[fn],
                     )
-                # OP_ALLOC_INSTANCE used by the constructor function xxx_new()
-                ops = self.lloperations.setdefault('OP_ALLOC_INSTANCE', {})
-                sig = (r, self.R_OBJECT)
-                ops[sig] = genc_op.LoAllocInstance.With(
-                    llclass = self.genc.llclasses[value],
+            # Calling a built-in defined in genc.h, if we have a macro
+            # CALL_funcname()
+            opname = 'CALL_' + getattr(fn, '__name__', '?')
+            if opname in self.lloperations:
+                for sig, llopcls in self.lloperations[opname].items():
+                    sig = (r,) + sig
+                    yield sig, llopcls
+
+    def build_OP_ALLOC_INSTANCE(self, hltypes):
+        # OP_ALLOC_INSTANCE is used by the constructor functions xxx_new()
+        if not hltypes:
+            return
+        r = hltypes[0]
+        if isinstance(r, CConstant):
+            fn = r.value
+            if not callable(fn):
+                return
+            if fn in self.genc.llclasses:
+                sig = (r, R_OBJECT)   # R_OBJECT is the result
+                yield sig, genc_op.LoAllocInstance.With(
+                    llclass = self.genc.llclasses[fn],
                     )
-            else:
-                print "// XXX not implemented: constant", key
-            return r
-
-    def can_convert_to_pyobj(self, r, initexpr, globalname):
-        self.register_conv(r, self.R_OBJECT, genc_op.LoKnownAnswer.With(
-            known_answer = [LLConst('PyObject*', globalname, initexpr,
-                                    to_declare = bool(initexpr))],
-            ))
-
-    def tuple_representation(self, items_r):
-        # a tuple is implemented by several C variables or fields
-        # instead of a single struct at the C level.
-        items_r = tuple(items_r)
-        try:
-            return self.r_tuples[items_r]
-        except KeyError:
-            impl = []
-            for r in items_r:
-                impl += r.impl
-            name = '<(%s)>' % ', '.join([str(r) for r in items_r])
-            rt = CRepr(impl, name=name)
-            self.r_tuples[items_r] = rt
-
-            # we can convert any item in the tuple to obtain another tuple
-            # representation.
-            conv = self.lloperations['convert']
-            for i in range(len(items_r)):
-                r = items_r[i]
-                for r_from, r_to in conv.keys():
-                    if r_from == r:
-                        target_r = list(items_r)
-                        target_r[i] = r_to
-                        rt_2 = self.tuple_representation(target_r)
-                        self.register_conv(rt, rt_2,
-                                           genc_op.LoConvertTupleItem.With(
-                            source_r = items_r,
-                            target_r = target_r,
-                            index    = i,
-                            ))
-
-            # a tuple containing only PyObject* can easily be converted to
-            # a PyTupleObject.  (For other kinds of tuple the conversion is
-            # indirect: all items can probably be converted, one by one, to
-            # PyObject*, and the conversions will be chained automatically.)
-            if items_r == (self.R_OBJECT,) * len(items_r):
-                self.register_conv(rt, self.R_OBJECT, genc_op.LoNewTuple)
 
-            return rt
+    # ____________________________________________________________
 
     def parse_operation_templates(self):
         # parse the genc.h header to figure out which macros are implemented
@@ -261,63 +192,5 @@
         ops.setdefault(sig, genc_op.LoStandardOperation.With(
             can_fail = can_fail,
             llname   = llname,
+            cost     = 1 + typecodes.count('o'),   # rough cost estimate
             ))
-
-    def register_conv(self, r_from, r_to, convopcls):
-        conv = self.lloperations['convert']
-        if r_from == r_to:
-            return
-        prevconvopcls = conv.get((r_from, r_to))
-        if prevconvopcls is not None:
-            if convert_length(prevconvopcls) > convert_length(convopcls):
-                # only replace a conversion with another if the previous one
-                # was a longer chain of conversions
-                del conv[r_from, r_to]
-            else:
-                return
-        #print 'conv: %s\t->\t%s' % (r_from, r_to)
-        convitems = conv.items()   # not iteritems()!
-        conv[r_from, r_to] = convopcls
-        # chain the conversion with any other possible conversion
-        for (r_from_2, r_to_2), convopcls_2 in convitems:
-            if r_to == r_from_2:
-                self.register_conv(r_from, r_to_2, genc_op.LoConvertChain.With(
-                    r_from         = r_from,
-                    r_middle       = r_to,
-                    r_to           = r_to_2,
-                    convert_length = convert_length(convopcls) +
-                                     convert_length(convopcls_2),
-                    ))
-            if r_to_2 == r_from:
-                self.register_conv(r_from_2, r_to, genc_op.LoConvertChain.With(
-                    r_from         = r_from_2,
-                    r_middle       = r_to_2,
-                    r_to           = r_to,
-                    convert_length = convert_length(convopcls_2) +
-                                     convert_length(convopcls),
-                    ))
-
-def convert_length(convopcls):
-    if issubclass(convopcls, genc_op.LoConvertChain):
-        return convopcls.convert_length
-    else:
-        return 1
-
-def c_str(s):
-    "Return the C expression for the string 's'."
-    s = repr(s)
-    if s.startswith("'"):
-        s = '"' + s[1:-1].replace('"', r'\"') + '"'
-    return s
-
-def manglestr(s):
-    "Return an identifier name unique for the string 's'."
-    l = []
-    for c in s:
-        if not ('a' <= c <= 'z' or 'A' <= c <= 'Z' or '0' <= c <= '9'):
-            if c == '_':
-                c = '__'
-            else:
-                c = '_%02x' % ord(c)
-        l.append(c)
-    return ''.join(l)

Modified: pypy/trunk/src/pypy/translator/typer.py
==============================================================================
--- pypy/trunk/src/pypy/translator/typer.py	(original)
+++ pypy/trunk/src/pypy/translator/typer.py	Thu Sep  9 21:16:06 2004
@@ -56,13 +56,12 @@
 #       ...}
 #       This dict contains the known signatures of each space operation.
 #       Special opnames:
-#         'convert' v w : convert some v to w
-#         'caseXXX'   v : fails (i.e. jump to errlabel) if v is not XXX
+#         'caseXXX'    v : fails (i.e. jump to errlabel) if v is not XXX
 #
 #   rawoperations = {
 #       'opname': subclass-of-LLOp,
 #       ...}
-#       Low-level-only operations on raw LLVars (as opposed to llopeerations,
+#       Low-level-only operations on raw LLVars (as opposed to lloperations,
 #       which are on flow.model.Variables as found in SpaceOperations):
 #         'goto'          : fails unconditionally (i.e. jump to errlabel)
 #         'move'    x y   : raw copy of the LLVar x to the LLVar y
@@ -73,6 +72,11 @@
 #         'comment'       : comment (text is in errtarget)
 #         'return'  x y...: return the value stored in the LLVars
 #
+#   def getconvertion(self, hltype1, hltype2):
+#       If it is possible to convert from 'hltype1' to 'hltype2', this
+#       function should return the conversion operation (as a subclass of
+#       LLOp).  Otherwise, it should raise CannotConvert.
+#
 #   def typingerror(self, opname, hltypes):
 #       Called when no match is found in lloperations.  This function must
 #       either extend lloperations and return True to retry, or return
@@ -88,6 +92,7 @@
         self.represent    = typeset.represent
         self.lloperations = typeset.lloperations
         self.rawoperations= typeset.rawoperations
+        self.getconversion= typeset.getconversion
         self.typingerror  = typeset.typingerror
         self.hltypes = {}
         self.llreprs = {}
@@ -210,48 +215,54 @@
         # 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)
+        directions = [False] * 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
+            directions.append(True)
+        # look for an exact match first
         llsigs = self.lloperations.get(opname, {})
-        for sig in variants(tuple(args_t), directions):
-            if sig in llsigs:
-                llopcls = llsigs[sig]
-                break
+        sig = tuple(args_t)
+        if sig in llsigs:
+            llopcls = llsigs[sig]
         else:
-            retry = self.typingerror(opname, tuple(args_t))
-            # if 'typingerror' did not raise an exception, try again.
-            # infinite recursion here means that 'typingerror' did not
-            # correctly extend 'lloperations'.
-            if retry:
+            # enumerate the existing operation signatures and their costs
+            choices = []
+            for sig, llopcls in llsigs.items():
+                if len(sig) != len(args_t):
+                    continue   # wrong number of arguments
                 try:
-                    self.operation(opname, args, result, errlabel)
-                    return
-                except RuntimeError:   # infinite recursion
-                    pass
-            raise TypingError([opname] + args_t)
+                    cost = llopcls.cost
+                    for hltype1, hltype2, reverse in zip(args_t, sig,
+                                                         directions):
+                        if hltype1 != hltype2:
+                            if reverse:
+                                hltype1, hltype2 = hltype2, hltype1
+                            convop = self.getconversion(hltype1, hltype2)
+                            cost += convop.cost
+                    choices.append((cost, sig, llopcls))
+                except CannotConvert:
+                    continue   # non-matching signature
+            if not choices:
+                retry = self.typingerror(opname, tuple(args_t))
+                # if 'typingerror' did not raise an exception, try again.
+                # infinite recursion here means that 'typingerror' did not
+                # correctly extend 'lloperations'.
+                if retry:
+                    try:
+                        self.operation(opname, args, result, errlabel)
+                        return
+                    except RuntimeError:   # infinite recursion
+                        pass
+                raise TypingError([opname] + args_t)
+            cost, sig, llopcls = min(choices)
         # convert input args to temporary variables
         llargs = []
         for v, v_t, s_t in zip(args, args_t, sig):
@@ -259,59 +270,66 @@
                 llargs += self.convert(v_t, self.llreprs[v], s_t)
             else:
                 llargs += self.llreprs[v]
-        # generate an error label if the operation can fail
-        if llopcls.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
-                tmp = result
+                if self.writeoperation(llopcls, llargs,
+                                       self.llreprs[result], errlabel):
+                    self.mark_release(result)
             else:
                 # the result has to be converted
                 tmp = Variable()
                 self.makevar(tmp, hltype=sig[-1])
-            llargs += self.llreprs[tmp]
-            llop = llopcls(llargs, errlabel)
-            constantllreprs = llop.optimize(self)
-            if constantllreprs is not None:
-                # the result is a constant: patch the llrepr of result,
-                # i.e. replace its LLVars with the given constants which
-                # will be used by the following operations.
-                assert len(constantllreprs) == len(self.llreprs[tmp])
-                diffs = []
-                interesting = False
-                for x, y in zip(constantllreprs, self.llreprs[tmp]):
-                    if x != y:
-                        diffs.append('%s = %s;' % (y.name, x.name))
-                        interesting = interesting or not isinstance(x, LLConst)
-                self.llreprs[tmp] = list(constantllreprs)
-                if interesting:
-                    llcomment = self.rawoperations['comment']
-                    self.blockops.append(llcomment([], '%s: %s' % (
-                        opname, ' '.join(diffs))))
-            else:
-                # common case: emit the LLOp.
-                self.mark_release(tmp)
-                self.blockops.append(llop)
-            if tmp is not result:
-                self.operation('convert', [tmp], result)
+                if self.writeoperation(llopcls, llargs,
+                                       self.llreprs[tmp], errlabel):
+                    self.mark_release(tmp)
+                self.convert_variable(tmp, result)
         else:
             # no result variable
-            self.blockops.append(llopcls(llargs, errlabel))
+            self.writeoperation(llopcls, llargs, [], errlabel)
+
+    def writeoperation(self, llopcls, llargs, llresult, errlabel=None):
+        # generate an error label if the operation can fail
+        if llopcls.can_fail and errlabel is None:
+            errlabel = self.to_release.getlabel()
+        # create the LLOp instance
+        llop = llopcls(llargs + llresult, errlabel)
+        constantllreprs = llop.optimize(self)
+        if constantllreprs is not None:
+            # the result is a constant: patch llresult, i.e. replace its
+            # LLVars with the given constants which will be used by the
+            # following operations.
+            assert len(constantllreprs) == len(llresult)
+            llresult[:] = constantllreprs
+            return False   # operation skipped
+        else:
+            # common case: emit the LLOp.
+            self.blockops.append(llop)
+            return True
 
     def convert(self, inputtype, inputrepr, outputtype, outputrepr=None):
-        tmpin = Variable()
-        self.makevar(tmpin, hltype=inputtype)
-        tmpout = Variable()
-        self.makevar(tmpout, hltype=outputtype)
-        self.llreprs[tmpin] = inputrepr
+        convop = self.getconversion(inputtype, outputtype)
         if outputrepr is None:
-            outputrepr = self.llreprs[tmpout]
+            tmp = Variable()
+            self.makevar(tmp, hltype=outputtype)
+            outputrepr = self.llreprs[tmp]
+            if self.writeoperation(convop, inputrepr, outputrepr):
+                self.mark_release(tmp)
         else:
-            self.llreprs[tmpout] = outputrepr
-        self.operation('convert', [tmpin], tmpout)
-        return self.llreprs[tmpout]
+            if self.writeoperation(convop, inputrepr, outputrepr):
+                tmp = Variable()
+                self.hltypes[tmp] = outputtype
+                self.llreprs[tmp] = outputrepr
+                self.mark_release(tmp)
+        return outputrepr
+
+    def convert_variable(self, v1, v2):
+        self.makevar(v1)
+        self.makevar(v2)
+        convop = self.getconversion(self.hltypes[v1], self.hltypes[v2])
+        if self.writeoperation(convop, self.llreprs[v1], self.llreprs[v2]):
+            self.mark_release(v2)
 
     def goto(self, exit):
         # generate the exit.args -> target.inputargs copying operations
@@ -324,7 +342,7 @@
                 if self.hltypes[v] != self.hltypes[w]:
                     tmp = Variable()
                     self.makevar(tmp, hltype=self.hltypes[w])
-                    self.operation('convert', [v], tmp)
+                    self.convert_variable(v, tmp)
                     v = tmp
                 exitargs.append(v)
             # move the data from exit.args to target.inputargs
@@ -423,16 +441,5 @@
         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 ()
+class CannotConvert:
+    pass



More information about the Pypy-commit mailing list