[pypy-svn] r69735 - in pypy/branch/virtual-forcing/pypy: annotation rpython/lltypesystem rpython/lltypesystem/test translator/backendopt translator/backendopt/test translator/stackless

arigo at codespeak.net arigo at codespeak.net
Sun Nov 29 16:38:50 CET 2009


Author: arigo
Date: Sun Nov 29 16:38:49 2009
New Revision: 69735

Modified:
   pypy/branch/virtual-forcing/pypy/annotation/description.py
   pypy/branch/virtual-forcing/pypy/rpython/lltypesystem/rffi.py
   pypy/branch/virtual-forcing/pypy/rpython/lltypesystem/test/test_rffi.py
   pypy/branch/virtual-forcing/pypy/translator/backendopt/canraise.py
   pypy/branch/virtual-forcing/pypy/translator/backendopt/graphanalyze.py
   pypy/branch/virtual-forcing/pypy/translator/backendopt/test/test_canraise.py
   pypy/branch/virtual-forcing/pypy/translator/backendopt/test/test_writeanalyze.py
   pypy/branch/virtual-forcing/pypy/translator/backendopt/writeanalyze.py
   pypy/branch/virtual-forcing/pypy/translator/stackless/transform.py
Log:
issue487 testing

(pedronis, arigo)
Fix, mostly for the WriteAnalyzer and the VirtualizableAnalyzer:
record exactly which RPython callback functions a given llexternal
function can invoke, and use this information in the GraphAnalyzer.



Modified: pypy/branch/virtual-forcing/pypy/annotation/description.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/annotation/description.py	(original)
+++ pypy/branch/virtual-forcing/pypy/annotation/description.py	Sun Nov 29 16:38:49 2009
@@ -202,6 +202,9 @@
             graph.name = alt_name
         return graph
 
+    def getgraphs(self):
+        return self._cache.values()
+
     def getuniquegraph(self):
         if len(self._cache) != 1:
             raise NoStandardGraph(self)

Modified: pypy/branch/virtual-forcing/pypy/rpython/lltypesystem/rffi.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/rpython/lltypesystem/rffi.py	(original)
+++ pypy/branch/virtual-forcing/pypy/rpython/lltypesystem/rffi.py	Sun Nov 29 16:38:49 2009
@@ -57,7 +57,7 @@
 def llexternal(name, args, result, _callable=None,
                compilation_info=ExternalCompilationInfo(),
                sandboxsafe=False, threadsafe='auto',
-               canraise=False, _nowrapper=False, calling_conv='c',
+               _nowrapper=False, calling_conv='c',
                oo_primitive=None, pure_function=False):
     """Build an external function that will invoke the C function 'name'
     with the given 'args' types and 'result' type.
@@ -68,6 +68,10 @@
     pointing to a read-only null-terminated character of arrays, as usual
     for C.
 
+    The C function can have callbacks, but they must be specified explicitly
+    as constant RPython functions.  We don't support yet C functions that
+    invoke callbacks passed otherwise (e.g. set by a previous C call).
+
     threadsafe: whether it's ok to release the GIL around the call.
                 Default is yes, unless sandboxsafe is set, in which case
                 we consider that the function is really short-running and
@@ -84,12 +88,22 @@
     kwds = {}
     if oo_primitive:
         kwds['oo_primitive'] = oo_primitive
+
+    has_callback = False
+    for ARG in args:
+        if _isfunctype(ARG):
+            has_callback = True
+    if has_callback:
+        kwds['_callbacks'] = callbackholder = CallbackHolder()
+    else:
+        callbackholder = None
+
     funcptr = lltype.functionptr(ext_type, name, external='C',
                                  compilation_info=compilation_info,
                                  _callable=_callable,
                                  _safe_not_sandboxed=sandboxsafe,
                                  _debugexc=True, # on top of llinterp
-                                 canraise=canraise,
+                                 canraise=False,
                                  **kwds)
     if isinstance(_callable, ll2ctypes.LL2CtypesCallable):
         _callable.funcptr = funcptr
@@ -170,9 +184,11 @@
                 # XXX pass additional arguments
                 if invoke_around_handlers:
                     arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg,
+                                                             callbackholder,
                                                              aroundstate))
                 else:
-                    arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg))
+                    arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg,
+                                                             callbackholder))
             else:
                 SOURCE = lltype.typeOf(arg)
                 if SOURCE != TARGET:
@@ -202,7 +218,11 @@
 
     return func_with_new_name(wrapper, name)
 
-def _make_wrapper_for(TP, callable, aroundstate=None):
+class CallbackHolder:
+    def __init__(self):
+        self.callbacks = {}
+
+def _make_wrapper_for(TP, callable, callbackholder, aroundstate=None):
     """ Function creating wrappers for callbacks. Note that this is
     cheating as we assume constant callbacks and we just memoize wrappers
     """
@@ -213,6 +233,7 @@
     else:
         errorcode = TP.TO.RESULT._example()
     callable_name = getattr(callable, '__name__', '?')
+    callbackholder.callbacks[callable] = True
     args = ', '.join(['a%d' % i for i in range(len(TP.TO.ARGS))])
     source = py.code.Source(r"""
         def wrapper(%s):    # no *args - no GIL for mallocing the tuple

