Author: Maciej Fijalkowski <fij...@gmail.com> Branch: Changeset: r45737:10d2740f6ab6 Date: 2011-07-19 12:24 +0200 http://bitbucket.org/pypy/pypy/changeset/10d2740f6ab6/
Log: (fijal, alex_gaynor) merge release-gil-flush-heapcache diff --git a/pypy/jit/codewriter/call.py b/pypy/jit/codewriter/call.py --- a/pypy/jit/codewriter/call.py +++ b/pypy/jit/codewriter/call.py @@ -5,10 +5,9 @@ from pypy.jit.codewriter import support from pypy.jit.codewriter.jitcode import JitCode -from pypy.jit.codewriter.effectinfo import VirtualizableAnalyzer -from pypy.jit.codewriter.effectinfo import QuasiImmutAnalyzer -from pypy.jit.codewriter.effectinfo import effectinfo_from_writeanalyze -from pypy.jit.codewriter.effectinfo import EffectInfo, CallInfoCollection +from pypy.jit.codewriter.effectinfo import (VirtualizableAnalyzer, + QuasiImmutAnalyzer, CanReleaseGILAnalyzer, effectinfo_from_writeanalyze, + EffectInfo, CallInfoCollection) from pypy.translator.simplify import get_funcobj, get_functype from pypy.rpython.lltypesystem import lltype, llmemory from pypy.translator.backendopt.canraise import RaiseAnalyzer @@ -32,6 +31,7 @@ self.readwrite_analyzer = ReadWriteAnalyzer(translator) self.virtualizable_analyzer = VirtualizableAnalyzer(translator) self.quasiimmut_analyzer = QuasiImmutAnalyzer(translator) + self.canreleasegil_analyzer = CanReleaseGILAnalyzer(translator) # for index, jd in enumerate(jitdrivers_sd): jd.index = index @@ -219,7 +219,9 @@ assert not NON_VOID_ARGS, ("arguments not supported for " "loop-invariant function!") # build the extraeffect - can_invalidate = self.quasiimmut_analyzer.analyze(op) + can_release_gil = self.canreleasegil_analyzer.analyze(op) + # can_release_gil implies can_invalidate + can_invalidate = can_release_gil or self.quasiimmut_analyzer.analyze(op) if extraeffect is None: if self.virtualizable_analyzer.analyze(op): extraeffect = EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE @@ -235,7 +237,7 @@ # effectinfo = effectinfo_from_writeanalyze( self.readwrite_analyzer.analyze(op), self.cpu, extraeffect, - oopspecindex, can_invalidate) + oopspecindex, can_invalidate, can_release_gil) # if oopspecindex != EffectInfo.OS_NONE: assert effectinfo is not None diff --git a/pypy/jit/codewriter/effectinfo.py b/pypy/jit/codewriter/effectinfo.py --- a/pypy/jit/codewriter/effectinfo.py +++ b/pypy/jit/codewriter/effectinfo.py @@ -79,13 +79,15 @@ write_descrs_fields, write_descrs_arrays, extraeffect=EF_CAN_RAISE, oopspecindex=OS_NONE, - can_invalidate=False): + can_invalidate=False, can_release_gil=False): key = (frozenset(readonly_descrs_fields), frozenset(readonly_descrs_arrays), frozenset(write_descrs_fields), frozenset(write_descrs_arrays), extraeffect, - oopspecindex) + oopspecindex, + can_invalidate, + can_release_gil) if key in cls._cache: return cls._cache[key] result = object.__new__(cls) @@ -100,6 +102,7 @@ result.write_descrs_arrays = write_descrs_arrays result.extraeffect = extraeffect result.can_invalidate = can_invalidate + result.can_release_gil = can_release_gil result.oopspecindex = oopspecindex cls._cache[key] = result return result @@ -111,12 +114,13 @@ return self.extraeffect >= self.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE def has_random_effects(self): - return self.oopspecindex == self.OS_LIBFFI_CALL + return self.oopspecindex == self.OS_LIBFFI_CALL or self.can_release_gil def effectinfo_from_writeanalyze(effects, cpu, extraeffect=EffectInfo.EF_CAN_RAISE, oopspecindex=EffectInfo.OS_NONE, - can_invalidate=False): + can_invalidate=False, + can_release_gil=False): from pypy.translator.backendopt.writeanalyze import top_set if effects is top_set: return None @@ -158,7 +162,8 @@ write_descrs_arrays, extraeffect, oopspecindex, - can_invalidate) + can_invalidate, + can_release_gil) def consider_struct(TYPE, fieldname): if fieldType(TYPE, fieldname) is lltype.Void: @@ -194,6 +199,16 @@ def analyze_simple_operation(self, op, graphinfo): return op.opname == 'jit_force_quasi_immutable' +class CanReleaseGILAnalyzer(BoolGraphAnalyzer): + def analyze_direct_call(self, graph, seen=None): + releases_gil = False + if hasattr(graph, "func") and hasattr(graph.func, "_ptr"): + releases_gil = graph.func._ptr._obj.releases_gil + return releases_gil or super(CanReleaseGILAnalyzer, self).analyze_direct_call(graph, seen) + + def analyze_simple_operation(self, op, graphinfo): + return False + # ____________________________________________________________ class CallInfoCollection(object): diff --git a/pypy/jit/codewriter/support.py b/pypy/jit/codewriter/support.py --- a/pypy/jit/codewriter/support.py +++ b/pypy/jit/codewriter/support.py @@ -185,7 +185,7 @@ return llop.int_floordiv(lltype.Signed, x, y) def _ll_2_int_floordiv_ovf(x, y): - if x == -sys.maxint - 1 and y == -1: + if x == -sys.maxint - 1 and y == -1: raise OverflowError return llop.int_floordiv(lltype.Signed, x, y) @@ -222,7 +222,7 @@ return -x else: return x - + # math support # ------------ diff --git a/pypy/jit/codewriter/test/test_call.py b/pypy/jit/codewriter/test/test_call.py --- a/pypy/jit/codewriter/test/test_call.py +++ b/pypy/jit/codewriter/test/test_call.py @@ -1,6 +1,6 @@ import py from pypy.objspace.flow.model import SpaceOperation, Constant, Variable -from pypy.rpython.lltypesystem import lltype +from pypy.rpython.lltypesystem import lltype, rffi from pypy.translator.unsimplify import varoftype from pypy.rlib import jit from pypy.jit.codewriter.call import CallControl @@ -103,7 +103,7 @@ op = SpaceOperation('direct_call', [Constant(object())], Variable()) - assert cc.guess_call_kind(op) == 'residual' + assert cc.guess_call_kind(op) == 'residual' class funcptr: class graph: @@ -118,7 +118,7 @@ op = SpaceOperation('direct_call', [Constant(funcptr)], Variable()) res = cc.graphs_from(op) - assert res == [g] + assert res == [g] assert cc.guess_call_kind(op) == 'regular' class funcptr: @@ -126,7 +126,7 @@ op = SpaceOperation('direct_call', [Constant(funcptr)], Variable()) res = cc.graphs_from(op) - assert res is None + assert res is None assert cc.guess_call_kind(op) == 'residual' h = object() @@ -142,7 +142,7 @@ Variable()) res = cc.graphs_from(op) assert res is None - assert cc.guess_call_kind(op) == 'residual' + assert cc.guess_call_kind(op) == 'residual' # ____________________________________________________________ @@ -171,3 +171,24 @@ def test_jit_force_virtualizable_effectinfo(): py.test.skip("XXX add a test for CallControl.getcalldescr() -> EF_xxx") + +def test_releases_gil_analyzer(): + from pypy.jit.backend.llgraph.runner import LLtypeCPU + + T = rffi.CArrayPtr(rffi.TIME_T) + external = rffi.llexternal("time", [T], rffi.TIME_T, threadsafe=True) + + @jit.dont_look_inside + def f(): + return external(lltype.nullptr(T.TO)) + + rtyper = support.annotate(f, []) + jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0]) + cc = CallControl(LLtypeCPU(rtyper), jitdrivers_sd=[jitdriver_sd]) + res = cc.find_all_graphs(FakePolicy()) + + [f_graph] = [x for x in res if x.func is f] + [block, _] = list(f_graph.iterblocks()) + [op] = block.operations + call_descr = cc.getcalldescr(op) + assert call_descr.extrainfo.can_release_gil \ No newline at end of file diff --git a/pypy/jit/metainterp/optimizeopt/__init__.py b/pypy/jit/metainterp/optimizeopt/__init__.py --- a/pypy/jit/metainterp/optimizeopt/__init__.py +++ b/pypy/jit/metainterp/optimizeopt/__init__.py @@ -61,7 +61,6 @@ optimizations, unroll = build_opt_chain(metainterp_sd, enable_opts, inline_short_preamble, retraced) - if unroll: optimize_unroll(metainterp_sd, loop, optimizations) else: 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 @@ -11,7 +11,7 @@ from pypy import conftest from pypy.rlib.rarithmetic import ovfcheck from pypy.jit.metainterp.typesystem import LLTypeHelper, OOTypeHelper -from pypy.rpython.lltypesystem import lltype, llmemory +from pypy.rpython.lltypesystem import lltype, llmemory, rffi from pypy.rpython.ootypesystem import ootype from pypy.jit.metainterp.optimizeopt import ALL_OPTS_DICT from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin @@ -1616,8 +1616,6 @@ assert res == 1 def test_raw_malloc_and_access(self): - from pypy.rpython.lltypesystem import rffi - TP = rffi.CArray(lltype.Signed) def f(n): @@ -1631,8 +1629,6 @@ assert res == 10 def test_raw_malloc_and_access_float(self): - from pypy.rpython.lltypesystem import rffi - TP = rffi.CArray(lltype.Float) def f(n, f): @@ -2061,7 +2057,7 @@ myjitdriver.jit_merge_point(a=a, b=b, n=n, sa=sa) if 0 < a <= 5: pass if 0 < b <= 5: pass - sa += (((((a << b) << b) << b) >> b) >> b) >> b + sa += (((((a << b) << b) << b) >> b) >> b) >> b n += 1 return sa @@ -2071,10 +2067,10 @@ myjitdriver.jit_merge_point(a=a, b=b, n=n, sa=sa) if 0 < a < promote(sys.maxint/2): pass if 0 < b < 100: pass - sa += (((((a << b) << b) << b) >> b) >> b) >> b + sa += (((((a << b) << b) << b) >> b) >> b) >> b n += 1 return sa - + assert self.meta_interp(f1, [5, 5]) == 50 self.check_loops(int_rshift=0, everywhere=True) @@ -2106,7 +2102,7 @@ myjitdriver.jit_merge_point(a=a, b=b, n=n, sa=sa) if -5 <= a < 0: pass if 0 < b <= 5: pass - sa += (((((a << b) << b) << b) >> b) >> b) >> b + sa += (((((a << b) << b) << b) >> b) >> b) >> b n += 1 return sa @@ -2116,10 +2112,10 @@ myjitdriver.jit_merge_point(a=a, b=b, n=n, sa=sa) if -promote(sys.maxint/2) < a < 0: pass if 0 < b < 100: pass - sa += (((((a << b) << b) << b) >> b) >> b) >> b + sa += (((((a << b) << b) << b) >> b) >> b) >> b n += 1 return sa - + assert self.meta_interp(f1, [-5, 5]) == -50 self.check_loops(int_rshift=0, everywhere=True) @@ -2190,7 +2186,7 @@ def get_printable_location(i): return str(i) - + myjitdriver = JitDriver(greens = ['i'], reds = ['j', 'c', 'a'], get_printable_location=get_printable_location) bytecode = "0j10jc20a3" @@ -2299,7 +2295,7 @@ assert self.meta_interp(build, []) == 7 self.check_loops(getfield_gc_pure=0) self.check_loops(getfield_gc_pure=2, everywhere=True) - + def test_frame_finished_during_retrace(self): class Base(object): pass @@ -2330,7 +2326,7 @@ return sa res = self.meta_interp(f, []) assert res == f() - + def test_frame_finished_during_continued_retrace(self): class Base(object): pass @@ -2414,12 +2410,12 @@ def g(n1, n2): for i in range(10): f(n1) - for i in range(10): + for i in range(10): f(n2) nn = [10, 3] assert self.meta_interp(g, nn) == g(*nn) - + # The attempts of retracing first loop will end up retracing the # second and thus fail 5 times, saturating the retrace_count. Instead a # bridge back to the preamble of the first loop is produced. A guard in @@ -2430,7 +2426,7 @@ self.check_tree_loop_count(2 + 3) # FIXME: Add a gloabl retrace counter and test that we are not trying more than 5 times. - + def g(n): for i in range(n): for j in range(10): @@ -2619,5 +2615,41 @@ self.meta_interp(f, [], enable_opts='') self.check_loops(new_with_vtable=1) + def test_release_gil_flush_heap_cache(self): + T = rffi.CArrayPtr(rffi.TIME_T) + + external = rffi.llexternal("time", [T], rffi.TIME_T, threadsafe=True) + # Not a real lock, has all the same properties with respect to GIL + # release though, so good for this test. + class Lock(object): + @dont_look_inside + def acquire(self): + external(lltype.nullptr(T.TO)) + @dont_look_inside + def release(self): + external(lltype.nullptr(T.TO)) + class X(object): + def __init__(self, idx): + self.field = idx + @dont_look_inside + def get_obj(z): + return X(z) + myjitdriver = JitDriver(greens=[], reds=["n", "l", "z", "lock"]) + def f(n, z): + lock = Lock() + l = 0 + while n > 0: + myjitdriver.jit_merge_point(lock=lock, l=l, n=n, z=z) + x = get_obj(z) + l += x.field + lock.acquire() + # This must not reuse the previous one. + n -= x.field + lock.release() + return n + res = self.meta_interp(f, [10, 1]) + self.check_loops(getfield_gc=2) + + class TestLLtype(BaseLLtypeTests, LLJitMixin): pass 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 @@ -102,19 +102,6 @@ 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=False, - **kwds) - if isinstance(_callable, ll2ctypes.LL2CtypesCallable): - _callable.funcptr = funcptr - - if _nowrapper: - return funcptr - if threadsafe in (False, True): # invoke the around-handlers, which release the GIL, if and only if # the C function is thread-safe. @@ -125,6 +112,21 @@ # sandboxsafe is a hint for "too-small-ness" (e.g. math functions). invoke_around_handlers = not sandboxsafe + 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=False, + releases_gil=invoke_around_handlers, + **kwds) + if isinstance(_callable, ll2ctypes.LL2CtypesCallable): + _callable.funcptr = funcptr + + if _nowrapper: + return funcptr + + if invoke_around_handlers: # The around-handlers are releasing the GIL in a threaded pypy. # We need tons of care to ensure that no GC operation and no _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit