[pypy-svn] r56234 - pypy/dist/pypy/rpython/lltypesystem

arigo at codespeak.net arigo at codespeak.net
Wed Jul 2 12:38:45 CEST 2008


Author: arigo
Date: Wed Jul  2 12:38:44 2008
New Revision: 56234

Modified:
   pypy/dist/pypy/rpython/lltypesystem/rffi.py
Log:
Whack whack whack even more.
Now, looking at the generated C code, we can see that
there is really no operation at all in the critical path except:

    - release GIL
    - call the C function with arguments that are all prepared earlier
    - acquire the GIL

I should write a test that check that this is really the case,
probably by looking at the graph.


Modified: pypy/dist/pypy/rpython/lltypesystem/rffi.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/rffi.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/rffi.py	Wed Jul  2 12:38:44 2008
@@ -101,85 +101,29 @@
         # sandboxsafe is a hint for "too-small-ness" (e.g. math functions).
         invoke_around_handlers = not sandboxsafe
 
-    unrolling_arg_tps = unrolling_iterable(enumerate(args))
-
-    def decode_args(*args):
-        """Decode the arguments passed to the external function,
-        automatically normalizing to the exact low-level types.
-        """
-        # XXX the next line is a workaround for the annotation bug
-        # shown in rpython.test.test_llann:test_pbctype.  Remove it
-        # when the test is fixed...
-        assert isinstance(lltype.Signed, lltype.Number)
-        real_args = ()
-        to_free = ()
-        for i, TARGET in unrolling_arg_tps:
-            arg = args[i]
-            freeme = None
-            if TARGET == CCHARP:
-                if arg is None:
-                    arg = lltype.nullptr(CCHARP.TO)   # None => (char*)NULL
-                    freeme = arg
-                elif isinstance(arg, str):
-                    arg = str2charp(arg)
-                    # XXX leaks if a str2charp() fails with MemoryError
-                    # and was not the first in this function
-                    freeme = arg
-            elif _isfunctype(TARGET) and not _isllptr(arg):
-                # XXX pass additional arguments
-                if invoke_around_handlers:
-                    arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg,
-                                                             aroundstate))
-                else:
-                    arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg))
-            else:
-                SOURCE = lltype.typeOf(arg)
-                if SOURCE != TARGET:
-                    if TARGET is lltype.Float:
-                        arg = float(arg)
-                    elif ((isinstance(SOURCE, lltype.Number)
-                           or SOURCE is lltype.Bool)
-                      and (isinstance(TARGET, lltype.Number)
-                           or TARGET is lltype.Bool)):
-                        arg = cast(TARGET, arg)
-            real_args = real_args + (arg,)
-            to_free = to_free + (freeme,)
-        return real_args, to_free
-    decode_args._annspecialcase_ = 'specialize:ll'
-    decode_args._always_inline_ = True
-
-    def decode_result(res, *to_free):
-        """Decode the result returned by the low-level function,
-        automatically normalizing it to a common RPython type
-        and freeing temporary values.
-        """
-        for i, TARGET in unrolling_arg_tps:
-            if to_free[i]:
-                lltype.free(to_free[i], flavor='raw')
-        if rarithmetic.r_int is not r_int:
-            if result is INT:
-                return cast(lltype.Signed, res)
-            elif result is UINT:
-                return cast(lltype.Unsigned, res)
-        return res
-    decode_result._annspecialcase_ = 'specialize:ll'
-    decode_result._always_inline_ = True
-
     # The actual wrapper is generated code in order to avoid *args.  The
     # issue is that if malloc-removal is not performed, we cannot write
     # a call to 'funcptr(*real_args)' because this needs to read values
     # from the GC-managed tuple 'real_args', which we cannot do between
     # before() and after() calls.
-
-    argnames = ', '.join(['a%d' % i for i in range(len(args))])
-    if len(args):
-        unpackargnames = '(' + argnames + ',)'
+    argnames =     ', '.join(['a%d' % i for i in range(len(args))])
+    realargnames = ', '.join(['r%d' % i for i in range(len(args))])
+    decode_lines = [
+        'r%d = _decode_arg(ARGS[%d], a%d, tag_invoke_around_handlers)'
+        % (i, i, i) for i in range(len(args))]
+    decode_lines = '; '.join(decode_lines)
+    free_lines = [
+        '_maybe_free_arg(ARGS[%d], a%d, r%d)'
+        % (i, i, i) for i in range(len(args))]
+    free_lines = '; '.join(free_lines)
+    if invoke_around_handlers:
+        tag_invoke_around_handlers = _tagged_true
     else:
