Author: Maciej Fijalkowski <[email protected]>
Branch: 
Changeset: r70114:0679ba1c1143
Date: 2014-03-20 16:37 +0200
http://bitbucket.org/pypy/pypy/changeset/0679ba1c1143/

Log:    Merge "improve-consecutive-dict-lookups". This branch makes it
        possible to cache consecutive dict lookups (for both normal and
        ordered dicts)

diff --git a/rpython/jit/backend/test/runner_test.py 
b/rpython/jit/backend/test/runner_test.py
--- a/rpython/jit/backend/test/runner_test.py
+++ b/rpython/jit/backend/test/runner_test.py
@@ -3736,7 +3736,7 @@
             assert False, 'should not be called'
         from rpython.jit.codewriter.effectinfo import EffectInfo
 
-        effectinfo = EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE, 
EffectInfo.OS_MATH_SQRT)
+        effectinfo = EffectInfo([], [], [], [], [], [], 
EffectInfo.EF_CANNOT_RAISE, EffectInfo.OS_MATH_SQRT)
         FPTR = self.Ptr(self.FuncType([lltype.Float], lltype.Float))
         func_ptr = llhelper(FPTR, math_sqrt)
         FUNC = deref(FPTR)
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
@@ -178,7 +178,7 @@
         return (fnaddr, calldescr)
 
     def getcalldescr(self, op, oopspecindex=EffectInfo.OS_NONE,
-                     extraeffect=None):
+                     extraeffect=None, extradescr=None):
         """Return the calldescr that describes all calls done by 'op'.
         This returns a calldescr that we can put in the corresponding
         call operation in the calling jitcode.  It gets an effectinfo
@@ -259,6 +259,7 @@
         effectinfo = effectinfo_from_writeanalyze(
             self.readwrite_analyzer.analyze(op, self.seen), self.cpu,
             extraeffect, oopspecindex, can_invalidate, call_release_gil_target,
+            extradescr,
         )
         #
         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
@@ -21,6 +21,7 @@
     OS_ARRAYCOPY                = 1    # "list.ll_arraycopy"
     OS_STR2UNICODE              = 2    # "str.str2unicode"
     OS_SHRINK_ARRAY             = 3    # rgc.ll_shrink_array
+    OS_DICT_LOOKUP              = 4    # ll_dict_lookup
     #
     OS_STR_CONCAT               = 22   # "stroruni.concat"
     OS_STR_SLICE                = 23   # "stroruni.slice"
@@ -88,15 +89,18 @@
     # for debugging:
     _OS_CANRAISE = set([
         OS_NONE, OS_STR2UNICODE, OS_LIBFFI_CALL, OS_RAW_MALLOC_VARSIZE_CHAR,
-        OS_JIT_FORCE_VIRTUAL, OS_SHRINK_ARRAY,
+        OS_JIT_FORCE_VIRTUAL, OS_SHRINK_ARRAY, OS_DICT_LOOKUP,
     ])
 
     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):
+                call_release_gil_target=llmemory.NULL,
+                extradescrs=None):
         key = (frozenset_or_none(readonly_descrs_fields),
                frozenset_or_none(readonly_descrs_arrays),
                frozenset_or_none(write_descrs_fields),
@@ -121,17 +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.extradescrs = extradescrs
         result.call_release_gil_target = call_release_gil_target
         if result.check_can_raise():
             assert oopspecindex in cls._OS_CANRAISE
@@ -163,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)
 
@@ -172,19 +180,24 @@
                                  extraeffect=EffectInfo.EF_CAN_RAISE,
                                  oopspecindex=EffectInfo.OS_NONE,
                                  can_invalidate=False,
-                                 call_release_gil_target=llmemory.NULL):
+                                 call_release_gil_target=llmemory.NULL,
+                                 extradescr=None):
     from rpython.translator.backendopt.writeanalyze import top_set
     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)
@@ -198,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)
@@ -205,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":
@@ -216,12 +246,15 @@
     #
     return EffectInfo(readonly_descrs_fields,
                       readonly_descrs_arrays,
+                      readonly_descrs_interiorfields,
                       write_descrs_fields,
                       write_descrs_arrays,
+                      write_descrs_interiorfields,
                       extraeffect,
                       oopspecindex,
                       can_invalidate,
-                      call_release_gil_target)
+                      call_release_gil_target,
+                      extradescr)
 
 def consider_struct(TYPE, fieldname):
     if fieldType(TYPE, fieldname) is lltype.Void:
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
@@ -403,6 +403,9 @@
             prepare = self._handle_math_sqrt_call
         elif oopspec_name.startswith('rgc.'):
             prepare = self._handle_rgc_call
+        elif oopspec_name.endswith('dict.lookup'):
+            # also ordereddict.lookup
+            prepare = self._handle_dict_lookup_call
         else:
             prepare = self.prepare_builtin_call
         try:
@@ -1680,9 +1683,11 @@
     # ----------
     # Strings and Unicodes.
 
-    def _handle_oopspec_call(self, op, args, oopspecindex, extraeffect=None):
+    def _handle_oopspec_call(self, op, args, oopspecindex, extraeffect=None,
+                             extradescr=None):
         calldescr = self.callcontrol.getcalldescr(op, oopspecindex,
-                                                  extraeffect)
+                                                  extraeffect,
+                                                  extradescr=extradescr)
         if extraeffect is not None:
             assert (is_test_calldescr(calldescr)      # for tests
                     or calldescr.get_extra_info().extraeffect == extraeffect)
@@ -1846,6 +1851,14 @@
         return self._handle_oopspec_call(op, args, EffectInfo.OS_MATH_SQRT,
                                          EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
 
+    def _handle_dict_lookup_call(self, op, oopspec_name, args):
+        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=[extradescr1, extradescr2])
+
     def _handle_rgc_call(self, op, oopspec_name, args):
         if oopspec_name == 'rgc.ll_shrink_array':
             return self._handle_oopspec_call(op, args, 
EffectInfo.OS_SHRINK_ARRAY, EffectInfo.EF_CAN_RAISE)
diff --git a/rpython/jit/codewriter/test/test_jtransform.py 
b/rpython/jit/codewriter/test/test_jtransform.py
--- a/rpython/jit/codewriter/test/test_jtransform.py
+++ b/rpython/jit/codewriter/test/test_jtransform.py
@@ -60,7 +60,8 @@
 class FakeResidualCallControl:
     def guess_call_kind(self, op):
         return 'residual'
-    def getcalldescr(self, op, oopspecindex=None, extraeffect=None):
+    def getcalldescr(self, op, oopspecindex=None, extraeffect=None,
+                     extradescr=None):
         return 'calldescr'
     def calldescr_canraise(self, calldescr):
         return True
@@ -117,7 +118,8 @@
         self.callinfocollection = FakeCallInfoCollection()
     def guess_call_kind(self, op):
         return 'builtin'
-    def getcalldescr(self, op, oopspecindex=None, extraeffect=None):
+    def getcalldescr(self, op, oopspecindex=None, extraeffect=None,
+                     extradescr=None):
         assert oopspecindex is not None    # in this test
         EI = effectinfo.EffectInfo
         if oopspecindex != EI.OS_ARRAYCOPY:
diff --git a/rpython/jit/codewriter/test/test_list.py 
b/rpython/jit/codewriter/test/test_list.py
--- a/rpython/jit/codewriter/test/test_list.py
+++ b/rpython/jit/codewriter/test/test_list.py
@@ -37,7 +37,8 @@
 
 class FakeCallControl:
     class getcalldescr(AbstractDescr):
-        def __init__(self, op, oopspecindex=0, extraeffect=None):
+        def __init__(self, op, oopspecindex=0, extraeffect=None,
+                     extradescr=None):
             self.op = op
             self.oopspecindex = oopspecindex
         def __repr__(self):
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
@@ -1,8 +1,10 @@
 import os
 
+from rpython.jit.codewriter.effectinfo import EffectInfo
+from rpython.jit.metainterp.optimizeopt.util import args_dict
 from rpython.jit.metainterp.history import Const
 from rpython.jit.metainterp.jitexc import JitException
-from rpython.jit.metainterp.optimizeopt.optimizer import Optimization, 
MODE_ARRAY, LEVEL_KNOWNCLASS
+from rpython.jit.metainterp.optimizeopt.optimizer import Optimization, 
MODE_ARRAY, LEVEL_KNOWNCLASS, REMOVED
 from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
 from rpython.jit.metainterp.resoperation import rop, ResOperation
 from rpython.rlib.objectmodel import we_are_translated
@@ -173,6 +175,10 @@
         self.cached_fields = {}
         # cached array items:  {array descr: {index: CachedField}}
         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
@@ -180,9 +186,13 @@
         self.postponed_op = None
 
     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()
 
@@ -214,6 +224,7 @@
         del self._lazy_setfields_and_arrayitems[:]
         self.cached_fields.clear()
         self.cached_arrayitems.clear()
+        self.cached_dict_reads.clear()
 
     def field_cache(self, descr):
         try:
@@ -282,6 +293,44 @@
         self.force_all_lazy_setfields_and_arrayitems()
         self.clean_caches()
 
+    def optimize_CALL(self, op):
+        # dispatch based on 'oopspecindex' to a method that handles
+        # specifically the given oopspec call.  For non-oopspec calls,
+        # oopspecindex is just zero.
+        effectinfo = op.getdescr().get_extra_info()
+        oopspecindex = effectinfo.oopspecindex
+        if oopspecindex == EffectInfo.OS_DICT_LOOKUP:
+            if self._optimize_CALL_DICT_LOOKUP(op):
+                return
+        self.emit_operation(op)
+
+    def _optimize_CALL_DICT_LOOKUP(self, op):
+        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[descr1] = args_dict()
+            self.corresponding_array_descrs[descr2] = descr1
+        args = self.optimizer.make_args_key(op)
+        try:
+            res_v = d[args]
+        except KeyError:
+            d[args] = self.getvalue(op.result)
+            return False
+        else:
+            self.make_equal_to(op.result, res_v)
+            self.last_emitted_operation = REMOVED
+            return True
+
+    def optimize_GUARD_NO_EXCEPTION(self, op):
+        if self.last_emitted_operation is REMOVED:
+            return
+        self.emit_operation(op)
+
+    optimize_GUARD_EXCEPTION = optimize_GUARD_NO_EXCEPTION
+
     def force_from_effectinfo(self, effectinfo):
         # XXX we can get the wrong complexity here, if the lists
         # XXX stored on effectinfo are large
@@ -290,9 +339,20 @@
         for arraydescr in effectinfo.readonly_descrs_arrays:
             self.force_lazy_setarrayitem(arraydescr)
         for fielddescr in effectinfo.write_descrs_fields:
+            try:
+                del self.cached_dict_reads[fielddescr]
+            except KeyError:
+                pass
             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/optimizeopt/test/test_optimizebasic.py 
b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -5444,6 +5444,21 @@
         """
         self.optimize_loop(ops, expected)
 
