[pypy-svn] r70495 - in pypy/branch/separate-compilation/pypy/translator/c: . test

afa at codespeak.net afa at codespeak.net
Mon Jan 11 14:09:32 CET 2010


Author: afa
Date: Mon Jan 11 14:09:32 2010
New Revision: 70495

Added:
   pypy/branch/separate-compilation/pypy/translator/c/separate.py
   pypy/branch/separate-compilation/pypy/translator/c/test/test_separate.py
Modified:
   pypy/branch/separate-compilation/pypy/translator/c/genc.py
Log:
Introduce CSharedModuleBuilder: it builds a .so or .dll
which exports RPython functions.
Other modules can link with it and call the functions without
the help of dlopen or ctypes.

Only works with primitive types so far.


Modified: pypy/branch/separate-compilation/pypy/translator/c/genc.py
==============================================================================
--- pypy/branch/separate-compilation/pypy/translator/c/genc.py	(original)
+++ pypy/branch/separate-compilation/pypy/translator/c/genc.py	Mon Jan 11 14:09:32 2010
@@ -7,7 +7,7 @@
 from pypy.translator.llsupport.wrapper import new_wrapper
 from pypy.translator.gensupp import uniquemodulename, NameManager
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
-from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.tool.udir import udir
 from pypy.tool import isolate
 from pypy.translator.c.support import log, c_string_constant
@@ -300,27 +300,24 @@
         except KeyError:
             pass
 
-
-class CExtModuleBuilder(CBuilder):
+class CSharedModuleBuilder(CBuilder):
+    "Builds a simple .so or .dll file"
     standalone = False
-    _module = None
-    _wrapper = None
 
-    def getentrypointptr(self): # xxx
-        if self._wrapper is None:
-            self._wrapper = new_wrapper(self.entrypoint, self.translator)
-        return self._wrapper
+    def getentrypointptr(self):
+        return self.entrypoint.values()
+
+    def getexportsymbols(self):
+        self.export_node_names = dict(
+            (funcname, self.db.get(funcptr))
+            for funcname, funcptr in self.entrypoint.items())
+        return self.export_node_names.values()
 
     def compile(self):
-        assert self.c_source_filename 
+        assert self.c_source_filename
         assert not self._compiled
-        export_symbols = [self.db.get(self.getentrypointptr()),
-                          'RPython_StartupCode',
-                          ]
-        if self.config.translation.countmallocs:
-            export_symbols.append('malloc_counters')
-        extsymeci = ExternalCompilationInfo(export_symbols=export_symbols)
-        self.eci = self.eci.merge(extsymeci)
+        self.eci = self.eci.merge(ExternalCompilationInfo(
+            export_symbols=self.getexportsymbols()))
 
         if sys.platform == 'win32':
             self.eci = self.eci.merge(ExternalCompilationInfo(
@@ -329,11 +326,62 @@
                                 ],
                 ))
 
-
         files = [self.c_source_filename] + self.extrafiles
-        self.translator.platform.compile(files, self.eci, standalone=False)
+
+        self.so_name = str(self.targetdir.join(self.modulename))
+        self.translator.platform.compile(
+            files, self.eci, standalone=False,
+            outputfilename=str(self.so_name)
+            )
         self._compiled = True
 
+    def make_import_module(self):
+        class Module:
+            pass
+        mod = Module()
+        mod.__file__ = self.so_name
+
+        forwards = []
+        node_names = self.export_node_names.values()
+        for node in self.db.globalcontainers():
+            if node.nodekind == 'func' and node.name in node_names:
+                forwards.append('\n'.join(node.forward_declaration()))
+
+        import_eci = ExternalCompilationInfo(
+            libraries = [self.so_name],
+            post_include_bits = forwards
+            )
+
+        for funcname, import_name in self.export_node_names.items():
+            functype = lltype.typeOf(self.entrypoint[funcname])
+            setattr(mod, funcname,
+                    rffi.llexternal(
+                        import_name, functype.TO.ARGS, functype.TO.RESULT,
+                        compilation_info=import_eci,
+                        ))
+        return mod
+
+    def gen_makefile(self, targetdir):
+        pass
+
+class CExtModuleBuilder(CSharedModuleBuilder):
+    "Build a CPython extension module"
+    _module = None
+    _wrapper = None
+
+    def getentrypointptr(self): # xxx
+        if self._wrapper is None:
+            self._wrapper = new_wrapper(self.entrypoint, self.translator)
+        return self._wrapper
+
+    def getexportsymbols(self):
+        export_symbols = [self.db.get(self.getentrypointptr()),
+                          'RPython_StartupCode',
+                          ]
+        if self.config.translation.countmallocs:
+            export_symbols.append('malloc_counters')
+        return export_symbols
+
     def _make_wrapper_module(self):
         fname = 'wrap_' + self.c_source_filename.purebasename
         modfile = self.c_source_filename.new(purebasename=fname, ext=".py")
