[pypy-commit] pypy default: Move the CollectAnalyzer to 'backendopt' and use it from the JIT codewriter

arigo pypy.commits at gmail.com
Wed Jun 1 11:32:45 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r84861:211242489109
Date: 2016-06-01 15:37 +0200
http://bitbucket.org/pypy/pypy/changeset/211242489109/

Log:	Move the CollectAnalyzer to 'backendopt' and use it from the JIT
	codewriter

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
@@ -14,6 +14,7 @@
 from rpython.translator.backendopt.canraise import RaiseAnalyzer
 from rpython.translator.backendopt.writeanalyze import ReadWriteAnalyzer
 from rpython.translator.backendopt.graphanalyze import DependencyTracker
+from rpython.translator.backendopt.collectanalyze import CollectAnalyzer
 
 
 class CallControl(object):
@@ -37,9 +38,9 @@
             self.virtualizable_analyzer = VirtualizableAnalyzer(translator)
             self.quasiimmut_analyzer = QuasiImmutAnalyzer(translator)
             self.randomeffects_analyzer = RandomEffectsAnalyzer(translator)
-            self.seen = DependencyTracker(self.readwrite_analyzer)
-        else:
-            self.seen = None
+            self.collect_analyzer = CollectAnalyzer(translator)
+            self.seen_rw = DependencyTracker(self.readwrite_analyzer)
+            self.seen_gc = DependencyTracker(self.collect_analyzer)
         #
         for index, jd in enumerate(jitdrivers_sd):
             jd.index = index
@@ -294,9 +295,9 @@
                     "but the function has no result" % (op, ))
         #
         effectinfo = effectinfo_from_writeanalyze(
-            self.readwrite_analyzer.analyze(op, self.seen), self.cpu,
+            self.readwrite_analyzer.analyze(op, self.seen_rw), self.cpu,
             extraeffect, oopspecindex, can_invalidate, call_release_gil_target,
-            extradescr,
+            extradescr, self.collect_analyzer.analyze(op, self.seen_gc),
         )
         #
         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
@@ -116,7 +116,8 @@
                 oopspecindex=OS_NONE,
                 can_invalidate=False,
                 call_release_gil_target=_NO_CALL_RELEASE_GIL_TARGET,
-                extradescrs=None):
+                extradescrs=None,
+                can_collect=True):
         readonly_descrs_fields = frozenset_or_none(readonly_descrs_fields)
         readonly_descrs_arrays = frozenset_or_none(readonly_descrs_arrays)
         readonly_descrs_interiorfields = frozenset_or_none(
@@ -133,7 +134,8 @@
                write_descrs_interiorfields,
                extraeffect,
                oopspecindex,
-               can_invalidate)
+               can_invalidate,
+               can_collect)
         tgt_func, tgt_saveerr = call_release_gil_target
         if tgt_func:
             key += (object(),)    # don't care about caching in this case
@@ -184,6 +186,7 @@
         #
         result.extraeffect = extraeffect
         result.can_invalidate = can_invalidate
+        result.can_collect = can_collect
         result.oopspecindex = oopspecindex
         result.extradescrs = extradescrs
         result.call_release_gil_target = call_release_gil_target
@@ -230,6 +233,9 @@
     def check_can_invalidate(self):
         return self.can_invalidate
 
+    def check_can_collect(self):
+        return self.can_collect
+
     def check_is_elidable(self):
         return (self.extraeffect == self.EF_ELIDABLE_CAN_RAISE or
                 self.extraeffect == self.EF_ELIDABLE_OR_MEMORYERROR or
@@ -268,7 +274,8 @@
                                  can_invalidate=False,
                                  call_release_gil_target=
                                      EffectInfo._NO_CALL_RELEASE_GIL_TARGET,
-                                 extradescr=None):
+                                 extradescr=None,
+                                 can_collect=True):
     from rpython.translator.backendopt.writeanalyze import top_set
     if effects is top_set or extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
         readonly_descrs_fields = None
@@ -343,6 +350,9 @@
             else:
                 assert 0
     #
+    if extraeffect >= EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE:
+        can_collect = True
+    #
     return EffectInfo(readonly_descrs_fields,
                       readonly_descrs_arrays,
                       readonly_descrs_interiorfields,
@@ -353,7 +363,8 @@
                       oopspecindex,
                       can_invalidate,
                       call_release_gil_target,
-                      extradescr)
+                      extradescr,
+                      can_collect)
 
 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
@@ -334,3 +334,37 @@
     assert call_op.opname == 'direct_call'
     with py.test.raises(Exception):
         call_descr = cc.getcalldescr(call_op)
