[pypy-svn] r69055 - in pypy/trunk/pypy/jit/metainterp: . test

cfbolz at codespeak.net cfbolz at codespeak.net
Sun Nov 8 12:16:30 CET 2009


Author: cfbolz
Date: Sun Nov  8 12:16:29 2009
New Revision: 69055

Added:
   pypy/trunk/pypy/jit/metainterp/effectinfo.py   (contents, props changed)
Modified:
   pypy/trunk/pypy/jit/metainterp/codewriter.py
   pypy/trunk/pypy/jit/metainterp/optimizeopt.py
   pypy/trunk/pypy/jit/metainterp/test/test_basic.py
   pypy/trunk/pypy/jit/metainterp/test/test_optimizefindnode.py
   pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py
   pypy/trunk/pypy/jit/metainterp/test/test_virtualizable.py
Log:
(pedronis, cfbolz): don't delete all of the cached information about structure
fields and array items. Instead, use the write analyzer to figure out which
fields an external function can actually write to and invalidate the caches of
those fields.


Modified: pypy/trunk/pypy/jit/metainterp/codewriter.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/codewriter.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/codewriter.py	Sun Nov  8 12:16:29 2009
@@ -11,7 +11,9 @@
 from pypy.tool.udir import udir
 from pypy.translator.simplify import get_funcobj, get_functype
 from pypy.translator.backendopt.canraise import RaiseAnalyzer
+from pypy.translator.backendopt.writeanalyze import WriteAnalyzer
 from pypy.jit.metainterp.typesystem import deref, arrayItem, fieldType
+from pypy.jit.metainterp.effectinfo import effectinfo_from_writeanalyze
 
 import py, sys
 from pypy.tool.ansi_print import ansi_log
@@ -177,6 +179,7 @@
         self.cpu = metainterp_sd.cpu
         self.portal_runner_ptr = portal_runner_ptr
         self.raise_analyzer = RaiseAnalyzer(self.rtyper.annotator.translator)
+        self.write_analyzer = WriteAnalyzer(self.rtyper.annotator.translator)
 
     def make_portal_bytecode(self, graph):
         log.info("making JitCodes...")
@@ -302,7 +305,7 @@
                 jitcodes[oocls] = jitcode
         methdescr.setup(jitcodes)
 
-    def getcalldescr(self, v_func, args, result):
+    def getcalldescr(self, v_func, args, result, consider_effects_of=None):
         non_void_args = [x for x in args if x.concretetype is not lltype.Void]
         NON_VOID_ARGS = [x.concretetype for x in non_void_args]
         RESULT = result.concretetype
@@ -312,7 +315,13 @@
         assert NON_VOID_ARGS == [T for T in ARGS if T is not lltype.Void]
         assert RESULT == FUNC.RESULT
         # ok
-        calldescr = self.cpu.calldescrof(FUNC, tuple(NON_VOID_ARGS), RESULT)
+        if (self.rtyper.type_system.name == 'lltypesystem' and
+                consider_effects_of is not None):
+            effectinfo = effectinfo_from_writeanalyze(
+                    self.write_analyzer.analyze(consider_effects_of), self.cpu)
+            calldescr = self.cpu.calldescrof(FUNC, tuple(NON_VOID_ARGS), RESULT, effectinfo)
+        else:
+            calldescr = self.cpu.calldescrof(FUNC, tuple(NON_VOID_ARGS), RESULT)
         return calldescr, non_void_args
 
     def register_known_gctype(self, vtable, STRUCT):
@@ -1147,9 +1156,8 @@
             args = op.args[1:-1]
         else:
             args = op.args[1:]
-        calldescr, non_void_args = self.codewriter.getcalldescr(op.args[0],
-                                                                args,
-                                                                op.result)
+        calldescr, non_void_args = self.codewriter.getcalldescr(
+            op.args[0], args, op.result, consider_effects_of=op)
         pure = False
         if op.opname == "direct_call":
             func = getattr(get_funcobj(op.args[0].value), '_callable', None)