+    def test_consecutive_getinteriorfields(self):
+        py.test.skip("we want this to pass")
+        ops = """
+        [p0, i0]
+        i1 = getinteriorfield_gc(p0, i0, descr=valuedescr)
+        i2 = getinteriorfield_gc(p0, i0, descr=valuedescr)
+        jump(i1, i2)
+        """
+        expected = """
+        [p0, i0]
+        i1 = getinteriorfield_gc(p0, i0, descr=valuedescr)
+        jump(i1, i1)
+        """
+        self.optimize_loop(ops, expected)
+
 
 class TestLLtype(BaseTestOptimizeBasic, LLtypeMixin):
     pass
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py 
b/rpython/jit/metainterp/optimizeopt/test/test_util.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_util.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py
@@ -181,28 +181,29 @@
     plaincalldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
                                      EffectInfo.MOST_GENERAL)
     nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                    EffectInfo([], [], [], []))
+                                    EffectInfo([], [], [], [], [], []))
     writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                  EffectInfo([], [], [adescr], []))
+                                  EffectInfo([], [], [], [adescr], [], []))
     writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                  EffectInfo([], [], [adescr], [arraydescr]))
+                                  EffectInfo([], [], [], [adescr], 
[arraydescr],
+                                             []))
     readadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                 EffectInfo([adescr], [], [], []))
