From arigo at codespeak.net Thu Sep 2 15:32:48 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Thu, 2 Sep 2004 15:32:48 +0200 (MEST) Subject: [pypy-svn] r6267 - pypy/branch/pypy-genc/translator Message-ID: <20040902133248.4B0D25A10D@thoth.codespeak.net> Author: arigo Date: Thu Sep 2 15:32:47 2004 New Revision: 6267 Modified: pypy/branch/pypy-genc/translator/genc.py pypy/branch/pypy-genc/translator/typer.py Log: In-line constants by allowing convertion operations to provide a known answer. Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Thu Sep 2 15:32:47 2004 @@ -5,7 +5,7 @@ import autopath, os, re from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant from pypy.objspace.flow.model import Block, Link, traverse -from pypy.translator.typer import LLFunction, LLOp +from pypy.translator.typer import LLFunction, LLOp, LLConst from pypy.annotation import model as annmodel @@ -45,6 +45,7 @@ self.genc = genc self.bindings = bindings self.r_constants = {} + self.globaldefs = [] self.lloperations = {'convert': {}, 'release': {}} self.parse_operation_templates() @@ -88,6 +89,15 @@ return # retry raise TypingError((opname,) + hltypes) + def knownanswer(self, llname): + if hasattr(llname, 'known_answer'): + if hasattr(llname, 'globaldef'): # global definition required + self.consts_used.append(llname.globaldef) + del llname.globaldef + return llname.known_answer + else: + return None + # ____________________________________________________________ def constant_representation(self, value): @@ -114,6 +124,7 @@ # can convert the constant to a C int def writer(z): return '%s = %d;' % (z, value) + writer.known_answer = [LLConst(self.R_INT, '%d' % value)] conv[r, self.R_INT] = writer, False # can convert the constant to a PyObject* def writer(z, err): @@ -310,7 +321,8 @@ seen[a] = True for a in lllocals: if a not in seen: - print >> f, '\t%s %s;' % (a.type, a.name) + if not isinstance(a, LLConst): + print >> f, '\t%s %s;' % (a.type, a.name) seen[a] = True print >> f Modified: pypy/branch/pypy-genc/translator/typer.py ============================================================================== --- pypy/branch/pypy-genc/translator/typer.py (original) +++ pypy/branch/pypy-genc/translator/typer.py Thu Sep 2 15:32:47 2004 @@ -13,6 +13,9 @@ self.type = type # low-level type in any format, e.g. a C type name self.name = name +class LLConst(LLVar): + "An LLVar whose name is abused to be a constant expression." + class LLOp: "A low-level operation." def __init__(self, name, args, errtarget=None): @@ -53,6 +56,10 @@ # Called when no match is found in lloperations. This function must # either extend lloperations and return (to retry), or raise an # exception (to stop). +# +# def knownanswer(self, llname): +# Optionally returns a list of LLVars that give the well-known, constant +# answer to the low-level operation name 'llname'; else return None. # ____________________________________________________________ @@ -67,6 +74,7 @@ self.represent = typeset.represent self.lloperations = typeset.lloperations self.typingerror = typeset.typingerror + self.knownanswer = typeset.knownanswer self.hltypes = {} self.llreprs = {} @@ -207,7 +215,8 @@ if to == hltype: yield frm - def operation(self, opname, args, result=None, errlabel=None): + def operation(self, opname, args, result=None, errlabel=None, + resulttype=None): "Helper to build the LLOps for a single high-level operation." # get the hltypes of the input arguments for v in args: @@ -216,7 +225,7 @@ directions = [self.convert_from] * len(args) # append the hltype of the result if result: - self.makevar(result) + self.makevar(result, resulttype) args_t.append(self.hltypes[result]) directions.append(self.convert_to) # enumerate possible signatures until we get a match @@ -230,15 +239,21 @@ # if 'typingerror' did not raise an exception, try again. # infinite recursion here means that 'typingerror' did not # correctly extend 'lloperations'. - self.operation(opname, args, result, errlabel) + self.operation(opname, args, result, errlabel, resulttype) return + # check if the answer has an existing well-known answer + if resulttype is not None: # if we are allowed to patch self.llreprs + llrepr = self.knownanswer(llname) + if llrepr is not None: + # patch self.llreprs and we're done + self.llreprs[result] = llrepr + return # 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) + self.operation('convert', [v], tmp, resulttype=s_t) v = tmp llargs += self.llreprs[v] # generate an error label if the operation can fail From arigo at codespeak.net Thu Sep 2 16:05:35 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Thu, 2 Sep 2004 16:05:35 +0200 (MEST) Subject: [pypy-svn] r6268 - pypy/branch/pypy-genc/translator Message-ID: <20040902140535.E44FF5A10D@thoth.codespeak.net> Author: arigo Date: Thu Sep 2 16:05:35 2004 New Revision: 6268 Modified: pypy/branch/pypy-genc/translator/genc.py Log: Precomputing constant PyIntObjects. Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Thu Sep 2 16:05:35 2004 @@ -45,7 +45,8 @@ self.genc = genc self.bindings = bindings self.r_constants = {} - self.globaldefs = [] + self.globaldefs_decl = [] + self.globaldefs_impl = [] self.lloperations = {'convert': {}, 'release': {}} self.parse_operation_templates() @@ -91,9 +92,12 @@ def knownanswer(self, llname): if hasattr(llname, 'known_answer'): - if hasattr(llname, 'globaldef'): # global definition required - self.consts_used.append(llname.globaldef) - del llname.globaldef + if hasattr(llname, 'globaldefs_decl'): # global definition required + self.globaldefs_decl.extend(llname.globaldefs_decl) + del llname.globaldefs_decl + if hasattr(llname, 'globaldefs_impl'): # global definition required + self.globaldefs_impl.extend(llname.globaldefs_impl) + del llname.globaldefs_impl return llname.known_answer else: return None @@ -124,12 +128,17 @@ # can convert the constant to a C int def writer(z): return '%s = %d;' % (z, value) - writer.known_answer = [LLConst(self.R_INT, '%d' % value)] conv[r, self.R_INT] = writer, False + writer.known_answer = [LLConst(self.R_INT, '%d' % value)] # can convert the constant to a PyObject* def writer(z, err): return 'convert_io(%d, %s, %s)' % (value, z, err) conv[r, self.R_OBJECT] = writer, True + llconst = LLConst('PyObject*', 'g_IntObject_%d' % value) + writer.known_answer = [llconst] + writer.globaldefs_decl = [llconst] + writer.globaldefs_impl = ['%s = PyInt_FromLong(%d);' % + (llconst.name, value)] elif isinstance(value, str): # can convert the constant to a PyObject* def writer(z, err): @@ -255,7 +264,9 @@ # footer print >> f, C_METHOD_TABLE % info - print >> f, C_FOOTER % info + print >> f, C_INIT_HEADER % info + self.gen_globaldefs() + print >> f, C_INIT_FOOTER % info def build_llfunctions(self): @@ -303,13 +314,26 @@ f = self.f llfunc = self.llfunctions[func] + # generate the body of the function + body = list(llfunc.ll_body()) + + # print the declaration of the new global constants needed by + # the current function + if self.typeset.globaldefs_decl: + if len(self.typeset.globaldefs_decl) > 1: + s = 's' + else: + s = '' + print >> f, '/* global constant%s */' % s + for llconst in self.typeset.globaldefs_decl: + print >> f, 'static %s %s;' % (llconst.type, llconst.name) + del self.typeset.globaldefs_decl[:] + print >> f + # print header print >> f, self.cfunction_header(func) print >> f, '{' - # generate the body of the function - body = list(llfunc.ll_body()) - # collect and print all the local variables from the body llargs, rettype = llfunc.ll_header() lllocals = llargs[:] @@ -348,6 +372,13 @@ print >> f print >> f, '}' + def gen_globaldefs(self): + # generate the global definitions + f = self.f + for code in self.typeset.globaldefs_impl: + for codeline in code.split('\n'): + print >> f, '\t' + codeline + LL_ONLY_OPERATIONS = { 'move': lambda x,y: '%s = %s;' % (y,x), @@ -372,10 +403,11 @@ \t{NULL, NULL} };''' -C_FOOTER = ''' +C_INIT_HEADER = ''' void init%(modname)s(void) { -\tPy_InitModule("%(modname)s", %(modname)sMethods); -}''' +\tPy_InitModule("%(modname)s", %(modname)sMethods);''' + +C_INIT_FOOTER = '''}''' # ____________________________________________________________ From arigo at codespeak.net Thu Sep 2 17:43:22 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Thu, 2 Sep 2004 17:43:22 +0200 (MEST) Subject: [pypy-svn] r6270 - pypy/branch/pypy-genc/translator Message-ID: <20040902154322.E23E85A10D@thoth.codespeak.net> Author: arigo Date: Thu Sep 2 17:43:22 2004 New Revision: 6270 Modified: pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/genc.py pypy/branch/pypy-genc/translator/typer.py Log: - Support for constant string objects. - Factored bits of constant_representation() to can_convert_to_pyobj(). It's still quite big, though. - Support for built-in functions in genc.py. - New macro syntax CALL_funcname_typecodes in genc.h for typed built-in calls. - In typer.py, replaced an infinite recursion with an error message (always a good idea). Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Thu Sep 2 17:43:22 2004 @@ -91,5 +91,11 @@ } +/* a few built-in functions */ + +#define CALL_len_oi(o,r,err) if ((r=PyObject_Size(o))<0) goto err; +#define CALL_pow_iii(x,y,r) { int i=y; r=1; while (--i>=0) r*=x; } /*slow*/ + + /************************************************************/ /*** The rest is produced by genc.py ***/ Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Thu Sep 2 17:43:22 2004 @@ -2,10 +2,10 @@ Generate a C source file from the flowmodel. """ -import autopath, os, re +import autopath, os, re, types, __builtin__ from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant from pypy.objspace.flow.model import Block, Link, traverse -from pypy.translator.typer import LLFunction, LLOp, LLConst +from pypy.translator.typer import LLFunction, LLOp, LLConst, TypingError from pypy.annotation import model as annmodel @@ -24,10 +24,6 @@ return '' % ' + '.join(self.impl) -class TypingError(Exception): - pass - - class CTypeSet: "A (small) set of C types that typer.LLFunction can manipulate." @@ -72,10 +68,12 @@ return hltype.impl def typingerror(self, opname, hltypes): - # build operations with a variable number of argument on demand + # 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 def writer(*stuff): content = stuff[:-2] result = stuff[-2] @@ -87,8 +85,23 @@ result, i, content[i], content[i])) return '\n'.join(ls) opnewlist[sig] = writer, True - return # retry - raise TypingError((opname,) + hltypes) + 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 + def writer(func, *stuff): + args = stuff[:-2] + result = stuff[-2] + err = stuff[-1] + format = 'O' * len(args) + return ('if (!(%s = PyObject_CallFunction(%s, "%s", %s)))' + ' goto %s;' % (result, func, format, + ', '.join(args), err)) + opsimplecall[sig] = writer, True + return True # retry + return False def knownanswer(self, llname): if hasattr(llname, 'known_answer'): @@ -131,25 +144,17 @@ conv[r, self.R_INT] = writer, False writer.known_answer = [LLConst(self.R_INT, '%d' % value)] # can convert the constant to a PyObject* - def writer(z, err): - return 'convert_io(%d, %s, %s)' % (value, z, err) - conv[r, self.R_OBJECT] = writer, True - llconst = LLConst('PyObject*', 'g_IntObject_%d' % value) - writer.known_answer = [llconst] - writer.globaldefs_decl = [llconst] - writer.globaldefs_impl = ['%s = PyInt_FromLong(%d);' % - (llconst.name, value)] + self.can_convert_to_pyobj(r, 'PyInt_FromLong(%d)' % value, + 'g_IntObj_%d' % value) elif isinstance(value, str): # can convert the constant to a PyObject* - def writer(z, err): - return 'convert_so(%s, %d, %s, %s)' % ( - (c_str(value), len(value), z, err)) - conv[r, self.R_OBJECT] = writer, True + 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 - def writer(z): - return 'convert_vo(%s)' % (z,) - conv[r, self.R_OBJECT] = writer, False + self.can_convert_to_pyobj(r, '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] @@ -176,10 +181,42 @@ else: XXX("to do") ops[tuple(sig)] = writer, True + 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, ll in self.lloperations[opname].items(): + sig = (r,) + sig + ops[sig] = ll else: print "// XXX not implemented: constant", key return r + def can_convert_to_pyobj(self, r, initexpr, globalname=None): + conv = self.lloperations['convert'] + if globalname is not None: + def writer(z, err): + return 'if (!(%s = %s)) goto %s;' % (z, initexpr, err) + conv[r, self.R_OBJECT] = writer, True + llconst = LLConst('PyObject*', globalname) + writer.globaldefs_decl = [llconst] + writer.globaldefs_impl = ['%s = %s;' % (llconst.name, initexpr)] + else: + def writer(z): + return '%s = %s; Py_INCREF(%s);' % (z, initexpr, z) + conv[r, self.R_OBJECT] = writer, False + llconst = LLConst('PyObject*', initexpr) + writer.known_answer = [llconst] + def parse_operation_templates(self): # parse the genc.h header to figure out which macros are implemented codes = ''.join(self.REPR_BY_CODE.keys()) @@ -209,6 +246,18 @@ 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/branch/pypy-genc/translator/typer.py ============================================================================== --- pypy/branch/pypy-genc/translator/typer.py (original) +++ pypy/branch/pypy-genc/translator/typer.py Thu Sep 2 17:43:22 2004 @@ -23,6 +23,9 @@ self.args = args # list of LLVars self.errtarget = errtarget # label to jump to in case of error +class TypingError(Exception): + pass + # ____________________________________________________________ # @@ -54,8 +57,8 @@ # # def typingerror(self, opname, hltypes): # Called when no match is found in lloperations. This function must -# either extend lloperations and return (to retry), or raise an -# exception (to stop). +# either extend lloperations and return True to retry, or return +# False to fail. # # def knownanswer(self, llname): # Optionally returns a list of LLVars that give the well-known, constant @@ -235,12 +238,17 @@ llname, can_fail = llsigs[sig] break else: - self.typingerror(opname, tuple(args_t)) + 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'. - self.operation(opname, args, result, errlabel, resulttype) - return + if retry: + try: + self.operation(opname, args, result, errlabel, resulttype) + return + except RuntimeError: # infinite recursion + pass + raise TypingError([opname] + args_t) # check if the answer has an existing well-known answer if resulttype is not None: # if we are allowed to patch self.llreprs llrepr = self.knownanswer(llname) From arigo at codespeak.net Thu Sep 2 17:49:03 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Thu, 2 Sep 2004 17:49:03 +0200 (MEST) Subject: [pypy-svn] r6271 - pypy/branch/pypy-genc/translator Message-ID: <20040902154903.01B4F5A10D@thoth.codespeak.net> Author: arigo Date: Thu Sep 2 17:49:03 2004 New Revision: 6271 Modified: pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/genc.py Log: - OP_GETATTR_ooo(). - Bug fix in generating PyObject_CallFunction(). Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Thu Sep 2 17:49:03 2004 @@ -45,6 +45,7 @@ #define OP_GETITEM_ooo(x,y,r,err) if (!(r=PyObject_GetItem(x,y))) goto err; #define OP_SETITEM_ooov(x,y,z,err) if ((PyObject_SetItem(x,y,z))<0) goto err; +#define OP_GETATTR_ooo(x,y,r,err) if (!(r=PyObject_GetAttr(x,y))) goto err; /*** conversions ***/ Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Thu Sep 2 17:49:03 2004 @@ -95,10 +95,10 @@ args = stuff[:-2] result = stuff[-2] err = stuff[-1] - format = 'O' * len(args) - return ('if (!(%s = PyObject_CallFunction(%s, "%s", %s)))' - ' goto %s;' % (result, func, format, - ', '.join(args), err)) + format = '"' + 'O' * len(args) + '"' + args = (func, format) + args + return ('if (!(%s = PyObject_CallFunction(%s)))' + ' goto %s;' % (result, ', '.join(args), err)) opsimplecall[sig] = writer, True return True # retry return False From arigo at codespeak.net Thu Sep 2 17:53:01 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Thu, 2 Sep 2004 17:53:01 +0200 (MEST) Subject: [pypy-svn] r6272 - pypy/branch/pypy-genc/translator Message-ID: <20040902155301.D8C5A5A10D@thoth.codespeak.net> Author: arigo Date: Thu Sep 2 17:53:01 2004 New Revision: 6272 Modified: pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/genc.py Log: - OP_NEWSLICE_oooo(). - Bug fix: "g_IntObj_-1" is not a valid C identifier. Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Thu Sep 2 17:53:01 2004 @@ -46,6 +46,7 @@ #define OP_GETITEM_ooo(x,y,r,err) if (!(r=PyObject_GetItem(x,y))) goto err; #define OP_SETITEM_ooov(x,y,z,err) if ((PyObject_SetItem(x,y,z))<0) goto err; #define OP_GETATTR_ooo(x,y,r,err) if (!(r=PyObject_GetAttr(x,y))) goto err; +#define OP_NEWSLICE_oooo(x,y,z,r,err) if (!(r=PySlice_New(x,y,z))) goto err; /*** conversions ***/ Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Thu Sep 2 17:53:01 2004 @@ -144,8 +144,12 @@ conv[r, self.R_INT] = writer, False writer.known_answer = [LLConst(self.R_INT, '%d' % value)] # 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, - 'g_IntObj_%d' % value) + name) elif isinstance(value, str): # can convert the constant to a PyObject* self.can_convert_to_pyobj(r, From arigo at codespeak.net Mon Sep 6 14:22:31 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 6 Sep 2004 14:22:31 +0200 (MEST) Subject: [pypy-svn] r6289 - pypy/branch/pypy-genc/annotation Message-ID: <20040906122231.631BA5A5D1@thoth.codespeak.net> Author: arigo Date: Mon Sep 6 14:22:30 2004 New Revision: 6289 Modified: pypy/branch/pypy-genc/annotation/binaryop.py Log: Bug fix. Modified: pypy/branch/pypy-genc/annotation/binaryop.py ============================================================================== --- pypy/branch/pypy-genc/annotation/binaryop.py (original) +++ pypy/branch/pypy-genc/annotation/binaryop.py Mon Sep 6 14:22:30 2004 @@ -87,6 +87,7 @@ def inplace_add((lst1, lst2)): pair(lst1, SomeInteger()).setitem(lst2.s_item) + return lst1 class __extend__(pairtype(SomeTuple, SomeTuple)): From arigo at codespeak.net Mon Sep 6 14:26:30 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 6 Sep 2004 14:26:30 +0200 (MEST) Subject: [pypy-svn] r6290 - pypy/branch/pypy-genc/translator Message-ID: <20040906122630.AE77F5A5D1@thoth.codespeak.net> Author: arigo Date: Mon Sep 6 14:26:30 2004 New Revision: 6290 Modified: pypy/branch/pypy-genc/translator/genc.py pypy/branch/pypy-genc/translator/typer.py Log: Implement and use static global constants for constant PyObjects. Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Mon Sep 6 14:26:30 2004 @@ -41,8 +41,6 @@ self.genc = genc self.bindings = bindings self.r_constants = {} - self.globaldefs_decl = [] - self.globaldefs_impl = [] self.lloperations = {'convert': {}, 'release': {}} self.parse_operation_templates() @@ -104,16 +102,7 @@ return False def knownanswer(self, llname): - if hasattr(llname, 'known_answer'): - if hasattr(llname, 'globaldefs_decl'): # global definition required - self.globaldefs_decl.extend(llname.globaldefs_decl) - del llname.globaldefs_decl - if hasattr(llname, 'globaldefs_impl'): # global definition required - self.globaldefs_impl.extend(llname.globaldefs_impl) - del llname.globaldefs_impl - return llname.known_answer - else: - return None + return getattr(llname, 'known_answer', None) # ____________________________________________________________ @@ -158,7 +147,7 @@ 'g_StrObj_%s' % manglestr(value)) elif value is None: # can convert the constant to Py_None - self.can_convert_to_pyobj(r, '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] @@ -205,20 +194,13 @@ print "// XXX not implemented: constant", key return r - def can_convert_to_pyobj(self, r, initexpr, globalname=None): + def can_convert_to_pyobj(self, r, initexpr, globalname): conv = self.lloperations['convert'] - if globalname is not None: - def writer(z, err): - return 'if (!(%s = %s)) goto %s;' % (z, initexpr, err) - conv[r, self.R_OBJECT] = writer, True - llconst = LLConst('PyObject*', globalname) - writer.globaldefs_decl = [llconst] - writer.globaldefs_impl = ['%s = %s;' % (llconst.name, initexpr)] - else: - def writer(z): - return '%s = %s; Py_INCREF(%s);' % (z, initexpr, z) - conv[r, self.R_OBJECT] = writer, False - llconst = LLConst('PyObject*', initexpr) + def writer(z): + return '%s = %s; Py_INCREF(%s);' % (z, globalname, z) + conv[r, self.R_OBJECT] = writer, False + llconst = LLConst('PyObject*', globalname, initexpr, + to_declare = bool(initexpr)) writer.known_answer = [llconst] def parse_operation_templates(self): @@ -283,6 +265,7 @@ else: bindings = {} self.typeset = CTypeSet(self, bindings) + self.initializationcode = [] self.llfunctions = {} self.build_llfunctions() self.gen_source() @@ -318,7 +301,8 @@ # footer print >> f, C_METHOD_TABLE % info print >> f, C_INIT_HEADER % info - self.gen_globaldefs() + for codeline in self.initializationcode: + print >> f, '\t' + codeline print >> f, C_INIT_FOOTER % info @@ -372,15 +356,20 @@ # print the declaration of the new global constants needed by # the current function - if self.typeset.globaldefs_decl: - if len(self.typeset.globaldefs_decl) > 1: - s = 's' - else: - s = '' - print >> f, '/* global constant%s */' % s - for llconst in self.typeset.globaldefs_decl: - print >> f, 'static %s %s;' % (llconst.type, llconst.name) - del self.typeset.globaldefs_decl[:] + to_declare = [] + for line in body: + if isinstance(line, LLOp): + for a in line.args + getattr(line.name, 'known_answer', []): + if isinstance(a, LLConst) and a.to_declare: + to_declare.append(a) + if a.initexpr: + self.initializationcode.append('%s = %s;' % ( + a.name, a.initexpr)) + a.to_declare = False + if to_declare: + print >> f, '/* global constant%s */' % ('s'*(len(to_declare)>1)) + for a in to_declare: + print >> f, 'static %s %s;' % (a.type, a.name) print >> f # print header @@ -425,13 +414,6 @@ print >> f print >> f, '}' - def gen_globaldefs(self): - # generate the global definitions - f = self.f - for code in self.typeset.globaldefs_impl: - for codeline in code.split('\n'): - print >> f, '\t' + codeline - LL_ONLY_OPERATIONS = { 'move': lambda x,y: '%s = %s;' % (y,x), Modified: pypy/branch/pypy-genc/translator/typer.py ============================================================================== --- pypy/branch/pypy-genc/translator/typer.py (original) +++ pypy/branch/pypy-genc/translator/typer.py Mon Sep 6 14:26:30 2004 @@ -14,7 +14,11 @@ self.name = name class LLConst(LLVar): - "An LLVar whose name is abused to be a constant expression." + "A global, constant, preinitialized variable." + def __init__(self, type, name, initexpr=None, to_declare=False): + LLVar.__init__(self, type, name) + self.initexpr = initexpr + self.to_declare = to_declare class LLOp: "A low-level operation." From arigo at codespeak.net Mon Sep 6 14:37:24 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 6 Sep 2004 14:37:24 +0200 (MEST) Subject: [pypy-svn] r6291 - pypy/branch/pypy-genc/translator Message-ID: <20040906123724.9641F5A5D1@thoth.codespeak.net> Author: arigo Date: Mon Sep 6 14:37:23 2004 New Revision: 6291 Added: pypy/branch/pypy-genc/translator/genc_class.h pypy/branch/pypy-genc/translator/genc_typeset.py Modified: pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/genc.py Log: Translating simple classes to C structs and a PyTypeObject declaration. Moved the CTypeSet class to its own file. Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Mon Sep 6 14:37:23 2004 @@ -3,6 +3,7 @@ /*** Generic C header section ***/ #include +#include /*** operations on ints ***/ @@ -46,6 +47,7 @@ #define OP_GETITEM_ooo(x,y,r,err) if (!(r=PyObject_GetItem(x,y))) goto err; #define OP_SETITEM_ooov(x,y,z,err) if ((PyObject_SetItem(x,y,z))<0) goto err; #define OP_GETATTR_ooo(x,y,r,err) if (!(r=PyObject_GetAttr(x,y))) goto err; +#define OP_SETATTR_ooov(x,y,z,err) if ((PyObject_SetAttr(x,y,z))<0) goto err; #define OP_NEWSLICE_oooo(x,y,z,r,err) if (!(r=PySlice_New(x,y,z))) goto err; @@ -92,6 +94,8 @@ } \ } +#define INSTANTIATE(cls, r, err) if (!(r=cls##_new())) goto err; + /* a few built-in functions */ Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Mon Sep 6 14:37:23 2004 @@ -2,251 +2,17 @@ Generate a C source file from the flowmodel. """ -import autopath, os, re, types, __builtin__ -from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant -from pypy.objspace.flow.model import Block, Link, traverse -from pypy.translator.typer import LLFunction, LLOp, LLConst, TypingError -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, err_check=None, parse_code=None): - self.impl = impl # list [(C type, prefix for variable name)] - self.err_check = err_check # condition to check for error return value - self.parse_code = parse_code # character(s) for PyArg_ParseTuple() - - def __repr__(self): - if hasattr(self, 'const'): - return '' % (self.const,) - else: - return '' % ' + '.join(self.impl) - - -class CTypeSet: - "A (small) set of C types that typer.LLFunction can manipulate." - - R_VOID = CRepr([]) - R_INT = CRepr(['int'], err_check='< 0', parse_code='i') - R_OBJECT = CRepr(['PyObject*'], err_check='== NULL', parse_code='O') - - REPR_BY_CODE = { - 'v': R_VOID, - 'i': R_INT, - 'o': R_OBJECT, - } - - def __init__(self, genc, bindings): - self.genc = genc - 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 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 - def writer(*stuff): - content = stuff[:-2] - result = stuff[-2] - err = stuff[-1] - ls = ['if (!(%s = PyList_New(%d))) goto %s;' % ( - result, len(content), err)] - for i in range(len(content)): - ls.append('PyList_SET_ITEM(%s, %d, %s); Py_INCREF(%s);' % ( - result, i, content[i], content[i])) - return '\n'.join(ls) - opnewlist[sig] = writer, True - 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 - def writer(func, *stuff): - args = stuff[:-2] - result = stuff[-2] - err = stuff[-1] - format = '"' + 'O' * len(args) + '"' - args = (func, format) + args - return ('if (!(%s = PyObject_CallFunction(%s)))' - ' goto %s;' % (result, ', '.join(args), err)) - opsimplecall[sig] = writer, True - return True # retry - return False - - def knownanswer(self, llname): - return getattr(llname, 'known_answer', None) - - # ____________________________________________________________ - - 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([]) - r.const = value - # returning a constant - def writer(): - return 'return 0;' - self.lloperations['return'][r,] = writer, False - def writer(): - return 'return -1;' - self.lloperations['returnerr'][r,] = writer, False - - # 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 - def writer(z): - return '%s = %d;' % (z, value) - conv[r, self.R_INT] = writer, False - writer.known_answer = [LLConst(self.R_INT, '%d' % value)] - # 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) - 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', {}) - sig = [r] - for v in llfunc.graph.getargs(): - sig.append(self.gethltype(v)) - hltype = self.gethltype(llfunc.graph.getreturnvar()) - sig.append(hltype) - if len(hltype.impl) == 0: # no return value - def writer(*stuff): - args = stuff[:-1] - err = stuff[-1] - return 'if (%s(%s) < 0) goto %s;' % ( - llfunc.name, ', '.join(args), err) - elif len(hltype.impl) == 1: # one LLVar for the return value - def writer(*stuff): - args = stuff[:-2] - result = stuff[-2] - err = stuff[-1] - return ('if ((%s = %s(%s)) %s) goto %s;' % ( - result, llfunc.name, ', '.join(args), - hltype.err_check, err)) - else: - XXX("to do") - ops[tuple(sig)] = writer, True - 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, ll in self.lloperations[opname].items(): - sig = (r,) + sig - ops[sig] = ll - else: - print "// XXX not implemented: constant", key - return r - - def can_convert_to_pyobj(self, r, initexpr, globalname): - conv = self.lloperations['convert'] - def writer(z): - return '%s = %s; Py_INCREF(%s);' % (z, globalname, z) - conv[r, self.R_OBJECT] = writer, False - llconst = LLConst('PyObject*', globalname, initexpr, - to_declare = bool(initexpr)) - writer.known_answer = [llconst] - - 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: - self.register_operation_template(*match.groups()) - - def register_operation_template(self, opname, typecodes, formalargs): - 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 - # the operation's low-level name is a callable that will - # produce the correct macro call - def writer(*args): - return llname + '(' + ', '.join(args) + ')' - ops.setdefault(sig, (writer, can_fail)) - -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) +import autopath, os +from pypy.translator.typer import LLFunction, LLOp, LLConst +from pypy.translator import genc_typeset + +class CClass: + def __init__(self, name, cdef): + self.name = name + self.cdef = cdef # instance of pypy.annotator.factory.ClassDef # ____________________________________________________________ - class GenC: def __init__(self, f, translator, modname=None): @@ -264,9 +30,11 @@ pass else: bindings = {} - self.typeset = CTypeSet(self, bindings) + self.typeset = genc_typeset.CTypeSet(self, bindings) self.initializationcode = [] + self.cclasses = {} self.llfunctions = {} + self.build_cclasses() self.build_llfunctions() self.gen_source() @@ -277,7 +45,7 @@ 'exported': self.translator.functions[0].__name__, } # header - print >> f, C_HEADER + print >> f, self.C_HEADER # forward declarations print >> f, '/* forward declarations */' @@ -285,26 +53,44 @@ print >> f, 'static %s;' % self.cfunction_header(func) print >> f + # class declarations + if self.cclasses: + # keep original order, so don't iterate over the dict self.cclasses + for cdef in self.translator.annotator.getuserclassdefinitions(): + self.gen_cclass(self.cclasses[cdef.cls]) + # function implementation - print >> f, C_SEP + print >> f, self.C_SEP print >> f for func in self.translator.functions: self.gen_cfunction(func) print >> f # entry point - print >> f, C_SEP - print >> f, C_ENTRYPOINT_HEADER % info + print >> f, self.C_SEP + print >> f, self.C_ENTRYPOINT_HEADER % info self.gen_entrypoint(self.translator.functions[0]) - print >> f, C_ENTRYPOINT_FOOTER % info + print >> f, self.C_ENTRYPOINT_FOOTER % info # footer - print >> f, C_METHOD_TABLE % info - print >> f, C_INIT_HEADER % info + print >> f, self.C_METHOD_TABLE % info + print >> f, self.C_INIT_HEADER % info for codeline in self.initializationcode: print >> f, '\t' + codeline - print >> f, C_INIT_FOOTER % info + print >> f, self.C_INIT_FOOTER % info + + def build_cclasses(self): + if not self.translator.annotator: + return + n = 0 + for cdef in self.translator.annotator.getuserclassdefinitions(): + cls = cdef.cls + self.cclasses[cls] = CClass( + name = '%s__%d' % (cls.__name__, n), + cdef = cdef, + ) + n += 1 def build_llfunctions(self): n = 0 @@ -403,7 +189,7 @@ # operation that comes from typer.py. writer = line.name if isinstance(writer, str): - writer = LL_ONLY_OPERATIONS[writer] + writer = self.LL_ONLY_OPERATIONS[writer] code = writer(*args) for codeline in code.split('\n'): print >> f, '\t' + codeline @@ -414,35 +200,43 @@ print >> f print >> f, '}' + LL_ONLY_OPERATIONS = { + 'move': lambda x,y: '%s = %s;' % (y,x), + 'goto': lambda err: 'goto %s;' % err, + } -LL_ONLY_OPERATIONS = { - 'move': lambda x,y: '%s = %s;' % (y,x), - 'goto': lambda err: 'goto %s;' % err, - } + def gen_cclass(self, cclass): + f = self.f + info = { + 'name': cclass.name, + 'module': cclass.cdef.cls.__module__, + } + print >> f, self.C_CLASS % info # ____________________________________________________________ -C_HEADER = open(os.path.join(autopath.this_dir, 'genc.h')).read() + C_HEADER = open(os.path.join(autopath.this_dir, 'genc.h')).read() + C_CLASS = open(os.path.join(autopath.this_dir, 'genc_class.h')).read() -C_SEP = "/************************************************************/" + C_SEP = "/************************************************************/" -C_ENTRYPOINT_HEADER = ''' + C_ENTRYPOINT_HEADER = ''' static PyObject* c_%(exported)s(PyObject* self, PyObject* args) {''' -C_ENTRYPOINT_FOOTER = '''}''' + C_ENTRYPOINT_FOOTER = '''}''' -C_METHOD_TABLE = ''' + C_METHOD_TABLE = ''' static PyMethodDef %(modname)sMethods[] = { \t{"%(exported)s", (PyCFunction)c_%(exported)s, METH_VARARGS}, \t{NULL, NULL} };''' -C_INIT_HEADER = ''' + C_INIT_HEADER = ''' void init%(modname)s(void) { \tPy_InitModule("%(modname)s", %(modname)sMethods);''' -C_INIT_FOOTER = '''}''' + C_INIT_FOOTER = '''}''' # ____________________________________________________________ Added: pypy/branch/pypy-genc/translator/genc_class.h ============================================================================== --- (empty file) +++ pypy/branch/pypy-genc/translator/genc_class.h Mon Sep 6 14:37:23 2004 @@ -0,0 +1,83 @@ +/************************************************************/ +/* Definition of class %(module)s.%(name)s */ + +typedef struct { + PyObject_HEAD + PyObject* dict; +} %(name)s_Object; +staticforward PyTypeObject %(name)s_Type; + +static PyObject* %(name)s_new(void) +{ + %(name)s_Object* op = PyObject_GC_New(%(name)s_Object, &%(name)s_Type); + if (op) + PyObject_GC_Track(op); + return (PyObject*) op; +} + +static void %(name)s_dealloc(%(name)s_Object* op) +{ + PyObject_GC_UnTrack(op); + Py_XDECREF(op->dict); + op->ob_type->tp_free((PyObject *)op); +} + +static int %(name)s_traverse(%(name)s_Object* op, visitproc visit, void* arg) +{ + if (op->dict != NULL) + return visit(op->dict, arg); + else + return 0; +} + +static int %(name)s_clear(%(name)s_Object* op) +{ + PyObject* d = op->dict; + op->dict = NULL; + Py_XDECREF(d); + return 0; +} + +statichere PyTypeObject %(name)s_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "%(name)s", + sizeof(%(name)s_Object), + 0, + (destructor)%(name)s_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)%(name)s_traverse, /* tp_traverse */ + (inquiry)%(name)s_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + offsetof(%(name)s_Object, dict), /* tp_dictoffset */ + 0, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; Added: pypy/branch/pypy-genc/translator/genc_typeset.py ============================================================================== --- (empty file) +++ pypy/branch/pypy-genc/translator/genc_typeset.py Mon Sep 6 14:37:23 2004 @@ -0,0 +1,250 @@ +import re, types, __builtin__ +from pypy.objspace.flow.model import Variable, Constant +from pypy.annotation import model as annmodel +from pypy.translator.typer import LLConst + + +class CRepr: + "A possible representation of a flow-graph variable as C-level variables." + + def __init__(self, impl, err_check=None, parse_code=None): + self.impl = impl # list [(C type, prefix for variable name)] + self.err_check = err_check # condition to check for error return value + self.parse_code = parse_code # character(s) for PyArg_ParseTuple() + + def __repr__(self): + if hasattr(self, 'const'): + return '' % (self.const,) + else: + return '' % ' + '.join(self.impl) + + +class CTypeSet: + "A (small) set of C types that typer.LLFunction can manipulate." + + R_VOID = CRepr([]) + R_INT = CRepr(['int'], err_check='< 0', parse_code='i') + R_OBJECT = CRepr(['PyObject*'], err_check='== NULL', parse_code='O') + + REPR_BY_CODE = { + 'v': R_VOID, + 'i': R_INT, + 'o': R_OBJECT, + } + + def __init__(self, genc, bindings): + self.genc = genc + 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 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 + def writer(*stuff): + content = stuff[:-2] + result = stuff[-2] + err = stuff[-1] + ls = ['if (!(%s = PyList_New(%d))) goto %s;' % ( + result, len(content), err)] + for i in range(len(content)): + ls.append('PyList_SET_ITEM(%s, %d, %s); Py_INCREF(%s);' % ( + result, i, content[i], content[i])) + return '\n'.join(ls) + opnewlist[sig] = writer, True + 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 + def writer(func, *stuff): + args = stuff[:-2] + result = stuff[-2] + err = stuff[-1] + format = '"' + 'O' * len(args) + '"' + args = (func, format) + args + return ('if (!(%s = PyObject_CallFunction(%s)))' + ' goto %s;' % (result, ', '.join(args), err)) + opsimplecall[sig] = writer, True + return True # retry + return False + + def knownanswer(self, llname): + return getattr(llname, 'known_answer', None) + + # ____________________________________________________________ + + 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([]) + r.const = value + # returning a constant + def writer(): + return 'return 0;' + self.lloperations['return'][r,] = writer, False + def writer(): + return 'return -1;' + self.lloperations['returnerr'][r,] = writer, False + + # 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 + def writer(z): + return '%s = %d;' % (z, value) + conv[r, self.R_INT] = writer, False + writer.known_answer = [LLConst(self.R_INT, '%d' % value)] + # 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) + 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', {}) + sig = [r] + for v in llfunc.graph.getargs(): + sig.append(self.gethltype(v)) + hltype = self.gethltype(llfunc.graph.getreturnvar()) + sig.append(hltype) + if len(hltype.impl) == 0: # no return value + def writer(*stuff): + args = stuff[:-1] + err = stuff[-1] + return 'if (%s(%s) < 0) goto %s;' % ( + llfunc.name, ', '.join(args), err) + elif len(hltype.impl) == 1: # one LLVar for the return value + def writer(*stuff): + args = stuff[:-2] + result = stuff[-2] + err = stuff[-1] + return ('if ((%s = %s(%s)) %s) goto %s;' % ( + result, llfunc.name, ', '.join(args), + hltype.err_check, err)) + else: + XXX("to do") + ops[tuple(sig)] = writer, True + 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, ll in self.lloperations[opname].items(): + sig = (r,) + sig + ops[sig] = ll + elif (isinstance(value, (type, types.ClassType)) and + value in self.genc.cclasses): + # a user-defined class + ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {}) + # XXX do __init__ + sig = (r, self.R_OBJECT) + def writer(res, err): + return 'INSTANTIATE(%s, %s, %s)' % ( + self.genc.cclasses[value].name, res, err) + ops[sig] = writer, True + else: + print "// XXX not implemented: constant", key + return r + + def can_convert_to_pyobj(self, r, initexpr, globalname): + conv = self.lloperations['convert'] + def writer(z): + return '%s = %s; Py_INCREF(%s);' % (z, globalname, z) + conv[r, self.R_OBJECT] = writer, False + llconst = LLConst('PyObject*', globalname, initexpr, + to_declare = bool(initexpr)) + writer.known_answer = [llconst] + + 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 self.genc.C_HEADER.split('\n'): + match = rexp.match(line) + if match: + self.register_operation_template(*match.groups()) + + def register_operation_template(self, opname, typecodes, formalargs): + 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 + # the operation's low-level name is a callable that will + # produce the correct macro call + def writer(*args): + return llname + '(' + ', '.join(args) + ')' + ops.setdefault(sig, (writer, can_fail)) + +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) From arigo at codespeak.net Mon Sep 6 14:39:54 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 6 Sep 2004 14:39:54 +0200 (MEST) Subject: [pypy-svn] r6292 - pypy/branch/pypy-genc/translator Message-ID: <20040906123954.0810C5A5D1@thoth.codespeak.net> Author: arigo Date: Mon Sep 6 14:39:54 2004 New Revision: 6292 Added: pypy/branch/pypy-genc/translator/formatter.py Modified: pypy/branch/pypy-genc/translator/genc.py pypy/branch/pypy-genc/translator/genc_class.h pypy/branch/pypy-genc/translator/genc_typeset.py Log: Generate C structs with correctly typed fields. (This is getting messy. As these check-ins are actually a replay of what I did last week, I know what's coming next, so don't look too closely here: genc_class.h and formatter.py will soon be deleted again!) --This line, and those below, will be ignored-- M translator/genc_typeset.py M translator/genc_class.h A translator/formatter.py M translator/genc.py Added: pypy/branch/pypy-genc/translator/formatter.py ============================================================================== --- (empty file) +++ pypy/branch/pypy-genc/translator/formatter.py Mon Sep 6 14:39:54 2004 @@ -0,0 +1,30 @@ +import sys +from StringIO import StringIO + +class TemplateFormatter: + """For use with format strings. + + 'formatstring % formatter' will evaluate all %(xxx)s expressions + found in the format string in the given globals/locals. + + Multiline expressions are assumed to be one or several complete + statements; they are executed and whatever they print is inserted back + into the format string.""" + + def __init__(self, globals, locals): + self.globals = globals + self.locals = locals + + def __getitem__(self, expr): + if '\n' in expr: + if not expr.endswith('\n'): + expr += '\n' + prevstdout = sys.stdout + try: + sys.stdout = f = StringIO() + exec expr in self.globals, self.locals + finally: + sys.stdout = prevstdout + return f.getvalue() + else: + return eval(expr, self.globals, self.locals) Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Mon Sep 6 14:39:54 2004 @@ -2,9 +2,9 @@ Generate a C source file from the flowmodel. """ -import autopath, os +import autopath, os, re from pypy.translator.typer import LLFunction, LLOp, LLConst -from pypy.translator import genc_typeset +from pypy.translator import genc_typeset, formatter class CClass: def __init__(self, name, cdef): @@ -50,7 +50,7 @@ # forward declarations print >> f, '/* forward declarations */' for func in self.translator.functions: - print >> f, 'static %s;' % self.cfunction_header(func) + print >> f, '%s;' % self.cfunction_header(func) print >> f # class declarations @@ -107,9 +107,9 @@ llargs, rettype = llfunc.ll_header() l = ['%s %s' % (a.type, a.name) for a in llargs] l = l or ['void'] - return '%s %s(%s)' % (rettype or 'int', - llfunc.name, - ', '.join(l)) + return 'static %s %s(%s)' % (rettype or 'int', + llfunc.name, + ', '.join(l)) def gen_entrypoint(self, func): f = self.f @@ -207,11 +207,13 @@ def gen_cclass(self, cclass): f = self.f - info = { + loc = { 'name': cclass.name, - 'module': cclass.cdef.cls.__module__, + 'cdef': cclass.cdef, + 'cls': cclass.cdef.cls, + 'typeset': self.typeset, } - print >> f, self.C_CLASS % info + print >> f, self.C_CLASS % formatter.TemplateFormatter(globals(), loc) # ____________________________________________________________ Modified: pypy/branch/pypy-genc/translator/genc_class.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc_class.h (original) +++ pypy/branch/pypy-genc/translator/genc_class.h Mon Sep 6 14:39:54 2004 @@ -1,43 +1,107 @@ /************************************************************/ -/* Definition of class %(module)s.%(name)s */ +/* Definition of class %(cls.__module__)s.%(cls.__name__)s */ typedef struct { PyObject_HEAD - PyObject* dict; -} %(name)s_Object; +%( +# write here the definition of all fields +# (see formatter.py for the definition of this small templating language) +mainletters = [c.lower() for c in cls.__name__ if 'A' <= c <= 'Z'] +field_prefix = ''.join(mainletters or ['f']) +pyobj_fields = [] +fields = [] +for attr, s_value in cdef.attrs.items(): + hltype = typeset.gethltype(s_value) + lltypes = typeset.represent(hltype) + # to avoid name collisions between the 2nd lltype of a field called xyz + # and another field whose name is exactly xyz_1, forbid field names ending + # in '_\d+'. + field = '%s_%s' % (field_prefix, attr) + if re.search(r'_\d+$', attr): + field += '_' + fields.append((attr, field, hltype)) + if hltype is typeset.R_OBJECT: + pyobj_fields.append(field) + for i, lltype in zip(range(len(lltypes)), lltypes): + if i: + suffix = '_%d' % i + else: + suffix = '' + print '\t%s %s%s;' % (lltype, field, suffix) + +)s} %(name)s_Object; + staticforward PyTypeObject %(name)s_Type; static PyObject* %(name)s_new(void) { %(name)s_Object* op = PyObject_GC_New(%(name)s_Object, &%(name)s_Type); - if (op) - PyObject_GC_Track(op); + if (op) { +%( +# initialize PyObject* fields to NULL +for field in pyobj_fields: + print '\t\top->%s = NULL;' % field + +)s PyObject_GC_Track(op); + /* XXX call __init__ */ + } return (PyObject*) op; } static void %(name)s_dealloc(%(name)s_Object* op) { PyObject_GC_UnTrack(op); - Py_XDECREF(op->dict); - op->ob_type->tp_free((PyObject *)op); +%( +# release PyObject* fields XXX should be more general +for field in pyobj_fields: + print '\tPy_XDECREF(op->%s);' % field + +)s op->ob_type->tp_free((PyObject *)op); } static int %(name)s_traverse(%(name)s_Object* op, visitproc visit, void* arg) { - if (op->dict != NULL) - return visit(op->dict, arg); - else - return 0; +%( +# traverse PyObject* fields +for field in pyobj_fields: + print '\tif (op->%s != NULL) {' % field + print '\t\tint err = visit(op->%s, arg);' % field + print '\t\tif (err) return err;' + print '\t}' + +)s return 0; } static int %(name)s_clear(%(name)s_Object* op) { - PyObject* d = op->dict; - op->dict = NULL; - Py_XDECREF(d); - return 0; +%( +# clear PyObject* fields +for field in pyobj_fields: + print '\tif (op->%s != NULL) {' % field + print '\t\tPyObject* tmp = op->%s;' % field + print '\t\top->%s = NULL;' % field + print '\t\tPy_DECREF(tmp);' + print '\t}' + +)s return 0; } +static PyMemberDef %(name)s_memberlist[] = { +%( +# write member definitions for member with well-known types XXX generalize +for attr, field, hltype in fields: + if hltype is typeset.R_OBJECT: + t = 'T_OBJECT_EX' + elif hltype is typeset.R_INT: + t = 'T_INT' + else: + continue # ignored + print '\t{"%s",\t%s,\toffsetof(%s_Object, %s)},' % ( + attr, t, name, field) + +)s {NULL} /* Sentinel */ +}; + statichere PyTypeObject %(name)s_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, @@ -69,13 +133,13 @@ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ - 0, /* tp_members */ + %(name)s_memberlist, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ - offsetof(%(name)s_Object, dict), /* tp_dictoffset */ + 0, /* tp_dictoffset */ 0, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ PyType_GenericNew, /* tp_new */ Modified: pypy/branch/pypy-genc/translator/genc_typeset.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc_typeset.py (original) +++ pypy/branch/pypy-genc/translator/genc_typeset.py Mon Sep 6 14:39:54 2004 @@ -46,10 +46,11 @@ 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): + var = self.bindings.get(var) or annmodel.SomeObject() + if isinstance(var, annmodel.SomeObject): + if var.is_constant(): + return self.constant_representation(var.const) + if issubclass(var.knowntype, int): return self.R_INT # fall-back return self.R_OBJECT From arigo at codespeak.net Mon Sep 6 14:41:51 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 6 Sep 2004 14:41:51 +0200 (MEST) Subject: [pypy-svn] r6293 - pypy/branch/pypy-genc/translator Message-ID: <20040906124151.746BB5A5D1@thoth.codespeak.net> Author: arigo Date: Mon Sep 6 14:41:50 2004 New Revision: 6293 Modified: pypy/branch/pypy-genc/translator/formatter.py pypy/branch/pypy-genc/translator/genc.py pypy/branch/pypy-genc/translator/genc_class.h pypy/branch/pypy-genc/translator/genc_typeset.py Log: More messiness, but a lot of tests pass. Checked in for reference before things get cleaned up in a different way. Modified: pypy/branch/pypy-genc/translator/formatter.py ============================================================================== --- pypy/branch/pypy-genc/translator/formatter.py (original) +++ pypy/branch/pypy-genc/translator/formatter.py Mon Sep 6 14:41:50 2004 @@ -1,3 +1,4 @@ +from __future__ import generators import sys from StringIO import StringIO @@ -11,7 +12,9 @@ statements; they are executed and whatever they print is inserted back into the format string.""" - def __init__(self, globals, locals): + def __init__(self, globals, locals=None): + if locals is None: + locals = globals self.globals = globals self.locals = locals Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Mon Sep 6 14:41:50 2004 @@ -2,7 +2,8 @@ Generate a C source file from the flowmodel. """ -import autopath, os, re +from __future__ import generators +import autopath, os from pypy.translator.typer import LLFunction, LLOp, LLConst from pypy.translator import genc_typeset, formatter @@ -211,9 +212,33 @@ 'name': cclass.name, 'cdef': cclass.cdef, 'cls': cclass.cdef.cls, + 'genc': self, 'typeset': self.typeset, } - print >> f, self.C_CLASS % formatter.TemplateFormatter(globals(), loc) + print >> f, self.C_CLASS % formatter.TemplateFormatter(loc) + + def copy(self, srcrepr, srcvars, dstrepr, dstvars, errlabel): + # write the code to copy a value from src to dst. + if srcrepr == dstrepr: + for s, d in zip(srcvars, dstvars): + yield '%s = %s;' % (d, s) + try: + sig = (dstrepr,) + llname, can_fail = self.typeset.lloperations['incref'][sig] + except KeyError: + pass # no incref + else: + assert not can_fail + for codeline in llname(*dstvars).split('\n'): + yield codeline + else: + sig = (srcrepr, dstrepr) + llname, can_fail = self.typeset.lloperations['convert'][sig] + args = srcvars + dstvars + if can_fail: + args.append(errlabel) + for codeline in llname(*args).split('\n'): + yield codeline # ____________________________________________________________ Modified: pypy/branch/pypy-genc/translator/genc_class.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc_class.h (original) +++ pypy/branch/pypy-genc/translator/genc_class.h Mon Sep 6 14:41:50 2004 @@ -6,10 +6,22 @@ %( # write here the definition of all fields # (see formatter.py for the definition of this small templating language) +import re + mainletters = [c.lower() for c in cls.__name__ if 'A' <= c <= 'Z'] field_prefix = ''.join(mainletters or ['f']) pyobj_fields = [] fields = [] + +def llfield(field, hltype): + lltypes = typeset.represent(hltype) + for i in range(len(lltypes)): + if i: + suffix = '_%d' % i + else: + suffix = '' + yield field + suffix + for attr, s_value in cdef.attrs.items(): hltype = typeset.gethltype(s_value) lltypes = typeset.represent(hltype) @@ -22,13 +34,9 @@ fields.append((attr, field, hltype)) if hltype is typeset.R_OBJECT: pyobj_fields.append(field) - for i, lltype in zip(range(len(lltypes)), lltypes): - if i: - suffix = '_%d' % i - else: - suffix = '' - print '\t%s %s%s;' % (lltype, field, suffix) - + for lltype, llname in zip(lltypes, llfield(field, hltype)): + print '\t%s %s;' % (lltype, llname) +# ---- )s} %(name)s_Object; staticforward PyTypeObject %(name)s_Type; @@ -36,16 +44,27 @@ static PyObject* %(name)s_new(void) { %(name)s_Object* op = PyObject_GC_New(%(name)s_Object, &%(name)s_Type); - if (op) { + if (op == NULL) goto err; %( -# initialize PyObject* fields to NULL +# initialize fields from the PyObject* fields to NULL for field in pyobj_fields: - print '\t\top->%s = NULL;' % field - -)s PyObject_GC_Track(op); - /* XXX call __init__ */ - } + print '\top->%s = NULL;' % field +# initialize fields using the class attributes as a default +for attr, field, hltype in fields: + if hasattr(cls, attr): + r = typeset.constant_representation(getattr(cls, attr)) + for codeline in genc.copy(r, [], + hltype, ['op->%s' % f for f in llfield(field, hltype)], + 'err'): + print '\t' + codeline +# ---- +)s PyObject_GC_Track(op); + /* XXX call __init__ */ return (PyObject*) op; + + err: + Py_XDECREF(op); + return NULL; } static void %(name)s_dealloc(%(name)s_Object* op) @@ -55,7 +74,7 @@ # release PyObject* fields XXX should be more general for field in pyobj_fields: print '\tPy_XDECREF(op->%s);' % field - +# ---- )s op->ob_type->tp_free((PyObject *)op); } @@ -68,7 +87,7 @@ print '\t\tint err = visit(op->%s, arg);' % field print '\t\tif (err) return err;' print '\t}' - +# ---- )s return 0; } @@ -82,7 +101,7 @@ print '\t\top->%s = NULL;' % field print '\t\tPy_DECREF(tmp);' print '\t}' - +# ---- )s return 0; } @@ -98,7 +117,7 @@ continue # ignored print '\t{"%s",\t%s,\toffsetof(%s_Object, %s)},' % ( attr, t, name, field) - +# ---- )s {NULL} /* Sentinel */ }; Modified: pypy/branch/pypy-genc/translator/genc_typeset.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc_typeset.py (original) +++ pypy/branch/pypy-genc/translator/genc_typeset.py Mon Sep 6 14:41:50 2004 @@ -52,6 +52,8 @@ return self.constant_representation(var.const) if issubclass(var.knowntype, int): return self.R_INT + if isinstance(var, annmodel.SomeImpossibleValue): + return self.R_VOID # fall-back return self.R_OBJECT if isinstance(var, Constant): From arigo at codespeak.net Mon Sep 6 14:47:07 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 6 Sep 2004 14:47:07 +0200 (MEST) Subject: [pypy-svn] r6295 - pypy/branch/pypy-genc/translator Message-ID: <20040906124707.8FC865A5D1@thoth.codespeak.net> Author: arigo Date: Mon Sep 6 14:47:06 2004 New Revision: 6295 Added: pypy/branch/pypy-genc/translator/classtyper.py Removed: pypy/branch/pypy-genc/translator/formatter.py pypy/branch/pypy-genc/translator/genc_class.h Modified: pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/genc.py pypy/branch/pypy-genc/translator/genc_typeset.py pypy/branch/pypy-genc/translator/typer.py Log: Replaced the combination genc_class.h/formatter.py with classtyper.py which preprocesses class definitions into simple structures and support functions. The support functions are generated by producing a control flow graph, which is then translated into a C function by genc.py like all user-defined functions. --This line, and those below, will be ignored-- A translator/classtyper.py M translator/genc_typeset.py D translator/genc_class.h M translator/typer.py D translator/formatter.py M translator/genc.h M translator/genc.py Added: pypy/branch/pypy-genc/translator/classtyper.py ============================================================================== --- (empty file) +++ pypy/branch/pypy-genc/translator/classtyper.py Mon Sep 6 14:47:06 2004 @@ -0,0 +1,93 @@ +""" +Extension to typer.py to preprocess class definitions and usage into +lower-level field and function definitions. +""" +from __future__ import generators +import re +from pypy.objspace.flow.model import Variable, Constant, SpaceOperation +from pypy.objspace.flow.model import FunctionGraph, Block, Link +from pypy.annotation import model as annmodel +from pypy.translator.typer import LLTyper, LLFunction + +r_ends_in_underscore_digit = re.compile(r'_\d+$') + +# ____________________________________________________________ + +class ClassField: + "An attribute of a class, mapped to some field(s) of a low-level struct." + + def __init__(self, hltype, name, llclass): + self.hltype = hltype + self.name = name + self.llclass = llclass + varname = '%s_%s' % (llclass.field_prefix, name) + # to avoid name collisions between the 2nd lltype of a field called xyz + # and another field whose name is exactly xyz_1, forbid field names + # ending in '_\d+' + if r_ends_in_underscore_digit.search(name): + varname += '_' + # self.var is a Variable that can stand for this field + self.var = Variable(varname) + llclass.makevar(self.var, hltype=hltype) + # this (high-level) field is implemented as a list of LLVars + # that are real fields for the C struct + self.llvars = llclass.llreprs[self.var] + +# ____________________________________________________________ + +class LLClass(LLTyper): + """Low-level representation of a class as a structure and + global functions that operate on it.""" + + def __init__(self, typeset, name, cdef): + LLTyper.__init__(self, typeset) + self.typeset = typeset + self.name = name + self.cdef = cdef # instance of pypy.annotator.factory.ClassDef + self.bindings = typeset.bindings + + # collect the fields that the annotator deduced for this class + cls = cdef.cls + mainletters = [c.lower() for c in cls.__name__ if 'A' <= c <= 'Z'] + self.field_prefix = ''.join(mainletters[:3] or ['f']) + self.fields = [ClassField(typeset.gethltype(s_value), attr, self) + 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] + self.s_instance = annmodel.SomeInstance(self.cdef) + + def get_management_functions(self): + "Generate LLFunctions that operate on this class' structure." + yield self.make_fn_new() + + def build_llfunc(self, graph): + return LLFunction(self.typeset, graph.name, graph) + + def put_op(self, b): + def op(opname, *args, **kw): + assert kw.keys() == ['s_result'] + result = Variable() + self.bindings[result] = kw['s_result'] + b.operations.append(SpaceOperation(opname, args, result)) + return result + return op + + def make_fn_new(self): + # generate the flow graph of the xxx_new() function + b = Block([]) + op = self.put_op(b) + cls = self.cdef.cls + v1 = op('alloc_instance', Constant(cls), + s_result = self.s_instance) + # class attributes are used as defaults to initialize fields + for fld in self.fields: + if hasattr(cls, fld.name): + value = getattr(cls, fld.name) + op('setattr', v1, Constant(fld.name), Constant(value), + s_result = annmodel.SomeImpossibleValue()) + # finally, return v1 + graph = FunctionGraph('%s_new' % self.name, b) + self.bindings[graph.getreturnvar()] = self.bindings[v1] + b.closeblock(Link([v1], graph.returnblock)) + return self.build_llfunc(graph) Deleted: /pypy/branch/pypy-genc/translator/formatter.py ============================================================================== --- /pypy/branch/pypy-genc/translator/formatter.py Mon Sep 6 14:47:06 2004 +++ (empty file) @@ -1,33 +0,0 @@ -from __future__ import generators -import sys -from StringIO import StringIO - -class TemplateFormatter: - """For use with format strings. - - 'formatstring % formatter' will evaluate all %(xxx)s expressions - found in the format string in the given globals/locals. - - Multiline expressions are assumed to be one or several complete - statements; they are executed and whatever they print is inserted back - into the format string.""" - - def __init__(self, globals, locals=None): - if locals is None: - locals = globals - self.globals = globals - self.locals = locals - - def __getitem__(self, expr): - if '\n' in expr: - if not expr.endswith('\n'): - expr += '\n' - prevstdout = sys.stdout - try: - sys.stdout = f = StringIO() - exec expr in self.globals, self.locals - finally: - sys.stdout = prevstdout - return f.getvalue() - else: - return eval(expr, self.globals, self.locals) Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Mon Sep 6 14:47:06 2004 @@ -48,6 +48,7 @@ #define OP_SETITEM_ooov(x,y,z,err) if ((PyObject_SetItem(x,y,z))<0) goto err; #define OP_GETATTR_ooo(x,y,r,err) if (!(r=PyObject_GetAttr(x,y))) goto err; #define OP_SETATTR_ooov(x,y,z,err) if ((PyObject_SetAttr(x,y,z))<0) goto err; +#define OP_DELATTR_oov(x,y,err) if ((PyObject_SetAttr(x,y,NULL))<0)goto err; #define OP_NEWSLICE_oooo(x,y,z,r,err) if (!(r=PySlice_New(x,y,z))) goto err; @@ -82,6 +83,7 @@ #define returnerr_o() return NULL; #define incref_o(o) Py_INCREF(o); #define decref_o(o) Py_DECREF(o); +#define xdecref_o(o) Py_XDECREF(o); #define OP_EXCEPTION_ov(x) /* XXX exceptions not implemented */ @@ -95,6 +97,8 @@ } #define INSTANTIATE(cls, r, err) if (!(r=cls##_new())) goto err; +#define ALLOC_INSTANCE(cls, r, err) \ + if (!(r=PyType_GenericAlloc(&cls##_Type, 0))) goto err; /* a few built-in functions */ Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Mon Sep 6 14:47:06 2004 @@ -5,12 +5,8 @@ from __future__ import generators import autopath, os from pypy.translator.typer import LLFunction, LLOp, LLConst -from pypy.translator import genc_typeset, formatter - -class CClass: - def __init__(self, name, cdef): - self.name = name - self.cdef = cdef # instance of pypy.annotator.factory.ClassDef +from pypy.translator.classtyper import LLClass +from pypy.translator import genc_typeset # ____________________________________________________________ @@ -33,9 +29,9 @@ bindings = {} self.typeset = genc_typeset.CTypeSet(self, bindings) self.initializationcode = [] - self.cclasses = {} - self.llfunctions = {} - self.build_cclasses() + self.llclasses = {}; self.classeslist = [] + self.llfunctions = {}; self.functionslist = [] + self.build_llclasses() self.build_llfunctions() self.gen_source() @@ -50,21 +46,19 @@ # forward declarations print >> f, '/* forward declarations */' - for func in self.translator.functions: - print >> f, '%s;' % self.cfunction_header(func) + for llfunc in self.functionslist: + print >> f, '%s;' % self.cfunction_header(llfunc) print >> f - # class declarations - if self.cclasses: - # keep original order, so don't iterate over the dict self.cclasses - for cdef in self.translator.annotator.getuserclassdefinitions(): - self.gen_cclass(self.cclasses[cdef.cls]) + # declaration of class structures + for llclass in self.classeslist: + self.gen_cclass(llclass) # function implementation print >> f, self.C_SEP print >> f - for func in self.translator.functions: - self.gen_cfunction(func) + for llfunc in self.functionslist: + self.gen_cfunction(llfunc) print >> f # entry point @@ -81,30 +75,36 @@ print >> f, self.C_INIT_FOOTER % info - def build_cclasses(self): + def build_llclasses(self): if not self.translator.annotator: return n = 0 for cdef in self.translator.annotator.getuserclassdefinitions(): cls = cdef.cls - self.cclasses[cls] = CClass( + assert cls not in self.llclasses, '%r duplicate' % (cls,) + llclass = LLClass( + typeset = self.typeset, name = '%s__%d' % (cls.__name__, n), cdef = cdef, ) + self.llclasses[cls] = llclass + self.classeslist.append(llclass) + self.functionslist += llclass.get_management_functions() n += 1 def build_llfunctions(self): n = 0 for func in self.translator.functions: assert func not in self.llfunctions, '%r duplicate' % (func,) - self.llfunctions[func] = LLFunction( + llfunc = LLFunction( typeset = self.typeset, name = '%s__%d' % (func.func_name, n), graph = self.translator.flowgraphs[func]) + self.llfunctions[func] = llfunc + self.functionslist.append(llfunc) n += 1 - def cfunction_header(self, func): - llfunc = self.llfunctions[func] + def cfunction_header(self, llfunc): llargs, rettype = llfunc.ll_header() l = ['%s %s' % (a.type, a.name) for a in llargs] l = l or ['void'] @@ -134,9 +134,8 @@ print >> f, '\t\treturn NULL;' print >> f, '\treturn %s(%s);' % (llfunc.name, ', '.join(l2)) - def gen_cfunction(self, func): + def gen_cfunction(self, llfunc): f = self.f - llfunc = self.llfunctions[func] # generate the body of the function body = list(llfunc.ll_body()) @@ -160,7 +159,7 @@ print >> f # print header - print >> f, self.cfunction_header(func) + print >> f, self.cfunction_header(llfunc) print >> f, '{' # collect and print all the local variables from the body @@ -201,49 +200,61 @@ print >> f print >> f, '}' - LL_ONLY_OPERATIONS = { - 'move': lambda x,y: '%s = %s;' % (y,x), - 'goto': lambda err: 'goto %s;' % err, - } - - def gen_cclass(self, cclass): + def gen_cclass(self, llclass): f = self.f - loc = { - 'name': cclass.name, - 'cdef': cclass.cdef, - 'cls': cclass.cdef.cls, - 'genc': self, - 'typeset': self.typeset, + cls = llclass.cdef.cls + info = { + 'module': cls.__module__, + 'basename': cls.__name__, + 'name': llclass.name, } - print >> f, self.C_CLASS % formatter.TemplateFormatter(loc) - def copy(self, srcrepr, srcvars, dstrepr, dstvars, errlabel): - # write the code to copy a value from src to dst. - if srcrepr == dstrepr: - for s, d in zip(srcvars, dstvars): - yield '%s = %s;' % (d, s) + # print the C struct declaration + print >> f, self.C_STRUCT_HEADER % info + for fld in llclass.fields: + for llvar in fld.llvars: + print >> f, '\t%s %s;' % (llvar.type, llvar.name) + print >> f, self.C_STRUCT_FOOTER % info + + # generate the deallocator function -- must special-case it; + # other functions are generated by LLClass.get_management_functions() + print >> f, self.C_DEALLOC_HEADER % info + for fld in llclass.fields: try: - sig = (dstrepr,) - llname, can_fail = self.typeset.lloperations['incref'][sig] + sig = (fld.hltype,) + llname, can_fail = self.typeset.lloperations['xdecref'][sig] except KeyError: - pass # no incref + continue # ignore fields with no xdecref operation + assert not can_fail + args = ['op->%s' % llvar.name for llvar in fld.llvars] + print >> f, '\t' + llname(*args) + print >> f, self.C_DEALLOC_FOOTER % info + + # generate the member list for the type object + 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: + t = 'T_OBJECT_EX' + elif fld.hltype is self.typeset.R_INT: + t = 'T_INT' else: - assert not can_fail - for codeline in llname(*dstvars).split('\n'): - yield codeline - else: - sig = (srcrepr, dstrepr) - llname, can_fail = self.typeset.lloperations['convert'][sig] - args = srcvars + dstvars - if can_fail: - args.append(errlabel) - for codeline in llname(*args).split('\n'): - yield codeline + continue # ignored + print >> f, '\t{"%s",\t%s,\toffsetof(%s_Object, %s)},' % ( + fld.name, t, llclass.name, fld.llvars[0].name) + print >> f, self.C_MEMBERLIST_FOOTER % info + + # declare and initialize the static PyTypeObject + print >> f, self.C_TYPEOBJECT % info # ____________________________________________________________ + LL_ONLY_OPERATIONS = { + 'move': lambda x,y: '%s = %s;' % (y,x), + 'goto': lambda err: 'goto %s;' % err, + } + C_HEADER = open(os.path.join(autopath.this_dir, 'genc.h')).read() - C_CLASS = open(os.path.join(autopath.this_dir, 'genc_class.h')).read() C_SEP = "/************************************************************/" @@ -266,4 +277,70 @@ C_INIT_FOOTER = '''}''' + C_STRUCT_HEADER = C_SEP + ''' +/*** Definition of class %(module)s.%(basename)s ***/ + +typedef struct { + PyObject_HEAD''' + + C_STRUCT_FOOTER = '''} %(name)s_Object; +''' + + C_DEALLOC_HEADER = '''static void %(name)s_dealloc(%(name)s_Object* op) +{''' + + C_DEALLOC_FOOTER = ''' op->ob_type->tp_free((PyObject*) op); +} +''' + + C_MEMBERLIST_HEADER = '''static PyMemberDef %(name)s_memberlist[] = {''' + + C_MEMBERLIST_FOOTER = ''' {NULL} /* Sentinel */ +}; +''' + + C_TYPEOBJECT = '''static PyTypeObject %(name)s_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "%(name)s", + sizeof(%(name)s_Object), + 0, + (destructor)%(name)s_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/* tp_flags */ + 0, /* tp_doc */ + 0, /* XXX need GC */ /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + %(name)s_memberlist, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* XXX call %(name)s_new() */ /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_Del, /* tp_free */ +}; +''' + # ____________________________________________________________ Deleted: /pypy/branch/pypy-genc/translator/genc_class.h ============================================================================== --- /pypy/branch/pypy-genc/translator/genc_class.h Mon Sep 6 14:47:06 2004 +++ (empty file) @@ -1,166 +0,0 @@ -/************************************************************/ -/* Definition of class %(cls.__module__)s.%(cls.__name__)s */ - -typedef struct { - PyObject_HEAD -%( -# write here the definition of all fields -# (see formatter.py for the definition of this small templating language) -import re - -mainletters = [c.lower() for c in cls.__name__ if 'A' <= c <= 'Z'] -field_prefix = ''.join(mainletters or ['f']) -pyobj_fields = [] -fields = [] - -def llfield(field, hltype): - lltypes = typeset.represent(hltype) - for i in range(len(lltypes)): - if i: - suffix = '_%d' % i - else: - suffix = '' - yield field + suffix - -for attr, s_value in cdef.attrs.items(): - hltype = typeset.gethltype(s_value) - lltypes = typeset.represent(hltype) - # to avoid name collisions between the 2nd lltype of a field called xyz - # and another field whose name is exactly xyz_1, forbid field names ending - # in '_\d+'. - field = '%s_%s' % (field_prefix, attr) - if re.search(r'_\d+$', attr): - field += '_' - fields.append((attr, field, hltype)) - if hltype is typeset.R_OBJECT: - pyobj_fields.append(field) - for lltype, llname in zip(lltypes, llfield(field, hltype)): - print '\t%s %s;' % (lltype, llname) -# ---- -)s} %(name)s_Object; - -staticforward PyTypeObject %(name)s_Type; - -static PyObject* %(name)s_new(void) -{ - %(name)s_Object* op = PyObject_GC_New(%(name)s_Object, &%(name)s_Type); - if (op == NULL) goto err; -%( -# initialize fields from the PyObject* fields to NULL -for field in pyobj_fields: - print '\top->%s = NULL;' % field -# initialize fields using the class attributes as a default -for attr, field, hltype in fields: - if hasattr(cls, attr): - r = typeset.constant_representation(getattr(cls, attr)) - for codeline in genc.copy(r, [], - hltype, ['op->%s' % f for f in llfield(field, hltype)], - 'err'): - print '\t' + codeline -# ---- -)s PyObject_GC_Track(op); - /* XXX call __init__ */ - return (PyObject*) op; - - err: - Py_XDECREF(op); - return NULL; -} - -static void %(name)s_dealloc(%(name)s_Object* op) -{ - PyObject_GC_UnTrack(op); -%( -# release PyObject* fields XXX should be more general -for field in pyobj_fields: - print '\tPy_XDECREF(op->%s);' % field -# ---- -)s op->ob_type->tp_free((PyObject *)op); -} - -static int %(name)s_traverse(%(name)s_Object* op, visitproc visit, void* arg) -{ -%( -# traverse PyObject* fields -for field in pyobj_fields: - print '\tif (op->%s != NULL) {' % field - print '\t\tint err = visit(op->%s, arg);' % field - print '\t\tif (err) return err;' - print '\t}' -# ---- -)s return 0; -} - -static int %(name)s_clear(%(name)s_Object* op) -{ -%( -# clear PyObject* fields -for field in pyobj_fields: - print '\tif (op->%s != NULL) {' % field - print '\t\tPyObject* tmp = op->%s;' % field - print '\t\top->%s = NULL;' % field - print '\t\tPy_DECREF(tmp);' - print '\t}' -# ---- -)s return 0; -} - -static PyMemberDef %(name)s_memberlist[] = { -%( -# write member definitions for member with well-known types XXX generalize -for attr, field, hltype in fields: - if hltype is typeset.R_OBJECT: - t = 'T_OBJECT_EX' - elif hltype is typeset.R_INT: - t = 'T_INT' - else: - continue # ignored - print '\t{"%s",\t%s,\toffsetof(%s_Object, %s)},' % ( - attr, t, name, field) -# ---- -)s {NULL} /* Sentinel */ -}; - -statichere PyTypeObject %(name)s_Type = { - PyObject_HEAD_INIT(&PyType_Type) - 0, - "%(name)s", - sizeof(%(name)s_Object), - 0, - (destructor)%(name)s_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - PyObject_GenericSetAttr, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)%(name)s_traverse, /* tp_traverse */ - (inquiry)%(name)s_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - %(name)s_memberlist, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - PyType_GenericAlloc, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - PyObject_GC_Del, /* tp_free */ -}; Modified: pypy/branch/pypy-genc/translator/genc_typeset.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc_typeset.py (original) +++ pypy/branch/pypy-genc/translator/genc_typeset.py Mon Sep 6 14:47:06 2004 @@ -189,14 +189,21 @@ sig = (r,) + sig ops[sig] = ll elif (isinstance(value, (type, types.ClassType)) and - value in self.genc.cclasses): + value in self.genc.llclasses): # a user-defined class ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {}) # XXX do __init__ sig = (r, self.R_OBJECT) def writer(res, err): return 'INSTANTIATE(%s, %s, %s)' % ( - self.genc.cclasses[value].name, res, err) + self.genc.llclasses[value].name, res, err) + ops[sig] = writer, True + # OP_ALLOC_INSTANCE used by the constructor function xxx_new() + ops = self.lloperations.setdefault('OP_ALLOC_INSTANCE', {}) + sig = (r, self.R_OBJECT) + def writer(res, err): + return 'ALLOC_INSTANCE(%s, %s, %s)' % ( + self.genc.llclasses[value].name, res, err) ops[sig] = writer, True else: print "// XXX not implemented: constant", key Modified: pypy/branch/pypy-genc/translator/typer.py ============================================================================== --- pypy/branch/pypy-genc/translator/typer.py (original) +++ pypy/branch/pypy-genc/translator/typer.py Mon Sep 6 14:47:06 2004 @@ -70,13 +70,10 @@ # ____________________________________________________________ -class LLFunction: - "A low-level version of a function from a control flow graph." +class LLTyper: + "Base class for type-oriented low-level generators." - def __init__(self, typeset, name, graph): - remove_direct_loops(graph) - self.name = name - self.graph = graph + def __init__(self, typeset): self.gethltype = typeset.gethltype self.represent = typeset.represent self.lloperations = typeset.lloperations @@ -85,6 +82,34 @@ self.hltypes = {} self.llreprs = {} + 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 + +# ____________________________________________________________ + +class LLFunction(LLTyper): + "A low-level version of a function from a control flow graph." + + def __init__(self, typeset, name, graph): + LLTyper.__init__(self, typeset) + remove_direct_loops(graph) + self.name = name + self.graph = graph + def ll_header(self): """ Get the low-level representation of the header. @@ -182,23 +207,6 @@ # __________ 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: From arigo at codespeak.net Mon Sep 6 14:51:17 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 6 Sep 2004 14:51:17 +0200 (MEST) Subject: [pypy-svn] r6297 - pypy/branch/pypy-genc/translator Message-ID: <20040906125117.702A85A5D1@thoth.codespeak.net> Author: arigo Date: Mon Sep 6 14:51:16 2004 New Revision: 6297 Modified: pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/genc.py pypy/branch/pypy-genc/translator/genc_typeset.py pypy/branch/pypy-genc/translator/typer.py Log: * Reorganization of reference counters code. * Support for tuples. Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Mon Sep 6 14:51:16 2004 @@ -81,9 +81,6 @@ #define return_o(o) return o; #define returnerr_i() return -1; #define returnerr_o() return NULL; -#define incref_o(o) Py_INCREF(o); -#define decref_o(o) Py_DECREF(o); -#define xdecref_o(o) Py_XDECREF(o); #define OP_EXCEPTION_ov(x) /* XXX exceptions not implemented */ Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Mon Sep 6 14:51:16 2004 @@ -6,7 +6,7 @@ import autopath, os from pypy.translator.typer import LLFunction, LLOp, LLConst from pypy.translator.classtyper import LLClass -from pypy.translator import genc_typeset +from pypy.translator.genc_typeset import CTypeSet, consts_used # ____________________________________________________________ @@ -27,7 +27,7 @@ pass else: bindings = {} - self.typeset = genc_typeset.CTypeSet(self, bindings) + self.typeset = CTypeSet(self, bindings) self.initializationcode = [] self.llclasses = {}; self.classeslist = [] self.llfunctions = {}; self.functionslist = [] @@ -145,7 +145,7 @@ to_declare = [] for line in body: if isinstance(line, LLOp): - for a in line.args + getattr(line.name, 'known_answer', []): + for a in line.args + consts_used(line.name): if isinstance(a, LLConst) and a.to_declare: to_declare.append(a) if a.initexpr: @@ -181,18 +181,20 @@ # 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) - # line.name is actually not a string, but a callable that - # generates the C code -- unless it is a special low-level-only - # operation that comes from typer.py. - writer = line.name - if isinstance(writer, str): - writer = self.LL_ONLY_OPERATIONS[writer] - code = writer(*args) - for codeline in code.split('\n'): - print >> f, '\t' + codeline + writer = line.name + if isinstance(writer, str): # special low-level operation + meth = getattr(self, 'lloperation_' + writer) + code = meth(line) + else: + # line.name is actually not a string, but a callable that + # generates the C code. + args = [a.name for a in line.args] + if line.errtarget: + args.append(line.errtarget) + code = writer(*args) + if code: + for codeline in code.split('\n'): + print >> f, '\t' + codeline elif line: # label print >> f, ' %s:' % line @@ -200,6 +202,46 @@ print >> f print >> f, '}' + def lloperation_move(self, line): + vx, vy = line.args + return '%s = %s;' % (vy.name, vx.name) + + def lloperation_goto(self, line): + return 'goto %s;' % line.errtarget + + def lloperation_copy(self, line): + ls = [] + assert len(line.args) % 2 == 0 + half = len(line.args) // 2 + for i in range(half): + vx = line.args[i] + vy = line.args[half+i] + ls.append('%s = %s;' % (vy.name, vx.name)) + if vy.type == 'PyObject*': + ls.append('Py_INCREF(%s);' % vy.name) + return ' '.join(ls) + + def lloperation_incref(self, line): + ls = [] + for a in line.args: + if a.type == 'PyObject*': + ls.append('Py_INCREF(%s);' % a.name) + return ' '.join(ls) + + def lloperation_decref(self, line): + ls = [] + for a in line.args: + if a.type == 'PyObject*': + ls.append('Py_DECREF(%s);' % a.name) + return ' '.join(ls) + + def lloperation_xdecref(self, line): + ls = [] + for a in line.args: + if a.type == 'PyObject*': + ls.append('Py_XDECREF(%s);' % a.name) + return ' '.join(ls) + def gen_cclass(self, llclass): f = self.f cls = llclass.cdef.cls @@ -249,11 +291,6 @@ # ____________________________________________________________ - LL_ONLY_OPERATIONS = { - 'move': lambda x,y: '%s = %s;' % (y,x), - 'goto': lambda err: 'goto %s;' % err, - } - C_HEADER = open(os.path.join(autopath.this_dir, 'genc.h')).read() C_SEP = "/************************************************************/" Modified: pypy/branch/pypy-genc/translator/genc_typeset.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc_typeset.py (original) +++ pypy/branch/pypy-genc/translator/genc_typeset.py Mon Sep 6 14:51:16 2004 @@ -36,6 +36,7 @@ self.genc = genc self.bindings = bindings self.r_constants = {} + self.r_tuples = {} self.lloperations = {'convert': {}, 'release': {}} self.parse_operation_templates() @@ -54,6 +55,9 @@ return self.R_INT if isinstance(var, annmodel.SomeImpossibleValue): return self.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) # fall-back return self.R_OBJECT if isinstance(var, Constant): @@ -82,6 +86,14 @@ return '\n'.join(ls) opnewlist[sig] = writer, True 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] = 'copy', False + return True # retry if opname == 'OP_SIMPLE_CALL' and hltypes: opsimplecall = self.lloperations.setdefault('OP_SIMPLE_CALL', {}) sig = (self.R_OBJECT,) * len(hltypes) @@ -109,6 +121,11 @@ 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([]) r.const = value @@ -218,6 +235,62 @@ to_declare = bool(initexpr)) writer.known_answer = [llconst] + 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 + rt = CRepr(impl) + if items_r: + rt.err_check = items_r[0].err_check + self.r_tuples[items_r] = rt + # can convert the tuple to a PyTupleObject only if each item can be + conv = self.lloperations['convert'] + also_using = [] + for r in items_r: + if r == self.R_OBJECT: + continue + if (r, self.R_OBJECT) not in conv: + break + llname, can_fail = conv[r, self.R_OBJECT] + also_using.append(llname) + else: + def writer(*args): + content = args[:-2] + result = args[-2] + err = args[-1] + ls = ['{', + 'PyObject* o;', + 'if (!(%s = PyTuple_New(%d))) goto %s;' % ( + result, len(items_r), err)] + j = 0 + for i in range(len(items_r)): + r = items_r[i] + if r == self.R_OBJECT: + o = content[j] + j = j+1 + ls.append('Py_INCREF(%s);' % o) + else: + o = 'o' + llname, can_fail = conv[r, self.R_OBJECT] + k = len(r.impl) + args = content[j:j+k] + (o,) + j = j+k + if can_fail: + args += (err,) + ls.append(llname(*args)) + ls.append('PyTuple_SET_ITEM(%s, %d, %s);' % + (result, i, o)) + return '\n'.join(ls).replace('\n', '\n\t') + '\n}' + writer.also_using = also_using + conv[rt, self.R_OBJECT] = writer, True + return rt + def parse_operation_templates(self): # parse the genc.h header to figure out which macros are implemented codes = ''.join(self.REPR_BY_CODE.keys()) @@ -258,3 +331,12 @@ c = '_%02x' % ord(c) l.append(c) return ''.join(l) + +def consts_used(writer): + "Enumerate the global constants that a writer function uses." + result = getattr(writer, 'known_answer', []) + if hasattr(writer, 'also_using'): + result = list(result) + for w in writer.also_using: + result += consts_used(w) + return result Modified: pypy/branch/pypy-genc/translator/typer.py ============================================================================== --- pypy/branch/pypy-genc/translator/typer.py (original) +++ pypy/branch/pypy-genc/translator/typer.py Mon Sep 6 14:51:16 2004 @@ -49,15 +49,17 @@ # 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 # 'returnerr' z : same, but return an error code instead of z's content # # 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 +# 'goto' : fails unconditionally (i.e. jump to errlabel) +# 'move' x y : raw copy of the LLVar x to the LLVar y +# 'copy' x1 x2.. y1 y2...: raw copy x1 to y1, x2 to y2, etc. and incref +# 'incref' x y...: raw incref of the LLVars x, y, etc. +# 'decref' x y...: raw decref of the LLVars x, y, etc. +# 'xdecref' x y...: raw xdecref of the LLVars x, y, etc. # # def typingerror(self, opname, hltypes): # Called when no match is found in lloperations. This function must @@ -154,13 +156,7 @@ # 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]) + yield LLOp('incref', self.llreprs[v]) # generate the body of each block for block in allblocks: @@ -208,13 +204,7 @@ # __________ Type checking and conversion routines __________ 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]) + llop = LLOp('decref', self.llreprs[v]) # make a new node for the release tree self.to_release = ReleaseNode(v, llop, self.to_release) @@ -263,6 +253,13 @@ raise TypingError([opname] + args_t) # check if the answer has an existing well-known answer if resulttype is not None: # if we are allowed to patch self.llreprs + if llname == 'copy': + # patch self.llreprs and we're done + llrepr = [] + for v in args: + llrepr += self.llreprs[v] + self.llreprs[result] = llrepr + return llrepr = self.knownanswer(llname) if llrepr is not None: # patch self.llreprs and we're done @@ -305,38 +302,36 @@ # copy the data, performing any necessary conversion # See also remove_direct_loops() for why we don't worry about # the order of the operations - escapingreferences = {} + current_refcnt = {} + needed_refcnt = {} 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])) - escapingreferences.setdefault(v, 0) - escapingreferences[v] += 1 + needed_refcnt.setdefault(v, 0) + needed_refcnt[v] += 1 else: self.operation('convert', [v], w) - escapingreferences.setdefault(w, 0) - escapingreferences[w] += 1 - # then release all the variables that go out of scope minus the - # ones that are carried over to the next block + needed_refcnt.setdefault(w, 0) + needed_refcnt[w] += 1 + # list all variables that go out of scope: as needing by default + # no reference for node in self.to_release.getbranch(): - v = node.var - escaping = escapingreferences.get(v, 0) - if escaping == 0: # no reference escapes the current scope - self.blockops.append(node.release_operation) - else: - # sometimes, a value is duplicated, so that several - # references exit to the next scope. In this case we need - # to generate increfs. - while escaping > 1: - sig = (self.hltypes[v],) - try: - llname, can_fail = self.lloperations['incref'][sig] - except KeyError: - break # no incref operation - assert not can_fail - self.blockops.append(LLOp(llname, self.llreprs[v])) - escaping -= 1 + current_refcnt[node.var] = 1 + needed_refcnt.setdefault(node.var, 0) + # now adjust all reference counters: first increfs, then decrefs + # (in case a variable to decref points to the same objects than + # another variable to incref). + for v, needed in needed_refcnt.items(): + current_refcnt.setdefault(v, 0) + while current_refcnt[v] < needed: + self.blockops.append(LLOp('incref', self.llreprs[v])) + current_refcnt[v] += 1 + for v, needed in needed_refcnt.items(): + while current_refcnt[v] > needed: + self.blockops.append(LLOp('decref', self.llreprs[v])) + current_refcnt[v] -= 1 # finally jump to the target block self.blockops.append(LLOp('goto', [], self.blockname[exit.target])) finally: From arigo at codespeak.net Mon Sep 6 15:53:14 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 6 Sep 2004 15:53:14 +0200 (MEST) Subject: [pypy-svn] r6299 - in pypy/branch/pypy-genc/translator: . test Message-ID: <20040906135314.970715A5D1@thoth.codespeak.net> Author: arigo Date: Mon Sep 6 15:53:14 2004 New Revision: 6299 Modified: pypy/branch/pypy-genc/translator/test/test_ctrans.py pypy/branch/pypy-genc/translator/typer.py Log: Some more tweaking reference counters. The resulting C code looks reasonable now -- it tries hard to minimize the Py_{INC,DEC}REF(). Moved a couple of private tests to test_ctrans.py. I'll try to figure out later why you cannot call two test functions 'fn' without the two Translator instances getting confused. --This line, and those below, will be ignored-- M translator/test/test_ctrans.py M translator/typer.py Modified: pypy/branch/pypy-genc/translator/test/test_ctrans.py ============================================================================== --- pypy/branch/pypy-genc/translator/test/test_ctrans.py (original) +++ pypy/branch/pypy-genc/translator/test/test_ctrans.py Mon Sep 6 15:53:14 2004 @@ -131,5 +131,24 @@ call_five = self.getcompiled(snippet.call_five) self.assertEquals(call_five(), [5]) + def test_class_defaultattr(self): + class K: + n = "hello" + def class_defaultattr(): + k = K() + k.n += " world" + return k.n + fn = self.getcompiled(class_defaultattr) + self.assertEquals(fn(), "hello world") + + def test_tuple_repr(self): + def tuple_repr(x=int, y=object): + z = x, y + while x: + x = x-1 + return z + fn = self.getcompiled(tuple_repr) + self.assertEquals(fn(6,'a'), (6,'a')) + if __name__ == '__main__': testit.main() Modified: pypy/branch/pypy-genc/translator/typer.py ============================================================================== --- pypy/branch/pypy-genc/translator/typer.py (original) +++ pypy/branch/pypy-genc/translator/typer.py Mon Sep 6 15:53:14 2004 @@ -220,8 +220,7 @@ if to == hltype: yield frm - def operation(self, opname, args, result=None, errlabel=None, - resulttype=None): + 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: @@ -230,7 +229,7 @@ directions = [self.convert_from] * len(args) # append the hltype of the result if result: - self.makevar(result, resulttype) + self.makevar(result) args_t.append(self.hltypes[result]) directions.append(self.convert_to) # enumerate possible signatures until we get a match @@ -246,13 +245,15 @@ # correctly extend 'lloperations'. if retry: try: - self.operation(opname, args, result, errlabel, resulttype) + self.operation(opname, args, result, errlabel) return except RuntimeError: # infinite recursion pass raise TypingError([opname] + args_t) - # check if the answer has an existing well-known answer - if resulttype is not None: # if we are allowed to patch self.llreprs + # check for some operations that have an existing well-known answer + # that doesn't involve side-effects, so that we can just provide + # this answer and not generate any LLOp. + if result: if llname == 'copy': # patch self.llreprs and we're done llrepr = [] @@ -270,7 +271,8 @@ for v, v_t, s_t in zip(args, args_t, sig): if v_t != s_t: tmp = Variable() - self.operation('convert', [v], tmp, resulttype=s_t) + 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 @@ -299,39 +301,44 @@ # generate the exit.args -> target.inputargs copying operations to_release_copy = self.to_release try: - # copy the data, performing any necessary conversion + # convert the exit.args to the type expected by the target.inputargs + exitargs = [] + for v, w in zip(exit.args, exit.target.inputargs): + self.makevar(v) + if self.hltypes[v] != self.hltypes[w]: + tmp = Variable() + self.makevar(tmp, hltype=self.hltypes[w]) + self.operation('convert', [v], tmp) + v = tmp + exitargs.append(v) + # move the data from exit.args to target.inputargs # See also remove_direct_loops() for why we don't worry about - # the order of the operations + # the order of the move operations current_refcnt = {} needed_refcnt = {} - 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])) - needed_refcnt.setdefault(v, 0) - needed_refcnt[v] += 1 - else: - self.operation('convert', [v], w) - needed_refcnt.setdefault(w, 0) - needed_refcnt[w] += 1 - # list all variables that go out of scope: as needing by default - # no reference + for v, w in zip(exitargs, exit.target.inputargs): + for x, y in zip(self.llreprs[v], self.llreprs[w]): + self.blockops.append(LLOp('move', [x, y])) + needed_refcnt.setdefault(x, 0) + needed_refcnt[x] += 1 + # list all variables that go out of scope: by default + # they need no reference, but have one reference. for node in self.to_release.getbranch(): - current_refcnt[node.var] = 1 - needed_refcnt.setdefault(node.var, 0) + for x in self.llreprs[node.var]: + current_refcnt[x] = 1 + needed_refcnt.setdefault(x, 0) # now adjust all reference counters: first increfs, then decrefs # (in case a variable to decref points to the same objects than # another variable to incref). - for v, needed in needed_refcnt.items(): - current_refcnt.setdefault(v, 0) - while current_refcnt[v] < needed: - self.blockops.append(LLOp('incref', self.llreprs[v])) - current_refcnt[v] += 1 - for v, needed in needed_refcnt.items(): - while current_refcnt[v] > needed: - self.blockops.append(LLOp('decref', self.llreprs[v])) - current_refcnt[v] -= 1 + for x, needed in needed_refcnt.items(): + current_refcnt.setdefault(x, 0) + while current_refcnt[x] < needed: + self.blockops.append(LLOp('incref', [x])) + current_refcnt[x] += 1 + for x, needed in needed_refcnt.items(): + while current_refcnt[x] > needed: + self.blockops.append(LLOp('decref', [x])) + current_refcnt[x] -= 1 # finally jump to the target block self.blockops.append(LLOp('goto', [], self.blockname[exit.target])) finally: From arigo at codespeak.net Mon Sep 6 15:55:25 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 6 Sep 2004 15:55:25 +0200 (MEST) Subject: [pypy-svn] r6301 - pypy/trunk/src/pypy Message-ID: <20040906135525.6B4FF5AE8F@thoth.codespeak.net> Author: arigo Date: Mon Sep 6 15:55:24 2004 New Revision: 6301 Removed: pypy/trunk/src/pypy/ Log: Delete this, to put the branch back here. From arigo at codespeak.net Mon Sep 6 15:56:10 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 6 Sep 2004 15:56:10 +0200 (MEST) Subject: [pypy-svn] r6304 - in pypy: branch/pypy-genc trunk/src/pypy Message-ID: <20040906135610.C311F5AE8F@thoth.codespeak.net> Author: arigo Date: Mon Sep 6 15:56:10 2004 New Revision: 6304 Added: pypy/trunk/src/pypy/ - copied from r6302, pypy/branch/pypy-genc/ Removed: pypy/branch/pypy-genc/ Log: Moved the branch back into the trunk. From arigo at codespeak.net Mon Sep 6 15:56:10 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 6 Sep 2004 15:56:10 +0200 (MEST) Subject: [pypy-svn] r6304 - in pypy: branch/pypy-genc trunk/src/pypy Message-ID: <20040906135610.E5D465AEA3@thoth.codespeak.net> Author: arigo Date: Mon Sep 6 15:56:10 2004 New Revision: 6304 Added: pypy/trunk/src/pypy/ - copied from r6302, pypy/branch/pypy-genc/ Removed: pypy/branch/pypy-genc/ Log: Moved the branch back into the trunk. From arigo at codespeak.net Wed Sep 8 15:30:29 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Wed, 8 Sep 2004 15:30:29 +0200 (MEST) Subject: [pypy-svn] r6359 - in pypy/trunk/src/pypy: objspace/flow translator Message-ID: <20040908133029.811155A0CB@thoth.codespeak.net> Author: arigo Date: Wed Sep 8 15:30:28 2004 New Revision: 6359 Modified: pypy/trunk/src/pypy/objspace/flow/model.py pypy/trunk/src/pypy/translator/transform.py Log: Bug fix in the transformations. Actually caused by list objects that were shared between the 'args' attributes of several Link objects, so that modifying the list of args of one Link changed another one unexpectedly. Modified: pypy/trunk/src/pypy/objspace/flow/model.py ============================================================================== --- pypy/trunk/src/pypy/objspace/flow/model.py (original) +++ pypy/trunk/src/pypy/objspace/flow/model.py Wed Sep 8 15:30:28 2004 @@ -35,7 +35,7 @@ class Link: def __init__(self, args, target, exitcase=None): assert len(args) == len(target.inputargs), "output args mismatch" - self.args = args # mixed list of var/const + self.args = list(args) # mixed list of var/const self.target = target # block self.exitcase = exitcase # this is a concrete value self.prevblock = None # the block this Link is an exit of @@ -44,10 +44,10 @@ isstartblock = False def __init__(self, inputargs): - self.inputargs = inputargs # mixed list of variable/const - self.operations = [] # list of SpaceOperation(s) - self.exitswitch = None # variable - self.exits = [] # list of Link(s) + self.inputargs = list(inputargs) # mixed list of variable/const + self.operations = [] # list of SpaceOperation(s) + self.exitswitch = None # variable + self.exits = [] # list of Link(s) def getvariables(self): "Return all variables mentionned in this Block." Modified: pypy/trunk/src/pypy/translator/transform.py ============================================================================== --- pypy/trunk/src/pypy/translator/transform.py (original) +++ pypy/trunk/src/pypy/translator/transform.py Wed Sep 8 15:30:28 2004 @@ -158,9 +158,11 @@ # Otherwise the link.args get out of sync with the # link.target.inputargs. for link in block.exits: + assert len(link.args) == len(link.target.inputargs) for i in range(len(link.args)-1, -1, -1): if link.target.inputargs[i] not in read_vars: del link.args[i] + # the above assert would fail here for block in self.annotated: # look for input variables never used From arigo at codespeak.net Wed Sep 8 15:55:54 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Wed, 8 Sep 2004 15:55:54 +0200 (MEST) Subject: [pypy-svn] r6360 - in pypy/trunk/src/pypy/translator: . test Message-ID: <20040908135554.6D4BB5A0CB@thoth.codespeak.net> Author: arigo Date: Wed Sep 8 15:55:53 2004 New Revision: 6360 Added: pypy/trunk/src/pypy/translator/genc_op.py Modified: pypy/trunk/src/pypy/translator/genc.h pypy/trunk/src/pypy/translator/genc.py pypy/trunk/src/pypy/translator/genc_typeset.py pypy/trunk/src/pypy/translator/test/test_ctrans.py pypy/trunk/src/pypy/translator/translator.py pypy/trunk/src/pypy/translator/typer.py Log: Large check-in. The main addition is a set of classes in genc_op.py that correspond to the individual kinds of operations, and how to write them in C. Each of these classes replace a 'def writer(...)' function. This is much cleaner because the 'writer' functions were increasingly populated with optional attributes to control the operation. Less important changes: * Automatic chaining of conversions. Was needed to cleanly convert tuples item by item. * Avoid generating two .c modules with exactly the same name during the same session, because we cannot reload the .so/.pyd extension. * Python functions can return and be called with a return value spread over several LLVars (e.g. when returning a tuple). * Cleaned up the generation of the entry_point() in the C module. Modified: pypy/trunk/src/pypy/translator/genc.h ============================================================================== --- pypy/trunk/src/pypy/translator/genc.h (original) +++ pypy/trunk/src/pypy/translator/genc.h Wed Sep 8 15:55:53 2004 @@ -51,6 +51,13 @@ #define OP_DELATTR_oov(x,y,err) if ((PyObject_SetAttr(x,y,NULL))<0)goto err; #define OP_NEWSLICE_oooo(x,y,z,r,err) if (!(r=PySlice_New(x,y,z))) goto err; +/* temporary hack */ +#define OP_GETITEM_ooi(x,y,r,err) { \ + PyObject* o = PyObject_GetItem(x,y); \ + if (!o) goto err; \ + if ((r=PyInt_AsLong(o)) == -1 && PyErr_Occurred()) goto err; \ +} + /*** conversions ***/ @@ -58,9 +65,9 @@ #define convert_so(c,l,r,err) if (!(r=PyString_FromStringAndSize(c,l)))goto err; #define convert_vo(r) r = Py_None; Py_INCREF(r); -#define convert_oi(x,r,err) if ((r=PyInt_AsLong(x)) == -1 \ - && PyErr_Occurred()) goto err; - +/*#define convert_oi(x,r,err) if ((r=PyInt_AsLong(x)) == -1 \ + * && PyErr_Occurred()) goto err; + * -- should be done differently */ /*** tests ***/ @@ -77,11 +84,6 @@ /*** misc ***/ -#define return_i(n) return n; -#define return_o(o) return o; -#define returnerr_i() return -1; -#define returnerr_o() return NULL; - #define OP_EXCEPTION_ov(x) /* XXX exceptions not implemented */ #define OP_ALLOC_AND_SET_ioo(l,o,r,err) { \ Modified: pypy/trunk/src/pypy/translator/genc.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc.py (original) +++ pypy/trunk/src/pypy/translator/genc.py Wed Sep 8 15:55:53 2004 @@ -4,27 +4,36 @@ """ from __future__ import generators import autopath, os -from pypy.translator.typer import LLFunction, LLOp, LLConst +from pypy.objspace.flow.model import Variable, Constant, SpaceOperation +from pypy.objspace.flow.model import FunctionGraph, Block, Link +from pypy.translator.typer import LLFunction, LLOp, LLVar, LLConst from pypy.translator.classtyper import LLClass -from pypy.translator.genc_typeset import CTypeSet, consts_used +from pypy.translator.genc_typeset import CTypeSet +from pypy.translator.genc_op import ERROR_RETVAL # ____________________________________________________________ +def uniquemodulename(name, SEEN={}): + # never reuse the same module name within a Python session! + i = 0 + while True: + i += 1 + result = '%s_%d' % (name, i) + if result not in SEEN: + SEEN[result] = True + return result + + class GenC: + MODNAMES = {} def __init__(self, f, translator, modname=None): self.f = f self.translator = translator - self.modname = modname or translator.functions[0].__name__ + self.modname = (modname or + uniquemodulename(translator.functions[0].__name__)) if translator.annotator: bindings = translator.annotator.bindings.copy() - # for simplicity, 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(self, bindings) @@ -33,6 +42,7 @@ self.llfunctions = {}; self.functionslist = [] self.build_llclasses() self.build_llfunctions() + self.build_llentrypoint() self.gen_source() def gen_source(self): @@ -64,7 +74,7 @@ # entry point print >> f, self.C_SEP print >> f, self.C_ENTRYPOINT_HEADER % info - self.gen_entrypoint(self.translator.functions[0]) + self.gen_entrypoint() print >> f, self.C_ENTRYPOINT_FOOTER % info # footer @@ -104,18 +114,49 @@ self.functionslist.append(llfunc) n += 1 + def build_llentrypoint(self): + # create a LLFunc that calls the entry point function and returns + # whatever it returns, but converted to PyObject*. + main = self.translator.functions[0] + llmain = self.llfunctions[main] + inputargs = llmain.graph.getargs() + b = Block(inputargs) + v1 = Variable() + b.operations.append(SpaceOperation('simple_call', + [Constant(main)] + inputargs, + v1)) + # finally, return v1 + graph = FunctionGraph('entry_point', b) + b.closeblock(Link([v1], graph.returnblock)) + llfunc = LLFunction(self.typeset, graph.name, graph) + self.functionslist.append(llfunc) + + def get_llfunc_header(self, llfunc): + llargs, llret = llfunc.ll_header() + if len(llret) == 0: + retlltype = None + elif len(llret) == 1: + retlltype = llret[0].type + else: + # if there is more than one return LLVar, only the first one is + # returned and the other ones are returned via ptr output args + retlltype = llret[0].type + llargs += [LLVar(a.type+'*', 'output_'+a.name) for a in llret[1:]] + return llargs, retlltype + def cfunction_header(self, llfunc): - llargs, rettype = llfunc.ll_header() + llargs, rettype = self.get_llfunc_header(llfunc) l = ['%s %s' % (a.type, a.name) for a in llargs] l = l or ['void'] return 'static %s %s(%s)' % (rettype or 'int', llfunc.name, ', '.join(l)) - def gen_entrypoint(self, func): + def gen_entrypoint(self): f = self.f - llfunc = self.llfunctions[func] - llargs, rettype = llfunc.ll_header() + llfunc = self.functionslist[-1] + llargs, rettype = self.get_llfunc_header(llfunc) + assert llfunc.name == 'entry_point', llfunc.name assert rettype == 'PyObject*', rettype l = [] l2 = [] @@ -132,20 +173,22 @@ l.insert(0, '"' + ''.join(formatstr) + '"') print >> f, '\tif (!PyArg_ParseTuple(args, %s))' % ', '.join(l) print >> f, '\t\treturn NULL;' - print >> f, '\treturn %s(%s);' % (llfunc.name, ', '.join(l2)) + print >> f, '\treturn entry_point(%s);' % (', '.join(l2)) def gen_cfunction(self, llfunc): f = self.f # generate the body of the function - body = list(llfunc.ll_body()) + llargs, rettype = self.get_llfunc_header(llfunc) + error_retval = LLConst(rettype, ERROR_RETVAL[rettype]) + body = list(llfunc.ll_body([error_retval])) # print the declaration of the new global constants needed by # the current function to_declare = [] for line in body: if isinstance(line, LLOp): - for a in line.args + consts_used(line.name): + for a in line.using(): if isinstance(a, LLConst) and a.to_declare: to_declare.append(a) if a.initexpr: @@ -163,11 +206,10 @@ print >> f, '{' # collect and print all the local variables from the body - llargs, rettype = llfunc.ll_header() - lllocals = llargs[:] + lllocals = [] for line in body: if isinstance(line, LLOp): - lllocals += line.args + lllocals += line.using() seen = {} for a in llargs: seen[a] = True @@ -181,67 +223,16 @@ # print the body for line in body: if isinstance(line, LLOp): - writer = line.name - if isinstance(writer, str): # special low-level operation - meth = getattr(self, 'lloperation_' + writer) - code = meth(line) - else: - # line.name is actually not a string, but a callable that - # generates the C code. - args = [a.name for a in line.args] - if line.errtarget: - args.append(line.errtarget) - code = writer(*args) + code = line.write() if code: for codeline in code.split('\n'): print >> f, '\t' + codeline - elif line: # label print >> f, ' %s:' % line else: # empty line print >> f print >> f, '}' - def lloperation_move(self, line): - vx, vy = line.args - return '%s = %s;' % (vy.name, vx.name) - - def lloperation_goto(self, line): - return 'goto %s;' % line.errtarget - - def lloperation_copy(self, line): - ls = [] - assert len(line.args) % 2 == 0 - half = len(line.args) // 2 - for i in range(half): - vx = line.args[i] - vy = line.args[half+i] - ls.append('%s = %s;' % (vy.name, vx.name)) - if vy.type == 'PyObject*': - ls.append('Py_INCREF(%s);' % vy.name) - return ' '.join(ls) - - def lloperation_incref(self, line): - ls = [] - for a in line.args: - if a.type == 'PyObject*': - ls.append('Py_INCREF(%s);' % a.name) - return ' '.join(ls) - - def lloperation_decref(self, line): - ls = [] - for a in line.args: - if a.type == 'PyObject*': - ls.append('Py_DECREF(%s);' % a.name) - return ' '.join(ls) - - def lloperation_xdecref(self, line): - ls = [] - for a in line.args: - if a.type == 'PyObject*': - ls.append('Py_XDECREF(%s);' % a.name) - return ' '.join(ls) - def gen_cclass(self, llclass): f = self.f cls = llclass.cdef.cls Added: pypy/trunk/src/pypy/translator/genc_op.py ============================================================================== --- (empty file) +++ pypy/trunk/src/pypy/translator/genc_op.py Wed Sep 8 15:55:53 2004 @@ -0,0 +1,244 @@ +""" +Low-level operations for C code generation. +""" + +from pypy.translator.typer import LLOp + +# This file defines one class per possible operation. But there are families +# of very similar operations (e.g. incref/decref/xdecref). To make it easy +# to follow which class is really an operation and which class represents a +# family, we introduce a simple mecanism: class attributes that are set to +# PARAMETER are parameters that can be set by calling the class method With(). +PARAMETER = object() # marker + + +class LoC(LLOp): + # base class for LLOps that produce C code. + + def write(self): + "Default write method, delegating to writestr()." + args = [a.name for a in self.args] + if self.can_fail: + args.append(self.errtarget) + return self.writestr(*args) + + def using(self): + return self.args # all locals and constants needed by write() + + def With(cls, **params): + class subcls(cls): + pass + items = params.items() + items.sort() + info = [repr(value) for name, value in items] + subcls.__name__ = '%s.With(%s)' % (cls.__name__, ', '.join(info)) + for name, value in items: + assert hasattr(cls, name), 'not a parameter: %r' % (name,) + setattr(subcls, name, value) + # check that all PARAMETERs, at least from this class, + # have been given a value + for name, value in cls.__dict__.items(): + if value is PARAMETER: + assert name in params, 'missing definition for parameter '+name + return subcls + With = classmethod(With) + +class LoOptimized(LoC): + def write(self): + raise NotImplementedError, 'should be optimized away' + +# ____________________________________________________________ + +class LoStandardOperation(LoC): + "A standard operation is one defined by a macro in genc.h." + can_fail = PARAMETER + llname = PARAMETER + def writestr(self, *args): + return self.llname + '(' + ', '.join(args) + ')' + +class LoKnownAnswer(LoOptimized): + known_answer = PARAMETER + def optimize(self, typer): + return self.known_answer + +class LoNewList(LoC): + can_fail = True + def writestr(self, *stuff): + content = stuff[:-2] + result = stuff[-2] + err = stuff[-1] + ls = ['if (!(%s = PyList_New(%d))) goto %s;' % ( + result, len(content), err)] + for i in range(len(content)): + ls.append('PyList_SET_ITEM(%s, %d, %s); Py_INCREF(%s);' % ( + result, i, content[i], content[i])) + return '\n'.join(ls) + +class LoCallFunction(LoC): + can_fail = True + def writestr(self, func, *stuff): + args = stuff[:-2] + result = stuff[-2] + err = stuff[-1] + format = '"' + 'O' * len(args) + '"' + args = (func, format) + args + return ('if (!(%s = PyObject_CallFunction(%s)))' + ' goto %s;' % (result, ', '.join(args), err)) + +class LoInstantiate(LoC): + can_fail = True + llclass = PARAMETER + def writestr(self, res, err): + return 'INSTANTIATE(%s, %s, %s)' % ( + self.llclass.name, res, err) + +class LoAllocInstance(LoC): + can_fail = True + llclass = PARAMETER + def writestr(self, res, err): + return 'ALLOC_INSTANCE(%s, %s, %s)' % ( + self.llclass.name, res, err) + +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 + + def optimize(self, typer): + # replace this complex conversion by the simpler conversion of + # only the indexth item + llinputs = [] + pos = 0 + for r in self.source_r: + L = len(r.impl) + llinputs.append(self.args[pos:pos+L]) + pos += L + lloutputs = [] + for r in self.target_r: + L = len(r.impl) + lloutputs.append(self.args[pos:pos+L]) + pos += L + + llrepr = [] # answer + for i in range(len(self.source_r)): + if i == self.index: + # convert this item + llrepr += typer.convert(self.source_r[i], llinputs[i], + self.target_r[i], lloutputs[i]) + else: + # don't convert, just pass this item unchanged to the result + llrepr += llinputs[i] + return llrepr + +class LoNewTuple(LoC): + can_fail = True + + def writestr(self, *stuff): + args = stuff[:-2] + result = stuff[-2] + err = stuff[-1] + ls = ['if (!(%s = PyTuple_New(%d))) goto %s;' % + (result, len(args), err)] + for i, a in zip(range(len(args)), args): + ls.append('PyTuple_SET_ITEM(%s, %d, %s); Py_INCREF(%s);' % + (result, i, a, a)) + return '\n'.join(ls) + +class LoConvertChain(LoOptimized): + r_from = PARAMETER + r_middle = PARAMETER + r_to = PARAMETER + convert_length = PARAMETER + + def optimize(self, typer): + half = len(self.r_from.impl) + assert half + len(self.r_to.impl) == len(self.args) + input = self.args[:half] + output = self.args[half:] + middle = typer.convert(self.r_from, input, self.r_middle) + return typer.convert(self.r_middle, middle, self.r_to, output) + +# ____________________________________________________________ + +class LoMove(LoC): + def writestr(self, x, y): + return '%s = %s;' % (y, x) + +class LoGoto(LoC): + def write(self): + return 'goto %s;' % self.errtarget + +class LoCopy(LoOptimized): + def optimize(self, typer): + # the result's llvars is equal to the input's llvars. + assert len(self.args) % 2 == 0 + half = len(self.args) // 2 + return self.args[:half] + +class LoDoSomethingWithRef(LoC): + do_what = PARAMETER + def write(self): + ls = [] + for a in self.args: + if a.type == 'PyObject*': + ls.append('%s(%s);' % (self.do_what, a.name)) + return ' '.join(ls) + +LoIncref = LoDoSomethingWithRef.With(do_what = 'Py_INCREF') +LoDecref = LoDoSomethingWithRef.With(do_what = 'Py_DECREF') +LoXDecref = LoDoSomethingWithRef.With(do_what = 'Py_XDECREF') + +class LoComment(LoC): + def write(self): + s = self.errtarget + s = s.replace('/*', '/+') + s = s.replace('*/', '+/') + return '/* %s */' % s + +# ____________________________________________________________ + +ERROR_RETVAL = { + None: '-1', + 'int': '-1', + 'PyObject*': 'NULL', + } + +ERROR_CHECK = { + None: '< 0', + 'int': '== -1 && PyErr_Occurred()', + 'PyObject*': '== NULL', + } + +class LoCallPyFunction(LoC): + can_fail = True + llfunc = PARAMETER + hlrettype = PARAMETER + def write(self): + L = len(self.hlrettype.impl) + R = len(self.args) - L + args = [a.name for a in self.args[:R]] + err = self.errtarget + if L == 0: # no return value + return 'if (%s(%s) %s) goto %s;' % ( + self.llfunc.name, ', '.join(args), ERROR_CHECK[None], err) + else: + # the return value is the first return LLVar: + retvar = self.args[R] + # if there are several return LLVars, the extra ones are passed + # in by reference as output arguments + args += ['&%s' % a.name for a in self.args[R+1:]] + return ('if ((%s = %s(%s)) %s) goto %s;' % ( + retvar.name, self.llfunc.name, ', '.join(args), + ERROR_CHECK[retvar.type], err)) + +class LoReturn(LoC): + def write(self): + if not self.args: + return 'return 0;' + ls = [] + for extra in self.args[1:]: + ls.append('*output_%s = %s;' % (extra.name, extra.name)) + ls.append('return %s;' % self.args[0].name) + return '\n'.join(ls) + +# ____________________________________________________________ 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 Wed Sep 8 15:55:53 2004 @@ -1,30 +1,29 @@ import re, types, __builtin__ -from pypy.objspace.flow.model import Variable, Constant +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, err_check=None, parse_code=None): + def __init__(self, impl, parse_code=None, name=None): self.impl = impl # list [(C type, prefix for variable name)] - self.err_check = err_check # condition to check for error return value self.parse_code = parse_code # character(s) for PyArg_ParseTuple() + self.name = name or '' % ' + '.join(self.impl) def __repr__(self): - if hasattr(self, 'const'): - return '' % (self.const,) - else: - return '' % ' + '.join(self.impl) + return self.name class CTypeSet: "A (small) set of C types that typer.LLFunction can manipulate." - R_VOID = CRepr([]) - R_INT = CRepr(['int'], err_check='< 0', parse_code='i') - R_OBJECT = CRepr(['PyObject*'], err_check='== NULL', parse_code='O') + 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, @@ -32,12 +31,23 @@ 'o': R_OBJECT, } + rawoperations = { + 'goto' : genc_op.LoGoto, + 'move' : genc_op.LoMove, + 'copy' : genc_op.LoCopy, + 'incref' : genc_op.LoIncref, + 'decref' : genc_op.LoDecref, + 'xdecref': genc_op.LoXDecref, + 'comment': genc_op.LoComment, + 'return' : genc_op.LoReturn, + } + def __init__(self, genc, bindings): self.genc = genc self.bindings = bindings self.r_constants = {} self.r_tuples = {} - self.lloperations = {'convert': {}, 'release': {}} + self.lloperations = {'convert': {}} self.parse_operation_templates() # __________ methods required by LLFunction __________ @@ -60,6 +70,8 @@ return self.tuple_representation(items_r) # fall-back return self.R_OBJECT + #if isinstance(var, UndefinedConstant): + # return self.R_DONTCARE if isinstance(var, Constant): return self.constant_representation(var.value) raise TypeError, var @@ -74,17 +86,7 @@ sig = (self.R_OBJECT,) * len(hltypes) if sig in opnewlist: return False - def writer(*stuff): - content = stuff[:-2] - result = stuff[-2] - err = stuff[-1] - ls = ['if (!(%s = PyList_New(%d))) goto %s;' % ( - result, len(content), err)] - for i in range(len(content)): - ls.append('PyList_SET_ITEM(%s, %d, %s); Py_INCREF(%s);' % ( - result, i, content[i], content[i])) - return '\n'.join(ls) - opnewlist[sig] = writer, True + opnewlist[sig] = genc_op.LoNewList return True # retry if opname == 'OP_NEWTUPLE': opnewtuple = self.lloperations.setdefault('OP_NEWTUPLE', {}) @@ -92,28 +94,20 @@ sig = tuple(hltypes[:-1]) + (rt,) if sig in opnewtuple: return False - opnewtuple[sig] = 'copy', 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 - def writer(func, *stuff): - args = stuff[:-2] - result = stuff[-2] - err = stuff[-1] - format = '"' + 'O' * len(args) + '"' - args = (func, format) + args - return ('if (!(%s = PyObject_CallFunction(%s)))' - ' goto %s;' % (result, ', '.join(args), err)) - opsimplecall[sig] = writer, True + opsimplecall[sig] = genc_op.LoCallFunction return True # retry return False - def knownanswer(self, llname): - return getattr(llname, 'known_answer', None) - # ____________________________________________________________ def constant_representation(self, value): @@ -127,26 +121,13 @@ 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([]) + r = self.r_constants[key] = CRepr([], name='' % (value,)) r.const = value - # returning a constant - def writer(): - return 'return 0;' - self.lloperations['return'][r,] = writer, False - def writer(): - return 'return -1;' - self.lloperations['returnerr'][r,] = writer, False - + # 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 - def writer(z): - return '%s = %d;' % (z, value) - conv[r, self.R_INT] = writer, False - writer.known_answer = [LLConst(self.R_INT, '%d' % value)] # can convert the constant to a PyObject* if value >= 0: name = 'g_IntObj_%d' % value @@ -154,6 +135,10 @@ 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, @@ -172,23 +157,10 @@ sig.append(self.gethltype(v)) hltype = self.gethltype(llfunc.graph.getreturnvar()) sig.append(hltype) - if len(hltype.impl) == 0: # no return value - def writer(*stuff): - args = stuff[:-1] - err = stuff[-1] - return 'if (%s(%s) < 0) goto %s;' % ( - llfunc.name, ', '.join(args), err) - elif len(hltype.impl) == 1: # one LLVar for the return value - def writer(*stuff): - args = stuff[:-2] - result = stuff[-2] - err = stuff[-1] - return ('if ((%s = %s(%s)) %s) goto %s;' % ( - result, llfunc.name, ', '.join(args), - hltype.err_check, err)) - else: - XXX("to do") - ops[tuple(sig)] = writer, True + ops[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* @@ -202,38 +174,33 @@ opname = 'CALL_' + value.__name__ if opname in self.lloperations: ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {}) - for sig, ll in self.lloperations[opname].items(): + for sig, llopcls in self.lloperations[opname].items(): sig = (r,) + sig - ops[sig] = ll + 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', {}) # XXX do __init__ sig = (r, self.R_OBJECT) - def writer(res, err): - return 'INSTANTIATE(%s, %s, %s)' % ( - self.genc.llclasses[value].name, res, err) - ops[sig] = writer, True + ops[sig] = genc_op.LoInstantiate.With( + llclass = self.genc.llclasses[value], + ) # OP_ALLOC_INSTANCE used by the constructor function xxx_new() ops = self.lloperations.setdefault('OP_ALLOC_INSTANCE', {}) sig = (r, self.R_OBJECT) - def writer(res, err): - return 'ALLOC_INSTANCE(%s, %s, %s)' % ( - self.genc.llclasses[value].name, res, err) - ops[sig] = writer, True + ops[sig] = genc_op.LoAllocInstance.With( + llclass = self.genc.llclasses[value], + ) else: print "// XXX not implemented: constant", key return r def can_convert_to_pyobj(self, r, initexpr, globalname): - conv = self.lloperations['convert'] - def writer(z): - return '%s = %s; Py_INCREF(%s);' % (z, globalname, z) - conv[r, self.R_OBJECT] = writer, False - llconst = LLConst('PyObject*', globalname, initexpr, - to_declare = bool(initexpr)) - writer.known_answer = [llconst] + 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 @@ -245,50 +212,34 @@ impl = [] for r in items_r: impl += r.impl - rt = CRepr(impl) - if items_r: - rt.err_check = items_r[0].err_check + name = '<(%s)>' % ', '.join([str(r) for r in items_r]) + rt = CRepr(impl, name=name) self.r_tuples[items_r] = rt - # can convert the tuple to a PyTupleObject only if each item can be + + # we can convert any item in the tuple to obtain another tuple + # representation. conv = self.lloperations['convert'] - also_using = [] - for r in items_r: - if r == self.R_OBJECT: - continue - if (r, self.R_OBJECT) not in conv: - break - llname, can_fail = conv[r, self.R_OBJECT] - also_using.append(llname) - else: - def writer(*args): - content = args[:-2] - result = args[-2] - err = args[-1] - ls = ['{', - 'PyObject* o;', - 'if (!(%s = PyTuple_New(%d))) goto %s;' % ( - result, len(items_r), err)] - j = 0 - for i in range(len(items_r)): - r = items_r[i] - if r == self.R_OBJECT: - o = content[j] - j = j+1 - ls.append('Py_INCREF(%s);' % o) - else: - o = 'o' - llname, can_fail = conv[r, self.R_OBJECT] - k = len(r.impl) - args = content[j:j+k] + (o,) - j = j+k - if can_fail: - args += (err,) - ls.append(llname(*args)) - ls.append('PyTuple_SET_ITEM(%s, %d, %s);' % - (result, i, o)) - return '\n'.join(ls).replace('\n', '\n\t') + '\n}' - writer.also_using = also_using - conv[rt, self.R_OBJECT] = writer, True + 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): @@ -307,11 +258,50 @@ can_fail = formalargs.replace(' ','').endswith(',err') ops = self.lloperations.setdefault(opname, {}) assert sig not in ops, llname - # the operation's low-level name is a callable that will - # produce the correct macro call - def writer(*args): - return llname + '(' + ', '.join(args) + ')' - ops.setdefault(sig, (writer, can_fail)) + ops.setdefault(sig, genc_op.LoStandardOperation.With( + can_fail = can_fail, + llname = llname, + )) + + 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'." @@ -331,12 +321,3 @@ c = '_%02x' % ord(c) l.append(c) return ''.join(l) - -def consts_used(writer): - "Enumerate the global constants that a writer function uses." - result = getattr(writer, 'known_answer', []) - if hasattr(writer, 'also_using'): - result = list(result) - for w in writer.also_using: - result += consts_used(w) - return result Modified: pypy/trunk/src/pypy/translator/test/test_ctrans.py ============================================================================== --- pypy/trunk/src/pypy/translator/test/test_ctrans.py (original) +++ pypy/trunk/src/pypy/translator/test/test_ctrans.py Wed Sep 8 15:55:53 2004 @@ -96,7 +96,8 @@ if isinstance(spec, tuple): spec = spec[0] # use the first type only for the tests argstypelist.append(spec) - t.annotate(argstypelist) + a = t.annotate(argstypelist) + a.simplify() return t.ccompile() def test_set_attr(self): Modified: pypy/trunk/src/pypy/translator/translator.py ============================================================================== --- pypy/trunk/src/pypy/translator/translator.py (original) +++ pypy/trunk/src/pypy/translator/translator.py Wed Sep 8 15:55:53 2004 @@ -37,7 +37,7 @@ from pypy.translator.simplify import simplify_graph from pypy.translator.genpyrex import GenPyrex from pypy.translator.gencl import GenCL -from pypy.translator.genc import GenC +from pypy.translator.genc import GenC, uniquemodulename from pypy.translator.tool.buildpyxmodule import make_module_from_pyxstring from pypy.translator.tool.buildpyxmodule import make_module_from_c from pypy.objspace.flow import FlowObjSpace @@ -220,13 +220,13 @@ """Returns compiled function, compiled using the C generator. """ from pypy.tool.udir import udir - name = self.entrypoint.func_name + name = uniquemodulename(self.entrypoint.func_name) cfile = udir.join('%s.c' % name) f = cfile.open('w') GenC(f, self, name) f.close() mod = make_module_from_c(cfile) - return getattr(mod, name) + return getattr(mod, self.entrypoint.func_name) def call(self, *args): """Calls underlying Python function.""" Modified: pypy/trunk/src/pypy/translator/typer.py ============================================================================== --- pypy/trunk/src/pypy/translator/typer.py (original) +++ pypy/trunk/src/pypy/translator/typer.py Wed Sep 8 15:55:53 2004 @@ -3,7 +3,7 @@ """ from __future__ import generators -from pypy.objspace.flow.model import Constant, Variable, Block, Link, traverse +from pypy.objspace.flow.model import Variable, Block, Link, traverse from pypy.translator.simplify import remove_direct_loops @@ -20,13 +20,21 @@ self.initexpr = initexpr self.to_declare = to_declare -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 +class LLOp(object): + "A low-level operation. Must be subclassed, one subclass per operation." + can_fail = False # boolean attribute: should an errtarget be generated? + + def __init__(self, args, errtarget=None): + self.args = args # list of LLVars self.errtarget = errtarget # label to jump to in case of error + def optimize(self, typer): + """If the operation can be statically optimized, this method can + return a list [new-llvars-for-result] and optionally generate + replacement operations by calling typer.operation() or + typer.convert().""" + return None + class TypingError(Exception): pass @@ -43,32 +51,32 @@ # # lloperations = { # 'opname': { -# (tuple-of-hltypes): (low-level-name, flag-can-fail), +# (tuple-of-hltypes): subclass-of-LLOp, # ... }, # ...} # This dict contains the known signatures of each space operation. # Special opnames: -# 'convert' x y : convert some x to y -# 'caseXXX' z : fails (i.e. jump to errlabel) if z is not XXX -# 'return' z : z is the return value of the function -# 'returnerr' z : same, but return an error code instead of z's content +# 'convert' v w : convert some v to w +# 'caseXXX' v : fails (i.e. jump to errlabel) if v is not XXX # -# Low-level-only operation names: +# rawoperations = { +# 'opname': subclass-of-LLOp, +# ...} +# Low-level-only operations on raw LLVars (as opposed to llopeerations, +# 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 # 'copy' x1 x2.. y1 y2...: raw copy x1 to y1, x2 to y2, etc. and incref # 'incref' x y...: raw incref of the LLVars x, y, etc. # 'decref' x y...: raw decref of the LLVars x, y, etc. # 'xdecref' x y...: raw xdecref of the LLVars x, y, etc. +# 'comment' : comment (text is in errtarget) +# 'return' x y...: return the value stored in the LLVars # # 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 # False to fail. -# -# def knownanswer(self, llname): -# Optionally returns a list of LLVars that give the well-known, constant -# answer to the low-level operation name 'llname'; else return None. # ____________________________________________________________ @@ -79,8 +87,8 @@ self.gethltype = typeset.gethltype self.represent = typeset.represent self.lloperations = typeset.lloperations + self.rawoperations= typeset.rawoperations self.typingerror = typeset.typingerror - self.knownanswer = typeset.knownanswer self.hltypes = {} self.llreprs = {} @@ -123,26 +131,17 @@ 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 + return llrepr, llret - def ll_body(self): + def ll_body(self, error_retvals): """ Get the body by flattening and low-level-izing the flow graph. Enumerates low-level operations: LLOps with labels inbetween (strings). """ self.blockname = {} - v = self.graph.getreturnvar() - self.makevar(v) - sig = (self.hltypes[v],) - llname, can_fail = self.lloperations['returnerr'][sig] - assert not can_fail - self.release_root = ReleaseNode(None, LLOp(llname, []), None) + llreturn = self.rawoperations['return'] + assert not llreturn.can_fail + self.release_root = ReleaseNode(None, llreturn(error_retvals), None) allblocks = [] # collect all blocks @@ -156,7 +155,7 @@ # generate an incref for each input argument for v in self.graph.getargs(): - yield LLOp('incref', self.llreprs[v]) + yield self.rawoperations['incref'](self.llreprs[v]) # generate the body of each block for block in allblocks: @@ -165,7 +164,7 @@ yield '' # empty line # generate the code to handle errors - for op in self.release_root.error_code(): + for op in self.release_root.error_code(self.rawoperations): yield op def generate_block(self, block): @@ -198,13 +197,16 @@ elif hasattr(block, 'exc_type'): XXX("to do") else: - self.operation('return', block.inputargs) + llreturn = self.rawoperations['return'] + assert not llreturn.can_fail + llrepr = self.llreprs[block.inputargs[0]] + self.blockops.append(llreturn(llrepr)) return self.blockops # __________ Type checking and conversion routines __________ def mark_release(self, v): - llop = LLOp('decref', self.llreprs[v]) + llop = self.rawoperations['decref'](self.llreprs[v]) # make a new node for the release tree self.to_release = ReleaseNode(v, llop, self.to_release) @@ -236,7 +238,7 @@ llsigs = self.lloperations.get(opname, {}) for sig in variants(tuple(args_t), directions): if sig in llsigs: - llname, can_fail = llsigs[sig] + llopcls = llsigs[sig] break else: retry = self.typingerror(opname, tuple(args_t)) @@ -250,52 +252,66 @@ except RuntimeError: # infinite recursion pass raise TypingError([opname] + args_t) - # check for some operations that have an existing well-known answer - # that doesn't involve side-effects, so that we can just provide - # this answer and not generate any LLOp. - if result: - if llname == 'copy': - # patch self.llreprs and we're done - llrepr = [] - for v in args: - llrepr += self.llreprs[v] - self.llreprs[result] = llrepr - return - llrepr = self.knownanswer(llname) - if llrepr is not None: - # patch self.llreprs and we're done - self.llreprs[result] = llrepr - return # 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] + 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 can_fail and errlabel is None: + 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 - llargs += self.llreprs[result] - self.blockops.append(LLOp(llname, llargs, errlabel)) - self.mark_release(result) + tmp = 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)) + 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) else: # no result variable - self.blockops.append(LLOp(llname, llargs, errlabel)) + self.blockops.append(llopcls(llargs, errlabel)) + + 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 + if outputrepr is None: + outputrepr = self.llreprs[tmpout] + else: + self.llreprs[tmpout] = outputrepr + self.operation('convert', [tmpin], tmpout) + return self.llreprs[tmpout] def goto(self, exit): # generate the exit.args -> target.inputargs copying operations @@ -316,9 +332,10 @@ # the order of the move operations current_refcnt = {} needed_refcnt = {} + llmove = self.rawoperations['move'] for v, w in zip(exitargs, exit.target.inputargs): for x, y in zip(self.llreprs[v], self.llreprs[w]): - self.blockops.append(LLOp('move', [x, y])) + self.blockops.append(llmove([x, y])) needed_refcnt.setdefault(x, 0) needed_refcnt[x] += 1 # list all variables that go out of scope: by default @@ -330,17 +347,20 @@ # now adjust all reference counters: first increfs, then decrefs # (in case a variable to decref points to the same objects than # another variable to incref). + llincref = self.rawoperations['incref'] for x, needed in needed_refcnt.items(): current_refcnt.setdefault(x, 0) while current_refcnt[x] < needed: - self.blockops.append(LLOp('incref', [x])) + self.blockops.append(llincref([x])) current_refcnt[x] += 1 + lldecref = self.rawoperations['decref'] for x, needed in needed_refcnt.items(): while current_refcnt[x] > needed: - self.blockops.append(LLOp('decref', [x])) + self.blockops.append(lldecref([x])) current_refcnt[x] -= 1 # finally jump to the target block - self.blockops.append(LLOp('goto', [], self.blockname[exit.target])) + llgoto = self.rawoperations['goto'] + self.blockops.append(llgoto([], self.blockname[exit.target])) finally: self.to_release = to_release_copy # after a call to goto() we are back to generating ops for @@ -387,13 +407,14 @@ yield self self = self.parent - def error_code(self): + def error_code(self, rawoperations): N = len(self.accessible_children) for i in range(N): if i > 0: - yield LLOp('goto', [], self.getlabel()) + llgoto = rawoperations['goto'] + yield llgoto([], self.getlabel()) node = self.accessible_children[~i] - for op in node.error_code(): + for op in node.error_code(rawoperations): yield op if self.label: yield self.label From arigo at codespeak.net Thu Sep 9 21:16:08 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Thu, 9 Sep 2004 21:16:08 +0200 (MEST) Subject: [pypy-svn] r6372 - pypy/trunk/src/pypy/translator Message-ID: <20040909191608.5BDDA5B369@thoth.codespeak.net> 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 '' % ' + '.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='' % (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 From arigo at codespeak.net Fri Sep 10 12:40:46 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 10 Sep 2004 12:40:46 +0200 (MEST) Subject: [pypy-svn] r6375 - pypy/trunk/src/pypy/translator Message-ID: <20040910104046.A6D515AAAF@thoth.codespeak.net> Author: arigo Date: Fri Sep 10 12:40:46 2004 New Revision: 6375 Modified: pypy/trunk/src/pypy/translator/genc_typeset.py pypy/trunk/src/pypy/translator/typer.py Log: Still cleaning a bit operations and conversions. See the new logic in typer.LLFunction.find_best_match(). While generally useful, the precise goal of this algorithm (next check-in!) is that a user-defined class gets a chance to generate a custom OP_SET_ATTR for its attributes in the method CTypeSet.extend_OP_SET_ATTR(), to perform a direct store into the structure, before the fall-back macro OP_SET_ATTR_ooov() is considered -- which would generate a conversion of the attribute to PyObject* and a call to PyObject_SetAttr(). 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 Fri Sep 10 12:40:46 2004 @@ -82,31 +82,29 @@ self.conversion_errors[sig] = True raise - def typingerror(self, opname, hltypes): + def typemismatch(self, opname, hltypes): # 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) + extender = getattr(self, 'extend_' + opname) except AttributeError: - return False - - progress = False - for newsig, newop in builder(hltypes): + return + for newsig, newop in extender(hltypes): + # record the new operation only if it has a lower cost than + # an existing operation with the same signature llops = self.lloperations.setdefault(opname, {}) - if newsig not in llops: + if newsig not in llops or newop.cost < llops[newsig].cost: llops[newsig] = newop - progress = True - return progress # ____________________________________________________________ - def build_OP_NEWLIST(self, hltypes): + def extend_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): + def extend_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. @@ -114,7 +112,7 @@ sig = tuple(hltypes[:-1]) + (rt,) yield sig, genc_op.LoCopy - def build_OP_SIMPLE_CALL(self, hltypes): + def extend_OP_SIMPLE_CALL(self, hltypes): if not hltypes: return # We can call the function using PyObject_CallFunction(), if @@ -156,7 +154,7 @@ sig = (r,) + sig yield sig, llopcls - def build_OP_ALLOC_INSTANCE(self, hltypes): + def extend_OP_ALLOC_INSTANCE(self, hltypes): # OP_ALLOC_INSTANCE is used by the constructor functions xxx_new() if not hltypes: return Modified: pypy/trunk/src/pypy/translator/typer.py ============================================================================== --- pypy/trunk/src/pypy/translator/typer.py (original) +++ pypy/trunk/src/pypy/translator/typer.py Fri Sep 10 12:40:46 2004 @@ -77,10 +77,11 @@ # 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 -# False to fail. +# def typemismatch(self, opname, hltypes): +# Called when no exact match is found in lloperations. This function +# can extend lloperations[opname] to provide a better match for hltypes. +# Partial matches (i.e. ones requiring conversions) are only considered +# after this function returns. # ____________________________________________________________ @@ -93,7 +94,7 @@ self.lloperations = typeset.lloperations self.rawoperations= typeset.rawoperations self.getconversion= typeset.getconversion - self.typingerror = typeset.typingerror + self.typemismatch = typeset.typemismatch self.hltypes = {} self.llreprs = {} @@ -215,6 +216,42 @@ # make a new node for the release tree self.to_release = ReleaseNode(v, llop, self.to_release) + def find_best_match(self, opname, args_t, directions): + # look for an exact match first + llsigs = self.lloperations.setdefault(opname, {}) + sig = tuple(args_t) + if sig in llsigs: + return sig, llsigs[sig] + # no exact match, give the typeset a chance to provide an + # accurate version + self.typemismatch(opname, tuple(args_t)) + if sig in llsigs: + return sig, llsigs[sig] + # 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: + 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 choices: + cost, sig, llopcls = min(choices) + # for performance, cache the approximate match + # back into self.lloperations + llsigs[sig] = llopcls + return sig, llopcls + raise TypingError([opname] + list(args_t)) + 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 @@ -227,43 +264,9 @@ self.makevar(result) args_t.append(self.hltypes[result]) directions.append(True) - # look for an exact match first - llsigs = self.lloperations.get(opname, {}) - sig = tuple(args_t) - if sig in llsigs: - llopcls = llsigs[sig] - else: - # 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: - 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 + # look for the low-level operation class that implements these types + sig, llopcls = self.find_best_match(opname, args_t, directions) + # convert input args to temporary variables, if needed llargs = [] for v, v_t, s_t in zip(args, args_t, sig): if v_t != s_t: From arigo at codespeak.net Fri Sep 10 12:52:39 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 10 Sep 2004 12:52:39 +0200 (MEST) Subject: [pypy-svn] r6376 - pypy/trunk/src/pypy/translator Message-ID: <20040910105239.09F5E5AAAF@thoth.codespeak.net> Author: arigo Date: Fri Sep 10 12:52:39 2004 New Revision: 6376 Modified: pypy/trunk/src/pypy/translator/classtyper.py pypy/trunk/src/pypy/translator/genc.py Log: Fixed a (hopefully last) reference to the old self.lloperations format. Modified: pypy/trunk/src/pypy/translator/classtyper.py ============================================================================== --- pypy/trunk/src/pypy/translator/classtyper.py (original) +++ pypy/trunk/src/pypy/translator/classtyper.py Fri Sep 10 12:52:39 2004 @@ -7,7 +7,7 @@ from pypy.objspace.flow.model import Variable, Constant, SpaceOperation 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.typer import LLTyper, LLFunction, LLVar from pypy.translator.genc_repr import R_OBJECT r_ends_in_underscore_digit = re.compile(r'_\d+$') @@ -34,6 +34,14 @@ # that are real fields for the C struct self.llvars = llclass.llreprs[self.var] + def getllvars(self, pattern='%s'): + """Return a list of fresh LLVars that implement the fields in this + C struct. The name of the variables are %'ed with 'pattern', + which can be a C expression template to generate expressions to + access this field. + """ + return [LLVar(x.type, pattern % x.name) for x in self.llvars] + # ____________________________________________________________ class LLClass(LLTyper): Modified: pypy/trunk/src/pypy/translator/genc.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc.py (original) +++ pypy/trunk/src/pypy/translator/genc.py Fri Sep 10 12:52:39 2004 @@ -253,15 +253,14 @@ # generate the deallocator function -- must special-case it; # other functions are generated by LLClass.get_management_functions() print >> f, self.C_DEALLOC_HEADER % info + llxdecref = self.typeset.rawoperations['xdecref'] for fld in llclass.fields: - try: - sig = (fld.hltype,) - llname, can_fail = self.typeset.lloperations['xdecref'][sig] - except KeyError: - continue # ignore fields with no xdecref operation - assert not can_fail - args = ['op->%s' % llvar.name for llvar in fld.llvars] - print >> f, '\t' + llname(*args) + llvars = fld.getllvars('op->%s') + line = llxdecref(llvars) + code = line.write() + if code: + for codeline in code.split('\n'): + print >> f, '\t' + codeline print >> f, self.C_DEALLOC_FOOTER % info # generate the member list for the type object From arigo at codespeak.net Fri Sep 10 14:15:18 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 10 Sep 2004 14:15:18 +0200 (MEST) Subject: [pypy-svn] r6378 - pypy/trunk/src/pypy/translator Message-ID: <20040910121518.992BA5AAAF@thoth.codespeak.net> Author: arigo Date: Fri Sep 10 14:15:18 2004 New Revision: 6378 Modified: pypy/trunk/src/pypy/translator/classtyper.py pypy/trunk/src/pypy/translator/genc.h pypy/trunk/src/pypy/translator/genc.py pypy/trunk/src/pypy/translator/genc_op.py pypy/trunk/src/pypy/translator/genc_repr.py pypy/trunk/src/pypy/translator/genc_typeset.py Log: Class instance representations, with support for OP_GET_ATTR and OP_SET_ATTR reading and writing directly into the C struct fields. Fixes bugs with inheritance: parent fields where not included in the C struct for subclasses. Also, tp_base is set now in the PyTypeObjects. Modified: pypy/trunk/src/pypy/translator/classtyper.py ============================================================================== --- pypy/trunk/src/pypy/translator/classtyper.py (original) +++ pypy/trunk/src/pypy/translator/classtyper.py Fri Sep 10 14:15:18 2004 @@ -66,6 +66,32 @@ fld.name for fld in self.fields if fld.hltype == R_OBJECT] self.s_instance = annmodel.SomeInstance(self.cdef) + def getparent(self): + if self.cdef.basedef is None: + return None + else: + return self.typeset.genc.llclasses[self.cdef.basedef.cls] + + def getfield(self, name): + """Returns the ClassField corresponding to this attribute name. + Keep in mind that it might be from some parent LLClass.""" + while self is not None: + for fld in self.fields: + if fld.name == name: + return fld + self = self.getparent() + return None + + def getfields(self): + "Return the list of all fields, including the ones from the parent." + parent = self.getparent() + if parent is None: + fields = [] + else: + fields = parent.getfields() + fields += self.fields + return fields + def get_management_functions(self): "Generate LLFunctions that operate on this class' structure." yield self.make_fn_new() @@ -90,7 +116,7 @@ v1 = op('alloc_instance', Constant(cls), s_result = self.s_instance) # class attributes are used as defaults to initialize fields - for fld in self.fields: + for fld in self.getfields(): if hasattr(cls, fld.name): value = getattr(cls, fld.name) op('setattr', v1, Constant(fld.name), Constant(value), Modified: pypy/trunk/src/pypy/translator/genc.h ============================================================================== --- pypy/trunk/src/pypy/translator/genc.h (original) +++ pypy/trunk/src/pypy/translator/genc.h Fri Sep 10 14:15:18 2004 @@ -99,6 +99,9 @@ #define ALLOC_INSTANCE(cls, r, err) \ if (!(r=PyType_GenericAlloc(&cls##_Type, 0))) goto err; +#define GET_ATTR_py(fld, r) r=fld; Py_INCREF(r); +#define SET_ATTR_py(fld, v) { PyObject* o=fld; fld=v; \ + Py_INCREF(v); Py_XDECREF(o); } /* a few built-in functions */ Modified: pypy/trunk/src/pypy/translator/genc.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc.py (original) +++ pypy/trunk/src/pypy/translator/genc.py Fri Sep 10 14:15:18 2004 @@ -241,11 +241,14 @@ 'module': cls.__module__, 'basename': cls.__name__, 'name': llclass.name, + 'base': '0', } + if llclass.getparent() is not None: + info['base'] = '&%s_Type' % llclass.getparent().name # print the C struct declaration print >> f, self.C_STRUCT_HEADER % info - for fld in llclass.fields: + for fld in llclass.getfields(): for llvar in fld.llvars: print >> f, '\t%s %s;' % (llvar.type, llvar.name) print >> f, self.C_STRUCT_FOOTER % info @@ -254,7 +257,7 @@ # other functions are generated by LLClass.get_management_functions() print >> f, self.C_DEALLOC_HEADER % info llxdecref = self.typeset.rawoperations['xdecref'] - for fld in llclass.fields: + for fld in llclass.getfields(): llvars = fld.getllvars('op->%s') line = llxdecref(llvars) code = line.write() @@ -266,7 +269,7 @@ # generate the member list for the type object print >> f, self.C_MEMBERLIST_HEADER % info # XXX write member definitions for member with well-known types only - for fld in llclass.fields: + for fld in llclass.fields: # members from parent inherited via tp_base if fld.hltype == R_OBJECT: t = 'T_OBJECT_EX' elif fld.hltype == R_INT: @@ -280,6 +283,8 @@ # declare and initialize the static PyTypeObject print >> f, self.C_TYPEOBJECT % info + self.initializationcode += (self.C_INITTYPE % info).split('\n') + # ____________________________________________________________ C_HEADER = open(os.path.join(autopath.this_dir, 'genc.h')).read() @@ -359,7 +364,7 @@ 0, /* tp_methods */ %(name)s_memberlist, /* tp_members */ 0, /* tp_getset */ - 0, /* tp_base */ + %(base)s, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ @@ -371,4 +376,6 @@ }; ''' + C_INITTYPE = '''PyType_Ready(&%(name)s_Type);''' + # ____________________________________________________________ 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 Fri Sep 10 14:15:18 2004 @@ -150,6 +150,40 @@ (result, i, a, a)) return '\n'.join(ls) +class LoGetAttr(LoC): + cost = 1 + fld = PARAMETER + + def writestr(self, inst, *result): + assert len(result) == len(self.fld.llvars) + ls = [] + llclass = self.fld.llclass + for src, dstname in zip(self.fld.llvars, result): + fldexpr = '((%s_Object*) %s)->%s' % (llclass.name, inst, + src.name) + if src.type == 'PyObject*': + ls.append('GET_ATTR_py(%s, %s)' % (fldexpr, dstname)) + else: + ls.append('%s = %s;' % (dstname, fldexpr)) + return '\n'.join(ls) + +class LoSetAttr(LoC): + cost = 1 + fld = PARAMETER + + def writestr(self, inst, *value): + assert len(value) == len(self.fld.llvars) + ls = [] + llclass = self.fld.llclass + for srcname, dst in zip(value, self.fld.llvars): + fldexpr = '((%s_Object*) %s)->%s' % (llclass.name, inst, + dst.name) + if dst.type == 'PyObject*': + ls.append('SET_ATTR_py(%s, %s)' % (fldexpr, srcname)) + else: + ls.append('%s = %s;' % (fldexpr, srcname)) + return '\n'.join(ls) + class LoConvertChain(LoOptimized): r_from = PARAMETER r_middle = PARAMETER Modified: pypy/trunk/src/pypy/translator/genc_repr.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc_repr.py (original) +++ pypy/trunk/src/pypy/translator/genc_repr.py Fri Sep 10 14:15:18 2004 @@ -149,6 +149,30 @@ else: raise CannotConvert + +class CInstance(CRepr): + "An instance of some class (or a subclass of it)." + impl = ['PyObject*'] + + def __init__(self, llclass): + self.llclass = llclass # instance of classtyper.LLClass + + def __repr__(self): + cls = self.llclass.cdef.cls + return 'CInstance(%s.%s)' % (cls.__module__, cls.__name__) + + def convert_to(self, target, typeset): + if isinstance(target, CInstance): + # can convert to an instance of a parent class + if target.llclass.cdef not in self.llclass.cdef.getmro(): + raise CannotConvert + return genc_op.LoCopy + elif target == R_OBJECT: + # can convert to a generic PyObject* + return genc_op.LoCopy + else: + raise CannotConvert + # ____________________________________________________________ # # Predefined CReprs and caches for building more @@ -160,6 +184,7 @@ R_TUPLE_CACHE = {} R_CONSTANT_CACHE = {} +R_INSTANCE_CACHE = {} def tuple_representation(items_r): items_r = tuple(items_r) @@ -195,6 +220,13 @@ r = R_CONSTANT_CACHE[key] = cls(value) return r +def instance_representation(llclass): + try: + return R_INSTANCE_CACHE[llclass] + except KeyError: + r = R_INSTANCE_CACHE[llclass] = CInstance(llclass) + return r + def c_str(s): "Return the C expression for the string 's'." 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 Fri Sep 10 14:15:18 2004 @@ -7,7 +7,7 @@ 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 +from pypy.translator.genc_repr import instance_representation, CInstance class CTypeSet: @@ -56,6 +56,9 @@ if isinstance(var, annmodel.SomeTuple): items_r = [self.gethltype(s_item) for s_item in var.items] return tuple_representation(items_r) + if isinstance(var, annmodel.SomeInstance): + llclass = self.genc.llclasses[var.knowntype] + return instance_representation(llclass) # fall-back return R_OBJECT if isinstance(var, UndefinedConstant): @@ -142,7 +145,9 @@ # Instantiating a user-defined class if fn in self.genc.llclasses: # XXX do __init__ - sig = (r, R_OBJECT) # R_OBJECT is the result of the op + llclass = self.genc.llclasses[fn] + r_result = instance_representation(llclass) + sig = (r, r_result) # r_result is the result of the op yield sig, genc_op.LoInstantiate.With( llclass = self.genc.llclasses[fn], ) @@ -164,9 +169,38 @@ if not callable(fn): return if fn in self.genc.llclasses: - sig = (r, R_OBJECT) # R_OBJECT is the result + llclass = self.genc.llclasses[fn] + r_result = instance_representation(llclass) + sig = (r, r_result) # r_result is the result of the op yield sig, genc_op.LoAllocInstance.With( - llclass = self.genc.llclasses[fn], + llclass = llclass, + ) + + def extend_OP_GETATTR(self, hltypes): + if len(hltypes) != 3: + return + r_obj, r_attr, r_result = hltypes + if isinstance(r_obj, CInstance) and isinstance(r_attr, CConstant): + # record the OP_GETATTR operation for this field + fld = r_obj.llclass.getfield(r_attr.value) + if fld is not None: + sig = (r_obj, constant_representation(fld.name), fld.hltype) + yield sig, genc_op.LoGetAttr.With( + fld = fld, + ) + + def extend_OP_SETATTR(self, hltypes): + if len(hltypes) != 4: + return + r_obj, r_attr, r_value, r_voidresult = hltypes + if isinstance(r_obj, CInstance): + # record the OP_SETATTR operation for this field + fld = r_obj.llclass.getfield(r_attr.value) + if fld is not None: + sig = (r_obj, constant_representation(fld.name), fld.hltype, + R_VOID) + yield sig, genc_op.LoSetAttr.With( + fld = fld, ) # ____________________________________________________________ From arigo at codespeak.net Fri Sep 10 14:58:06 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 10 Sep 2004 14:58:06 +0200 (MEST) Subject: [pypy-svn] r6379 - in pypy/trunk/src/pypy: annotation translator Message-ID: <20040910125806.EA3675AAAF@thoth.codespeak.net> Author: arigo Date: Fri Sep 10 14:58:06 2004 New Revision: 6379 Modified: pypy/trunk/src/pypy/annotation/factory.py pypy/trunk/src/pypy/annotation/unaryop.py pypy/trunk/src/pypy/translator/classtyper.py pypy/trunk/src/pypy/translator/genc.py pypy/trunk/src/pypy/translator/genc_typeset.py Log: When annotating the usage of class instances, record for which attributes setattr is used, and which ones it is not. The latter are class attributes that don't have to be allocated in each instance's C struct. Cleaned up classtyper.py. Next step is to figure out in which C struct the class attributes should be stored (given that the goal is that subclasses can override them). Maybe in an extended PyTypeObject? Modified: pypy/trunk/src/pypy/annotation/factory.py ============================================================================== --- pypy/trunk/src/pypy/annotation/factory.py (original) +++ pypy/trunk/src/pypy/annotation/factory.py Fri Sep 10 14:58:06 2004 @@ -152,6 +152,7 @@ def __init__(self, cls, bookkeeper): self.attrs = {} # attrs is updated with new information + self.readonly = {} # {attr: True-or-False} self.revision = 0 # which increases the revision number self.instancefactories = {} self.cls = cls @@ -208,7 +209,7 @@ factories.update(clsdef.instancefactories) return factories - def generalize(self, attr, s_value, bookkeeper=None): + def generalize(self, attr, s_value, bookkeeper=None, readonly=True): # we make sure that an attribute never appears both in a class # and in some subclass, in two steps: # (1) check if the attribute is already in a superclass @@ -221,10 +222,13 @@ for subdef in self.getallsubdefs(): if attr in subdef.attrs: subclass_values.append(subdef.attrs[attr]) + readonly = readonly and subdef.readonly[attr] del subdef.attrs[attr] + del subdef.readonly[attr] # bump the revision number of this class and all subclasses subdef.revision += 1 self.attrs[attr] = unionof(s_value, *subclass_values) + self.readonly[attr] = readonly # reflow from all factories if bookkeeper: for factory in self.getallfactories(): Modified: pypy/trunk/src/pypy/annotation/unaryop.py ============================================================================== --- pypy/trunk/src/pypy/annotation/unaryop.py (original) +++ pypy/trunk/src/pypy/annotation/unaryop.py Fri Sep 10 14:58:06 2004 @@ -107,13 +107,14 @@ # look for the attribute in ins.classdef or a parent class s_existing = clsdef.attrs[attr] if s_existing.contains(s_value): + clsdef.readonly[attr] = False return # already general enough, nothing to do break else: # if the attribute doesn't exist yet, create it here clsdef = ins.classdef # create or update the attribute in clsdef - clsdef.generalize(attr, s_value, getbookkeeper()) + clsdef.generalize(attr, s_value, getbookkeeper(), readonly=False) raise BlockedInference return SomeObject() Modified: pypy/trunk/src/pypy/translator/classtyper.py ============================================================================== --- pypy/trunk/src/pypy/translator/classtyper.py (original) +++ pypy/trunk/src/pypy/translator/classtyper.py Fri Sep 10 14:58:06 2004 @@ -17,10 +17,11 @@ class ClassField: "An attribute of a class, mapped to some field(s) of a low-level struct." - def __init__(self, hltype, name, llclass): + def __init__(self, hltype, name, llclass, is_class_attr): self.hltype = hltype self.name = name self.llclass = llclass + self.is_class_attr = is_class_attr varname = '%s_%s' % (llclass.field_prefix, name) # to avoid name collisions between the 2nd lltype of a field called xyz # and another field whose name is exactly xyz_1, forbid field names @@ -48,49 +49,50 @@ """Low-level representation of a class as a structure and global functions that operate on it.""" - def __init__(self, typeset, name, cdef): + def __init__(self, typeset, name, cdef, llparent): LLTyper.__init__(self, typeset) self.typeset = typeset self.name = name self.cdef = cdef # instance of pypy.annotator.factory.ClassDef + self.llparent = llparent self.bindings = typeset.bindings + self.s_instance = annmodel.SomeInstance(self.cdef) # collect the fields that the annotator deduced for this class cls = cdef.cls mainletters = [c.lower() for c in cls.__name__ if 'A' <= c <= 'Z'] self.field_prefix = ''.join(mainletters[:3] or ['f']) - self.fields = [ClassField(typeset.gethltype(s_value), attr, self) - 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 == R_OBJECT] - self.s_instance = annmodel.SomeInstance(self.cdef) - - def getparent(self): - if self.cdef.basedef is None: - return None + self.fields_here = [ClassField(typeset.gethltype(s_value), attr, self, + is_class_attr = cdef.readonly[attr]) + for attr, s_value in cdef.attrs.items()] + # fields are divided in instance attributes and class attributes + # according to whether they are ever accessed with SET_ATTR or not + if llparent: + self.instance_fields = list(llparent.instance_fields) + self.class_fields = list(llparent.class_fields) else: - return self.typeset.genc.llclasses[self.cdef.basedef.cls] + self.instance_fields = [] + self.class_fields = [] + self.instance_fields += [fld for fld in self.fields_here + if not fld.is_class_attr] + self.class_fields += [fld for fld in self.fields_here + if fld.is_class_attr] - def getfield(self, name): + def get_instance_field(self, name): """Returns the ClassField corresponding to this attribute name. Keep in mind that it might be from some parent LLClass.""" - while self is not None: - for fld in self.fields: - if fld.name == name: - return fld - self = self.getparent() + for fld in self.instance_fields: + if fld.name == name: + return fld return None - def getfields(self): - "Return the list of all fields, including the ones from the parent." - parent = self.getparent() - if parent is None: - fields = [] - else: - fields = parent.getfields() - fields += self.fields - return fields + def get_class_field(self, name): + """Returns the ClassField corresponding to this class attribute name. + Keep in mind that it might be from some parent LLClass.""" + for fld in self.class_fields: + if fld.name == name: + return fld + return None def get_management_functions(self): "Generate LLFunctions that operate on this class' structure." @@ -115,8 +117,8 @@ cls = self.cdef.cls v1 = op('alloc_instance', Constant(cls), s_result = self.s_instance) - # class attributes are used as defaults to initialize fields - for fld in self.getfields(): + # class attributes are used as defaults to initialize instance fields + for fld in self.instance_fields: if hasattr(cls, fld.name): value = getattr(cls, fld.name) op('setattr', v1, Constant(fld.name), Constant(value), Modified: pypy/trunk/src/pypy/translator/genc.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc.py (original) +++ pypy/trunk/src/pypy/translator/genc.py Fri Sep 10 14:58:06 2004 @@ -91,12 +91,19 @@ return n = 0 for cdef in self.translator.annotator.getuserclassdefinitions(): + # annotation.factory guarantees that this will enumerate + # the ClassDefs in a parent-first, children-last order. cls = cdef.cls assert cls not in self.llclasses, '%r duplicate' % (cls,) + if cdef.basedef is None: + llparent = None + else: + llparent = self.llclasses[cdef.basedef.cls] llclass = LLClass( typeset = self.typeset, name = '%s__%d' % (cls.__name__, n), cdef = cdef, + llparent = llparent, ) self.llclasses[cls] = llclass self.classeslist.append(llclass) @@ -243,12 +250,12 @@ 'name': llclass.name, 'base': '0', } - if llclass.getparent() is not None: - info['base'] = '&%s_Type' % llclass.getparent().name + if llclass.llparent is not None: + info['base'] = '&%s_Type' % llclass.llparent.name # print the C struct declaration print >> f, self.C_STRUCT_HEADER % info - for fld in llclass.getfields(): + for fld in llclass.instance_fields: for llvar in fld.llvars: print >> f, '\t%s %s;' % (llvar.type, llvar.name) print >> f, self.C_STRUCT_FOOTER % info @@ -257,7 +264,7 @@ # other functions are generated by LLClass.get_management_functions() print >> f, self.C_DEALLOC_HEADER % info llxdecref = self.typeset.rawoperations['xdecref'] - for fld in llclass.getfields(): + for fld in llclass.instance_fields: llvars = fld.getllvars('op->%s') line = llxdecref(llvars) code = line.write() @@ -269,7 +276,10 @@ # generate the member list for the type object print >> f, self.C_MEMBERLIST_HEADER % info # XXX write member definitions for member with well-known types only - for fld in llclass.fields: # members from parent inherited via tp_base + # members from the parents are inherited via tp_base + for fld in llclass.fields_here: + if fld.is_class_attr: + continue # XXX should provide a reader if fld.hltype == R_OBJECT: t = 'T_OBJECT_EX' elif fld.hltype == R_INT: 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 Fri Sep 10 14:58:06 2004 @@ -182,7 +182,7 @@ r_obj, r_attr, r_result = hltypes if isinstance(r_obj, CInstance) and isinstance(r_attr, CConstant): # record the OP_GETATTR operation for this field - fld = r_obj.llclass.getfield(r_attr.value) + fld = r_obj.llclass.get_instance_field(r_attr.value) if fld is not None: sig = (r_obj, constant_representation(fld.name), fld.hltype) yield sig, genc_op.LoGetAttr.With( @@ -195,7 +195,7 @@ r_obj, r_attr, r_value, r_voidresult = hltypes if isinstance(r_obj, CInstance): # record the OP_SETATTR operation for this field - fld = r_obj.llclass.getfield(r_attr.value) + fld = r_obj.llclass.get_instance_field(r_attr.value) if fld is not None: sig = (r_obj, constant_representation(fld.name), fld.hltype, R_VOID) From arigo at codespeak.net Fri Sep 10 16:44:46 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 10 Sep 2004 16:44:46 +0200 (MEST) Subject: [pypy-svn] r6382 - in pypy/trunk/src/pypy/translator: . test Message-ID: <20040910144446.617D95AAAF@thoth.codespeak.net> Author: arigo Date: Fri Sep 10 16:44:45 2004 New Revision: 6382 Modified: pypy/trunk/src/pypy/translator/classtyper.py pypy/trunk/src/pypy/translator/genc.h 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/test/snippet.py pypy/trunk/src/pypy/translator/test/test_ctrans.py Log: Class attributes. (Methods are the next step.) This is done by storing the attributes in an extension of the PyTypeObjects, as described in genc.py. There is a bit of code duplication between class and instance attributes, that should be cleaned up sometime. --This line, and those below, will be ignored-- M translator/test/test_ctrans.py M translator/test/snippet.py M translator/classtyper.py M translator/genc_typeset.py M translator/genc_op.py M translator/genc.h M translator/genc.py Modified: pypy/trunk/src/pypy/translator/classtyper.py ============================================================================== --- pypy/trunk/src/pypy/translator/classtyper.py (original) +++ pypy/trunk/src/pypy/translator/classtyper.py Fri Sep 10 16:44:45 2004 @@ -97,6 +97,7 @@ def get_management_functions(self): "Generate LLFunctions that operate on this class' structure." yield self.make_fn_new() + yield self.make_fn_typenew() def build_llfunc(self, graph): return LLFunction(self.typeset, graph.name, graph) @@ -128,3 +129,21 @@ self.bindings[graph.getreturnvar()] = self.bindings[v1] b.closeblock(Link([v1], graph.returnblock)) return self.build_llfunc(graph) + + def make_fn_typenew(self): + # generate the flow graph of the xxx_typenew() function that + # initializes the class attributes of the type object + b = Block([]) + op = self.put_op(b) + cls = self.cdef.cls + v1 = Constant(cls) + # initialize class attributes + for fld in self.class_fields: + value = getattr(cls, fld.name) + op('initclassattr', v1, Constant(fld.name), Constant(value), + s_result = annmodel.SomeImpossibleValue()) + # finally, return None + graph = FunctionGraph('%s_typenew' % self.name, b) + self.bindings[graph.getreturnvar()] = annmodel.immutablevalue(None) + b.closeblock(Link([Constant(None)], graph.returnblock)) + return self.build_llfunc(graph) Modified: pypy/trunk/src/pypy/translator/genc.h ============================================================================== --- pypy/trunk/src/pypy/translator/genc.h (original) +++ pypy/trunk/src/pypy/translator/genc.h Fri Sep 10 16:44:45 2004 @@ -97,12 +97,18 @@ #define INSTANTIATE(cls, r, err) if (!(r=cls##_new())) goto err; #define ALLOC_INSTANCE(cls, r, err) \ - if (!(r=PyType_GenericAlloc(&cls##_Type, 0))) goto err; + if (!(r=PyType_GenericAlloc(&cls##_Type.type, 0))) goto err; +#define SETUP_TYPE(cls) \ + PyType_Ready(&cls##_Type.type); \ + cls##_typenew(); #define GET_ATTR_py(fld, r) r=fld; Py_INCREF(r); #define SET_ATTR_py(fld, v) { PyObject* o=fld; fld=v; \ Py_INCREF(v); Py_XDECREF(o); } +#define GET_ATTR_cls(fld, r) r=fld; Py_INCREF(r); +#define SET_ATTR_cls(fld, v) fld=v; Py_INCREF(v); /* initialization only */ + /* a few built-in functions */ #define CALL_len_oi(o,r,err) if ((r=PyObject_Size(o))<0) goto err; Modified: pypy/trunk/src/pypy/translator/genc.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc.py (original) +++ pypy/trunk/src/pypy/translator/genc.py Fri Sep 10 16:44:45 2004 @@ -251,7 +251,7 @@ 'base': '0', } if llclass.llparent is not None: - info['base'] = '&%s_Type' % llclass.llparent.name + info['base'] = '&%s_Type.type' % llclass.llparent.name # print the C struct declaration print >> f, self.C_STRUCT_HEADER % info @@ -260,6 +260,14 @@ print >> f, '\t%s %s;' % (llvar.type, llvar.name) print >> f, self.C_STRUCT_FOOTER % info + # print the struct Xxx_TypeObject, which is an extension of + # PyTypeObject with the class attributes of this class + print >> f, self.C_TYPESTRUCT_HEADER % info + for fld in llclass.class_fields: + for llvar in fld.llvars: + print >> f, '\t%s %s;' % (llvar.type, llvar.name) + print >> f, self.C_TYPESTRUCT_FOOTER % info + # generate the deallocator function -- must special-case it; # other functions are generated by LLClass.get_management_functions() print >> f, self.C_DEALLOC_HEADER % info @@ -293,7 +301,7 @@ # declare and initialize the static PyTypeObject print >> f, self.C_TYPEOBJECT % info - self.initializationcode += (self.C_INITTYPE % info).split('\n') + self.initializationcode.append('SETUP_TYPE(%s)' % llclass.name) # ____________________________________________________________ @@ -332,7 +340,7 @@ C_DEALLOC_HEADER = '''static void %(name)s_dealloc(%(name)s_Object* op) {''' - C_DEALLOC_FOOTER = ''' op->ob_type->tp_free((PyObject*) op); + C_DEALLOC_FOOTER = ''' PyObject_Del((PyObject*) op); } ''' @@ -342,7 +350,24 @@ }; ''' - C_TYPEOBJECT = '''static PyTypeObject %(name)s_Type = { + # NB: our types don't have Py_TPFLAGS_BASETYPE because we do not want + # the complications of dynamically created subclasses. This doesn't + # prevent the various types from inheriting from each other via + # tp_base. This is ok because we expect all RPython classes to exist + # and be analyzed in advance. This allows class attributes to be stored + # as an extensison of the PyTypeObject structure, which are then + # accessed with ((PyXxxTypeObject*)op->ob_type)->classattrname. + # This doesn't work if op->ob_type can point to a heap-allocated + # type object over which we have no control. + + C_TYPESTRUCT_HEADER = '''typedef struct { + PyTypeObject type; + /* class attributes follow */''' + + C_TYPESTRUCT_FOOTER = '''} %(name)s_TypeObject; +''' + + C_TYPEOBJECT = '''static %(name)s_TypeObject %(name)s_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, "%(name)s", @@ -363,7 +388,7 @@ PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericSetAttr, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ 0, /* tp_doc */ 0, /* XXX need GC */ /* tp_traverse */ 0, /* tp_clear */ @@ -386,6 +411,4 @@ }; ''' - C_INITTYPE = '''PyType_Ready(&%(name)s_Type);''' - # ____________________________________________________________ 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 Fri Sep 10 16:44:45 2004 @@ -158,25 +158,34 @@ assert len(result) == len(self.fld.llvars) ls = [] llclass = self.fld.llclass - for src, dstname in zip(self.fld.llvars, result): - fldexpr = '((%s_Object*) %s)->%s' % (llclass.name, inst, - src.name) - if src.type == 'PyObject*': - ls.append('GET_ATTR_py(%s, %s)' % (fldexpr, dstname)) - else: - ls.append('%s = %s;' % (dstname, fldexpr)) + if self.fld.is_class_attr: + for src, dstname in zip(self.fld.llvars, result): + fldexpr = '((%s_TypeObject*)(%s->ob_type))->%s' % ( + llclass.name, inst, src.name) + if src.type == 'PyObject*': + ls.append('GET_ATTR_cls(%s, %s)' % (fldexpr, dstname)) + else: + ls.append('%s = %s;' % (dstname, fldexpr)) + else: + for src, dstname in zip(self.fld.llvars, result): + fldexpr = '((%s_Object*) %s)->%s' % (llclass.name, inst, + src.name) + if src.type == 'PyObject*': + ls.append('GET_ATTR_py(%s, %s)' % (fldexpr, dstname)) + else: + ls.append('%s = %s;' % (dstname, fldexpr)) return '\n'.join(ls) class LoSetAttr(LoC): - cost = 1 - fld = PARAMETER + cost = 1 + llclass = PARAMETER # the class involved in the operation + fld = PARAMETER # the field, which might come from a parent class def writestr(self, inst, *value): assert len(value) == len(self.fld.llvars) ls = [] - llclass = self.fld.llclass for srcname, dst in zip(value, self.fld.llvars): - fldexpr = '((%s_Object*) %s)->%s' % (llclass.name, inst, + fldexpr = '((%s_Object*) %s)->%s' % (self.llclass.name, inst, dst.name) if dst.type == 'PyObject*': ls.append('SET_ATTR_py(%s, %s)' % (fldexpr, srcname)) @@ -184,6 +193,23 @@ ls.append('%s = %s;' % (fldexpr, srcname)) return '\n'.join(ls) +class LoInitClassAttr(LoC): + cost = 1 + llclass = PARAMETER # the class involved in the operation + fld = PARAMETER # the field, which might come from a parent class + + def writestr(self, *value): + assert len(value) == len(self.fld.llvars) + ls = [] + # setting class attributes is only used for initialization + for srcname, dst in zip(value, self.fld.llvars): + fldexpr = '%s_Type.%s' % (self.llclass.name, dst.name) + if dst.type == 'PyObject*': + ls.append('SET_ATTR_cls(%s, %s)' % (fldexpr, srcname)) + else: + ls.append('%s = %s;' % (fldexpr, srcname)) + return '\n'.join(ls) + class LoConvertChain(LoOptimized): r_from = PARAMETER r_middle = PARAMETER 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 Fri Sep 10 16:44:45 2004 @@ -180,11 +180,14 @@ if len(hltypes) != 3: return r_obj, r_attr, r_result = hltypes - if isinstance(r_obj, CInstance) and isinstance(r_attr, CConstant): + if not isinstance(r_attr, CConstant): + return + if isinstance(r_obj, CInstance): # record the OP_GETATTR operation for this field - fld = r_obj.llclass.get_instance_field(r_attr.value) + fld = (r_obj.llclass.get_instance_field(r_attr.value) or + r_obj.llclass.get_class_field(r_attr.value)) if fld is not None: - sig = (r_obj, constant_representation(fld.name), fld.hltype) + sig = (r_obj, r_attr, fld.hltype) yield sig, genc_op.LoGetAttr.With( fld = fld, ) @@ -193,16 +196,35 @@ if len(hltypes) != 4: return r_obj, r_attr, r_value, r_voidresult = hltypes + if not isinstance(r_attr, CConstant): + return if isinstance(r_obj, CInstance): # record the OP_SETATTR operation for this field fld = r_obj.llclass.get_instance_field(r_attr.value) if fld is not None: - sig = (r_obj, constant_representation(fld.name), fld.hltype, - R_VOID) + sig = (r_obj, r_attr, fld.hltype, R_VOID) yield sig, genc_op.LoSetAttr.With( fld = fld, + llclass = r_obj.llclass, ) + def extend_OP_INITCLASSATTR(self, hltypes): + # only to initialize class attributes + if len(hltypes) != 4: + return + r_obj, r_attr, r_value, r_voidresult = hltypes + if isinstance(r_attr, CConstant) and isinstance(r_obj, CConstant): + cls = r_obj.value + if cls in self.genc.llclasses: + llclass = self.genc.llclasses[cls] + fld = llclass.get_class_field(r_attr.value) + if fld is not None: + sig = (r_obj, r_attr, fld.hltype, R_VOID) + yield sig, genc_op.LoInitClassAttr.With( + fld = fld, + llclass = llclass, + ) + # ____________________________________________________________ def parse_operation_templates(self): Modified: pypy/trunk/src/pypy/translator/test/snippet.py ============================================================================== --- pypy/trunk/src/pypy/translator/test/snippet.py (original) +++ pypy/trunk/src/pypy/translator/test/snippet.py Fri Sep 10 16:44:45 2004 @@ -319,6 +319,30 @@ def prime(n=int): return len([i for i in range(1,n+1) if n%i==0]) == 2 +class A1: + clsattr = 123 +class A2(A1): + clsattr = 456 +class A3(A2): + clsattr = 789 +class A4(A3): + pass +class A5(A1): + clsattr = 101112 + +def classattribute(flag=int): + if flag == 1: + x = A1() + elif flag == 2: + x = A2() + elif flag == 3: + x = A3() + elif flag == 4: + x = A4() + else: + x = A5() + return x.clsattr + class Z: def my_method(self): Modified: pypy/trunk/src/pypy/translator/test/test_ctrans.py ============================================================================== --- pypy/trunk/src/pypy/translator/test/test_ctrans.py (original) +++ pypy/trunk/src/pypy/translator/test/test_ctrans.py Fri Sep 10 16:44:45 2004 @@ -151,5 +151,13 @@ fn = self.getcompiled(tuple_repr) self.assertEquals(fn(6,'a'), (6,'a')) + def test_classattribute(self): + fn = self.getcompiled(snippet.classattribute) + self.assertEquals(fn(1), 123) + self.assertEquals(fn(2), 456) + self.assertEquals(fn(3), 789) + self.assertEquals(fn(4), 789) + self.assertEquals(fn(5), 101112) + if __name__ == '__main__': testit.main() From arigo at codespeak.net Fri Sep 10 19:48:58 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 10 Sep 2004 19:48:58 +0200 (MEST) Subject: [pypy-svn] r6385 - pypy/trunk/src/pypy/translator Message-ID: <20040910174858.1D2095AAAF@thoth.codespeak.net> Author: arigo Date: Fri Sep 10 19:48:55 2004 New Revision: 6385 Modified: pypy/trunk/src/pypy/translator/genc.py pypy/trunk/src/pypy/translator/genc_op.py pypy/trunk/src/pypy/translator/genc_repr.py pypy/trunk/src/pypy/translator/genc_typeset.py pypy/trunk/src/pypy/translator/typer.py Log: Support for C function pointers. Calling other C functions is now a special case of calling a C function pointer. If you really know C, you'll remember the strange syntax of its type declarations, e.g. how to declare a function that returns a pointer to another function... This is easier with typedefs to give a name to a type "pointer to function with such-and-such signature", but after some hesitations this is now done without typedefs using the approach outlined in genc_repr.cdecl(). A masochistic C nerd might consider enabling the declaration of several variables of the same type in a single line: PyObject *v1, *v2, *v3; This is really messy because the "*" must be repeated (or more precisely, attached to the name instead of the type). Currently we just pretend the type is "PyObject*" and the names "v1", "v2", "v3", etc. Modified: pypy/trunk/src/pypy/translator/genc.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc.py (original) +++ pypy/trunk/src/pypy/translator/genc.py Fri Sep 10 19:48:55 2004 @@ -10,7 +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 +from pypy.translator.genc_repr import R_INT, R_OBJECT, cdecl # ____________________________________________________________ @@ -149,16 +149,15 @@ # if there is more than one return LLVar, only the first one is # returned and the other ones are returned via ptr output args retlltype = llret[0].type - llargs += [LLVar(a.type+'*', 'output_'+a.name) for a in llret[1:]] + llargs += [LLVar(a.type, '*output_'+a.name) for a in llret[1:]] return llargs, retlltype def cfunction_header(self, llfunc): llargs, rettype = self.get_llfunc_header(llfunc) - l = ['%s %s' % (a.type, a.name) for a in llargs] + l = [cdecl(a.type, a.name) for a in llargs] l = l or ['void'] - return 'static %s %s(%s)' % (rettype or 'int', - llfunc.name, - ', '.join(l)) + return 'static ' + cdecl(rettype or 'int', + '%s(%s)' % (llfunc.name, ', '.join(l))) def gen_entrypoint(self): f = self.f @@ -169,7 +168,7 @@ l = [] l2 = [] for a in llargs: - print >> f, '\t%s %s;' % (a.type, a.name) + print >> f, '\t%s;' % cdecl(a.type, a.name) l.append('&' + a.name) l2.append(a.name) formatstr = [] @@ -188,7 +187,7 @@ # generate the body of the function llargs, rettype = self.get_llfunc_header(llfunc) - error_retval = LLConst(rettype, ERROR_RETVAL[rettype]) + error_retval = LLConst(rettype, ERROR_RETVAL.get(rettype, 'NULL')) body = list(llfunc.ll_body([error_retval])) # print the declaration of the new global constants needed by @@ -206,7 +205,7 @@ if to_declare: print >> f, '/* global constant%s */' % ('s'*(len(to_declare)>1)) for a in to_declare: - print >> f, 'static %s %s;' % (a.type, a.name) + print >> f, 'static %s;' % cdecl(a.type, a.name) print >> f # print header @@ -224,7 +223,7 @@ for a in lllocals: if a not in seen: if not isinstance(a, LLConst): - print >> f, '\t%s %s;' % (a.type, a.name) + print >> f, '\t%s;' % cdecl(a.type, a.name) seen[a] = True print >> f @@ -257,7 +256,7 @@ print >> f, self.C_STRUCT_HEADER % info for fld in llclass.instance_fields: for llvar in fld.llvars: - print >> f, '\t%s %s;' % (llvar.type, llvar.name) + print >> f, '\t%s;' % cdecl(llvar.type, llvar.name) print >> f, self.C_STRUCT_FOOTER % info # print the struct Xxx_TypeObject, which is an extension of @@ -265,7 +264,7 @@ print >> f, self.C_TYPESTRUCT_HEADER % info for fld in llclass.class_fields: for llvar in fld.llvars: - print >> f, '\t%s %s;' % (llvar.type, llvar.name) + print >> f, '\t%s;' % cdecl(llvar.type, llvar.name) print >> f, self.C_TYPESTRUCT_FOOTER % info # generate the deallocator function -- must special-case it; 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 Fri Sep 10 19:48:55 2004 @@ -281,27 +281,25 @@ ERROR_RETVAL = { None: '-1', 'int': '-1', - 'PyObject*': 'NULL', } ERROR_CHECK = { None: '< 0', 'int': '== -1 && PyErr_Occurred()', - 'PyObject*': '== NULL', } class LoCallPyFunction(LoC): can_fail = True - llfunc = PARAMETER hlrettype = PARAMETER def write(self): + funcptr = self.args[0].name L = len(self.hlrettype.impl) R = len(self.args) - L - args = [a.name for a in self.args[:R]] + args = [a.name for a in self.args[1:R]] err = self.errtarget if L == 0: # no return value return 'if (%s(%s) %s) goto %s;' % ( - self.llfunc.name, ', '.join(args), ERROR_CHECK[None], err) + funcptr, ', '.join(args), ERROR_CHECK[None], err) else: # the return value is the first return LLVar: retvar = self.args[R] @@ -309,8 +307,8 @@ # in by reference as output arguments args += ['&%s' % a.name for a in self.args[R+1:]] return ('if ((%s = %s(%s)) %s) goto %s;' % ( - retvar.name, self.llfunc.name, ', '.join(args), - ERROR_CHECK[retvar.type], err)) + retvar.name, funcptr, ', '.join(args), + ERROR_CHECK.get(retvar.type, '== NULL'), err)) class LoReturn(LoC): cost = 1 Modified: pypy/trunk/src/pypy/translator/genc_repr.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc_repr.py (original) +++ pypy/trunk/src/pypy/translator/genc_repr.py Fri Sep 10 19:48:55 2004 @@ -104,6 +104,27 @@ else: raise CannotConvert +class CConstantFunction(CConstant): + def convert_to(self, target, typeset): + if isinstance(target, CFunction): + name, my_header_r = self.get_function_signature(typeset) + if my_header_r != target.header_r: + raise CannotConvert(self, my_header_r, target) + cfunctype, = target.impl + return genc_op.LoKnownAnswer.With( + known_answer = [LLConst(cfunctype, name)], + ) + else: + raise CannotConvert + + def get_function_signature(self, typeset): + fn = self.value + if fn in typeset.genc.llfunctions: + llfunc = typeset.genc.llfunctions[fn] + return llfunc.name, llfunc.hl_header() + else: + return None, None + class CTuple(CRepr): "An unpacked tuple. Represents all its items independently." @@ -173,6 +194,39 @@ else: raise CannotConvert + +class CFunction(CRepr): + "A C-level function or a pointer to a function." + + def __init__(self, header_r): + self.header_r = list(header_r) # CReprs: [arg1, arg2, ..., retval] + # C syntax is quite strange: a pointer to a function is declared + # with a syntax like "int (*varname)(long, char*)". We build here + # a 'type' string like "int (*@)(long, char*)" that will be combined + # with an actual name by cdecl() below. Oh, and by the way, to + # make a string like "int (*@)(long, char*)" we need to use cdecl() + # as well, because the "int" which is the return type could itself + # contain a "@"... for functions returning pointers to functions. + llargs = [] + for r in header_r[:-1]: + llargs += [cdecl(atype, '') for atype in r.impl] + llret = header_r[-1].impl + # same logic as get_llfunc_header() + if len(llret) == 0: + retlltype = 'int' # no return value needed, returns an error flag + elif len(llret) == 1: + retlltype = llret[0] + else: + # if there is more than one C return type, only the first one is + # returned and the other ones are returned via ptr output args + retlltype = llret[0] + llargs += [cdecl(atype, '*') for atype in llret[1:]] + cfunctype = cdecl(retlltype, '(*@)(%s)' % ', '.join(llargs)) + self.impl = [cfunctype] + + def get_function_signature(self, typeset): + return None, self.header_r + # ____________________________________________________________ # # Predefined CReprs and caches for building more @@ -185,6 +239,7 @@ R_TUPLE_CACHE = {} R_CONSTANT_CACHE = {} R_INSTANCE_CACHE = {} +R_FUNCTION_CACHE = {} def tuple_representation(items_r): items_r = tuple(items_r) @@ -199,6 +254,8 @@ str: CConstantStr, types.NoneType: CConstantNone, types.BuiltinFunctionType: CConstantBuiltin, + types.FunctionType: CConstantFunction, +# types.MethodType: CConstantMethod, } def constant_representation(value): @@ -227,6 +284,14 @@ r = R_INSTANCE_CACHE[llclass] = CInstance(llclass) return r +def function_representation(sig): + sig = tuple(sig) + try: + return R_FUNCTION_CACHE[sig] + except KeyError: + r = R_FUNCTION_CACHE[sig] = CFunction(sig) + return r + def c_str(s): "Return the C expression for the string 's'." @@ -246,3 +311,14 @@ c = '_%02x' % ord(c) l.append(c) return ''.join(l) + +def cdecl(type, name): + # Utility to generate a typed name declaration in C syntax. + # For local variables, struct fields, function declarations, etc. + # For complex C types, the 'type' can contain a '@' character that + # specifies where the 'name' should be inserted; for example, an + # array of 10 ints has a type of "int @[10]". + if '@' in type: + return type.replace('@', name) + else: + return ('%s %s' % (type, name)).rstrip() 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 Fri Sep 10 19:48:55 2004 @@ -3,11 +3,13 @@ from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant from pypy.annotation import model as annmodel from pypy.translator import genc_op -from pypy.translator.typer import CannotConvert +from pypy.translator.typer import CannotConvert, TypingError 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 instance_representation, CInstance +from pypy.translator.genc_repr import function_representation, CFunction +from pypy.translator.genc_repr import CConstantFunction class CTypeSet: @@ -59,6 +61,9 @@ if isinstance(var, annmodel.SomeInstance): llclass = self.genc.llclasses[var.knowntype] return instance_representation(llclass) + if isinstance(var, annmodel.SomeFunction): + func_sig = self.regroup_func_sig(var.funcs) + return function_representation(func_sig) # fall-back return R_OBJECT if isinstance(var, UndefinedConstant): @@ -73,7 +78,7 @@ def getconversion(self, hltype1, hltype2): sig = hltype1, hltype2 if sig in self.conversion_errors: - raise CannotConvert # shortcut + raise CannotConvert(hltype1, hltype2) # shortcut try: return self.conversion_cache[sig] except KeyError: @@ -81,8 +86,10 @@ llopcls = hltype1.convert_to(hltype2, self) self.conversion_cache[sig] = llopcls return llopcls - except CannotConvert: + except CannotConvert, e: self.conversion_errors[sig] = True + if not e.args: + e.args = hltype1, hltype2 raise def typemismatch(self, opname, hltypes): @@ -102,6 +109,22 @@ # ____________________________________________________________ + def regroup_func_sig(self, funcs): + # find a common signature for the given functions + signatures = [] + for func in funcs: + llfunc = self.genc.llfunctions[func] + signatures.append(llfunc.hl_header()) + # XXX we expect the functions to have exactly the same signature + # but this is currently not enforced by the annotator. + assert signatures + for test in signatures[1:]: + if test != signatures[0]: + raise TypingError(signatures[0], test) + return signatures[0] + + # ____________________________________________________________ + def extend_OP_NEWLIST(self, hltypes): # LoNewList can build a list of any length from PyObject* args. sig = (R_OBJECT,) * len(hltypes) @@ -122,26 +145,23 @@ # it can be converted to PyObject*. sig = (R_OBJECT,) * len(hltypes) yield sig, genc_op.LoCallFunction + # Calling another user-defined generated function + r = hltypes[0] + if isinstance(r, (CConstantFunction, CFunction)): + # it might be a known one or just one we have a pointer to + sig = tuple(r.get_function_signature(self)[1]) + r_func = function_representation(sig) + sig = (r_func,) + sig + yield sig, genc_op.LoCallPyFunction.With( + hlrettype = sig[-1], + ) # 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) - yield tuple(sig), genc_op.LoCallPyFunction.With( - llfunc = llfunc, - hlrettype = hltype, - ) # Instantiating a user-defined class if fn in self.genc.llclasses: # XXX do __init__ Modified: pypy/trunk/src/pypy/translator/typer.py ============================================================================== --- pypy/trunk/src/pypy/translator/typer.py (original) +++ pypy/trunk/src/pypy/translator/typer.py Fri Sep 10 19:48:55 2004 @@ -38,6 +38,9 @@ class TypingError(Exception): pass +class CannotConvert(Exception): + pass + # ____________________________________________________________ # @@ -126,6 +129,19 @@ self.name = name self.graph = graph + def hl_header(self): + """ + Get the high-level signature (argument types and return type). + """ + result = [] + for v in self.graph.getargs(): + self.makevar(v) + result.append(self.hltypes[v]) + v = self.graph.getreturnvar() + self.makevar(v) + result.append(self.hltypes[v]) + return result + def ll_header(self): """ Get the low-level representation of the header. @@ -442,7 +458,3 @@ elif not N: return yield self.release_operation - - -class CannotConvert: - pass From arigo at codespeak.net Sat Sep 11 00:45:11 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 11 Sep 2004 00:45:11 +0200 (MEST) Subject: [pypy-svn] r6387 - in pypy/trunk/src/pypy: annotation translator Message-ID: <20040910224511.56FE85AAAF@thoth.codespeak.net> Author: arigo Date: Sat Sep 11 00:45:10 2004 New Revision: 6387 Modified: pypy/trunk/src/pypy/annotation/model.py 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_repr.py pypy/trunk/src/pypy/translator/genc_typeset.py pypy/trunk/src/pypy/translator/typer.py Log: All tests pass! Argh, method support was non-obvious. There is an "XXX messy", too. But the idea is clean: a CMethod representation represents a callable together with a PyObject*. Calling it is equivalent to calling the underlying function with an extra first argument of type R_INSTANCE. We rely on the optimization of LLOp.optimize() to avoid copying around the 'self' argument all the time. This is equivalent to Psyco's virtualized objects; in Psyco a virtual method object is one that is not actually built by the generated machine code, but the compiler (Psyco or typer.py) remembers that there is a method object that virtually exists, with a 'self' field still stored in some other register, that doesn't have to be really copied physically. (A machine register for Psyco is the equivalent of a LLVar in typer.py.) Modified: pypy/trunk/src/pypy/annotation/model.py ============================================================================== --- pypy/trunk/src/pypy/annotation/model.py (original) +++ pypy/trunk/src/pypy/annotation/model.py Sat Sep 11 00:45:10 2004 @@ -122,6 +122,8 @@ knowntype = FunctionType def __init__(self, funcs): self.funcs = funcs # set of functions that this one may be + if len(funcs) == 1: + self.const, = funcs class SomeMethod(SomeObject): "Stands for a bound Python method (or some method out of a list)." Modified: pypy/trunk/src/pypy/translator/classtyper.py ============================================================================== --- pypy/trunk/src/pypy/translator/classtyper.py (original) +++ pypy/trunk/src/pypy/translator/classtyper.py Sat Sep 11 00:45:10 2004 @@ -58,18 +58,27 @@ self.bindings = typeset.bindings self.s_instance = annmodel.SomeInstance(self.cdef) + def setup(self): + # two-phase initialization. The code below might require other + # classes to be already present in GenC.llclasses. + # collect the fields that the annotator deduced for this class + typeset = self.typeset + cdef = self.cdef cls = cdef.cls mainletters = [c.lower() for c in cls.__name__ if 'A' <= c <= 'Z'] self.field_prefix = ''.join(mainletters[:3] or ['f']) - self.fields_here = [ClassField(typeset.gethltype(s_value), attr, self, - is_class_attr = cdef.readonly[attr]) - for attr, s_value in cdef.attrs.items()] + self.fields_here = [] + for attr, s_value in cdef.attrs.items(): + is_class_attr = cdef.readonly[attr] + hltype = typeset.gethltype(s_value, unbound=is_class_attr) + fld = ClassField(hltype, attr, self, is_class_attr) + self.fields_here.append(fld) # fields are divided in instance attributes and class attributes # according to whether they are ever accessed with SET_ATTR or not - if llparent: - self.instance_fields = list(llparent.instance_fields) - self.class_fields = list(llparent.class_fields) + if self.llparent: + self.instance_fields = list(self.llparent.instance_fields) + self.class_fields = list(self.llparent.class_fields) else: self.instance_fields = [] self.class_fields = [] Modified: pypy/trunk/src/pypy/translator/genc.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc.py (original) +++ pypy/trunk/src/pypy/translator/genc.py Sat Sep 11 00:45:10 2004 @@ -41,8 +41,9 @@ self.initializationcode = [] self.llclasses = {}; self.classeslist = [] self.llfunctions = {}; self.functionslist = [] - self.build_llclasses() + # must build functions first, otherwise methods are not found self.build_llfunctions() + self.build_llclasses() self.build_llentrypoint() self.gen_source() @@ -107,8 +108,13 @@ ) self.llclasses[cls] = llclass self.classeslist.append(llclass) - self.functionslist += llclass.get_management_functions() n += 1 + for llclass in self.classeslist: + llclass.setup() + management_functions = [] + for llclass in self.classeslist: + management_functions += llclass.get_management_functions() + self.functionslist[:0] = management_functions def build_llfunctions(self): n = 0 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 Sat Sep 11 00:45:10 2004 @@ -47,6 +47,12 @@ class LoOptimized(LoC): def write(self): raise NotImplementedError, 'should be optimized away' + def optimize(self, typer, llresult): + # patch the result with the known answer + constantsllrepr = self.optimized_result(typer) + assert len(constantsllrepr) == len(llresult) + llresult[:] = constantsllrepr + return True # ____________________________________________________________ @@ -61,7 +67,7 @@ class LoKnownAnswer(LoOptimized): known_answer = PARAMETER cost = 0 - def optimize(self, typer): + def optimized_result(self, typer): return self.known_answer class LoNewList(LoC): @@ -109,7 +115,7 @@ target_r = PARAMETER # tuple-of-hltypes, one per item of the output tuple cost = PARAMETER - def optimize(self, typer): + def optimized_result(self, typer): # replace this complex conversion by the simpler conversion of # only the items that changed llinputs = [] @@ -155,7 +161,6 @@ fld = PARAMETER def writestr(self, inst, *result): - assert len(result) == len(self.fld.llvars) ls = [] llclass = self.fld.llclass if self.fld.is_class_attr: @@ -176,6 +181,15 @@ ls.append('%s = %s;' % (dstname, fldexpr)) return '\n'.join(ls) +class LoGetAttrMethod(LoGetAttr): + def optimize(self, typer, llresult): + # for a OP_GETATTR that must return a bound method. The 'self' + # part of the result can be statically copied from self.args[0]. + # The rest is done as with LoGetAttr. + inst = self.args[0] + llresult[-1] = inst # patch to do the copy statically + return False # proceed with the normal LoGetAttr.writestr() + class LoSetAttr(LoC): cost = 1 llclass = PARAMETER # the class involved in the operation @@ -210,13 +224,30 @@ ls.append('%s = %s;' % (fldexpr, srcname)) return '\n'.join(ls) +class LoConvertBoundMethod(LoOptimized): + r_source = PARAMETER + r_target = PARAMETER + cost = PARAMETER + + def optimized_result(self, typer): + # self.args: [input-func..., PyObject*, output-func..., PyObject*] + slen = len(self.r_source.impl) + tlen = len(self.r_target.impl) + assert len(self.args) == slen+1+tlen+1 + # convert the 'func' part of the method + answer = typer.convert(self.r_source, self.args[:slen], + self.r_target, self.args[slen+1:-1]) + # pass the 'self' argument unchanged + answer.append(self.args[slen]) + return answer + class LoConvertChain(LoOptimized): r_from = PARAMETER r_middle = PARAMETER r_to = PARAMETER cost = PARAMETER - def optimize(self, typer): + def optimized_result(self, typer): half = len(self.r_from.impl) assert half + len(self.r_to.impl) == len(self.args) input = self.args[:half] @@ -248,7 +279,7 @@ class LoCopy(LoOptimized): cost = 0 - def optimize(self, typer): + def optimized_result(self, typer): # the result's llvars is equal to the input's llvars. assert len(self.args) % 2 == 0 half = len(self.args) // 2 Modified: pypy/trunk/src/pypy/translator/genc_repr.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc_repr.py (original) +++ pypy/trunk/src/pypy/translator/genc_repr.py Sat Sep 11 00:45:10 2004 @@ -107,24 +107,20 @@ class CConstantFunction(CConstant): def convert_to(self, target, typeset): if isinstance(target, CFunction): - name, my_header_r = self.get_function_signature(typeset) + fn = self.value + if fn not in typeset.genc.llfunctions: + raise CannotConvert + llfunc = typeset.genc.llfunctions[fn] + my_header_r = typeset.getfunctionsig(llfunc) if my_header_r != target.header_r: raise CannotConvert(self, my_header_r, target) cfunctype, = target.impl return genc_op.LoKnownAnswer.With( - known_answer = [LLConst(cfunctype, name)], + known_answer = [LLConst(cfunctype, llfunc.name)], ) else: raise CannotConvert - def get_function_signature(self, typeset): - fn = self.value - if fn in typeset.genc.llfunctions: - llfunc = typeset.genc.llfunctions[fn] - return llfunc.name, llfunc.hl_header() - else: - return None, None - class CTuple(CRepr): "An unpacked tuple. Represents all its items independently." @@ -176,17 +172,22 @@ impl = ['PyObject*'] def __init__(self, llclass): - self.llclass = llclass # instance of classtyper.LLClass + self.llclass = llclass # instance of classtyper.LLClass or None + # R_INSTANCE is CInstance(None): a match-all, any-type instance. def __repr__(self): - cls = self.llclass.cdef.cls - return 'CInstance(%s.%s)' % (cls.__module__, cls.__name__) + if self.llclass is None: + return 'CInstance(*)' + else: + cls = self.llclass.cdef.cls + return 'CInstance(%s.%s)' % (cls.__module__, cls.__name__) def convert_to(self, target, typeset): if isinstance(target, CInstance): - # can convert to an instance of a parent class - if target.llclass.cdef not in self.llclass.cdef.getmro(): - raise CannotConvert + if not (self.llclass is None or target.llclass is None): + # can only convert to an instance of a parent class + if target.llclass.cdef not in self.llclass.cdef.getmro(): + raise CannotConvert return genc_op.LoCopy elif target == R_OBJECT: # can convert to a generic PyObject* @@ -199,7 +200,7 @@ "A C-level function or a pointer to a function." def __init__(self, header_r): - self.header_r = list(header_r) # CReprs: [arg1, arg2, ..., retval] + self.header_r = header_r # list of CReprs: [arg1, arg2, ..., retval] # C syntax is quite strange: a pointer to a function is declared # with a syntax like "int (*varname)(long, char*)". We build here # a 'type' string like "int (*@)(long, char*)" that will be combined @@ -224,8 +225,30 @@ cfunctype = cdecl(retlltype, '(*@)(%s)' % ', '.join(llargs)) self.impl = [cfunctype] - def get_function_signature(self, typeset): - return None, self.header_r + def __repr__(self): + args = [repr(r) for r in self.header_r[:-1]] + return 'CFunction(%s -> %r)' % (', '.join(args), self.header_r[-1]) + + +class CMethod(CRepr): + "A CFunction or CConstantFunction with its first argument bound." + + def __init__(self, r_func): + self.r_func = r_func + self.impl = r_func.impl + ['PyObject*'] + + def __repr__(self): + return 'CMethod(%r)' % (self.r_func,) + + def convert_to(self, target, typeset): + if not isinstance(target, CMethod): + raise CannotConvert + cost = typeset.getconversion(self.r_func, target.r_func).cost + return genc_op.LoConvertBoundMethod.With( + r_source = self.r_func, + r_target = target.r_func, + cost = cost, + ) # ____________________________________________________________ # @@ -235,11 +258,13 @@ R_INT = CAtomicRepr(['int'], parse_code='i') R_OBJECT = CAtomicRepr(['PyObject*'], parse_code='O') R_UNDEFINED = CUndefined() +R_INSTANCE = CInstance(None) R_TUPLE_CACHE = {} R_CONSTANT_CACHE = {} R_INSTANCE_CACHE = {} R_FUNCTION_CACHE = {} +R_METHOD_CACHE = {} def tuple_representation(items_r): items_r = tuple(items_r) @@ -255,7 +280,6 @@ types.NoneType: CConstantNone, types.BuiltinFunctionType: CConstantBuiltin, types.FunctionType: CConstantFunction, -# types.MethodType: CConstantMethod, } def constant_representation(value): @@ -273,6 +297,9 @@ cls = CONST_TYPES[cls] break else: + if isinstance(value, types.MethodType) and value.im_self is None: + # replace unbound methods with plain funcs + return constant_representation(value.im_func) cls = CConstant r = R_CONSTANT_CACHE[key] = cls(value) return r @@ -285,11 +312,19 @@ return r def function_representation(sig): - sig = tuple(sig) + key = tuple(sig) + try: + return R_FUNCTION_CACHE[key] + except KeyError: + r = R_FUNCTION_CACHE[key] = CFunction(sig) + return r + +def method_representation(r_func): + key = r_func try: - return R_FUNCTION_CACHE[sig] + return R_METHOD_CACHE[key] except KeyError: - r = R_FUNCTION_CACHE[sig] = CFunction(sig) + r = R_METHOD_CACHE[key] = CMethod(r_func) return r 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 Sat Sep 11 00:45:10 2004 @@ -3,13 +3,15 @@ from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant from pypy.annotation import model as annmodel from pypy.translator import genc_op -from pypy.translator.typer import CannotConvert, TypingError +from pypy.translator.typer import CannotConvert, TypingError, LLConst from pypy.translator.genc_repr import R_VOID, R_INT, R_OBJECT, R_UNDEFINED +from pypy.translator.genc_repr import R_INSTANCE from pypy.translator.genc_repr import tuple_representation from pypy.translator.genc_repr import constant_representation, CConstant from pypy.translator.genc_repr import instance_representation, CInstance from pypy.translator.genc_repr import function_representation, CFunction from pypy.translator.genc_repr import CConstantFunction +from pypy.translator.genc_repr import method_representation, CMethod class CTypeSet: @@ -45,7 +47,7 @@ # 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): + def gethltype(self, var, unbound=False): if isinstance(var, Variable): var = self.bindings.get(var) or annmodel.SomeObject() if isinstance(var, annmodel.SomeObject): @@ -64,6 +66,12 @@ if isinstance(var, annmodel.SomeFunction): func_sig = self.regroup_func_sig(var.funcs) return function_representation(func_sig) + if isinstance(var, annmodel.SomeMethod): + r_func = self.gethltype(annmodel.SomeFunction(var.meths)) + if unbound: + return r_func + else: + return method_representation(r_func) # fall-back return R_OBJECT if isinstance(var, UndefinedConstant): @@ -109,19 +117,34 @@ # ____________________________________________________________ + def getfunctionsig(self, llfunc): + "Get the high-level header (argument types and result) for a function." + # XXX messy! we allow different CInstances to appear as function + # arguments by replacing them with the generic R_INSTANCE. + # This is not too clean, but it might be unavoidable, because + # the whole implementation relies on this "coincidence" that + # pointers to objects of different classes are interchangeable. + result = [] + for v in llfunc.graph.getargs() + [llfunc.graph.getreturnvar()]: + r = self.gethltype(v) + if isinstance(r, CInstance): + r = R_INSTANCE + result.append(r) + return result + def regroup_func_sig(self, funcs): # find a common signature for the given functions signatures = [] for func in funcs: llfunc = self.genc.llfunctions[func] - signatures.append(llfunc.hl_header()) + signatures.append(self.getfunctionsig(llfunc)) # XXX we expect the functions to have exactly the same signature # but this is currently not enforced by the annotator. - assert signatures + result = signatures[0] for test in signatures[1:]: - if test != signatures[0]: - raise TypingError(signatures[0], test) - return signatures[0] + if test != result: + raise TypingError(test, result) + return result # ____________________________________________________________ @@ -145,20 +168,47 @@ # it can be converted to PyObject*. sig = (R_OBJECT,) * len(hltypes) yield sig, genc_op.LoCallFunction - # Calling another user-defined generated function + r = hltypes[0] - if isinstance(r, (CConstantFunction, CFunction)): - # it might be a known one or just one we have a pointer to - sig = tuple(r.get_function_signature(self)[1]) - r_func = function_representation(sig) - sig = (r_func,) + sig + if (isinstance(r, CConstantFunction) and + r.value in self.genc.llfunctions): + # a constant function can be converted to a function pointer, + # so we fall back to the latter case + llfunc = self.genc.llfunctions[r.value] + r = function_representation(self.getfunctionsig(llfunc)) + + if isinstance(r, CFunction): + # a function pointer (the pointer is a C pointer, but points + # to a C function generated from a user-defined Python function). + sig = (r,) + tuple(r.header_r) yield sig, genc_op.LoCallPyFunction.With( hlrettype = sig[-1], ) - # But not all variables holding pointers to function can nicely - # be converted to PyObject*. We might be calling a well-known - # (constant) function: + + if isinstance(r, CMethod): + # first consider how we could call the underlying function + # with an extra R_INSTANCE first argument + hltypes2 = (r.r_func, R_INSTANCE) + hltypes[1:] + self.typemismatch('OP_SIMPLE_CALL', hltypes2) + # then lift all OP_SIMPLE_CALLs to method calls + opsimplecall = self.lloperations.setdefault('OP_SIMPLE_CALL', {}) + for sig, opcls in opsimplecall.items(): + if sig[1:2] == (R_INSTANCE,): + r = method_representation(sig[0]) + sig2 = (r,) + sig[2:] + yield sig2, opcls + # Note that we are reusing the same opcls. Indeed, both the + # original 'sig' and the modified one expand to the same list + # of LLVars, so opcls cannot tell the difference: + # + # sig = r.r_func R_INSTANCE ... + # /-----\ /------------\ + # LLVars: funcptr, PyObject* self, arguments..., result + # \-----------------------/ + # sig2 = r ... + if isinstance(r, CConstant): + # maybe it is a well-known constant non-user-defined function fn = r.value if not callable(fn): return @@ -206,11 +256,24 @@ # record the OP_GETATTR operation for this field fld = (r_obj.llclass.get_instance_field(r_attr.value) or r_obj.llclass.get_class_field(r_attr.value)) - if fld is not None: - sig = (r_obj, r_attr, fld.hltype) - yield sig, genc_op.LoGetAttr.With( - fld = fld, - ) + if fld is None: + return + sig = (r_obj, r_attr, fld.hltype) + # special case: reading a function out of a class attribute + # produces a bound method + if fld.is_class_attr: + r = sig[-1] + if isinstance(r, (CFunction, CConstantFunction)): + r_method = method_representation(r) + sig = (r_obj, r_attr, r_method) + yield sig, genc_op.LoGetAttrMethod.With( + fld = fld, + ) + return + # common case + yield sig, genc_op.LoGetAttr.With( + fld = fld, + ) def extend_OP_SETATTR(self, hltypes): if len(hltypes) != 4: Modified: pypy/trunk/src/pypy/translator/typer.py ============================================================================== --- pypy/trunk/src/pypy/translator/typer.py (original) +++ pypy/trunk/src/pypy/translator/typer.py Sat Sep 11 00:45:10 2004 @@ -28,12 +28,12 @@ self.args = args # list of LLVars self.errtarget = errtarget # label to jump to in case of error - def optimize(self, typer): + def optimize(self, typer, llresult): """If the operation can be statically optimized, this method can - return a list [new-llvars-for-result] and optionally generate + patch the llresult list of LLVars and optionally generate replacement operations by calling typer.operation() or - typer.convert().""" - return None + typer.convert(). Can return True to skip the 'self' operation.""" + return False class TypingError(Exception): pass @@ -129,19 +129,6 @@ self.name = name self.graph = graph - def hl_header(self): - """ - Get the high-level signature (argument types and return type). - """ - result = [] - for v in self.graph.getargs(): - self.makevar(v) - result.append(self.hltypes[v]) - v = self.graph.getreturnvar() - self.makevar(v) - result.append(self.hltypes[v]) - return result - def ll_header(self): """ Get the low-level representation of the header. @@ -314,13 +301,7 @@ 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 + if llop.optimize(self, llresult): return False # operation skipped else: # common case: emit the LLOp. From arigo at codespeak.net Sat Sep 11 00:53:16 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 11 Sep 2004 00:53:16 +0200 (MEST) Subject: [pypy-svn] r6388 - in pypy/trunk/src/pypy/translator: . test Message-ID: <20040910225316.06CB95AAAF@thoth.codespeak.net> Author: arigo Date: Sat Sep 11 00:53:16 2004 New Revision: 6388 Modified: pypy/trunk/src/pypy/translator/classtyper.py pypy/trunk/src/pypy/translator/test/snippet.py Log: Abstract base classes can have uninitialized class attributes (e.g. missing virtual methods). Modified: pypy/trunk/src/pypy/translator/classtyper.py ============================================================================== --- pypy/trunk/src/pypy/translator/classtyper.py (original) +++ pypy/trunk/src/pypy/translator/classtyper.py Sat Sep 11 00:53:16 2004 @@ -148,9 +148,12 @@ v1 = Constant(cls) # initialize class attributes for fld in self.class_fields: - value = getattr(cls, fld.name) - op('initclassattr', v1, Constant(fld.name), Constant(value), - s_result = annmodel.SomeImpossibleValue()) + # some attributes might be missing from 'cls' if it is an abstract + # base class. We left the field uninitialized in this case. + if hasattr(cls, fld.name): + value = getattr(cls, fld.name) + op('initclassattr', v1, Constant(fld.name), Constant(value), + s_result = annmodel.SomeImpossibleValue()) # finally, return None graph = FunctionGraph('%s_typenew' % self.name, b) self.bindings[graph.getreturnvar()] = annmodel.immutablevalue(None) Modified: pypy/trunk/src/pypy/translator/test/snippet.py ============================================================================== --- pypy/trunk/src/pypy/translator/test/snippet.py (original) +++ pypy/trunk/src/pypy/translator/test/snippet.py Sat Sep 11 00:53:16 2004 @@ -319,7 +319,9 @@ def prime(n=int): return len([i for i in range(1,n+1) if n%i==0]) == 2 -class A1: +class A0: + pass +class A1(A0): clsattr = 123 class A2(A1): clsattr = 456 @@ -327,7 +329,7 @@ clsattr = 789 class A4(A3): pass -class A5(A1): +class A5(A0): clsattr = 101112 def classattribute(flag=int): From arigo at codespeak.net Sat Sep 11 11:49:20 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 11 Sep 2004 11:49:20 +0200 (MEST) Subject: [pypy-svn] r6389 - pypy/trunk/src/pypy/translator Message-ID: <20040911094920.C26FE5A585@thoth.codespeak.net> Author: arigo Date: Sat Sep 11 11:49:19 2004 New Revision: 6389 Modified: pypy/trunk/src/pypy/translator/genc_op.py Log: New comments describing the arguments taken by each operation class. 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 Sat Sep 11 11:49:19 2004 @@ -55,24 +55,36 @@ return True # ____________________________________________________________ +# +# For each operation we describe what argument list it expected. +# These arguments are LLVars expanded from high-level arguments (Variables) +# and the result, also as zero or more LLVars, all put together in a single +# list. The occasionally hackish look of self.args manipulations come from +# the fact that the boundaries between the original high-level arguments +# are lost by design. In the #self.args comments below, a trailing '..' +# means that zero or more items can come here. class LoStandardOperation(LoC): "A standard operation is one defined by a macro in genc.h." can_fail = PARAMETER llname = PARAMETER cost = PARAMETER + # self.args: [macro args expanded from Variables..] def writestr(self, *args): return self.llname + '(' + ', '.join(args) + ')' class LoKnownAnswer(LoOptimized): known_answer = PARAMETER cost = 0 + # self.args: [ignored input args.., + # output args to replace with known_answer..] def optimized_result(self, typer): return self.known_answer class LoNewList(LoC): can_fail = True cost = 3 + # self.args: [input PyObjects.., output PyObject] def writestr(self, *stuff): content = stuff[:-2] result = stuff[-2] @@ -87,6 +99,7 @@ class LoCallFunction(LoC): can_fail = True cost = 3 + # self.args: [callable PyObject, argument PyObjects.., result PyObject] def writestr(self, func, *stuff): args = stuff[:-2] result = stuff[-2] @@ -99,6 +112,7 @@ class LoInstantiate(LoC): can_fail = True llclass = PARAMETER + # self.args: [output PyObject instance] def writestr(self, res, err): return 'INSTANTIATE(%s, %s, %s)' % ( self.llclass.name, res, err) @@ -106,6 +120,7 @@ class LoAllocInstance(LoC): can_fail = True llclass = PARAMETER + # self.args: [output PyObject instance] def writestr(self, res, err): return 'ALLOC_INSTANCE(%s, %s, %s)' % ( self.llclass.name, res, err) @@ -114,7 +129,7 @@ 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 cost = PARAMETER - + # self.args: [input unpacked tuple items.., output unpacked tuple items..] def optimized_result(self, typer): # replace this complex conversion by the simpler conversion of # only the items that changed @@ -144,7 +159,7 @@ class LoNewTuple(LoC): can_fail = True cost = 3 - + # self.args: [input PyObjects.., output PyObject] def writestr(self, *stuff): args = stuff[:-2] result = stuff[-2] @@ -159,7 +174,7 @@ class LoGetAttr(LoC): cost = 1 fld = PARAMETER - + # self.args: [PyObject instance, result..] def writestr(self, inst, *result): ls = [] llclass = self.fld.llclass @@ -182,6 +197,7 @@ return '\n'.join(ls) class LoGetAttrMethod(LoGetAttr): + # self.args: [PyObject instance, result..] def optimize(self, typer, llresult): # for a OP_GETATTR that must return a bound method. The 'self' # part of the result can be statically copied from self.args[0]. @@ -194,7 +210,7 @@ cost = 1 llclass = PARAMETER # the class involved in the operation fld = PARAMETER # the field, which might come from a parent class - + # self.args: [PyObject instance, new value..] def writestr(self, inst, *value): assert len(value) == len(self.fld.llvars) ls = [] @@ -211,7 +227,7 @@ cost = 1 llclass = PARAMETER # the class involved in the operation fld = PARAMETER # the field, which might come from a parent class - + # self.args: [constant value to store..] def writestr(self, *value): assert len(value) == len(self.fld.llvars) ls = [] @@ -228,9 +244,8 @@ r_source = PARAMETER r_target = PARAMETER cost = PARAMETER - + # self.args: [im_func.., im_self PyObject, output im_func.., im_self PyObj] def optimized_result(self, typer): - # self.args: [input-func..., PyObject*, output-func..., PyObject*] slen = len(self.r_source.impl) tlen = len(self.r_target.impl) assert len(self.args) == slen+1+tlen+1 @@ -246,7 +261,7 @@ r_middle = PARAMETER r_to = PARAMETER cost = PARAMETER - + # self.args: [input value.., output value..] def optimized_result(self, typer): half = len(self.r_from.impl) assert half + len(self.r_to.impl) == len(self.args) @@ -257,6 +272,7 @@ class LoDummyResult(LoC): cost = 1 + # self.args: [output value..] def write(self): ls = [] for a in self.args: @@ -269,16 +285,19 @@ class LoMove(LoC): cost = 1 + # self.args: [input LLVar, output LLVar] def writestr(self, x, y): return '%s = %s;' % (y, x) class LoGoto(LoC): cost = 0 + # self.args: [] def write(self): return 'goto %s;' % self.errtarget class LoCopy(LoOptimized): cost = 0 + # self.args: [input LLVars.., output LLVars..] def optimized_result(self, typer): # the result's llvars is equal to the input's llvars. assert len(self.args) % 2 == 0 @@ -288,6 +307,7 @@ class LoDoSomethingWithRef(LoC): do_what = PARAMETER cost = 1 + # self.args: [value..] def write(self): ls = [] for a in self.args: @@ -301,6 +321,7 @@ class LoComment(LoC): cost = 0 + # self.args: [] def write(self): s = self.errtarget s = s.replace('/*', '/+') @@ -322,6 +343,7 @@ class LoCallPyFunction(LoC): can_fail = True hlrettype = PARAMETER + # self.args: [C function pointer, arguments.., result..] def write(self): funcptr = self.args[0].name L = len(self.hlrettype.impl) @@ -343,6 +365,7 @@ class LoReturn(LoC): cost = 1 + # self.args: [return value..] def write(self): if not self.args: return 'return 0;' From arigo at codespeak.net Sat Sep 11 12:33:51 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 11 Sep 2004 12:33:51 +0200 (MEST) Subject: [pypy-svn] r6390 - pypy/trunk/src/pypy/translator Message-ID: <20040911103351.893DA5A585@thoth.codespeak.net> Author: arigo Date: Sat Sep 11 12:33:50 2004 New Revision: 6390 Modified: pypy/trunk/src/pypy/translator/genc.h pypy/trunk/src/pypy/translator/genc_op.py pypy/trunk/src/pypy/translator/genc_typeset.py pypy/trunk/src/pypy/translator/typer.py Log: Uniformly generates macro calls in the function bodies. It simplifies a bit genc_op.py and more importantly it produces more readable C files. (suggested by Holger) Modified: pypy/trunk/src/pypy/translator/genc.h ============================================================================== --- pypy/trunk/src/pypy/translator/genc.h (original) +++ pypy/trunk/src/pypy/translator/genc.h Sat Sep 11 12:33:50 2004 @@ -61,9 +61,9 @@ /*** conversions ***/ -#define convert_io(x,r,err) if (!(r=PyInt_FromLong(x))) goto err; -#define convert_so(c,l,r,err) if (!(r=PyString_FromStringAndSize(c,l)))goto err; -#define convert_vo(r) r = Py_None; Py_INCREF(r); +#define CONVERT_io(x,r,err) if (!(r=PyInt_FromLong(x))) goto err; +#define CONVERT_so(c,l,r,err) if (!(r=PyString_FromStringAndSize(c,l)))goto err; +#define CONVERT_vo(r) r = Py_None; Py_INCREF(r); /*#define convert_oi(x,r,err) if ((r=PyInt_AsLong(x)) == -1 \ * && PyErr_Occurred()) goto err; @@ -71,15 +71,15 @@ /*** 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; +#define CASE_False_i(n, err) if (n) goto err; +#define CASE_True_i(n, err) if (!n) goto err; +#define CASE_0_i(n, err) if (n != 0) goto err; +#define CASE_1_i(n, err) if (n != 1) goto err; + +#define CASE_False_o(o, err) if (!(PyInt_Check(o) && !PyInt_AS_LONG(o))) goto err; +#define CASE_True_o(o, err) if (!(PyInt_Check(o) && PyInt_AS_LONG(o))) goto err; +#define CASE_0_o(o, err) if (!(PyInt_Check(o) && PyInt_AS_LONG(o)==0)) goto err; +#define CASE_1_o(o, err) if (!(PyInt_Check(o) && PyInt_AS_LONG(o)==1)) goto err; /*** misc ***/ @@ -95,25 +95,51 @@ } \ } -#define INSTANTIATE(cls, r, err) if (!(r=cls##_new())) goto err; +/* a few built-in functions */ + +#define CALL_len_oi(o,r,err) if ((r=PyObject_Size(o))<0) goto err; +#define CALL_pow_iii(x,y,r) { int i=y; r=1; while (--i>=0) r*=x; } /*slow*/ + + +/*** macros used directly by genc_op.py ***/ + +#define OP_NEWLIST(len, r, err) if (!(r=PyList_New(len))) goto err; +#define OP_NEWLIST_SET(r, i, o) PyList_SET_ITEM(r, i, o); Py_INCREF(o); +#define OP_NEWTUPLE(len, r, err) if (!(r=PyTuple_New(len))) goto err; +#define OP_NEWTUPLE_SET(r, i, o) PyTuple_SET_ITEM(r, i, o); Py_INCREF(o); + +#define OP_CALL_PYOBJ(args, r, err) if (!(r=PyObject_CallFunction args)) \ + goto err; + +#define OP_INSTANTIATE(cls, r, err) if (!(r=cls##_new())) goto err; #define ALLOC_INSTANCE(cls, r, err) \ if (!(r=PyType_GenericAlloc(&cls##_Type.type, 0))) goto err; #define SETUP_TYPE(cls) \ PyType_Ready(&cls##_Type.type); \ cls##_typenew(); -#define GET_ATTR_py(fld, r) r=fld; Py_INCREF(r); -#define SET_ATTR_py(fld, v) { PyObject* o=fld; fld=v; \ - Py_INCREF(v); Py_XDECREF(o); } - -#define GET_ATTR_cls(fld, r) r=fld; Py_INCREF(r); -#define SET_ATTR_cls(fld, v) fld=v; Py_INCREF(v); /* initialization only */ - -/* a few built-in functions */ - -#define CALL_len_oi(o,r,err) if ((r=PyObject_Size(o))<0) goto err; -#define CALL_pow_iii(x,y,r) { int i=y; r=1; while (--i>=0) r*=x; } /*slow*/ - +#define OP_GETINSTATTR(cls, o, f, r) r=((cls##_Object*) o)->f; +#define OP_GETINSTATTR_o(cls, o, f, r) r=((cls##_Object*) o)->f; Py_INCREF(r); +#define OP_GETCLASSATTR(cls, o, f, r) r=((cls##_TypeObject*)(o->ob_type))->f; +#define OP_GETCLASSATTR_o(cls, o, f, r) r=((cls##_TypeObject*)(o->ob_type))->f;\ + Py_INCREF(r); +#define OP_SETINSTATTR(cls, o, f, v) ((cls##_Object*) o)->f=v; +#define OP_SETINSTATTR_o(cls, o, f, v) { PyObject* tmp; \ + OP_GETINSTATTR(cls, o, f, tmp) \ + OP_SETINSTATTR(cls, o, f, v) \ + Py_INCREF(v); Py_XDECREF(tmp); \ + } +#define OP_INITCLASSATTR(cls, f, v) cls##_Type.f=v; +#define OP_INITCLASSATTR_o(cls, f, v) cls##_Type.f=v; Py_INCREF(v); + +#define OP_DUMMYREF(r) r = Py_None; Py_INCREF(r); + +#define MOVE(x, y) y = x; + +#define OP_CCALL_v(fn, args, err) if (fn args < 0) goto err; +#define OP_CCALL(fn, args, r, err) if ((r=fn args) == NULL) goto err; +#define OP_CCALL_i(fn, args, r, err) if ((r=fn args) == -1 && \ + PyErr_Occurred()) goto err; /************************************************************/ /*** The rest is produced by genc.py ***/ 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 Sat Sep 11 12:33:50 2004 @@ -81,21 +81,6 @@ def optimized_result(self, typer): return self.known_answer -class LoNewList(LoC): - can_fail = True - cost = 3 - # self.args: [input PyObjects.., output PyObject] - def writestr(self, *stuff): - content = stuff[:-2] - result = stuff[-2] - err = stuff[-1] - ls = ['if (!(%s = PyList_New(%d))) goto %s;' % ( - result, len(content), err)] - for i in range(len(content)): - ls.append('PyList_SET_ITEM(%s, %d, %s); Py_INCREF(%s);' % ( - result, i, content[i], content[i])) - return '\n'.join(ls) - class LoCallFunction(LoC): can_fail = True cost = 3 @@ -106,24 +91,21 @@ err = stuff[-1] format = '"' + 'O' * len(args) + '"' args = (func, format) + args - return ('if (!(%s = PyObject_CallFunction(%s)))' - ' goto %s;' % (result, ', '.join(args), err)) + return ('OP_CALL_PYOBJ((%s), %s, %s)' % (', '.join(args), result, err)) class LoInstantiate(LoC): can_fail = True llclass = PARAMETER # self.args: [output PyObject instance] def writestr(self, res, err): - return 'INSTANTIATE(%s, %s, %s)' % ( - self.llclass.name, res, err) + return 'OP_INSTANTIATE(%s, %s, %s)' % (self.llclass.name, res, err) class LoAllocInstance(LoC): can_fail = True llclass = PARAMETER # self.args: [output PyObject instance] def writestr(self, res, err): - return 'ALLOC_INSTANCE(%s, %s, %s)' % ( - self.llclass.name, res, err) + return 'ALLOC_INSTANCE(%s, %s, %s)' % (self.llclass.name, res, err) class LoConvertTupleItem(LoOptimized): source_r = PARAMETER # tuple-of-hltypes, one per item of the input tuple @@ -159,41 +141,39 @@ class LoNewTuple(LoC): can_fail = True cost = 3 + macro = 'OP_NEWTUPLE' # self.args: [input PyObjects.., output PyObject] def writestr(self, *stuff): args = stuff[:-2] result = stuff[-2] err = stuff[-1] - ls = ['if (!(%s = PyTuple_New(%d))) goto %s;' % - (result, len(args), err)] - for i, a in zip(range(len(args)), args): - ls.append('PyTuple_SET_ITEM(%s, %d, %s); Py_INCREF(%s);' % - (result, i, a, a)) + ls = ['%s(%d, %s, %s)' % (self.macro, len(args), result, err)] + for i in range(len(args)): + ls.append('%s_SET(%s, %d, %s)' % (self.macro, result, i, args[i])) return '\n'.join(ls) +class LoNewList(LoNewTuple): + macro = 'OP_NEWLIST' + # self.args: [input PyObjects.., output PyObject] + class LoGetAttr(LoC): cost = 1 fld = PARAMETER # self.args: [PyObject instance, result..] def writestr(self, inst, *result): ls = [] - llclass = self.fld.llclass if self.fld.is_class_attr: - for src, dstname in zip(self.fld.llvars, result): - fldexpr = '((%s_TypeObject*)(%s->ob_type))->%s' % ( - llclass.name, inst, src.name) - if src.type == 'PyObject*': - ls.append('GET_ATTR_cls(%s, %s)' % (fldexpr, dstname)) - else: - ls.append('%s = %s;' % (dstname, fldexpr)) + macro = 'OP_GETCLASSATTR' else: - for src, dstname in zip(self.fld.llvars, result): - fldexpr = '((%s_Object*) %s)->%s' % (llclass.name, inst, - src.name) - if src.type == 'PyObject*': - ls.append('GET_ATTR_py(%s, %s)' % (fldexpr, dstname)) - else: - ls.append('%s = %s;' % (dstname, fldexpr)) + macro = 'OP_GETINSTATTR' + llclass = self.fld.llclass + for src, dstname in zip(self.fld.llvars, result): + if src.type == 'PyObject*': + typecode = '_o' + else: + typecode = '' + ls.append('%s%s(%s, %s, %s, %s)' % ( + macro, typecode, llclass.name, inst, src.name, dstname)) return '\n'.join(ls) class LoGetAttrMethod(LoGetAttr): @@ -215,12 +195,12 @@ assert len(value) == len(self.fld.llvars) ls = [] for srcname, dst in zip(value, self.fld.llvars): - fldexpr = '((%s_Object*) %s)->%s' % (self.llclass.name, inst, - dst.name) if dst.type == 'PyObject*': - ls.append('SET_ATTR_py(%s, %s)' % (fldexpr, srcname)) + typecode = '_o' else: - ls.append('%s = %s;' % (fldexpr, srcname)) + typecode = '' + ls.append('OP_SETINSTATTR%s(%s, %s, %s, %s)' % ( + typecode, self.llclass.name, inst, dst.name, srcname)) return '\n'.join(ls) class LoInitClassAttr(LoC): @@ -233,11 +213,12 @@ ls = [] # setting class attributes is only used for initialization for srcname, dst in zip(value, self.fld.llvars): - fldexpr = '%s_Type.%s' % (self.llclass.name, dst.name) if dst.type == 'PyObject*': - ls.append('SET_ATTR_cls(%s, %s)' % (fldexpr, srcname)) + typecode = '_o' else: - ls.append('%s = %s;' % (fldexpr, srcname)) + typecode = '' + ls.append('OP_INITCLASSATTR%s(%s, %s, %s)' % ( + typecode, self.llclass.name, dst.name, srcname)) return '\n'.join(ls) class LoConvertBoundMethod(LoOptimized): @@ -277,8 +258,7 @@ ls = [] for a in self.args: if a.type == 'PyObject*': - ls.append('%s = Py_None; Py_INCREF(%s); /* dummy */' % ( - a.name, a.name)) + ls.append('OP_DUMMYREF(%s)' % a.name) return '\n'.join(ls) # ____________________________________________________________ @@ -287,7 +267,7 @@ cost = 1 # self.args: [input LLVar, output LLVar] def writestr(self, x, y): - return '%s = %s;' % (y, x) + return 'MOVE(%s, %s)' % (x, y) class LoGoto(LoC): cost = 0 @@ -335,11 +315,6 @@ 'int': '-1', } -ERROR_CHECK = { - None: '< 0', - 'int': '== -1 && PyErr_Occurred()', - } - class LoCallPyFunction(LoC): can_fail = True hlrettype = PARAMETER @@ -351,17 +326,19 @@ args = [a.name for a in self.args[1:R]] err = self.errtarget if L == 0: # no return value - return 'if (%s(%s) %s) goto %s;' % ( - funcptr, ', '.join(args), ERROR_CHECK[None], err) + return 'OP_CCALL_v(%s, (%s), %s)' % (funcptr, ', '.join(args), err) else: # the return value is the first return LLVar: retvar = self.args[R] # if there are several return LLVars, the extra ones are passed # in by reference as output arguments args += ['&%s' % a.name for a in self.args[R+1:]] - return ('if ((%s = %s(%s)) %s) goto %s;' % ( - retvar.name, funcptr, ', '.join(args), - ERROR_CHECK.get(retvar.type, '== NULL'), err)) + if retvar.type == 'int': + typecode = '_i' + else: + typecode = '' + return 'OP_CCALL%s(%s, (%s), %s, %s)' % ( + typecode, funcptr, ', '.join(args), retvar.name, err) class LoReturn(LoC): cost = 1 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 Sat Sep 11 12:33:50 2004 @@ -39,7 +39,7 @@ self.bindings = bindings self.conversion_cache = {} self.conversion_errors = {} - self.lloperations = {'convert': self.conversion_cache} + self.lloperations = {'CONVERT': self.conversion_cache} self.parse_operation_templates() # __________ methods required by LLFunction __________ Modified: pypy/trunk/src/pypy/translator/typer.py ============================================================================== --- pypy/trunk/src/pypy/translator/typer.py (original) +++ pypy/trunk/src/pypy/translator/typer.py Sat Sep 11 12:33:50 2004 @@ -59,7 +59,7 @@ # ...} # This dict contains the known signatures of each space operation. # Special opnames: -# 'caseXXX' v : fails (i.e. jump to errlabel) if v is not XXX +# 'CASE_XXX' v : fails (i.e. jump to errlabel) if v is not XXX # # rawoperations = { # 'opname': subclass-of-LLOp, @@ -189,12 +189,12 @@ # exits if block.exits: for exit in block.exits[:-1]: - # generate | caseXXX v elselabel + # generate | CASE_XXX 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, + self.operation('CASE_%s' % exit.exitcase, [block.exitswitch], errlabel = elselabel) self.goto(exit) From arigo at codespeak.net Sat Sep 11 12:50:27 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 11 Sep 2004 12:50:27 +0200 (MEST) Subject: [pypy-svn] r6391 - pypy/trunk/src/pypy/translator Message-ID: <20040911105027.BC6D95A585@thoth.codespeak.net> Author: arigo Date: Sat Sep 11 12:50:27 2004 New Revision: 6391 Modified: pypy/trunk/src/pypy/translator/typer.py Log: Reference counting was buggy in case of errors. It is difficult to write tests for that... and the code still doesn't look rock-solid. Hum. Modified: pypy/trunk/src/pypy/translator/typer.py ============================================================================== --- pypy/trunk/src/pypy/translator/typer.py (original) +++ pypy/trunk/src/pypy/translator/typer.py Sat Sep 11 12:50:27 2004 @@ -179,8 +179,10 @@ def generate_block(self, block): "Generate the operations for one basic block." self.to_release = self.release_root + llrepr = [] for v in block.inputargs: - self.mark_release(v) + llrepr += self.llreprs[v] + self.mark_release(llrepr) # entry point self.blockops = [self.blockname[block]] # label # basic block operations @@ -214,10 +216,11 @@ # __________ Type checking and conversion routines __________ - def mark_release(self, v): - llop = self.rawoperations['decref'](self.llreprs[v]) - # make a new node for the release tree - self.to_release = ReleaseNode(v, llop, self.to_release) + def mark_release(self, llrepr): + if llrepr: + llop = self.rawoperations['decref'](llrepr) + # make a new node for the release tree + self.to_release = ReleaseNode(llrepr, llop, self.to_release) def find_best_match(self, opname, args_t, directions): # look for an exact match first @@ -280,16 +283,14 @@ if result: if args_t[-1] == sig[-1]: # the result has the correct type - if self.writeoperation(llopcls, llargs, - self.llreprs[result], errlabel): - self.mark_release(result) + self.writeoperation(llopcls, llargs, + self.llreprs[result], errlabel) else: # the result has to be converted tmp = Variable() self.makevar(tmp, hltype=sig[-1]) - if self.writeoperation(llopcls, llargs, - self.llreprs[tmp], errlabel): - self.mark_release(tmp) + self.writeoperation(llopcls, llargs, + self.llreprs[tmp], errlabel) self.convert_variable(tmp, result) else: # no result variable @@ -300,13 +301,18 @@ if llopcls.can_fail and errlabel is None: errlabel = self.to_release.getlabel() # create the LLOp instance + llresultcopy = list(llresult) llop = llopcls(llargs + llresult, errlabel) if llop.optimize(self, llresult): - return False # operation skipped - else: - # common case: emit the LLOp. - self.blockops.append(llop) - return True + return # skip the operation + # common case: emit the LLOp. + self.blockops.append(llop) + # all LLVars that are still in llresult (haven't been optimized away) + # are new and marked as "to be released". + assert len(llresult) == len(llresultcopy) + llrepr = [new for new, prev in zip(llresult, llresultcopy) + if new == prev] + self.mark_release(llrepr) def convert(self, inputtype, inputrepr, outputtype, outputrepr=None): convop = self.getconversion(inputtype, outputtype) @@ -314,22 +320,14 @@ tmp = Variable() self.makevar(tmp, hltype=outputtype) outputrepr = self.llreprs[tmp] - if self.writeoperation(convop, inputrepr, outputrepr): - self.mark_release(tmp) - else: - if self.writeoperation(convop, inputrepr, outputrepr): - tmp = Variable() - self.hltypes[tmp] = outputtype - self.llreprs[tmp] = outputrepr - self.mark_release(tmp) + self.writeoperation(convop, inputrepr, outputrepr) 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) + self.writeoperation(convop, self.llreprs[v1], self.llreprs[v2]) def goto(self, exit): # generate the exit.args -> target.inputargs copying operations @@ -359,7 +357,7 @@ # list all variables that go out of scope: by default # they need no reference, but have one reference. for node in self.to_release.getbranch(): - for x in self.llreprs[node.var]: + for x in node.llrepr: current_refcnt[x] = 1 needed_refcnt.setdefault(x, 0) # now adjust all reference counters: first increfs, then decrefs @@ -395,8 +393,8 @@ label = None counter = 0 - def __init__(self, var, release_operation, parent): - self.var = var + def __init__(self, llrepr, release_operation, parent): + self.llrepr = llrepr self.release_operation = release_operation self.parent = parent self.accessible_children = [] From pedronis at codespeak.net Mon Sep 13 21:18:47 2004 From: pedronis at codespeak.net (pedronis at codespeak.net) Date: Mon, 13 Sep 2004 21:18:47 +0200 (MEST) Subject: [pypy-svn] r6425 - pypy/trunk/doc/funding/negotiations Message-ID: <20040913191847.75CFF5A3E1@thoth.codespeak.net> Author: pedronis Date: Mon Sep 13 21:18:46 2004 New Revision: 6425 Added: pypy/trunk/doc/funding/negotiations/part_b_2004_07_29.pdf (contents, props changed) Log: pdf for ref Added: pypy/trunk/doc/funding/negotiations/part_b_2004_07_29.pdf ============================================================================== Binary file. No diff available. From pedronis at codespeak.net Mon Sep 13 21:32:31 2004 From: pedronis at codespeak.net (pedronis at codespeak.net) Date: Mon, 13 Sep 2004 21:32:31 +0200 (MEST) Subject: [pypy-svn] r6426 - pypy/trunk/doc/funding/negotiations Message-ID: <20040913193231.B36FD5A3E1@thoth.codespeak.net> Author: pedronis Date: Mon Sep 13 21:32:31 2004 New Revision: 6426 Modified: pypy/trunk/doc/funding/negotiations/part_b_2004_07_29.pdf (props changed) Log: mime-type From arigo at codespeak.net Tue Sep 21 19:59:48 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 21 Sep 2004 19:59:48 +0200 (MEST) Subject: [pypy-svn] r6628 - pypy/trunk/src/pypy/translator Message-ID: <20040921175948.A89545ADFD@thoth.codespeak.net> Author: arigo Date: Tue Sep 21 19:59:47 2004 New Revision: 6628 Modified: pypy/trunk/src/pypy/translator/genc.h pypy/trunk/src/pypy/translator/genc.py pypy/trunk/src/pypy/translator/genc_op.py pypy/trunk/src/pypy/translator/genc_repr.py pypy/trunk/src/pypy/translator/genc_typeset.py Log: Implement lists as arrays of C structs. (Intermediate check-in with the new code disabled; many tests still failing) Modified: pypy/trunk/src/pypy/translator/genc.h ============================================================================== --- pypy/trunk/src/pypy/translator/genc.h (original) +++ pypy/trunk/src/pypy/translator/genc.h Tue Sep 21 19:59:47 2004 @@ -141,5 +141,10 @@ #define OP_CCALL_i(fn, args, r, err) if ((r=fn args) == -1 && \ PyErr_Occurred()) goto err; +#define OP_NEWARRAY(name,len,r,err) if (!(r=alloclist_##name(len))) goto err; +#define OP_NEWARRAY_SET(name,r,i,f,v) ((PyList_##name*) r)->ob_item[i].f=v; +#define OP_NEWARRAY_SET_o(name,r,i,f,o) ((PyList_##name*) r)->ob_item[i].f=o; \ + Py_INCREF(o); + /************************************************************/ /*** The rest is produced by genc.py ***/ Modified: pypy/trunk/src/pypy/translator/genc.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc.py (original) +++ pypy/trunk/src/pypy/translator/genc.py Tue Sep 21 19:59:47 2004 @@ -41,6 +41,7 @@ self.initializationcode = [] self.llclasses = {}; self.classeslist = [] self.llfunctions = {}; self.functionslist = [] + self.llarrays = {} # must build functions first, otherwise methods are not found self.build_llfunctions() self.build_llclasses() @@ -308,6 +309,44 @@ self.initializationcode.append('SETUP_TYPE(%s)' % llclass.name) + def declare_list(self, lltypes): + # called by genc_typer.py to write the type definition of a list + # as an array of "struct { lltypes };" + lltypes = tuple(lltypes) + if lltypes in self.llarrays: + return self.llarrays[lltypes] # already seen + + s = '_'.join(lltypes) + name = ''.join([('a'<=c<='z' or '0'<=c<='9') and c or '_' for c in s]) + name += '_%d' % len(self.llarrays) + self.llarrays[lltypes] = name + + f = self.f + info = { + 'name': name, + } + print >> f, self.C_LIST_HEADER % info + llvars1 = [] + for i in range(len(lltypes)): + print >> f, '\t%s;' % cdecl(lltypes[i], 'a%d'%i) + llvars1.append(LLVar(lltypes[i], 'item->a%d'%i)) + print >> f, self.C_LIST_FOOTER % info + + print >> f, self.C_LIST_DEALLOC_HEADER % info + lldecref = self.typeset.rawoperations['xdecref'] + line = lldecref(llvars1) + code = line.write() + if code: + print >> f, self.C_LIST_DEALLOC_LOOP_HEADER % info + for codeline in code.split('\n'): + print >> f, '\t\t' + codeline + print >> f, self.C_LIST_DEALLOC_LOOP_FOOTER % info + print >> f, self.C_LIST_DEALLOC_FOOTER % info + + print >> f, self.C_LIST_TYPEOBJECT % info + return name + + # ____________________________________________________________ C_HEADER = open(os.path.join(autopath.this_dir, 'genc.h')).read() @@ -416,4 +455,88 @@ }; ''' + C_LIST_HEADER = '''/* the type "list of %(name)s" */ +typedef struct {''' + + C_LIST_FOOTER = '''} ListItem_%(name)s; + +typedef struct { + PyObject_VAR_HEAD + ListItem_%(name)s* ob_item; +} PyList_%(name)s; +''' + + C_LIST_DEALLOC_HEADER = ( +'''static void dealloclist_%(name)s(PyList_%(name)s* op) +{''') + + C_LIST_DEALLOC_LOOP_HEADER = ( +''' int i = op->ob_size; + ListItem_%(name)s item = op->ob_item + i; + while (--i >= 0) {''') + + C_LIST_DEALLOC_LOOP_FOOTER = ( +''' --item; + }''') + + C_LIST_DEALLOC_FOOTER = ( +''' PyMem_Free(op->ob_item); + PyObject_Del((PyObject*) op); +} +''') + + C_LIST_TYPEOBJECT = '''static PyTypeObject g_ListType_%(name)s = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "list of %(name)s", + sizeof(PyList_%(name)s), + 0, + (destructor)dealloclist_%(name)s, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* XXX need GC */ /* tp_traverse */ + 0, /* tp_clear */ +}; + +static PyObject* alloclist_%(name)s(int len) +{ + void* buffer; + PyList_%(name)s* o; + + if (len > 0) { + buffer = PyMem_Malloc(len * sizeof(ListItem_%(name)s)); + if (buffer == NULL) { + PyErr_NoMemory(); + return NULL; + } + } + else + buffer = NULL; + + o = PyObject_New(PyList_%(name)s, &g_ListType_%(name)s); + if (o != NULL) { + o->ob_size = len; + o->ob_item = (ListItem_%(name)s*) buffer; + } + else { + PyMem_Free(buffer); + } + return o; +} +''' + # ____________________________________________________________ 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 Tue Sep 21 19:59:47 2004 @@ -156,6 +156,29 @@ macro = 'OP_NEWLIST' # self.args: [input PyObjects.., output PyObject] +class LoNewArray(LoC): + can_fail = True + typename = PARAMETER # the name of the PyList_Xxx type in the C source + lltypes = PARAMETER # the C types needed to represent each array item + # self.args: [item0.., item1.., item2.., ..., output PyObject] + def writestr(self, *stuff): + args = stuff[:-2] + result = stuff[-2] + err = stuff[-1] + ls = ['OP_NEWARRAY(%s, %d, %s, %s)' % (self.typename, + len(args) / len(self.lltypes), + result, + err)] + for i in range(0, len(args), len(self.lltypes)): + for j in range(len(self.lltypes)): + if self.lltypes[j] == 'PyObject*': + typecode = '_o' + else: + typecode = '' + ls.append('OP_NEWARRAY_SET%s(%s, %s, %d, a%d, %s)' % ( + typecode, self.typename, result, i, j, args[i+j])) + return '\n'.join(ls) + class LoGetAttr(LoC): cost = 1 fld = PARAMETER Modified: pypy/trunk/src/pypy/translator/genc_repr.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc_repr.py (original) +++ pypy/trunk/src/pypy/translator/genc_repr.py Tue Sep 21 19:59:47 2004 @@ -250,6 +250,16 @@ cost = cost, ) +class CList(CRepr): + "A list as an array whose items are represented as r_item." + + def __init__(self, r_item): + self.r_item = r_item + self.impl = ['PyObject*'] + + def __repr__(self): + return 'CList(%r)' % (self.r_item,) + # ____________________________________________________________ # # Predefined CReprs and caches for building more @@ -265,6 +275,7 @@ R_INSTANCE_CACHE = {} R_FUNCTION_CACHE = {} R_METHOD_CACHE = {} +R_LIST_CACHE = {} def tuple_representation(items_r): items_r = tuple(items_r) @@ -327,6 +338,13 @@ r = R_METHOD_CACHE[key] = CMethod(r_func) return r +def list_representation(r_item): + try: + return R_LIST_CACHE[r_item] + except KeyError: + r = R_LIST_CACHE[r_item] = CList(r_item) + return r + def c_str(s): "Return the C expression for the string 's'." 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 Tue Sep 21 19:59:47 2004 @@ -12,6 +12,7 @@ from pypy.translator.genc_repr import function_representation, CFunction from pypy.translator.genc_repr import CConstantFunction from pypy.translator.genc_repr import method_representation, CMethod +from pypy.translator.genc_repr import list_representation, CList class CTypeSet: @@ -72,6 +73,12 @@ return r_func else: return method_representation(r_func) + if 0: # still working on it -- isinstance(var, annmodel.SomeList): + r_item = self.gethltype(var.s_item) + typename = self.genc.declare_list(r_item.impl) + r = list_representation(r_item) + r.typename = typename + return r # fall-back return R_OBJECT if isinstance(var, UndefinedConstant): @@ -149,10 +156,21 @@ # ____________________________________________________________ def extend_OP_NEWLIST(self, hltypes): + if not hltypes: + return # LoNewList can build a list of any length from PyObject* args. sig = (R_OBJECT,) * len(hltypes) yield sig, genc_op.LoNewList + # LoNewArray can build an array from items of the correct type. + r = hltypes[-1] + if isinstance(r, CList): + sig = (r.r_item,) * (len(hltypes)-1) + (r,) + yield sig, genc_op.LoNewArray.With( + typename = r.typename, + lltypes = r.r_item.impl, + ) + def extend_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 From arigo at codespeak.net Tue Sep 21 20:40:29 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 21 Sep 2004 20:40:29 +0200 (MEST) Subject: [pypy-svn] r6630 - pypy/trunk/src/pypy/translator Message-ID: <20040921184029.89DF15ADFD@thoth.codespeak.net> Author: arigo Date: Tue Sep 21 20:40:28 2004 New Revision: 6630 Modified: pypy/trunk/src/pypy/translator/genc.h pypy/trunk/src/pypy/translator/genc.py pypy/trunk/src/pypy/translator/genc_op.py pypy/trunk/src/pypy/translator/genc_typeset.py Log: Bug fixes and progress on arrays... Modified: pypy/trunk/src/pypy/translator/genc.h ============================================================================== --- pypy/trunk/src/pypy/translator/genc.h (original) +++ pypy/trunk/src/pypy/translator/genc.h Tue Sep 21 20:40:28 2004 @@ -142,9 +142,12 @@ PyErr_Occurred()) goto err; #define OP_NEWARRAY(name,len,r,err) if (!(r=alloclist_##name(len))) goto err; -#define OP_NEWARRAY_SET(name,r,i,f,v) ((PyList_##name*) r)->ob_item[i].f=v; -#define OP_NEWARRAY_SET_o(name,r,i,f,o) ((PyList_##name*) r)->ob_item[i].f=o; \ +#define OP_NEWARRAY_SET(name,a,i,f,v) ((PyList_##name*) a)->ob_item[i].f=v; +#define OP_NEWARRAY_SET_o(name,a,i,f,o) ((PyList_##name*) a)->ob_item[i].f=o; \ Py_INCREF(o); +#define OP_GETARRAYITEM(name,a,i,f,r) r=((PyList_##name*) a)->ob_item[i].f; +#define OP_GETARRAYITEM_o(name,a,i,f,r) r=((PyList_##name*) a)->ob_item[i].f; \ + Py_INCREF(r); /************************************************************/ /*** The rest is produced by genc.py ***/ Modified: pypy/trunk/src/pypy/translator/genc.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc.py (original) +++ pypy/trunk/src/pypy/translator/genc.py Tue Sep 21 20:40:28 2004 @@ -317,7 +317,11 @@ return self.llarrays[lltypes] # already seen s = '_'.join(lltypes) - name = ''.join([('a'<=c<='z' or '0'<=c<='9') and c or '_' for c in s]) + name = ''.join([('a'<=c<='z' or + 'A'<=c<='Z' or + '0'<=c<='9') and c or '_' + for c in s]) + name = name or 'void' name += '_%d' % len(self.llarrays) self.llarrays[lltypes] = name @@ -333,7 +337,7 @@ print >> f, self.C_LIST_FOOTER % info print >> f, self.C_LIST_DEALLOC_HEADER % info - lldecref = self.typeset.rawoperations['xdecref'] + lldecref = self.typeset.rawoperations['decref'] line = lldecref(llvars1) code = line.write() if code: @@ -472,12 +476,12 @@ C_LIST_DEALLOC_LOOP_HEADER = ( ''' int i = op->ob_size; - ListItem_%(name)s item = op->ob_item + i; - while (--i >= 0) {''') + ListItem_%(name)s* item = op->ob_item + i; + while (--i >= 0) { + --item;''') C_LIST_DEALLOC_LOOP_FOOTER = ( -''' --item; - }''') +''' }''') C_LIST_DEALLOC_FOOTER = ( ''' PyMem_Free(op->ob_item); @@ -535,7 +539,7 @@ else { PyMem_Free(buffer); } - return o; + return (PyObject*) o; } ''' 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 Tue Sep 21 20:40:28 2004 @@ -165,18 +165,63 @@ args = stuff[:-2] result = stuff[-2] err = stuff[-1] - ls = ['OP_NEWARRAY(%s, %d, %s, %s)' % (self.typename, - len(args) / len(self.lltypes), - result, - err)] - for i in range(0, len(args), len(self.lltypes)): + if len(args) == len(self.lltypes) == 0: + arraylen = 0 + else: + assert len(args) % len(self.lltypes) == 0 + arraylen = len(args) / len(self.lltypes) + ls = ['OP_NEWARRAY(%s, %d, %s, %s)' % ( + self.typename, arraylen, result, err)] + for i in range(arraylen): for j in range(len(self.lltypes)): if self.lltypes[j] == 'PyObject*': typecode = '_o' else: typecode = '' + a = args[i*len(self.lltypes) + j] ls.append('OP_NEWARRAY_SET%s(%s, %s, %d, a%d, %s)' % ( - typecode, self.typename, result, i, j, args[i+j])) + typecode, self.typename, result, i, j, a)) + return '\n'.join(ls) + +class LoAllocAndSetArray(LoC): + can_fail = True + typename = PARAMETER # the name of the PyList_Xxx type in the C source + lltypes = PARAMETER # the C types needed to represent each array item + # self.args: [length, input_item.., output PyObject] + def writestr(self, length, *stuff): + input = stuff[:-2] + result = stuff[-2] + err = stuff[-1] + assert len(input) == len(self.lltypes) + ls = ['OP_NEWARRAY(%s, %s, %s, %s)' % ( + self.typename, length, result, err)] + if len(self.lltypes) > 0: + ls.append('{ int i; for (i=0; i<%s; i++) {' % length) + for j in range(len(self.lltypes)): + if self.lltypes[j] == 'PyObject*': + typecode = '_o' + else: + typecode = '' + a = input[j] + ls.append('\tOP_NEWARRAY_SET%s(%s, %s, i, a%d, %s)' % ( + typecode, self.typename, result, j, a)) + ls.append('} }') + return '\n'.join(ls) + +class LoGetArrayItem(LoC): + typename = PARAMETER # the name of the PyList_Xxx type in the C source + lltypes = PARAMETER # the C types needed to represent each array item + # self.args: [PyObject, int_index, output_item..] + def writestr(self, array, index, *output): + assert len(output) == len(self.lltypes) + ls = [] + for j in range(len(self.lltypes)): + if self.lltypes[j] == 'PyObject*': + typecode = '_o' + else: + typecode = '' + ls.append('OP_GETARRAYITEM%s(%s, %s, %s, a%d, %s)' % ( + typecode, self.typename, array, index, j, output[j])) return '\n'.join(ls) class LoGetAttr(LoC): 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 Tue Sep 21 20:40:28 2004 @@ -171,6 +171,18 @@ lltypes = r.r_item.impl, ) + def extend_OP_ALLOC_AND_SET(self, hltypes): + if len(hltypes) != 3: + return + # we have LoAllocAndSetArray + r_length, r_input, r = hltypes + if isinstance(r, CList): + sig = (R_INT, r.r_item, r) + yield sig, genc_op.LoAllocAndSetArray.With( + typename = r.typename, + lltypes = r.r_item.impl, + ) + def extend_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 @@ -326,6 +338,18 @@ llclass = llclass, ) + def extend_OP_GETITEM(self, hltypes): + if len(hltypes) != 3: + return + r, r_index, r_result = hltypes + # reading from a CList + if isinstance(r, CList): + sig = (r, R_INT, r.r_item) + yield sig, genc_op.LoGetArrayItem.With( + typename = r.typename, + lltypes = r.r_item.impl, + ) + # ____________________________________________________________ def parse_operation_templates(self): From arigo at codespeak.net Tue Sep 21 20:56:02 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 21 Sep 2004 20:56:02 +0200 (MEST) Subject: [pypy-svn] r6632 - pypy/trunk/src/pypy/translator Message-ID: <20040921185602.9F7505ADFD@thoth.codespeak.net> Author: arigo Date: Tue Sep 21 20:56:02 2004 New Revision: 6632 Modified: pypy/trunk/src/pypy/translator/genc.h pypy/trunk/src/pypy/translator/genc_op.py pypy/trunk/src/pypy/translator/genc_typeset.py Log: SETARRAYITEM support. Modified: pypy/trunk/src/pypy/translator/genc.h ============================================================================== --- pypy/trunk/src/pypy/translator/genc.h (original) +++ pypy/trunk/src/pypy/translator/genc.h Tue Sep 21 20:56:02 2004 @@ -143,11 +143,17 @@ #define OP_NEWARRAY(name,len,r,err) if (!(r=alloclist_##name(len))) goto err; #define OP_NEWARRAY_SET(name,a,i,f,v) ((PyList_##name*) a)->ob_item[i].f=v; -#define OP_NEWARRAY_SET_o(name,a,i,f,o) ((PyList_##name*) a)->ob_item[i].f=o; \ - Py_INCREF(o); +#define OP_NEWARRAY_SET_o(name,a,i,f,v) ((PyList_##name*) a)->ob_item[i].f=v; \ + Py_INCREF(v); #define OP_GETARRAYITEM(name,a,i,f,r) r=((PyList_##name*) a)->ob_item[i].f; #define OP_GETARRAYITEM_o(name,a,i,f,r) r=((PyList_##name*) a)->ob_item[i].f; \ Py_INCREF(r); +#define OP_SETARRAYITEM(name,a,i,f,v) ((PyList_##name*) a)->ob_item[i].f=v; +#define OP_SETARRAYITEM_o(name,a,i,f,v) { PyObject* tmp; \ + OP_GETARRAYITEM(name,a,i,f,tmp) \ + OP_SETARRAYITEM(name,a,i,f,v) \ + Py_INCREF(v); Py_DECREF(tmp); \ + } /************************************************************/ /*** The rest is produced by genc.py ***/ 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 Tue Sep 21 20:56:02 2004 @@ -211,6 +211,7 @@ class LoGetArrayItem(LoC): typename = PARAMETER # the name of the PyList_Xxx type in the C source lltypes = PARAMETER # the C types needed to represent each array item + macro = 'OP_GETARRAYITEM' # self.args: [PyObject, int_index, output_item..] def writestr(self, array, index, *output): assert len(output) == len(self.lltypes) @@ -220,10 +221,15 @@ typecode = '_o' else: typecode = '' - ls.append('OP_GETARRAYITEM%s(%s, %s, %s, a%d, %s)' % ( - typecode, self.typename, array, index, j, output[j])) + ls.append('%s%s(%s, %s, %s, a%d, %s)' % ( + self.macro, typecode, self.typename, + array, index, j, output[j])) return '\n'.join(ls) +class LoSetArrayItem(LoGetArrayItem): + macro = 'OP_SETARRAYITEM' + # self.args: [PyObject, int_index, input_item..] + class LoGetAttr(LoC): cost = 1 fld = PARAMETER 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 Tue Sep 21 20:56:02 2004 @@ -73,7 +73,7 @@ return r_func else: return method_representation(r_func) - if 0: # still working on it -- isinstance(var, annmodel.SomeList): + if isinstance(var, annmodel.SomeList): r_item = self.gethltype(var.s_item) typename = self.genc.declare_list(r_item.impl) r = list_representation(r_item) @@ -154,6 +154,23 @@ return result # ____________________________________________________________ + # + # Each extend_OPNAME() method below is called when there is no direct + # match for the given hltypes for the operation OPNAME. + # The extend_XXX() methods should produce (yield) as many low-level + # operation signatures as they like, using the hltypes as a guide; + # once all these have been generated, the algorithms in typer.py will + # look for a best approximate match, doing conversions if needed. + # + # For example, extend_OP_GETITEM() is called with + # hltypes == (repr_of_obj, repr_of_index, repr_of_result). + # When it is called with an instance of CList in hltypes[0], it + # generates the operation that reads an item out of this kind of array. + # This operation takes exactly an R_INT index (which may differ from + # hltypes[1]) and produce a result of exactly the CList's r_item type + # (which may differ from hltypes[2]). Then typer.py will try to + # convert the index from hltypes[1] to R_INT and convert the result back + # to hltypes[2]. def extend_OP_NEWLIST(self, hltypes): if not hltypes: @@ -350,6 +367,18 @@ lltypes = r.r_item.impl, ) + def extend_OP_SETITEM(self, hltypes): + if len(hltypes) != 4: + return + r, r_index, r_value, r_void = hltypes + # writing into a CList + if isinstance(r, CList): + sig = (r, R_INT, r.r_item, R_VOID) + yield sig, genc_op.LoSetArrayItem.With( + typename = r.typename, + lltypes = r.r_item.impl, + ) + # ____________________________________________________________ def parse_operation_templates(self): From arigo at codespeak.net Tue Sep 21 22:06:08 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 21 Sep 2004 22:06:08 +0200 (MEST) Subject: [pypy-svn] r6634 - pypy/trunk/src/pypy/translator Message-ID: <20040921200608.7B6565ADFD@thoth.codespeak.net> Author: arigo Date: Tue Sep 21 22:06:07 2004 New Revision: 6634 Modified: pypy/trunk/src/pypy/translator/genc.h pypy/trunk/src/pypy/translator/genc.py pypy/trunk/src/pypy/translator/genc_op.py pypy/trunk/src/pypy/translator/genc_typeset.py Log: Array concatenation (+=) done but probably wrong. We need to concatenate arrays of different types, e.g. in 'lst += [5]' the '[5]' is an array of constants, whereas 'lst' typically is not. One remaining test fails because it returns an array and there is no provision for converting it to a PyObject* (i.e. to a regular PyListObject*). Modified: pypy/trunk/src/pypy/translator/genc.h ============================================================================== --- pypy/trunk/src/pypy/translator/genc.h (original) +++ pypy/trunk/src/pypy/translator/genc.h Tue Sep 21 22:06:07 2004 @@ -154,6 +154,8 @@ OP_SETARRAYITEM(name,a,i,f,v) \ Py_INCREF(v); Py_DECREF(tmp); \ } +#define OP_ARRAYLEN(name,a,r) r=((PyList_##name*) a)->ob_size; +#define OP_CONCATARRAY(name,a1,a2,err) if (concatlist_##name(a1,a2)) goto err; /************************************************************/ /*** The rest is produced by genc.py ***/ Modified: pypy/trunk/src/pypy/translator/genc.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc.py (original) +++ pypy/trunk/src/pypy/translator/genc.py Tue Sep 21 22:06:07 2004 @@ -348,8 +348,16 @@ print >> f, self.C_LIST_DEALLOC_FOOTER % info print >> f, self.C_LIST_TYPEOBJECT % info - return name + print >> f, self.C_LIST_CONCAT_HEADER % info + for j in range(len(lltypes)): + print >> f, '\t\tdest->a%d = src->a%d;' % (j, j), + if lltypes[j] == 'PyObject*': + print >> f, 'Py_INCREF(dest->a%d);' % j, + print >> f + print >> f, self.C_LIST_CONCAT_FOOTER % info + + return name # ____________________________________________________________ @@ -543,4 +551,32 @@ } ''' + C_LIST_CONCAT_HEADER = ( +'''static int concatlist_%(name)s(PyObject* a1, PyObject* a2) +{ + PyList_%(name)s* o1 = (PyList_%(name)s*) a1; + PyList_%(name)s* o2 = (PyList_%(name)s*) a2; + int l1 = o1->ob_size; + int l2 = o2->ob_size; + ListItem_%(name)s* src; + ListItem_%(name)s* dest; + void* buffer = PyMem_Realloc(o1->ob_item, + (l1+l2)*sizeof(ListItem_%(name)s)); + if (buffer == NULL) { + PyErr_NoMemory(); + return -1; + } + o1->ob_item = (ListItem_%(name)s*) buffer; + src = o2->ob_item; + dest = o1->ob_item + l1; + while (--l2 >= 0) {''') + + C_LIST_CONCAT_FOOTER = ( +''' src++; + dest++; + } + return 0; +} +''') + # ____________________________________________________________ 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 Tue Sep 21 22:06:07 2004 @@ -230,6 +230,21 @@ macro = 'OP_SETARRAYITEM' # self.args: [PyObject, int_index, input_item..] +class LoConcatArray(LoC): + can_fail = True + typename = PARAMETER # the name of the PyList_Xxx type in the C source + lltypes = PARAMETER # the C types needed to represent each array item + # self.args: [PyObject, PyObject, output-PyObject] + def writestr(self, array1, array2, output_array, err): + return 'OP_CONCATARRAY(%s, %s, %s, %s)' % ( + self.typename, array1, array2, err) + def optimize(self, typer, llresult): + # the result of the concatenation is the same array as the first + # input argument. + array1 = self.args[0] + llresult[-1] = array1 # patch to do the copy statically + return False # proceed with the normal writestr() + class LoGetAttr(LoC): cost = 1 fld = PARAMETER 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 Tue Sep 21 22:06:07 2004 @@ -379,6 +379,18 @@ lltypes = r.r_item.impl, ) + def extend_OP_INPLACE_ADD(self, hltypes): + if len(hltypes) != 3: + return + r1, r2, r_result = hltypes + # concatenating CLists of the same type + if isinstance(r1, CList) and r1 == r2: + sig = (r1, r1, r1) + yield sig, genc_op.LoConcatArray.With( + typename = r1.typename, + lltypes = r1.r_item.impl, + ) + # ____________________________________________________________ def parse_operation_templates(self): From arigo at codespeak.net Tue Sep 21 22:09:15 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 21 Sep 2004 22:09:15 +0200 (MEST) Subject: [pypy-svn] r6635 - pypy/trunk/src/pypy/translator/test Message-ID: <20040921200915.7A0C65ADFD@thoth.codespeak.net> Author: arigo Date: Tue Sep 21 22:09:14 2004 New Revision: 6635 Modified: pypy/trunk/src/pypy/translator/test/snippet.py Log: I guess this is what was originally intended, though the other version is also a meaningful test. Modified: pypy/trunk/src/pypy/translator/test/snippet.py ============================================================================== --- pypy/trunk/src/pypy/translator/test/snippet.py (original) +++ pypy/trunk/src/pypy/translator/test/snippet.py Tue Sep 21 22:09:14 2004 @@ -242,7 +242,7 @@ def factorial2(n=int): # analysed in a different order if n > 1: - return n * factorial(n-1) + return n * factorial2(n-1) else: return 1 From arigo at codespeak.net Fri Sep 24 19:32:22 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 24 Sep 2004 19:32:22 +0200 (MEST) Subject: [pypy-svn] r6726 - in pypy/trunk/src/pypy/translator: . tool Message-ID: <20040924173222.7D77B5BDE8@thoth.codespeak.net> Author: arigo Date: Fri Sep 24 19:32:07 2004 New Revision: 6726 Modified: pypy/trunk/src/pypy/translator/classtyper.py pypy/trunk/src/pypy/translator/genc_typeset.py pypy/trunk/src/pypy/translator/tool/flowtrace.py Log: Minor typos and fixes. Modified: pypy/trunk/src/pypy/translator/classtyper.py ============================================================================== --- pypy/trunk/src/pypy/translator/classtyper.py (original) +++ pypy/trunk/src/pypy/translator/classtyper.py Fri Sep 24 19:32:07 2004 @@ -74,7 +74,7 @@ hltype = typeset.gethltype(s_value, unbound=is_class_attr) fld = ClassField(hltype, attr, self, is_class_attr) self.fields_here.append(fld) - # fields are divided in instance attributes and class attributes + # fields are categorized as instance attributes and class attributes # according to whether they are ever accessed with SET_ATTR or not if self.llparent: self.instance_fields = list(self.llparent.instance_fields) 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 Fri Sep 24 19:32:07 2004 @@ -241,8 +241,8 @@ opsimplecall = self.lloperations.setdefault('OP_SIMPLE_CALL', {}) for sig, opcls in opsimplecall.items(): if sig[1:2] == (R_INSTANCE,): - r = method_representation(sig[0]) - sig2 = (r,) + sig[2:] + r_meth = method_representation(sig[0]) + sig2 = (r_meth,) + sig[2:] yield sig2, opcls # Note that we are reusing the same opcls. Indeed, both the # original 'sig' and the modified one expand to the same list @@ -252,7 +252,7 @@ # /-----\ /------------\ # LLVars: funcptr, PyObject* self, arguments..., result # \-----------------------/ - # sig2 = r ... + # sig2 = r_meth ... if isinstance(r, CConstant): # maybe it is a well-known constant non-user-defined function Modified: pypy/trunk/src/pypy/translator/tool/flowtrace.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/flowtrace.py (original) +++ pypy/trunk/src/pypy/translator/tool/flowtrace.py Fri Sep 24 19:32:07 2004 @@ -271,10 +271,6 @@ return now() - start if __name__ == '__main__': - from earthenware.common import chunk - from earthenware.utils.stacktrace import init_module - init_module(True) - from pypy.objspace.std import Space space = Space() From arigo at codespeak.net Fri Sep 24 20:02:09 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 24 Sep 2004 20:02:09 +0200 (MEST) Subject: [pypy-svn] r6727 - in pypy/trunk/src/pypy/translator: . test Message-ID: <20040924180209.7A70F5BDE8@thoth.codespeak.net> Author: arigo Date: Fri Sep 24 20:02:08 2004 New Revision: 6727 Modified: pypy/trunk/src/pypy/translator/genc_repr.py pypy/trunk/src/pypy/translator/test/test_ctrans.py Log: Make the test pass, half by adding the feature in the source and half (shame) by changing the test. I'm not sure we'll ever need to convert the internal array-like objects to regular lists, so let's not worry about it now. Modified: pypy/trunk/src/pypy/translator/genc_repr.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc_repr.py (original) +++ pypy/trunk/src/pypy/translator/genc_repr.py Fri Sep 24 20:02:08 2004 @@ -260,6 +260,14 @@ def __repr__(self): return 'CList(%r)' % (self.r_item,) + def convert_to(self, target, typeset): + if target == R_OBJECT: + # can convert to a generic PyObject* + # (but not, or not yet, to a regular PyListObject*!) + return genc_op.LoCopy + else: + raise CannotConvert + # ____________________________________________________________ # # Predefined CReprs and caches for building more Modified: pypy/trunk/src/pypy/translator/test/test_ctrans.py ============================================================================== --- pypy/trunk/src/pypy/translator/test/test_ctrans.py (original) +++ pypy/trunk/src/pypy/translator/test/test_ctrans.py Fri Sep 24 20:02:08 2004 @@ -130,7 +130,11 @@ def test_call_five(self): call_five = self.getcompiled(snippet.call_five) - self.assertEquals(call_five(), [5]) + result = call_five() + #self.assertEquals(result, [5]) + # -- currently result isn't a real list, but a pseudo-array + # that can't be inspected from Python. + self.assertEquals(result.__class__.__name__[:8], "list of ") def test_class_defaultattr(self): class K: From arigo at codespeak.net Fri Sep 24 20:10:33 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 24 Sep 2004 20:10:33 +0200 (MEST) Subject: [pypy-svn] r6728 - pypy/trunk/src/pypy/translator/test Message-ID: <20040924181033.248305BDE8@thoth.codespeak.net> Author: arigo Date: Fri Sep 24 20:10:32 2004 New Revision: 6728 Modified: pypy/trunk/src/pypy/translator/test/snippet.py pypy/trunk/src/pypy/translator/test/test_annrpython.py pypy/trunk/src/pypy/translator/test/test_ctrans.py Log: A new failing test that shows a limitation of genc.py with list concatenation. This check-in also adds the new test to test_annrpython to ensure that it is correctly annotated (this succeeds). Modified: pypy/trunk/src/pypy/translator/test/snippet.py ============================================================================== --- pypy/trunk/src/pypy/translator/test/snippet.py (original) +++ pypy/trunk/src/pypy/translator/test/snippet.py Fri Sep 24 20:10:32 2004 @@ -254,6 +254,15 @@ _append_five(a) return a +def _append_six(lst): + lst += [6] + +def call_five_six(): + a = [] + _append_five(a) + _append_six(a) + return a + # INHERITANCE / CLASS TESTS class C(object): pass Modified: pypy/trunk/src/pypy/translator/test/test_annrpython.py ============================================================================== --- pypy/trunk/src/pypy/translator/test/test_annrpython.py (original) +++ pypy/trunk/src/pypy/translator/test/test_annrpython.py Fri Sep 24 20:10:32 2004 @@ -237,6 +237,20 @@ # XXX not sure this is the best behavior... self.assertEquals(s, annmodel.immutablevalue(42)) + def test_call_five(self): + a = RPythonAnnotator() + s = a.build_types(snippet.call_five, []) + # returns should be a list of constants (= 5) + self.assert_(isinstance(s, annmodel.SomeList)) + self.assertEquals(s.s_item, annmodel.immutablevalue(5)) + + def test_call_five_six(self): + a = RPythonAnnotator() + s = a.build_types(snippet.call_five_six, []) + # returns should be a list of positive integers + self.assert_(isinstance(s, annmodel.SomeList)) + self.assertEquals(s.s_item, annmodel.SomeInteger(nonneg=True)) + def g(n): return [0,1,2,n] Modified: pypy/trunk/src/pypy/translator/test/test_ctrans.py ============================================================================== --- pypy/trunk/src/pypy/translator/test/test_ctrans.py (original) +++ pypy/trunk/src/pypy/translator/test/test_ctrans.py Fri Sep 24 20:10:32 2004 @@ -136,6 +136,14 @@ # that can't be inspected from Python. self.assertEquals(result.__class__.__name__[:8], "list of ") + def test_call_five_six(self): + call_five_six = self.getcompiled(snippet.call_five_six) + result = call_five_six() + #self.assertEquals(result, [5, 6]) + # -- currently result isn't a real list, but a pseudo-array + # that can't be inspected from Python. + self.assertEquals(result.__class__.__name__[:8], "list of ") + def test_class_defaultattr(self): class K: n = "hello" From arigo at codespeak.net Fri Sep 24 21:43:52 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 24 Sep 2004 21:43:52 +0200 (MEST) Subject: [pypy-svn] r6729 - pypy/trunk/src/pypy/translator Message-ID: <20040924194352.CABBF5BDE8@thoth.codespeak.net> 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) From arigo at codespeak.net Sat Sep 25 11:56:47 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 25 Sep 2004 11:56:47 +0200 (MEST) Subject: [pypy-svn] r6730 - in pypy/trunk/src/pypy/translator: . test Message-ID: <20040925095647.92EE85A0DC@thoth.codespeak.net> Author: arigo Date: Sat Sep 25 11:56:46 2004 New Revision: 6730 Modified: pypy/trunk/src/pypy/translator/genc.h 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/test/snippet.py pypy/trunk/src/pypy/translator/test/test_ctrans.py Log: Finished the list += list support. Modified: pypy/trunk/src/pypy/translator/genc.h ============================================================================== --- pypy/trunk/src/pypy/translator/genc.h (original) +++ pypy/trunk/src/pypy/translator/genc.h Sat Sep 25 11:56:46 2004 @@ -154,8 +154,9 @@ OP_SETARRAYITEM(name,a,i,f,v) \ Py_INCREF(v); Py_DECREF(tmp); \ } -#define OP_ARRAYLEN(name,a,r) r=((PyList_##name*) a)->ob_size; -#define OP_CONCATARRAY(name,a1,a2,err) if (concatlist_##name(a1,a2)) goto err; +#define ARRAYLEN(a) (((PyVarObject*) a)->ob_size) +#define OP_ARRAYLEN(a,r) r=ARRAYLEN(a); +#define OP_GROWLIST(name,a,l,err) if (growlist_##name(a,l) < 0) goto err; /************************************************************/ /*** The rest is produced by genc.py ***/ Modified: pypy/trunk/src/pypy/translator/genc.py ============================================================================== --- pypy/trunk/src/pypy/translator/genc.py (original) +++ pypy/trunk/src/pypy/translator/genc.py Sat Sep 25 11:56:46 2004 @@ -349,14 +349,6 @@ print >> f, self.C_LIST_TYPEOBJECT % info - print >> f, self.C_LIST_CONCAT_HEADER % info - for j in range(len(lltypes)): - print >> f, '\t\tdest->a%d = src->a%d;' % (j, j), - if lltypes[j] == 'PyObject*': - print >> f, 'Py_INCREF(dest->a%d);' % j, - print >> f - print >> f, self.C_LIST_CONCAT_FOOTER % info - return name # ____________________________________________________________ @@ -549,34 +541,21 @@ } return (PyObject*) o; } -''' - C_LIST_CONCAT_HEADER = ( -'''static int concatlist_%(name)s(PyObject* a1, PyObject* a2) +static int growlist_%(name)s(PyObject* a, int extralen) { - PyList_%(name)s* o1 = (PyList_%(name)s*) a1; - PyList_%(name)s* o2 = (PyList_%(name)s*) a2; - int l1 = o1->ob_size; - int l2 = o2->ob_size; - ListItem_%(name)s* src; - ListItem_%(name)s* dest; - void* buffer = PyMem_Realloc(o1->ob_item, - (l1+l2)*sizeof(ListItem_%(name)s)); + /* NB. this function does not update o->ob_size */ + PyList_%(name)s* o = (PyList_%(name)s*) a; + int newlen = o->ob_size + extralen; + void* buffer = PyMem_Realloc(o->ob_item, + newlen*sizeof(ListItem_%(name)s)); if (buffer == NULL) { PyErr_NoMemory(); return -1; } - o1->ob_item = (ListItem_%(name)s*) buffer; - src = o2->ob_item; - dest = o1->ob_item + l1; - while (--l2 >= 0) {''') - - C_LIST_CONCAT_FOOTER = ( -''' src++; - dest++; - } + o->ob_item = (ListItem_%(name)s*) buffer; return 0; } -''') +''' # ____________________________________________________________ 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 Sat Sep 25 11:56:46 2004 @@ -160,19 +160,16 @@ can_fail = True typename = PARAMETER # the name of the PyList_Xxx type in the C source lltypes = PARAMETER # the C types needed to represent each array item + length = PARAMETER # self.args: [item0.., item1.., item2.., ..., output PyObject] def writestr(self, *stuff): args = stuff[:-2] result = stuff[-2] err = stuff[-1] - if len(args) == len(self.lltypes) == 0: - arraylen = 0 - else: - assert len(args) % len(self.lltypes) == 0 - arraylen = len(args) / len(self.lltypes) + assert len(args) == self.length * len(self.lltypes) ls = ['OP_NEWARRAY(%s, %d, %s, %s)' % ( - self.typename, arraylen, result, err)] - for i in range(arraylen): + self.typename, self.length, result, err)] + for i in range(self.length): for j in range(len(self.lltypes)): if self.lltypes[j] == 'PyObject*': typecode = '_o' @@ -230,20 +227,22 @@ macro = 'OP_SETARRAYITEM' # self.args: [PyObject, int_index, input_item..] -class LoConcatArray(LoC): +class LoArrayGrow(LoC): can_fail = True - typename = PARAMETER # the name of the PyList_Xxx type in the C source - lltypes = PARAMETER # the C types needed to represent each array item - # self.args: [PyObject, PyObject, output-PyObject] - def writestr(self, array1, array2, output_array, err): - return 'OP_CONCATARRAY(%s, %s, %s, %s)' % ( - self.typename, array1, array2, err) - def optimize(self, typer, llresult): - # the result of the concatenation is the same array as the first - # input argument. - array1 = self.args[0] - llresult[-1] = array1 # patch to do the copy statically - return False # proceed with the normal writestr() + cost = 3 + typename = PARAMETER + # self.args: [PyObject, int_extralen] + def writestr(self, array, extralen, err): + return 'OP_GROWLIST(%s, %s, %s, %s)' % (self.typename, array, + extralen, err) + +class LoArrayFastAppend(LoSetArrayItem): + # self.args: [PyObject, new_item..] + def writestr(self, array, *newitem): + index = 'ARRAYLEN(%s)' % array + ls = [LoSetArrayItem.writestr(self, array, index, *newitem), + '%s++;' % index] + return '\n'.join(ls) class LoGetAttr(LoC): cost = 1 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 Sat Sep 25 11:56:46 2004 @@ -186,6 +186,7 @@ yield sig, genc_op.LoNewArray.With( typename = r.typename, lltypes = r.r_item.impl, + length = len(hltypes)-1, ) def extend_OP_ALLOC_AND_SET(self, hltypes): @@ -271,6 +272,7 @@ # Calling a built-in defined in genc.h, if we have a macro # CALL_funcname() opname = 'CALL_' + getattr(fn, '__name__', '?') + self.typemismatch(opname, hltypes[1:]) # invoke extend_CALL_xxx() if opname in self.lloperations: for sig, llopcls in self.lloperations[opname].items(): sig = (r,) + sig @@ -379,16 +381,44 @@ lltypes = r.r_item.impl, ) - def extend_OP_INPLACE_ADD(self, hltypes): + def extend_OP_LEN(self, hltypes): + if len(hltypes) != 2: + return + r, r_result = hltypes + # the length of a CList + if isinstance(r, CList): + sig = (r, R_INT) + yield sig, genc_op.LoStandardOperation.With( + can_fail = False, + llname = 'OP_ARRAYLEN', + cost = 1, + ) + + extend_CALL_len = extend_OP_LEN + + def extend_OP_GROWLIST(self, hltypes): + if len(hltypes) != 3: + return + r, r_newlen, r_void = hltypes + # grow a CList + if isinstance(r, CList): + sig = (r, R_INT, R_VOID) + yield sig, genc_op.LoArrayGrow.With( + typename = r.typename, + ) + + def extend_OP_FASTAPPEND(self, hltypes): + # like append() but can assume that the list was already + # reallocated with OP_GROWLIST and has enough room if len(hltypes) != 3: return - r1, r2, r_result = hltypes - # concatenating CLists of the same type - if isinstance(r1, CList) and r1 == r2: - sig = (r1, r1, r1) - yield sig, genc_op.LoConcatArray.With( - typename = r1.typename, - lltypes = r1.r_item.impl, + r, r_newitem, r_void = hltypes + # append to a CList + if isinstance(r, CList): + sig = (r, r.r_item, R_VOID) + yield sig, genc_op.LoArrayFastAppend.With( + typename = r.typename, + lltypes = r.r_item.impl, ) # ____________________________________________________________ Modified: pypy/trunk/src/pypy/translator/test/snippet.py ============================================================================== --- pypy/trunk/src/pypy/translator/test/snippet.py (original) +++ pypy/trunk/src/pypy/translator/test/snippet.py Sat Sep 25 11:56:46 2004 @@ -263,6 +263,10 @@ _append_six(a) return a +def call_unpack_56(): + a = call_five_six() + return len(a), a[0], a[1] + # INHERITANCE / CLASS TESTS class C(object): pass Modified: pypy/trunk/src/pypy/translator/test/test_ctrans.py ============================================================================== --- pypy/trunk/src/pypy/translator/test/test_ctrans.py (original) +++ pypy/trunk/src/pypy/translator/test/test_ctrans.py Sat Sep 25 11:56:46 2004 @@ -136,13 +136,10 @@ # that can't be inspected from Python. self.assertEquals(result.__class__.__name__[:8], "list of ") - def test_call_five_six(self): - call_five_six = self.getcompiled(snippet.call_five_six) - result = call_five_six() - #self.assertEquals(result, [5, 6]) - # -- currently result isn't a real list, but a pseudo-array - # that can't be inspected from Python. - self.assertEquals(result.__class__.__name__[:8], "list of ") + def test_call_unpack_56(self): + call_unpack_56 = self.getcompiled(snippet.call_unpack_56) + result = call_unpack_56() + self.assertEquals(result, (2, 5, 6)) def test_class_defaultattr(self): class K: From arigo at codespeak.net Sat Sep 25 14:03:04 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 25 Sep 2004 14:03:04 +0200 (MEST) Subject: [pypy-svn] r6733 - pypy/trunk/src/pypy/annotation Message-ID: <20040925120304.18D2A5A0DC@thoth.codespeak.net> Author: arigo Date: Sat Sep 25 14:03:03 2004 New Revision: 6733 Removed: pypy/trunk/src/pypy/annotation/README.txt Log: this text was not relevant any more Deleted: /pypy/trunk/src/pypy/annotation/README.txt ============================================================================== --- /pypy/trunk/src/pypy/annotation/README.txt Sat Sep 25 14:03:03 2004 +++ (empty file) @@ -1,47 +0,0 @@ -guiding design/implementation ideas ------------------------------------ - -Annotation aims at providing type inference to RPython programs. -Annotation works from the flowmodel of a program. (A flowmodel -can be viewed as an information-preserving 'dead code' representation -of a program.) - -Annotation mainly deals with connecting interesting information -to SomeValue's which are instances of a very simple class:: - - class SomeValue: - pass - -A SomeValue represents a possible state within a program. -Any information about the type or more specific information -like being a constant is done by an Annotation. E.g. the -an int-type annotation is expressed like this: - - Annotation(op.type, someval1, someval2) - Annotation(op.constant(int), someval2) - -Note especially how the constant-ness of someval2 encodes the -information 'someval2 is exactly int'. SomeValue's are values on which -little is known by default, unless Annotation's are used to restrict -them. - -Keep in mind that the more Annotation's you have on SomeValue, the more -restricted it is, i.e. the less real values it can represent. A newly -created SomeValue() has no annotation by default, i.e. can represent -anything at all. At the other extreme, there is a special -'blackholevalue' that behaves as if it had all Annotation's set on it; -it stands for an impossible, non-existent value (because all these -Annotations are contradictory). The name 'blackholevalue' reminds you -that during type inference SomeValue's start with a lot of annotations -(possibly as a 'blackholevalue'), and annotations are killed -- less -annotations, more possibilities. - -Annotations are stored in a global list, which is an AnnotationSet instance. AnnotationSet provides (via Transactions) methods to query, add and kill annotations. It also manages "sharing": two different SomeValue's can be later found to be identical (in the Python sense of "is"), and the AnnotationSet can be taught about this. - -You can't directly add annotations to an AnnotationSet. Adding an -annotation is considered to be dependent on previous annotations. -Thus you invoke annset.record(func), and your function 'func' will -be invoked with a 'Recorder' instance: you perform queries with it -and when you add/set a new annotation the recorder will remember -the dependency of the previous (queried) annotation towards the -new annotation. From arigo at codespeak.net Sat Sep 25 20:50:56 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 25 Sep 2004 20:50:56 +0200 (MEST) Subject: [pypy-svn] r6734 - pypy/trunk/doc/objspace Message-ID: <20040925185056.EDF4A5A0DC@thoth.codespeak.net> Author: arigo Date: Sat Sep 25 20:50:56 2004 New Revision: 6734 Modified: pypy/trunk/doc/objspace/restrictedpy.txt Log: A few updates on RPython. Modified: pypy/trunk/doc/objspace/restrictedpy.txt ============================================================================== --- pypy/trunk/doc/objspace/restrictedpy.txt (original) +++ pypy/trunk/doc/objspace/restrictedpy.txt Sat Sep 25 20:50:56 2004 @@ -101,11 +101,15 @@ **lists** lists are used as an allocated array; list.append() does naive resizing, so as - far as possible use list comprehensions (see below) + far as possible use list comprehensions (see below). list.extend() or the += + operator are allowed and efficient. Unless there is really a use case for it, + repetition is limited to initialization purposes: '[single_value] * length'. **dicts** - no dicts + dicts with string keys only (preferrably the kind of strings that are usually + interned in CPython, i.e. short strings that look like identifiers). The + implementation could safely decide that all dict keys should be interned. **control structures** @@ -150,8 +154,7 @@ **classes** -+ methods do not change after startup -+ methods are never hidden by attributes ++ methods and other class attributes do not change after startup + inheritance is supported + classes are first-class objects too