[pypy-svn] r6372 - pypy/trunk/src/pypy/translator
arigo at codespeak.net
arigo at codespeak.net
Thu Sep 9 21:16:08 CEST 2004
Author: arigo
Date: Thu Sep 9 21:16:06 2004
New Revision: 6372
Added:
pypy/trunk/src/pypy/translator/genc_repr.py
Modified:
pypy/trunk/src/pypy/translator/classtyper.py
pypy/trunk/src/pypy/translator/genc.py
pypy/trunk/src/pypy/translator/genc_op.py
pypy/trunk/src/pypy/translator/genc_typeset.py
pypy/trunk/src/pypy/translator/typer.py
Log:
Urgh, another huge check-in. This one introduces classes for various
kinds of representations, and put them to the new genc_repr.py. This is a
clean-up of the conversion mess. Now the representation classes,
subclassing CRepr, provide a convert_to() method called when a value of
some type (i.e. with some repr) needs to be converted to a value of some
other, given type.
To support this model, the operations have a "cost" and typer.py will
select, among the variants of an operation, the one with the less cost
(including the cost of conversions). The operation() method is (a bit)
simpler now.
genc_typeset.py is much more readable now: the three huge functions have
been split up in genc_repr.py and in a set of build_OP_XXX() methods.
Automatic chaining of conversion is gone again.
Modified: pypy/trunk/src/pypy/translator/classtyper.py
==============================================================================
--- pypy/trunk/src/pypy/translator/classtyper.py (original)
+++ pypy/trunk/src/pypy/translator/classtyper.py Thu Sep 9 21:16:06 2004
@@ -8,6 +8,7 @@
from pypy.objspace.flow.model import FunctionGraph, Block, Link
from pypy.annotation import model as annmodel
from pypy.translator.typer import LLTyper, LLFunction
+from pypy.translator.genc_repr import R_OBJECT
r_ends_in_underscore_digit = re.compile(r'_\d+$')
@@ -54,7 +55,7 @@
for attr, s_value in cdef.attrs.items()]
self.pyobj_fields = [ # XXX this should not be necessary
- fld.name for fld in self.fields if fld.hltype is typeset.R_OBJECT]
+ fld.name for fld in self.fields if fld.hltype == R_OBJECT]
self.s_instance = annmodel.SomeInstance(self.cdef)
def get_management_functions(self):
Modified: pypy/trunk/src/pypy/translator/genc.py
==============================================================================
--- pypy/trunk/src/pypy/translator/genc.py (original)
+++ pypy/trunk/src/pypy/translator/genc.py Thu Sep 9 21:16:06 2004
@@ -10,6 +10,7 @@
from pypy.translator.classtyper import LLClass
from pypy.translator.genc_typeset import CTypeSet
from pypy.translator.genc_op import ERROR_RETVAL
+from pypy.translator.genc_repr import R_INT, R_OBJECT
# ____________________________________________________________
@@ -267,9 +268,9 @@
print >> f, self.C_MEMBERLIST_HEADER % info
# XXX write member definitions for member with well-known types only
for fld in llclass.fields:
- if fld.hltype is self.typeset.R_OBJECT:
+ if fld.hltype == R_OBJECT:
t = 'T_OBJECT_EX'
- elif fld.hltype is self.typeset.R_INT:
+ elif fld.hltype == R_INT:
t = 'T_INT'
else:
continue # ignored
Modified: pypy/trunk/src/pypy/translator/genc_op.py
==============================================================================
--- pypy/trunk/src/pypy/translator/genc_op.py (original)
+++ pypy/trunk/src/pypy/translator/genc_op.py Thu Sep 9 21:16:06 2004
@@ -14,6 +14,7 @@
class LoC(LLOp):
# base class for LLOps that produce C code.
+ cost = 2
def write(self):
"Default write method, delegating to writestr()."
@@ -53,16 +54,19 @@
"A standard operation is one defined by a macro in genc.h."
can_fail = PARAMETER
llname = PARAMETER
+ cost = PARAMETER
def writestr(self, *args):
return self.llname + '(' + ', '.join(args) + ')'
class LoKnownAnswer(LoOptimized):
known_answer = PARAMETER
+ cost = 0
def optimize(self, typer):
return self.known_answer
class LoNewList(LoC):
can_fail = True
+ cost = 3
def writestr(self, *stuff):
content = stuff[:-2]
result = stuff[-2]
@@ -76,6 +80,7 @@
class LoCallFunction(LoC):
can_fail = True
+ cost = 3
def writestr(self, func, *stuff):
args = stuff[:-2]
result = stuff[-2]
@@ -102,11 +107,11 @@
class LoConvertTupleItem(LoOptimized):
source_r = PARAMETER # tuple-of-hltypes, one per item of the input tuple
target_r = PARAMETER # tuple-of-hltypes, one per item of the output tuple
- index = PARAMETER # index of the item to convert
+ cost = PARAMETER
def optimize(self, typer):
# replace this complex conversion by the simpler conversion of
- # only the indexth item
+ # only the items that changed
llinputs = []
pos = 0
for r in self.source_r:
@@ -121,7 +126,7 @@
llrepr = [] # answer
for i in range(len(self.source_r)):
- if i == self.index:
+ if self.source_r[i] != self.target_r[i]:
# convert this item
llrepr += typer.convert(self.source_r[i], llinputs[i],
self.target_r[i], lloutputs[i])
@@ -132,6 +137,7 @@
class LoNewTuple(LoC):
can_fail = True
+ cost = 3
def writestr(self, *stuff):
args = stuff[:-2]
@@ -145,10 +151,10 @@
return '\n'.join(ls)
class LoConvertChain(LoOptimized):
- r_from = PARAMETER
- r_middle = PARAMETER
- r_to = PARAMETER
- convert_length = PARAMETER
+ r_from = PARAMETER
+ r_middle = PARAMETER
+ r_to = PARAMETER
+ cost = PARAMETER
def optimize(self, typer):
half = len(self.r_from.impl)
@@ -158,17 +164,30 @@
middle = typer.convert(self.r_from, input, self.r_middle)
return typer.convert(self.r_middle, middle, self.r_to, output)
+class LoDummyResult(LoC):
+ cost = 1
+ def write(self):
+ ls = []
+ for a in self.args:
+ if a.type == 'PyObject*':
+ ls.append('%s = Py_None; Py_INCREF(%s); /* dummy */' % (
+ a.name, a.name))
+ return '\n'.join(ls)
+
# ____________________________________________________________
class LoMove(LoC):
+ cost = 1
def writestr(self, x, y):
return '%s = %s;' % (y, x)
class LoGoto(LoC):
+ cost = 0
def write(self):
return 'goto %s;' % self.errtarget
class LoCopy(LoOptimized):
+ cost = 0
def optimize(self, typer):
# the result's llvars is equal to the input's llvars.
assert len(self.args) % 2 == 0
@@ -177,6 +196,7 @@
class LoDoSomethingWithRef(LoC):
do_what = PARAMETER
+ cost = 1
def write(self):
ls = []
for a in self.args:
@@ -189,6 +209,7 @@
LoXDecref = LoDoSomethingWithRef.With(do_what = 'Py_XDECREF')
class LoComment(LoC):
+ cost = 0
def write(self):
s = self.errtarget
s = s.replace('/*', '/+')
@@ -232,6 +253,7 @@
ERROR_CHECK[retvar.type], err))
class LoReturn(LoC):
+ cost = 1
def write(self):
if not self.args:
return 'return 0;'
Added: pypy/trunk/src/pypy/translator/genc_repr.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/translator/genc_repr.py Thu Sep 9 21:16:06 2004
@@ -0,0 +1,216 @@
+"""
+High-level types and how variables of that type are represented by a
+series of low-level variables.
+"""
+
+import types, __builtin__
+from pypy.translator.typer import CannotConvert, LLConst
+from pypy.translator import genc_op
+
+
+class CRepr:
+ "A possible representation of a flow-graph variable as C-level variables."
+ parse_code = None # character(s) for PyArg_ParseTuple()
+
+ def __repr__(self):
+ return '%s(%s)' % (self.__class__.__name__, ' + '.join(self.impl))
+
+ def convert_to(self, target, typeset):
+ raise CannotConvert
+
+
+class CAtomicRepr(CRepr):
+ "A simple C type."
+
+ def __init__(self, impl, parse_code=None):
+ self.impl = impl
+ self.parse_code = parse_code
+
+
+class CUndefined(CRepr):
+ "An undefined value. (singleton class)"
+ impl = []
+
+ def convert_to(self, target, typeset):
+ return genc_op.LoDummyResult
+
+
+class CConstant(CRepr):
+ "A constant value."
+ impl = []
+
+ def __init__(self, value):
+ self.value = value
+
+ def __repr__(self):
+ return 'CConstant(%r)' % (self.value,)
+
+ def convert_to_pyobj(self, initexpr, globalname):
+ # helper for subclasses
+ return genc_op.LoKnownAnswer.With(
+ known_answer = [LLConst('PyObject*', globalname, initexpr,
+ to_declare = bool(initexpr))],
+ )
+
+class CConstantInt(CConstant):
+ def convert_to(self, target, typeset):
+ if target == R_INT:
+ # can convert the constant to a C int
+ return genc_op.LoKnownAnswer.With(
+ known_answer = [LLConst('int', '%d' % self.value)],
+ )
+ elif target == R_OBJECT:
+ # can convert the constant to a PyObject*
+ if self.value >= 0:
+ name = 'g_IntObj_%d' % self.value
+ else:
+ name = 'g_IntObj_minus%d' % abs(self.value)
+ return self.convert_to_pyobj(
+ 'PyInt_FromLong(%d)' % self.value,
+ name)
+ else:
+ raise CannotConvert
+
+class CConstantStr(CConstant):
+ def convert_to(self, target, typeset):
+ if target == R_OBJECT:
+ # can convert the constant to a PyObject*
+ return self.convert_to_pyobj(
+ 'PyString_FromStringAndSize(%s, %d)' % (c_str(self.value),
+ len(self.value)),
+ 'g_StrObj_%s' % manglestr(self.value))
+ else:
+ raise CannotConvert
+
+class CConstantNone(CConstant):
+ def convert_to(self, target, typeset):
+ if target == R_OBJECT:
+ # can convert the constant to a PyObject*
+ return self.convert_to_pyobj(None, 'Py_None')
+ else:
+ raise CannotConvert
+
+class CConstantBuiltin(CConstant): # a constant from the __builtin__ module
+ def convert_to(self, target, typeset):
+ if target == R_OBJECT:
+ # can load the constant as a PyObject* from the builtins
+ fn = self.value
+ if getattr(__builtin__, fn.__name__, None) is not fn:
+ raise CannotConvert # unless it doesn't appear to be there
+ return self.convert_to_pyobj(
+ 'PyMapping_GetItemString(PyEval_GetBuiltins(), %s)' % (
+ c_str(fn.__name__)),
+ 'g_Builtin_%s' % manglestr(fn.__name__))
+ else:
+ raise CannotConvert
+
+
+class CTuple(CRepr):
+ "An unpacked tuple. Represents all its items independently."
+
+ def __init__(self, items_r):
+ self.items_r = tuple(items_r)
+ self.impl = []
+ for r in items_r:
+ self.impl += r.impl
+
+ def __repr__(self):
+ return 'CTuple(%s)' % ', '.join([repr(r) for r in self.items_r])
+
+ def convert_to(self, target, typeset):
+ if isinstance(target, CTuple):
+ if len(self.items_r) != len(target.items_r):
+ raise CannotConvert
+ # convert each tuple item that needs to be converted
+ cost = 0
+ for r1, r2 in zip(self.items_r, target.items_r):
+ if r1 != r2:
+ cost += typeset.getconversion(r1, r2).cost
+ return genc_op.LoConvertTupleItem.With(
+ source_r = self.items_r,
+ target_r = target.items_r,
+ cost = cost,
+ )
+ elif target == R_OBJECT:
+ # to convert a tuple to a PyTupleObject* we first need to
+ # make sure that all items are PyObject*.
+ intermediate_r = (R_OBJECT,) * len(self.items_r)
+ if self.items_r == intermediate_r:
+ return genc_op.LoNewTuple # already the case
+ else:
+ r_middle = tuple_representation(intermediate_r)
+ cost1 = typeset.getconversion(self, r_middle).cost
+ return genc_op.LoConvertChain.With(
+ r_from = self,
+ r_middle = r_middle,
+ r_to = target,
+ cost = cost1 + genc_op.LoNewTuple.cost,
+ )
+ else:
+ raise CannotConvert
+
+# ____________________________________________________________
+#
+# Predefined CReprs and caches for building more
+
+R_VOID = CAtomicRepr([])
+R_INT = CAtomicRepr(['int'], parse_code='i')
+R_OBJECT = CAtomicRepr(['PyObject*'], parse_code='O')
+R_UNDEFINED = CUndefined()
+
+R_TUPLE_CACHE = {}
+R_CONSTANT_CACHE = {}
+
+def tuple_representation(items_r):
+ items_r = tuple(items_r)
+ try:
+ return R_TUPLE_CACHE[items_r]
+ except KeyError:
+ rt = R_TUPLE_CACHE[items_r] = CTuple(items_r)
+ return rt
+
+CONST_TYPES = {
+ int: CConstantInt,
+ str: CConstantStr,
+ types.NoneType: CConstantNone,
+ types.BuiltinFunctionType: CConstantBuiltin,
+ }
+
+def constant_representation(value):
+ key = type(value), value # to avoid mixing for example 0 and 0.0
+ try:
+ return R_CONSTANT_CACHE[key]
+ except KeyError:
+ if isinstance(value, tuple):
+ # tuples have their own representation and
+ # don't need a fully constant representation
+ items_r = [constant_representation(x) for x in value]
+ return tuple_representation(items_r)
+ for cls in type(value).__mro__:
+ if cls in CONST_TYPES:
+ cls = CONST_TYPES[cls]
+ break
+ else:
+ cls = CConstant
+ r = R_CONSTANT_CACHE[key] = cls(value)
+ return r
+
+
+def c_str(s):
+ "Return the C expression for the string 's'."
+ s = repr(s)
+ if s.startswith("'"):
+ s = '"' + s[1:-1].replace('"', r'\"') + '"'
+ return s
+
+def manglestr(s):
+ "Return an identifier name unique for the string 's'."
+ l = []
+ for c in s:
+ if not ('a' <= c <= 'z' or 'A' <= c <= 'Z' or '0' <= c <= '9'):
+ if c == '_':
+ c = '__'
+ else:
+ c = '_%02x' % ord(c)
+ l.append(c)
+ return ''.join(l)
Modified: pypy/trunk/src/pypy/translator/genc_typeset.py
==============================================================================
--- pypy/trunk/src/pypy/translator/genc_typeset.py (original)
+++ pypy/trunk/src/pypy/translator/genc_typeset.py Thu Sep 9 21:16:06 2004
@@ -1,30 +1,18 @@
-import re, types, __builtin__
+from __future__ import generators
+import re
from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant
from pypy.annotation import model as annmodel
-from pypy.translator.typer import LLConst
from pypy.translator import genc_op
-
-
-class CRepr:
- "A possible representation of a flow-graph variable as C-level variables."
-
- def __init__(self, impl, parse_code=None, name=None):
- self.impl = impl # list [(C type, prefix for variable name)]
- self.parse_code = parse_code # character(s) for PyArg_ParseTuple()
- self.name = name or '<C %s>' % ' + '.join(self.impl)
-
- def __repr__(self):
- return self.name
+from pypy.translator.typer import CannotConvert
+from pypy.translator.genc_repr import R_VOID, R_INT, R_OBJECT, R_UNDEFINED
+from pypy.translator.genc_repr import tuple_representation
+from pypy.translator.genc_repr import constant_representation, CConstant
+from pypy.translator.genc_repr import c_str, manglestr
class CTypeSet:
"A (small) set of C types that typer.LLFunction can manipulate."
- R_VOID = CRepr([])
- R_INT = CRepr(['int'], parse_code='i')
- R_OBJECT = CRepr(['PyObject*'], parse_code='O')
- #R_DONTCARE = CRepr([]) # for uninitialized variables
-
REPR_BY_CODE = {
'v': R_VOID,
'i': R_INT,
@@ -45,9 +33,9 @@
def __init__(self, genc, bindings):
self.genc = genc
self.bindings = bindings
- self.r_constants = {}
- self.r_tuples = {}
- self.lloperations = {'convert': {}}
+ self.conversion_cache = {}
+ self.conversion_errors = {}
+ self.lloperations = {'convert': self.conversion_cache}
self.parse_operation_templates()
# __________ methods required by LLFunction __________
@@ -60,187 +48,130 @@
var = self.bindings.get(var) or annmodel.SomeObject()
if isinstance(var, annmodel.SomeObject):
if var.is_constant():
- return self.constant_representation(var.const)
+ return constant_representation(var.const)
if issubclass(var.knowntype, int):
- return self.R_INT
+ return R_INT
if isinstance(var, annmodel.SomeImpossibleValue):
- return self.R_VOID
+ return R_VOID
if isinstance(var, annmodel.SomeTuple):
items_r = [self.gethltype(s_item) for s_item in var.items]
- return self.tuple_representation(items_r)
+ return tuple_representation(items_r)
# fall-back
- return self.R_OBJECT
- #if isinstance(var, UndefinedConstant):
- # return self.R_DONTCARE
+ return R_OBJECT
+ if isinstance(var, UndefinedConstant):
+ return R_UNDEFINED
if isinstance(var, Constant):
- return self.constant_representation(var.value)
+ return constant_representation(var.value)
raise TypeError, var
def represent(self, hltype):
return hltype.impl
+ def getconversion(self, hltype1, hltype2):
+ sig = hltype1, hltype2
+ if sig in self.conversion_errors:
+ raise CannotConvert # shortcut
+ try:
+ return self.conversion_cache[sig]
+ except KeyError:
+ try:
+ llopcls = hltype1.convert_to(hltype2, self)
+ self.conversion_cache[sig] = llopcls
+ return llopcls
+ except CannotConvert:
+ self.conversion_errors[sig] = True
+ raise
+
def typingerror(self, opname, hltypes):
- # build operations with a variable number of arguments on demand
- if opname == 'OP_NEWLIST':
- opnewlist = self.lloperations.setdefault('OP_NEWLIST', {})
- sig = (self.R_OBJECT,) * len(hltypes)
- if sig in opnewlist:
- return False
- opnewlist[sig] = genc_op.LoNewList
- return True # retry
- if opname == 'OP_NEWTUPLE':
- opnewtuple = self.lloperations.setdefault('OP_NEWTUPLE', {})
- rt = self.tuple_representation(hltypes[:-1])
- sig = tuple(hltypes[:-1]) + (rt,)
- if sig in opnewtuple:
- return False
- opnewtuple[sig] = genc_op.LoCopy
- # Note that we can use LoCopy to virtually build a tuple because
- # the tuple representation 'rt' is just the collection of all the
- # representations for the input args.
- return True # retry
- if opname == 'OP_SIMPLE_CALL' and hltypes:
- opsimplecall = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
- sig = (self.R_OBJECT,) * len(hltypes)
- if sig in opsimplecall:
- return False
- opsimplecall[sig] = genc_op.LoCallFunction
- return True # retry
- return False
+ # build operations on demand, e.g. if they take a variable number
+ # of arguments or depend on a constant value (e.g. the class being
+ # instantiated).
+ try:
+ builder = getattr(self, 'build_' + opname)
+ except AttributeError:
+ return False
+
+ progress = False
+ for newsig, newop in builder(hltypes):
+ llops = self.lloperations.setdefault(opname, {})
+ if newsig not in llops:
+ llops[newsig] = newop
+ progress = True
+ return progress
# ____________________________________________________________
- def constant_representation(self, value):
- key = type(value), value # to avoid mixing for example 0 and 0.0
- try:
- return self.r_constants[key]
- except KeyError:
- if isinstance(value, tuple):
- # tuples have their own representation and
- # don't need a fully constant representation
- items_r = [self.constant_representation(x) for x in value]
- return self.tuple_representation(items_r)
- # a constant doesn't need any C variable to be encoded
- r = self.r_constants[key] = CRepr([], name='<const %r>' % (value,))
- r.const = value
-
- # but to convert it to something more general like an int or
- # a PyObject* we need to revive its value, which is done by
- # new conversion operations that we define now
- if isinstance(value, int):
- # can convert the constant to a PyObject*
- if value >= 0:
- name = 'g_IntObj_%d' % value
- else:
- name = 'g_IntObj_minus%d' % abs(value)
- self.can_convert_to_pyobj(r, 'PyInt_FromLong(%d)' % value,
- name)
- # can convert the constant to a C int
- self.register_conv(r, self.R_INT, genc_op.LoKnownAnswer.With(
- known_answer = [LLConst(self.R_INT, '%d' % value)],
- ))
- elif isinstance(value, str):
- # can convert the constant to a PyObject*
- self.can_convert_to_pyobj(r,
- 'PyString_FromStringAndSize(%s, %d)' % (c_str(value),
- len(value)),
- 'g_StrObj_%s' % manglestr(value))
- elif value is None:
- # can convert the constant to Py_None
- self.can_convert_to_pyobj(r, None, 'Py_None')
- elif callable(value) and value in self.genc.llfunctions:
- # another Python function: can be called with OP_SIMPLE_CALL
- llfunc = self.genc.llfunctions[value]
- ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
+ def build_OP_NEWLIST(self, hltypes):
+ # LoNewList can build a list of any length from PyObject* args.
+ sig = (R_OBJECT,) * len(hltypes)
+ yield sig, genc_op.LoNewList
+
+ def build_OP_NEWTUPLE(self, hltypes):
+ # We can use LoCopy to virtually build a tuple because
+ # the tuple representation 'rt' is just the collection of all the
+ # representations for the input args.
+ rt = tuple_representation(hltypes[:-1])
+ sig = tuple(hltypes[:-1]) + (rt,)
+ yield sig, genc_op.LoCopy
+
+ def build_OP_SIMPLE_CALL(self, hltypes):
+ if not hltypes:
+ return
+ # We can call the function using PyObject_CallFunction(), if
+ # it can be converted to PyObject*.
+ sig = (R_OBJECT,) * len(hltypes)
+ yield sig, genc_op.LoCallFunction
+ # But not all variables holding pointers to function can nicely
+ # be converted to PyObject*. We might be calling a well-known
+ # (constant) function:
+ r = hltypes[0]
+ if isinstance(r, CConstant):
+ fn = r.value
+ if not callable(fn):
+ return
+ # Calling another user-defined generated function
+ if fn in self.genc.llfunctions:
+ llfunc = self.genc.llfunctions[fn]
sig = [r]
for v in llfunc.graph.getargs():
sig.append(self.gethltype(v))
hltype = self.gethltype(llfunc.graph.getreturnvar())
sig.append(hltype)
- ops[tuple(sig)] = genc_op.LoCallPyFunction.With(
+ yield tuple(sig), genc_op.LoCallPyFunction.With(
llfunc = llfunc,
hlrettype = hltype,
)
- elif (isinstance(value, types.BuiltinFunctionType) and
- value is getattr(__builtin__, value.__name__, None)):
- # a function from __builtin__: can convert to PyObject*
- self.can_convert_to_pyobj(r,
- 'PyMapping_GetItemString(PyEval_GetBuiltins(), %s)' % (
- c_str(value.__name__)),
- 'g_Builtin_%s' % manglestr(value.__name__))
- # if the function is defined in genc.h, import its definition
- # by copying the operation CALL_xxx to OP_SIMPLE_CALL with
- # a first argument which is the constant function xxx.
- opname = 'CALL_' + value.__name__
- if opname in self.lloperations:
- ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
- for sig, llopcls in self.lloperations[opname].items():
- sig = (r,) + sig
- ops[sig] = llopcls
- elif (isinstance(value, (type, types.ClassType)) and
- value in self.genc.llclasses):
- # a user-defined class
- ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
+ # Instantiating a user-defined class
+ if fn in self.genc.llclasses:
# XXX do __init__
- sig = (r, self.R_OBJECT)
- ops[sig] = genc_op.LoInstantiate.With(
- llclass = self.genc.llclasses[value],
+ sig = (r, R_OBJECT) # R_OBJECT is the result of the op
+ yield sig, genc_op.LoInstantiate.With(
+ llclass = self.genc.llclasses[fn],
)
- # OP_ALLOC_INSTANCE used by the constructor function xxx_new()
- ops = self.lloperations.setdefault('OP_ALLOC_INSTANCE', {})
- sig = (r, self.R_OBJECT)
- ops[sig] = genc_op.LoAllocInstance.With(
- llclass = self.genc.llclasses[value],
+ # Calling a built-in defined in genc.h, if we have a macro
+ # CALL_funcname()
+ opname = 'CALL_' + getattr(fn, '__name__', '?')
+ if opname in self.lloperations:
+ for sig, llopcls in self.lloperations[opname].items():
+ sig = (r,) + sig
+ yield sig, llopcls
+
+ def build_OP_ALLOC_INSTANCE(self, hltypes):
+ # OP_ALLOC_INSTANCE is used by the constructor functions xxx_new()
+ if not hltypes:
+ return
+ r = hltypes[0]
+ if isinstance(r, CConstant):
+ fn = r.value
+ if not callable(fn):
+ return
+ if fn in self.genc.llclasses:
+ sig = (r, R_OBJECT) # R_OBJECT is the result
+ yield sig, genc_op.LoAllocInstance.With(
+ llclass = self.genc.llclasses[fn],
)
- else:
- print "// XXX not implemented: constant", key
- return r
-
- def can_convert_to_pyobj(self, r, initexpr, globalname):
- self.register_conv(r, self.R_OBJECT, genc_op.LoKnownAnswer.With(
- known_answer = [LLConst('PyObject*', globalname, initexpr,
- to_declare = bool(initexpr))],
- ))
-
- def tuple_representation(self, items_r):
- # a tuple is implemented by several C variables or fields
- # instead of a single struct at the C level.
- items_r = tuple(items_r)
- try:
- return self.r_tuples[items_r]
- except KeyError:
- impl = []
- for r in items_r:
- impl += r.impl
- name = '<(%s)>' % ', '.join([str(r) for r in items_r])
- rt = CRepr(impl, name=name)
- self.r_tuples[items_r] = rt
-
- # we can convert any item in the tuple to obtain another tuple
- # representation.
- conv = self.lloperations['convert']
- for i in range(len(items_r)):
- r = items_r[i]
- for r_from, r_to in conv.keys():
- if r_from == r:
- target_r = list(items_r)
- target_r[i] = r_to
- rt_2 = self.tuple_representation(target_r)
- self.register_conv(rt, rt_2,
- genc_op.LoConvertTupleItem.With(
- source_r = items_r,
- target_r = target_r,
- index = i,
- ))
-
- # a tuple containing only PyObject* can easily be converted to
- # a PyTupleObject. (For other kinds of tuple the conversion is
- # indirect: all items can probably be converted, one by one, to
- # PyObject*, and the conversions will be chained automatically.)
- if items_r == (self.R_OBJECT,) * len(items_r):
- self.register_conv(rt, self.R_OBJECT, genc_op.LoNewTuple)
- return rt
+ # ____________________________________________________________
def parse_operation_templates(self):
# parse the genc.h header to figure out which macros are implemented
@@ -261,63 +192,5 @@
ops.setdefault(sig, genc_op.LoStandardOperation.With(
can_fail = can_fail,
llname = llname,
+ cost = 1 + typecodes.count('o'), # rough cost estimate
))
-
- def register_conv(self, r_from, r_to, convopcls):
- conv = self.lloperations['convert']
- if r_from == r_to:
- return
- prevconvopcls = conv.get((r_from, r_to))
- if prevconvopcls is not None:
- if convert_length(prevconvopcls) > convert_length(convopcls):
- # only replace a conversion with another if the previous one
- # was a longer chain of conversions
- del conv[r_from, r_to]
- else:
- return
- #print 'conv: %s\t->\t%s' % (r_from, r_to)
- convitems = conv.items() # not iteritems()!
- conv[r_from, r_to] = convopcls
- # chain the conversion with any other possible conversion
- for (r_from_2, r_to_2), convopcls_2 in convitems:
- if r_to == r_from_2:
- self.register_conv(r_from, r_to_2, genc_op.LoConvertChain.With(
- r_from = r_from,
- r_middle = r_to,
- r_to = r_to_2,
- convert_length = convert_length(convopcls) +
- convert_length(convopcls_2),
- ))
- if r_to_2 == r_from:
- self.register_conv(r_from_2, r_to, genc_op.LoConvertChain.With(
- r_from = r_from_2,
- r_middle = r_to_2,
- r_to = r_to,
- convert_length = convert_length(convopcls_2) +
- convert_length(convopcls),
- ))
-
-def convert_length(convopcls):
- if issubclass(convopcls, genc_op.LoConvertChain):
- return convopcls.convert_length
- else:
- return 1
-
-def c_str(s):
- "Return the C expression for the string 's'."
- s = repr(s)
- if s.startswith("'"):
- s = '"' + s[1:-1].replace('"', r'\"') + '"'
- return s
-
-def manglestr(s):
- "Return an identifier name unique for the string 's'."
- l = []
- for c in s:
- if not ('a' <= c <= 'z' or 'A' <= c <= 'Z' or '0' <= c <= '9'):
- if c == '_':
- c = '__'
- else:
- c = '_%02x' % ord(c)
- l.append(c)
- return ''.join(l)
Modified: pypy/trunk/src/pypy/translator/typer.py
==============================================================================
--- pypy/trunk/src/pypy/translator/typer.py (original)
+++ pypy/trunk/src/pypy/translator/typer.py Thu Sep 9 21:16:06 2004
@@ -56,13 +56,12 @@
# ...}
# This dict contains the known signatures of each space operation.
# Special opnames:
-# 'convert' v w : convert some v to w
-# 'caseXXX' v : fails (i.e. jump to errlabel) if v is not XXX
+# 'caseXXX' v : fails (i.e. jump to errlabel) if v is not XXX
#
# rawoperations = {
# 'opname': subclass-of-LLOp,
# ...}
-# Low-level-only operations on raw LLVars (as opposed to llopeerations,
+# Low-level-only operations on raw LLVars (as opposed to lloperations,
# which are on flow.model.Variables as found in SpaceOperations):
# 'goto' : fails unconditionally (i.e. jump to errlabel)
# 'move' x y : raw copy of the LLVar x to the LLVar y
@@ -73,6 +72,11 @@
# 'comment' : comment (text is in errtarget)
# 'return' x y...: return the value stored in the LLVars
#
+# def getconvertion(self, hltype1, hltype2):
+# If it is possible to convert from 'hltype1' to 'hltype2', this
+# function should return the conversion operation (as a subclass of
+# LLOp). Otherwise, it should raise CannotConvert.
+#
# def typingerror(self, opname, hltypes):
# Called when no match is found in lloperations. This function must
# either extend lloperations and return True to retry, or return
@@ -88,6 +92,7 @@
self.represent = typeset.represent
self.lloperations = typeset.lloperations
self.rawoperations= typeset.rawoperations
+ self.getconversion= typeset.getconversion
self.typingerror = typeset.typingerror
self.hltypes = {}
self.llreprs = {}
@@ -210,48 +215,54 @@
# make a new node for the release tree
self.to_release = ReleaseNode(v, llop, self.to_release)
- def convert_from(self, hltype):
- # enumerate all types that hltype can be converted to
- for frm, to in self.lloperations['convert']:
- if frm == hltype:
- yield to
-
- def convert_to(self, hltype):
- # enumerate all types that can be converted to hltype
- for frm, to in self.lloperations['convert']:
- if to == hltype:
- yield frm
-
def operation(self, opname, args, result=None, errlabel=None):
"Helper to build the LLOps for a single high-level operation."
# get the hltypes of the input arguments
for v in args:
self.makevar(v)
args_t = [self.hltypes[v] for v in args]
- directions = [self.convert_from] * len(args)
+ directions = [False] * len(args)
# append the hltype of the result
if result:
self.makevar(result)
args_t.append(self.hltypes[result])
- directions.append(self.convert_to)
- # enumerate possible signatures until we get a match
+ directions.append(True)
+ # look for an exact match first
llsigs = self.lloperations.get(opname, {})
- for sig in variants(tuple(args_t), directions):
- if sig in llsigs:
- llopcls = llsigs[sig]
- break
+ sig = tuple(args_t)
+ if sig in llsigs:
+ llopcls = llsigs[sig]
else:
- retry = self.typingerror(opname, tuple(args_t))
- # if 'typingerror' did not raise an exception, try again.
- # infinite recursion here means that 'typingerror' did not
- # correctly extend 'lloperations'.
- if retry:
+ # enumerate the existing operation signatures and their costs
+ choices = []
+ for sig, llopcls in llsigs.items():
+ if len(sig) != len(args_t):
+ continue # wrong number of arguments
try:
- self.operation(opname, args, result, errlabel)
- return
- except RuntimeError: # infinite recursion
- pass
- raise TypingError([opname] + args_t)
+ cost = llopcls.cost
+ for hltype1, hltype2, reverse in zip(args_t, sig,
+ directions):
+ if hltype1 != hltype2:
+ if reverse:
+ hltype1, hltype2 = hltype2, hltype1
+ convop = self.getconversion(hltype1, hltype2)
+ cost += convop.cost
+ choices.append((cost, sig, llopcls))
+ except CannotConvert:
+ continue # non-matching signature
+ if not choices:
+ retry = self.typingerror(opname, tuple(args_t))
+ # if 'typingerror' did not raise an exception, try again.
+ # infinite recursion here means that 'typingerror' did not
+ # correctly extend 'lloperations'.
+ if retry:
+ try:
+ self.operation(opname, args, result, errlabel)
+ return
+ except RuntimeError: # infinite recursion
+ pass
+ raise TypingError([opname] + args_t)
+ cost, sig, llopcls = min(choices)
# convert input args to temporary variables
llargs = []
for v, v_t, s_t in zip(args, args_t, sig):
@@ -259,59 +270,66 @@
llargs += self.convert(v_t, self.llreprs[v], s_t)
else:
llargs += self.llreprs[v]
- # generate an error label if the operation can fail
- if llopcls.can_fail and errlabel is None:
- errlabel = self.to_release.getlabel()
# case-by-case analysis of the result variable
if result:
if args_t[-1] == sig[-1]:
# the result has the correct type
- tmp = result
+ if self.writeoperation(llopcls, llargs,
+ self.llreprs[result], errlabel):
+ self.mark_release(result)
else:
# the result has to be converted
tmp = Variable()
self.makevar(tmp, hltype=sig[-1])
- llargs += self.llreprs[tmp]
- llop = llopcls(llargs, errlabel)
- constantllreprs = llop.optimize(self)
- if constantllreprs is not None:
- # the result is a constant: patch the llrepr of result,
- # i.e. replace its LLVars with the given constants which
- # will be used by the following operations.
- assert len(constantllreprs) == len(self.llreprs[tmp])
- diffs = []
- interesting = False
- for x, y in zip(constantllreprs, self.llreprs[tmp]):
- if x != y:
- diffs.append('%s = %s;' % (y.name, x.name))
- interesting = interesting or not isinstance(x, LLConst)
- self.llreprs[tmp] = list(constantllreprs)
- if interesting:
- llcomment = self.rawoperations['comment']
- self.blockops.append(llcomment([], '%s: %s' % (
- opname, ' '.join(diffs))))
- else:
- # common case: emit the LLOp.
- self.mark_release(tmp)
- self.blockops.append(llop)
- if tmp is not result:
- self.operation('convert', [tmp], result)
+ if self.writeoperation(llopcls, llargs,
+ self.llreprs[tmp], errlabel):
+ self.mark_release(tmp)
+ self.convert_variable(tmp, result)
else:
# no result variable
- self.blockops.append(llopcls(llargs, errlabel))
+ self.writeoperation(llopcls, llargs, [], errlabel)
+
+ def writeoperation(self, llopcls, llargs, llresult, errlabel=None):
+ # generate an error label if the operation can fail
+ if llopcls.can_fail and errlabel is None:
+ errlabel = self.to_release.getlabel()
+ # create the LLOp instance
+ llop = llopcls(llargs + llresult, errlabel)
+ constantllreprs = llop.optimize(self)
+ if constantllreprs is not None:
+ # the result is a constant: patch llresult, i.e. replace its
+ # LLVars with the given constants which will be used by the
+ # following operations.
+ assert len(constantllreprs) == len(llresult)
+ llresult[:] = constantllreprs
+ return False # operation skipped
+ else:
+ # common case: emit the LLOp.
+ self.blockops.append(llop)
+ return True
def convert(self, inputtype, inputrepr, outputtype, outputrepr=None):
- tmpin = Variable()
- self.makevar(tmpin, hltype=inputtype)
- tmpout = Variable()
- self.makevar(tmpout, hltype=outputtype)
- self.llreprs[tmpin] = inputrepr
+ convop = self.getconversion(inputtype, outputtype)
if outputrepr is None:
- outputrepr = self.llreprs[tmpout]
+ tmp = Variable()
+ self.makevar(tmp, hltype=outputtype)
+ outputrepr = self.llreprs[tmp]
+ if self.writeoperation(convop, inputrepr, outputrepr):
+ self.mark_release(tmp)
else:
- self.llreprs[tmpout] = outputrepr
- self.operation('convert', [tmpin], tmpout)
- return self.llreprs[tmpout]
+ if self.writeoperation(convop, inputrepr, outputrepr):
+ tmp = Variable()
+ self.hltypes[tmp] = outputtype
+ self.llreprs[tmp] = outputrepr
+ self.mark_release(tmp)
+ return outputrepr
+
+ def convert_variable(self, v1, v2):
+ self.makevar(v1)
+ self.makevar(v2)
+ convop = self.getconversion(self.hltypes[v1], self.hltypes[v2])
+ if self.writeoperation(convop, self.llreprs[v1], self.llreprs[v2]):
+ self.mark_release(v2)
def goto(self, exit):
# generate the exit.args -> target.inputargs copying operations
@@ -324,7 +342,7 @@
if self.hltypes[v] != self.hltypes[w]:
tmp = Variable()
self.makevar(tmp, hltype=self.hltypes[w])
- self.operation('convert', [v], tmp)
+ self.convert_variable(v, tmp)
v = tmp
exitargs.append(v)
# move the data from exit.args to target.inputargs
@@ -423,16 +441,5 @@
yield self.release_operation
-def variants(args_t, directions):
- # enumerate all variants of the given signature of hltypes
- # XXX this can become quadratically slow for large programs because
- # XXX it enumerates all conversions of the result from a constant value
- if len(args_t):
- for sig in variants(args_t[:-1], directions[:-1]):
- yield sig + args_t[-1:]
- choices_for_last_arg = list(directions[-1](args_t[-1]))
- for sig in variants(args_t[:-1], directions[:-1]):
- for last_arg in choices_for_last_arg:
- yield sig + (last_arg,)
- else:
- yield ()
+class CannotConvert:
+ pass
More information about the Pypy-commit
mailing list