Author: Maciej Fijalkowski <[email protected]>
Branch: 
Changeset: r59775:df9336b282bb
Date: 2013-01-05 21:56 +0200
http://bitbucket.org/pypy/pypy/changeset/df9336b282bb/

Log:    merge callback-jit - this adds jitting to each callback that calls
        Python from C. Speeds up XML parsing using pyexpat quite a lot.
        Additionally adds some missing operations to the JIT and looks into
        pyexpat module

diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -1379,16 +1379,15 @@
         """The 'residual_call' operation is emitted in two cases:
         when we have to generate a residual CALL operation, but also
         to handle an indirect_call that may need to be inlined."""
-        assert isinstance(funcbox, Const)
-        sd = self.metainterp.staticdata
-        key = sd.cpu.ts.getaddr_for_box(funcbox)
-        jitcode = sd.bytecode_for_address(key)
-        if jitcode is not None:
-            # we should follow calls to this graph
-            return self.metainterp.perform_call(jitcode, argboxes)
-        else:
-            # but we should not follow calls to that graph
-            return self.do_residual_call(funcbox, argboxes, calldescr)
+        if isinstance(funcbox, Const):
+            sd = self.metainterp.staticdata
+            key = sd.cpu.ts.getaddr_for_box(funcbox)
+            jitcode = sd.bytecode_for_address(key)
+            if jitcode is not None:
+                # we should follow calls to this graph
+                return self.metainterp.perform_call(jitcode, argboxes)
+        # but we should not follow calls to that graph
+        return self.do_residual_call(funcbox, argboxes, calldescr)
 
 # ____________________________________________________________
 
diff --git a/pypy/jit/metainterp/test/test_ajit.py 
b/pypy/jit/metainterp/test/test_ajit.py
--- a/pypy/jit/metainterp/test/test_ajit.py
+++ b/pypy/jit/metainterp/test/test_ajit.py
@@ -3979,6 +3979,8 @@
             rgc.add_memory_pressure(1234)
             return 3
 
+        self.interp_operations(f, [])
+
     def test_external_call(self):
         from pypy.rlib.objectmodel import invoke_around_extcall
         
diff --git a/pypy/jit/metainterp/test/test_call.py 
b/pypy/jit/metainterp/test/test_call.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/test/test_call.py
@@ -0,0 +1,27 @@
+
+from pypy.jit.metainterp.test.support import LLJitMixin
+from pypy.rlib import jit
+
+class TestCall(LLJitMixin):
+    def test_indirect_call(self):
+        @jit.dont_look_inside
+        def f1(x):
+            return x + 1
+
+        @jit.dont_look_inside
+        def f2(x):
+            return x + 2
+
+        @jit.dont_look_inside
+        def choice(i):
+            if i:
+                return f1
+            return f2
+
+        def f(i):
+            func = choice(i)
+            return func(i)
+
+        res = self.interp_operations(f, [3])
+        assert res == f(3)
+    
diff --git a/pypy/jit/metainterp/test/test_warmspot.py 
b/pypy/jit/metainterp/test/test_warmspot.py
--- a/pypy/jit/metainterp/test/test_warmspot.py
+++ b/pypy/jit/metainterp/test/test_warmspot.py
@@ -519,6 +519,44 @@
         self.check_trace_count(1)
 
 
+    def test_callback_jit_merge_point(self):
+        from pypy.rlib.objectmodel import register_around_callback_hook
+        from pypy.rpython.lltypesystem import lltype, rffi
+        from pypy.translator.tool.cbuild import ExternalCompilationInfo
+        
+        callback_jit_driver = JitDriver(greens = ['name'], reds = 'auto')
+        
+        def callback_merge_point(name):
+            callback_jit_driver.jit_merge_point(name=name)
+    
+        @callback_jit_driver.inline(callback_merge_point)
+        def callback_hook(name):
+            pass
+
+        def callback(a, b):
+            if a > b:
+                return 1
+            return -1
+
+        CB_TP = rffi.CCallback([lltype.Signed, lltype.Signed], lltype.Signed)
+        eci = ExternalCompilationInfo(includes=['stdlib.h'])
+        qsort = rffi.llexternal('qsort',
+                                [rffi.VOIDP, lltype.Signed, lltype.Signed,
+                                 CB_TP], lltype.Void, compilation_info=eci)
+        ARR = rffi.CArray(lltype.Signed)
+
+        def main():
+            register_around_callback_hook(callback_hook)
+            raw = lltype.malloc(ARR, 10, flavor='raw')
+            for i in range(10):
+                raw[i] = 10 - i
+            qsort(raw, 10, rffi.sizeof(lltype.Signed), callback)
+            lltype.free(raw, flavor='raw')
+
+        self.meta_interp(main, [])
+        self.check_trace_count(1)
+
+
 class TestLLWarmspot(WarmspotTests, LLJitMixin):
     CPUClass = runner.LLGraphCPU
     type_system = 'lltype'
diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py
--- a/pypy/jit/metainterp/warmspot.py
+++ b/pypy/jit/metainterp/warmspot.py
@@ -290,11 +290,13 @@
         callgraph = inlinable_static_callers(self.translator.graphs, 
store_calls=True)
         new_callgraph = []
         new_portals = set()
+        inlined_jit_merge_points = set()
         for caller, block, op_call, callee in callgraph:
             func = getattr(callee, 'func', None)
             _inline_jit_merge_point_ = getattr(func, 
'_inline_jit_merge_point_', None)
             if _inline_jit_merge_point_:
                 _inline_jit_merge_point_._always_inline_ = True
+                inlined_jit_merge_points.add(_inline_jit_merge_point_)
                 op_jmp_call, jmp_graph = get_jmp_call(callee, 
_inline_jit_merge_point_)
                 #
                 # now we move the op_jmp_call from callee to caller, just
@@ -315,6 +317,9 @@
         # inline them!
         inline_threshold = 0.1 # we rely on the _always_inline_ set above
         auto_inlining(self.translator, inline_threshold, new_callgraph)
+        # clean up _always_inline_ = True, it can explode later
+        for item in inlined_jit_merge_points:
+            del item._always_inline_
 
         # make a fresh copy of the JitDriver in all newly created
         # jit_merge_points
@@ -1011,6 +1016,9 @@
         origblock.operations.append(newop)
         origblock.exitswitch = None
         origblock.recloseblock(Link([v_result], origportalgraph.returnblock))
+        # the origportal now can raise (even if it did not raise before),
+        # which means that we cannot inline it anywhere any more, but that's
+        # fine since any forced inlining has been done before
         #
         checkgraph(origportalgraph)
 