+                                 EffectInfo([adescr], [], [], [], [], []))
     mayforcevirtdescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                 EffectInfo([nextdescr], [], [], [],
+                 EffectInfo([nextdescr], [], [], [], [], [],
                             EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE,
                             can_invalidate=True))
     arraycopydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-             EffectInfo([], [arraydescr], [], [arraydescr],
+             EffectInfo([], [arraydescr], [], [], [arraydescr], [],
                         EffectInfo.EF_CANNOT_RAISE,
                         oopspecindex=EffectInfo.OS_ARRAYCOPY))
 
     raw_malloc_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-             EffectInfo([], [], [], [],
+             EffectInfo([], [], [], [], [], [],
                         EffectInfo.EF_CAN_RAISE,
                         oopspecindex=EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR))
     raw_free_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-             EffectInfo([], [], [], [],
+             EffectInfo([], [], [], [], [], [],
                         EffectInfo.EF_CANNOT_RAISE,
                         oopspecindex=EffectInfo.OS_RAW_FREE))
 
@@ -251,17 +252,18 @@
         _oopspecindex = getattr(EffectInfo, _os)
         locals()[_name] = \
             cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+                EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
                            oopspecindex=_oopspecindex))
         #
         _oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI'))
         locals()[_name.replace('str', 'unicode')] = \
             cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+                EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
                            oopspecindex=_oopspecindex))
 
     s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-            EffectInfo([], [], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE))
+            EffectInfo([], [], [], [], [], [],
+                       oopspecindex=EffectInfo.OS_STR2UNICODE))
     #
 
     class LoopToken(AbstractDescr):
@@ -277,7 +279,7 @@
     virtualtokendescr = vrefinfo.descr_virtual_token
     virtualforceddescr = vrefinfo.descr_forced
     FUNC = lltype.FuncType([], lltype.Void)
-    ei = EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+    ei = EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
                     can_invalidate=False,
                     oopspecindex=EffectInfo.OS_JIT_FORCE_VIRTUALIZABLE)
     clear_vable = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, ei)
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
@@ -193,6 +193,107 @@
         self.check_simple_loop({'int_sub': 1, 'int_gt': 1, 'guard_true': 1,
                                 'jump': 1})
 
+    def test_dict_two_lookups(self):
+        driver = JitDriver(greens = [], reds = 'auto')
+        d = {'a': 3, 'b': 4}
+        indexes = ['a', 'b']
+
+        def f(n):
+            s = 0
+            while n > 0:
+                driver.jit_merge_point()
+                s += d[indexes[n & 1]]
+                s += d[indexes[n & 1]]
+                n -= 1
+            return s
+
+        self.meta_interp(f, [10])
+        # XXX should be one getinteriorfield_gc
+        self.check_simple_loop(call=1, getinteriorfield_gc=2,
+                               guard_no_exception=1)
+
+    def test_ordered_dict_two_lookups(self):
+        driver = JitDriver(greens = [], reds = 'auto')
+        d = OrderedDict()
+        d['a'] = 3
+        d['b'] = 4
+        indexes = ['a', 'b']
+
+        def f(n):
+            s = 0
+            while n > 0:
+                driver.jit_merge_point()
+                s += d[indexes[n & 1]]
+                s += d[indexes[n & 1]]
+                n -= 1
+            return s
+
+        self.meta_interp(f, [10])
+        # XXX should be one getinteriorfield_gc
+        self.check_simple_loop(call=1, getinteriorfield_gc=2,
+                               guard_no_exception=1)
+
+    def test_dict_insert_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]
+                d['aa'] += 1 # this will invalidate the index
+                s += d[index]
+                n -= 1
+            return s
+
+        res = self.meta_interp(f, [10])
+        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']
+
+        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]
+                d[index] += 1
+                n -= 1
+            return s
+
+        res = self.meta_interp(f, [10])
+        assert res == f(10)
+        self.check_simple_loop(call=3)
+
 
 class TestLLtype(DictTests, LLJitMixin):
     pass