Added: pypy/trunk/pypy/jit/metainterp/effectinfo.py
==============================================================================
--- (empty file)
+++ pypy/trunk/pypy/jit/metainterp/effectinfo.py	Sun Nov  8 12:16:29 2009
@@ -0,0 +1,39 @@
+from pypy.rpython.lltypesystem import lltype
+
+class EffectInfo(object):
+    _cache = {}
+
+    def __new__(cls, write_descrs_fields, write_descrs_arrays):
+        key = frozenset(write_descrs_fields), frozenset(write_descrs_arrays)
+        if key in cls._cache:
+            return cls._cache[key]
+        result = object.__new__(cls)
+        result.write_descrs_fields = write_descrs_fields
+        result.write_descrs_arrays = write_descrs_arrays
+        cls._cache[key] = result
+        return result
+
+def effectinfo_from_writeanalyze(effects, cpu):
+    from pypy.translator.backendopt.writeanalyze import top_set
+    if effects is top_set:
+        return None
+    write_descrs_fields = []
+    write_descrs_arrays = []
+    for tup in effects:
+        if tup[0] == "struct":
+            _, T, fieldname = tup
+            if not isinstance(T.TO, lltype.GcStruct): # can be a non-GC-struct
+                continue
+            descr = cpu.fielddescrof(T.TO, fieldname)
+            write_descrs_fields.append(descr)
+        elif tup[0] == "array":
+            _, T = tup
+            if not isinstance(T.TO, lltype.GcArray): # can be a non-GC-array
+                continue
+            descr = cpu.arraydescrof(T.TO)
+            write_descrs_arrays.append(descr)
+        else:
+            assert 0
+    return EffectInfo(write_descrs_fields, write_descrs_arrays)
+
+

Modified: pypy/trunk/pypy/jit/metainterp/optimizeopt.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/optimizeopt.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/optimizeopt.py	Sun Nov  8 12:16:29 2009
@@ -852,6 +852,20 @@
             opnum == rop.SETARRAYITEM_GC or
             opnum == rop.DEBUG_MERGE_POINT):
             return
+        if opnum == rop.CALL:
+            effectinfo = op.descr.get_extra_info()
+            if effectinfo is not None:
+                for fielddescr in effectinfo.write_descrs_fields:
+                    try:
+                        del self.cached_fields[fielddescr]
+                    except KeyError:
+                        pass
+                for arraydescr in effectinfo.write_descrs_arrays:
+                    try:
+                        del self.cached_arrayitems[arraydescr]
+                    except KeyError:
+                        pass
+                return
         self.clean_caches()
 
     def optimize_GETFIELD_GC(self, op, value):

Modified: pypy/trunk/pypy/jit/metainterp/test/test_basic.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_basic.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_basic.py	Sun Nov  8 12:16:29 2009
@@ -972,7 +972,6 @@
         self.check_loop_count(1)
         self.check_loops(call=1)
 
-
 class TestOOtype(BasicTests, OOJitMixin):
 
     def test_oohash(self):
@@ -1104,5 +1103,65 @@
             history.BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, x))).value
         assert res == expected
 
+    def test_residual_call_doesnt_lose_info(self):
+        myjitdriver = JitDriver(greens = [], reds = ['x', 'y', 'l'])
+
+        class A(object):
+            pass
+
+        globall = [""]
+        @dont_look_inside
+        def g(x):
+            globall[0] = str(x)
+            return x
+
+        def f(x):
+            y = A()
+            y.v = x
+            l = [0]
+            while y.v > 0:
+                myjitdriver.can_enter_jit(x=x, y=y, l=l)
+                myjitdriver.jit_merge_point(x=x, y=y, l=l)
+                l[0] = y.v
+                lc = l[0]
+                y.v = g(y.v) - y.v/y.v + lc/l[0] - 1
+            return y.v
+        res = self.meta_interp(f, [20], listops=True)
+        self.check_loops(getfield_gc=1, getarrayitem_gc=0)
+
+    def test_writeanalyzer_top_set(self):
+        from pypy.rlib.objectmodel import instantiate
+        myjitdriver = JitDriver(greens = [], reds = ['x', 'y', 'l'])
+
+        class A(object):
+            pass
+        class B(A):
+            pass
+
+        @dont_look_inside
+        def g(x):
+            # instantiate cannot be followed by the writeanalyzer
+            if x % 2:
+                C = A
+            else:
+                C = B
+            a = instantiate(C)
+            a.v = x
+            return a.v
+
+        def f(x):
+            y = A()
+            y.v = x
+            l = [0]
+            while y.v > 0:
+                myjitdriver.can_enter_jit(x=x, y=y, l=l)
+                myjitdriver.jit_merge_point(x=x, y=y, l=l)
+                l[0] = y.v
+                lc = l[0]
+                y.v = g(y.v) - y.v/y.v + lc/l[0] - 1
+            return y.v
+        res = self.meta_interp(f, [20], listops=True)
+        self.check_loops(getfield_gc=2, getarrayitem_gc=1)
+
 class TestLLtype(BaseLLtypeTests, LLJitMixin):
     pass

