[pypy-svn] r6218 - pypy/branch/pypy-genc/translator
arigo at codespeak.net
arigo at codespeak.net
Mon Aug 30 12:55:25 CEST 2004
Author: arigo
Date: Mon Aug 30 12:55:25 2004
New Revision: 6218
Added:
pypy/branch/pypy-genc/translator/typer.py
Modified:
pypy/branch/pypy-genc/translator/genc.h
pypy/branch/pypy-genc/translator/genc.py
pypy/branch/pypy-genc/translator/simplify.py
Log:
Generate typed C code.
Modified: pypy/branch/pypy-genc/translator/genc.h
==============================================================================
--- pypy/branch/pypy-genc/translator/genc.h (original)
+++ pypy/branch/pypy-genc/translator/genc.h Mon Aug 30 12:55:25 2004
@@ -4,27 +4,64 @@
#include <Python.h>
-#define INCREF(x) Py_INCREF(x);
-#define DECREF(x) Py_DECREF(x);
-#define CASE(x,y) if (PyObject_RichCompareBool(x,y,Py_EQ))
-#define ELSE(x,y) assert(PyObject_RichCompareBool(x,y,Py_EQ));
+/*** operations on ints ***/
-#define OP_LT(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_LT))) goto err;
-#define OP_LE(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_LT))) goto err;
-#define OP_EQ(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_EQ))) goto err;
-#define OP_NE(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_NE))) goto err;
-#define OP_GT(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_GT))) goto err;
-#define OP_GE(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_GE))) goto err;
-
-#define OP_IS_TRUE(x,r,err) switch (PyObject_IsTrue(x)) { \
- case 0: r=Py_False; Py_INCREF(r); break; \
- case -1: goto err; \
- default: r=Py_True; Py_INCREF(r); }
-
-#define OP_ADD(x,y,r,err) if (!(r=PyNumber_Add(x,y))) goto err;
-#define OP_MOD(x,y,r,err) if (!(r=PyNumber_Remainder(x,y))) goto err;
-#define OP_INPLACE_ADD(x,y,r,err) if (!(r=PyNumber_InPlaceAdd(x,y))) goto err;
+#define OP_LT_iii(x,y,r) r = x < y;
+#define OP_LE_iii(x,y,r) r = x <= y;
+#define OP_EQ_iii(x,y,r) r = x == y;
+#define OP_NE_iii(x,y,r) r = x != y;
+#define OP_GT_iii(x,y,r) r = x > y;
+#define OP_GE_iii(x,y,r) r = x >= y;
+
+#define OP_IS_TRUE_ii(x,r) r = !(!x);
+
+#define OP_ADD_iii(x,y,r) r = x + y;
+#define OP_MOD_iii(x,y,r) r = x % y;
+#define OP_INPLACE_ADD_iii(x,y,r) r = x + y;
+
+
+/*** generic operations on PyObjects ***/
+
+#define op_richcmp_ooi(x,y,r,err,dir) \
+ if ((r=PyObject_RichCompareBool(x,y,dir))<0) goto err;
+#define OP_LT_ooi(x,y,r,err) op_richcmp_ooi(x,y,r,err, Py_LT)
+#define OP_LE_ooi(x,y,r,err) op_richcmp_ooi(x,y,r,err, Py_LE)
+#define OP_EQ_ooi(x,y,r,err) op_richcmp_ooi(x,y,r,err, Py_EQ)
+#define OP_NE_ooi(x,y,r,err) op_richcmp_ooi(x,y,r,err, Py_NE)
+#define OP_GT_ooi(x,y,r,err) op_richcmp_ooi(x,y,r,err, Py_GT)
+#define OP_GE_ooi(x,y,r,err) op_richcmp_ooi(x,y,r,err, Py_GE)
+
+#define OP_IS_TRUE_oi(x,r,err) if ((r=PyObject_IsTrue(x))<0) goto err;
+#define OP_ADD_ooo(x,y,r,err) if (!(r=PyNumber_Add(x,y))) goto err;
+#define OP_MOD_ooo(x,y,r,err) if (!(r=PyNumber_Remainder(x,y))) goto err;
+#define OP_INPLACE_ADD_ooo(x,y,r,err) if(!(r=PyNumber_InPlaceAdd(x,y)))goto err;
+
+
+/*** conversions ***/
+
+#define convert_io(x,r,err) if (!(r=PyInt_FromLong(x))) goto err;
+
+
+/*** tests ***/
+
+#define caseFalse_i(n, err) if (n) goto err;
+#define caseTrue_i(n, err) if (!n) goto err;
+#define case0_i(n, err) if (n != 0) goto err;
+#define case1_i(n, err) if (n != 1) goto err;
+
+#define caseFalse_o(o, err) if (!(PyInt_Check(o) && !PyInt_AS_LONG(o))) goto err;
+#define caseTrue_o(o, err) if (!(PyInt_Check(o) && PyInt_AS_LONG(o))) goto err;
+#define case0_o(o, err) if (!(PyInt_Check(o) && PyInt_AS_LONG(o)==0)) goto err;
+#define case1_o(o, err) if (!(PyInt_Check(o) && PyInt_AS_LONG(o)==1)) goto err;
+
+
+/*** misc ***/
+
+#define return_i(n) return n;
+#define return_o(o) return o;
+#define incref_o(o) Py_INCREF(o);
+#define decref_o(o) Py_DECREF(o);
/************************************************************/
Modified: pypy/branch/pypy-genc/translator/genc.py
==============================================================================
--- pypy/branch/pypy-genc/translator/genc.py (original)
+++ pypy/branch/pypy-genc/translator/genc.py Mon Aug 30 12:55:25 2004
@@ -2,10 +2,112 @@
Generate a C source file from the flowmodel.
"""
-import autopath, os
+import autopath, os, re
from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant
from pypy.objspace.flow.model import Block, Link, traverse
-import inspect
+from pypy.translator.typer import LLFunction, LLOp
+from pypy.annotation import model as annmodel
+
+
+class CRepr:
+ "A possible representation of a flow-graph variable as C-level variables."
+
+ def __init__(self, impl, error_retval, parse_code=None):
+ self.impl = impl # list [(C type, prefix for variable name)]
+ self.error_retval = error_retval # return value signalling an error
+ self.parse_code = parse_code # character(s) for PyArg_ParseTuple()
+
+ def __repr__(self):
+ if hasattr(self, 'const'):
+ return '<C const %s>' % (self.const,)
+ else:
+ return '<C: %s>' % ' '.join(self.impl)
+
+
+class CTypeSet:
+ "A (small) set of C types that typer.LLFunction can manipulate."
+
+ R_VOID = CRepr([], error_retval='-1')
+ R_INT = CRepr(['int'], error_retval='-1', parse_code='i')
+ R_OBJECT = CRepr(['PyObject*'], error_retval='NULL', parse_code='O')
+
+ REPR_BY_CODE = {
+ 'i': R_INT,
+ 'o': R_OBJECT,
+ }
+
+ def __init__(self, bindings):
+ self.bindings = bindings
+ self.r_constants = {}
+ self.lloperations = {'convert': {}, 'release': {}}
+ self.parse_operation_templates()
+
+ # __________ methods required by LLFunction __________
+ #
+ # Here, we assume that every high-level type has a canonical representation
+ # so a high-level type is just a CRepr.
+
+ def gethltype(self, var):
+ if isinstance(var, Variable):
+ s_value = self.bindings.get(var) or annmodel.SomeObject()
+ if s_value.is_constant():
+ return self.constant_representation(s_value.const)
+ if issubclass(s_value.knowntype, int):
+ return self.R_INT
+ # fall-back
+ return self.R_OBJECT
+ if isinstance(var, Constant):
+ return self.constant_representation(var.value)
+ raise TypeError, var
+
+ def represent(self, hltype):
+ return hltype.impl
+
+ # ____________________________________________________________
+
+ def constant_representation(self, value):
+ key = type(value), value # to avoid mixing for example 0 and 0.0
+ try:
+ return self.r_constants[key]
+ except KeyError:
+ # a constant doesn't need any C variable to be encoded
+ r = self.r_constants[key] = CRepr([], '-1')
+ # but to convert it to something more general like an int or
+ # a PyObject* we need to revive its value, which is done by
+ # new conversion operations that we define now
+ conv = self.lloperations['convert']
+ if isinstance(value, int):
+ # can convert the constant to a C int
+ r.const = str(int(value))
+ llname = '%s = ' + r.const + ';'
+ conv[r, self.R_INT] = llname, False
+ # can convert the constant to a PyObject*
+ llname = 'convert_io(' + r.const + ', %s, %s)'
+ conv[r, self.R_OBJECT] = llname, True
+ else:
+ pass # XXX not implemented
+ return r
+
+ def parse_operation_templates(self):
+ # parse the genc.h header to figure out which macros are implemented
+ codes = ''.join(self.REPR_BY_CODE.keys())
+ pattern = r"#define ([A-Za-z_][0-9A-Za-z_]*)_([%s]*)[(](.*?)[)]" % codes
+ rexp = re.compile(pattern)
+ for line in C_HEADER.split('\n'):
+ match = rexp.match(line)
+ if match:
+ opname, typecodes, formalargs = match.groups()
+ llname = '%s_%s' % (opname, typecodes)
+ sig = tuple([self.REPR_BY_CODE[code] for code in typecodes])
+ can_fail = formalargs.replace(' ','').endswith(',err')
+ ops = self.lloperations.setdefault(opname, {})
+ assert sig not in ops, llname
+ l = ['%s'] * (len(typecodes) + can_fail)
+ llname = llname + '(' + ', '.join(l) + ')'
+ ops.setdefault(sig, (llname, can_fail))
+
+
+# ____________________________________________________________
class GenC:
@@ -14,9 +116,19 @@
self.f = f
self.translator = translator
self.modname = modname or translator.functions[0].__name__
- self.annotator = translator.annotator
- self.funcname = self.choose_func_names()
- self.constname = self.choose_const_names()
+ if translator.annotator:
+ bindings = translator.annotator.bindings.copy()
+ # force the entry point's return type to be 'PyObject*'
+ try:
+ entrypoint = translator.functions[0]
+ del bindings[translator.flowgraphs[entrypoint].getreturnvar()]
+ except KeyError:
+ pass
+ else:
+ bindings = {}
+ self.typeset = CTypeSet(bindings)
+ self.llfunctions = {}
+ self.build_llfunctions()
self.gen_source()
def gen_source(self):
@@ -26,7 +138,7 @@
'exported': self.translator.functions[0].__name__,
}
# header
- print >> f, C_HEADER % info
+ print >> f, C_HEADER
# forward declarations
print >> f, '/* forward declarations */'
@@ -34,14 +146,6 @@
print >> f, 'static %s;' % self.cfunction_header(func)
print >> f
- # constants
- print >> f, '/* constants */'
- consts = self.constname.values()
- consts.sort()
- for name in consts:
- print >> f, 'static PyObject* %s;' % name
- print >> f
-
# function implementation
print >> f, C_SEP
print >> f
@@ -55,178 +159,98 @@
self.gen_entrypoint(self.translator.functions[0])
print >> f, C_ENTRYPOINT_FOOTER % info
- # initialization of constants
- print >> f, 'static void build_constants(void)'
- print >> f, '{'
- self.gen_constants()
- print >> f, '}'
-
# footer
print >> f, C_METHOD_TABLE % info
print >> f, C_FOOTER % info
- def choose_func_names(self):
- result = {}
+ def build_llfunctions(self):
n = 0
for func in self.translator.functions:
- assert func not in result, '%r duplicate' % (func,)
- result[func] = '%s__%d' % (func.func_name, n)
+ assert func not in self.llfunctions, '%r duplicate' % (func,)
+ self.llfunctions[func] = LLFunction(
+ typeset = self.typeset,
+ name = '%s__%d' % (func.func_name, n),
+ graph = self.translator.flowgraphs[func])
n += 1
- return result
-
- def choose_const_names(self):
- result = {}
- def see(value):
- if value not in result:
- if isinstance(value, int):
- name = ('kint_%d' % value).replace('-','minus')
- else:
- name = 'k%04d' % len(result)
- result[value] = name
- def visit(node):
- if isinstance(node, Block):
- for c in node.getconstants():
- see(c.value)
- elif isinstance(node, Link):
- see(node.exitcase)
- for graph in self.translator.flowgraphs.itervalues():
- traverse(visit, graph)
- return result
-
- def var_or_const_name(self, v):
- if isinstance(v, Variable):
- return v.name
- else:
- return self.constname[v.value]
def cfunction_header(self, func):
- graph = self.translator.flowgraphs[func]
- args = graph.getargs()
- _eval = self.var_or_const_name
- returntypename = 'PyObject*'
- l = ['PyObject* %s' % a for a in args]
- return '%s %s(%s)' % (returntypename,
- self.funcname[func],
+ llfunc = self.llfunctions[func]
+ llargs, rettype = llfunc.ll_header()
+ l = ['%s %s' % (a.type, a.name) for a in llargs]
+ return '%s %s(%s)' % (rettype,
+ llfunc.name,
', '.join(l))
def gen_entrypoint(self, func):
f = self.f
- graph = self.translator.flowgraphs[func]
- args = graph.getargs()
- l = ['"' + 'O'*len(args) + '"']
+ llfunc = self.llfunctions[func]
+ llargs, rettype = llfunc.ll_header()
+ assert rettype == 'PyObject*', rettype
+ l = []
l2 = []
- for i in range(len(args)):
- print >> f, '\tPyObject* a%d;' % i
- l.append('&a%d' % i)
- l2.append('a%d' % i)
+ for a in llargs:
+ print >> f, '\t%s %s;' % (a.type, a.name)
+ l.append('&' + a.name)
+ l2.append(a.name)
+ formatstr = []
+ for v in llfunc.graph.getargs():
+ hltype = self.typeset.gethltype(v)
+ assert hltype.parse_code, (
+ "entry point arg %s has unsupported type %s" % (v, hltype))
+ formatstr.append(hltype.parse_code)
+ l.insert(0, '"' + ''.join(formatstr) + '"')
print >> f, '\tif (!PyArg_ParseTuple(args, %s))' % ', '.join(l)
print >> f, '\t\treturn NULL;'
- print >> f, '\treturn %s(%s);' % (self.funcname[func],
- ', '.join(l2))
-
- def gen_constants(self):
- f = self.f
- consts = [(name, value) for value, name in self.constname.items()]
- consts.sort()
- for name, value in consts:
- if isinstance(value, int):
- print >> f, '\t%s = PyInt_FromLong(%d);' % (name, value)
- elif value is None:
- print >> f, '\t%s = Py_None;' % name
- else:
- raise ValueError, value
+ print >> f, '\treturn %s(%s);' % (llfunc.name, ', '.join(l2))
def gen_cfunction(self, func):
f = self.f
- graph = self.translator.flowgraphs[func]
- _eval = self.var_or_const_name
- allblocks = []
- blockname = {}
- def visit(n):
- if isinstance(n, Block):
- allblocks.append(n)
- blockname[n] = 'block%d' % len(blockname)
- traverse(visit, graph)
-
- # header
+ llfunc = self.llfunctions[func]
+
+ # print header
print >> f, self.cfunction_header(func)
print >> f, '{'
- # local variables
- input_args = {}
- for var in graph.getargs():
- input_args[var] = True
- for block in allblocks:
- for var in block.getvariables():
- if var not in input_args:
- print >> f, '\tPyObject* %s;' % var
- print >> f
-
- # body
- for block in allblocks:
- print >> f, ' %s:' % blockname[block]
- to_release = block.inputargs[:]
- errlabels = {}
-
- # basic block operations
- for op in block.operations:
- l = [_eval(a) for a in op.args]
- n = len(to_release)
- errlabel = '%s_err%d' % (blockname[block], n)
- errlabels[n] = errlabel
- to_release.append(op.result)
- print >> f, '\tOP_%s(%s, %s, %s)' % (op.opname.upper(),
- ', '.join(l),
- op.result,
- errlabel)
-
- # exits
- if block.exits:
- macros = ['CASE'] * (len(block.exits)-1) + ['ELSE']
- for macro, exit in zip(macros, block.exits):
- if len(block.exits) == 1:
- indent = '\t'
- close = ''
- else:
- print >> f, '\t%s(%s, %s) {' % (
- macro, _eval(block.exitswitch),
- self.constname[exit.exitcase])
- indent = '\t\t'
- close = '\t}'
- sourceargs = exit.args
- targetargs = exit.target.inputargs
- assert len(sourceargs) == len(targetargs)
- for n in range(len(to_release)-1, -1, -1):
- var = to_release[n]
- if var not in sourceargs:
- print >> f, '%sDECREF(%s)' % (indent, var)
- for s,t in zip(sourceargs, targetargs):
- sname = _eval(s)
- if isinstance(s, Constant):
- print >> f, '%sINCREF(%s)' % (indent, sname)
- print >> f, '%s%s = %s;' % (indent, t, sname)
- print >> f, '%sgoto %s;' % (indent,
- blockname[exit.target])
- if close:
- print >> f, close
- elif hasattr(block, 'exc_type'):
- xxx_raise
- else:
- print >> f, '\treturn %s;' % _eval(block.inputargs[0])
+ # generate the body of the function
+ body = list(llfunc.ll_body())
- # error paths
- reachable = False
- for n in range(len(to_release)-1, -1, -1):
- if reachable:
- print >> f, '\tDECREF(%s)' % to_release[n]
- if n in errlabels:
- print >> f, ' %s:' % errlabels[n]
- reachable = True
- if reachable:
- print >> f, '\treturn NULL;'
- print >> f
+ # collect and print all the local variables from the body
+ llargs, rettype = llfunc.ll_header()
+ locals = llargs[:]
+ for line in body:
+ if isinstance(line, LLOp):
+ locals += line.args
+ seen = {}
+ for a in llargs:
+ seen[a] = True
+ for a in locals:
+ if a not in seen:
+ print >> f, '\t%s %s;' % (a.type, a.name)
+ seen[a] = True
+ print >> f
+ # print the body
+ for line in body:
+ if isinstance(line, LLOp):
+ args = [a.name for a in line.args]
+ if line.errtarget:
+ args.append(line.errtarget)
+ s = line.name
+ if s == 'move':
+ print >> f, '\t%s = %s;' % (args[1], args[0])
+ elif s == 'goto':
+ print >> f, '\tgoto %s;' % tuple(args)
+ elif s == 'returnerror':
+ v = llfunc.graph.getreturnvar()
+ hltype = self.typeset.gethltype(v)
+ print >> f, '\treturn %s;' % hltype.error_retval
+ else: # common case of operations
+ print >> f, '\t' + s % tuple(args)
+ elif line: # label
+ print >> f, ' %s:' % line
+ else: # empty line
+ print >> f
print >> f, '}'
@@ -238,26 +262,20 @@
C_ENTRYPOINT_HEADER = '''
static PyObject* c_%(exported)s(PyObject* self, PyObject* args)
-{
-'''
+{'''
-C_ENTRYPOINT_FOOTER = '''
-}
-'''
+C_ENTRYPOINT_FOOTER = '''}'''
C_METHOD_TABLE = '''
static PyMethodDef %(modname)sMethods[] = {
\t{"%(exported)s", (PyCFunction)c_%(exported)s, METH_VARARGS},
\t{NULL, NULL}
-};
-'''
+};'''
C_FOOTER = '''
void init%(modname)s(void)
{
\tPy_InitModule("%(modname)s", %(modname)sMethods);
-\tbuild_constants();
-}
-'''
+}'''
# ____________________________________________________________
Modified: pypy/branch/pypy-genc/translator/simplify.py
==============================================================================
--- pypy/branch/pypy-genc/translator/simplify.py (original)
+++ pypy/branch/pypy-genc/translator/simplify.py Mon Aug 30 12:55:25 2004
@@ -85,3 +85,19 @@
remove_implicit_exceptions(graph)
join_blocks(graph)
return graph
+
+
+def remove_direct_loops(graph):
+ """This is useful for code generators: it ensures that no link has
+ common input and output variables, which could occur if a block's exit
+ points back directly to the same block. It allows code generators to be
+ simpler because they don't have to worry about overwriting input
+ variables when generating a sequence of assignments."""
+ def visit(link):
+ if isinstance(link, Link) and link.prevblock is link.target:
+ # insert an empty block with fresh variables.
+ intermediate = [Variable() for a in link.args]
+ b = Block(intermediate)
+ b.closeblock(Link(intermediate, link.target))
+ link.target = b
+ traverse(visit, graph)
Added: pypy/branch/pypy-genc/translator/typer.py
==============================================================================
--- (empty file)
+++ pypy/branch/pypy-genc/translator/typer.py Mon Aug 30 12:55:25 2004
@@ -0,0 +1,352 @@
+"""
+Graph-to-low-level transformer for C/Assembler code generators.
+"""
+
+from __future__ import generators
+from pypy.objspace.flow.model import Constant, Variable, Block, Link, traverse
+from pypy.translator.simplify import remove_direct_loops
+
+
+class LLVar:
+ "A variable in the low-level language."
+ def __init__(self, type, name):
+ self.type = type # low-level type in any format, e.g. a C type name
+ self.name = name
+
+class LLOp:
+ "A low-level operation."
+ def __init__(self, name, args, errtarget=None):
+ self.name = name # low-level operation name
+ self.args = args # list of LLVars
+ self.errtarget = errtarget # label to jump to in case of error
+
+
+# ____________________________________________________________
+#
+# below, a 'typeset' is an object with the following methods/attributes:
+#
+# def gethltype(self, var_or_const)
+# Return the high-level type of a var or const.
+#
+# def represent(self, hltype)
+# Return a list of LLTypes that together implement the high-level type.
+#
+# lloperations = {
+# 'opname': {
+# (tuple-of-hltypes): (low-level-name, flag-can-fail),
+# ... },
+# ...}
+# This dict contains the known signatures of each space operation.
+# Special opnames:
+# 'convert' x y : convert some x to y
+# 'incref' z : get a new ref to the object z
+# 'decref' z : release (or decref) the object z
+# 'caseXXX' z : fails (i.e. jump to errlabel) if z is not XXX
+# 'return' z : z is the return value of the function
+#
+# Low-level-only operation names:
+# 'goto' : fails unconditionally (i.e. jump to errlabel)
+# 'move' x y : raw copy of the LLVar x to the LLVar y
+# 'returnerror' : return an error code from the function
+# ____________________________________________________________
+
+
+class LLFunction:
+ "A low-level version of a function from a control flow graph."
+
+ def __init__(self, typeset, name, graph):
+ remove_direct_loops(graph)
+ self.name = name
+ self.graph = graph
+ self.gethltype = typeset.gethltype
+ self.represent = typeset.represent
+ self.lloperations = typeset.lloperations
+ self.hltypes = {}
+ self.llreprs = {}
+
+ def ll_header(self):
+ """
+ Get the low-level representation of the header.
+ """
+ llrepr = []
+ for v in self.graph.getargs():
+ self.makevar(v)
+ llrepr += self.llreprs[v]
+ v = self.graph.getreturnvar()
+ self.makevar(v)
+ llret = self.llreprs[v]
+ if len(llret) == 0:
+ retlltype = None
+ elif len(llret) == 1:
+ retlltype = llret[0].type
+ else:
+ XXX("to do")
+ return llrepr, retlltype
+
+ def ll_body(self):
+ """
+ Get the body by flattening and low-level-izing the flow graph.
+ Enumerates low-level operations: LLOps with labels inbetween (strings).
+ """
+ self.blockname = {}
+ self.release_root = ReleaseNode(None, LLOp('returnerror', []), None)
+ allblocks = []
+
+ # collect all blocks
+ def visit(block):
+ if isinstance(block, Block):
+ allblocks.append(block)
+ self.blockname[block] = 'block%d' % len(self.blockname)
+ for v in block.inputargs:
+ self.makevar(v)
+ traverse(visit, self.graph)
+
+ # generate an incref for each input argument
+ for v in self.graph.getargs():
+ sig = (self.hltypes[v],)
+ try:
+ llname, can_fail = self.lloperations['incref'][sig]
+ except KeyError:
+ continue # ignore types with no particular incref operation
+ assert not can_fail
+ yield LLOp(llname, self.llreprs[v])
+
+ # generate the body of each block
+ for block in allblocks:
+ for op in self.generate_block(block):
+ yield op
+ yield '' # empty line
+
+ # generate the code to handle errors
+ for op in self.release_root.error_code():
+ yield op
+
+ def generate_block(self, block):
+ "Generate the operations for one basic block."
+ self.to_release = self.release_root
+ for v in block.inputargs:
+ self.mark_release(v)
+ # entry point
+ self.blockops = [self.blockname[block]] # label
+ # basic block operations
+ for op in block.operations:
+ self.operation('OP_' + op.opname.upper(), list(op.args), op.result)
+ # exits
+ if block.exits:
+ for exit in block.exits[:-1]:
+ # generate | caseXXX v elselabel
+ # | copy output vars to next block's input vars
+ # | jump to next block
+ # | elselabel:
+ elselabel = '%s_not%s' % (self.blockname[block], exit.exitcase)
+ self.operation('case%s' % exit.exitcase,
+ [block.exitswitch],
+ errlabel = elselabel)
+ self.goto(exit)
+ self.blockops.append(elselabel)
+ # for the last exit, generate only the jump to next block
+ exit = block.exits[-1]
+ self.goto(exit)
+
+ elif hasattr(block, 'exc_type'):
+ XXX("to do")
+ else:
+ self.operation('return', block.inputargs)
+ return self.blockops
+
+ # __________ Type checking and conversion routines __________
+
+ def makevar(self, v, hltype=None):
+ "Record v in self.hltypes and self.llreprs."
+ if v in self.llreprs:
+ return
+ if hltype is None:
+ hltype = self.gethltype(v)
+ llrepr = []
+ lltypes = self.represent(hltype)
+ for i, lltype in zip(range(len(lltypes)), lltypes):
+ if i:
+ suffix = '_%d' % i
+ else:
+ suffix = ''
+ llrepr.append(LLVar(lltype, v.name + suffix))
+ self.hltypes[v] = hltype
+ self.llreprs[v] = llrepr
+
+ def mark_release(self, v):
+ sig = (self.hltypes[v],)
+ try:
+ llname, can_fail = self.lloperations['decref'][sig]
+ except KeyError:
+ return # ignore types with no particular decref operation
+ assert not can_fail
+ llop = LLOp(llname, self.llreprs[v])
+ # make a new node for the release tree
+ self.to_release = ReleaseNode(v, llop, self.to_release)
+
+ def convert_from(self, hltype):
+ # enumerate all types that hltype can be converted to
+ for frm, to in self.lloperations['convert']:
+ if frm == hltype:
+ yield to
+
+ def convert_to(self, hltype):
+ # enumerate all types that can be converted to hltype
+ for frm, to in self.lloperations['convert']:
+ if to == hltype:
+ yield frm
+
+ def operation(self, opname, args, result=None, errlabel=None):
+ "Helper to build the LLOps for a single high-level operation."
+ # get the hltypes of the input arguments
+ for v in args:
+ self.makevar(v)
+ args_t = [self.hltypes[v] for v in args]
+ directions = [self.convert_from] * len(args)
+ # append the hltype of the result
+ if result:
+ self.makevar(result)
+ args_t.append(self.hltypes[result])
+ directions.append(self.convert_to)
+ # enumerate possible signatures until we get a match
+ llsigs = self.lloperations[opname]
+ for sig in variants(tuple(args_t), directions):
+ if sig in llsigs:
+ llname, can_fail = llsigs[sig]
+ break
+ else:
+ raise TypingError, (opname, args_t)
+ # convert input args to temporary variables
+ llargs = []
+ for v, v_t, s_t in zip(args, args_t, sig):
+ if v_t != s_t:
+ tmp = Variable()
+ self.makevar(tmp, hltype=s_t)
+ self.operation('convert', [v], tmp)
+ v = tmp
+ llargs += self.llreprs[v]
+ # generate an error label if the operation can fail
+ if can_fail and errlabel is None:
+ errlabel = self.to_release.getlabel()
+ # case-by-case analysis of the result variable
+ if result:
+ if args_t[-1] == sig[-1]:
+ # the result has the correct type
+ llargs += self.llreprs[result]
+ self.blockops.append(LLOp(llname, llargs, errlabel))
+ self.mark_release(result)
+ else:
+ # the result has to be converted
+ tmp = Variable()
+ self.makevar(tmp, hltype=sig[-1])
+ llargs += self.llreprs[tmp]
+ self.blockops.append(LLOp(llname, llargs, errlabel))
+ self.mark_release(tmp)
+ self.operation('convert', [tmp], result)
+ else:
+ # no result variable
+ self.blockops.append(LLOp(llname, llargs, errlabel))
+
+ def goto(self, exit):
+ # generate the exit.args -> target.inputargs copying operations
+ to_release_copy = self.to_release
+ try:
+ # copy the data, performing any necessary conversion
+ # See also remove_direct_loops() for why we don't worry about
+ # the order of the operations
+ dontrelease = {}
+ for v, w in zip(exit.args, exit.target.inputargs):
+ self.makevar(v)
+ if self.hltypes[v] == self.hltypes[w]: # fast case
+ for x, y in zip(self.llreprs[v], self.llreprs[w]):
+ self.blockops.append(LLOp('move', [x, y]))
+ dontrelease[v] = True
+ else:
+ self.operation('convert', [v], w)
+ dontrelease[w] = True
+ # then release all the variables that go out of scope minus the
+ # ones that are carried over to the next block
+ for node in self.to_release.getbranch():
+ if node.var not in dontrelease:
+ self.blockops.append(node.release_operation)
+ # finally jump to the target block
+ self.blockops.append(LLOp('goto', [], self.blockname[exit.target]))
+ finally:
+ self.to_release = to_release_copy
+ # after a call to goto() we are back to generating ops for
+ # other cases, so we restore the previous self.to_release.
+
+# ____________________________________________________________
+
+
+class TypingError(Exception):
+ pass
+
+
+# In a function, all the variables that have to released can be organized
+# in a tree in which each node is a variable: whenever this variable has
+# to be released, its parent in the tree has to be release too, and its
+# parent's parent and so on up to the root.
+class ReleaseNode:
+ accessible = False
+ label = None
+ counter = 0
+
+ def __init__(self, var, release_operation, parent):
+ self.var = var
+ self.release_operation = release_operation
+ self.parent = parent
+ self.accessible_children = []
+
+ def mark_accessible(self):
+ if not self.accessible:
+ self.accessible = True
+ if self.parent:
+ self.parent.accessible_children.append(self)
+ self.parent.mark_accessible()
+
+ def nextlabel(self):
+ while self.parent:
+ self = self.parent
+ self.counter += 1
+ return 'err%d' % self.counter
+
+ def getlabel(self):
+ if self.label is None:
+ self.mark_accessible()
+ self.label = self.nextlabel()
+ return self.label
+
+ def getbranch(self):
+ while self.parent:
+ yield self
+ self = self.parent
+
+ def error_code(self):
+ N = len(self.accessible_children)
+ for i in range(N):
+ if i > 0:
+ yield LLOp('goto', [], self.getlabel())
+ node = self.accessible_children[~i]
+ for op in node.error_code():
+ yield op
+ if self.label:
+ yield self.label
+ elif not N:
+ return
+ yield self.release_operation
+
+
+def variants(args_t, directions):
+ # enumerate all variants of the given signature of hltypes
+ # XXX this can become quadratically slow for large programs because
+ # XXX it enumerates all conversions of the result from a constant value
+ if len(args_t):
+ for sig in variants(args_t[:-1], directions[:-1]):
+ yield sig + args_t[-1:]
+ choices_for_last_arg = list(directions[-1](args_t[-1]))
+ for sig in variants(args_t[:-1], directions[:-1]):
+ for last_arg in choices_for_last_arg:
+ yield sig + (last_arg,)
+ else:
+ yield ()
More information about the Pypy-commit
mailing list