diff --git a/rpython/jit/metainterp/virtualizable.py 
b/rpython/jit/metainterp/virtualizable.py
--- a/rpython/jit/metainterp/virtualizable.py
+++ b/rpython/jit/metainterp/virtualizable.py
@@ -302,7 +302,7 @@
         self.clear_vable_ptr = self.warmrunnerdesc.helper_func(
             FUNCPTR, self.clear_vable_token)
         FUNC = FUNCPTR.TO
-        ei = EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+        ei = EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
                         can_invalidate=False,
                         oopspecindex=EffectInfo.OS_JIT_FORCE_VIRTUALIZABLE)
 
diff --git a/rpython/rtyper/lltypesystem/rdict.py 
b/rpython/rtyper/lltypesystem/rdict.py
--- a/rpython/rtyper/lltypesystem/rdict.py
+++ b/rpython/rtyper/lltypesystem/rdict.py
@@ -569,6 +569,7 @@
 PERTURB_SHIFT = 5
 
 @jit.look_inside_iff(lambda d, key, hash: jit.isvirtual(d) and 
jit.isconstant(key))
[email protected]('dict.lookup(d, key, hash)')
 def ll_dict_lookup(d, key, hash):
     entries = d.entries
     ENTRIES = lltype.typeOf(entries).TO
diff --git a/rpython/rtyper/lltypesystem/rordereddict.py 
b/rpython/rtyper/lltypesystem/rordereddict.py
--- a/rpython/rtyper/lltypesystem/rordereddict.py
+++ b/rpython/rtyper/lltypesystem/rordereddict.py
@@ -709,6 +709,7 @@
 
 @jit.look_inside_iff(lambda d, key, hash, store_flag, T:
                      jit.isvirtual(d) and jit.isconstant(key))
[email protected]('ordereddict.lookup(d, key, hash, store_flag, T)')
 def ll_dict_lookup(d, key, hash, store_flag, T):
     INDEXES = _ll_ptr_to_array_of(T)
     entries = d.entries
diff --git a/rpython/translator/backendopt/test/test_writeanalyze.py 
b/rpython/translator/backendopt/test/test_writeanalyze.py
--- a/rpython/translator/backendopt/test/test_writeanalyze.py
+++ b/rpython/translator/backendopt/test/test_writeanalyze.py
@@ -353,3 +353,23 @@
 
         result = wa.analyze(fgraph.startblock.operations[-1])
         assert list(result) == [("struct", lltype.Ptr(S), "x")]
+
+    def test_interiorfield(self):
+        A = lltype.GcArray(lltype.Struct('x', ('x', lltype.Signed),
+                                         ('y', lltype.Signed)))
+
+        def g(x):
+            a = lltype.malloc(A, 1)
+            a[0].y = 3
+            return f(a, x)
+        
+        def f(a, x):
+            a[0].x = x
+            return a[0].y
+
+        t, wa = self.translate(g, [int])
+        ggraph = graphof(t, g)
+        result = wa.analyze(ggraph.startblock.operations[-1])
+        res = list(result)
+        assert ('readinteriorfield', lltype.Ptr(A), 'y') in res
+        assert ('interiorfield', lltype.Ptr(A), 'x') in res
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]):
@@ -45,11 +51,18 @@
         elif op.opname == "setarrayitem":
             if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]):
                 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]):
+                name = self._getinteriorname(op)
+                return self._interiorfield_result(op.args[0].concretetype, 
name)
         return empty_set
 
     def _array_result(self, TYPE):
         return frozenset([("array", TYPE)])
 
+    def _interiorfield_result(self, TYPE, fieldname):
+        return frozenset([("interiorfield", TYPE, fieldname)])
+
     def compute_graph_info(self, graph):
         return FreshMallocs(graph)
 
@@ -99,4 +112,8 @@
         elif op.opname == "getarrayitem":
             return frozenset([
                 ("readarray", op.args[0].concretetype)])
+        elif op.opname == "getinteriorfield":
+            name = self._getinteriorname(op)
+            return frozenset([("readinteriorfield", op.args[0].concretetype,
+                            name)])
         return WriteAnalyzer.analyze_simple_operation(self, op, graphinfo)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to