Modified: pypy/trunk/pypy/jit/metainterp/test/test_optimizefindnode.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_optimizefindnode.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_optimizefindnode.py	Sun Nov  8 12:16:29 2009
@@ -16,6 +16,7 @@
 from pypy.jit.metainterp.specnode import VirtualArraySpecNode
 from pypy.jit.metainterp.specnode import VirtualStructSpecNode
 from pypy.jit.metainterp.specnode import ConstantSpecNode
+from pypy.jit.metainterp.effectinfo import EffectInfo
 from pypy.jit.metainterp.test.oparser import parse
 
 def test_sort_descrs():
@@ -93,6 +94,11 @@
     usize = cpu.sizeof(U)
     onedescr = cpu.fielddescrof(U, 'one')
 
+    FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
+    nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], []))
+    writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([adescr], []))
+    writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([adescr], [arraydescr]))
+
     cpu.class_sizes = {cpu.cast_adr_to_int(node_vtable_adr): cpu.sizeof(NODE),
                       cpu.cast_adr_to_int(node_vtable_adr2): cpu.sizeof(NODE2),
                        cpu.cast_adr_to_int(u_vtable_adr): cpu.sizeof(U)}
@@ -159,6 +165,9 @@
     adescr.sort_key()
     bdescr.sort_key()
 
+    FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
+    nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT) # XXX fix ootype
+
     cpu.class_sizes = {node_vtable_adr: cpu.typedescrof(NODE),
                        node_vtable_adr2: cpu.typedescrof(NODE2),
                        u_vtable_adr: cpu.typedescrof(U)}

Modified: pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py	Sun Nov  8 12:16:29 2009
@@ -616,18 +616,18 @@
         guard_no_exception() []
         i1 = int_add(i, 3)
         guard_no_exception() []
-        i2 = call(i1)
+        i2 = call(i1, descr=nonwritedescr)
         guard_no_exception() [i1, i2]
         guard_no_exception() []
-        i3 = call(i2)
+        i3 = call(i2, descr=nonwritedescr)
         jump(i1)       # the exception is considered lost when we loop back
         """
         expected = """
         [i]
         i1 = int_add(i, 3)
-        i2 = call(i1)
+        i2 = call(i1, descr=nonwritedescr)
         guard_no_exception() [i1, i2]
