[pypy-commit] pypy stm-thread-2: merge heads

arigo noreply at buildbot.pypy.org
Tue Dec 11 15:54:20 CET 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: stm-thread-2
Changeset: r59399:40d946c71c64
Date: 2012-12-11 15:52 +0100
http://bitbucket.org/pypy/pypy/changeset/40d946c71c64/

Log:	merge heads

diff --git a/pypy/module/thread/ll_thread.py b/pypy/module/thread/ll_thread.py
--- a/pypy/module/thread/ll_thread.py
+++ b/pypy/module/thread/ll_thread.py
@@ -46,7 +46,8 @@
                                               # importantly, reacquire it
                                               # around the callback
 c_thread_get_ident = llexternal('RPyThreadGetIdent', [], rffi.LONG,
-                                _nowrapper=True)    # always call directly
+                                _nowrapper=True,
+                                transactionsafe=True)    # always call directly
 
 TLOCKP = rffi.COpaquePtr('struct RPyOpaque_ThreadLock',
                           compilation_info=eci)
diff --git a/pypy/rlib/rdtoa.py b/pypy/rlib/rdtoa.py
--- a/pypy/rlib/rdtoa.py
+++ b/pypy/rlib/rdtoa.py
@@ -52,9 +52,9 @@
     compilation_info=eci, sandboxsafe=True)
 
 def strtod(input):
-    end_ptr = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
+    ll_input = rffi.str2charp(input)
     try:
-        ll_input = rffi.str2charp(input)
+        end_ptr = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw')
         try:
             result = dg_strtod(ll_input, end_ptr)
 
@@ -66,9 +66,10 @@
 
             return result
         finally:
-            rffi.free_charp(ll_input)
+            lltype.free(end_ptr, flavor='raw')
     finally:
-        lltype.free(end_ptr, flavor='raw')
+        rffi.free_charp(ll_input)
+
 
 lower_special_strings = ['inf', '+inf', '-inf', 'nan']
 upper_special_strings = ['INF', '+INF', '-INF', 'NAN']
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
@@ -63,7 +63,8 @@
                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.
 
@@ -125,6 +126,7 @@
             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,
@@ -453,7 +455,7 @@
     TYPES += ['__int128_t']
 except CompilationError:
     pass
-    
+
 _TYPES_ARE_UNSIGNED = set(['size_t', 'uintptr_t'])   # plus "unsigned *"
 if os.name != 'nt':
     TYPES.append('mode_t')
@@ -712,6 +714,7 @@
             i -= 1
         return array
     str2charp._annenforceargs_ = [strtype]
+    str2charp._always_inline_ = True
 
     def free_charp(cp):
         lltype.free(cp, flavor='raw')
diff --git a/pypy/translator/stm/inevitable.py b/pypy/translator/stm/inevitable.py
--- a/pypy/translator/stm/inevitable.py
+++ b/pypy/translator/stm/inevitable.py
@@ -1,13 +1,14 @@
-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',
@@ -25,10 +26,9 @@
 SETTERS = set(['setfield', 'setarrayitem', 'setinteriorfield'])
 MALLOCS = set(['malloc', 'malloc_varsize',
                'malloc_nonmovable', 'malloc_nonmovable_varsize'])
-
 # ____________________________________________________________
 
-def should_turn_inevitable_getter_setter(op):
+def should_turn_inevitable_getter_setter(op, fresh_mallocs):
     # Getters and setters are allowed if their first argument is a GC pointer.
     # If it is a RAW pointer, and it is a read from a non-immutable place,
     # and it doesn't use the hint 'stm_dont_track_raw_accesses', then they
@@ -40,9 +40,9 @@
         return False
     if S._hints.get('stm_dont_track_raw_accesses', False):
         return False
-    return True
+    return not fresh_mallocs.is_fresh_malloc(op.args[0])
 
-def should_turn_inevitable(op):
+def should_turn_inevitable(op, block, fresh_mallocs):
     # Always-allowed operations never cause a 'turn inevitable'
     if op.opname in ALWAYS_ALLOW_OPERATIONS:
         return False
