Author: Carl Friedrich Bolz <[email protected]>
Branch: nonquadratic-heapcache
Changeset: r75933:33600f2c12d8
Date: 2015-02-17 11:36 +0100
http://bitbucket.org/pypy/pypy/changeset/33600f2c12d8/

Log:    refactor heap cache to use HeapCacheValues everywhere

diff --git a/rpython/jit/metainterp/heapcache.py 
b/rpython/jit/metainterp/heapcache.py
--- a/rpython/jit/metainterp/heapcache.py
+++ b/rpython/jit/metainterp/heapcache.py
@@ -5,13 +5,22 @@
     def __init__(self, box):
         self.box = box
         self.known_class = False
+        # did we see the allocation during tracing?
+        self.seen_allocation = False
+        self.is_unescaped = False
+        self.likely_virtual = False
         self.nonstandard_virtualizable = False
+        self.lengthvalue = None
+        self.dependencies = None
+
+    def __repr__(self):
+        return 'HeapCacheValue(%s)' % (self.box, )
 
 class HeapCache(object):
     def __init__(self):
         self.reset()
 
-    def reset(self, reset_virtuals=True, trace_branch=True):
+    def reset(self):
         # maps boxes to values
         self.values = {}
         # store the boxes that contain newly allocated objects, this maps the
@@ -19,54 +28,48 @@
         # escaped the trace or not (True means the box never escaped, False
         # means it did escape), its presences in the mapping shows that it was
         # allocated inside the trace
-        if trace_branch:
-            self.new_boxes = {}
-        else:
-            for box in self.new_boxes:
-                self.new_boxes[box] = False
-        if reset_virtuals:
-            self.likely_virtuals = {}      # only for jit.isvirtual()
+        #if trace_branch:
+            #self.new_boxes = {}
+        #    pass
+        #else:
+            #for box in self.new_boxes:
+            #    self.new_boxes[box] = False
+        #    pass
+        #if reset_virtuals:
+        #    self.likely_virtuals = {}      # only for jit.isvirtual()
         # Tracks which boxes should be marked as escaped when the key box
         # escapes.
-        self.dependencies = {}
-        # contains frame boxes that are not virtualizables
-        if trace_branch:
-            self.nonstandard_virtualizables = {}
+        #self.dependencies = {}
 
         # heap cache
-        # maps descrs to {from_box, to_box} dicts
+        # maps descrs to {from_value, to_value} dicts
         self.heap_cache = {}
         # heap array cache
-        # maps descrs to {index: {from_box: to_box}} dicts
+        # maps descrs to {index: {from_value: to_value}} dicts
         self.heap_array_cache = {}
-        # cache the length of arrays
-        self.length_cache = {}
 
-        # replace_box is called surprisingly often, therefore it's not 
efficient
-        # to go over all the dicts and fix them.
-        # instead, these two dicts are kept, and a replace_box adds an entry to
-        # each of them.
-        # every time one of the dicts heap_cache, heap_array_cache, 
length_cache
-        # is accessed, suitable indirections need to be performed
+    def reset_keep_likely_virtuals(self):
+        for value in self.values.itervalues():
+            value.is_unescaped = False
+        self.heap_cache = {}
+        self.heap_array_cache = {}
 
-        # this looks all very subtle, but in practice the patterns of
-        # replacements should not be that complex. Usually a box is replaced by
-        # a const, once. Also, if something goes wrong, the effect is that less
-        # caching than possible is done, which is not a huge problem.
-        self.input_indirections = {}
-        self.output_indirections = {}
-
-    def get_value(self, box):
+    def getvalue(self, box):
         value = self.values.get(box, None)
         if not value:
             value = self.values[box] = HeapCacheValue(box)
         return value
 
+    def getvalues(self, boxes):
+        return [self.getvalue(box) for box in boxes]
+
     def _input_indirection(self, box):
-        return self.input_indirections.get(box, box)
+        value = self.values.get(box, None)
+        if value is None:
+            return box
+        return value.box
 
-    def _output_indirection(self, box):
-        return self.output_indirections.get(box, box)
+    _output_indirection = _input_indirection
 
     def invalidate_caches(self, opnum, descr, argboxes):
         self.mark_escaped(opnum, descr, argboxes)
@@ -75,18 +78,22 @@
     def mark_escaped(self, opnum, descr, argboxes):
         if opnum == rop.SETFIELD_GC:
             assert len(argboxes) == 2