-        i3 = call(i2)
+        i3 = call(i2, descr=nonwritedescr)
         jump(i1)
         """
         self.optimize_loop(ops, 'Not', expected)
@@ -1841,7 +1841,117 @@
 
 
 class TestLLtype(BaseTestOptimizeOpt, LLtypeMixin):
-    pass
+
+    def test_residual_call_does_not_invalidate_caches(self):
+        ops = """
+        [p1, p2]
+        i1 = getfield_gc(p1, descr=valuedescr)
+        i2 = call(i1, descr=nonwritedescr)
+        i3 = getfield_gc(p1, descr=valuedescr)
+        escape(i1)
+        escape(i3)
+        jump(p1, p2)
+        """
+        expected = """
+        [p1, p2]
+        i1 = getfield_gc(p1, descr=valuedescr)
+        i2 = call(i1, descr=nonwritedescr)
+        escape(i1)
+        escape(i1)
+        jump(p1, p2)
+        """
+        self.optimize_loop(ops, 'Not, Not', expected)
+
+    def test_residual_call_invalidate_some_caches(self):
+        ops = """
+        [p1, p2]
+        i1 = getfield_gc(p1, descr=adescr)
+        i2 = getfield_gc(p1, descr=bdescr)
+        i3 = call(i1, descr=writeadescr)
+        i4 = getfield_gc(p1, descr=adescr)
+        i5 = getfield_gc(p1, descr=bdescr)
+        escape(i1)
+        escape(i2)
+        escape(i4)
+        escape(i5)
+        jump(p1, p2)
+        """
+        expected = """
+        [p1, p2]
+        i1 = getfield_gc(p1, descr=adescr)
+        i2 = getfield_gc(p1, descr=bdescr)
+        i3 = call(i1, descr=writeadescr)
+        i4 = getfield_gc(p1, descr=adescr)
+        escape(i1)
+        escape(i2)
+        escape(i4)
+        escape(i2)
+        jump(p1, p2)
+        """
+        self.optimize_loop(ops, 'Not, Not', expected)
+
+    def test_residual_call_invalidate_arrays(self):
+        ops = """
+        [p1, p2, i1]
+        p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+        p4 = getarrayitem_gc(p2, 1, descr=arraydescr2)
+        i3 = call(i1, descr=writeadescr)
+        p5 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+        p6 = getarrayitem_gc(p2, 1, descr=arraydescr2)
+        escape(p3)
+        escape(p4)
+        escape(p5)
+        escape(p6)
+        jump(p1, p2, i1)
+        """
+        expected = """
+        [p1, p2, i1]
+        p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+        p4 = getarrayitem_gc(p2, 1, descr=arraydescr2)
+        i3 = call(i1, descr=writeadescr)
+        escape(p3)
+        escape(p4)
+        escape(p3)
+        escape(p4)
+        jump(p1, p2, i1)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not', expected)
+
+    def test_residual_call_invalidate_some_arrays(self):
+        ops = """
+        [p1, p2, i1]
+        p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+        p4 = getarrayitem_gc(p2, 1, descr=arraydescr2)
+        i2 = getarrayitem_gc(p1, 1, descr=arraydescr)
+        i3 = call(i1, descr=writearraydescr)
+        p5 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+        p6 = getarrayitem_gc(p2, 1, descr=arraydescr2)
+        i4 = getarrayitem_gc(p1, 1, descr=arraydescr)
+        escape(p3)
+        escape(p4)
+        escape(p5)
+        escape(p6)
+        escape(i2)
+        escape(i4)
+        jump(p1, p2, i1)
+        """
+        expected = """
+        [p1, p2, i1]
+        p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+        p4 = getarrayitem_gc(p2, 1, descr=arraydescr2)
+        i2 = getarrayitem_gc(p1, 1, descr=arraydescr)
+        i3 = call(i1, descr=writearraydescr)
+        i4 = getarrayitem_gc(p1, 1, descr=arraydescr)
+        escape(p3)
+        escape(p4)
+        escape(p3)
+        escape(p4)
+        escape(i2)
+        escape(i4)
+        jump(p1, p2, i1)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not', expected)
+
 
 class TestOOtype(BaseTestOptimizeOpt, OOtypeMixin):
 

Modified: pypy/trunk/pypy/jit/metainterp/test/test_virtualizable.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_virtualizable.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_virtualizable.py	Sun Nov  8 12:16:29 2009
@@ -228,7 +228,8 @@
         assert f(20) == 10000*20 + (20*21)/2
         res = self.meta_interp(f, [20], policy=StopAtXPolicy(ext))
         assert res == 10000*20 + (20*21)/2
-        self.check_loops(call=1, getfield_gc=2, setfield_gc=2)
+        # there are no getfields because the optimizer gets rid of them
+        self.check_loops(call=1, getfield_gc=0, setfield_gc=2)
         # xxx for now a call that forces the virtualizable during tracing
         # is supposed to always force it later too.
 
@@ -259,7 +260,9 @@
             return m
         res = self.meta_interp(f, [20], policy=StopAtXPolicy(ext))
         assert res == f(20)
-        self.check_loops(call=1, getfield_gc=2, setfield_gc=2)
+        # the getfield_gc of inst_node is optimized away, because ext does not
+        # write to it
+        self.check_loops(call=1, getfield_gc=1, setfield_gc=2)
         # xxx for now a call that forces the virtualizable during tracing
         # is supposed to always force it later too.
 



More information about the Pypy-commit mailing list