@@ -51,16 +51,49 @@
     if op.opname in GETTERS:
         if op.result.concretetype is lltype.Void:
             return False
-        return should_turn_inevitable_getter_setter(op)
+        return should_turn_inevitable_getter_setter(op, fresh_mallocs)
     if op.opname in SETTERS:
         if op.args[-1].concretetype is lltype.Void:
             return False
-        return should_turn_inevitable_getter_setter(op)
+        return should_turn_inevitable_getter_setter(op, fresh_mallocs)
     #
-    # Mallocs
+    # Mallocs & Frees
     if op.opname in MALLOCS:
-        flags = op.args[1].value
-        return flags['flavor'] != 'gc'
+        # flags = op.args[1].value
+        # return flags['flavor'] != 'gc'
+        return False # XXX: Produces memory leaks on aborts
+    if op.opname == 'free':
+        # We can only run a CFG in non-inevitable mode from start
+        # to end in one transaction (every free gets called once
+        # for every fresh malloc). No need to turn inevitable.
+        # If the transaction is splitted, the remaining parts of the
+        # CFG will always run in inevitable mode anyways.
+        return not fresh_mallocs.is_fresh_malloc(op.args[0])
+
+    #
+    # 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
@@ -71,10 +104,12 @@
     return SpaceOperation('stm_become_inevitable', [c_info],
                           varoftype(lltype.Void))
 
-def insert_turn_inevitable(translator, graph):
+def insert_turn_inevitable(graph):
+    from pypy.translator.backendopt.writeanalyze import FreshMallocs
+    fresh_mallocs = FreshMallocs(graph)
     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, fresh_mallocs):
                 inev_op = turn_inevitable_op(op.opname)
                 block.operations.insert(i, inev_op)
diff --git a/pypy/translator/stm/stmgcintf.py b/pypy/translator/stm/stmgcintf.py
--- a/pypy/translator/stm/stmgcintf.py
+++ b/pypy/translator/stm/stmgcintf.py
@@ -17,7 +17,8 @@
 
 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))
diff --git a/pypy/translator/stm/test/test_inevitable.py b/pypy/translator/stm/test/test_inevitable.py
--- a/pypy/translator/stm/test/test_inevitable.py
+++ b/pypy/translator/stm/test/test_inevitable.py
@@ -1,4 +1,4 @@
-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
@@ -20,7 +20,7 @@
         interp, self.graph = get_interpreter(fn, args, view=False)
         interp.frame_class = LLSTMInevFrame
         self.translator = interp.typer.annotator.translator
-        insert_turn_inevitable(self.translator, self.graph)
+        insert_turn_inevitable(self.graph)
         if option.view:
             self.translator.view()
         #
@@ -113,4 +113,103 @@
             lltype.free(p, flavor='raw')
 
         res = self.interpret_inevitable(f1, [])
-        assert res == 'malloc'
+        assert res is None
+        assert 0, """we do not turn inevitable before
+        raw-mallocs which causes leaks on aborts"""
+
+    def test_unknown_raw_free(self):
+        X = lltype.Struct('X', ('foo', lltype.Signed))
+        def f2(p):
+            lltype.free(p, flavor='raw')
+
+        res = self.interpret_inevitable(f2, [lltype.malloc(X, flavor='raw')])
+        assert res == 'free'
+
+
+    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
+
+
diff --git a/pypy/translator/stm/transform2.py b/pypy/translator/stm/transform2.py
--- a/pypy/translator/stm/transform2.py
+++ b/pypy/translator/stm/transform2.py
@@ -25,7 +25,7 @@
         from pypy.translator.stm.inevitable import insert_turn_inevitable
         #
         for graph in self.translator.graphs:
-            insert_turn_inevitable(self.translator, graph)
+            insert_turn_inevitable(graph)
 
     def start_log(self):
         from pypy.translator.c.support import log


More information about the pypy-commit mailing list