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