Author: Armin Rigo <ar...@tunes.org> Branch: stmgc-c4 Changeset: r66910:4c548ac9cced Date: 2013-09-11 20:17 +0200 http://bitbucket.org/pypy/pypy/changeset/4c548ac9cced/
Log: merge heads diff --git a/rpython/jit/backend/llsupport/descr.py b/rpython/jit/backend/llsupport/descr.py --- a/rpython/jit/backend/llsupport/descr.py +++ b/rpython/jit/backend/llsupport/descr.py @@ -404,12 +404,27 @@ category = 'i' else: assert 0 - source = py.code.Source(""" - def call_stub(func, args_i, args_r, args_f): - fnptr = rffi.cast(lltype.Ptr(FUNC), func) - res = support.maybe_on_top_of_llinterp(rtyper, fnptr)(%(args)s) - return %(result)s - """ % locals()) + + llop1 = llop + if not stm or ( + self.extrainfo and self.extrainfo.call_needs_inevitable()): + source = py.code.Source(""" + def call_stub(func, args_i, args_r, args_f): + fnptr = rffi.cast(lltype.Ptr(FUNC), func) + res = support.maybe_on_top_of_llinterp(rtyper, fnptr)(%(args)s) + return %(result)s + """ % locals()) + else: + # the above 'source' works on STM too, but always forces + # the transaction to become inevitable. Using jit_assembler_call + # in cases where it is not needed avoids that. + source = py.code.Source(""" + def call_stub(func, args_i, args_r, args_f): + fnptr = rffi.cast(lltype.Ptr(FUNC), func) + fun = support.maybe_on_top_of_llinterp(rtyper, fnptr) + res = llop1.jit_assembler_call(RESULT, fun, %(args)s) + return %(result)s + """ % locals()) ARGS = [TYPE(arg) for arg in self.arg_classes] FUNC = lltype.FuncType(ARGS, RESULT) d = globals().copy() diff --git a/rpython/jit/backend/llsupport/stmrewrite.py b/rpython/jit/backend/llsupport/stmrewrite.py --- a/rpython/jit/backend/llsupport/stmrewrite.py +++ b/rpython/jit/backend/llsupport/stmrewrite.py @@ -1,4 +1,5 @@ from rpython.jit.backend.llsupport.rewrite import GcRewriterAssembler +from rpython.jit.backend.llsupport.descr import CallDescr from rpython.jit.metainterp.resoperation import ResOperation, rop from rpython.jit.metainterp.history import BoxPtr, ConstPtr, ConstInt from rpython.rlib.objectmodel import specialize @@ -108,7 +109,13 @@ elif op.getopnum() == rop.CALL_ASSEMBLER: self.handle_call_assembler(op) else: - self.newops.append(op) + descr = op.getdescr() + assert not descr or isinstance(descr, CallDescr) + if descr and descr.get_extra_info() and \ + descr.get_extra_info().call_needs_inevitable(): + self.fallback_inevitable(op) + else: + self.newops.append(op) self.known_category.clear() continue # ---------- copystrcontent ---------- diff --git a/rpython/jit/backend/llsupport/test/test_descr.py b/rpython/jit/backend/llsupport/test/test_descr.py --- a/rpython/jit/backend/llsupport/test/test_descr.py +++ b/rpython/jit/backend/llsupport/test/test_descr.py @@ -353,6 +353,32 @@ descr5f = get_call_descr(c0, [lltype.Char], lltype.SingleFloat) assert repr_of_descr(descr5f) == '<CallS 4 i>' + +def test_call_stubs_inevitable(): + for inev in (True, False): + class fakeextrainfo: + def call_needs_inevitable(self): + return inev + class fakertyper: + class annotator: + class translator: + class config: + class translation: + stm = True + + c0 = GcCache(False, rtyper=fakertyper()) + ARGS = [lltype.Char, lltype.Signed] + RES = lltype.Char + descr1 = get_call_descr(c0, ARGS, RES, extrainfo=fakeextrainfo()) + def f(a, b): + return 'c' + + fnptr = llhelper(lltype.Ptr(lltype.FuncType(ARGS, RES)), f) + + res = descr1.call_stub_i(rffi.cast(lltype.Signed, fnptr), + [1, 2], None, None) + assert res == ord('c') + def test_call_stubs_1(): c0 = GcCache(False) ARGS = [lltype.Char, lltype.Signed] diff --git a/rpython/jit/backend/llsupport/test/test_stmrewrite.py b/rpython/jit/backend/llsupport/test/test_stmrewrite.py --- a/rpython/jit/backend/llsupport/test/test_stmrewrite.py +++ b/rpython/jit/backend/llsupport/test/test_stmrewrite.py @@ -62,6 +62,29 @@ RewriteTests.check_rewrite(self, frm_operations, to_operations, **namespace) + def test_inevitable_calls(self): + c1 = GcCache(True) + T = lltype.GcStruct('T') + U = lltype.GcStruct('U', ('x', lltype.Signed)) + for inev in (True, False): + class fakeextrainfo: + def call_needs_inevitable(self): + return inev + + calldescr = get_call_descr(c1, [lltype.Ptr(T)], lltype.Ptr(U), + fakeextrainfo()) + + self.check_rewrite(""" + [] + call(123, descr=cd) + jump() + """,""" + [] + %s + call(123, descr=cd) + jump() + """ % ("$INEV" if inev else "",), cd=calldescr) + def test_rewrite_one_setfield_gc(self): self.check_rewrite(""" [p1, p2] diff --git a/rpython/jit/codewriter/call.py b/rpython/jit/codewriter/call.py --- a/rpython/jit/codewriter/call.py +++ b/rpython/jit/codewriter/call.py @@ -196,6 +196,12 @@ elidable = False loopinvariant = False call_release_gil_target = llmemory.NULL + needs_inevitable = False + if op.opname == 'indirect_call' or op.opname == 'direct_call': + from rpython.translator.stm.inevitable import ( + should_turn_inevitable_call) + needs_inevitable = bool(should_turn_inevitable_call(op)) + if op.opname == "direct_call": funcobj = op.args[0].value._obj assert getattr(funcobj, 'calling_conv', 'c') == 'c', ( @@ -206,7 +212,8 @@ if loopinvariant: assert not NON_VOID_ARGS, ("arguments not supported for " "loop-invariant function!") - if getattr(func, "_call_aroundstate_target_", None): + funcptr = getattr(func, "_call_aroundstate_target_", None) + if funcptr: call_release_gil_target = func._call_aroundstate_target_ call_release_gil_target = llmemory.cast_ptr_to_adr( call_release_gil_target) @@ -234,6 +241,7 @@ effectinfo = effectinfo_from_writeanalyze( self.readwrite_analyzer.analyze(op), self.cpu, extraeffect, oopspecindex, can_invalidate, call_release_gil_target, + needs_inevitable ) # assert effectinfo is not None diff --git a/rpython/jit/codewriter/effectinfo.py b/rpython/jit/codewriter/effectinfo.py --- a/rpython/jit/codewriter/effectinfo.py +++ b/rpython/jit/codewriter/effectinfo.py @@ -96,14 +96,16 @@ extraeffect=EF_CAN_RAISE, oopspecindex=OS_NONE, can_invalidate=False, - call_release_gil_target=llmemory.NULL): + call_release_gil_target=llmemory.NULL, + needs_inevitable=False): key = (frozenset_or_none(readonly_descrs_fields), frozenset_or_none(readonly_descrs_arrays), frozenset_or_none(write_descrs_fields), frozenset_or_none(write_descrs_arrays), extraeffect, oopspecindex, - can_invalidate) + can_invalidate, + needs_inevitable) if call_release_gil_target: key += (object(),) # don't care about caching in this case if key in cls._cache: @@ -131,6 +133,7 @@ result.write_descrs_arrays = write_descrs_arrays result.extraeffect = extraeffect result.can_invalidate = can_invalidate + result.needs_inevitable = needs_inevitable result.oopspecindex = oopspecindex result.call_release_gil_target = call_release_gil_target if result.check_can_raise(): @@ -157,6 +160,9 @@ def is_call_release_gil(self): return bool(self.call_release_gil_target) + def call_needs_inevitable(self): + return self.needs_inevitable + def frozenset_or_none(x): if x is None: @@ -172,7 +178,8 @@ extraeffect=EffectInfo.EF_CAN_RAISE, oopspecindex=EffectInfo.OS_NONE, can_invalidate=False, - call_release_gil_target=llmemory.NULL): + call_release_gil_target=llmemory.NULL, + needs_inevitable=False): from rpython.translator.backendopt.writeanalyze import top_set if effects is top_set or extraeffect == EffectInfo.EF_RANDOM_EFFECTS: readonly_descrs_fields = None @@ -221,7 +228,8 @@ extraeffect, oopspecindex, can_invalidate, - call_release_gil_target) + call_release_gil_target, + needs_inevitable) def consider_struct(TYPE, fieldname): if fieldType(TYPE, fieldname) is lltype.Void: diff --git a/rpython/jit/codewriter/test/test_call.py b/rpython/jit/codewriter/test/test_call.py --- a/rpython/jit/codewriter/test/test_call.py +++ b/rpython/jit/codewriter/test/test_call.py @@ -198,6 +198,33 @@ call_descr = cc.getcalldescr(op) assert call_descr.extrainfo.has_random_effects() assert call_descr.extrainfo.is_call_release_gil() is False + assert call_descr.extrainfo.call_needs_inevitable() is False + +def test_releases_gil_analyzer_needs_inevitable(): + from rpython.jit.backend.llgraph.runner import LLGraphCPU + + for transactionsafe in (True, False): + T = rffi.CArrayPtr(rffi.TIME_T) + external = rffi.llexternal("time", [T], rffi.TIME_T, + _nowrapper=True, + threadsafe=False, + transactionsafe=transactionsafe) + + @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(LLGraphCPU(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.is_call_release_gil() is False + needs_inev = not transactionsafe + assert call_descr.extrainfo.call_needs_inevitable() is needs_inev def test_call_release_gil(): from rpython.jit.backend.llgraph.runner import LLGraphCPU diff --git a/rpython/translator/stm/inevitable.py b/rpython/translator/stm/inevitable.py --- a/rpython/translator/stm/inevitable.py +++ b/rpython/translator/stm/inevitable.py @@ -7,7 +7,9 @@ ALWAYS_ALLOW_OPERATIONS = set([ 'force_cast', 'keepalive', 'cast_ptr_to_adr', 'cast_adr_to_int', - 'debug_print', 'debug_assert', 'cast_opaque_ptr', 'hint', + 'debug_print', 'debug_assert', + 'debug_start', 'debug_stop', 'have_debug_prints', + 'cast_opaque_ptr', 'hint', 'stack_current', 'gc_stack_bottom', 'cast_current_ptr_to_int', # this variant of 'cast_ptr_to_int' is ok 'jit_force_virtual', 'jit_force_virtualizable', @@ -53,6 +55,29 @@ return False return not fresh_mallocs.is_fresh_malloc(op.args[0]) +def should_turn_inevitable_call(op): + if op.opname == 'direct_call': + funcptr = op.args[0].value._obj + if not hasattr(funcptr, "external"): + return False + if getattr(funcptr, "transactionsafe", False): + return False + try: + return funcptr._name + '()' + except AttributeError: + return True + + elif op.opname == 'indirect_call': + tographs = op.args[-1].value + if tographs is not None: + # Set of RPython functions + return False + # unknown function + return True + + assert False + + def should_turn_inevitable(op, block, fresh_mallocs): # Always-allowed operations never cause a 'turn inevitable' if op.opname in ALWAYS_ALLOW_OPERATIONS: @@ -80,25 +105,8 @@ return not fresh_mallocs.is_fresh_malloc(op.args[0]) # # Function calls - if op.opname == 'direct_call': - funcptr = op.args[0].value._obj - if not hasattr(funcptr, "external"): - return False - if getattr(funcptr, "transactionsafe", False): - return False - try: - return funcptr._name + '()' - except AttributeError: - return True - - if op.opname == 'indirect_call': - tographs = op.args[-1].value - if tographs is not None: - # Set of RPython functions - return False - # unknown function - return True - + if op.opname == 'direct_call' or op.opname == 'indirect_call': + return should_turn_inevitable_call(op) # # Entirely unsupported operations cause a 'turn inevitable' return True _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit