Commits

Remi Meier committed 78e7bda

stm: turn inevitable before (in)direct_call, add transactionsafe flag

  • Participants
  • Parent commits 8dd73a8
  • Branches stm-thread-2

Comments (0)

Files changed (4)

pypy/rpython/lltypesystem/rffi.py

                sandboxsafe=False, threadsafe='auto',
                _nowrapper=False, calling_conv='c',
                oo_primitive=None, elidable_function=False,
-               macro=None, random_effects_on_gcobjs='auto'):
+               macro=None, random_effects_on_gcobjs='auto',
+               transactionsafe=False):
     """Build an external function that will invoke the C function 'name'
     with the given 'args' types and 'result' type.
 
             has_callback)               # because the callback can do it
 
     funcptr = lltype.functionptr(ext_type, name, external='C',
+                                 transactionsafe=transactionsafe,
                                  compilation_info=compilation_info,
                                  _callable=_callable,
                                  _safe_not_sandboxed=sandboxsafe,
     TYPES += ['__int128_t']
 except CompilationError:
     pass
-    
+
 _TYPES_ARE_UNSIGNED = set(['size_t', 'uintptr_t'])   # plus "unsigned *"
 if os.name != 'nt':
     TYPES.append('mode_t')

pypy/translator/stm/inevitable.py

-from pypy.rpython.lltypesystem import lltype, lloperation
+from pypy.rpython.lltypesystem import lltype, lloperation, rclass
 from pypy.translator.stm.writebarrier import is_immutable
 from pypy.objspace.flow.model import SpaceOperation, Constant
 from pypy.translator.unsimplify import varoftype
+from pypy.translator.simplify import get_funcobj
 
 
 ALWAYS_ALLOW_OPERATIONS = set([
-    'direct_call', 'force_cast', 'keepalive', 'cast_ptr_to_adr',
+    'force_cast', 'keepalive', 'cast_ptr_to_adr',
     'debug_print', 'debug_assert', 'cast_opaque_ptr', 'hint',
-    'indirect_call', 'stack_current', 'gc_stack_bottom',
+    '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',
     'jit_force_quasi_immutable', 'jit_marker', 'jit_is_virtual',
         return False
     return True
 
-def should_turn_inevitable(op):
+def should_turn_inevitable(op, block):
     # Always-allowed operations never cause a 'turn inevitable'
     if op.opname in ALWAYS_ALLOW_OPERATIONS:
         return False
     if op.opname in MALLOCS:
         flags = op.args[1].value
         return flags['flavor'] != 'gc'
+
+    #
+    # Function calls
+    if op.opname == 'direct_call':
+        funcptr = get_funcobj(op.args[0].value)
+        if not hasattr(funcptr, "external"):
+            return False
+        return not getattr(funcptr, "transactionsafe", False)
+
+    if op.opname == 'indirect_call':
+        tographs = op.args[-1].value
+        if tographs is not None:
+            # Set of RPython functions
+            return False
+        # special-case to detect 'instantiate'
+        v_func = op.args[0]
+        for op1 in block.operations:
+            if (v_func is op1.result and
+                op1.opname == 'getfield' and
+                op1.args[0].concretetype == rclass.CLASSTYPE and
+                op1.args[1].value == 'instantiate'):
+                return False
+        # unknown function
+        return True
+
     #
     # Entirely unsupported operations cause a 'turn inevitable'
     return True
     for block in graph.iterblocks():
         for i in range(len(block.operations)-1, -1, -1):
             op = block.operations[i]
-            if should_turn_inevitable(op):
+            if should_turn_inevitable(op, block):
                 inev_op = turn_inevitable_op(op.opname)
                 block.operations.insert(i, inev_op)

pypy/translator/stm/stmgcintf.py

 
 def _llexternal(name, args, result, **kwds):
     return rffi.llexternal(name, args, result, compilation_info=eci,
-                           _nowrapper=True, **kwds)
+                           _nowrapper=True, transactionsafe=True,
+                           **kwds)
 
 def smexternal(name, args, result):
     return staticmethod(_llexternal(name, args, result))

pypy/translator/stm/test/test_inevitable.py

-from pypy.rpython.lltypesystem import lltype, llmemory
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 from pypy.rpython.llinterp import LLFrame
 from pypy.rpython.test import test_llinterp
 from pypy.rpython.test.test_llinterp import get_interpreter, clear_tcache
 
         res = self.interpret_inevitable(f1, [])
         assert res == 'malloc'
+
+
+    def test_ext_direct_call_safe(self):
+        TYPE = lltype.FuncType([], lltype.Void)
+        extfunc = lltype.functionptr(TYPE, 'extfunc',
+                                     external='C',
+                                     transactionsafe=True,
+                                     _callable=lambda:0)
+        def f1():
+            extfunc()
+
+        res = self.interpret_inevitable(f1, [])
+        assert res is None
+
+
+    def test_ext_direct_call_unsafe(self):
+        TYPE = lltype.FuncType([], lltype.Void)
+        extfunc = lltype.functionptr(TYPE, 'extfunc',
+                                     external='C',
+                                     _callable=lambda:0)
+        def f1():
+            extfunc()
+
+        res = self.interpret_inevitable(f1, [])
+        assert res == 'direct_call'
+
+    def test_rpy_direct_call(self):
+        def f2():
+            pass
+        def f1():
+            f2()
+
+        res = self.interpret_inevitable(f1, [])
+        assert res is None
+
+    def test_rpy_indirect_call(self):
+        def f2():
+            pass
+        def f3():
+            pass
+        def f1(i):
+            if i:
+                f = f2
+            else:
+                f = f3
+            f()
+
+        res = self.interpret_inevitable(f1, [True])
+        assert res is None
+
+    def test_ext_indirect_call(self):
+        TYPE = lltype.FuncType([], lltype.Void)
+        extfunc = lltype.functionptr(TYPE, 'extfunc',
+                                     external='C',
+                                     _callable=lambda:0)
+        rpyfunc = lltype.functionptr(TYPE, 'rpyfunc',
+                                     _callable=lambda:0)
+
+
+        def f1(i):
+            if i:
+                f = extfunc
+            else:
+                f = rpyfunc
+            f()
+
+        res = self.interpret_inevitable(f1, [True])
+        assert res == 'indirect_call'
+
+    def test_instantiate_indirect_call(self):
+        # inits are necessary to generate indirect_call
+        class A:
+            def __init__(self): pass
+        class B(A):
+            def __init__(self): pass
+        class C(A):
+            def __init__(self): pass
+
+        def f1(i):
+            if i:
+                c = B
+            else:
+                c = C
+            c()
+
+        res = self.interpret_inevitable(f1, [True])
+        assert res is None
+
+