-            box, valuebox = argboxes
-            if self.is_unescaped(box) and self.is_unescaped(valuebox):
-                self.dependencies.setdefault(box, []).append(valuebox)
+            value, fieldvalue = self.getvalues(argboxes)
+            if value.is_unescaped and fieldvalue.is_unescaped:
+                if value.dependencies is None:
+                    value.dependencies = []
+                value.dependencies.append(fieldvalue)
             else:
-                self._escape(valuebox)
+                self._escape(fieldvalue)
         elif opnum == rop.SETARRAYITEM_GC:
             assert len(argboxes) == 3
-            box, indexbox, valuebox = argboxes
-            if self.is_unescaped(box) and self.is_unescaped(valuebox):
-                self.dependencies.setdefault(box, []).append(valuebox)
+            value, indexvalue, fieldvalue = self.getvalues(argboxes)
+            if value.is_unescaped and fieldvalue.is_unescaped:
+                if value.dependencies is None:
+                    value.dependencies = []
+                value.dependencies.append(fieldvalue)
             else:
-                self._escape(valuebox)
+                self._escape(fieldvalue)
         elif (opnum == rop.CALL and
               descr.get_extra_info().oopspecindex == 
descr.get_extra_info().OS_ARRAYCOPY and
               isinstance(argboxes[3], ConstInt) and
@@ -106,25 +113,20 @@
               opnum != rop.INSTANCE_PTR_EQ and
               opnum != rop.INSTANCE_PTR_NE):
             for box in argboxes:
-                self._escape(box)
+                self._escape_box(box)
 
-    def _escape(self, box):
-        try:
-            unescaped = self.new_boxes[box]
-        except KeyError:
-            pass
-        else:
-            if unescaped:
-                self.new_boxes[box] = False
-        try:
-            del self.likely_virtuals[box]
-        except KeyError:
-            pass
-        try:
-            deps = self.dependencies.pop(box)
-        except KeyError:
-            pass
-        else:
+    def _escape_box(self, box):
+        value = self.values.get(box, None)
+        if not value:
+            return
+        self._escape(value)
+
+    def _escape(self, value):
+        value.is_unescaped = False
+        value.likely_virtual = False
+        deps = value.dependencies
+        value.dependencies = None
+        if deps is not None:
             for dep in deps:
                 self._escape(dep)
 
@@ -157,6 +159,7 @@
             # A special case for ll_arraycopy, because it is so common, and its
             # effects are so well defined.
             elif effectinfo.oopspecindex == effectinfo.OS_ARRAYCOPY:
