Hi Alex,

This is part of a more general problem: If a virtual is forced the heap
cache is not informed of the values that are written into the newly
allocated object. This is useful also for fields that are not
immutable. Do you maybe feel like generalizing this?

Cheers,

Carl Friedrich

On 06/21/2011 08:33 PM, alex_gaynor wrote:
Author: Alex Gaynor<[email protected]>
Branch:
Changeset: r45043:456273d0b54f
Date: 2011-06-21 11:37 -0700
http://bitbucket.org/pypy/pypy/changeset/456273d0b54f/

Log:    When a virtual is forced, and then subsequenly an immutable field is
        read out of it, the value is known if it was seen in a setfield,
        because it can't be set again by anything, therefore remove the
        getfield_gc_pure for it. Thanks to fijal for the review.

diff --git a/pypy/jit/metainterp/optimizeopt/heap.py 
b/pypy/jit/metainterp/optimizeopt/heap.py
--- a/pypy/jit/metainterp/optimizeopt/heap.py
+++ b/pypy/jit/metainterp/optimizeopt/heap.py
@@ -112,7 +112,7 @@

  class OptHeap(Optimization):
      """Cache repeated heap accesses"""
-
+
      def __init__(self):
          # cached fields:  {descr: CachedField}
          self.cached_fields = {}
@@ -129,7 +129,7 @@
              self.force_all_lazy_setfields()
          else:
              assert 0   # was: new.lazy_setfields = self.lazy_setfields
-
+
          for descr, d in self.cached_fields.items():
              new.cached_fields[descr] = d.get_reconstructed(optimizer, 
valuemap)

diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py 
b/pypy/jit/metainterp/optimizeopt/optimizer.py
--- a/pypy/jit/metainterp/optimizeopt/optimizer.py
+++ b/pypy/jit/metainterp/optimizeopt/optimizer.py
@@ -141,6 +141,9 @@
          # meaning it has been forced.
          return self.box is None

+    def is_forced_virtual(self):
+        return False
+
      def getfield(self, ofs, default):
          raise NotImplementedError

diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py 
b/pypy/jit/metainterp/optimizeopt/rewrite.py
--- a/pypy/jit/metainterp/optimizeopt/rewrite.py
+++ b/pypy/jit/metainterp/optimizeopt/rewrite.py
@@ -219,7 +219,7 @@
                  break
              arg_consts.append(const)
          else:
-            # all constant arguments: check if we already know the reslut
+            # all constant arguments: check if we already know the result
              try:
                  result = self.optimizer.call_pure_results[arg_consts]
              except KeyError:
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py 
b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -5837,3 +5837,30 @@
          jump(i3, i4)
          """
          self.optimize_loop(ops, expected)
+
+    def test_forced_virtual_pure_getfield(self):
+        ops = """
+        [p0]
+        p1 = getfield_gc_pure(p0, descr=valuedescr)
+        jump(p1)
+        """
+        self.optimize_loop(ops, ops)
+
+        ops = """
+        [p0]
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p1, p0, descr=valuedescr)
+        escape(p1)
+        p2 = getfield_gc_pure(p1, descr=valuedescr)
+        escape(p2)
+        jump(p0)
+        """
+        expected = """
+        [p0]
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p1, p0, descr=valuedescr)
+        escape(p1)
+        escape(p0)
+        jump(p0)
+        """
+        self.optimize_loop(ops, expected)
\ No newline at end of file
diff --git a/pypy/jit/metainterp/optimizeopt/virtualize.py 
b/pypy/jit/metainterp/optimizeopt/virtualize.py
--- a/pypy/jit/metainterp/optimizeopt/virtualize.py
+++ b/pypy/jit/metainterp/optimizeopt/virtualize.py
@@ -20,6 +20,9 @@
          self.source_op = source_op  # the NEW_WITH_VTABLE/NEW_ARRAY operation
                                      # that builds this box

+    def is_forced_virtual(self):
+        return self.box is not None
+
      def get_key_box(self):
          if self.box is None:
              return self.keybox
@@ -120,7 +123,6 @@
                  op = ResOperation(rop.SETFIELD_GC, [box, subbox], None,
                                    descr=ofs)
                  newoperations.append(op)
-            self._fields = None

      def _get_field_descr_list(self):
          _cached_sorted_fields = self._cached_sorted_fields
@@ -351,7 +353,7 @@
          if not self.optimizer.cpu.ts.CONST_NULL.same_constant(objbox):
              seo(ResOperation(rop.SETFIELD_GC, op.getarglist(), None,
                               descr = vrefinfo.descr_forced))
-
+
          # - set 'virtual_token' to TOKEN_NONE
          args = [op.getarg(0), ConstInt(vrefinfo.TOKEN_NONE)]
          seo(ResOperation(rop.SETFIELD_GC, args, None,
@@ -365,6 +367,14 @@

      def optimize_GETFIELD_GC(self, op):
          value = self.getvalue(op.getarg(0))
+        # If this is an immutable field (as indicated by op.is_always_pure())
+        # then it's safe to reuse the virtual's field, even if it has been
+        # forced, because it should never be written to again.
+        if value.is_forced_virtual() and op.is_always_pure():
+            fieldvalue = value.getfield(op.getdescr(), None)
+            if fieldvalue is not None:
+                self.make_equal_to(op.result, fieldvalue)
+                return
          if value.is_virtual():
              assert isinstance(value, AbstractVirtualValue)
              fieldvalue = value.getfield(op.getdescr(), None)
@@ -382,6 +392,7 @@

      def optimize_SETFIELD_GC(self, op):
          value = self.getvalue(op.getarg(0))
+
          if value.is_virtual():
              fieldvalue = self.getvalue(op.getarg(1))
              value.setfield(op.getdescr(), fieldvalue)
diff --git a/pypy/jit/metainterp/test/test_dict.py 
b/pypy/jit/metainterp/test/test_dict.py
--- a/pypy/jit/metainterp/test/test_dict.py
+++ b/pypy/jit/metainterp/test/test_dict.py
@@ -130,6 +130,38 @@
          assert res == 50
          self.check_loops(int_mod=1)

+    def test_repeated_lookup(self):
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'd'])
+        class Wrapper(object):
+            _immutable_fields_ = ["value"]
+            def __init__(self, value):
+                self.value = value
+        def eq_func(a, b):
+            return a.value == b.value
+        def hash_func(x):
+            return objectmodel.compute_hash(x.value)
+
+        def f(n):
+            d = None
+            while n>  0:
+                myjitdriver.jit_merge_point(n=n, d=d)
+                d = objectmodel.r_dict(eq_func, hash_func)
+                y = Wrapper(str(n))
+                d[y] = n - 1
+                n = d[y]
+            return d[Wrapper(str(n + 1))]
+
+        res = self.meta_interp(f, [100], listops=True)
+        assert res == f(50)
+        # XXX: ideally there would be 7 calls here, but repeated CALL_PURE with
+        # the same arguments are not folded, because we have conflicting
+        # definitions of pure, once strhash can be appropriately folded
+        # this should be decreased to seven.
+        self.check_loops({"call": 8, "guard_false": 1, "guard_no_exception": 5,
+                          "guard_true": 1, "int_and": 1, "int_gt": 1,
+                          "int_is_true": 1, "int_sub": 1, "jump": 1,
+                          "new_with_vtable": 1, "setfield_gc": 1})
+

  class TestOOtype(DictTests, OOJitMixin):
      pass
diff --git a/pypy/rpython/lltypesystem/rstr.py 
b/pypy/rpython/lltypesystem/rstr.py
--- a/pypy/rpython/lltypesystem/rstr.py
+++ b/pypy/rpython/lltypesystem/rstr.py
@@ -323,6 +323,8 @@
          return s
      ll_str2unicode.oopspec = 'str.str2unicode(str)'

+    # it's pure but it does not look like it
+    @purefunction
      def ll_strhash(s):
          # unlike CPython, there is no reason to avoid to return -1
          # but our malloc initializes the memory to zero, so we use zero as the
@@ -334,7 +336,6 @@
                  x = 29872897
              s.hash = x
          return x
-    ll_strhash._pure_function_ = True # it's pure but it does not look like it

      def ll_strfasthash(s):
          return s.hash     # assumes that the hash is already computed
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to