-        unpackargnames = '_'
+        tag_invoke_around_handlers = _tagged_false
 
     source = py.code.Source("""
         def wrapper(%(argnames)s):
-            %(unpackargnames)s, to_free = decode_args(%(argnames)s)
+            %(decode_lines)s
             if invoke_around_handlers:
                 before = aroundstate.before
                 after = aroundstate.after
@@ -190,17 +134,26 @@
                 # and the end of the function, so that the external function
                 # calls below don't need to be guarded by GC shadow stack logic
                 # that would crash if not protected by the GIL!
-            res = funcptr(%(argnames)s)
+            res = funcptr(%(realargnames)s)
             if invoke_around_handlers:
                 if after: after()
-            return decode_result(res, *to_free)
+            %(free_lines)s
+            return _decode_result(RESULT, res)
     """ % locals())
 
-    miniglobals = {'decode_args':            decode_args,
+    # xxx workaround workaround: to avoid strange annotation issues,
+    # make one version of _decode_arg for each wrapper
+    my_decode_arg = func_with_new_name(_decode_arg, 'decode_arg')
+    my_maybe_free_arg = func_with_new_name(_maybe_free_arg, 'maybe_free_arg')
+    miniglobals = {'ARGS':                   args,
+                   'RESULT':                 result,
                    'invoke_around_handlers': invoke_around_handlers,
+                   'tag_invoke_around_handlers': tag_invoke_around_handlers,
                    'aroundstate':            aroundstate,
                    'funcptr':                funcptr,
-                   'decode_result':          decode_result,
+                   '_decode_arg':            my_decode_arg,
+                   '_maybe_free_arg':        my_maybe_free_arg,
+                   '_decode_result':         _decode_result,
                    }
     exec source.compile() in miniglobals
     wrapper = miniglobals['wrapper']
@@ -216,10 +169,67 @@
 
     return func_with_new_name(wrapper, name)
 
-def _make_wrapper_for(TP, callable, aroundstate=None):
+_tagged_false = lambda: None
+_tagged_true  = lambda: None
+
+def _decode_arg(TARGET, arg, tag_invoke_around_handlers):
+    """Decode one of the arguments passed to the external function,
+    automatically normalizing to the exact low-level type TARGET.
+    """
+    # XXX the next line is a workaround for the annotation bug
+    # shown in rpython.test.test_llann:test_pbctype.  Remove it
+    # when the test is fixed...
+    assert isinstance(lltype.Signed, lltype.Number)
+    if TARGET == CCHARP:
+        if arg is None:
+            arg = lltype.nullptr(CCHARP.TO)   # None => (char*)NULL
+        elif isinstance(arg, str):
+            arg = str2charp(arg)
+            # XXX leaks if a str2charp() fails with MemoryError
+            # and was not the first in this function
+    elif _isfunctype(TARGET) and not _isllptr(arg):
+        # XXX pass additional arguments
+        wrapper = _make_wrapper_for(TARGET, arg, tag_invoke_around_handlers)
+        arg = llhelper(TARGET, wrapper)
+    else:
+        SOURCE = lltype.typeOf(arg)
+        if SOURCE != TARGET:
+            if TARGET is lltype.Float:
+                arg = float(arg)
+            elif ((isinstance(SOURCE, lltype.Number)
+                   or SOURCE is lltype.Bool)
+              and (isinstance(TARGET, lltype.Number)
+                   or TARGET is lltype.Bool)):
+                arg = cast(TARGET, arg)
+    return arg
+_decode_arg._annspecialcase_ = 'specialize:ll'
+_decode_arg._always_inline_ = True
+
+def _maybe_free_arg(TARGET, original_arg, decoded_arg):
+    if TARGET == CCHARP:
+        if isinstance(original_arg, str):
+            lltype.free(decoded_arg, flavor='raw')
+_maybe_free_arg._annspecialcase_ = 'specialize:ll'
+_maybe_free_arg._always_inline_ = True
+
+def _decode_result(RESULT, res):
+    """Decode the result returned by the low-level function,
+    automatically normalizing it to a common RPython type.
+    """
+    if rarithmetic.r_int is not r_int:
+        if RESULT is INT:
+            return cast(lltype.Signed, res)
+        elif RESULT is UINT:
+            return cast(lltype.Unsigned, res)
+    return res
+_decode_result._annspecialcase_ = 'specialize:ll'
+_decode_result._always_inline_ = True
+
+def _make_wrapper_for(TP, callable, tag_invoke_around_handlers):
     """ Function creating wrappers for callbacks. Note that this is
     cheating as we assume constant callbacks and we just memoize wrappers
     """
+    invoke_around_handlers = tag_invoke_around_handlers is _tagged_true
     if hasattr(callable, '_errorcode_'):
         errorcode = callable._errorcode_
     else:
@@ -228,7 +238,7 @@
     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
-            if aroundstate is not None:
+            if invoke_around_handlers:
                 before = aroundstate.before
                 after = aroundstate.after
             else:
@@ -258,6 +268,7 @@
     miniglobals['Exception'] = Exception
     miniglobals['os'] = os
     miniglobals['we_are_translated'] = we_are_translated
+    miniglobals['aroundstate'] = aroundstate
     exec source.compile() in miniglobals
     return miniglobals['wrapper']
 _make_wrapper_for._annspecialcase_ = 'specialize:memo'



More information about the Pypy-commit mailing list