[pypy-svn] r6360 - in pypy/trunk/src/pypy/translator: . test
arigo at codespeak.net
arigo at codespeak.net
Wed Sep 8 15:55:54 CEST 2004
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 '<C %s>' % ' + '.join(self.impl)
def __repr__(self):
- if hasattr(self, 'const'):
- return '<C:= %r>' % (self.const,)
- else:
- return '<C: %s>' % ' + '.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='<const %r>' % (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
More information about the Pypy-commit
mailing list