[pypy-svn] r79131 - in pypy/release/1.4.x: . pypy pypy/jit/backend/llsupport pypy/jit/backend/llsupport/test pypy/jit/metainterp/optimizeopt pypy/module/__pypy__ pypy/module/array/benchmark pypy/module/array/test pypy/module/gc pypy/module/gc/test pypy/objspace/std pypy/objspace/std/test pypy/rlib pypy/rlib/test pypy/rpython pypy/rpython/lltypesystem pypy/rpython/memory/gc pypy/rpython/memory/gc/test pypy/rpython/memory/gctransform pypy/tool pypy/translator/c pypy/translator/c/test

fijal at codespeak.net fijal at codespeak.net
Mon Nov 15 20:25:18 CET 2010


Author: fijal
Date: Mon Nov 15 20:25:15 2010
New Revision: 79131

Added:
   pypy/release/1.4.x/pypy/tool/gcdump.py
      - copied unchanged from r79067, pypy/trunk/pypy/tool/gcdump.py
Modified:
   pypy/release/1.4.x/   (props changed)
   pypy/release/1.4.x/pypy/   (props changed)
   pypy/release/1.4.x/pypy/jit/backend/llsupport/descr.py
   pypy/release/1.4.x/pypy/jit/backend/llsupport/test/test_descr.py
   pypy/release/1.4.x/pypy/jit/metainterp/optimizeopt/optimizer.py   (props changed)
   pypy/release/1.4.x/pypy/module/__pypy__/__init__.py
   pypy/release/1.4.x/pypy/module/__pypy__/interp_magic.py
   pypy/release/1.4.x/pypy/module/array/benchmark/Makefile   (props changed)
   pypy/release/1.4.x/pypy/module/array/benchmark/intimg.c   (props changed)
   pypy/release/1.4.x/pypy/module/array/benchmark/intimgtst.c   (props changed)
   pypy/release/1.4.x/pypy/module/array/benchmark/intimgtst.py   (props changed)
   pypy/release/1.4.x/pypy/module/array/benchmark/loop.c   (props changed)
   pypy/release/1.4.x/pypy/module/array/benchmark/sum.c   (props changed)
   pypy/release/1.4.x/pypy/module/array/benchmark/sumtst.c   (props changed)
   pypy/release/1.4.x/pypy/module/array/benchmark/sumtst.py   (props changed)
   pypy/release/1.4.x/pypy/module/array/test/test_array_old.py   (props changed)
   pypy/release/1.4.x/pypy/module/gc/__init__.py
   pypy/release/1.4.x/pypy/module/gc/app_referents.py
   pypy/release/1.4.x/pypy/module/gc/interp_gc.py
   pypy/release/1.4.x/pypy/module/gc/referents.py
   pypy/release/1.4.x/pypy/module/gc/test/test_gc.py
   pypy/release/1.4.x/pypy/objspace/std/callmethod.py
   pypy/release/1.4.x/pypy/objspace/std/mapdict.py
   pypy/release/1.4.x/pypy/objspace/std/test/test_dictmultiobject.py
   pypy/release/1.4.x/pypy/objspace/std/test/test_mapdict.py
   pypy/release/1.4.x/pypy/rlib/rerased.py   (props changed)
   pypy/release/1.4.x/pypy/rlib/rgc.py
   pypy/release/1.4.x/pypy/rlib/test/test_rerased.py   (props changed)
   pypy/release/1.4.x/pypy/rpython/llinterp.py
   pypy/release/1.4.x/pypy/rpython/lltypesystem/lloperation.py
   pypy/release/1.4.x/pypy/rpython/memory/gc/inspector.py
   pypy/release/1.4.x/pypy/rpython/memory/gc/minimark.py
   pypy/release/1.4.x/pypy/rpython/memory/gc/test/test_minimark.py
   pypy/release/1.4.x/pypy/rpython/memory/gctransform/framework.py
   pypy/release/1.4.x/pypy/translator/c/node.py
   pypy/release/1.4.x/pypy/translator/c/test/test_lltyped.py
   pypy/release/1.4.x/pypy/translator/c/test/test_newgc.py
Log:
Merged revisions: 790{36,37,52,53,55,66,67,88} from trunk


Modified: pypy/release/1.4.x/pypy/jit/backend/llsupport/descr.py
==============================================================================
--- pypy/release/1.4.x/pypy/jit/backend/llsupport/descr.py	(original)
+++ pypy/release/1.4.x/pypy/jit/backend/llsupport/descr.py	Mon Nov 15 20:25:15 2010
@@ -130,6 +130,7 @@
 # ArrayDescrs
 
 _A = lltype.GcArray(lltype.Signed)     # a random gcarray
+_AF = lltype.GcArray(lltype.Float)     # an array of C doubles
 
 
 class BaseArrayDescr(AbstractDescr):
@@ -171,16 +172,21 @@
     _clsname = 'GcPtrArrayDescr'
     _is_array_of_pointers = True
 
-_CA = rffi.CArray(lltype.Signed)
+class FloatArrayDescr(BaseArrayDescr):
+    _clsname = 'FloatArrayDescr'
+    _is_array_of_floats = True
+    def get_base_size(self, translate_support_code):
+        basesize, _, _ = symbolic.get_array_token(_AF, translate_support_code)
+        return basesize
+    def get_item_size(self, translate_support_code):
+        return symbolic.get_size(lltype.Float, translate_support_code)
 
 class BaseArrayNoLengthDescr(BaseArrayDescr):
     def get_base_size(self, translate_support_code):
-        basesize, _, _ = symbolic.get_array_token(_CA, translate_support_code)
-        return basesize
+        return 0
 
     def get_ofs_length(self, translate_support_code):
-        _, _, ofslength = symbolic.get_array_token(_CA, translate_support_code)
-        return ofslength
+        return -1
 
 class NonGcPtrArrayNoLengthDescr(BaseArrayNoLengthDescr):
     _clsname = 'NonGcPtrArrayNoLengthDescr'
@@ -192,6 +198,8 @@
     _is_array_of_pointers = True
 
 def getArrayDescrClass(ARRAY):
+    if ARRAY.OF is lltype.Float:
+        return FloatArrayDescr
     return getDescrClass(ARRAY.OF, BaseArrayDescr, GcPtrArrayDescr,
                          NonGcPtrArrayDescr, 'Array', 'get_item_size',
                          '_is_array_of_floats', '_is_item_signed')
@@ -219,7 +227,8 @@
         basesize, itemsize, ofslength = symbolic.get_array_token(ARRAY, False)
         assert basesize == arraydescr.get_base_size(False)
         assert itemsize == arraydescr.get_item_size(False)
-        assert ofslength == arraydescr.get_ofs_length(False)
+        if not ARRAY._hints.get('nolength', False):
+            assert ofslength == arraydescr.get_ofs_length(False)
         if isinstance(ARRAY, lltype.GcArray):
             gccache.init_array_descr(ARRAY, arraydescr)
         cache[ARRAY] = arraydescr

Modified: pypy/release/1.4.x/pypy/jit/backend/llsupport/test/test_descr.py
==============================================================================
--- pypy/release/1.4.x/pypy/jit/backend/llsupport/test/test_descr.py	(original)
+++ pypy/release/1.4.x/pypy/jit/backend/llsupport/test/test_descr.py	Mon Nov 15 20:25:15 2010
@@ -5,6 +5,7 @@
 from pypy.rpython.annlowlevel import llhelper
 from pypy.jit.metainterp.history import BoxInt, BoxFloat, BoxPtr
 from pypy.jit.metainterp import history
