[pypy-svn] r68405 - in pypy/branch/gc-hash/pypy: rpython/lltypesystem rpython/memory/gc rpython/memory/gctransform translator/c/test

arigo at codespeak.net arigo at codespeak.net
Tue Oct 13 23:29:37 CEST 2009


Author: arigo
Date: Tue Oct 13 23:29:36 2009
New Revision: 68405

Modified:
   pypy/branch/gc-hash/pypy/rpython/lltypesystem/lloperation.py
   pypy/branch/gc-hash/pypy/rpython/lltypesystem/lltype.py
   pypy/branch/gc-hash/pypy/rpython/memory/gc/semispace.py
   pypy/branch/gc-hash/pypy/rpython/memory/gctransform/framework.py
   pypy/branch/gc-hash/pypy/translator/c/test/test_newgc.py
Log:
Test and fix, still for the SemiSpace GC.  Needs
to shrink temporarily the end of the space, and
possibly trigger collects from identityhash().


Modified: pypy/branch/gc-hash/pypy/rpython/lltypesystem/lloperation.py
==============================================================================
--- pypy/branch/gc-hash/pypy/rpython/lltypesystem/lloperation.py	(original)
+++ pypy/branch/gc-hash/pypy/rpython/lltypesystem/lloperation.py	Tue Oct 13 23:29:36 2009
@@ -442,6 +442,7 @@
     # see rlib/objectmodel for gc_identityhash and gc_id
     'gc_identityhash':      LLOp(canraise=(MemoryError,), sideeffects=False),
     'gc_id':                LLOp(canraise=(MemoryError,), sideeffects=False),
+    'gc_obtain_free_space': LLOp(),
     'gc_set_max_heap_size': LLOp(),
     'gc_can_move'         : LLOp(sideeffects=False),
     'gc_thread_prepare'   : LLOp(canraise=(MemoryError,)),

Modified: pypy/branch/gc-hash/pypy/rpython/lltypesystem/lltype.py
==============================================================================
--- pypy/branch/gc-hash/pypy/rpython/lltypesystem/lltype.py	(original)
+++ pypy/branch/gc-hash/pypy/rpython/lltypesystem/lltype.py	Tue Oct 13 23:29:36 2009
@@ -1851,7 +1851,7 @@
 def identityhash(p):
     """Returns the lltype-level hash of the given GcStruct.
     Also works with most ootype objects.  Not for NULL.
-    See rlib.objectmodel.compute_identiy_hash() for more
+    See rlib.objectmodel.compute_identity_hash() for more
     information about the RPython-level meaning of this.
     """
     assert p

Modified: pypy/branch/gc-hash/pypy/rpython/memory/gc/semispace.py
==============================================================================
--- pypy/branch/gc-hash/pypy/rpython/memory/gc/semispace.py	(original)
+++ pypy/branch/gc-hash/pypy/rpython/memory/gc/semispace.py	Tue Oct 13 23:29:36 2009
@@ -36,7 +36,9 @@
 
     HDR = lltype.Struct('header', ('tid', lltype.Signed))   # XXX or rffi.INT?
     typeid_is_in_field = 'tid'
-    withhash_flag_is_in_field = 'tid', (GCFLAG_HASHTAKEN|GCFLAG_HASHFIELD)
+    withhash_flag_is_in_field = 'tid', GCFLAG_HASHFIELD
+    # ^^^ all prebuilt objects have GCFLAG_HASHTAKEN, but only some have
+    #     GCFLAG_HASHFIELD (and then they are one word longer).
     FORWARDSTUB = lltype.GcStruct('forwarding_stub',
                                   ('forw', llmemory.Address))
     FORWARDSTUBPTR = lltype.Ptr(FORWARDSTUB)
@@ -430,7 +432,7 @@
 
     def init_gc_object_immortal(self, addr, typeid16, flags=0):
         hdr = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(self.HDR))
-        flags |= GCFLAG_EXTERNAL | GCFLAG_FORWARDED
+        flags |= GCFLAG_EXTERNAL | GCFLAG_FORWARDED | GCFLAG_HASHTAKEN
         hdr.tid = self.combine(typeid16, flags)
         # immortal objects always have GCFLAG_FORWARDED set;
         # see get_forwarding_address().
@@ -593,12 +595,33 @@
 
     STATISTICS_NUMBERS = 0
 
