[pypy-commit] pypy default: Merge branch 'no-release-gil':

arigo noreply at buildbot.pypy.org
Thu Sep 5 22:42:58 CEST 2013


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r66816:c86260fa25c1
Date: 2013-08-30 12:47 +0200
http://bitbucket.org/pypy/pypy/changeset/c86260fa25c1/

Log:	Merge branch 'no-release-gil':

	 Adds @rgc.no_release_gil and puts it around the JIT assembler
	functions

diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -77,12 +77,12 @@
         raise NotImplementedError("only for interp-level user subclasses "
                                   "from typedef.py")
 
-    def getname(self, space, default='?'):
+    def getname(self, space):
         try:
             return space.str_w(space.getattr(self, space.wrap('__name__')))
         except OperationError, e:
             if e.match(space, space.w_TypeError) or e.match(space, space.w_AttributeError):
-                return default
+                return '?'
             raise
 
     def getaddrstring(self, space):
diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py
--- a/pypy/interpreter/function.py
+++ b/pypy/interpreter/function.py
@@ -482,17 +482,16 @@
                 space.abstract_isinstance_w(w_firstarg, self.w_class)):
             pass  # ok
         else:
-            clsdescr = self.w_class.getname(space, "")
-            if clsdescr:
+            clsdescr = self.w_class.getname(space)
+            if clsdescr and clsdescr != '?':
                 clsdescr += " instance"
             else:
                 clsdescr = "instance"
             if w_firstarg is None:
                 instdescr = "nothing"
             else:
-                instname = space.abstract_getclass(w_firstarg).getname(space,
-                                                                       "")
-                if instname:
+                instname = space.abstract_getclass(w_firstarg).getname(space)
+                if instname and instname != '?':
                     instdescr = instname + " instance"
                 else:
                     instdescr = "instance"
diff --git a/pypy/interpreter/test/test_argument.py b/pypy/interpreter/test/test_argument.py
--- a/pypy/interpreter/test/test_argument.py
+++ b/pypy/interpreter/test/test_argument.py
@@ -128,7 +128,7 @@
 
     def type(self, obj):
         class Type:
-            def getname(self, space, default='?'):
+            def getname(self, space):
                 return type(obj).__name__
         return Type()
 
diff --git a/pypy/module/_weakref/interp__weakref.py b/pypy/module/_weakref/interp__weakref.py
--- a/pypy/module/_weakref/interp__weakref.py
+++ b/pypy/module/_weakref/interp__weakref.py
@@ -175,8 +175,8 @@
             state = '; dead'
         else:
             typename = space.type(w_obj).getname(space)
-            objname = w_obj.getname(space, '')
-            if objname:
+            objname = w_obj.getname(space)
+            if objname and objname != '?':
                 state = "; to '%s' (%s)" % (typename, objname)
             else:
                 state = "; to '%s'" % (typename,)
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -597,8 +597,8 @@
         if num1 != num2:
             lt = num1      # if obj1 is a number, it is Lower Than obj2
         else:
-            name1 = w_typ1.getname(space, "")
-            name2 = w_typ2.getname(space, "")
+            name1 = w_typ1.getname(space)
+            name2 = w_typ2.getname(space)
             if name1 != name2:
                 lt = name1 < name2
             else:
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -494,6 +494,12 @@
         else:
             return w_self.name
 
+    def getname(w_self, space):
+        name = w_self.name
+        if name is None:
+            name = '?'
+        return name
+
     def add_subclass(w_self, w_subclass):
         space = w_self.space
         if not space.config.translation.rweakref:
diff --git a/rpython/jit/backend/arm/assembler.py b/rpython/jit/backend/arm/assembler.py
--- a/rpython/jit/backend/arm/assembler.py
+++ b/rpython/jit/backend/arm/assembler.py
@@ -29,6 +29,7 @@
 from rpython.rtyper.annlowlevel import llhelper, cast_instance_to_gcref
 from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.jit.backend.arm import callbuilder
+from rpython.rtyper.lltypesystem.lloperation import llop
 
 class AssemblerARM(ResOpAssembler):
 
@@ -1488,7 +1489,9 @@
 
 
 def not_implemented(msg):
