Author: Manuel Jacob
Branch: llvm-translation-backend
Changeset: r66463:c2a3e1e2cefe
Date: 2012-03-02 18:03 +0100
http://bitbucket.org/pypy/pypy/changeset/c2a3e1e2cefe/

Log:    Add a new LLVM translation backend. So far it can translate some
        simple tests but when translated with garbarge collection the
        resulting binary segfaults.

diff --git a/pypy/config/translationoption.py b/pypy/config/translationoption.py
--- a/pypy/config/translationoption.py
+++ b/pypy/config/translationoption.py
@@ -41,9 +41,10 @@
                                 ]
                      }),
     ChoiceOption("backend", "Backend to use for code generation",
-                 ["c", "cli", "jvm"], default="c",
+                 ["c", "llvm", "cli", "jvm"], default="c",
                  requires={
                      "c":      [("translation.type_system", "lltype")],
+                     "llvm":   [("translation.type_system", "lltype")],
                      "cli":    [("translation.type_system", "ootype")],
                      "jvm":    [("translation.type_system", "ootype")],
                      },
diff --git a/pypy/translator/driver.py b/pypy/translator/driver.py
--- a/pypy/translator/driver.py
+++ b/pypy/translator/driver.py
@@ -34,6 +34,7 @@
 
 _BACKEND_TO_TYPESYSTEM = {
     'c': 'lltype',
+    'llvm': 'ootype'
 }
 
 def backend_to_typesystem(backend):
@@ -619,6 +620,40 @@
                                       [STACKCHECKINSERTION, '?'+BACKENDOPT, 
RTYPE], 
                                       "LLInterpreting")
 
+    def task_source_llvm(self):
+        translator = self.translator
+        if translator.annotator is None:
+            raise ValueError, "llvm requires annotation."
+
+        from pypy.translator.llvm import genllvm
+
+        self.llvmgen = genllvm.GenLLVM(translator, self.standalone)
+
+        llvm_filename = self.llvmgen.gen_source(self.entry_point)
+        self.log.info("written: %s" % (llvm_filename,))
+
+    task_source_llvm = taskdef(task_source_llvm,
+                               [STACKCHECKINSERTION, BACKENDOPT, RTYPE],
+                               "Generating llvm source")
+
+    def task_compile_llvm(self):
+        gen = self.llvmgen
+        if self.standalone:
+            exe_name = (self.exe_name or 'testing') % self.get_info()
+            self.c_entryp = gen.compile_standalone(exe_name)
+            self.create_exe()
+        else:
+            self.c_entryp = gen.compile_module()
+
+    task_compile_llvm = taskdef(task_compile_llvm, ['source_llvm'],
+                                "Compiling llvm source")
+
+    def task_run_llvm(self):
+        self.backend_run('llvm')
+
+    task_run_llvm = taskdef(task_run_llvm, ['compile_llvm'],
+                            "Running compiled llvm source", idemp=True)
+
     def task_source_cli(self):
         from pypy.translator.cli.gencli import GenCli
         from pypy.translator.cli.entrypoint import get_entrypoint
diff --git a/pypy/translator/interactive.py b/pypy/translator/interactive.py
--- a/pypy/translator/interactive.py
+++ b/pypy/translator/interactive.py
@@ -140,10 +140,10 @@
         self.ensure_backend('c')
         self.driver.source_c()
 
-    def source_cl(self, argtypes=None, **kwds):
+    def source_llvm(self, argtypes=None, **kwds):
         self.update_options(argtypes, kwds)
-        self.ensure_backend('cl')
-        self.driver.source_cl()
+        self.ensure_backend('llvm')
+        self.driver.source_llvm()
 
     def compile(self, argtypes=None, **kwds):
         self.update_options(argtypes, kwds)
@@ -156,6 +156,12 @@
         self.ensure_backend('c')
         self.driver.compile_c()
         return self.driver.c_entryp
+
+    def compile_llvm(self, argtypes=None, **kwds):
+        self.update_options(argtypes, kwds)
+        self.ensure_backend('llvm')
+        self.driver.compile_llvm()
+        return self.driver.c_entryp
   
     def compile_cli(self, argtypes=None, **kwds):
         self.update_options(argtypes, kwds)
diff --git a/pypy/translator/llvm/__init__.py b/pypy/translator/llvm/__init__.py
new file mode 100644
diff --git a/pypy/translator/llvm/genllvm.py b/pypy/translator/llvm/genllvm.py
new file mode 100644
--- /dev/null
+++ b/pypy/translator/llvm/genllvm.py
@@ -0,0 +1,660 @@
+from itertools import count
+
+from py.process import cmdexec
+from pypy.objspace.flow.model import mkentrymap, Constant
+from pypy.rlib.jit import _we_are_jitted
+from pypy.rlib.objectmodel import (ComputedIntSymbolic, CDefinedIntSymbolic,
+     malloc_zero_filled, running_on_llinterp)
+from pypy.rpython.lltypesystem import llgroup, llmemory, lltype, rffi
+from pypy.rpython.memory.gctransform.framework import FrameworkGCTransformer
+from pypy.rpython.rmodel import inputconst
+from pypy.rpython.typesystem import getfunctionptr
+from pypy.translator.gensupp import uniquemodulename
+from pypy.translator.unsimplify import remove_double_links
+from pypy.tool.udir import udir
+
+
+class Database(object):
+    def __init__(self, genllvm, f):
+        self.genllvm = genllvm
+        self.f = f
+        self.names_counter = {}
+        self.types = {}
+        self.globals_counter = count()
+        self.globals_ = {None: 'null'}
+        self.delayed_ptrs = []
+
+    def repr_type(self, type_):
+        try:
+            return self.types[type_]
+        except KeyError:
+            self.add_type(type_)
+            return self.types[type_]
+
+    def repr_ptr(self, ptr):
+        try:
+            obj = ptr._obj
+        except lltype.DelayedPointer:
+            if isinstance(ptr._obj0, str):
+                self.delayed_ptrs.append(ptr)
+                return '@%s' % ptr._obj0[len('delayed!'):]
+            obj = ptr._obj0
+        try:
+            return self.globals_[obj]
+        except KeyError:
+            self.add_global(obj._TYPE, obj)
+            return self.globals_[obj]
+
+    def _unique(self, name):
+        if name not in self.names_counter:
+            self.names_counter[name] = 0
+            return name
+        else:
+            ret = '%s_%s' % (name, self.names_counter[name])
+            self.names_counter[name] += 1
+            return ret
+
+    def add_type(self, type_):
+        if isinstance(type_, lltype.FixedSizeArray):
+            self.types[type_] = '[%s x %s]' % (type_.length, _T(type_.OF))
+        elif isinstance(type_, lltype.Struct):
+            self.types[type_] = name = self._unique('%struct.' + type_._name)
+            self.f.write('%s = type { %s }\n' % (
+                    name, _Tm(type_._flds[fld] for fld in type_._names)))
+        elif isinstance(type_, lltype.Array):
+            of = _T(type_.OF)
+            name = '%array_of_' + of.strip('%').replace('*', '_ptr')
+            self.types[type_] = name = self._unique(name)
+            self.f.write('%s = type { i64, [0 x %s] }\n' % (name, of))
+        elif isinstance(type_, lltype.FuncType):
+            self.types[type_] = '%s (%s)' % (_T(type_.RESULT), _Tm(type_.ARGS))
+        elif isinstance(type_, lltype.OpaqueType):
+            self.types[type_] = 'i8*'
+        elif isinstance(type_, llgroup.GroupType):
+            self.types[type_] = 'i8*'
+        else:
+            raise TypeError('type_ is %r' % type_)
+
+    def add_global(self, type_, obj):
+        if isinstance(type_, lltype.FuncType):
+            name = ('@%s' % obj._name).replace('<', '_').replace('>', '_')
+            self.globals_[obj] = name = self._unique(name)
+            if getattr(obj, 'external', None) == 'C':
+                if name == '@ROUND_UP_FOR_ALLOCATION': # XXX
+                    self.f.write('define i64 @ROUND_UP_FOR_ALLOCATION(\n'
+                                 '        i64 %tmp, i64 %ignore) {\n'
+                                 '    ret i64 %tmp\n'
+                                 '}\n')
+                    return
+                self.f.write('declare %s %s(%s)\n' % (
+                        _T(type_.RESULT), name, _Tm(type_.ARGS)))
+            else:
+                writer = self.genllvm.get_function_writer(name, obj.graph)
+                self.f.writelines(writer.lines)
+            return
+
+        name = '@global_%s' % next(self.globals_counter)
+        if isinstance(type_, lltype.Array):
+            typedef = '{ i64, [%s x %s] }' % (len(obj.items), _T(type_.OF))
+            self.globals_[obj] = 'bitcast(%s* %s to %s*)' % (
+                    typedef, name, _T(type_))
+        else:
+            typedef = _T(type_)
+            self.globals_[obj] = name
+
+        self.f.write('%s = global %s %s\n' % (
+                name, typedef, self.repr_obj(type_, obj)))
+
+    def _repr_array(self, flds):
+        if all(_V(t, i) == 'null' for t, i in flds):
+            return 'zeroinitializer'
+        return '[ %s ]' % _TVm(flds)
+
+    def repr_obj(self, type_, obj):
+        if isinstance(type_, lltype.FixedSizeArray):
+            flds = [(type_.OF, getattr(obj, na)) for na in type_._names]
+            return self._repr_array(flds)
+        elif isinstance(type_, lltype.Struct):
+            flds = [(type_._flds[na], getattr(obj, na)) for na in type_._names]
+            if all(_V(t, i) == 'null' for t, i in flds):
+                return 'zeroinitializer'
+            return '{ %s }' % _TVm(flds)
+        elif isinstance(type_, lltype.Array):
+            return '{ i64 %s, [%s x %s ] %s }' % (
+                    len(obj.items), len(obj.items), _T(type_.OF),
+                    self._repr_array([(type_.OF, i) for i in obj.items]))
+        elif isinstance(type_, lltype.OpaqueType):
+            return 'null'
+        elif isinstance(type_, llgroup.GroupType):
+            return 'null'
+        else:
+            raise TypeError('type_ is %r' % type_)
+
+
+def repr_void(value):
+    if value is None:
+        return 'null'
+    raise TypeError
+
+def repr_number(value):
+    if isinstance(value, (int, long)):
+        return str(value)
+
+    if isinstance(value, ComputedIntSymbolic):
+        return value.compute_fn()
+    elif isinstance(value, llmemory.AddressOffset):
+        return '0' # XXX
+        raise NotImplementedError
+    elif isinstance(value, llgroup.GroupMemberOffset):
+        return value.index
+    elif isinstance(value, CDefinedIntSymbolic):
+        if value is malloc_zero_filled:
+            return '1'
+        elif value is _we_are_jitted:
+            return '0'
+        elif value is running_on_llinterp:
+            return '0'
+    raise NotImplementedError(value)
+
+def repr_address(value):
+    if value.ptr is None:
+        return 'null'
+    return "bitcast(%s to i8*)" % TV(C(value.ptr))
+
+PRIMITIVES = {
+    lltype.Void: ('void', repr_void),
+    lltype.Signed: ('i64', repr_number),
+    lltype.Unsigned: ('i64', repr_number),
+    lltype.Char: ('i8', lambda x: str(ord(x))),
+    lltype.Bool: ('i1', lambda x: 'true' if x else 'false'),
+    lltype.Float: ('double', repr),
+    lltype.SingleFloat: ('float', repr),
+    lltype.LongFloat: ('x86_fp80', repr),
+    llmemory.Address: ('i8*', repr_address)
+}
+for type_ in rffi.NUMBER_TYPES:
+    if type_ not in PRIMITIVES:
+        PRIMITIVES[type_] = ('i%s' % (rffi.sizeof(type_) * 8), repr_number)
+
+def _T(type_):
+    """Represent LLType `type_`."""
+    if isinstance(type_, lltype.Primitive):
+        return PRIMITIVES[type_][0]
+    elif isinstance(type_, lltype.Ptr):
+        return '%s*' % _T(type_.TO)
+    else:
+        return database.repr_type(type_)
+
+def T(variable_or_const):
+    """Represent LLType of `variable_or_const`."""
+    return _T(variable_or_const.concretetype)
+
+def _V(type_, value):
+    """Represent LLValue `value` of type `type_`."""
+    if isinstance(type_, lltype.Primitive):
+        return PRIMITIVES[type_][1](value)
+    elif isinstance(type_, lltype.Ptr):
+        return database.repr_ptr(value)
+    else:
+        return database.repr_obj(value._TYPE, value)
+
+def V(variable_or_const):
+    """Represent LLValue of `variable_or_const`."""
+    if isinstance(variable_or_const, Constant):
+        return _V(variable_or_const.concretetype, variable_or_const.value)
+    return '%%%s' % variable_or_const.name
+
+def TV(variable_or_const):
+    """Same as '%s %s' % (T(`variable_or_const`), V(`variable_or_const`))."""
+    return '%s %s' % (_T(variable_or_const.concretetype), V(variable_or_const))
+
+def _Tm(types):
+    """Represent multiple types. `types` is an iterable of LLTypes."""
+    return ', '.join(_T(type_) for type_ in types if type_ is not lltype.Void)
+
+def Vm(vals):
+    """Represent multiple values. `vals` is an iterable of LLValues."""
+    return ', '.join(V(val) for val in vals)
+
+def _TVm(vocs):
+    """
+    Represent multiple types and values. `vocs` is an iterable of
+    (LLType, LLValue) pairs.
+    """
+    return ', '.join('%s %s' % (_T(type_), _V(type_, val))
+                     for type_, val in vocs if type_ is not lltype.Void)
+
+def TVm(vocs):
+    """
+    Represent multiple types and values. `vocs` is an iterable of LLValues.
+    """
+    return ', '.join(TV(voc) for voc in vocs
+                     if voc.concretetype is not lltype.Void)
+
+def C(value):
+    """Return `value` as Constant."""
+    return inputconst(lltype.typeOf(value), value)
+
+
+OPS = {
+        'int_add_ovf': 'add', # XXX check for overflow
+        'int_mul_ovf': 'mul', # XXX check for overflow
+}
+for type_ in ['int', 'uint', 'llong', 'ullong']:
+    OPS['%s_lshift' % type_] = 'shl'
+    OPS['%s_rshift' % type_] = 'lshr' if type_[0] == 'u' else 'ashr'
+    OPS['%s_floordiv' % type_] = 'udiv' if type_[0] == 'u' else 'sdiv'
+    OPS['%s_mod' % type_] = 'urem' if type_[0] == 'u' else 'srem'
+    for op in ['add', 'sub', 'mul', 'and', 'or', 'xor']:
+        OPS['%s_%s' % (type_, op)] = op
+
+for type_ in ['float']:
+    for op in ['add', 'sub', 'mul', 'div']:
+        OPS['%s_%s' % (type_, op)] = 'f%s' % op
+
+for type_, prefix in [('char', 'u'), ('unichar', 'u'), ('int', 's'),
+                      ('uint', 'u'), ('llong', 's'), ('ullong', 'u'),
+                      ('adr', 's'), ('ptr', 's')]:
+    OPS['%s_eq' % type_] = 'icmp eq'
+    OPS['%s_ne' % type_] = 'icmp ne'
+    for op in ['gt', 'ge', 'lt', 'le']:
+        OPS['%s_%s' % (type_, op)] = 'icmp %s%s' % (prefix, op)
+
+for type_ in ['float']:
+    for op in ['eq', 'ne', 'gt', 'ge', 'lt', 'le']:
+        OPS['%s_%s' % (type_, op)] = 'fcmp o%s' % op
+
+
+class FunctionWriter(object):
+    def __init__(self):
+        self.lines = []
+        self.tmp_counter = count()
+
+    def w(self, line, indent='    '):
+        self.lines.append('%s%s\n' % (indent, line))
+
+    def write_graph(self, name, graph):
+        self.w('define %s %s(%s) {' % (T(graph.getreturnvar()), name,
+                                       TVm(graph.getargs())), '')
+
+        self.entrymap = mkentrymap(graph)
+        self.block_to_name = dict(
+                (bl, 'block%s' % i) for i, bl in enumerate(graph.iterblocks()))
+        for block in graph.iterblocks():
+            self.w('%s:' % self.block_to_name[block], '  ')
+            if block is not graph.startblock:
+                self.write_phi_nodes(block)
+            self.write_operations(block)
+            self.write_branches(block)
+        self.w('}', '')
+
+    def write_phi_nodes(self, block):
+        for i, arg in enumerate(block.inputargs):
+            if arg.concretetype == lltype.Void:
+                continue
+            argsstr = ', '.join('[%s, %%%s]' %
+                    (V(l.args[i]), self.block_to_name[l.prevblock])
+                    for l in self.entrymap[block] if l.prevblock is not None)
+            self.w('%s = phi %s %s' % (V(arg), T(arg), argsstr))
+
+    def write_operations(self, block):
+        for op in block.operations:
+            self.w('; %s' % op)
+            opname = op.opname
+            if opname in OPS:
+                self.w('%s = %s %s %s' % (
+                        V(op.result), OPS[opname], T(op.args[0]), Vm(op.args)))
+            elif opname.startswith('cast_'):
+                if opname == 'cast_adr_to_int':
+                    self._cast(op.result, op.args[0], 'ptrtoint')
+                elif opname == 'cast_int_to_adr':
+                    self._cast(op.result, op.args[0], 'inttoptr')
+                else:
+                    self._cast(op.result, op.args[0])
+            else:
+                func = getattr(self, 'op_' + opname, None)
+                if func is not None:
+                    func(op.result, *op.args)
+                else:
+                    raise NotImplementedError(op)
+
+    def op_debug_print(self, result, *args):
+        pass
+
+    def op_debug_assert(self, result, *args):
+        pass
+
+    def op_debug_llinterpcall(self, result, *args):
+        if result.concretetype is not lltype.Void:
+            self.w('%s = bitcast %s undef to %s' % (
+                    V(result), T(result), T(result)))
+
+    def op_debug_fatalerror(self, result, *args):
+        pass
+
+    def op_debug_record_traceback(self, result, *args):
+        pass
+
+    def op_debug_start_traceback(self, result, *args):
+        pass
+
+    def op_debug_catch_exception(self, result, *args):
+        pass
+
+    def op_debug_reraise_traceback(self, result, *args):
+        pass
+
+    def op_debug_start(self, result, *args):
+        pass
+
+    def op_debug_stop(self, result, *args):
+        pass
+
+    def op_debug_nonnull_pointer(self, result, *args):
+        pass
+
+    def op_track_alloc_start(self, result, *args):
+        pass
+
+    def op_track_alloc_stop(self, result, *args):
+        pass
+
+    def _ptr_or_adr(self, var):
+        return (isinstance(var.concretetype, lltype.Ptr) or
+                var.concretetype is llmemory.Address)
+
+    def _cast(self, to, fr, op=None):
+        if op is None:
+            if ((fr.concretetype is to.concretetype) or
+                (self._ptr_or_adr(fr) and self._ptr_or_adr(to))):
+                op = 'bitcast'
+            else:
+                fr_size = rffi.sizeof(fr.concretetype)
+                to_size = rffi.sizeof(to.concretetype)
+                if fr_size == to_size:
+                    op = 'bitcast'
+                elif fr_size > to_size:
+                    op = 'trunc'
+                elif fr_size < to_size:
+                    if rffi.size_and_sign(fr.concretetype)[1]:
+                        op = 'sext'
+                    else:
+                        op = 'zext'
+        self.w('%s = %s %s %s to %s' % (V(to), op, T(fr), V(fr), T(to)))
+    op_raw_malloc_usage = _cast
+
+    def op_force_cast(self, result, var):
+        r_ptr = self._ptr_or_adr(result)
+        v_ptr = self._ptr_or_adr(var)
+        if r_ptr and v_ptr:
+            self._cast(result, var, 'bitcast')
+        elif r_ptr:
+            self._cast(result, var, 'inttoptr')
+        elif v_ptr:
+            self._cast(result, var, 'ptrtoint')
+        else:
+            self._cast(result, var)
+
+    def op_direct_call(self, result, fn, *args):
+        if result.concretetype is lltype.Void:
+            self.w('call void %s(%s)' % (V(fn), TVm(args)))
+        else:
+            if (isinstance(result.concretetype, lltype.Ptr) and
+                isinstance(result.concretetype.TO, lltype.FuncType)):
+                self.w('%s = call %s %s(%s)' %
+                        (V(result), T(fn), V(fn), TVm(args)))
+            else:
+                self.w('%s = call %s %s(%s)' %
+                        (V(result), T(result), V(fn), TVm(args)))
+    op_indirect_call = op_direct_call
+
+    def _get_field_ptr(self, ptr, field, target_var):
+        self.w('%s = getelementptr %s %s, i64 0, i32 %s' % (
+                target_var, T(ptr), V(ptr),
+                ptr.concretetype.TO._names_without_voids().index(field.value)))
+
+    def op_getsubstruct(self, result, var, field):
+        self._get_field_ptr(var, field, V(result))
+
+    def op_direct_fieldptr(self, result, var, field):
+        t = '%%tmp%s' % next(self.tmp_counter)
+        self._get_field_ptr(var, field, t)
+        self.w('%s = bitcast %s %s to %s' % (
+                V(result), _T(result.concretetype.TO.OF)+'*', t, T(result)))
+
+    def op_getfield(self, result, var, field):
+        t = '%%tmp%s' % next(self.tmp_counter)
+        self._get_field_ptr(var, field, t)
+        self.w('%s = load %s %s' % (V(result), T(result)+'*', t))
+    op_bare_getfield = op_getfield
+
+    def op_setfield(self, result, var, field, value):
+        t = '%%tmp%s' % next(self.tmp_counter)
+        self._get_field_ptr(var, field, t)
+        self.w('store %s, %s %s' % (TV(value), T(value)+'*', t))
+    op_bare_setfield = op_setfield
+
+    def _get_item_ptr(self, ptr, index, target_var):
+        if isinstance(ptr.concretetype.TO, lltype.FixedSizeArray):
+            self.w('%s = getelementptr %s %s, i64 0, i64 %s' % (
+                    target_var, T(ptr), V(ptr), V(index)))
+        else:
+            self.w('%s = getelementptr %s %s, i64 0, i32 1, i64 %s' % (
+                    target_var, T(ptr), V(ptr), V(index)))
+
+    def op_getarraysubstruct(self, result, var, index):
+        self._get_item_ptr(var, index, V(result))
+
+    def op_getarrayitem(self, result, var, index):
+        t = '%%tmp%s' % next(self.tmp_counter)
+        self._get_item_ptr(var, index, t)
+        self.w('%s = load %s %s' % (V(result), T(result)+'*', t))
+    op_bare_getarrayitem = op_getarrayitem
+
+    def op_setarrayitem(self, result, var, index, value):
+        t = '%%tmp%s' % next(self.tmp_counter)
+        self._get_item_ptr(var, index, t)
+        self.w('store %s, %s %s' % (TV(value), T(value)+'*', t))
+    op_bare_setarrayitem = op_setarrayitem
+
+    def op_getarraysize(self, result, var):
+        type_ = var.concretetype.TO
+        assert (isinstance(type_, lltype.Array) and
+                not type_._hints.get("nolength", False))
+
+        t = '%%tmp%s' % next(self.tmp_counter)
+        self.w('%s = getelementptr %s, i64 0, i32 0' % (t, TV(var)))
+        self.w('%s = load i64* %s' % (V(result), t))
+
+    def op_int_is_true(self, result, var):
+        self.w('%s = icmp ne i64 %s, 0' % (V(result), V(var)))
+
+    def op_int_neg(self, result, var):
+        self.w('%s = sub %s 0, %s' % (V(result), T(var), V(var)))
+
+    def op_ptr_iszero(self, result, var):
+        self.w('%s = icmp eq %s %s, null' % (V(result), T(var), V(var)))
+
+    def op_ptr_nonzero(self, result, var):
+        self.w('%s = icmp ne %s %s, null' % (V(result), T(var), V(var)))
+
+    def op_adr_delta(self, result, arg1, arg2):
+        t1 = '%%tmp%s' % next(self.tmp_counter)
+        t2 = '%%tmp%s' % next(self.tmp_counter)
+        self.w('%s = ptrtoint %s %s to i64' % (t1, T(arg1), V(arg1)))
+        self.w('%s = ptrtoint %s %s to i64' % (t2, T(arg2), V(arg2)))
+        self.w('%s = sub i64 %s, %s' % (V(result), t1, t2))
+
+    def _adr_op(int_op):
+        def f(self, result, arg1, arg2):
+            t = '%%tmp%s' % next(self.tmp_counter)
+            tr = '%%tmp%s' % next(self.tmp_counter)
+            self.w('%s = ptrtoint %s %s to i64' % (t, T(arg1), V(arg1)))
+            self.w('%s = %s i64 %s, %s' % (tr, int_op, t, V(arg2)))
+            self.w('%s = inttoptr i64 %s to %s' % (V(result), tr, T(result)))
+        return f
+
+    op_adr_add = _adr_op('add')
+    op_adr_sub = _adr_op('sub')
+
+    def op_raw_malloc(self, result, size):
+        self.w('%s = call %s @malloc(%s)' % (V(result), T(result), TV(size)))
+
+    def _get_addr(self, type_, addr, incr):
+        t1 = '%%tmp%s' % next(self.tmp_counter)
+        t2 = '%%tmp%s' % next(self.tmp_counter)
+        tt = T(type_)+'*'
+        self.w('%s = bitcast %s to %s' % (t1, TV(addr), tt))
+        self.w('%s = getelementptr %s %s, i64 %s' % (t2, tt, t1, incr))
+        return '%s %s' % (tt, t2)
+
+    def op_raw_load(self, result, addr, _, incr):
+        self.w('%s = load %s' % (V(result), self._get_addr(result,addr, incr)))
+
+    def op_raw_store(self, result, addr, _, incr, value):
+        self.w('store %s, %s' % (TV(value), self._get_addr(value, addr, incr)))
+
+    def op_raw_memclear(self, result, pointer, size):
+        self.w('call void @llvm.memset.p0i8.i64 (%s, i8 0, %s, i32 0, i1 0)' %
+                (TV(pointer), TV(size)))
+
+    def op_raw_memcopy(self, result, source, dest, size):
+        self.w('call void @llvm.memcpy.p0i8.p0i8.i64 (%s, %s, %s, i32 0, i1 0)'
+                % (TV(dest), TV(source), TV(size)))
+
+    def op_raw_free(self, result, var):
+        self.w('call void @free(%s)' % TV(var))
+
+    def op_extract_ushort(self, result, value):
+        self.w('%s = trunc %s to %s' % (V(result), TV(value), T(result)))
+
+    def op_combine_ushort(self, result, ushort, rest):
+        t = '%%tmp%s' % next(self.tmp_counter)
+        self.w('%s = sext %s to %s' % (t, TV(ushort), T(rest)))
+        self.w('%s = or %s %s, %s' % (V(result), T(result), t, V(rest)))
+
+    def op_get_group_member(self, result, groupptr, compactoffset):
+        t1 = '%%tmp%s' % next(self.tmp_counter)
+        t2 = '%%tmp%s' % next(self.tmp_counter)
+        tr = '%%tmp%s' % next(self.tmp_counter)
+        self.w('%s = ptrtoint %s %s to i64' % (t1, T(groupptr), V(groupptr)))
+        self.w('%s = sext %s to i64' % (t2, TV(compactoffset)))
+        self.w('%s = add i64 %s, %s' % (tr, t1, t2))
+        self.w('%s = inttoptr i64 %s to %s' % (V(result), tr, T(result)))
+
+    def op_gc_gettypeptr_group(self, result, v_obj, grpptr, skipoffset, 
vtableinfo):
+        # XXX
+        self.w('%s = inttoptr i64 0 to %s' % (V(result), T(result)))
+
+    def op_gc_reload_possibly_moved(self, result, v_newaddr, v_targetvar):
+        pass
+
+    def op_keepalive(self, result, var):
+        pass
+
+    def write_branches(self, block):
+        if len(block.exits) == 0:
+            self.write_returnblock(block)
+        elif len(block.exits) == 1:
+            self.w('br label %%%s' % self.block_to_name[block.exits[0].target])
+        elif len(block.exits) == 2:
+            assert block.exitswitch.concretetype is lltype.Bool
+            for link in block.exits:
+                if link.llexitcase:
+                    true = self.block_to_name[link.target]
+                else:
+                    false = self.block_to_name[link.target]
+            self.w('br i1 %s, label %%%s, label %%%s' % (
+                    V(block.exitswitch), true, false))
+        else:
+            raise NotImplementedError
+
+    def write_returnblock(self, block):
+        ret = block.inputargs[0]
+        if ret.concretetype is lltype.Void:
+            self.w('ret void')
+        else:
+            self.w('ret %s %s' % (T(ret), V(ret)))
+
+
+class GCPolicy(object):
+    def __init__(self, genllvm):
+        self.genllvm = genllvm
+
+    def transform_graph(self, graph):
+        pass
+
+    def finish(self):
+        assert not database.delayed_ptrs
+
+
+class FrameworkGCPolicy(GCPolicy):
+    def __init__(self, genllvm):
+        self.genllvm = genllvm
+        self.gctransformer = FrameworkGCTransformer(genllvm.translator)
+
+    def transform_graph(self, graph):
+        self.gctransformer.transform_graph(graph)
+
+    def finish(self):
+        while database.delayed_ptrs:
+            self.gctransformer.finish_helpers()
+
+            delayed_ptrs = database.delayed_ptrs
+            database.delayed_ptrs = []
+            for ptr in delayed_ptrs:
+                _V(lltype.typeOf(ptr), ptr)
+
+            list(self.gctransformer.get_finish_tables())
+
+
+class GenLLVM(object):
+    def __init__(self, translator, standalone):
+        self.translator = translator
+
+        self.exctransformer = translator.getexceptiontransformer()
+        self.gcpolicy = {
+            'none': GCPolicy,
+            'framework': FrameworkGCPolicy
+        }[translator.config.translation.gctransformer](self)
+
+    def get_function_writer(self, name, graph):
+        self.exctransformer.create_exception_handling(graph)
+        self.gcpolicy.transform_graph(graph)
+        remove_double_links(self.translator.annotator, graph)
+        writer = FunctionWriter()
+        writer.write_graph(name, graph)
+        return writer
+
+    def gen_source(self, entry_point):
+        global database
+
+        self.entry_point = entry_point
+        self.base_path = udir.join(uniquemodulename('main'))
+
+        bk = self.translator.annotator.bookkeeper
+        ptr = getfunctionptr(bk.getdesc(entry_point).getuniquegraph())
+
+        with self.base_path.new(ext='.ll').open('w') as f:
+            # XXX
+            f.write('declare void @llvm.memcpy.p0i8.p0i8.i64 ('
+                    'i8*, i8*, i64, i32, i1)\n')
+            f.write('declare void @llvm.memset.p0i8.i64 ('
+                    'i8*, i8, i64, i32, i1)\n')
+            database = Database(self, f)
+            self.c_entry_point_name = V(C(ptr))[1:]
+            self.gcpolicy.finish()
+
+    def compile_standalone(self, exe_name):
+        pass
+
+    def compile_module(self):
+        base_path = str(self.base_path)
+        cmdexec('opt -std-compile-opts %s.ll -o %s.bc' % ((base_path,)*2))
+        cmdexec('llc -relocation-model=pic %s.bc -o %s.s' % ((base_path,)*2))
+        cmdexec('llvm-mc %s.s -filetype=obj -o %s.o' % ((base_path,)*2))
+        cmdexec('ld -shared -o %s.so %s.o' % ((base_path,)*2))
+
+        import ctypes
+        cdll = ctypes.CDLL(str(self.base_path) + '.so')
+        return getattr(cdll, self.c_entry_point_name)
diff --git a/pypy/translator/llvm/test/__init__.py 
b/pypy/translator/llvm/test/__init__.py
new file mode 100644
diff --git a/pypy/translator/llvm/test/test_genllvm.py 
b/pypy/translator/llvm/test/test_genllvm.py
new file mode 100644
--- /dev/null
+++ b/pypy/translator/llvm/test/test_genllvm.py
@@ -0,0 +1,78 @@
+from pypy.translator.interactive import Translation
+
+
+def compile(func, argtypes=None, gc=False):
+    translation = Translation(func, argtypes, backend='llvm', verbose=False)
+    translation.disable(['backendopt_lltype'])
+    translation.config.translation.backendopt.none = True
+    if gc:
+        translation.config.translation.gctransformer = 'framework'
+        translation.config.translation.gc = 'minimark'
+    else:
+        translation.config.translation.gctransformer = 'none'
+        translation.config.translation.gc = 'none'
+
+    translation.annotate()
+    return translation.compile_llvm()
+
+
+class TestSimple(object):
+    def test_pass(self):
+        def f():
+            pass
+
+        fc = compile(f)
+        assert fc() == 0
+
+    def test_return(self):
+        def f():
+            return 42
+
+        fc = compile(f)
+        assert fc() == 42
+
+    def test_argument(self):
+        def f(echo):
+            return echo
+
+        fc = compile(f, [int])
+        assert fc(123) == 123
+
+    def test_add_int(self):
+        def f(i):
+            return i + 1
+
+        fc = compile(f, [int])
+        assert fc(2) == 3
+        assert fc(3) == 4
+
+    def test_call(self):
+        def g():
+            return 11
+        def f():
+            return g()
+
+        fc = compile(f)
+        assert fc() == 11
+
+    def test_bool(self):
+        def f(b):
+            return not b
+
+        fc = compile(f, [bool])
+        assert fc(True) == False
+        assert fc(False) == True
+
+
+class TestGarbageCollected(object):
+    def test_struct(self):
+        class C(object):
+            pass
+
+        def f(i):
+            c = C()
+            c.i = i
+            return c.i
+
+        fc = compile(f, [int], gc=True)
+        assert fc(33) == 33
diff --git a/pypy/translator/test/test_driver.py 
b/pypy/translator/test/test_driver.py
--- a/pypy/translator/test/test_driver.py
+++ b/pypy/translator/test/test_driver.py
@@ -34,10 +34,11 @@
                  'source_cli', 'source_c',
                  'compile_cli', 'compile_c',
                  'run_c', 'run_cli',
+                 'source_llvm', 'compile_llvm', 'run_llvm',
                  'compile_jvm', 'source_jvm', 'run_jvm',
                  'pyjitpl_lltype',
                  'pyjitpl_ootype']
-    assert set(td.exposed) == set(expected)                             
+    assert set(td.exposed) == set(expected)
 
     td = TranslationDriver({'backend': None, 'type_system': 'lltype'})
 
diff --git a/pypy/translator/test/test_interactive.py 
b/pypy/translator/test/test_interactive.py
--- a/pypy/translator/test/test_interactive.py
+++ b/pypy/translator/test/test_interactive.py
@@ -71,6 +71,18 @@
 
     t = Translation(f, [int, int])
     py.test.raises(Exception, "t.source()")
+
+def test_simple_source_llvm():
+    def f(x,y):
+        return x+y
+
+    t = Translation(f, [int, int], backend='llvm')
+    t.source(gc='boehm')
+    assert 'source_llvm' in t.driver.done
+    
+    t = Translation(f, [int, int])
+    t.source_llvm()
+    assert 'source_llvm' in t.driver.done
     
 def test_disable_logic():
 
diff --git a/pypy/translator/unsimplify.py b/pypy/translator/unsimplify.py
--- a/pypy/translator/unsimplify.py
+++ b/pypy/translator/unsimplify.py
@@ -129,6 +129,22 @@
     # in the second block!
     return split_block(annotator, block, 0, _forcelink=block.inputargs)
 
+def remove_double_links(annotator, graph):
+    """This can be useful for code generators: it ensures that no block has
+    more than one incoming links from one and the same other block. It allows
+    argument passing along links to be implemented with phi nodes since the
+    value of an argument can be determined by looking from which block the
+    control passed. """
+    for block in graph.iterblocks():
+        double_links = []
+        seen = {}
+        for link in block.exits:
+            if link.target in seen:
+                double_links.append(link)
+            seen[link.target] = True
+        for link in double_links:
+            insert_empty_block(annotator, link)
+
 def call_initial_function(translator, initial_func, annhelper=None):
     """Before the program starts, call 'initial_func()'."""
     from pypy.annotation import model as annmodel
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to