+
+def test_can_or_cannot_collect():
+    from rpython.jit.backend.llgraph.runner import LLGraphCPU
+    prebuilts = [[5], [6]]
+    l = []
+    def f1(n):
+        if n > 1:
+            raise IndexError
+        return prebuilts[n]    # cannot collect
+    f1._dont_inline_ = True
+
+    def f2(n):
+        return [n]         # can collect
+    f2._dont_inline_ = True
+
+    def f(n):
+        a = f1(n)
+        b = f2(n)
+        return len(a) + len(b)
+
+    rtyper = support.annotate(f, [1])
+    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]
+    for index, expected in [
+            (0, False),    # f1()
+            (1, True),     # f2()
+            (2, False),    # len()
+            (3, False)]:   # len()
+        call_op = f_graph.startblock.operations[index]
+        assert call_op.opname == 'direct_call'
+        call_descr = cc.getcalldescr(call_op)
+        assert call_descr.extrainfo.check_can_collect() == expected
diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -5,7 +5,7 @@
 from rpython.rlib.unroll import unrolling_iterable
 from rpython.rtyper import rmodel, annlowlevel
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, llgroup
-from rpython.rtyper.lltypesystem.lloperation import LL_OPERATIONS, llop
+from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.memory import gctypelayout
 from rpython.memory.gctransform.log import log
 from rpython.memory.gctransform.support import get_rtti, ll_call_destructor
@@ -14,7 +14,7 @@
 from rpython.memory.gctypelayout import ll_weakref_deref, WEAKREF, WEAKREFPTR
 from rpython.memory.gctypelayout import FIN_TRIGGER_FUNC, FIN_HANDLER_ARRAY
 from rpython.tool.sourcetools import func_with_new_name
-from rpython.translator.backendopt import graphanalyze
+from rpython.translator.backendopt.collectanalyze import CollectAnalyzer
 from rpython.translator.backendopt.finalizer import FinalizerAnalyzer
 from rpython.translator.backendopt.support import var_needsgc
 import types
@@ -23,33 +23,6 @@
 TYPE_ID = llgroup.HALFWORD
 
 
-class CollectAnalyzer(graphanalyze.BoolGraphAnalyzer):
-
-    def analyze_direct_call(self, graph, seen=None):
-        try:
-            func = graph.func
-        except AttributeError:
-            pass
-        else:
-            if getattr(func, '_gctransformer_hint_cannot_collect_', False):
-                return False
-            if getattr(func, '_gctransformer_hint_close_stack_', False):
-                return True
-        return graphanalyze.BoolGraphAnalyzer.analyze_direct_call(self, graph,
-                                                                  seen)
-    def analyze_external_call(self, funcobj, seen=None):
-        if funcobj.random_effects_on_gcobjs:
-            return True
-        return graphanalyze.BoolGraphAnalyzer.analyze_external_call(
-            self, funcobj, seen)
-    def analyze_simple_operation(self, op, graphinfo):
-        if op.opname in ('malloc', 'malloc_varsize'):
-            flags = op.args[1].value
-            return flags['flavor'] == 'gc'
-        else:
-            return (op.opname in LL_OPERATIONS and
-                    LL_OPERATIONS[op.opname].canmallocgc)
-
 def propagate_no_write_barrier_needed(result, block, mallocvars,
                                       collect_analyzer, entrymap,
                                       startindex=0):
diff --git a/rpython/translator/backendopt/collectanalyze.py b/rpython/translator/backendopt/collectanalyze.py
new file mode 100644
--- /dev/null
+++ b/rpython/translator/backendopt/collectanalyze.py
@@ -0,0 +1,33 @@
+from rpython.translator.backendopt import graphanalyze
+from rpython.rtyper.lltypesystem.lloperation import LL_OPERATIONS
+
+# NB. tests are in rpython/memory/gctransform/test/test_framework.py
+
+
+class CollectAnalyzer(graphanalyze.BoolGraphAnalyzer):
+
+    def analyze_direct_call(self, graph, seen=None):
+        try:
+            func = graph.func
+        except AttributeError:
+            pass
+        else:
+            if getattr(func, '_gctransformer_hint_cannot_collect_', False):
+                return False
+            if getattr(func, '_gctransformer_hint_close_stack_', False):
+                return True
+        return graphanalyze.BoolGraphAnalyzer.analyze_direct_call(self, graph,
+                                                                  seen)
+    def analyze_external_call(self, funcobj, seen=None):
+        if funcobj.random_effects_on_gcobjs:
+            return True
+        return graphanalyze.BoolGraphAnalyzer.analyze_external_call(
+            self, funcobj, seen)
+
+    def analyze_simple_operation(self, op, graphinfo):
+        if op.opname in ('malloc', 'malloc_varsize'):
+            flags = op.args[1].value
+            return flags['flavor'] == 'gc'
+        else:
+            return (op.opname in LL_OPERATIONS and
+                    LL_OPERATIONS[op.opname].canmallocgc)


More information about the pypy-commit mailing list