-    os.write(2, '[ARM/asm] %s\n' % msg)
+    msg = '[ARM/asm] %s\n' % msg
+    if we_are_translated():
+        llop.debug_print(lltype.Void, msg)
     raise NotImplementedError(msg)
 
 
diff --git a/rpython/jit/backend/llsupport/regalloc.py b/rpython/jit/backend/llsupport/regalloc.py
--- a/rpython/jit/backend/llsupport/regalloc.py
+++ b/rpython/jit/backend/llsupport/regalloc.py
@@ -2,6 +2,8 @@
 from rpython.jit.metainterp.history import Const, Box, REF, JitCellToken
 from rpython.rlib.objectmodel import we_are_translated, specialize
 from rpython.jit.metainterp.resoperation import rop
+from rpython.rtyper.lltypesystem import lltype
+from rpython.rtyper.lltypesystem.lloperation import llop
 
 try:
     from collections import OrderedDict
@@ -753,5 +755,7 @@
 
 
 def not_implemented(msg):
-    os.write(2, '[llsupport/regalloc] %s\n' % msg)
+    msg = '[llsupport/regalloc] %s\n' % msg
+    if we_are_translated():
+        llop.debug_print(lltype.Void, msg)
     raise NotImplementedError(msg)
diff --git a/rpython/jit/backend/x86/assembler.py b/rpython/jit/backend/x86/assembler.py
--- a/rpython/jit/backend/x86/assembler.py
+++ b/rpython/jit/backend/x86/assembler.py
@@ -434,6 +434,7 @@
         else:
             self.wb_slowpath[withcards + 2 * withfloats] = rawstart
 