@@ -399,9 +447,6 @@
         if isinstance(self._module, isolate.Isolate):
             isolate.close_isolate(self._module)
 
-    def gen_makefile(self, targetdir):
-        pass
-
 class CStandaloneBuilder(CBuilder):
     standalone = True
     executable_name = None

Added: pypy/branch/separate-compilation/pypy/translator/c/separate.py
==============================================================================
--- (empty file)
+++ pypy/branch/separate-compilation/pypy/translator/c/separate.py	Mon Jan 11 14:09:32 2010
@@ -0,0 +1,29 @@
+import types
+
+class export(object):
+    """decorator to mark a function as exported by a shared module.
+    Can be used with a signature::
+        @export([float, float])
+        def f(x, y):
+            return x + y
+    or without any argument at all::
+        @export
+        def f(x, y):
+            return x + y
+    in which case the function must be used somewhere else, which will
+    trigger its annotation."""
+    argtypes = None
+
+    def __new__(cls, *args, **kwds):
+        if len(args) == 1 and isinstance(args[0], types.FunctionType):
+            func = args[0]
+            return export()(func)
+        return object.__new__(cls, *args, **kwds)
+
+    def __init__(self, args=None):
+        self.argtypes = args
+
+    def __call__(self, func):
+        if self.argtypes is not None:
+            func.argtypes = self.argtypes
+        return func

Added: pypy/branch/separate-compilation/pypy/translator/c/test/test_separate.py
==============================================================================
--- (empty file)
+++ pypy/branch/separate-compilation/pypy/translator/c/test/test_separate.py	Mon Jan 11 14:09:32 2010
@@ -0,0 +1,101 @@
+from pypy.translator.c.separate import export
+from pypy.translator.translator import TranslationContext
+from pypy.translator.c.genc import CExtModuleBuilder, CSharedModuleBuilder, gen_forwarddecl
+from pypy.translator.tool.cbuild import ExternalCompilationInfo
+from pypy.rpython.typesystem import getfunctionptr
+from pypy.rpython.lltypesystem import rffi, lltype
+import py
+import sys, os
+
+class TestSeparation:
+    def compile_function(self, func, argtypes):
+        t = TranslationContext()
+        t.buildannotator().build_types(func, argtypes)
+        t.buildrtyper().specialize()
+        builder = CExtModuleBuilder(t, func, config=t.config)
+        builder.generate_source()
+        builder.compile()
+        return builder.get_entry_point()
+
+    def compile_separated(self, name, **exports):
+        t = TranslationContext()
+        t.buildannotator()
+        for funcname, func in exports.items():
+            if hasattr(func, 'argtypes'):
+                t.annotator.build_types(func, func.argtypes)
+        t.buildrtyper().specialize()
+
+        exported_funcptr = {}
+        for funcname, func in exports.items():
+            bk = t.annotator.bookkeeper
+            graph = bk.getdesc(func).getuniquegraph()
+            funcptr = getfunctionptr(graph)
+
+            exported_funcptr[funcname] = funcptr
+
+        builder = CSharedModuleBuilder(t, exported_funcptr, config=t.config)
+        builder.generate_source()
+        builder.compile()
+
+        mod = builder.make_import_module()
+        return mod
+
+    def test_simple_call(self):
+        # function exported from the 'first' module
+        @export(args=[float])
+        def f(x):
+            return x + 1.5
+        firstmodule = self.compile_separated("first", f=f)
+
+        # call it from a function compiled in another module
+        def fn():
+            return firstmodule.f(41.0)
+
+        assert fn() == 42.5
+        c_fn = self.compile_function(fn, [])
+        assert c_fn() == 42.5
+
+    def test_nested_call(self):
+        # function exported from the 'first' module
+        @export(args=[float])
+        def f(x):
+            return x + 1.5
+        firstmodule = self.compile_separated("first", f=f)
+
+        # function exported from the 'second' module
+        @export(args=[float])
+        def g(x):
+            return firstmodule.f(x) / 2
+        secondmodule = self.compile_separated("second", g=g)
+
+        # call it from a function compiled in another module
+        def fn():
+            return secondmodule.g(41)
+
+        if sys.platform == 'win32':
+            filepath = os.path.dirname(firstmodule.__file__)
+            os.environ['PATH'] = "%s;%s" % (filepath, os.environ['PATH'])
+
+        assert fn() == 21.25
+        c_fn = self.compile_function(fn, [])
+        assert c_fn() == 21.25
+
+    def test_implied_signature(self):
+        # function exported from the 'first' module
+        @export
+        def f(x):
+            return x + 1.5
+        @export(args=[])
+        def f2():
+            f(1.0)
+            f(2.0)
+        firstmodule = self.compile_separated("first", f=f, f2=f2)
+
+        # call it from a function compiled in another module
+        def fn():
+            return firstmodule.f(41)
+
+        assert fn() == 42.5
+        c_fn = self.compile_function(fn, [])
+        assert c_fn() == 42.5
+



More information about the Pypy-commit mailing list