[pypy-commit] pypy stmgc-c4: try to reduce becomeinevitable because of indirect_calls in blackhole.py (call_stub)

Raemi noreply at buildbot.pypy.org
Wed Sep 11 20:05:59 CEST 2013


Author: Remi Meier <remi.meier at gmail.com>
Branch: stmgc-c4
Changeset: r66906:32a78cfb3fbc
Date: 2013-09-11 20:04 +0200
http://bitbucket.org/pypy/pypy/changeset/32a78cfb3fbc/

Log:	try to reduce becomeinevitable because of indirect_calls in
	blackhole.py (call_stub)

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


More information about the pypy-commit mailing list