Modified: pypy/branch/virtual-forcing/pypy/rpython/lltypesystem/test/test_rffi.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/rpython/lltypesystem/test/test_rffi.py	(original)
+++ pypy/branch/virtual-forcing/pypy/rpython/lltypesystem/test/test_rffi.py	Sun Nov 29 16:38:49 2009
@@ -387,6 +387,7 @@
 
         fn = self.compile(f, [])
         assert fn() == 6
+        assert eating_callback._ptr._obj._callbacks.callbacks == {g: True}
 
     def test_double_callback(self):
         eating_callback = self.eating_callback()
@@ -406,6 +407,8 @@
         fn = self.compile(f, [int])
         assert fn(4) == 4
         assert fn(1) == 3
+        assert eating_callback._ptr._obj._callbacks.callbacks == {one: True,
+                                                                  two: True}
 
     def test_exception_callback(self):
         eating_callback = self.eating_callback()

Modified: pypy/branch/virtual-forcing/pypy/translator/backendopt/canraise.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/translator/backendopt/canraise.py	(original)
+++ pypy/branch/virtual-forcing/pypy/translator/backendopt/canraise.py	Sun Nov 29 16:38:49 2009
@@ -17,7 +17,7 @@
             log.WARNING("Unknown operation: %s" % op.opname)
             return True
 
-    def analyze_external_call(self, op):
+    def analyze_external_call(self, op, seen=None):
         fnobj = get_funcobj(op.args[0].value)
         return getattr(fnobj, 'canraise', True)
 

Modified: pypy/branch/virtual-forcing/pypy/translator/backendopt/graphanalyze.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/translator/backendopt/graphanalyze.py	(original)
+++ pypy/branch/virtual-forcing/pypy/translator/backendopt/graphanalyze.py	Sun Nov 29 16:38:49 2009
@@ -1,4 +1,4 @@
-from pypy.translator.simplify import get_graph
+from pypy.translator.simplify import get_graph, get_funcobj
 from pypy.rpython.lltypesystem.lloperation import llop, LL_OPERATIONS
 from pypy.rpython.lltypesystem import lltype
 
@@ -38,8 +38,17 @@
     def analyze_startblock(self, block, seen=None):
         return self.bottom_result()
 
-    def analyze_external_call(self, op):
-        return self.top_result()
+    def analyze_external_call(self, op, seen=None):
+        funcobj = get_funcobj(op.args[0].value)
+        result = self.bottom_result()
+        if hasattr(funcobj, '_callbacks'):
+            bk = self.translator.annotator.bookkeeper
+            for function in funcobj._callbacks.callbacks:
+                desc = bk.getdesc(function)
+                for graph in desc.getgraphs():
+                    result = self.join_two_results(
+                        result, self.analyze_direct_call(graph, seen))
+        return result
 
     def analyze_external_method(self, op, TYPE, meth):
         return self.top_result()