+    @rgc.no_release_gil
     def assemble_loop(self, logger, loopname, inputargs, operations, looptoken,
                       log):
         '''adds the following attributes to looptoken:
@@ -513,6 +514,7 @@
         return AsmInfo(ops_offset, rawstart + looppos,
                        size_excluding_failure_stuff - looppos)
 
+    @rgc.no_release_gil
     def assemble_bridge(self, logger, faildescr, inputargs, operations,
                         original_loop_token, log):
         if not we_are_translated():
@@ -2388,7 +2390,9 @@
     return AddressLoc(ImmedLoc(addr), imm0, 0, 0)
 
 def not_implemented(msg):
-    os.write(2, '[x86/asm] %s\n' % msg)
+    msg = '[x86/asm] %s\n' % msg
+    if we_are_translated():
+        llop.debug_print(lltype.Void, msg)
     raise NotImplementedError(msg)
 
 cond_call_register_arguments = [edi, esi, edx, ecx]
diff --git a/rpython/jit/backend/x86/regalloc.py b/rpython/jit/backend/x86/regalloc.py
--- a/rpython/jit/backend/x86/regalloc.py
+++ b/rpython/jit/backend/x86/regalloc.py
@@ -28,6 +28,7 @@
 from rpython.rlib.rarithmetic import r_longlong, r_uint
 from rpython.rtyper.annlowlevel import cast_instance_to_gcref
 from rpython.rtyper.lltypesystem import lltype, rffi, rstr
+from rpython.rtyper.lltypesystem.lloperation import llop
 
 
 class X86RegisterManager(RegisterManager):
@@ -1375,7 +1376,9 @@
     return base_ofs + WORD * (position + JITFRAME_FIXED_SIZE)
 
 def not_implemented(msg):
-    os.write(2, '[x86/regalloc] %s\n' % msg)
+    msg = '[x86/regalloc] %s\n' % msg
+    if we_are_translated():
+        llop.debug_print(lltype.Void, msg)
     raise NotImplementedError(msg)
 
 # xxx hack: set a default value for TargetToken._ll_loop_code.
diff --git a/rpython/jit/backend/x86/runner.py b/rpython/jit/backend/x86/runner.py
--- a/rpython/jit/backend/x86/runner.py
+++ b/rpython/jit/backend/x86/runner.py
@@ -1,6 +1,7 @@
 import py
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 from rpython.rlib.jit_hooks import LOOP_RUN_CONTAINER
+from rpython.rlib import rgc
 from rpython.jit.backend.x86.assembler import Assembler386
 from rpython.jit.backend.x86.regalloc import gpr_reg_mgr_cls, xmm_reg_mgr_cls
 from rpython.jit.backend.x86.profagent import ProfileAgent
@@ -63,10 +64,12 @@
         assert self.assembler is not None
         return RegAlloc(self.assembler, False)
 
+    @rgc.no_release_gil
     def setup_once(self):
         self.profile_agent.startup()
         self.assembler.setup_once()
 
+    @rgc.no_release_gil
     def finish_once(self):
         self.assembler.finish_once()
         self.profile_agent.shutdown()
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -262,7 +262,11 @@
     keepalive_until_here(newp)
     return newp
 
-
+def no_release_gil(func):
+    func._dont_inline_ = True
+    func._no_release_gil_ = True
+    return func
+    
 def no_collect(func):
     func._dont_inline_ = True
     func._gc_no_collect_ = True
diff --git a/rpython/translator/backendopt/all.py b/rpython/translator/backendopt/all.py
--- a/rpython/translator/backendopt/all.py
+++ b/rpython/translator/backendopt/all.py
@@ -10,6 +10,7 @@
 from rpython.translator.backendopt.removeassert import remove_asserts
 from rpython.translator.backendopt.support import log
 from rpython.translator.backendopt.storesink import storesink_graph
+from rpython.translator.backendopt import gilanalysis
 from rpython.flowspace.model import checkgraph
 
 INLINE_THRESHOLD_FOR_TEST = 33
@@ -138,6 +139,9 @@
     for graph in graphs:
         checkgraph(graph)
 
+    gilanalysis.analyze(graphs, translator)
+
+
 def constfold(config, graphs):
     if config.constfold:
         for graph in graphs:
diff --git a/rpython/translator/backendopt/gilanalysis.py b/rpython/translator/backendopt/gilanalysis.py
new file mode 100644
--- /dev/null
+++ b/rpython/translator/backendopt/gilanalysis.py
@@ -0,0 +1,60 @@
+from rpython.translator.backendopt import graphanalyze        
+
+# This is not an optimization. It checks for possible releases of the
+# GIL in all graphs starting from rgc.no_release_gil.
+
+
+class GilAnalyzer(graphanalyze.BoolGraphAnalyzer):
+    
+    def analyze_direct_call(self, graph, seen=None):
+        try:
+            func = graph.func
+        except AttributeError:
+            pass
+        else:
+            if getattr(func, '_gctransformer_hint_close_stack_', False):
+                return True
+            if getattr(func, '_transaction_break_', False):
+                return True
+      
+        return graphanalyze.BoolGraphAnalyzer.analyze_direct_call(
+            self, graph, seen)
+
+    def analyze_external_call(self, op, seen=None):
+        funcobj = op.args[0].value._obj
+        if getattr(funcobj, 'transactionsafe', False):
+            return False
+        else:
+            return False
+
+    def analyze_instantiate_call(self, seen=None):
+        return False
+                
+    def analyze_simple_operation(self, op, graphinfo):
+        return False
+
+def analyze(graphs, translator):
+    gilanalyzer = GilAnalyzer(translator)
+    for graph in graphs:
+        func = getattr(graph, 'func', None)
+        if func and getattr(func, '_no_release_gil_', False):
+            if gilanalyzer.analyze_direct_call(graph):
+                # 'no_release_gil' function can release the gil
+                import cStringIO
+                err = cStringIO.StringIO()
+                import sys
+                prev = sys.stdout
+                try:
+                    sys.stdout = err
+                    ca = GilAnalyzer(translator)
+                    ca.verbose = True
+                    ca.analyze_direct_call(graph)  # print the "traceback" here
+                    sys.stdout = prev
+                except:
+                    sys.stdout = prev
+                # ^^^ for the dump of which operation in which graph actually
+                # causes it to return True
+                raise Exception("'no_release_gil' function can release the GIL:"
+                                " %s\n%s" % (func, err.getvalue()))
+
+        
diff --git a/rpython/translator/backendopt/graphanalyze.py b/rpython/translator/backendopt/graphanalyze.py
--- a/rpython/translator/backendopt/graphanalyze.py
+++ b/rpython/translator/backendopt/graphanalyze.py
@@ -1,6 +1,7 @@
 from rpython.rtyper.lltypesystem.lltype import DelayedPointer
 from rpython.translator.simplify import get_graph
 from rpython.tool.algo.unionfind import UnionFind
+from rpython.rtyper.lltypesystem import rclass
 
 
 class GraphAnalyzer(object):
@@ -67,6 +68,9 @@
                         result, self.analyze_direct_call(graph, seen))
         return result
 
+    def analyze_instantiate_call(self, seen=None):
+        return self.top_result()
+
     def analyze_link(self, graph, link):
         return self.bottom_result()
 
@@ -75,7 +79,7 @@
     def compute_graph_info(self, graph):
         return None
 
-    def analyze(self, op, seen=None, graphinfo=None):
+    def analyze(self, op, seen=None, graphinfo=None, block=None):
         if op.opname == "direct_call":
             graph = get_graph(op.args[0], self.translator)
             if graph is None:
@@ -90,6 +94,18 @@
         elif op.opname == "indirect_call":
             graphs = op.args[-1].value
             if graphs is None:
+                if block is not None:
+                    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'):
+                            x = self.analyze_instantiate_call(seen)
+                            if self.verbose and x:
+                                self.dump_info('analyze_instantiate(%s): %r' % (
+                                    graphs, x))
+                            return x
                 if self.verbose:
                     self.dump_info('%s to unknown' % (op,))
                 return self.top_result()
@@ -127,7 +143,7 @@
                 for op in block.operations:
                     result = self.add_to_result(
                         result,
-                        self.analyze(op, seen, graphinfo)
+                        self.analyze(op, seen, graphinfo, block=block)
                     )
                     if self.is_top_result(result):
                         break
@@ -161,7 +177,7 @@
             graphs = self.translator.graphs
         for graph in graphs:
             for block, op in graph.iterblockops():
-                self.analyze(op)
+                self.analyze(op, block=block)
 
 
 class Dependency(object):
diff --git a/rpython/translator/backendopt/test/test_gilanalysis.py b/rpython/translator/backendopt/test/test_gilanalysis.py
new file mode 100644
--- /dev/null
+++ b/rpython/translator/backendopt/test/test_gilanalysis.py
@@ -0,0 +1,80 @@
+import py
+
+from rpython.annotator.listdef import s_list_of_strings
+from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.translator.backendopt import gilanalysis
+from rpython.memory.gctransform.test.test_transform import rtype
+from rpython.translator.translator import graphof
+
+def test_canrelease_external():
+    for ths in ['auto', True, False]:
+        for sbxs in [True, False]:
+            fext = rffi.llexternal('fext2', [], lltype.Void, 
+                                   threadsafe=ths, sandboxsafe=sbxs)
+            def g():
+                fext()
+            t = rtype(g, [])
+            gg = graphof(t, g)
+
+            releases = (ths == 'auto' and not sbxs) or ths is True
+            assert releases == gilanalysis.GilAnalyzer(t).analyze_direct_call(gg)
+
+def test_canrelease_instantiate():
+    class O:
+        pass
+    class A(O):
+        pass
+    class B(O):
+        pass
+
+    classes = [A, B]
+    def g(i):
+        classes[i]()
+
+    t = rtype(g, [int])
+    gg = graphof(t, g)
+    assert not gilanalysis.GilAnalyzer(t).analyze_direct_call(gg)
+
+
+
+def test_no_release_gil():
+    from rpython.rlib import rgc
+
+    @rgc.no_release_gil
+    def g():
+        return 1
+
+    assert g._dont_inline_
+    assert g._no_release_gil_
+
+    def entrypoint(argv):
+        return g() + 2
+    
+    t = rtype(entrypoint, [s_list_of_strings])
+    gilanalysis.analyze(t.graphs, t)
+
+
+
+def test_no_release_gil_detect(gc="minimark"):
+    from rpython.rlib import rgc
+
+    fext1 = rffi.llexternal('fext1', [], lltype.Void, threadsafe=True)
+    @rgc.no_release_gil
+    def g():
+        fext1()
+        return 1
+
+    assert g._dont_inline_
+    assert g._no_release_gil_
+
+    def entrypoint(argv):
+        return g() + 2
+    
+    t = rtype(entrypoint, [s_list_of_strings])
+    f = py.test.raises(Exception, gilanalysis.analyze, t.graphs, t)
+    expected = "'no_release_gil' function can release the GIL: <function g at "
+    assert str(f.value).startswith(expected)
+
+
+
+


More information about the pypy-commit mailing list