+import struct
 
 def test_get_size_descr():
     c0 = GcCache(False)
@@ -130,11 +131,13 @@
     assert not descr3.is_array_of_floats()
     assert     descr4.is_array_of_floats()
     #
-    WORD = rffi.sizeof(lltype.Signed)
-    assert descr1.get_base_size(False) == WORD
-    assert descr2.get_base_size(False) == WORD
-    assert descr3.get_base_size(False) == WORD
-    assert descr4.get_base_size(False) == WORD
+    def get_alignment(code):
+        # Retrieve default alignment for the compiler/platform
+        return struct.calcsize('l' + code) - struct.calcsize(code)
+    assert descr1.get_base_size(False) == get_alignment('c')
+    assert descr2.get_base_size(False) == get_alignment('p')
+    assert descr3.get_base_size(False) == get_alignment('p')
+    assert descr4.get_base_size(False) == get_alignment('d')
     assert descr1.get_ofs_length(False) == 0
     assert descr2.get_ofs_length(False) == 0
     assert descr3.get_ofs_length(False) == 0

Modified: pypy/release/1.4.x/pypy/module/__pypy__/__init__.py
==============================================================================
--- pypy/release/1.4.x/pypy/module/__pypy__/__init__.py	(original)
+++ pypy/release/1.4.x/pypy/module/__pypy__/__init__.py	Mon Nov 15 20:25:15 2010
@@ -23,6 +23,9 @@
                                  'interp_magic.method_cache_counter')
             self.extra_interpdef('reset_method_cache_counter',
                                  'interp_magic.reset_method_cache_counter')
+            if self.space.config.objspace.std.withmapdict:
+                self.extra_interpdef('mapdict_cache_counter',
+                                     'interp_magic.mapdict_cache_counter')
         PYC_MAGIC = get_pyc_magic(self.space)
         self.extra_interpdef('PYC_MAGIC', 'space.wrap(%d)' % PYC_MAGIC)
         #

Modified: pypy/release/1.4.x/pypy/module/__pypy__/interp_magic.py
==============================================================================
--- pypy/release/1.4.x/pypy/module/__pypy__/interp_magic.py	(original)
+++ pypy/release/1.4.x/pypy/module/__pypy__/interp_magic.py	Mon Nov 15 20:25:15 2010
@@ -2,6 +2,7 @@
 from pypy.interpreter.gateway import ObjSpace
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.objspace.std.typeobject import MethodCache
+from pypy.objspace.std.mapdict import IndexCache
 
 def internal_repr(space, w_object):
     return space.wrap('%r' % (w_object,))
@@ -36,4 +37,17 @@
     cache = space.fromcache(MethodCache)
     cache.misses = {}
     cache.hits = {}
-
+    if space.config.objspace.std.withmapdict:
+        cache = space.fromcache(IndexCache)
+        cache.misses = {}
+        cache.hits = {}
+
+def mapdict_cache_counter(space, name):
+    """Return a tuple (index_cache_hits, index_cache_misses) for lookups
+    in the mapdict cache with the given attribute name."""
+    assert space.config.objspace.std.withmethodcachecounter
+    assert space.config.objspace.std.withmapdict
+    cache = space.fromcache(IndexCache)
+    return space.newtuple([space.newint(cache.hits.get(name, 0)),
+                           space.newint(cache.misses.get(name, 0))])
+mapdict_cache_counter.unwrap_spec = [ObjSpace, str]

Modified: pypy/release/1.4.x/pypy/module/gc/__init__.py
==============================================================================
--- pypy/release/1.4.x/pypy/module/gc/__init__.py	(original)
+++ pypy/release/1.4.x/pypy/module/gc/__init__.py	Mon Nov 15 20:25:15 2010
@@ -29,6 +29,7 @@
                 'get_referents': 'referents.get_referents',
                 'get_referrers': 'referents.get_referrers',
                 '_dump_rpy_heap': 'referents._dump_rpy_heap',
+                'get_typeids_z': 'referents.get_typeids_z',
                 'GcRef': 'referents.W_GcRef',
                 })
         MixedModule.__init__(self, space, w_name)

Modified: pypy/release/1.4.x/pypy/module/gc/app_referents.py
==============================================================================
--- pypy/release/1.4.x/pypy/module/gc/app_referents.py	(original)
+++ pypy/release/1.4.x/pypy/module/gc/app_referents.py	Mon Nov 15 20:25:15 2010
@@ -14,11 +14,25 @@
     and [addr1]..[addrn] are addresses of other objects that this object
     points to.  The full dump is a list of such objects, with a marker
     [0][0][0][-1] inserted after all GC roots, before all non-roots.