@@ -59,7 +68,7 @@
         if op.opname == "direct_call":
             graph = get_graph(op.args[0], self.translator)
             if graph is None:
-                return self.analyze_external_call(op)
+                return self.analyze_external_call(op, seen)
             return self.analyze_direct_call(graph, seen)
         elif op.opname == "indirect_call":
             if op.args[-1].value is None:

Modified: pypy/branch/virtual-forcing/pypy/translator/backendopt/test/test_canraise.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/translator/backendopt/test/test_canraise.py	(original)
+++ pypy/branch/virtual-forcing/pypy/translator/backendopt/test/test_canraise.py	Sun Nov 29 16:38:49 2009
@@ -189,7 +189,8 @@
         result = ra.can_raise(fgraph.startblock.operations[0])
         assert not result
 
-        z = llexternal('z', [lltype.Signed], lltype.Signed, canraise=True)
+        z = lltype.functionptr(lltype.FuncType([lltype.Signed], lltype.Signed),
+                               'foobar')
         def g(x):
             return z(x)
         t, ra = self.translate(g, [int])

Modified: pypy/branch/virtual-forcing/pypy/translator/backendopt/test/test_writeanalyze.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/translator/backendopt/test/test_writeanalyze.py	(original)
+++ pypy/branch/virtual-forcing/pypy/translator/backendopt/test/test_writeanalyze.py	Sun Nov 29 16:38:49 2009
@@ -178,6 +178,31 @@
         assert name == "length"
         assert S1 is S2
 
+    def test_llexternal_with_callback(self):
+        from pypy.rpython.lltypesystem.rffi import llexternal
+        from pypy.rpython.lltypesystem import lltype
+
+        class Abc:
+            pass
+        abc = Abc()
+
+        FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
+        z = llexternal('z', [lltype.Ptr(FUNC)], lltype.Signed)
+        def g(n):
+            abc.foobar = n
+            return n + 1
+        def f(x):
+            return z(g)
+        t, wa = self.translate(f, [int])
+        fgraph = graphof(t, f)
+        backend_optimizations(t)
+        assert fgraph.startblock.operations[0].opname == 'direct_call'
+
+        result = wa.analyze(fgraph.startblock.operations[0])
+        assert len(result) == 1
+        (struct, T, name), = result
+        assert struct == "struct"
+        assert name.endswith("foobar")
 
 
 class TestOOtype(BaseTestCanRaise):

Modified: pypy/branch/virtual-forcing/pypy/translator/backendopt/writeanalyze.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/translator/backendopt/writeanalyze.py	(original)
+++ pypy/branch/virtual-forcing/pypy/translator/backendopt/writeanalyze.py	Sun Nov 29 16:38:49 2009
@@ -37,9 +37,6 @@
     def _array_result(self, TYPE):
         return frozenset([("array", TYPE)])
 
-    def analyze_external_call(self, op):
-        return self.bottom_result() # an external call cannot change anything
-
     def analyze_external_method(self, op, TYPE, meth):
         if isinstance(TYPE, ootype.Array):
             methname = op.args[0].value

Modified: pypy/branch/virtual-forcing/pypy/translator/stackless/transform.py
==============================================================================
--- pypy/branch/virtual-forcing/pypy/translator/stackless/transform.py	(original)
+++ pypy/branch/virtual-forcing/pypy/translator/stackless/transform.py	Sun Nov 29 16:38:49 2009
@@ -260,7 +260,7 @@
             return  LL_OPERATIONS[op.opname].canunwindgc
         return False
 
-    def analyze_external_call(self, op):
+    def analyze_external_call(self, op, seen=None):
         # An external call cannot cause a stack unwind
         # Note that this is essential to get good performance in framework GCs
         # because there is a pseudo-external call to ROUND_UP_FOR_ALLOCATION



More information about the Pypy-commit mailing list