+                seen_allocation_of_target = 
self.getvalue(argboxes[2]).seen_allocation
                 if (
                     isinstance(argboxes[3], ConstInt) and
                     isinstance(argboxes[4], ConstInt) and
@@ -187,43 +190,43 @@
                             except KeyError:
                                 pass
                             else:
-                                if argboxes[2] in self.new_boxes:
-                                    for frombox in idx_cache.keys():
-                                        if not self.is_unescaped(frombox):
-                                            del idx_cache[frombox]
+                                if seen_allocation_of_target:
+                                    for fromvalue in idx_cache.keys():
+                                        if not fromvalue.seen_allocation:
+                                            del idx_cache[fromvalue]
                                 else:
                                     idx_cache.clear()
                     return
                 elif (
-                    argboxes[2] in self.new_boxes and
+                    seen_allocation_of_target and
                     len(effectinfo.write_descrs_arrays) == 1
                 ):
                     # Fish the descr out of the effectinfo
                     cache = 
self.heap_array_cache.get(effectinfo.write_descrs_arrays[0], None)
                     if cache is not None:
                         for idx, cache in cache.iteritems():
-                            for frombox in cache.keys():
-                                if not self.is_unescaped(frombox):
-                                    del cache[frombox]
+                            for fromvalue in cache.keys():
+                                if not fromvalue.seen_allocation:
+                                    del cache[fromvalue]
                     return
             else:
                 # Only invalidate things that are either escaped or arguments
-                for descr, boxes in self.heap_cache.iteritems():
-                    for box in boxes.keys():
-                        if not self.is_unescaped(box) or box in argboxes:
-                            del boxes[box]
+                for descr, values in self.heap_cache.iteritems():
+                    for value in values.keys():
+                        if not self.is_unescaped(value.box) or value.box in 
argboxes:
+                            del values[value]
                 for descr, indices in self.heap_array_cache.iteritems():
-                    for boxes in indices.itervalues():
-                        for box in boxes.keys():
-                            if not self.is_unescaped(box) or box in argboxes:
-                                del boxes[box]
+                    for values in indices.itervalues():
+                        for value in values.keys():
+                            if not self.is_unescaped(value.box) or value.box 
in argboxes:
+                                del values[value]
                 return
 
         # XXX not completely sure, but I *think* it is needed to reset() the
         # state at least in the 'CALL_*' operations that release the GIL.  We
         # tried to do only the kind of resetting done by the two loops just
         # above, but hit an assertion in "pypy test_multiprocessing.py".
-        self.reset(reset_virtuals=False, trace_branch=False)
+        self.reset_keep_likely_virtuals()
 
     def is_class_known(self, box):
         value = self.values.get(box, None)
@@ -232,7 +235,7 @@
         return False
 
     def class_now_known(self, box):
-        self.get_value(box).known_class = True
+        self.getvalue(box).known_class = True
 
     def is_nonstandard_virtualizable(self, box):
         value = self.values.get(box, None)
@@ -241,34 +244,44 @@
         return False
 
     def nonstandard_virtualizables_now_known(self, box):
-        self.get_value(box).nonstandard_virtualizable = True
+        self.getvalue(box).nonstandard_virtualizable = True
 
     def is_unescaped(self, box):
-        return self.new_boxes.get(box, False)
+        value = self.values.get(box, None)
+        if value:
+            return value.is_unescaped
+        return False
 
     def is_likely_virtual(self, box):
-        return box in self.likely_virtuals
+        value = self.values.get(box, None)
+        if value:
+            return value.likely_virtual
+        return False
 
     def new(self, box):
-        self.new_boxes[box] = True
-        self.likely_virtuals[box] = None
+        value = self.getvalue(box)
+        value.is_unescaped = True
+        value.likely_virtual = True
+        value.seen_allocation = True
 
     def new_array(self, box, lengthbox):
         self.new(box)
         self.arraylen_now_known(box, lengthbox)
 
     def getfield(self, box, descr):
-        box = self._input_indirection(box)
-        d = self.heap_cache.get(descr, None)
-        if d:
-            tobox = d.get(box, None)
-            return self._output_indirection(tobox)
+        value = self.values.get(box, None)
+        if value:
+            d = self.heap_cache.get(descr, None)
+            if d:
+                tovalue = d.get(value, None)
+                if tovalue:
+                    return tovalue.box
         return None
 
     def getfield_now_known(self, box, descr, fieldbox):
-        box = self._input_indirection(box)
-        fieldbox = self._input_indirection(fieldbox)
-        self.heap_cache.setdefault(descr, {})[box] = fieldbox
+        value = self.getvalue(box)
+        fieldvalue = self.getvalue(fieldbox)
+        self.heap_cache.setdefault(descr, {})[value] = fieldvalue
 
     def setfield(self, box, fieldbox, descr):
         d = self.heap_cache.get(descr, None)
@@ -276,51 +289,55 @@
         self.heap_cache[descr] = new_d
 
     def _do_write_with_aliasing(self, d, box, fieldbox):
-        box = self._input_indirection(box)
-        fieldbox = self._input_indirection(fieldbox)
+        value = self.getvalue(box)
+        fieldvalue = self.getvalue(fieldbox)
         # slightly subtle logic here
-        # a write to an arbitrary box, all other boxes can alias this one
-        if not d or box not in self.new_boxes:
+        # a write to an arbitrary value, all other valuees can alias this one
+        if not d or not value.seen_allocation:
             # therefore we throw away the cache
-            return {box: fieldbox}
+            return {value: fieldvalue}
         # the object we are writing to is freshly allocated
-        # only remove some boxes from the cache
+        # only remove some valuees from the cache
         new_d = {}
-        for frombox, tobox in d.iteritems():
-            # the other box is *also* freshly allocated
-            # therefore frombox and box *must* contain different objects
+        for fromvalue, tovalue in d.iteritems():
+            # the other value is *also* one where we saw the allocation
+            # therefore fromvalue and value *must* contain different objects
             # thus we can keep it in the cache
-            if frombox in self.new_boxes:
-                new_d[frombox] = tobox
-        new_d[box] = fieldbox
-        print len(self.new_boxes), len(d), len(new_d)
+            if fromvalue.seen_allocation:
+                new_d[fromvalue] = tovalue
+        new_d[value] = fieldvalue
         return new_d
 
     def getarrayitem(self, box, indexbox, descr):
         if not isinstance(indexbox, ConstInt):
-            return
-        box = self._input_indirection(box)
+            return None
+        value = self.values.get(box, None)
+        if value is None:
+            return None
         index = indexbox.getint()
         cache = self.heap_array_cache.get(descr, None)
         if cache:
             indexcache = cache.get(index, None)
             if indexcache is not None:
-                return self._output_indirection(indexcache.get(box, None))
+                resvalue = indexcache.get(value, None)
+                if resvalue:
+                    return resvalue.box
+                return None
 
-    def getarrayitem_now_known(self, box, indexbox, valuebox, descr):
+    def getarrayitem_now_known(self, box, indexbox, fieldbox, descr):
         if not isinstance(indexbox, ConstInt):
             return
-        box = self._input_indirection(box)
-        valuebox = self._input_indirection(valuebox)
+        value = self.getvalue(box)
+        fieldvalue = self.getvalue(fieldbox)
         index = indexbox.getint()
         cache = self.heap_array_cache.setdefault(descr, {})
         indexcache = cache.get(index, None)
         if indexcache is not None:
-            indexcache[box] = valuebox
+            indexcache[value] = fieldvalue
         else:
-            cache[index] = {box: valuebox}
+            cache[index] = {value: fieldvalue}
 
-    def setarrayitem(self, box, indexbox, valuebox, descr):
+    def setarrayitem(self, box, indexbox, fieldbox, descr):
         if not isinstance(indexbox, ConstInt):
             cache = self.heap_array_cache.get(descr, None)
             if cache is not None:
@@ -329,16 +346,21 @@
         index = indexbox.getint()
         cache = self.heap_array_cache.setdefault(descr, {})
         indexcache = cache.get(index, None)
-        cache[index] = self._do_write_with_aliasing(indexcache, box, valuebox)
+        cache[index] = self._do_write_with_aliasing(indexcache, box, fieldbox)
 
     def arraylen(self, box):
-        box = self._input_indirection(box)
-        return self._output_indirection(self.length_cache.get(box, None))
+        value = self.values.get(box, None)
+        if value and value.length:
+            return value.length.box
+        return None
 
     def arraylen_now_known(self, box, lengthbox):
-        box = self._input_indirection(box)
-        self.length_cache[box] = self._input_indirection(lengthbox)
+        value = self.getvalue(box)
+        value.length = self.getvalue(lengthbox)
 
     def replace_box(self, oldbox, newbox):
-        self.input_indirections[self._output_indirection(newbox)] = 
self._input_indirection(oldbox)
-        self.output_indirections[self._input_indirection(oldbox)] = 
self._output_indirection(newbox)
+        value = self.values.get(oldbox, None)
+        if value is None:
+            return
+        value.box = newbox
+        self.values[newbox] = value
diff --git a/rpython/jit/metainterp/pyjitpl.py 
b/rpython/jit/metainterp/pyjitpl.py
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -2192,7 +2192,8 @@
                 duplicates[box] = None
 
     def reached_loop_header(self, greenboxes, redboxes):
-        self.heapcache.reset(reset_virtuals=False)
+        #self.heapcache.reset(reset_virtuals=False)
+        self.heapcache.reset_keep_likely_virtuals()
 
         duplicates = {}
         self.remove_consts_and_duplicates(redboxes, len(redboxes),
diff --git a/rpython/jit/metainterp/test/test_heapcache.py 
b/rpython/jit/metainterp/test/test_heapcache.py
--- a/rpython/jit/metainterp/test/test_heapcache.py
+++ b/rpython/jit/metainterp/test/test_heapcache.py
@@ -594,9 +594,9 @@
         h.new(box1)
         assert h.is_unescaped(box1)
         assert h.is_likely_virtual(box1)
-        h.reset(reset_virtuals=False)
+        h.reset_keep_likely_virtuals()
         assert not h.is_unescaped(box1)
         assert h.is_likely_virtual(box1)
-        h._escape(box1)
+        h._escape_box(box1)
         assert not h.is_unescaped(box1)
         assert not h.is_likely_virtual(box1)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to