-    def identityhash(self, obj):
-        obj = llmemory.cast_ptr_to_adr(obj)
-        hdr = self.header(obj)
-        if hdr.tid & GCFLAG_HASHFIELD:
-            obj += self.get_size(obj)
-            return obj.signed[0]
-        else:
-            hdr.tid |= GCFLAG_HASHTAKEN
-            return llmemory.cast_adr_to_int(obj)
+    def identityhash(self, gcobj):
+        # The following code should run at most twice.
+        while 1:
+            obj = llmemory.cast_ptr_to_adr(gcobj)
+            hdr = self.header(obj)
+            #
+            if hdr.tid & GCFLAG_HASHFIELD:  # the hash is in a field at the end
+                obj += self.get_size(obj)
+                return obj.signed[0]
+            #
+            if not (hdr.tid & GCFLAG_HASHTAKEN):
+                # It's the first time we ask for a hash, and it's not an
+                # external object.  Shrink the top of space by the extra
+                # hash word that will be needed after a collect.
+                shrunk_top = self.top_of_space - llmemory.sizeof(lltype.Signed)
+                if shrunk_top < self.free:
+                    # Cannot shrink!  Do a collection, asking for at least
+                    # one word of free space, and try again.  May raise
+                    # MemoryError.  Obscure: not called directly, but
+                    # across an llop, to make sure that there is the
+                    # correct push_roots/pop_roots around the call...
+                    llop.gc_obtain_free_space(llmemory.Address,
+                                              llmemory.sizeof(lltype.Signed))
+                    continue
+                # Now we can have side-effects: set GCFLAG_HASHTAKEN
+                # and lower the top of space.
+                self.top_of_space = shrunk_top
+                hdr.tid |= GCFLAG_HASHTAKEN
+            #
+            return llmemory.cast_adr_to_int(obj)  # direct case

Modified: pypy/branch/gc-hash/pypy/rpython/memory/gctransform/framework.py
==============================================================================
--- pypy/branch/gc-hash/pypy/rpython/memory/gctransform/framework.py	(original)
+++ pypy/branch/gc-hash/pypy/rpython/memory/gctransform/framework.py	Tue Oct 13 23:29:36 2009
@@ -339,7 +339,12 @@
 
         self.identityhash_ptr = getfn(GCClass.identityhash.im_func,
                                       [s_gc, s_gcref],
-                                      annmodel.SomeInteger())
+                                      annmodel.SomeInteger(),
+                                      minimal_transform=False)
+        if getattr(GCClass, 'obtain_free_space', False):
+            self.obtainfreespace_ptr = getfn(GCClass.obtain_free_space.im_func,
+                                             [s_gc, annmodel.SomeInteger()],
+                                             annmodel.SomeAddress())
 
         if GCClass.moving_gc:
             self.id_ptr = getfn(GCClass.id.im_func,
@@ -752,6 +757,14 @@
         else:
             hop.rename('cast_ptr_to_int')     # works nicely for non-moving GCs
 
+    def gct_gc_obtain_free_space(self, hop):
+        livevars = self.push_roots(hop)
+        [v_number] = hop.spaceop.args
+        hop.genop("direct_call",
+                  [self.obtainfreespace_ptr, self.c_const_gc, v_number],
+                  resultvar=hop.spaceop.result)
+        self.pop_roots(hop, livevars)
+
     def gct_gc_set_max_heap_size(self, hop):
         [v_size] = hop.spaceop.args
         hop.genop("direct_call", [self.set_max_heap_size_ptr,

Modified: pypy/branch/gc-hash/pypy/translator/c/test/test_newgc.py
==============================================================================
--- pypy/branch/gc-hash/pypy/translator/c/test/test_newgc.py	(original)
+++ pypy/branch/gc-hash/pypy/translator/c/test/test_newgc.py	Tue Oct 13 23:29:36 2009
@@ -88,6 +88,7 @@
         if not args:
             args = (-1, )
         num = self.name_to_func[name]
+        print 'Running %r (test number %d)' % (name, num)
         res = self.c_allfuncs(num, *args)
         if self.funcsstr[num]:
             return res
@@ -720,6 +721,54 @@
         res = self.run('hash_preservation')
         assert res == 42
 
+    def define_hash_overflow(self):
+        from pypy.rlib.objectmodel import compute_identity_hash
+        class X(object):
+            pass
+        def g(n):
+            x1 = None
+            i = 0
+            while i < n:
+                x2 = X()
+                x2.prev = x1
+                x1 = x2
+                i += 1
+            return x1
+        def h(n):
+            xr = x1 = g(n)
+            i = 0
+            while i < n:
+                xr.hash = compute_identity_hash(xr)
+                # ^^^ likely to trigger a collection
+                xr = xr.prev
+                i += 1
+            assert xr is None
+            i = 0
+            xr = x1
+            i = 0
+            while i < n:
+                if xr.hash != compute_identity_hash(xr):
+                    return i
+                xr = xr.prev
+                i += 1
+            assert xr is None
+            return -1
+        def f():
+            # numbers optimized for a 8MB space
+            for n in [225000, 250000, 300000, 380000,
+                      460000, 570000, 800000]:
+                os.write(2, 'case %d\n' % n)
+                rgc.collect()
+                err = h(n)
+                if err >= 0:
+                    return err
+            return -42
+        return f
+
+    def test_hash_overflow(self):
+        res = self.run('hash_overflow')
+        assert res == -42
+
 class TestSemiSpaceGC(TestUsingFramework, snippet.SemiSpaceGCTestDefines):
     gcpolicy = "semispace"
     should_be_moving = True



More information about the Pypy-commit mailing list