diff --git a/pypy/module/_cffi_backend/ccallback.py 
b/pypy/module/_cffi_backend/ccallback.py
--- a/pypy/module/_cffi_backend/ccallback.py
+++ b/pypy/module/_cffi_backend/ccallback.py
@@ -3,10 +3,10 @@
 """
 import os
 from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.rlib.objectmodel import compute_unique_id, keepalive_until_here
-from pypy.rlib import clibffi, rweakref, rgc
-from pypy.rlib.rarithmetic import r_ulonglong
+from pypy.rlib import clibffi, rweakref
+from pypy.rlib import jit
 
 from pypy.module._cffi_backend.cdataobj import W_CData
 from pypy.module._cffi_backend.ctypefunc import SIZE_OF_FFI_ARG, BIG_ENDIAN
@@ -77,6 +77,7 @@
                                  space.wrap("expected a function ctype"))
         return ctype
 
+    @jit.unroll_safe
     def invoke(self, ll_args):
         space = self.space
         ctype = self.getfunctype()
diff --git a/pypy/module/pypyjit/interp_jit.py 
b/pypy/module/pypyjit/interp_jit.py
--- a/pypy/module/pypyjit/interp_jit.py
+++ b/pypy/module/pypyjit/interp_jit.py
@@ -6,7 +6,7 @@
 from pypy.tool.pairtype import extendabletype
 from pypy.rlib.rarithmetic import r_uint, intmask
 from pypy.rlib.jit import JitDriver, hint, we_are_jitted, dont_look_inside
-from pypy.rlib import jit
+from pypy.rlib import jit, objectmodel
 from pypy.rlib.jit import current_trace_length, unroll_parameters
 import pypy.interpreter.pyopcode   # for side-effects
 from pypy.interpreter.error import OperationError, operationerrfmt
@@ -97,6 +97,15 @@
                                     is_being_profiled=self.is_being_profiled)
         return jumpto
 
+callback_jit_driver = JitDriver(greens = ['name'], reds = 'auto')
+
+def callback_merge_point(name):
+    callback_jit_driver.jit_merge_point(name=name)
+
+@callback_jit_driver.inline(callback_merge_point)
+def callback_hook(name):
+    pass
+
 def _get_adapted_tick_counter():
     # Normally, the tick counter is decremented by 100 for every
     # Python opcode.  Here, to better support JIT compilation of
diff --git a/pypy/module/pypyjit/policy.py b/pypy/module/pypyjit/policy.py
--- a/pypy/module/pypyjit/policy.py
+++ b/pypy/module/pypyjit/policy.py
@@ -106,7 +106,7 @@
                        'posix', '_socket', '_sre', '_lsprof', '_weakref',
                        '__pypy__', 'cStringIO', '_collections', 'struct',
                        'mmap', 'marshal', '_codecs', 'rctime', 'cppyy',
-                       '_cffi_backend']:
+                       '_cffi_backend', 'pyexpat']:
             if modname == 'pypyjit' and 'interp_resop' in rest:
                 return False
             return True
diff --git a/pypy/rlib/objectmodel.py b/pypy/rlib/objectmodel.py
--- a/pypy/rlib/objectmodel.py
+++ b/pypy/rlib/objectmodel.py
@@ -595,6 +595,16 @@
     llhelper(rffi.AroundFnPtr, before)
     llhelper(rffi.AroundFnPtr, after)
 
+def register_around_callback_hook(hook):
+    """ Register a hook that's called before a callback from C calls RPython.
+    Primary usage is for JIT to have 'started from' hook.
+    """
+    from pypy.rpython.lltypesystem import rffi
+    from pypy.rpython.annlowlevel import llhelper
+   
+    rffi.aroundstate.callback_hook = hook
+    llhelper(rffi.CallbackHookPtr, hook)
+
 def is_in_callback():
     from pypy.rpython.lltypesystem import rffi
     return rffi.stackcounter.stacks_counter > 1
diff --git a/pypy/rpython/lltypesystem/rffi.py 
b/pypy/rpython/lltypesystem/rffi.py
--- a/pypy/rpython/lltypesystem/rffi.py
+++ b/pypy/rpython/lltypesystem/rffi.py
@@ -1,6 +1,6 @@
 import py
 from pypy.annotation import model as annmodel
-from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.lltypesystem import lltype, rstr
 from pypy.rpython.lltypesystem import ll2ctypes
 from pypy.rpython.lltypesystem.llmemory import cast_adr_to_ptr, cast_ptr_to_adr
 from pypy.rpython.lltypesystem.llmemory import itemoffsetof, raw_memcopy
@@ -13,7 +13,7 @@
 from pypy.rlib.unroll import unrolling_iterable
 from pypy.rpython.tool.rfficache import platform, sizeof_c_type
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
-from pypy.rpython.annlowlevel import llhelper
+from pypy.rpython.annlowlevel import llhelper, llstr
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.rlib.rstring import StringBuilder, UnicodeBuilder, assert_str0
 from pypy.rlib import jit
@@ -279,9 +279,17 @@
     callable_name = getattr(callable, '__name__', '?')
     if callbackholder is not None:
         callbackholder.callbacks[callable] = True
+    callable_name_descr = str(callable).replace('"', '\\"')
     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
+        def inner_wrapper(%(args)s):
+            callback_hook = aroundstate.callback_hook
+            if callback_hook:
+                callback_hook(llstr("%(callable_name_descr)s"))
+            return callable(%(args)s)
+        inner_wrapper._never_inline_ = True
+        
+        def wrapper(%(args)s):    # no *args - no GIL for mallocing the tuple
             llop.gc_stack_bottom(lltype.Void)   # marker for trackgcroot.py
             if aroundstate is not None:
                 after = aroundstate.after
@@ -290,7 +298,7 @@
             # from now on we hold the GIL
             stackcounter.stacks_counter += 1
             try:
-                result = callable(%s)
+                result = inner_wrapper(%(args)s)
             except Exception, e:
                 os.write(2,
                     "Warning: uncaught exception in callback: %%s %%s\n" %%
@@ -308,10 +316,11 @@
             # by llexternal, it is essential that no exception checking occurs
             # after the call to before().
             return result
-    """ % (args, args))
+    """ % locals())
     miniglobals = locals().copy()
     miniglobals['Exception'] = Exception
     miniglobals['os'] = os
+    miniglobals['llstr'] = llstr
     miniglobals['we_are_translated'] = we_are_translated
     miniglobals['stackcounter'] = stackcounter
     exec source.compile() in miniglobals
@@ -319,10 +328,14 @@
 _make_wrapper_for._annspecialcase_ = 'specialize:memo'
 
 AroundFnPtr = lltype.Ptr(lltype.FuncType([], lltype.Void))
+CallbackHookPtr = lltype.Ptr(lltype.FuncType([lltype.Ptr(rstr.STR)], 
lltype.Void))
+
 class AroundState:
+    callback_hook = None
+    
     def _cleanup_(self):
-        self.before = None    # or a regular RPython function
-        self.after = None     # or a regular RPython function
+        self.before = None        # or a regular RPython function
+        self.after = None         # or a regular RPython function
 aroundstate = AroundState()
 aroundstate._cleanup_()
 
diff --git a/pypy/translator/goal/targetpypystandalone.py 
b/pypy/translator/goal/targetpypystandalone.py
--- a/pypy/translator/goal/targetpypystandalone.py
+++ b/pypy/translator/goal/targetpypystandalone.py
@@ -28,6 +28,11 @@
     w_call_startup_gateway = space.wrap(gateway.interp2app(call_startup))
     withjit = space.config.objspace.usemodules.pypyjit
 
+    if withjit:
+        from pypy.module.pypyjit.interp_jit import callback_hook
+        from pypy.rlib import objectmodel
+        objectmodel.register_around_callback_hook(callback_hook)
+
     def entry_point(argv):
         if withjit:
             from pypy.jit.backend.hlinfo import highleveljitinfo
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to