[pypy-commit] pypy improve-consecutive-dict-lookups: sort out the issue with interiorfielddescrs (I think) and dicts

fijal noreply at buildbot.pypy.org
Wed Mar 19 20:55:35 CET 2014


Author: Maciej Fijalkowski <fijall at gmail.com>
Branch: improve-consecutive-dict-lookups
Changeset: r70098:2cf24861ad15
Date: 2014-03-19 21:48 +0200
http://bitbucket.org/pypy/pypy/changeset/2cf24861ad15/

Log:	sort out the issue with interiorfielddescrs (I think) and dicts

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
@@ -93,12 +93,14 @@
     ])
 
     def __new__(cls, readonly_descrs_fields, readonly_descrs_arrays,
+                readonly_descrs_interiorfields,
                 write_descrs_fields, write_descrs_arrays,
+                write_descrs_interiorfields,
                 extraeffect=EF_CAN_RAISE,
                 oopspecindex=OS_NONE,
                 can_invalidate=False,
                 call_release_gil_target=llmemory.NULL,
-                extradescr=None):
+                extradescrs=None):
         key = (frozenset_or_none(readonly_descrs_fields),
                frozenset_or_none(readonly_descrs_arrays),
                frozenset_or_none(write_descrs_fields),
@@ -123,18 +125,21 @@
         result = object.__new__(cls)
         result.readonly_descrs_fields = readonly_descrs_fields
         result.readonly_descrs_arrays = readonly_descrs_arrays
+        result.readonly_descrs_interiorfields = readonly_descrs_interiorfields
         if extraeffect == EffectInfo.EF_LOOPINVARIANT or \
            extraeffect == EffectInfo.EF_ELIDABLE_CANNOT_RAISE or \
            extraeffect == EffectInfo.EF_ELIDABLE_CAN_RAISE:
             result.write_descrs_fields = []
             result.write_descrs_arrays = []
+            result.write_descrs_interiorfields = []
         else:
             result.write_descrs_fields = write_descrs_fields
             result.write_descrs_arrays = write_descrs_arrays
+            result.write_descrs_interiorfields = write_descrs_interiorfields
         result.extraeffect = extraeffect
         result.can_invalidate = can_invalidate
         result.oopspecindex = oopspecindex
-        result.extradescr = extradescr
+        result.extradescrs = extradescrs
         result.call_release_gil_target = call_release_gil_target
         if result.check_can_raise():
             assert oopspecindex in cls._OS_CANRAISE
@@ -166,7 +171,7 @@
         return None
     return frozenset(x)
 
-EffectInfo.MOST_GENERAL = EffectInfo(None, None, None, None,
+EffectInfo.MOST_GENERAL = EffectInfo(None, None, None, None, None, None,
                                      EffectInfo.EF_RANDOM_EFFECTS,
                                      can_invalidate=True)
 
@@ -181,14 +186,18 @@
     if effects is top_set or extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
         readonly_descrs_fields = None
         readonly_descrs_arrays = None
+        readonly_descrs_interiorfields = None
         write_descrs_fields = None
         write_descrs_arrays = None
+        write_descrs_interiorfields = None
         extraeffect = EffectInfo.EF_RANDOM_EFFECTS
     else:
         readonly_descrs_fields = []
         readonly_descrs_arrays = []
+        readonly_descrs_interiorfields = []
         write_descrs_fields = []
         write_descrs_arrays = []
+        write_descrs_interiorfields = []
 
         def add_struct(descrs_fields, (_, T, fieldname)):
             T = deref(T)
@@ -202,6 +211,17 @@
                 descr = cpu.arraydescrof(ARRAY)
                 descrs_arrays.append(descr)
 
+        def add_interiorfield(descrs_interiorfields, (_, T, fieldname)):
+            T = deref(T)
+            if not isinstance(T, lltype.Array):
+                return # let's not consider structs for now
+            if not consider_array(T):
+                return
+            if getattr(T.OF, fieldname) is lltype.Void:
+                return
+            descr = cpu.interiorfielddescrof(T, fieldname)
+            descrs_interiorfields.append(descr)
+
         for tup in effects:
             if tup[0] == "struct":
                 add_struct(write_descrs_fields, tup)
@@ -209,6 +229,12 @@
                 tupw = ("struct",) + tup[1:]
                 if tupw not in effects:
                     add_struct(readonly_descrs_fields, tup)
+            elif tup[0] == "interiorfield":
+                add_interiorfield(write_descrs_interiorfields, tup)
+            elif tup[0] == "readinteriorfield":
+                tupw = ('interiorfield',) + tup[1:]
+                if tupw not in effects:
+                    add_interiorfield(readonly_descrs_interiorfields, tup)
             elif tup[0] == "array":
                 add_array(write_descrs_arrays, tup)
             elif tup[0] == "readarray":
@@ -220,8 +246,10 @@
     #
     return EffectInfo(readonly_descrs_fields,
                       readonly_descrs_arrays,
+                      readonly_descrs_interiorfields,
                       write_descrs_fields,
                       write_descrs_arrays,
+                      write_descrs_interiorfields,
                       extraeffect,
                       oopspecindex,
                       can_invalidate,
diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py
--- a/rpython/jit/codewriter/jtransform.py
+++ b/rpython/jit/codewriter/jtransform.py
@@ -1852,10 +1852,12 @@
                                          EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
 
     def _handle_dict_lookup_call(self, op, oopspec_name, args):
-        extradescr = self.cpu.fielddescrof(op.args[1].concretetype.TO,
-                                           'entries')
+        extradescr1 = self.cpu.fielddescrof(op.args[1].concretetype.TO,
+                                            'entries')
+        extradescr2 = self.cpu.interiorfielddescrof(
+            op.args[1].concretetype.TO.entries.TO, 'key')
         return self._handle_oopspec_call(op, args, EffectInfo.OS_DICT_LOOKUP,
-                                         extradescr=extradescr)
+                                         extradescr=[extradescr1, extradescr2])
 
     def _handle_rgc_call(self, op, oopspec_name, args):
         if oopspec_name == 'rgc.ll_shrink_array':
diff --git a/rpython/jit/metainterp/optimizeopt/heap.py b/rpython/jit/metainterp/optimizeopt/heap.py
--- a/rpython/jit/metainterp/optimizeopt/heap.py
+++ b/rpython/jit/metainterp/optimizeopt/heap.py
@@ -177,6 +177,8 @@
         self.cached_arrayitems = {}
         # cached dict items: {dict descr: {(optval, index): box-or-const}}
         self.cached_dict_reads = {}
+        # cache of corresponding array descrs
+        self.corresponding_array_descrs = {}
         #
         self._lazy_setfields_and_arrayitems = []
         self._remove_guard_not_invalidated = False
@@ -185,10 +187,12 @@
 
     def force_at_end_of_preamble(self):
         self.cached_dict_reads.clear()
+        self.corresponding_array_descrs.clear()
         self.force_all_lazy_setfields_and_arrayitems()
 
     def flush(self):
         self.cached_dict_reads.clear()
+        self.corresponding_array_descrs.clear()
         self.force_all_lazy_setfields_and_arrayitems()
         self.emit_postponed_op()
 
@@ -301,11 +305,14 @@
         self.emit_operation(op)
 
     def _optimize_CALL_DICT_LOOKUP(self, op):
-        descr = op.getdescr().get_extra_info().extradescr
-        if descr in self.cached_dict_reads:
-            d = self.cached_dict_reads[descr]
+        descrs = op.getdescr().get_extra_info().extradescrs
+        descr1 = descrs[0]
+        descr2 = descrs[1]
+        if descr1 in self.cached_dict_reads:
+            d = self.cached_dict_reads[descr1]
         else:
-            d = self.cached_dict_reads[descr] = args_dict()
+            d = self.cached_dict_reads[descr1] = args_dict()
+            self.corresponding_array_descrs[descr2] = descr1
         args = self.optimizer.make_args_key(op)
         try:
             res_v = d[args]
@@ -339,6 +346,13 @@
             self.force_lazy_setfield(fielddescr, can_cache=False)
         for arraydescr in effectinfo.write_descrs_arrays:
             self.force_lazy_setarrayitem(arraydescr, can_cache=False)
+        for descr in effectinfo.write_descrs_interiorfields:
+            if descr in self.corresponding_array_descrs:
+                dictdescr = self.corresponding_array_descrs.pop(descr)
+                try:
+                    del self.cached_dict_reads[dictdescr]
+                except KeyError:
+                    pass # someone did it already
         if effectinfo.check_forces_virtual_or_virtualizable():
             vrefinfo = self.optimizer.metainterp_sd.virtualref_info
             self.force_lazy_setfield(vrefinfo.descr_forced)
diff --git a/rpython/jit/metainterp/test/test_dict.py b/rpython/jit/metainterp/test/test_dict.py
--- a/rpython/jit/metainterp/test/test_dict.py
+++ b/rpython/jit/metainterp/test/test_dict.py
@@ -253,6 +253,28 @@
         assert res == f(10)
         self.check_simple_loop(call=5)
 
+    def test_dict_array_write_invalidates_caches(self):
+        driver = JitDriver(greens = [], reds = 'auto')
+        indexes = ['aa', 'b', 'cc']
+
+        def f(n):
+            d = {'aa': 3, 'b': 4, 'cc': 5}
+            s = 0
+            while n > 0:
+                driver.jit_merge_point()
+                index = indexes[n & 1]
+                s += d[index]
+                del d['cc']
+                s += d[index]
+                d['cc'] = 3
+                n -= 1
+            return s
+
+        exp = f(10)
+        res = self.meta_interp(f, [10])
+        assert res == exp
+        self.check_simple_loop(call=7)
+
     def test_dict_double_lookup_2(self):
         driver = JitDriver(greens = [], reds = 'auto')
         indexes = ['aa', 'b', 'cc']
diff --git a/rpython/translator/backendopt/writeanalyze.py b/rpython/translator/backendopt/writeanalyze.py
--- a/rpython/translator/backendopt/writeanalyze.py
+++ b/rpython/translator/backendopt/writeanalyze.py
@@ -1,4 +1,4 @@
-from rpython.flowspace.model import Variable
+from rpython.flowspace.model import Variable, Constant
 from rpython.translator.backendopt import graphanalyze
 
 top_set = object()
@@ -37,6 +37,12 @@
             return top_set
         return result1.union(result2)
 
+    def _getinteriorname(self, op):
+        if (isinstance(op.args[1], Constant) and
+            isinstance(op.args[1].value, str)):
+            return op.args[1].value
+        return op.args[2].value
+
     def analyze_simple_operation(self, op, graphinfo):
         if op.opname == "setfield":
             if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]):
@@ -47,8 +53,8 @@
                 return self._array_result(op.args[0].concretetype)
         elif op.opname == "setinteriorfield":
             if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]):
-                return self._interiorfield_result(op.args[0].concretetype,
-                                                  op.args[2].value)
+                name = self._getinteriorname(op)
+                return self._interiorfield_result(op.args[0].concretetype, name)
         return empty_set
 
     def _array_result(self, TYPE):
@@ -107,7 +113,7 @@
             return frozenset([
                 ("readarray", op.args[0].concretetype)])
         elif op.opname == "getinteriorfield":
-            return frozenset([
-                ("readinteriorfield", op.args[0].concretetype,
-                 op.args[2].value)])
+            name = self._getinteriorname(op)
+            return frozenset([("readinteriorfield", op.args[0].concretetype,
+                            name)])
         return WriteAnalyzer.analyze_simple_operation(self, op, graphinfo)


More information about the pypy-commit mailing list