+
+    If the argument is a filename and the 'zlib' module is available,
+    we also write a 'typeids.txt' in the same directory, if none exists.
     """
     if isinstance(file, str):
         f = open(file, 'wb')
         gc._dump_rpy_heap(f.fileno())
         f.close()
+        try:
+            import zlib, os
+        except ImportError:
+            pass
+        else:
+            filename2 = os.path.join(os.path.dirname(file), 'typeids.txt')
+            if not os.path.exists(filename2):
+                data = zlib.decompress(gc.get_typeids_z())
+                f = open(filename2, 'wb')
+                f.write(data)
+                f.close()
     else:
         if isinstance(file, int):
             fd = file

Modified: pypy/release/1.4.x/pypy/module/gc/interp_gc.py
==============================================================================
--- pypy/release/1.4.x/pypy/module/gc/interp_gc.py	(original)
+++ pypy/release/1.4.x/pypy/module/gc/interp_gc.py	Mon Nov 15 20:25:15 2010
@@ -10,6 +10,10 @@
         from pypy.objspace.std.typeobject import MethodCache
         cache = space.fromcache(MethodCache)
         cache.clear()
+        if space.config.objspace.std.withmapdict:
+            from pypy.objspace.std.mapdict import IndexCache
+            cache = space.fromcache(IndexCache)
+            cache.clear()
     rgc.collect()
     return space.wrap(0)
     

Modified: pypy/release/1.4.x/pypy/module/gc/referents.py
==============================================================================
--- pypy/release/1.4.x/pypy/module/gc/referents.py	(original)
+++ pypy/release/1.4.x/pypy/module/gc/referents.py	Mon Nov 15 20:25:15 2010
@@ -177,3 +177,9 @@
     if not ok:
         raise missing_operation(space)
 _dump_rpy_heap.unwrap_spec = [ObjSpace, int]
+
+def get_typeids_z(space):
+    a = rgc.get_typeids_z()
+    s = ''.join([a[i] for i in range(len(a))])
+    return space.wrap(s)
+get_typeids_z.unwrap_spec = [ObjSpace]

Modified: pypy/release/1.4.x/pypy/module/gc/test/test_gc.py
==============================================================================
--- pypy/release/1.4.x/pypy/module/gc/test/test_gc.py	(original)
+++ pypy/release/1.4.x/pypy/module/gc/test/test_gc.py	Mon Nov 15 20:25:15 2010
@@ -117,6 +117,33 @@
                     pass
             C().f()    # Fill the method cache
             rlist.append(weakref.ref(C))
+        for i in range(10):
+            f()
+        gc.collect()    # the classes C should all go away here
+        # the last class won't go in mapdict, as long as the code object of f
+        # is around
+        rlist.pop()
+        for r in rlist:
+            assert r() is None
+
+class AppTestGcMapDictIndexCache(AppTestGcMethodCache):
+    def setup_class(cls):
+        cls.space = gettestobjspace(**{"objspace.std.withmethodcache": True,
+                                       "objspace.std.withmapdict": True})
+
+
+    def test_clear_index_cache(self):
+        import gc, weakref
+        rlist = []
+        def f():
+            class C(object):
+                def f(self):
+                    pass
+            c = C()
+            c.x = 1
+            getattr(c, "x") # fill the index cache without using the local cache
+            getattr(c, "x")
+            rlist.append(weakref.ref(C))
         for i in range(5):
             f()
         gc.collect()    # the classes C should all go away here

Modified: pypy/release/1.4.x/pypy/objspace/std/callmethod.py
==============================================================================
--- pypy/release/1.4.x/pypy/objspace/std/callmethod.py	(original)
+++ pypy/release/1.4.x/pypy/objspace/std/callmethod.py	Mon Nov 15 20:25:15 2010
@@ -13,6 +13,9 @@
 from pypy.interpreter import function
 from pypy.objspace.descroperation import object_getattribute
 from pypy.rlib import jit, rstack # for resume points
+from pypy.objspace.std.mapdict import LOOKUP_METHOD_mapdict, \
+    LOOKUP_METHOD_mapdict_fill_cache_method
+
 
 # This module exports two extra methods for StdObjSpaceFrame implementing
 # the LOOKUP_METHOD and CALL_METHOD opcodes in an efficient way, as well
@@ -30,6 +33,13 @@
     #
     space = f.space
     w_obj = f.popvalue()
+
+    if space.config.objspace.std.withmapdict and not jit.we_are_jitted():
+        # mapdict has an extra-fast version of this function
+        from pypy.objspace.std.mapdict import LOOKUP_METHOD_mapdict
+        if LOOKUP_METHOD_mapdict(f, nameindex, w_obj):
+            return
+
     w_name = f.getname_w(nameindex)
     w_value = None
 
@@ -50,6 +60,11 @@
                     # nothing in the instance
                     f.pushvalue(w_descr)
                     f.pushvalue(w_obj)
+                    if (space.config.objspace.std.withmapdict and
+                            not jit.we_are_jitted()):
+                        # let mapdict cache stuff
+                        LOOKUP_METHOD_mapdict_fill_cache_method(
+                            f.getcode(), nameindex, w_obj, w_type, w_descr)
                     return
     if w_value is None:
         w_value = space.getattr(w_obj, w_name)

Modified: pypy/release/1.4.x/pypy/objspace/std/mapdict.py
==============================================================================
--- pypy/release/1.4.x/pypy/objspace/std/mapdict.py	(original)
+++ pypy/release/1.4.x/pypy/objspace/std/mapdict.py	Mon Nov 15 20:25:15 2010
@@ -1,4 +1,5 @@
 from pypy.rlib import jit, objectmodel, debug
+from pypy.rlib.rarithmetic import intmask, r_uint
 
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.objspace.std.dictmultiobject import W_DictMultiObject
@@ -15,24 +16,70 @@
 # we want to propagate knowledge that the result cannot be negative
 
 class AbstractAttribute(object):
-    _immutable_fields_ = ['w_cls']
+    _immutable_fields_ = ['terminator']
     cache_attrs = None
     _size_estimate = 0
 
-    def __init__(self, space, w_cls):
+    def __init__(self, space, terminator):
         self.space = space
-        self.w_cls = w_cls
+        assert isinstance(terminator, Terminator)
+        self.terminator = terminator
 
     def read(self, obj, selector):
-        raise NotImplementedError("abstract base class")
+        index = self.index(selector)
+        if index < 0:
+            return self.terminator._read_terminator(obj, selector)
+        return obj._mapdict_read_storage(index)
 
     def write(self, obj, selector, w_value):
-        raise NotImplementedError("abstract base class")
+        index = self.index(selector)
+        if index < 0:
+            return self.terminator._write_terminator(obj, selector, w_value)
+        obj._mapdict_write_storage(index, w_value)
+        return True
 
     def delete(self, obj, selector):
         return None
 
     def index(self, selector):
+        if (self.space.config.objspace.std.withmethodcache and 
+                not jit.we_are_jitted()):
+            return self._index_cache(selector)
+        else:
+            return self._index(selector)
+
+    @jit.dont_look_inside
+    def _index_cache(self, selector):
+        space = self.space
+        cache = space.fromcache(IndexCache)
+        SHIFT2 = r_uint.BITS - space.config.objspace.std.methodcachesizeexp
+        SHIFT1 = SHIFT2 - 5
+        attrs_as_int = objectmodel.current_object_addr_as_int(self)
+        # ^^^Note: see comment in typeobject.py for
+        # _pure_lookup_where_with_method_cache()
+        hash_selector = objectmodel.compute_hash(selector)
+        product = intmask(attrs_as_int * hash_selector)
+        index_hash = (r_uint(product) ^ (r_uint(product) << SHIFT1)) >> SHIFT2
+        # ^^^Note2: same comment too
+        cached_attr = cache.attrs[index_hash]
+        if cached_attr is self:
+            cached_selector = cache.selectors[index_hash]
+            if cached_selector == selector:
+                index = cache.indices[index_hash]
+                if space.config.objspace.std.withmethodcachecounter:
+                    name = selector[0]
+                    cache.hits[name] = cache.hits.get(name, 0) + 1
+                return index
+        index = self._index(selector)
+        cache.attrs[index_hash] = self
+        cache.selectors[index_hash] = selector
+        cache.indices[index_hash] = index
+        if space.config.objspace.std.withmethodcachecounter:
+            name = selector[0]
+            cache.misses[name] = cache.misses.get(name, 0) + 1
+        return index
+
+    def _index(self, selector):
         return -1
 
     def copy(self, obj):
@@ -42,7 +89,7 @@
         raise NotImplementedError("abstract base class")
 
     def get_terminator(self):
-        raise NotImplementedError("abstract base class")
+        return self.terminator
 
     def set_terminator(self, obj, terminator):
         raise NotImplementedError("abstract base class")
@@ -95,15 +142,20 @@
         raise NotImplementedError("abstract base class")
 
     def __repr__(self):
-        return "<%s w_cls=%s>" % (self.__class__.__name__, self.w_cls)
+        return "<%s>" % (self.__class__.__name__,)
 
 
 class Terminator(AbstractAttribute):
+    _immutable_fields_ = ['w_cls']
 
-    def read(self, obj, selector):
+    def __init__(self, space, w_cls):
+        AbstractAttribute.__init__(self, space, self)
+        self.w_cls = w_cls
+
+    def _read_terminator(self, obj, selector):
         return None
 
-    def write(self, obj, selector, w_value):
+    def _write_terminator(self, obj, selector, w_value):
         obj._get_mapdict_map().add_attr(obj, selector, w_value)
         return True
 
@@ -116,9 +168,6 @@
     def length(self):
         return 0
 
-    def get_terminator(self):
-        return self
-
     def set_terminator(self, obj, terminator):
         result = Object()
         result.space = self.space
@@ -128,6 +177,9 @@
     def remove_dict_entries(self, obj):
         return self.copy(obj)
 
+    def __repr__(self):
+        return "<%s w_cls=%s>" % (self.__class__.__name__, self.w_cls)
+
 class DictTerminator(Terminator):
     _immutable_fields_ = ['devolved_dict_terminator']
     def __init__(self, space, w_cls):
@@ -142,27 +194,27 @@
 
 
 class NoDictTerminator(Terminator):
-    def write(self, obj, selector, w_value):
+    def _write_terminator(self, obj, selector, w_value):
         if selector[1] == DICT:
             return False
-        return Terminator.write(self, obj, selector, w_value)
+        return Terminator._write_terminator(self, obj, selector, w_value)
 
 
 class DevolvedDictTerminator(Terminator):
-    def read(self, obj, selector):
+    def _read_terminator(self, obj, selector):
         if selector[1] == DICT:
             w_dict = obj.getdict()
             space = self.space
             return space.finditem_str(w_dict, selector[0])
-        return Terminator.read(self, obj, selector)
+        return Terminator._read_terminator(self, obj, selector)
 
-    def write(self, obj, selector, w_value):
+    def _write_terminator(self, obj, selector, w_value):
         if selector[1] == DICT:
             w_dict = obj.getdict()
             space = self.space
             space.setitem_str(w_dict, selector[0], w_value)
             return True
-        return Terminator.write(self, obj, selector, w_value)
+        return Terminator._write_terminator(self, obj, selector, w_value)
 
     def delete(self, obj, selector):
         from pypy.interpreter.error import OperationError
@@ -189,7 +241,7 @@
 class PlainAttribute(AbstractAttribute):
     _immutable_fields_ = ['selector', 'position', 'back']
     def __init__(self, selector, back):
-        AbstractAttribute.__init__(self, back.space, back.w_cls)
+        AbstractAttribute.__init__(self, back.space, back.terminator)
         self.selector = selector
         self.position = back.length()
         self.back = back
@@ -199,17 +251,6 @@
         w_value = self.read(obj, self.selector)
         new_obj._get_mapdict_map().add_attr(new_obj, self.selector, w_value)
 
-    def read(self, obj, selector):
-        if selector == self.selector:
-            return obj._mapdict_read_storage(self.position)
-        return self.back.read(obj, selector)
-
-    def write(self, obj, selector, w_value):
-        if selector == self.selector:
-            obj._mapdict_write_storage(self.position, w_value)
-            return True
-        return self.back.write(obj, selector, w_value)
-
     def delete(self, obj, selector):
         if selector == self.selector:
             # ok, attribute is deleted
@@ -219,10 +260,10 @@
             self._copy_attr(obj, new_obj)
         return new_obj
 
-    def index(self, selector):
+    def _index(self, selector):
         if selector == self.selector:
             return self.position
-        return self.back.index(selector)
+        return self.back._index(selector)
 
     def copy(self, obj):
         new_obj = self.back.copy(obj)
@@ -232,9 +273,6 @@
     def length(self):
         return self.position + 1
 
-    def get_terminator(self):
-        return self.back.get_terminator()
-
     def set_terminator(self, obj, terminator):
         new_obj = self.back.set_terminator(obj, terminator)
         self._copy_attr(obj, new_obj)
@@ -268,6 +306,24 @@
     # RPython reasons
     w_obj._set_mapdict_storage_and_map(new_obj.storage, new_obj.map)
 
+class IndexCache(object):
+    def __init__(self, space):
+        assert space.config.objspace.std.withmethodcache
+        SIZE = 1 << space.config.objspace.std.methodcachesizeexp
+        self.attrs = [None] * SIZE
+        self._empty_selector = (None, INVALID)
+        self.selectors = [self._empty_selector] * SIZE
+        self.indices = [0] * SIZE
+        if space.config.objspace.std.withmethodcachecounter:
+            self.hits = {}
+            self.misses = {}
+
+    def clear(self):
+        for i in range(len(self.attrs)):
+            self.attrs[i] = None
+        for i in range(len(self.selectors)):
+            self.selectors[i] = self._empty_selector
+
 # ____________________________________________________________
 # object implementation
 
@@ -328,7 +384,7 @@
         assert flag
 
     def getclass(self, space):
-        return self._get_mapdict_map().w_cls
+        return self._get_mapdict_map().terminator.w_cls
 
     def setclass(self, space, w_cls):
         new_obj = self._get_mapdict_map().set_terminator(self, w_cls.terminator)
@@ -373,6 +429,7 @@
         self.storage = make_sure_not_resized([None] * map.size_estimate())
 
     def _mapdict_read_storage(self, index):
+        assert index >= 0
         return self.storage[index]
     def _mapdict_write_storage(self, index, value):
         self.storage[index] = value
@@ -440,6 +497,7 @@
             return rerased.unerase_fixedsizelist(erased, W_Root)
 
         def _mapdict_read_storage(self, index):
+            assert index >= 0
             for i in rangenmin1:
                 if index == i:
                     erased = getattr(self, "_value%s" % i)
@@ -604,30 +662,54 @@
     map = None
     version_tag = None
     index = 0
+    w_method = None # for callmethod
     success_counter = 0
     failure_counter = 0
 
+    def is_valid_for_obj(self, w_obj):
+        map = w_obj._get_mapdict_map()
+        return self.is_valid_for_map(map)
+
+    def is_valid_for_map(self, map):
+        if map is self.map:
+            version_tag = map.terminator.w_cls.version_tag()
+            if version_tag is self.version_tag:
+                # everything matches, it's incredibly fast
+                if map.space.config.objspace.std.withmethodcachecounter:
+                    self.success_counter += 1
+                return True
+        return False
+
 INVALID_CACHE_ENTRY = CacheEntry()
 INVALID_CACHE_ENTRY.map = objectmodel.instantiate(AbstractAttribute)
                              # different from any real map ^^^
-INVALID_CACHE_ENTRY.map.w_cls = None
+INVALID_CACHE_ENTRY.map.terminator = None
+
 
 def init_mapdict_cache(pycode):
     num_entries = len(pycode.co_names_w)
     pycode._mapdict_caches = [INVALID_CACHE_ENTRY] * num_entries
 
+def _fill_cache(pycode, nameindex, map, version_tag, index, w_method=None):
+    entry = pycode._mapdict_caches[nameindex]
+    if entry is INVALID_CACHE_ENTRY:
+        entry = CacheEntry()
+        pycode._mapdict_caches[nameindex] = entry
+    entry.map = map
+    entry.version_tag = version_tag
+    entry.index = index
+    entry.w_method = w_method
+    if pycode.space.config.objspace.std.withmethodcachecounter:
+        entry.failure_counter += 1
+
 def LOAD_ATTR_caching(pycode, w_obj, nameindex):
     # this whole mess is to make the interpreter quite a bit faster; it's not
     # used if we_are_jitted().
     entry = pycode._mapdict_caches[nameindex]
     map = w_obj._get_mapdict_map()
-    if map is entry.map:
-        version_tag = map.w_cls.version_tag()
-        if version_tag is entry.version_tag:
-            # everything matches, it's incredibly fast
-            if pycode.space.config.objspace.std.withmethodcachecounter:
-                entry.success_counter += 1
-            return w_obj._mapdict_read_storage(entry.index)
+    if entry.is_valid_for_map(map) and entry.w_method is None:
+        # everything matches, it's incredibly fast
+        return w_obj._mapdict_read_storage(entry.index)
     return LOAD_ATTR_slowpath(pycode, w_obj, nameindex, map)
 LOAD_ATTR_caching._always_inline_ = True
 
@@ -635,7 +717,7 @@
     space = pycode.space
     w_name = pycode.co_names_w[nameindex]
     if map is not None:
-        w_type = map.w_cls
+        w_type = map.terminator.w_cls
         w_descr = w_type.getattribute_if_not_from_object()
         if w_descr is not None:
             return space._handle_getattribute(w_descr, w_obj, w_name)
@@ -655,17 +737,30 @@
             if selector[1] != INVALID:
                 index = map.index(selector)
                 if index >= 0:
-                    entry = pycode._mapdict_caches[nameindex]
-                    if entry is INVALID_CACHE_ENTRY:
-                        entry = CacheEntry()
-                        pycode._mapdict_caches[nameindex] = entry
-                    entry.map = map
-                    entry.version_tag = version_tag
-                    entry.index = index
-                    if space.config.objspace.std.withmethodcachecounter:
-                        entry.failure_counter += 1
+                    _fill_cache(pycode, nameindex, map, version_tag, index)
                     return w_obj._mapdict_read_storage(index)
     if space.config.objspace.std.withmethodcachecounter:
         INVALID_CACHE_ENTRY.failure_counter += 1
     return space.getattr(w_obj, w_name)
 LOAD_ATTR_slowpath._dont_inline_ = True
+
+def LOOKUP_METHOD_mapdict(f, nameindex, w_obj):
+    space = f.space
+    pycode = f.getcode()
+    entry = pycode._mapdict_caches[nameindex]
+    if entry.is_valid_for_obj(w_obj):
+        w_method = entry.w_method
+        if w_method is not None:
+            f.pushvalue(w_method)
+            f.pushvalue(w_obj)
+            return True
+    return False
+
+def LOOKUP_METHOD_mapdict_fill_cache_method(pycode, nameindex, w_obj, w_type, w_method):
+    version_tag = w_type.version_tag()
+    if version_tag is None:
+        return
+    map = w_obj._get_mapdict_map()
+    if map is None:
+        return
+    _fill_cache(pycode, nameindex, map, version_tag, -1, w_method)

Modified: pypy/release/1.4.x/pypy/objspace/std/test/test_dictmultiobject.py
==============================================================================
--- pypy/release/1.4.x/pypy/objspace/std/test/test_dictmultiobject.py	(original)
+++ pypy/release/1.4.x/pypy/objspace/std/test/test_dictmultiobject.py	Mon Nov 15 20:25:15 2010
@@ -616,6 +616,7 @@
             withdictmeasurement = False
             withsmalldicts = False
             withcelldict = False
+            withmethodcache = False
         class opcodes:
             CALL_LIKELY_BUILTIN = False
 

Modified: pypy/release/1.4.x/pypy/objspace/std/test/test_mapdict.py
==============================================================================
--- pypy/release/1.4.x/pypy/objspace/std/test/test_mapdict.py	(original)
+++ pypy/release/1.4.x/pypy/objspace/std/test/test_mapdict.py	Mon Nov 15 20:25:15 2010
@@ -24,13 +24,13 @@
         hasdict = False
 
 def test_plain_attribute():
-    space = " "
     w_cls = "class"
     aa = PlainAttribute(("b", DICT),
                         PlainAttribute(("a", DICT),
                                        Terminator(space, w_cls)))
     assert aa.space is space
-    assert aa.w_cls is w_cls
+    assert aa.terminator.w_cls is w_cls
+    assert aa.get_terminator() is aa.terminator
 
     obj = Object()
     obj.map, obj.storage = aa, [10, 20]
@@ -604,7 +604,8 @@
         from pypy.interpreter import gateway
         cls.space = gettestobjspace(
             **{"objspace.std.withmapdict": True,
-               "objspace.std.withmethodcachecounter": True})
+               "objspace.std.withmethodcachecounter": True,
+               "objspace.opcodes.CALL_METHOD": True})
         #
         def check(space, w_func, name):
             w_code = space.getattr(w_func, space.wrap('func_code'))
@@ -785,6 +786,135 @@
         res = self.check(f, 'x')
         assert res == (0, 0, 1)
 
+    def test_call_method_uses_cache(self):
+        # bit sucky
+        global C
+
+        class C(object):
+            def m(*args):
+                return args
+        C.sm = staticmethod(C.m.im_func)
+        C.cm = classmethod(C.m.im_func)
+
+        exec """if 1:
+
+            def f():
+                c = C()
+                res = c.m(1)
+                assert res == (c, 1)
+                return 42
+
+            def g():
+                c = C()
+                res = c.sm(1)
+                assert res == (1, )
+                return 42
+
+            def h():
+                c = C()
+                res = c.cm(1)
+                assert res == (C, 1)
+                return 42
+        """
+        res = self.check(f, 'm')
+        assert res == (1, 0, 0)
+        res = self.check(f, 'm')
+        assert res == (0, 1, 0)
+        res = self.check(f, 'm')
+        assert res == (0, 1, 0)
+        res = self.check(f, 'm')
+        assert res == (0, 1, 0)
+
+        # static methods are not cached
+        res = self.check(g, 'sm')
+        assert res == (0, 0, 0)
+        res = self.check(g, 'sm')
+        assert res == (0, 0, 0)
+
+        # neither are class methods
+        res = self.check(h, 'cm')
+        assert res == (0, 0, 0)
+        res = self.check(h, 'cm')
+        assert res == (0, 0, 0)
+
+    def test_mix_cache_bug(self):
+        # bit sucky
+        global C
+
+        class C(object):
+            def m(*args):
+                return args
+
+        exec """if 1:
+
+            def f():
+                c = C()
+                res = c.m(1)
+                assert res == (c, 1)
+                bm = c.m
+                res = bm(1)
+                assert res == (c, 1)
+                return 42
+
+        """
+        res = self.check(f, 'm')
+        assert res == (1, 1, 1)
+        res = self.check(f, 'm')
+        assert res == (0, 2, 1)
+        res = self.check(f, 'm')
+        assert res == (0, 2, 1)
+        res = self.check(f, 'm')
+        assert res == (0, 2, 1)
+
+class AppTestGlobalCaching(AppTestWithMapDict):
+    def setup_class(cls):
+        cls.space = gettestobjspace(
+            **{"objspace.std.withmethodcachecounter": True,
+               "objspace.std.withmapdict": True,
+               "objspace.opcodes.CALL_METHOD": True})
+
+    def test_mix_classes(self):
+        import __pypy__
+        class A(object):
+            def f(self):
+                return 42
+        class B(object):
+            def f(self):
+                return 43
+        class C(object):
+            def f(self):
+                return 44
+        l = [A(), B(), C()] * 10
+        __pypy__.reset_method_cache_counter()
+        # 'exec' to make sure that a.f() is compiled with CALL_METHOD
+        exec """for i, a in enumerate(l):
+                    assert a.f() == 42 + i % 3
+"""
+        cache_counter = __pypy__.mapdict_cache_counter("f")
+        assert cache_counter[0] >= 15
+        assert cache_counter[1] >= 3 # should be (27, 3)
+        assert sum(cache_counter) == 30
+
+    def test_mix_classes_attribute(self):
+        import __pypy__
+        class A(object):
+            def __init__(self):
+                self.x = 42
+        class B(object):
+            def __init__(self):
+                self.x = 43
+        class C(object):
+            def __init__(self):
+                self.x = 44
+        l = [A(), B(), C()] * 10
+        __pypy__.reset_method_cache_counter()
+        for i, a in enumerate(l):
+            assert a.x == 42 + i % 3
+        cache_counter = __pypy__.mapdict_cache_counter("x")
+        assert cache_counter[0] >= 15
+        assert cache_counter[1] >= 3 # should be (27, 3)
+        assert sum(cache_counter) == 30
+
 class TestDictSubclassShortcutBug(object):
     def setup_class(cls):
         cls.space = gettestobjspace(

Modified: pypy/release/1.4.x/pypy/rlib/rgc.py
==============================================================================
--- pypy/release/1.4.x/pypy/rlib/rgc.py	(original)
+++ pypy/release/1.4.x/pypy/rlib/rgc.py	Mon Nov 15 20:25:15 2010
@@ -379,6 +379,11 @@
     "NOT_RPYTHON"
     raise NotImplementedError
 
+def get_typeids_z():
+    "NOT_RPYTHON"
+    raise NotImplementedError
+
+ARRAY_OF_CHAR = lltype.Array(lltype.Char)
 NULL_GCREF = lltype.nullptr(llmemory.GCREF.TO)
 
 class _GcRef(object):
@@ -530,3 +535,12 @@
         vlist = hop.inputargs(lltype.Signed)
         hop.exception_is_here()
         return hop.genop('gc_dump_rpy_heap', vlist, resulttype = hop.r_result)
+
+class Entry(ExtRegistryEntry):
+    _about_ = get_typeids_z
+    def compute_result_annotation(self):
+        from pypy.annotation.model import SomePtr
+        return SomePtr(lltype.Ptr(ARRAY_OF_CHAR))
+    def specialize_call(self, hop):
+        hop.exception_is_here()
+        return hop.genop('gc_typeids_z', [], resulttype = hop.r_result)

Modified: pypy/release/1.4.x/pypy/rpython/llinterp.py
==============================================================================
--- pypy/release/1.4.x/pypy/rpython/llinterp.py	(original)
+++ pypy/release/1.4.x/pypy/rpython/llinterp.py	Mon Nov 15 20:25:15 2010
@@ -912,6 +912,9 @@
     def op_gc_dump_rpy_heap(self):
         raise NotImplementedError("gc_dump_rpy_heap")
 
+    def op_gc_typeids_z(self):
+        raise NotImplementedError("gc_typeids_z")
+
     def op_do_malloc_fixedsize_clear(self):
         raise NotImplementedError("do_malloc_fixedsize_clear")
 

Modified: pypy/release/1.4.x/pypy/rpython/lltypesystem/lloperation.py
==============================================================================
--- pypy/release/1.4.x/pypy/rpython/lltypesystem/lloperation.py	(original)
+++ pypy/release/1.4.x/pypy/rpython/lltypesystem/lloperation.py	Mon Nov 15 20:25:15 2010
@@ -476,6 +476,7 @@
     'gc_get_rpy_type_index': LLOp(),
     'gc_is_rpy_instance'  : LLOp(),
     'gc_dump_rpy_heap'    : LLOp(),
+    'gc_typeids_z'        : LLOp(),
 
     # ------- JIT & GC interaction, only for some GCs ----------
     

Modified: pypy/release/1.4.x/pypy/rpython/memory/gc/inspector.py
==============================================================================
--- pypy/release/1.4.x/pypy/rpython/memory/gc/inspector.py	(original)
+++ pypy/release/1.4.x/pypy/rpython/memory/gc/inspector.py	Mon Nov 15 20:25:15 2010
@@ -4,7 +4,7 @@
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 from pypy.rlib.objectmodel import free_non_gc_object
 from pypy.rpython.module.ll_os import underscore_on_windows
-from pypy.rlib import rposix
+from pypy.rlib import rposix, rgc
 
 from pypy.rpython.memory.support import AddressDict, get_address_stack
 
@@ -187,3 +187,7 @@
     heapdumper.flush()
     heapdumper.delete()
     return True
+
+def get_typeids_z(gc):
+    srcaddress = gc.root_walker.gcdata.typeids_z
+    return llmemory.cast_adr_to_ptr(srcaddress, lltype.Ptr(rgc.ARRAY_OF_CHAR))

Modified: pypy/release/1.4.x/pypy/rpython/memory/gc/minimark.py
==============================================================================
--- pypy/release/1.4.x/pypy/rpython/memory/gc/minimark.py	(original)
+++ pypy/release/1.4.x/pypy/rpython/memory/gc/minimark.py	Mon Nov 15 20:25:15 2010
@@ -1,3 +1,36 @@
+""" MiniMark GC.
+
+Environment variables can be used to fine-tune the following parameters:
+    
+ PYPY_GC_NURSERY        The nursery size.  Defaults to half the size of
+                        the L2 cache.  Try values like '1.2MB'.
+
+ PYPY_GC_MAJOR_COLLECT  Major collection memory factor.  Default is '1.82',
+                        which means trigger a major collection when the
+                        memory consumed equals 1.82 times the memory
+                        really used at the end of the previous major
+                        collection.
+
+ PYPY_GC_GROWTH         Major collection threshold's max growth rate.
+                        Default is '1.3'.  Useful to collect more often
+                        than normally on sudden memory growth, e.g. when
+                        there is a temporary peak in memory usage.
+
+ PYPY_GC_MAX            The max heap size.  If coming near this limit, it
+                        will first collect more often, then raise an
+                        RPython MemoryError, and if that is not enough,
+                        crash the program with a fatal error.  Try values
+                        like '1.6GB'.
+
+ PYPY_GC_MIN            Don't collect while the memory size is below this
+                        limit.  Useful to avoid spending all the time in
+                        the GC in very small programs.  Defaults to 8
+                        times the nursery.
+"""
+# XXX Should find a way to bound the major collection threshold by the
+# XXX total addressable size.  Maybe by keeping some minimarkpage arenas
+# XXX pre-reserved, enough for a few nursery collections?  What about
+# XXX raw-malloced memory?
 import sys
 from pypy.rpython.lltypesystem import lltype, llmemory, llarena, llgroup
 from pypy.rpython.lltypesystem.lloperation import llop
@@ -87,13 +120,8 @@
 
     TRANSLATION_PARAMS = {
         # Automatically adjust the size of the nursery and the
-        # 'major_collection_threshold' from the environment.  For
-        # 'nursery_size' it will look it up in the env var
-        # PYPY_GC_NURSERY and fall back to half the size of
-        # the L2 cache.  For 'major_collection_threshold' it will look
-        # it up in the env var PYPY_GC_MAJOR_COLLECT.  It also sets
-        # 'max_heap_size' to PYPY_GC_MAX.  Finally, PYPY_GC_MIN sets
-        # the minimal value of 'next_major_collection_threshold'.
+        # 'major_collection_threshold' from the environment.
+        # See docstring at the start of the file.
         "read_from_env": True,
 
         # The size of the nursery.  Note that this is only used as a
@@ -121,6 +149,13 @@
         # we trigger the next major collection.
         "major_collection_threshold": 1.82,
 
+        # Threshold to avoid that the total heap size grows by a factor of
+        # major_collection_threshold at every collection: it can only
+        # grow at most by the following factor from one collection to the
+        # next.  Used e.g. when there is a sudden, temporary peak in memory
+        # usage; this avoids that the upper bound grows too fast.
+        "growth_rate_max": 1.3,
+
         # The number of array indices that are mapped to a single bit in
         # write_barrier_from_array().  Must be a power of two.  The default
         # value of 128 means that card pages are 512 bytes (1024 on 64-bits)
@@ -146,6 +181,7 @@
                  arena_size=64*WORD,
                  small_request_threshold=5*WORD,
                  major_collection_threshold=2.5,
+                 growth_rate_max=2.5,   # for tests
                  card_page_indices=0,
                  large_object=8*WORD,
                  large_object_gcptrs=10*WORD,
@@ -157,6 +193,7 @@
         self.nursery_size = nursery_size
         self.small_request_threshold = small_request_threshold
         self.major_collection_threshold = major_collection_threshold
+        self.growth_rate_max = growth_rate_max
         self.num_major_collects = 0
         self.min_heap_size = 0.0
         self.max_heap_size = 0.0
@@ -257,9 +294,13 @@
             newsize = max(newsize, minsize)
             #
             major_coll = base.read_float_from_env('PYPY_GC_MAJOR_COLLECT')
-            if major_coll >= 1.0:
+            if major_coll > 1.0:
                 self.major_collection_threshold = major_coll
             #
+            growth = base.read_float_from_env('PYPY_GC_GROWTH')
+            if growth > 1.0:
+                self.growth_rate_max = growth
+            #
             min_heap_size = base.read_uint_from_env('PYPY_GC_MIN')
             if min_heap_size > 0:
                 self.min_heap_size = float(min_heap_size)
@@ -295,11 +336,19 @@
         # initialize the threshold
         self.min_heap_size = max(self.min_heap_size, self.nursery_size *
                                               self.major_collection_threshold)
+        self.next_major_collection_threshold = self.min_heap_size
         self.set_major_threshold_from(0.0)
         debug_stop("gc-set-nursery-size")
 
-    def set_major_threshold_from(self, threshold):
+
+    def set_major_threshold_from(self, threshold, reserving_size=0):
         # Set the next_major_collection_threshold.
+        threshold_max = (self.next_major_collection_threshold *
+                         self.growth_rate_max)
+        if threshold > threshold_max:
+            threshold = threshold_max
+        #
+        threshold += reserving_size
         if threshold < self.min_heap_size:
             threshold = self.min_heap_size
         #
@@ -1016,7 +1065,7 @@
             obj = oldlist.pop()
             #
             # Add the flag GCFLAG_NO_YOUNG_PTRS.  All live objects should have
-            # this flag after a nursery collection.
+            # this flag set after a nursery collection.
             self.header(obj).tid |= GCFLAG_NO_YOUNG_PTRS
             #
             # Trace the 'obj' to replace pointers to nursery with pointers
@@ -1079,7 +1128,7 @@
         # nursery are kept unchanged in this step.
         llmemory.raw_memcopy(obj - size_gc_header, newhdr, totalsize)
         #
-        # Set the old object's tid to -1 (containing all flags) and
+        # Set the old object's tid to -42 (containing all flags) and
         # replace the old object's content with the target address.
         # A bit of no-ops to convince llarena that we are changing
         # the layout, in non-translated versions.
@@ -1198,8 +1247,8 @@
         # have allocated 'major_collection_threshold' times more than
         # we currently have.
         bounded = self.set_major_threshold_from(
-            (self.get_total_memory_used() * self.major_collection_threshold)
-            + reserving_size)
+            self.get_total_memory_used() * self.major_collection_threshold,
+            reserving_size)
         #
         # Max heap size: gives an upper bound on the threshold.  If we
         # already have at least this much allocated, raise MemoryError.
@@ -1344,7 +1393,7 @@
         if self.is_valid_gc_object(obj):
             if self.is_in_nursery(obj):
                 #
-                # The object not a tagged pointer, and is it still in the
+                # The object is not a tagged pointer, and it is still in the
                 # nursery.  Find or allocate a "shadow" object, which is
                 # where the object will be moved by the next minor
                 # collection

Modified: pypy/release/1.4.x/pypy/rpython/memory/gc/test/test_minimark.py
==============================================================================
--- pypy/release/1.4.x/pypy/rpython/memory/gc/test/test_minimark.py	(original)
+++ pypy/release/1.4.x/pypy/rpython/memory/gc/test/test_minimark.py	Mon Nov 15 20:25:15 2010
@@ -28,3 +28,42 @@
     assert gc.card_marking_bytes_for_length(P+P+1) == 3
     assert gc.card_marking_bytes_for_length(P+P+P+P+P+P+P+P) == 8
     assert gc.card_marking_bytes_for_length(P+P+P+P+P+P+P+P+1) == 9
+
+def test_set_major_threshold():
+    gc = MiniMarkGC(None, major_collection_threshold=2.0,
+                    growth_rate_max=1.5)
+    gc.min_heap_size = 100.0
+    gc.max_heap_size = 300.0
+    gc.next_major_collection_threshold = 0.0
+    # first, we don't grow past min_heap_size
+    for i in range(5):
+        gc.set_major_threshold_from(100.0)
+        assert gc.next_major_collection_threshold == 100.0
+    # then we grow a lot
+    b = gc.set_major_threshold_from(100 * 2.0)
+    assert b is False
+    assert gc.next_major_collection_threshold == 150.0
+    b = gc.set_major_threshold_from(150 * 2.0)
+    assert b is False
+    assert gc.next_major_collection_threshold == 225.0
+    b = gc.set_major_threshold_from(225 * 2.0)
+    assert b is True
+    assert gc.next_major_collection_threshold == 300.0   # max reached
+    b = gc.set_major_threshold_from(300 * 2.0)
+    assert b is True
+    assert gc.next_major_collection_threshold == 300.0
+    # then we shrink instantly
+    b = gc.set_major_threshold_from(100.0)
+    assert b is False
+    assert gc.next_major_collection_threshold == 100.0
+    # then we grow a bit
+    b = gc.set_major_threshold_from(100 * 1.25)
+    assert b is False
+    assert gc.next_major_collection_threshold == 125.0
+    b = gc.set_major_threshold_from(125 * 1.25)
+    assert b is False
+    assert gc.next_major_collection_threshold == 156.25
+    # check that we cannot shrink below min_heap_size
+    b = gc.set_major_threshold_from(42.7)
+    assert b is False
+    assert gc.next_major_collection_threshold == 100.0

Modified: pypy/release/1.4.x/pypy/rpython/memory/gctransform/framework.py
==============================================================================
--- pypy/release/1.4.x/pypy/rpython/memory/gctransform/framework.py	(original)
+++ pypy/release/1.4.x/pypy/rpython/memory/gctransform/framework.py	Mon Nov 15 20:25:15 2010
@@ -172,6 +172,7 @@
         gcdata.static_root_nongcend = a_random_address   # patched in finish()
         gcdata.static_root_end = a_random_address        # patched in finish()
         gcdata.max_type_id = 13                          # patched in finish()
+        gcdata.typeids_z = a_random_address              # patched in finish()
         self.gcdata = gcdata
         self.malloc_fnptr_cache = {}
 
@@ -212,6 +213,9 @@
         data_classdef.generalize_attr(
             'max_type_id',
             annmodel.SomeInteger())
+        data_classdef.generalize_attr(
+            'typeids_z',
+            annmodel.SomeAddress())
 
         annhelper = annlowlevel.MixLevelHelperAnnotator(self.translator.rtyper)
 
@@ -415,6 +419,11 @@
                                        [s_gc, annmodel.SomeInteger()],
                                        annmodel.s_Bool,
                                        minimal_transform=False)
+        self.get_typeids_z_ptr = getfn(inspector.get_typeids_z,
+                                       [s_gc],
+                                       annmodel.SomePtr(
+                                           lltype.Ptr(rgc.ARRAY_OF_CHAR)),
+                                       minimal_transform=False)
 
         self.set_max_heap_size_ptr = getfn(GCClass.set_max_heap_size.im_func,
                                            [s_gc,
@@ -572,7 +581,14 @@
         newgcdependencies = []
         newgcdependencies.append(ll_static_roots_inside)
         ll_instance.inst_max_type_id = len(group.members)
-        self.write_typeid_list()
+        typeids_z = self.write_typeid_list()
+        ll_typeids_z = lltype.malloc(rgc.ARRAY_OF_CHAR,
+                                     len(typeids_z),
+                                     immortal=True)
+        for i in range(len(typeids_z)):
+            ll_typeids_z[i] = typeids_z[i]
+        ll_instance.inst_typeids_z = llmemory.cast_ptr_to_adr(ll_typeids_z)
+        newgcdependencies.append(ll_typeids_z)
         return newgcdependencies
 
     def get_finish_tables(self):
@@ -599,6 +615,11 @@
         for index in range(len(self.layoutbuilder.type_info_group.members)):
             f.write("member%-4d %s\n" % (index, all_ids.get(index, '?')))
         f.close()
+        try:
+            import zlib
+            return zlib.compress(udir.join("typeids.txt").read(), 9)
+        except ImportError:
+            return ''
 
     def transform_graph(self, graph):
         func = getattr(graph, 'func', None)
@@ -988,6 +1009,13 @@
                   resultvar=hop.spaceop.result)
         self.pop_roots(hop, livevars)
 
+    def gct_gc_typeids_z(self, hop):
+        livevars = self.push_roots(hop)
+        hop.genop("direct_call",
+                  [self.get_typeids_z_ptr, self.c_const_gc],
+                  resultvar=hop.spaceop.result)
+        self.pop_roots(hop, livevars)
+
     def gct_malloc_nonmovable_varsize(self, hop):
         TYPE = hop.spaceop.result.concretetype
         if self.gcdata.gc.can_malloc_nonmovable():

Modified: pypy/release/1.4.x/pypy/translator/c/node.py
==============================================================================
--- pypy/release/1.4.x/pypy/translator/c/node.py	(original)
+++ pypy/release/1.4.x/pypy/translator/c/node.py	Mon Nov 15 20:25:15 2010
@@ -714,7 +714,11 @@
                 s = ''.join([self.obj.getitem(i) for i in range(len(self.obj.items))])
             else:
                 s = ''.join(self.obj.items)
-            yield '\t%s%s' % (length, c_char_array_constant(s))
+            array_constant = c_char_array_constant(s)
+            if array_constant.startswith('{') and barebonearray(T):
+                assert array_constant.endswith('}')
+                array_constant = array_constant[1:-1].strip()
+            yield '\t%s%s' % (length, array_constant)
             yield '}'
         else:
             barebone = barebonearray(T)

Modified: pypy/release/1.4.x/pypy/translator/c/test/test_lltyped.py
==============================================================================
--- pypy/release/1.4.x/pypy/translator/c/test/test_lltyped.py	(original)
+++ pypy/release/1.4.x/pypy/translator/c/test/test_lltyped.py	Mon Nov 15 20:25:15 2010
@@ -1,6 +1,7 @@
 import py
 from pypy.rpython.lltypesystem.lltype import *
 from pypy.translator.c.test import test_typed
+from pypy.tool.sourcetools import func_with_new_name
 
 
 class TestLowLevelType(test_typed.CompilationTestCase):
@@ -655,6 +656,45 @@
             fn = self.getcompiled(llf)
             fn()
 
+    def test_prebuilt_raw_arrays(self):
+        from pypy.rpython.lltypesystem import rffi, ll2ctypes
+        #
+        def make_test_function(cast, haslength, length):
+            a = malloc(A, length, flavor='raw', immortal=True)
+            # two cases: a zero-terminated array if length == 6 or 1030,
+            # a non-zero-terminated array if length == 557 or 1031
+            for i in range(length):
+                a[i] = cast(256 - 5 + i)
+            def llf():
+                for i in range(length):
+                    if a[i] != cast(256 - 5 + i):
+                        return False
+                if haslength and len(a) != length:
+                    return False
+                return True
+            return func_with_new_name(llf, repr((A, haslength, length)))
+        #
+        testfns = []
+        records = []
+        for OF, cast in [(Void, lambda n: None),
+                         (Char, lambda n: chr(n & 0xFF)),
+                         (Signed, lambda n: n)]:
+            for A, haslength in [(rffi.CArray(OF), False),
+                                 (Array(OF), True)]:
+                for length in [0, 6, 557, 1030, 1031]:
+                    testfns.append(make_test_function(cast, haslength, length))
+                    records.append((A, haslength, length))
+        def llf():
+            i = 0
+            for fn in testfns:
+                if not fn():
+                    return i    # returns the index of the failing function
+                i += 1
+            return -42
+        fn = self.getcompiled(llf)
+        res = fn()
+        assert res == -42, "failing function: %r" % (records[res],)
+
     def test_prebuilt_ll2ctypes_array(self):
         from pypy.rpython.lltypesystem import rffi, ll2ctypes
         A = rffi.CArray(Char)

Modified: pypy/release/1.4.x/pypy/translator/c/test/test_newgc.py
==============================================================================
--- pypy/release/1.4.x/pypy/translator/c/test/test_newgc.py	(original)
+++ pypy/release/1.4.x/pypy/translator/c/test/test_newgc.py	Mon Nov 15 20:25:15 2010
@@ -1089,6 +1089,41 @@
         assert os.path.exists(self.filename_dump)
         assert os.path.getsize(self.filename_dump) > 0       # minimal test
 
+    filename_dump_typeids_z = str(udir.join('test_typeids_z'))
+    def define_write_typeids_z(self):
+        U = lltype.GcForwardReference()
+        U.become(lltype.GcStruct('U', ('next', lltype.Ptr(U)),
+                                 ('x', lltype.Signed)))
+        S = lltype.GcStruct('S', ('u', lltype.Ptr(U)))
+        A = lltype.GcArray(lltype.Ptr(S))
+        filename = self.filename_dump_typeids_z
+
+        def fn():
+            s = lltype.malloc(S)
+            s.u = lltype.malloc(U)
+            s.u.next = lltype.malloc(U)
+            s.u.next.next = lltype.malloc(U)
+            a = lltype.malloc(A, 1000)
+            s2 = lltype.malloc(S)
+            #
+            p = rgc.get_typeids_z()
+            s = ''.join([p[i] for i in range(len(p))])
+            fd = os.open(filename, os.O_WRONLY | os.O_CREAT, 0666)
+            os.write(fd, s)
+            os.close(fd)
+            return 0
+
+        return fn
+
+    def test_write_typeids_z(self):
+        self.run("write_typeids_z")
+        f = open(self.filename_dump_typeids_z)
+        data_z = f.read()
+        f.close()
+        import zlib
+        data = zlib.decompress(data_z)
+        assert data.startswith('member0')
+        assert 'GcArray of * GcStruct S {' in data
 
 class TestSemiSpaceGC(TestUsingFramework, snippet.SemiSpaceGCTestDefines):
     gcpolicy = "semispace"



More information about the Pypy-commit mailing list