[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