[pypy-svn] r68537 - in pypy/trunk/pypy: annotation annotation/test config doc/config interpreter jit/backend/cli jit/metainterp jit/metainterp/test lang/prolog/interpreter module/__builtin__ objspace objspace/std objspace/std/test rlib rlib/test rpython rpython/lltypesystem rpython/lltypesystem/test rpython/memory/gc rpython/memory/gctransform rpython/ootypesystem rpython/ootypesystem/test rpython/test translator/c translator/c/src translator/c/test translator/cli translator/cli/src translator/cli/test translator/jvm translator/oosupport/test_template

arigo at codespeak.net arigo at codespeak.net
Fri Oct 16 15:11:27 CEST 2009


Author: arigo
Date: Fri Oct 16 15:11:23 2009
New Revision: 68537

Added:
   pypy/trunk/pypy/jit/metainterp/test/test_typesystem.py
      - copied unchanged from r68536, pypy/branch/gc-hash/pypy/jit/metainterp/test/test_typesystem.py
   pypy/trunk/pypy/translator/c/src/align.h
      - copied unchanged from r68536, pypy/branch/gc-hash/pypy/translator/c/src/align.h
Removed:
   pypy/trunk/pypy/doc/config/objspace.std.withsmalldicts.txt
Modified:
   pypy/trunk/pypy/annotation/bookkeeper.py
   pypy/trunk/pypy/annotation/builtin.py
   pypy/trunk/pypy/annotation/dictdef.py
   pypy/trunk/pypy/annotation/test/test_annrpython.py
   pypy/trunk/pypy/annotation/unaryop.py
   pypy/trunk/pypy/config/pypyoption.py
   pypy/trunk/pypy/interpreter/buffer.py
   pypy/trunk/pypy/interpreter/pycode.py
   pypy/trunk/pypy/interpreter/typedef.py
   pypy/trunk/pypy/jit/backend/cli/method.py
   pypy/trunk/pypy/jit/metainterp/codewriter.py
   pypy/trunk/pypy/jit/metainterp/executor.py
   pypy/trunk/pypy/jit/metainterp/history.py
   pypy/trunk/pypy/jit/metainterp/optimizeopt.py
   pypy/trunk/pypy/jit/metainterp/pyjitpl.py
   pypy/trunk/pypy/jit/metainterp/resoperation.py
   pypy/trunk/pypy/jit/metainterp/resume.py
   pypy/trunk/pypy/jit/metainterp/support.py
   pypy/trunk/pypy/jit/metainterp/test/test_basic.py
   pypy/trunk/pypy/jit/metainterp/test/test_compile.py
   pypy/trunk/pypy/jit/metainterp/test/test_loop.py
   pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py
   pypy/trunk/pypy/jit/metainterp/test/test_resume.py
   pypy/trunk/pypy/jit/metainterp/test/test_warmspot.py
   pypy/trunk/pypy/jit/metainterp/typesystem.py
   pypy/trunk/pypy/jit/metainterp/warmspot.py
   pypy/trunk/pypy/lang/prolog/interpreter/term.py
   pypy/trunk/pypy/module/__builtin__/interp_classobj.py
   pypy/trunk/pypy/objspace/descroperation.py
   pypy/trunk/pypy/objspace/std/complextype.py
   pypy/trunk/pypy/objspace/std/dictmultiobject.py
   pypy/trunk/pypy/objspace/std/floattype.py
   pypy/trunk/pypy/objspace/std/frozensettype.py
   pypy/trunk/pypy/objspace/std/inttype.py
   pypy/trunk/pypy/objspace/std/longtype.py
   pypy/trunk/pypy/objspace/std/objecttype.py
   pypy/trunk/pypy/objspace/std/stringobject.py
   pypy/trunk/pypy/objspace/std/stringtype.py
   pypy/trunk/pypy/objspace/std/test/test_dictmultiobject.py
   pypy/trunk/pypy/objspace/std/test/test_floatobject.py
   pypy/trunk/pypy/objspace/std/test/test_userobject.py
   pypy/trunk/pypy/objspace/std/tupletype.py
   pypy/trunk/pypy/objspace/std/typeobject.py
   pypy/trunk/pypy/objspace/std/unicodeobject.py
   pypy/trunk/pypy/objspace/std/unicodetype.py
   pypy/trunk/pypy/rlib/jit.py
   pypy/trunk/pypy/rlib/objectmodel.py
   pypy/trunk/pypy/rlib/rarithmetic.py
   pypy/trunk/pypy/rlib/rope.py
   pypy/trunk/pypy/rlib/rweakrefimpl.py
   pypy/trunk/pypy/rlib/test/test_objectmodel.py
   pypy/trunk/pypy/rpython/llinterp.py
   pypy/trunk/pypy/rpython/lltypesystem/lloperation.py
   pypy/trunk/pypy/rpython/lltypesystem/lltype.py
   pypy/trunk/pypy/rpython/lltypesystem/rclass.py
   pypy/trunk/pypy/rpython/lltypesystem/rstr.py
   pypy/trunk/pypy/rpython/lltypesystem/test/test_lltype.py
   pypy/trunk/pypy/rpython/memory/gc/generation.py
   pypy/trunk/pypy/rpython/memory/gc/hybrid.py
   pypy/trunk/pypy/rpython/memory/gc/markcompact.py
   pypy/trunk/pypy/rpython/memory/gc/marksweep.py
   pypy/trunk/pypy/rpython/memory/gc/semispace.py
   pypy/trunk/pypy/rpython/memory/gctransform/boehm.py
   pypy/trunk/pypy/rpython/memory/gctransform/framework.py
   pypy/trunk/pypy/rpython/memory/gctransform/refcounting.py
   pypy/trunk/pypy/rpython/memory/gctransform/transform.py
   pypy/trunk/pypy/rpython/ootypesystem/ooregistry.py
   pypy/trunk/pypy/rpython/ootypesystem/ootype.py
   pypy/trunk/pypy/rpython/ootypesystem/rbuiltin.py
   pypy/trunk/pypy/rpython/ootypesystem/rclass.py
   pypy/trunk/pypy/rpython/ootypesystem/rstr.py
   pypy/trunk/pypy/rpython/ootypesystem/rtupletype.py
   pypy/trunk/pypy/rpython/ootypesystem/test/test_ooclean.py
   pypy/trunk/pypy/rpython/ootypesystem/test/test_oorecord.py
   pypy/trunk/pypy/rpython/ootypesystem/test/test_oortype.py
   pypy/trunk/pypy/rpython/ootypesystem/test/test_ootype.py
   pypy/trunk/pypy/rpython/rbuiltin.py
   pypy/trunk/pypy/rpython/rclass.py
   pypy/trunk/pypy/rpython/rdict.py
   pypy/trunk/pypy/rpython/rfloat.py
   pypy/trunk/pypy/rpython/rtuple.py
   pypy/trunk/pypy/rpython/rtyper.py
   pypy/trunk/pypy/rpython/test/test_rclass.py
   pypy/trunk/pypy/rpython/test/test_rfloat.py
   pypy/trunk/pypy/rpython/test/test_rstr.py
   pypy/trunk/pypy/rpython/test/test_rtuple.py
   pypy/trunk/pypy/rpython/test/test_runicode.py
   pypy/trunk/pypy/translator/c/gc.py
   pypy/trunk/pypy/translator/c/node.py
   pypy/trunk/pypy/translator/c/src/g_prerequisite.h
   pypy/trunk/pypy/translator/c/src/mem.h
   pypy/trunk/pypy/translator/c/test/test_boehm.py
   pypy/trunk/pypy/translator/c/test/test_lltyped.py
   pypy/trunk/pypy/translator/c/test/test_newgc.py
   pypy/trunk/pypy/translator/c/test/test_typed.py
   pypy/trunk/pypy/translator/cli/opcodes.py
   pypy/trunk/pypy/translator/cli/src/pypylib.cs
   pypy/trunk/pypy/translator/cli/test/test_dotnet.py
   pypy/trunk/pypy/translator/jvm/builtin.py
   pypy/trunk/pypy/translator/jvm/opcodes.py
   pypy/trunk/pypy/translator/jvm/typesystem.py
   pypy/trunk/pypy/translator/oosupport/test_template/string.py
Log:
Merge the branch gc-hash.  Cleans up the usage of hashes.  Now hash() is
no longer RPython, and the official interface is defined in
rlib/objectmodel: compute_hash() and compute_identity_hash().

Any GC object can now have its identity hash taken.  Simplifies code in
the PyPy interpreter, and allows a proper fix to be designed to replace
cast_ref_to_hashable() in the JIT.  "Small dicts" are gone now; the
other usages of hash() in RPython have been fixed.  A side effect is
that calling 'object.__hash__(x)' on top of pypy-c always works now.

On ootype, 'ooidentityhash' and 'oohash' are gone, replaced respectively
by 'identityhash' (which works both on lltype and ootype) and by a
method of the ootype.String class.

Adapt the GCs to add support for identityhash().  For moving GCs, this
is done by adding a 4-bytes field at the end of every object, *but only
when needed*.  This means that in the common case objects are 4 bytes
smaller, as long as we don't take their hash.  When we do, they grow by
4 bytes the next time the GC moves them.  This logic is not really
implemented yet for the mark&compact GC -- I disabled it right now.



Modified: pypy/trunk/pypy/annotation/bookkeeper.py
==============================================================================
--- pypy/trunk/pypy/annotation/bookkeeper.py	(original)
+++ pypy/trunk/pypy/annotation/bookkeeper.py	Fri Oct 16 15:11:23 2009
@@ -169,7 +169,6 @@
         self.pending_specializations = []   # list of callbacks
         self.external_class_cache = {}      # cache of ExternalType classes
 
-        self.needs_hash_support = {}
         self.needs_generic_instantiate = {}
 
         self.stats = Stats(self)
@@ -219,12 +218,6 @@
                 self.consider_call_site_for_pbc(pbc, 'simple_call', 
                                                 args_s, s_ImpossibleValue)
             self.emulated_pbc_calls = {}
-
-            for clsdef in self.needs_hash_support.keys():
-                for clsdef2 in self.needs_hash_support:
-                    if clsdef.issubclass(clsdef2) and clsdef is not clsdef2:
-                        del self.needs_hash_support[clsdef]
-                        break
         finally:
             self.leave()
 
@@ -399,6 +392,7 @@
                         for ek, ev in items:
                             result.dictdef.generalize_key(self.immutablevalue(ek))
                             result.dictdef.generalize_value(self.immutablevalue(ev))
+                            result.dictdef.seen_prebuilt_key(ek)
                         seen_elements = len(items)
                         # if the dictionary grew during the iteration,
                         # start over again
@@ -417,6 +411,7 @@
                 for ek, ev in x.iteritems():
                     dictdef.generalize_key(self.immutablevalue(ek, False))
                     dictdef.generalize_value(self.immutablevalue(ev, False))
+                    dictdef.seen_prebuilt_key(ek)
                 result = SomeDict(dictdef)
         elif tp is weakref.ReferenceType:
             x1 = x()

Modified: pypy/trunk/pypy/annotation/builtin.py
==============================================================================
--- pypy/trunk/pypy/annotation/builtin.py	(original)
+++ pypy/trunk/pypy/annotation/builtin.py	Fri Oct 16 15:11:23 2009
@@ -493,6 +493,10 @@
     assert PtrT.is_constant()
     return SomePtr(ll_ptrtype=PtrT.const)
 
+def identityhash(s_obj):
+    assert isinstance(s_obj, (SomePtr, SomeOOObject, SomeOOInstance))
+    return SomeInteger()
+
 def getRuntimeTypeInfo(T):
     assert T.is_constant()
     return immutablevalue(lltype.getRuntimeTypeInfo(T.const))
@@ -517,6 +521,7 @@
 BUILTIN_ANALYZERS[lltype.direct_ptradd] = direct_ptradd
 BUILTIN_ANALYZERS[lltype.cast_ptr_to_int] = cast_ptr_to_int
 BUILTIN_ANALYZERS[lltype.cast_int_to_ptr] = cast_int_to_ptr
+BUILTIN_ANALYZERS[lltype.identityhash] = identityhash
 BUILTIN_ANALYZERS[lltype.getRuntimeTypeInfo] = getRuntimeTypeInfo
 BUILTIN_ANALYZERS[lltype.runtime_type_info] = runtime_type_info
 BUILTIN_ANALYZERS[lltype.Ptr] = constPtr
@@ -562,10 +567,6 @@
     else:
         return SomeOOInstance(c.ootype)
 
-def ooidentityhash(i):
-    assert isinstance(i, (SomeOOInstance, SomeOOObject))
-    return SomeInteger()
-
 def ooupcast(I, i):
     assert isinstance(I.const, ootype.Instance)
     if ootype.isSubclass(i.ootype, I.const):
@@ -606,7 +607,6 @@
 BUILTIN_ANALYZERS[ootype.runtimenew] = runtimenew
 BUILTIN_ANALYZERS[ootype.classof] = classof
 BUILTIN_ANALYZERS[ootype.subclassof] = subclassof
-BUILTIN_ANALYZERS[ootype.ooidentityhash] = ooidentityhash
 BUILTIN_ANALYZERS[ootype.ooupcast] = ooupcast
 BUILTIN_ANALYZERS[ootype.oodowncast] = oodowncast
 BUILTIN_ANALYZERS[ootype.cast_to_object] = cast_to_object

Modified: pypy/trunk/pypy/annotation/dictdef.py
==============================================================================
--- pypy/trunk/pypy/annotation/dictdef.py	(original)
+++ pypy/trunk/pypy/annotation/dictdef.py	Fri Oct 16 15:11:23 2009
@@ -2,15 +2,16 @@
 from pypy.annotation.model import SomeInteger, s_Bool, unionof
 from pypy.annotation.model import SomeInstance
 from pypy.annotation.listdef import ListItem
+from pypy.rlib.objectmodel import compute_hash
 
 
 class DictKey(ListItem):
-    custom_eq_hash = False
+    s_rdict_eqfn = s_ImpossibleValue
+    s_rdict_hashfn = s_ImpossibleValue
 
     def __init__(self, bookkeeper, s_value, is_r_dict=False):
         ListItem.__init__(self, bookkeeper, s_value)
-        self.is_r_dict = is_r_dict
-        self.enable_hashing()
+        self.custom_eq_hash = is_r_dict
 
     def patch(self):
         for dictdef in self.itemof:
@@ -26,25 +27,16 @@
                                               other.s_rdict_hashfn,
                                               other=other)
 
-    def enable_hashing(self):
-        # r_dicts don't need the RPython hash of their keys
-        if isinstance(self.s_value, SomeInstance) and not self.is_r_dict:
-            self.bookkeeper.needs_hash_support[self.s_value.classdef] = True
-
     def generalize(self, s_other_value):
         updated = ListItem.generalize(self, s_other_value)
-        if updated:
-            self.enable_hashing()
         if updated and self.custom_eq_hash:
             self.emulate_rdict_calls()
         return updated
 
     def update_rdict_annotations(self, s_eqfn, s_hashfn, other=None):
-        if not self.custom_eq_hash:
-            self.custom_eq_hash = True
-        else:
-            s_eqfn = unionof(s_eqfn, self.s_rdict_eqfn)
-            s_hashfn = unionof(s_hashfn, self.s_rdict_hashfn)
+        assert self.custom_eq_hash
+        s_eqfn = unionof(s_eqfn, self.s_rdict_eqfn)
+        s_hashfn = unionof(s_hashfn, self.s_rdict_hashfn)
         self.s_rdict_eqfn = s_eqfn
         self.s_rdict_hashfn = s_hashfn
         self.emulate_rdict_calls(other=other)
@@ -139,6 +131,14 @@
     def generalize_value(self, s_value):
         self.dictvalue.generalize(s_value)
 
+    def seen_prebuilt_key(self, x):
+        # In case we are an r_dict, we don't ask for the hash ourselves.
+        # Note that if the custom hashing function ends up asking for
+        # the hash of x, then it must use compute_hash() itself, so it
+        # works out.
+        if not self.dictkey.custom_eq_hash:
+            compute_hash(x)
+
     def __repr__(self):
         return '<{%r: %r}>' % (self.dictkey.s_value, self.dictvalue.s_value)
 

Modified: pypy/trunk/pypy/annotation/test/test_annrpython.py
==============================================================================
--- pypy/trunk/pypy/annotation/test/test_annrpython.py	(original)
+++ pypy/trunk/pypy/annotation/test/test_annrpython.py	Fri Oct 16 15:11:23 2009
@@ -3187,14 +3187,24 @@
         s = a.build_types(f, [int])
         assert s.const == 0
 
-    def test_hash(self):
-        class A(object):
+    def test_hash_sideeffect(self):
+        class X:
             pass
-        def f():
-            return hash(A()) + hash(None)
+        x1 = X()
+        x2 = X()
+        x3 = X()
+        d = {(2, x1): 5, (3, x2): 7}
+        def f(n, m):
+            if   m == 1: x = x1
+            elif m == 2: x = x2
+            else:        x = x3
+            return d[n, x]
         a = self.RPythonAnnotator()
-        s = a.build_types(f, [])
+        s = a.build_types(f, [int, int])
         assert s.knowntype == int
+        assert hasattr(x1, '__precomputed_identity_hash')
+        assert hasattr(x2, '__precomputed_identity_hash')
+        assert not hasattr(x3, '__precomputed_identity_hash')
 
     def test_contains_of_empty_dict(self):
         class A(object):

Modified: pypy/trunk/pypy/annotation/unaryop.py
==============================================================================
--- pypy/trunk/pypy/annotation/unaryop.py	(original)
+++ pypy/trunk/pypy/annotation/unaryop.py	Fri Oct 16 15:11:23 2009
@@ -20,11 +20,12 @@
 def immutablevalue(x):
     return getbookkeeper().immutablevalue(x)
 
-UNARY_OPERATIONS = set(['len', 'is_true', 'getattr', 'setattr', 'delattr', 'hash',
+UNARY_OPERATIONS = set(['len', 'is_true', 'getattr', 'setattr', 'delattr',
                         'simple_call', 'call_args', 'str', 'repr',
                         'iter', 'next', 'invert', 'type', 'issubtype',
                         'pos', 'neg', 'nonzero', 'abs', 'hex', 'oct',
-                        'ord', 'int', 'float', 'long', 'id',
+                        'ord', 'int', 'float', 'long',
+                        'hash', 'id',    # <== not supported any more
                         'getslice', 'setslice', 'delslice',
                         'neg_ovf', 'abs_ovf', 'hint', 'unicode', 'unichr'])
 
@@ -98,7 +99,8 @@
         return obj.is_true()
 
     def hash(obj):
-        raise TypeError, "hash() is not generally supported"
+        raise TypeError, ("cannot use hash() in RPython; "
+                          "see objectmodel.compute_xxx()")
 
     def str(obj):
         getbookkeeper().count('str', obj)
@@ -121,10 +123,8 @@
         return SomeString()
 
     def id(obj):
-        raise Exception("cannot use id() in RPython; pick one of:\n"
-                        "\t\t objectmodel.compute_unique_id()\n"
-                        "\t\t hash()\n"
-                        "\t\t objectmodel.current_object_addr_as_int()")
+        raise Exception("cannot use id() in RPython; "
+                        "see objectmodel.compute_xxx()")
 
     def int(obj):
         return SomeInteger()
@@ -203,9 +203,6 @@
             return getbookkeeper().immutablevalue(bool(self.const))
         return s_Bool
 
-    def hash(flt):
-        return SomeInteger()
-
 class __extend__(SomeInteger):
 
     def invert(self):
@@ -272,11 +269,6 @@
     def getanyitem(tup):
         return unionof(*tup.items)
 
-    def hash(tup):
-        for s_item in tup.items:
-            s_item.hash()    # record that we need the hash of each item
-        return SomeInteger()
-
     def getslice(tup, s_start, s_stop):
         assert s_start.is_immutable_constant(),"tuple slicing: needs constants"
         assert s_stop.is_immutable_constant(), "tuple slicing: needs constants"
@@ -501,9 +493,6 @@
     def ord(str):
         return SomeInteger(nonneg=True)
 
-    def hash(str):
-        return SomeInteger()
-
     def method_split(str, patt): # XXX
         getbookkeeper().count("str_split", str, patt)
         return getbookkeeper().newlist(str.basestringclass())
@@ -632,10 +621,6 @@
             # create or update the attribute in clsdef
             clsdef.generalize_attr(attr, s_value)
 
-    def hash(ins):
-        getbookkeeper().needs_hash_support[ins.classdef] = True
-        return SomeInteger()
-
     def is_true_behavior(ins, s):
         if not ins.can_be_None:
             s.const = True
@@ -694,13 +679,6 @@
         else:
             return SomeObject()    # len() on a pbc? no chance
 
-    def hash(pbc):
-        if pbc.isNone():
-            # only supports hash(None) as part of hash(<SomeInstance>)
-            return SomeInteger()
-        else:
-            return SomeObject.hash(pbc)
-
 class __extend__(SomeGenericCallable):
     def call(self, args):
         bookkeeper = getbookkeeper()

Modified: pypy/trunk/pypy/config/pypyoption.py
==============================================================================
--- pypy/trunk/pypy/config/pypyoption.py	(original)
+++ pypy/trunk/pypy/config/pypyoption.py	Fri Oct 16 15:11:23 2009
@@ -250,11 +250,6 @@
                    default=False,
                    requires=[("objspace.std.withmultidict", True)]),
 
-        BoolOption("withsmalldicts",
-                   "handle small dictionaries differently",
-                   default=False,
-                   requires=[("objspace.std.withmultidict", True)]),
-
         BoolOption("withrangelist",
                    "enable special range list implementation that does not "
                    "actually create the full list until the resulting "

Modified: pypy/trunk/pypy/interpreter/buffer.py
==============================================================================
--- pypy/trunk/pypy/interpreter/buffer.py	(original)
+++ pypy/trunk/pypy/interpreter/buffer.py	Fri Oct 16 15:11:23 2009
@@ -19,6 +19,7 @@
 from pypy.interpreter.typedef import TypeDef
 from pypy.interpreter.gateway import interp2app, ObjSpace, W_Root
 from pypy.interpreter.error import OperationError
+from pypy.rlib.objectmodel import compute_hash
 
 
 class Buffer(Wrappable):
@@ -117,7 +118,7 @@
     descr_ge = _make_descr__cmp('ge')
 
     def descr_hash(self, space):
-        return space.wrap(hash(self.as_str()))
+        return space.wrap(compute_hash(self.as_str()))
     descr_hash.unwrap_spec = ['self', ObjSpace]
 
     def descr_mul(self, space, w_times):

Modified: pypy/trunk/pypy/interpreter/pycode.py
==============================================================================
--- pypy/trunk/pypy/interpreter/pycode.py	(original)
+++ pypy/trunk/pypy/interpreter/pycode.py	Fri Oct 16 15:11:23 2009
@@ -14,6 +14,7 @@
 from pypy.rlib.rarithmetic import intmask
 from pypy.rlib.debug import make_sure_not_resized, make_sure_not_modified
 from pypy.rlib import jit
+from pypy.rlib.objectmodel import compute_hash
 
 # helper
 
@@ -301,15 +302,15 @@
 
     def descr_code__hash__(self):
         space = self.space
-        result =  hash(self.co_name)
+        result =  compute_hash(self.co_name)
         result ^= self.co_argcount
         result ^= self.co_nlocals
         result ^= self.co_flags
         result ^= self.co_firstlineno
-        result ^= hash(self.co_code)
-        for name in self.co_varnames:  result ^= hash(name)
-        for name in self.co_freevars:  result ^= hash(name)
-        for name in self.co_cellvars:  result ^= hash(name)
+        result ^= compute_hash(self.co_code)
+        for name in self.co_varnames:  result ^= compute_hash(name)
+        for name in self.co_freevars:  result ^= compute_hash(name)
+        for name in self.co_cellvars:  result ^= compute_hash(name)
         w_result = space.wrap(intmask(result))
         for w_name in self.co_names_w:
             w_result = space.xor(w_result, space.hash(w_name))

Modified: pypy/trunk/pypy/interpreter/typedef.py
==============================================================================
--- pypy/trunk/pypy/interpreter/typedef.py	(original)
+++ pypy/trunk/pypy/interpreter/typedef.py	Fri Oct 16 15:11:23 2009
@@ -9,8 +9,7 @@
     DescrMismatch
 from pypy.interpreter.error import OperationError
 from pypy.tool.sourcetools import compile2, func_with_new_name
-from pypy.rlib.objectmodel import instantiate
-from pypy.rlib.rarithmetic import intmask
+from pypy.rlib.objectmodel import instantiate, compute_identity_hash
 from pypy.rlib.jit import hint
 
 class TypeDef:
@@ -20,12 +19,9 @@
         self.base = __base
         self.hasdict = '__dict__' in rawdict
         self.weakrefable = '__weakref__' in rawdict
-        self.custom_hash = '__hash__' in rawdict
         if __base is not None:
             self.hasdict     |= __base.hasdict
             self.weakrefable |= __base.weakrefable
-            self.custom_hash |= __base.custom_hash
-            # NB. custom_hash is sometimes overridden manually by callers
         self.rawdict = {}
         self.acceptable_as_base_class = True
         # xxx used by faking
@@ -50,39 +46,8 @@
 # ____________________________________________________________
 #  Hash support
 
-def get_default_hash_function(cls):
-    # go to the first parent class of 'cls' that has a typedef
-    while 'typedef' not in cls.__dict__:
-        cls = cls.__bases__[0]
-        if cls is object:
-            # not found: 'cls' must have been an abstract class,
-            # no hash function is needed
-            return None
-    if cls.typedef.custom_hash:
-        return None   # the typedef says that instances have their own
-                      # hash, so we don't need a default RPython-level
-                      # hash function.
-    try:
-        hashfunction = _hashfunction_cache[cls]
-    except KeyError:
-        def hashfunction(w_obj):
-            "Return the identity hash of 'w_obj'."
-            assert isinstance(w_obj, cls)
-            return hash(w_obj)   # forces a hash_cache only on 'cls' instances
-        hashfunction = func_with_new_name(hashfunction,
-                                       'hashfunction_for_%s' % (cls.__name__,))
-        _hashfunction_cache[cls] = hashfunction
-    return hashfunction
-get_default_hash_function._annspecialcase_ = 'specialize:memo'
-_hashfunction_cache = {}
-
 def default_identity_hash(space, w_obj):
-    fn = get_default_hash_function(w_obj.__class__)
-    if fn is None:
-        typename = space.type(w_obj).getname(space, '?')
-        msg = "%s objects have no default hash" % (typename,)
-        raise OperationError(space.w_TypeError, space.wrap(msg))
-    return space.wrap(intmask(fn(w_obj)))
+    return space.wrap(compute_identity_hash(w_obj))
 
 def descr__hash__unhashable(space, w_obj):
     typename = space.type(w_obj).getname(space, '?')

Modified: pypy/trunk/pypy/jit/backend/cli/method.py
==============================================================================
--- pypy/trunk/pypy/jit/backend/cli/method.py	(original)
+++ pypy/trunk/pypy/jit/backend/cli/method.py	Fri Oct 16 15:11:23 2009
@@ -576,9 +576,6 @@
         self.il.Emit(OpCodes.Call, methinfo)
         self.store_result(op)
 
-    def emit_op_ooidentityhash(self, op):
-        raise NotImplementedError
-
     def emit_op_call_impl(self, op):
         descr = op.descr
         assert isinstance(descr, runner.StaticMethDescr)

Modified: pypy/trunk/pypy/jit/metainterp/codewriter.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/codewriter.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/codewriter.py	Fri Oct 16 15:11:23 2009
@@ -1505,7 +1505,7 @@
 
     serialize_op_oostring  = handle_builtin_call
     serialize_op_oounicode = handle_builtin_call
-    serialize_op_oohash    = handle_builtin_call
+    serialize_op_gc_identityhash = handle_builtin_call
 
     # ----------
 

Modified: pypy/trunk/pypy/jit/metainterp/executor.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/executor.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/executor.py	Fri Oct 16 15:11:23 2009
@@ -145,10 +145,6 @@
         assert False
     return ConstInt(x)
 
-def do_ooidentityhash(cpu, box1):
-    obj = box1.getref_base()
-    return ConstInt(cpu.ts.ooidentityhash(obj))
-
 def do_subclassof(cpu, box1, box2):
     return ConstInt(cpu.ts.subclassOf(cpu, box1, box2))
 

Modified: pypy/trunk/pypy/jit/metainterp/history.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/history.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/history.py	Fri Oct 16 15:11:23 2009
@@ -3,6 +3,7 @@
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 from pypy.rpython.ootypesystem import ootype
 from pypy.rlib.objectmodel import we_are_translated, r_dict, Symbolic
+from pypy.rlib.objectmodel import compute_hash
 from pypy.rlib.rarithmetic import intmask
 from pypy.tool.uid import uid
 from pypy.conftest import option
@@ -310,7 +311,7 @@
         return self.value
 
     def _get_hash_(self):
-        return hash(self.value)
+        return compute_hash(self.value)
 
     def set_future_value(self, cpu, j):
         cpu.set_future_value_float(j, self.getfloat())
@@ -350,7 +351,10 @@
     getref._annspecialcase_ = 'specialize:arg(1)'
 
     def _get_hash_(self):
-        return lltype.cast_ptr_to_int(self.value)
+        if self.value:
+            return lltype.identityhash(self.value)
+        else:
+            return 0
 
     def getaddr(self, cpu):
         return llmemory.cast_ptr_to_adr(self.value)
@@ -398,7 +402,7 @@
 
     def _get_hash_(self):
         if self.value:
-            return ootype.ooidentityhash(self.value)
+            return ootype.identityhash(self.value)
         else:
             return 0
 
@@ -530,7 +534,7 @@
         return self.value
 
     def _get_hash_(self):
-        return hash(self.value)
+        return compute_hash(self.value)
 
     def set_future_value(self, cpu, j):
         cpu.set_future_value_float(j, self.value)
@@ -566,7 +570,10 @@
         return llmemory.cast_ptr_to_adr(self.value)
 
     def _get_hash_(self):
-        return lltype.cast_ptr_to_int(self.value)
+        if self.value:
+            return lltype.identityhash(self.value)
+        else:
+            return 0
 
     def set_future_value(self, cpu, j):
         cpu.set_future_value_ref(j, self.value)
@@ -602,7 +609,7 @@
 
     def _get_hash_(self):
         if self.value:
-            return ootype.ooidentityhash(self.value)
+            return ootype.identityhash(self.value)
         else:
             return 0
 
@@ -631,11 +638,20 @@
     return c1 == c2
 
 def dc_hash(c):
+    "NOT_RPYTHON"
+    # This is called during translation only.  Avoid using identityhash(),
+    # to avoid forcing a hash, at least on lltype objects.
     if not isinstance(c, Const):
         return hash(c)
     if isinstance(c.value, Symbolic):
         return id(c.value)
     try:
+        if isinstance(c, ConstPtr):
+            p = lltype.normalizeptr(c.value)
+            if p is not None:
+                return hash(p._obj)
+            else:
+                return 0
         return c._get_hash_()
     except lltype.DelayedPointer:
         return -2      # xxx risk of changing hash...

Modified: pypy/trunk/pypy/jit/metainterp/optimizeopt.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/optimizeopt.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/optimizeopt.py	Fri Oct 16 15:11:23 2009
@@ -364,7 +364,7 @@
         self.cpu = metainterp_sd.cpu
         self.loop = loop
         self.values = {}
-        self.interned_refs = {}
+        self.interned_refs = self.cpu.ts.new_ref_dict()
         self.resumedata_memo = resume.ResumeDataLoopMemo(self.cpu)
         self.heap_op_optimizer = HeapOpOptimizer(self)
 
@@ -380,12 +380,7 @@
             value = constbox.getref_base()
             if not value:
                 return box
-            key = self.cpu.ts.cast_ref_to_hashable(self.cpu, value)
-            try:
-                return self.interned_refs[key]
-            except KeyError:
-                self.interned_refs[key] = box
-                return box
+            return self.interned_refs.setdefault(value, box)
         else:
             return box
 

Modified: pypy/trunk/pypy/jit/metainterp/pyjitpl.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/pyjitpl.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/pyjitpl.py	Fri Oct 16 15:11:23 2009
@@ -329,10 +329,6 @@
     def opimpl_subclassof(self, box1, box2):
         self.execute(rop.SUBCLASSOF, box1, box2)
 
-    @arguments("box")
-    def opimpl_ooidentityhash(self, box):
-        self.execute(rop.OOIDENTITYHASH, box)
-
     @arguments("descr", "box")
     def opimpl_new_array(self, itemsize, countbox):
         self.execute_with_descr(rop.NEW_ARRAY, itemsize, countbox)
@@ -1034,7 +1030,7 @@
     def _setup_class_sizes(self):
         class_sizes = {}
         for vtable, sizedescr in self._class_sizes:
-            vtable = self.cpu.ts.cast_baseclass_to_hashable(self.cpu, vtable)
+            vtable = self.cpu.ts.cast_vtable_to_hashable(self.cpu, vtable)
             class_sizes[vtable] = sizedescr
         self.cpu.set_class_sizes(class_sizes)
 

Modified: pypy/trunk/pypy/jit/metainterp/resoperation.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/resoperation.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/resoperation.py	Fri Oct 16 15:11:23 2009
@@ -187,7 +187,6 @@
     'UNICODEGETITEM/2',
     #
     # ootype operations
-    'OOIDENTITYHASH/1',
     'INSTANCEOF/1d',
     'SUBCLASSOF/2',
     #

Modified: pypy/trunk/pypy/jit/metainterp/resume.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/resume.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/resume.py	Fri Oct 16 15:11:23 2009
@@ -101,7 +101,7 @@
         self.cpu = cpu
         self.consts = []
         self.large_ints = {}
-        self.refs = {}
+        self.refs = cpu.ts.new_ref_dict_2()
         self.numberings = {}
 
     def getconst(self, const):
@@ -124,7 +124,6 @@
             val = const.getref_base()
             if not val:
                 return NULLREF
-            val = self.cpu.ts.cast_ref_to_hashable(self.cpu, val)
             tagged = self.refs.get(val, UNASSIGNED)
             if not tagged_eq(tagged, UNASSIGNED):
                 return tagged

Modified: pypy/trunk/pypy/jit/metainterp/support.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/support.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/support.py	Fri Oct 16 15:11:23 2009
@@ -134,6 +134,10 @@
 _ll_2_list_getitem_foldable = _ll_2_list_getitem
 _ll_1_list_len_foldable     = _ll_1_list_len
 
+def _ll_1_gc_identityhash(x):
+    return lltype.identityhash(x)
+
+
 class LLtypeHelpers:
 
     # ---------- dict ----------
@@ -268,12 +272,6 @@
     def _ll_1_oounicode_string_foldable(s):
         return ootype.oounicode(s, -1)
 
-    def _ll_1_oohash_string_foldable(s):
-        return ootype.oohash(s)
-
-    def _ll_1_oohash_unicode_foldable(u):
-        return ootype.oohash(u)
-
 # -------------------------------------------------------
 
 def setup_extra_builtin(rtyper, oopspec_name, nb_args):
@@ -334,14 +332,8 @@
         T = ootype.ROOT
     return '%s_%s_foldable' % (op.opname, T._name.lower()), args
 
-def get_oohash_oopspec(op):
-    T = op.args[0].concretetype
-    if T is ootype.String:
-        return 'oohash_string_foldable', op.args
-    elif T is ootype.Unicode:
-        return 'oohash_unicode_foldable', op.args
-    else:
-        raise Exception("oohash() of type %r" % (T,))
+def get_identityhash_oopspec(op):
+    return 'gc_identityhash', op.args
 
 
 RENAMED_ADT_NAME = {
@@ -371,8 +363,8 @@
         return get_call_oopspec_opargs(fnobj, opargs)
     elif op.opname in ('oostring', 'oounicode'):
         return get_oostring_oopspec(op)
-    elif op.opname == 'oohash':
-        return get_oohash_oopspec(op)
+    elif op.opname == 'gc_identityhash':
+        return get_identityhash_oopspec(op)
     else:
         raise ValueError(op.opname)
 

Modified: pypy/trunk/pypy/jit/metainterp/test/test_basic.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_basic.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_basic.py	Fri Oct 16 15:11:23 2009
@@ -971,19 +971,19 @@
 class TestOOtype(BasicTests, OOJitMixin):
 
     def test_oohash(self):
-        def f():
-            s = ootype.oostring(5, -1)
-            return ootype.oohash(s)
-        res = self.interp_operations(f, [])
-        # xxx can we rely on oohash() returning the same value in and out of
-        # translation?
-        assert res == ootype.oohash(ootype.oostring(5, -1))
+        def f(n):
+            s = ootype.oostring(n, -1)
+            return s.ll_hash()
+        res = self.interp_operations(f, [5])
+        assert res == ootype.oostring(5, -1).ll_hash()
 
-    def test_ooidentityhash(self):
+    def test_identityhash(self):
+        A = ootype.Instance("A", ootype.ROOT)
         def f():
-            s1 = ootype.oostring(5, -1)
-            s2 = ootype.oostring(6, -1)
-            return ootype.ooidentityhash(s1) == ootype.ooidentityhash(s2)
+            obj1 = ootype.new(A)
+            obj2 = ootype.new(A)
+            return ootype.identityhash(obj1) == ootype.identityhash(obj2)
+        assert not f()
         res = self.interp_operations(f, [])
         assert not res
 
@@ -1057,6 +1057,16 @@
 
 class BaseLLtypeTests(BasicTests):
 
+    def test_identityhash(self):
+        A = lltype.GcStruct("A")
+        def f():
+            obj1 = lltype.malloc(A)
+            obj2 = lltype.malloc(A)
+            return lltype.identityhash(obj1) == lltype.identityhash(obj2)
+        assert not f()
+        res = self.interp_operations(f, [])
+        assert not res
+
     def test_oops_on_nongc(self):
         from pypy.rpython.lltypesystem import lltype
         

Modified: pypy/trunk/pypy/jit/metainterp/test/test_compile.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_compile.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_compile.py	Fri Oct 16 15:11:23 2009
@@ -1,7 +1,7 @@
 from pypy.jit.metainterp.history import LoopToken, ConstInt, History, Stats
 from pypy.jit.metainterp.specnode import NotSpecNode, ConstantSpecNode
 from pypy.jit.metainterp.compile import insert_loop_token, compile_new_loop
-from pypy.jit.metainterp import optimize, jitprof
+from pypy.jit.metainterp import optimize, jitprof, typesystem
 from pypy.jit.metainterp.test.oparser import parse
 from pypy.jit.metainterp.test.test_optimizefindnode import LLtypeMixin
 
@@ -26,6 +26,7 @@
 
 
 class FakeCPU:
+    ts = typesystem.llhelper
     def __init__(self):
         self.seen = []
     def compile_loop(self, inputargs, operations, token):

Modified: pypy/trunk/pypy/jit/metainterp/test/test_loop.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_loop.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_loop.py	Fri Oct 16 15:11:23 2009
@@ -1,5 +1,6 @@
 import py
 from pypy.rlib.jit import JitDriver, OPTIMIZER_SIMPLE, OPTIMIZER_FULL
+from pypy.rlib.objectmodel import compute_hash
 from pypy.jit.metainterp.warmspot import ll_meta_interp, get_stats
 from pypy.rpython.lltypesystem import lltype
 from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin
@@ -349,7 +350,7 @@
                 myjitdriver.jit_merge_point(n=n, x=x)
                 x += unichr(n)
                 n -= 1
-            return hash(x)
+            return compute_hash(x)
         expected = self.run_directly(f, [100])
         res = self.meta_interp(f, [100])
         assert res == expected
@@ -363,7 +364,7 @@
                 myjitdriver.jit_merge_point(n=n, x=x)
                 x += chr(n)
                 n -= 1
-            return hash(x)
+            return compute_hash(x)
         expected = self.run_directly(f, [100])
         res = self.meta_interp(f, [100])
         assert res == expected

Modified: pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py	Fri Oct 16 15:11:23 2009
@@ -1,5 +1,4 @@
 import py
-from pypy.rpython.ootypesystem import ootype
 from pypy.rlib.objectmodel import instantiate
 from pypy.jit.metainterp.test.test_resume import MyMetaInterp
 from pypy.jit.metainterp.test.test_optimizefindnode import (LLtypeMixin,
@@ -35,7 +34,8 @@
     from pypy.jit.metainterp.resume import tag, TAGBOX
     b0 = BoxInt()
     b1 = BoxInt()
-    opt = optimizeopt.Optimizer(FakeMetaInterpStaticData(None), None)
+    opt = optimizeopt.Optimizer(FakeMetaInterpStaticData(LLtypeMixin.cpu),
+                                None)
     fdescr = ResumeGuardDescr(None)
     op = ResOperation(rop.GUARD_TRUE, [], None, descr=fdescr)
     # setup rd data

Modified: pypy/trunk/pypy/jit/metainterp/test/test_resume.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_resume.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_resume.py	Fri Oct 16 15:11:23 2009
@@ -244,7 +244,7 @@
           FakeFrame("code1", 3, 7, b3, c2, b1),
           FakeFrame("code2", 9, -1, c3, b2)]
     capture_resumedata(fs, None, storage)
-    memo = ResumeDataLoopMemo(None)
+    memo = ResumeDataLoopMemo(LLtypeMixin.cpu)
     modifier = ResumeDataVirtualAdder(storage, memo)
     liveboxes = modifier.finish({})
     metainterp = MyMetaInterp()
@@ -268,7 +268,7 @@
           FakeFrame("code1", 3, 7, b3, c2, b1),
           FakeFrame("code2", 9, -1, c3, b2)]
     capture_resumedata(fs, [b4], storage)
-    memo = ResumeDataLoopMemo(None)
+    memo = ResumeDataLoopMemo(LLtypeMixin.cpu)
     modifier = ResumeDataVirtualAdder(storage, memo)
     liveboxes = modifier.finish({})
     metainterp = MyMetaInterp()
@@ -296,7 +296,7 @@
     fs = fs[:-1] + [FakeFrame("code2", 10, -1, c3, b2, b4)]
     capture_resumedata(fs, None, storage2)
     
-    memo = ResumeDataLoopMemo(None)
+    memo = ResumeDataLoopMemo(LLtypeMixin.cpu)
     modifier = ResumeDataVirtualAdder(storage, memo)
     liveboxes = modifier.finish({})
 
@@ -421,7 +421,7 @@
 
 
 def test_ResumeDataLoopMemo_ints():
-    memo = ResumeDataLoopMemo(None)
+    memo = ResumeDataLoopMemo(LLtypeMixin.cpu)
     tagged = memo.getconst(ConstInt(44))
     assert untag(tagged) == (44, TAGINT)
     tagged = memo.getconst(ConstInt(-3))
@@ -461,7 +461,7 @@
     assert tagged == NULLREF
 
 def test_ResumeDataLoopMemo_other():
-    memo = ResumeDataLoopMemo(None)
+    memo = ResumeDataLoopMemo(LLtypeMixin.cpu)
     const = ConstFloat(-1.0)
     tagged = memo.getconst(const)
     index, tagbits = untag(tagged)
@@ -479,7 +479,7 @@
     env2 = [c3, b3, b1, c3]
     snap2 = Snapshot(snap, env2)
 
-    memo = ResumeDataLoopMemo(None)
+    memo = ResumeDataLoopMemo(LLtypeMixin.cpu)
 
     numb, liveboxes, v = memo.number({}, snap1)
     assert v == 0

Modified: pypy/trunk/pypy/jit/metainterp/test/test_warmspot.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_warmspot.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_warmspot.py	Fri Oct 16 15:11:23 2009
@@ -1,5 +1,5 @@
 import py
-from pypy.jit.metainterp.warmspot import ll_meta_interp, cast_whatever_to_int
+from pypy.jit.metainterp.warmspot import ll_meta_interp, hash_whatever
 from pypy.jit.metainterp.warmspot import get_stats
 from pypy.rlib.jit import JitDriver, OPTIMIZER_FULL, OPTIMIZER_SIMPLE
 from pypy.rlib.jit import unroll_safe
@@ -8,11 +8,11 @@
 from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin
 
 
-def test_translate_cast_whatever_to_int():
+def test_translate_hash_whatever():
     from pypy.rpython.test.test_llinterp import interpret
     from pypy.rpython.lltypesystem import lltype
     def fn(x):
-        return cast_whatever_to_int(lltype.typeOf(x), x)
+        return hash_whatever(lltype.typeOf(x), x)
     for type_system in ('lltype', 'ootype'):
         res = interpret(fn, [42], type_system=type_system)
         assert res == 42

Modified: pypy/trunk/pypy/jit/metainterp/typesystem.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/typesystem.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/typesystem.py	Fri Oct 16 15:11:23 2009
@@ -4,7 +4,7 @@
 from pypy.rpython.annlowlevel import cast_instance_to_base_ptr
 from pypy.rpython.annlowlevel import cast_instance_to_base_obj
 from pypy.jit.metainterp import history
-from pypy.jit.metainterp import history
+from pypy.rlib.objectmodel import r_dict
 
 def deref(T):
     if isinstance(T, lltype.Ptr):
@@ -114,11 +114,15 @@
         ll = llstr(str)
         return history.ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, ll))
 
-    def cast_ref_to_hashable(self, cpu, ptr):
-        adr = llmemory.cast_ptr_to_adr(ptr)
-        return cpu.cast_adr_to_int(adr)
+    # A dict whose keys are refs (like the .value of BoxPtr).
+    # It is an r_dict on lltype.  Two copies, to avoid conflicts with
+    # the value type.  Note that NULL is not allowed as a key.
+    def new_ref_dict(self):
+        return r_dict(rd_eq, rd_hash)
+    def new_ref_dict_2(self):
+        return r_dict(rd_eq, rd_hash)
 
-    def cast_baseclass_to_hashable(self, cpu, ptr):
+    def cast_vtable_to_hashable(self, cpu, ptr):
         adr = llmemory.cast_ptr_to_adr(ptr)
         return cpu.cast_adr_to_int(adr)
 
@@ -133,8 +137,14 @@
     def getaddr_for_box(self, cpu, box):
         return box.getaddr(cpu)
 
-    def ooidentityhash(self, x):
-        raise NotImplementedError
+def rd_eq(ref1, ref2):
+    return ref1 == ref2
+
+def rd_hash(ref):
+    assert ref
+    return lltype.identityhash(ref)
+
+# ____________________________________________________________
 
 class OOTypeHelper(TypeSystemHelper):
 
@@ -212,10 +222,15 @@
         oo = oostr(str)
         return history.ConstObj(ootype.cast_to_object(oo))
 
-    def cast_ref_to_hashable(self, cpu, obj):
-        return ootype.cast_to_object(obj)
+    # A dict whose keys are refs (like the .value of BoxObj).
+    # It is a normal dict on ootype.  Two copies, to avoid conflicts
+    # with the value type.
+    def new_ref_dict(self):
+        return {}
+    def new_ref_dict_2(self):
+        return {}
 
-    def cast_baseclass_to_hashable(self, cpu, obj):
+    def cast_vtable_to_hashable(self, cpu, obj):
         return ootype.cast_to_object(obj)
 
     def cast_from_ref(self, TYPE, value):
@@ -228,8 +243,6 @@
 
     def getaddr_for_box(self, cpu, box):
         return box.getref_base()
-
-    ooidentityhash = staticmethod(ootype.ooidentityhash)
     
 llhelper = LLTypeHelper()
 oohelper = OOTypeHelper()

Modified: pypy/trunk/pypy/jit/metainterp/warmspot.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/warmspot.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/warmspot.py	Fri Oct 16 15:11:23 2009
@@ -637,18 +637,28 @@
     return x == y
 equal_whatever._annspecialcase_ = 'specialize:arg(0)'
 
-def cast_whatever_to_int(TYPE, x):
+def hash_whatever(TYPE, x):
+    # Hash of lltype or ootype object.
+    # Only supports strings, unicodes and regular instances,
+    # as well as primitives that can meaningfully be cast to Signed.
     if isinstance(TYPE, lltype.Ptr):
-        # only supports strings, unicodes and regular instances *with a hash
-        # cache*.  The 'jit_merge_point' hint forces a hash cache to appear.
-        return x.gethash()
+        if TYPE.TO is rstr.STR or TYPE.TO is rstr.UNICODE:
+            return rstr.LLHelpers.ll_strhash(x)    # assumed not null
+        else:
+            if x:
+                return lltype.identityhash(x)
+            else:
+                return 0
     elif TYPE is ootype.String or TYPE is ootype.Unicode:
-        return ootype.oohash(x)
+        return x.ll_hash()
     elif isinstance(TYPE, ootype.OOType):
-        return ootype.ooidentityhash(x)
+        if x:
+            return ootype.identityhash(x)
+        else:
+            return 0
     else:
         return lltype.cast_primitive(lltype.Signed, x)
-cast_whatever_to_int._annspecialcase_ = 'specialize:arg(0)'
+hash_whatever._annspecialcase_ = 'specialize:arg(0)'
 
 # ____________________________________________________________
 
@@ -914,7 +924,7 @@
                     result = result * mult
                     mult = mult + 82520 + 2*len(greenargs)
                 item = greenargs[i]
-                result = result ^ cast_whatever_to_int(TYPE, item)
+                result = result ^ hash_whatever(TYPE, item)
                 i = i + 1
             return result         # returns a r_uint
         getkeyhash._always_inline_ = True

Modified: pypy/trunk/pypy/lang/prolog/interpreter/term.py
==============================================================================
--- pypy/trunk/pypy/lang/prolog/interpreter/term.py	(original)
+++ pypy/trunk/pypy/lang/prolog/interpreter/term.py	Fri Oct 16 15:11:23 2009
@@ -5,6 +5,7 @@
 from pypy.lang.prolog.interpreter.error import UnificationFailed, UncatchableError
 from pypy.lang.prolog.interpreter import error
 from pypy.rlib.objectmodel import specialize
+from pypy.rlib.objectmodel import compute_hash, compute_identity_hash
 
 DEBUG = False
 
@@ -244,7 +245,7 @@
             raise UnificationFailed
 
     def get_unify_hash(self, heap):
-        return intmask(hash(self.name) << TAGBITS | self.TAG)
+        return intmask(compute_hash(self.name) << TAGBITS | self.TAG)
 
     def unify_hash_of_children(self, heap):
         return []
@@ -370,7 +371,7 @@
             raise UnificationFailed
 
     def get_unify_hash(self, heap):
-        return intmask(hash(self) << TAGBITS | self.TAG)
+        return intmask(compute_identity_hash(self) << TAGBITS | self.TAG)
 
 
 
@@ -450,7 +451,7 @@
             return self
 
     def get_unify_hash(self, heap):
-        return intmask(hash(self.signature) << TAGBITS | self.TAG)
+        return intmask(compute_hash(self.signature) << TAGBITS | self.TAG)
 
     def unify_hash_of_children(self, heap):
         unify_hash = []

Modified: pypy/trunk/pypy/module/__builtin__/interp_classobj.py
==============================================================================
--- pypy/trunk/pypy/module/__builtin__/interp_classobj.py	(original)
+++ pypy/trunk/pypy/module/__builtin__/interp_classobj.py	Fri Oct 16 15:11:23 2009
@@ -6,6 +6,7 @@
 from pypy.interpreter.argument import Arguments
 from pypy.interpreter.baseobjspace import Wrappable
 from pypy.rlib.rarithmetic import r_uint, intmask
+from pypy.rlib.objectmodel import compute_identity_hash
 
 
 def raise_type_err(space, argument, expected, w_obj):
@@ -569,7 +570,7 @@
                 raise OperationError(space.w_TypeError,
                                      space.wrap("unhashable instance"))
             else:
-                return space.wrap(hash(self))
+                return space.wrap(compute_identity_hash(self))
         w_ret = space.call_function(w_func)
         if (not space.is_true(space.isinstance(w_ret, space.w_int)) and
             not space.is_true(space.isinstance(w_ret, space.w_long))):

Modified: pypy/trunk/pypy/objspace/descroperation.py
==============================================================================
--- pypy/trunk/pypy/objspace/descroperation.py	(original)
+++ pypy/trunk/pypy/objspace/descroperation.py	Fri Oct 16 15:11:23 2009
@@ -320,10 +320,10 @@
     def hash(space, w_obj):
         w_hash = space.lookup(w_obj, '__hash__')
         if w_hash is None:
-            if space.lookup(w_obj, '__eq__') is not None or \
-               space.lookup(w_obj, '__cmp__') is not None: 
-                raise OperationError(space.w_TypeError, 
-                                     space.wrap("unhashable type"))
+            # xxx there used to be logic about "do we have __eq__ or __cmp__"
+            # here, but it does not really make sense, as 'object' has a
+            # default __hash__.  This path should only be taken under very
+            # obscure circumstances.
             return default_identity_hash(space, w_obj)
         # XXX CPython has a special case for types with "__hash__ = None"
         # to produce a nicer error message, namely "unhashable type: 'X'".

Modified: pypy/trunk/pypy/objspace/std/complextype.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/complextype.py	(original)
+++ pypy/trunk/pypy/objspace/std/complextype.py	Fri Oct 16 15:11:23 2009
@@ -222,5 +222,4 @@
     imag = complexwprop('imagval'),
     )
 
-complex_typedef.custom_hash = True
 complex_typedef.registermethods(globals())

Modified: pypy/trunk/pypy/objspace/std/dictmultiobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/dictmultiobject.py	(original)
+++ pypy/trunk/pypy/objspace/std/dictmultiobject.py	Fri Oct 16 15:11:23 2009
@@ -30,7 +30,6 @@
 #
 #              EmptyDictImplementation
 #                /                 \
-#  SmallStrDictImplementation   SmallDictImplementation
 #               |                   |
 #   StrDictImplementation           |
 #                \                 /
@@ -164,15 +163,9 @@
     def setitem(self, w_key, w_value):
         space = self.space
         if _is_str(space, w_key):
-            if space.config.objspace.std.withsmalldicts:
-                return SmallStrDictImplementation(space, w_key, w_value)
-            else:
-                return StrDictImplementation(space).setitem_str(w_key, w_value)
+            return StrDictImplementation(space).setitem_str(w_key, w_value)
         else:
-            if space.config.objspace.std.withsmalldicts:
-                return SmallDictImplementation(space, w_key, w_value)
-            else:
-                return space.DefaultDictImpl(space).setitem(w_key, w_value)
+            return space.DefaultDictImpl(space).setitem(w_key, w_value)
     def setitem_str(self, w_key, w_value, shadows_type=True):
         return StrDictImplementation(self.space).setitem_str(w_key, w_value)
         #return SmallStrDictImplementation(self.space, w_key, w_value)
@@ -202,228 +195,10 @@
     def items(self):
         return []
 
-
 class EmptyIteratorImplementation(IteratorImplementation):
     def next_entry(self):
         return None
 
-class Entry(object):
-    def __init__(self):
-        self.hash = 0
-        self.w_key = None
-        self.w_value = None
-    def __repr__(self):
-        return '<%r, %r, %r>'%(self.hash, self.w_key, self.w_value)
-
-class SmallDictImplementation(DictImplementation):
-    # XXX document the invariants here!
-    
-    def __init__(self, space, w_key, w_value):
-        self.space = space
-        self.entries = [Entry(), Entry(), Entry(), Entry(), Entry()]
-        self.entries[0].hash = space.hash_w(w_key)
-        self.entries[0].w_key = w_key
-        self.entries[0].w_value = w_value
-        self.valid = 1
-
-    def _lookup(self, w_key):
-        hash = self.space.hash_w(w_key)
-        i = 0
-        last = self.entries[self.valid]
-        last.hash = hash
-        last.w_key = w_key
-        while 1:
-            look_entry = self.entries[i]
-            if look_entry.hash == hash and self.space.eq_w(look_entry.w_key, w_key):
-                return look_entry
-            i += 1
-
-    def _convert_to_rdict(self):
-        newimpl = self.space.DefaultDictImpl(self.space)
-        i = 0
-        while 1:
-            entry = self.entries[i]
-            if entry.w_value is None:
-                break
-            newimpl.setitem(entry.w_key, entry.w_value)
-            i += 1
-        return newimpl
-
-    def setitem(self, w_key, w_value):
-        entry = self._lookup(w_key)
-        if entry.w_value is None:
-            if self.valid == 4:
-                return self._convert_to_rdict().setitem(w_key, w_value)
-            self.valid += 1
-        entry.w_value = w_value
-        return self
-
-    def setitem_str(self, w_key, w_value, shadows_type=True):
-        return self.setitem(w_key, w_value)
-
-    def delitem(self, w_key):
-        entry = self._lookup(w_key)
-        if entry.w_value is not None:
-            for i in range(self.entries.index(entry), self.valid):
-                self.entries[i] = self.entries[i+1]
-            self.entries[self.valid] = entry
-            entry.w_value = None
-            self.valid -= 1
-            if self.valid == 0:
-                return self.space.emptydictimpl
-            return self
-        else:
-            entry.w_key = None
-            raise KeyError
-
-    def length(self):
-        return self.valid
-    def get(self, w_lookup):
-        entry = self._lookup(w_lookup)
-        val = entry.w_value
-        if val is None:
-            entry.w_key = None
-        return val
-
-    def iteritems(self):
-        return self._convert_to_rdict().iteritems()
-    def iterkeys(self):
-        return self._convert_to_rdict().iterkeys()
-    def itervalues(self):
-        return self._convert_to_rdict().itervalues()
-
-    def keys(self):
-        return [self.entries[i].w_key for i in range(self.valid)]
-    def values(self):
-        return [self.entries[i].w_value for i in range(self.valid)]
-    def items(self):
-        return [self.space.newtuple([e.w_key, e.w_value])
-                    for e in [self.entries[i] for i in range(self.valid)]]
-
-
-class StrEntry(object):
-    def __init__(self):
-        self.key = None
-        self.w_value = None
-    def __repr__(self):
-        return '<%r, %r, %r>'%(self.hash, self.key, self.w_value)
-
-class SmallStrDictImplementation(DictImplementation):
-    # XXX document the invariants here!
-
-    def __init__(self, space, w_key, w_value):
-        self.space = space
-        self.entries = [StrEntry(), StrEntry(), StrEntry(), StrEntry(), StrEntry()]
-        key = space.str_w(w_key)
-        self.entries[0].key = key
-        self.entries[0].w_value = w_value
-        self.valid = 1
-
-    def _lookup(self, key):
-        assert isinstance(key, str)
-        _hash = hash(key)
-        i = 0
-        last = self.entries[self.valid]
-        last.key = key
-        while 1:
-            look_entry = self.entries[i]
-            if hash(look_entry.key) == _hash and look_entry.key == key:
-                return look_entry
-            i += 1
-
-    def _convert_to_rdict(self):
-        newimpl = self.space.DefaultDictImpl(self.space)
-        i = 0
-        while 1:
-            entry = self.entries[i]
-            if entry.w_value is None:
-                break
-            newimpl.setitem(self.space.wrap(entry.key), entry.w_value)
-            i += 1
-        return newimpl
-
-    def _convert_to_sdict(self, w_value):
-        # this relies on the fact that the new key is in the entries
-        # list already.
-        newimpl = StrDictImplementation(self.space)
-        i = 0
-        while 1:
-            entry = self.entries[i]
-            if entry.w_value is None:
-                newimpl.content[entry.key] = w_value
-                break
-            newimpl.content[entry.key] = entry.w_value
-            i += 1
-        return newimpl
-
-    def setitem(self, w_key, w_value):
-        if not _is_str(self.space, w_key):
-            return self._convert_to_rdict().setitem(w_key, w_value)
-        return self.setitem_str(w_key, w_value)
-
-    def setitem_str(self, w_key, w_value, shadows_type=True):
-        entry = self._lookup(self.space.str_w(w_key))
-        if entry.w_value is None:
-            if self.valid == 4:
-                return self._convert_to_sdict(w_value)
-            self.valid += 1
-        entry.w_value = w_value
-        return self
-
-    def delitem(self, w_key):
-        space = self.space
-        w_key_type = space.type(w_key)
-        if space.is_w(w_key_type, space.w_str):
-            entry = self._lookup(space.str_w(w_key))
-            if entry.w_value is not None:
-                for i in range(self.entries.index(entry), self.valid):
-                    self.entries[i] = self.entries[i+1]
-                self.entries[self.valid] = entry
-                entry.w_value = None
-                self.valid -= 1
-                if self.valid == 0:
-                    return self.space.emptydictimpl
-                return self
-            else:
-                entry.key = None
-                raise KeyError
-        elif _is_sane_hash(self.space, w_key_type):
-            raise KeyError
-        else:
-            return self._convert_to_rdict().delitem(w_key)
-
-    def length(self):
-        return self.valid
-
-    def get(self, w_lookup):
-        space = self.space
-        w_lookup_type = space.type(w_lookup)
-        if space.is_w(w_lookup_type, space.w_str):
-            entry = self._lookup(space.str_w(w_lookup))
-            val = entry.w_value
-            if val is None:
-                entry.key = None
-            return val
-        elif _is_sane_hash(self.space, w_lookup_type):
-            return None
-        else:
-            return self._convert_to_rdict().get(w_lookup)
-
-    def iteritems(self):
-        return self._convert_to_rdict().iteritems()
-    def iterkeys(self):
-        return self._convert_to_rdict().iterkeys()
-    def itervalues(self):
-        return self._convert_to_rdict().itervalues()
-
-    def keys(self):
-        return [self.space.wrap(self.entries[i].key) for i in range(self.valid)]
-    def values(self):
-        return [self.entries[i].w_value for i in range(self.valid)]
-    def items(self):
-        return [self.space.newtuple([self.space.wrap(e.key), e.w_value])
-                    for e in [self.entries[i] for i in range(self.valid)]]
-
 
 class StrDictImplementation(DictImplementation):
     def __init__(self, space):

Modified: pypy/trunk/pypy/objspace/std/floattype.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/floattype.py	(original)
+++ pypy/trunk/pypy/objspace/std/floattype.py	Fri Oct 16 15:11:23 2009
@@ -50,4 +50,3 @@
 Convert a string or number to a floating point number, if possible.''',
     __new__ = newmethod(descr__new__),
     )
-float_typedef.custom_hash = True

Modified: pypy/trunk/pypy/objspace/std/frozensettype.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/frozensettype.py	(original)
+++ pypy/trunk/pypy/objspace/std/frozensettype.py	Fri Oct 16 15:11:23 2009
@@ -55,5 +55,4 @@
     __new__ = newmethod(descr__frozenset__new__),
     )
 
-frozenset_typedef.custom_hash = True
 frozenset_typedef.registermethods(globals())

Modified: pypy/trunk/pypy/objspace/std/inttype.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/inttype.py	(original)
+++ pypy/trunk/pypy/objspace/std/inttype.py	Fri Oct 16 15:11:23 2009
@@ -145,4 +145,3 @@
 will be returned instead.''',
     __new__ = newmethod(descr__new__),
     )
-int_typedef.custom_hash = True

Modified: pypy/trunk/pypy/objspace/std/longtype.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/longtype.py	(original)
+++ pypy/trunk/pypy/objspace/std/longtype.py	Fri Oct 16 15:11:23 2009
@@ -77,4 +77,3 @@
 converting a non-string.''',
     __new__ = newmethod(descr__new__),
     )
-long_typedef.custom_hash = True

Modified: pypy/trunk/pypy/objspace/std/objecttype.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/objecttype.py	(original)
+++ pypy/trunk/pypy/objspace/std/objecttype.py	Fri Oct 16 15:11:23 2009
@@ -182,5 +182,3 @@
     __init__ = gateway.interp2app(descr__init__,
                                   unwrap_spec=[gateway.ObjSpace,gateway.W_Root,gateway.Arguments]),
     )
-
-object_typedef.custom_hash = False    # object.__hash__ is not a custom hash

Modified: pypy/trunk/pypy/objspace/std/stringobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/stringobject.py	(original)
+++ pypy/trunk/pypy/objspace/std/stringobject.py	Fri Oct 16 15:11:23 2009
@@ -2,8 +2,8 @@
 
 from pypy.objspace.std.objspace import *
 from pypy.interpreter import gateway
-from pypy.rlib.rarithmetic import ovfcheck, _hash_string
-from pypy.rlib.objectmodel import we_are_translated
+from pypy.rlib.rarithmetic import ovfcheck
+from pypy.rlib.objectmodel import we_are_translated, compute_hash
 from pypy.objspace.std.inttype import wrapint
 from pypy.objspace.std.sliceobject import W_SliceObject, normalize_simple_slice
 from pypy.objspace.std import slicetype
@@ -755,12 +755,7 @@
 
 def hash__String(space, w_str):
     s = w_str._value
-    if we_are_translated():
-        x = hash(s)            # to use the hash cache in rpython strings
-    else:
-        x = _hash_string(s)    # to make sure we get the same hash as rpython
-        # (otherwise translation will freeze W_DictObjects where we can't find
-        #  the keys any more!)
+    x = compute_hash(s)
     return wrapint(space, x)
 
 def lt__String_String(space, w_str1, w_str2):

Modified: pypy/trunk/pypy/objspace/std/stringtype.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/stringtype.py	(original)
+++ pypy/trunk/pypy/objspace/std/stringtype.py	Fri Oct 16 15:11:23 2009
@@ -301,7 +301,6 @@
 If the argument is a string, the return value is the same object.'''
     )
 
-str_typedef.custom_hash = True
 str_typedef.registermethods(globals())
 
 # ____________________________________________________________

Modified: pypy/trunk/pypy/objspace/std/test/test_dictmultiobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/test/test_dictmultiobject.py	(original)
+++ pypy/trunk/pypy/objspace/std/test/test_dictmultiobject.py	Fri Oct 16 15:11:23 2009
@@ -2,7 +2,7 @@
 from pypy.objspace.std.dictmultiobject import \
      W_DictMultiObject, setitem__DictMulti_ANY_ANY, getitem__DictMulti_ANY, \
      EmptyDictImplementation, RDictImplementation, StrDictImplementation, \
-     SmallDictImplementation, SmallStrDictImplementation, MeasuringDictImplementation
+     MeasuringDictImplementation
 
 from pypy.objspace.std.celldict import ModuleDictImplementation
 from pypy.conftest import gettestobjspace
@@ -91,16 +91,6 @@
         raises(KeyError, "d['def']")
 
 
-
-class TestW_DictSmall(test_dictobject.TestW_DictObject):
-    def setup_class(cls):
-        cls.space = gettestobjspace(**{"objspace.std.withsmalldicts": True})
-
-class AppTest_DictSmall(test_dictobject.AppTest_DictObject):
-    def setup_class(cls):
-        cls.space = gettestobjspace(**{"objspace.std.withsmalldicts": True})
-
-
 class C: pass
 
 class FakeSpace(test_dictobject.FakeSpace):
@@ -250,23 +240,11 @@
 class TestStrDictImplementation(TestRDictImplementation):
     ImplementionClass = StrDictImplementation
 
-class TestSmallDictImplementation(TestRDictImplementation):
-    ImplementionClass = SmallDictImplementation
-
-    def get_impl(self):
-        return self.ImplementionClass(self.space, self.string, self.string2)
-
 class TestMeasuringDictImplementation(TestRDictImplementation):
     ImplementionClass = MeasuringDictImplementation
     DevolvedClass = MeasuringDictImplementation
     EmptyClass = MeasuringDictImplementation
 
-class TestSmallStrDictImplementation(TestRDictImplementation):
-    ImplementionClass = SmallStrDictImplementation
-
-    def get_impl(self):
-        return self.ImplementionClass(self.space, self.string, self.string2)
-
 class TestModuleDictImplementation(TestRDictImplementation):
     ImplementionClass = ModuleDictImplementation
     EmptyClass = ModuleDictImplementation

Modified: pypy/trunk/pypy/objspace/std/test/test_floatobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/test/test_floatobject.py	(original)
+++ pypy/trunk/pypy/objspace/std/test/test_floatobject.py	Fri Oct 16 15:11:23 2009
@@ -56,8 +56,13 @@
             32,              # answer on 32-bit machines
             137438953472)    # answer on 64-bit machines
         # testing special overflow values
-        assert hash(1e200 * 1e200) == 314159
-        assert hash(-1e200 * 1e200) == -271828
+        inf = 1e200 * 1e200
+        assert hash(inf) == 314159
+        assert hash(-inf) == -271828
+        x = hash(inf/inf)
+        # ^^^ assert did not crash, even though the result is a bit random
+        #     e.g. it appears to be -32768 on Win32 and 0 on Linux
+        assert x == hash(inf/inf)
 
     def test_int_float(self):
         assert int(42.1234) == 42

Modified: pypy/trunk/pypy/objspace/std/test/test_userobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/test/test_userobject.py	(original)
+++ pypy/trunk/pypy/objspace/std/test/test_userobject.py	Fri Oct 16 15:11:23 2009
@@ -1,3 +1,4 @@
+from pypy.interpreter import gateway
 
 
 class AppTestUserObject:
@@ -6,6 +7,16 @@
     def setup_class(cls):
         from pypy import conftest
         cls.space = conftest.gettestobjspace(**cls.OPTIONS)
+        #
+        import random
+        def fn_rand():
+            return cls.space.wrap(random.randrange(0, 5))
+        fn_rand.unwrap_spec = []
+        if conftest.option.runappdirect:
+            cls.w_rand = fn_rand
+        else:
+            cls.w_rand = cls.space.wrap(gateway.interp2app(fn_rand))
+        cls.w_runappdirect = cls.space.wrap(bool(conftest.option.runappdirect))
 
     def test_emptyclass(self):
         class empty(object): pass
@@ -231,6 +242,38 @@
 
         raises(AttributeError, "del Foo.x")
 
+    def test_hash(self):
+        if not hasattr(self, 'runappdirect'):
+            skip("disabled")
+        if self.runappdirect:
+            total = 500000
+        else:
+            total = 50
+        #
+        class A(object):
+            hash = None
+        tail = any = A()
+        tail.next = tail
+        i = 0
+        while i < total:
+            a = A()
+            a.next = tail.next
+            tail.next = a
+            for j in range(self.rand()):
+                any = any.next
+            if any.hash is None:
+                any.hash = hash(any)
+            else:
+                assert any.hash == hash(any)
+            i += 1
+        i = 0
+        while i < total:
+            if any.hash is not None:
+                assert any.hash == hash(any)
+            any = any.next
+            i += 1
+
+
 class AppTestWithMultiMethodVersion2(AppTestUserObject):
     OPTIONS = {}    # for test_builtinshortcut.py
 

Modified: pypy/trunk/pypy/objspace/std/tupletype.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/tupletype.py	(original)
+++ pypy/trunk/pypy/objspace/std/tupletype.py	Fri Oct 16 15:11:23 2009
@@ -27,4 +27,3 @@
 If the argument is a tuple, the return value is the same object.''',
     __new__ = newmethod(descr__new__),
     )
-tuple_typedef.custom_hash = True

Modified: pypy/trunk/pypy/objspace/std/typeobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/typeobject.py	(original)
+++ pypy/trunk/pypy/objspace/std/typeobject.py	Fri Oct 16 15:11:23 2009
@@ -6,7 +6,7 @@
 from pypy.objspace.std.objecttype import object_typedef
 from pypy.objspace.std.dictproxyobject import W_DictProxyObject
 from pypy.rlib.objectmodel import we_are_translated
-from pypy.rlib.objectmodel import current_object_addr_as_int
+from pypy.rlib.objectmodel import current_object_addr_as_int, compute_hash
 from pypy.rlib.jit import hint, purefunction, we_are_jitted, dont_look_inside
 from pypy.rlib.rarithmetic import intmask, r_uint
 
@@ -231,7 +231,8 @@
         # assumption is that the version_tag object won't keep moving all
         # the time - so using the fast current_object_addr_as_int() instead
         # of a slower solution like hash() is still a good trade-off.
-        method_hash = r_uint(intmask(version_tag_as_int * hash(name))) >> SHIFT
+        hash_name = compute_hash(name)
+        method_hash = r_uint(intmask(version_tag_as_int * hash_name)) >> SHIFT
         cached_version_tag = space.method_cache_versions[method_hash]
         if cached_version_tag is version_tag:
             cached_name = space.method_cache_names[method_hash]

Modified: pypy/trunk/pypy/objspace/std/unicodeobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/unicodeobject.py	(original)
+++ pypy/trunk/pypy/objspace/std/unicodeobject.py	Fri Oct 16 15:11:23 2009
@@ -7,6 +7,7 @@
 from pypy.objspace.std import slicetype
 from pypy.objspace.std.tupleobject import W_TupleObject
 from pypy.rlib.rarithmetic import intmask, ovfcheck
+from pypy.rlib.objectmodel import compute_hash
 from pypy.module.unicodedata import unicodedb_4_1_0 as unicodedb
 from pypy.tool.sourcetools import func_with_new_name
 
@@ -211,13 +212,7 @@
         x ^= ord(s[0])
         h = intmask(x)
         return space.wrap(h)
-    if we_are_translated():
-        x = hash(s)            # to use the hash cache in rpython strings
-    else:
-        from pypy.rlib.rarithmetic import _hash_string
-        x = _hash_string(s)    # to make sure we get the same hash as rpython
-        # (otherwise translation will freeze W_DictObjects where we can't find
-        #  the keys any more!)
+    x = compute_hash(s)
     return space.wrap(x)
 
 def len__Unicode(space, w_uni):

Modified: pypy/trunk/pypy/objspace/std/unicodetype.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/unicodetype.py	(original)
+++ pypy/trunk/pypy/objspace/std/unicodetype.py	Fri Oct 16 15:11:23 2009
@@ -298,7 +298,6 @@
 errors can be 'strict', 'replace' or 'ignore' and defaults to 'strict'.'''
     )
 
-unicode_typedef.custom_hash = True
 unicode_typedef.registermethods(globals())
 
 unitypedef = unicode_typedef

Modified: pypy/trunk/pypy/rlib/jit.py
==============================================================================
--- pypy/trunk/pypy/rlib/jit.py	(original)
+++ pypy/trunk/pypy/rlib/jit.py	Fri Oct 16 15:11:23 2009
@@ -210,9 +210,6 @@
             raise JitHintError("%s expects the following keyword "
                                "arguments: %s" % (self.instance,
                                                   expected))
-        for name in driver.greens:
-            s_green_key = kwds_s['s_' + name]
-            s_green_key.hash()      # force the hash cache to appear
 
         if self.instance.__name__ == 'jit_merge_point':
             self.annotate_hooks(**kwds_s)

Modified: pypy/trunk/pypy/rlib/objectmodel.py
==============================================================================
--- pypy/trunk/pypy/rlib/objectmodel.py	(original)
+++ pypy/trunk/pypy/rlib/objectmodel.py	Fri Oct 16 15:11:23 2009
@@ -5,6 +5,7 @@
 
 import sys
 import types
+import math
 
 # specialize is a decorator factory for attaching _annspecialcase_
 # attributes to functions: for example
@@ -127,64 +128,192 @@
     obj.__dict__ = {}
     obj.__class__ = FREED_OBJECT
 
-from pypy.rpython.extregistry import ExtRegistryEntry
-
 # ____________________________________________________________
 #
-# id-like functions.
-# In addition, RPython supports hash(x) on RPython instances,
-# returning a number that is not guaranteed to be unique but
-# that doesn't change over time for a given 'x'.
+# id-like functions.  The idea is that calling hash() or id() is not
+# allowed in RPython.  You have to call one of the following more
+# precise functions.
+
+def compute_hash(x):
+    """RPython equivalent of hash(x), where 'x' is an immutable
+    RPython-level.  For strings or unicodes it computes the hash as
+    in Python.  For tuples it calls compute_hash() recursively.
+    For instances it uses compute_identity_hash().
+
+    Note that this can return 0 or -1 too.
+
+    Behavior across translation:
+
+      * on lltypesystem, it always returns the same number, both
+        before and after translation.  Dictionaries don't need to
+        be rehashed after translation.
+
+      * on ootypesystem, the value changes because of translation.
+        Dictionaries need to be rehashed.
+    """
+    if isinstance(x, (str, unicode)):
+        return _hash_string(x)
+    if isinstance(x, int):
+        return x
+    if isinstance(x, float):
+        return _hash_float(x)
+    if isinstance(x, tuple):
+        return _hash_tuple(x)
+    if x is None:
+        return 0
+    return compute_identity_hash(x)
+
+def compute_identity_hash(x):
+    """RPython equivalent of object.__hash__(x).  This returns the
+    so-called 'identity hash', which is the non-overridable default hash
+    of Python.  Can be called for any RPython-level object that turns
+    into a GC object, but not NULL.  The value is not guaranteed to be the
+    same before and after translation, except for RPython instances on the
+    lltypesystem.
+    """
+    result = object.__hash__(x)
+    try:
+        x.__dict__['__precomputed_identity_hash'] = result
+    except (TypeError, AttributeError):
+        pass
+    return result
 
 def compute_unique_id(x):
-    """RPython equivalent of id(x).  The 'x' must be an RPython instance.
-    This operation can be very costly depending on the garbage collector.
-    To remind you of this fact, we don't support id(x) directly.
+    """RPython equivalent of id(x).  The 'x' must be an RPython-level
+    object that turns into a GC object.  This operation can be very
+    costly depending on the garbage collector.  To remind you of this
+    fact, we don't support id(x) directly.
+    (XXX not implemented on ootype, falls back to compute_identity_hash)
     """
     return id(x)      # XXX need to return r_longlong on some platforms
 
 def current_object_addr_as_int(x):
     """A cheap version of id(x).  The current memory location of an
-    instance can change over time for moving GCs.  Also note that on
+    object can change over time for moving GCs.  Also note that on
     ootypesystem this typically doesn't return the real address but
-    just the same as hash(x).
+    just the same as compute_hash(x).
     """
     from pypy.rlib.rarithmetic import intmask
     return intmask(id(x))
 
+# ----------
+
+def _hash_string(s):
+    """The algorithm behind compute_hash() for a string or a unicode."""
+    from pypy.rlib.rarithmetic import intmask
+    length = len(s)
+    if length == 0:
+        return -1
+    x = ord(s[0]) << 7
+    i = 0
+    while i < length:
+        x = (1000003*x) ^ ord(s[i])
+        i += 1
+    x ^= length
+    return intmask(x)
+
+def _hash_float(f):
+    """The algorithm behind compute_hash() for a float.
+    This implementation is identical to the CPython implementation,
+    except the fact that the integer case is not treated specially.
+    In RPython, floats cannot be used with ints in dicts, anyway.
+    """
+    from pypy.rlib.rarithmetic import intmask, isinf, isnan
+    if isinf(f):
+        if f < 0.0:
+            return -271828
+        else:
+            return 314159
+    elif isnan(f):
+        return 0
+    v, expo = math.frexp(f)
+    v *= TAKE_NEXT
+    hipart = int(v)
+    v = (v - float(hipart)) * TAKE_NEXT
+    x = hipart + int(v) + (expo << 15)
+    return intmask(x)
+TAKE_NEXT = float(2**31)
+
+def _hash_tuple(t):
+    """NOT_RPYTHON.  The algorithm behind compute_hash() for a tuple.
+    It is modelled after the old algorithm of Python 2.3, which is
+    a bit faster than the one introduced by Python 2.4.  We assume
+    that nested tuples are very uncommon in RPython, making the bad
+    case unlikely.
+    """
+    from pypy.rlib.rarithmetic import intmask
+    x = 0x345678
+    for item in t:
+        y = compute_hash(item)
+        x = intmask((1000003 * x) ^ y)
+    return x
+
+# ----------
+
+from pypy.rpython.extregistry import ExtRegistryEntry
+
+class Entry(ExtRegistryEntry):
+    _about_ = compute_hash
+
+    def compute_result_annotation(self, s_x):
+        from pypy.annotation import model as annmodel
+        return annmodel.SomeInteger()
+
+    def specialize_call(self, hop):
+        r_obj, = hop.args_r
+        v_obj, = hop.inputargs(r_obj)
+        ll_fn = r_obj.get_ll_hash_function()
+        return hop.gendirectcall(ll_fn, v_obj)
+
+class Entry(ExtRegistryEntry):
+    _about_ = compute_identity_hash
+
+    def compute_result_annotation(self, s_x):
+        from pypy.annotation import model as annmodel
+        return annmodel.SomeInteger()
+
+    def specialize_call(self, hop):
+        from pypy.rpython.lltypesystem import lltype
+        vobj, = hop.inputargs(hop.args_r[0])
+        if hop.rtyper.type_system.name == 'lltypesystem':
+            ok = (isinstance(vobj.concretetype, lltype.Ptr) and
+                  vobj.concretetype.TO._gckind == 'gc')
+        else:
+            from pypy.rpython.ootypesystem import ootype
+            ok = isinstance(vobj.concretetype, ootype.OOType)
+        if not ok:
+            from pypy.rpython.error import TyperError
+            raise TyperError("compute_identity_hash() cannot be applied to"
+                             " %r" % (vobj.concretetype,))
+        return hop.genop('gc_identityhash', [vobj], resulttype=lltype.Signed)
+
 class Entry(ExtRegistryEntry):
     _about_ = compute_unique_id
 
     def compute_result_annotation(self, s_x):
         from pypy.annotation import model as annmodel
-        assert isinstance(s_x, annmodel.SomeInstance)
         return annmodel.SomeInteger()
 
     def specialize_call(self, hop):
+        from pypy.rpython.lltypesystem import lltype
         vobj, = hop.inputargs(hop.args_r[0])
         if hop.rtyper.type_system.name == 'lltypesystem':
-            from pypy.rpython.lltypesystem import lltype
-            if isinstance(vobj.concretetype, lltype.Ptr):
-                return hop.genop('gc_id', [vobj],
-                                 resulttype = lltype.Signed)
-        elif hop.rtyper.type_system.name == 'ootypesystem':
+            ok = (isinstance(vobj.concretetype, lltype.Ptr) and
+                  vobj.concretetype.TO._gckind == 'gc')
+        else:
             from pypy.rpython.ootypesystem import ootype
-            if isinstance(vobj.concretetype, ootype.Instance):
-                # XXX wrong implementation for now, fix me
-                from pypy.rpython.rmodel import warning
-                warning("compute_unique_id() is not fully supported on ootype")
-                return hop.genop('ooidentityhash', [vobj],
-                                 resulttype = ootype.Signed)
-        from pypy.rpython.error import TyperError
-        raise TyperError("compute_unique_id() cannot be applied to %r" % (
-            vobj.concretetype,))
+            ok = isinstance(vobj.concretetype, ootype.Instance)
+        if not ok:
+            from pypy.rpython.error import TyperError
+            raise TyperError("compute_unique_id() cannot be applied to"
+                             " %r" % (vobj.concretetype,))
+        return hop.genop('gc_id', [vobj], resulttype=lltype.Signed)
 
 class Entry(ExtRegistryEntry):
     _about_ = current_object_addr_as_int
 
     def compute_result_annotation(self, s_x):
         from pypy.annotation import model as annmodel
-        assert isinstance(s_x, annmodel.SomeInstance)
         return annmodel.SomeInteger()
 
     def specialize_call(self, hop):
@@ -197,12 +326,14 @@
         elif hop.rtyper.type_system.name == 'ootypesystem':
             from pypy.rpython.ootypesystem import ootype
             if isinstance(vobj.concretetype, ootype.Instance):
-                return hop.genop('ooidentityhash', [vobj],
+                return hop.genop('gc_identityhash', [vobj],
                                  resulttype = ootype.Signed)
         from pypy.rpython.error import TyperError
         raise TyperError("current_object_addr_as_int() cannot be applied to"
                          " %r" % (vobj.concretetype,))
 
+# ____________________________________________________________
+
 def hlinvoke(repr, llcallable, *args):
     raise TypeError, "hlinvoke is meant to be rtyped and not called direclty"
 

Modified: pypy/trunk/pypy/rlib/rarithmetic.py
==============================================================================
--- pypy/trunk/pypy/rlib/rarithmetic.py	(original)
+++ pypy/trunk/pypy/rlib/rarithmetic.py	Fri Oct 16 15:11:23 2009
@@ -463,23 +463,6 @@
 
     return formatd(fmt, x)
 
-# a common string hash function
-
-def _hash_string(s):
-    length = len(s)
-    if length == 0:
-        x = -1
-    else:
-        x = ord(s[0]) << 7
-        i = 0
-        while i < length:
-            x = (1000003*x) ^ ord(s[i])
-            i += 1
-        x ^= length
-        if x == 0:
-            x = -1
-    return intmask(x)
-
 # the 'float' C type
 
 class r_singlefloat(object):

Modified: pypy/trunk/pypy/rlib/rope.py
==============================================================================
--- pypy/trunk/pypy/rlib/rope.py	(original)
+++ pypy/trunk/pypy/rlib/rope.py	Fri Oct 16 15:11:23 2009
@@ -1,6 +1,6 @@
 import py
 import sys
-from pypy.rlib.rarithmetic import intmask, _hash_string, ovfcheck
+from pypy.rlib.rarithmetic import intmask, ovfcheck
 from pypy.rlib.rarithmetic import r_uint, LONG_BIT
 from pypy.rlib.objectmodel import we_are_translated
 import math

Modified: pypy/trunk/pypy/rlib/rweakrefimpl.py
==============================================================================
--- pypy/trunk/pypy/rlib/rweakrefimpl.py	(original)
+++ pypy/trunk/pypy/rlib/rweakrefimpl.py	Fri Oct 16 15:11:23 2009
@@ -91,6 +91,8 @@
                                     adtmeths=entrymeths,
                                     hints={'weakarray': 'value'})
 
+ll_strhash = rstr.LLHelpers.ll_strhash
+
 @jit.dont_look_inside
 def ll_new_weakdict():
     d = lltype.malloc(WEAKDICT)
@@ -101,7 +103,8 @@
 
 @jit.dont_look_inside
 def ll_get(d, llkey):
-    i = rdict.ll_dict_lookup(d, llkey, llkey.gethash())
+    hash = ll_strhash(llkey)
+    i = rdict.ll_dict_lookup(d, llkey, hash)
     #llop.debug_print(lltype.Void, i, 'get')
     valueref = d.entries[i].value
     if valueref:
@@ -118,8 +121,9 @@
 
 @jit.dont_look_inside
 def ll_set_nonnull(d, llkey, llvalue):
+    hash = ll_strhash(llkey)
     valueref = weakref_create(llvalue)    # GC effects here, before the rest
-    i = rdict.ll_dict_lookup(d, llkey, llkey.gethash())
+    i = rdict.ll_dict_lookup(d, llkey, hash)
     everused = d.entries.everused(i)
     d.entries[i].key = llkey
     d.entries[i].value = valueref
@@ -132,7 +136,8 @@
 
 @jit.dont_look_inside
 def ll_set_null(d, llkey):
-    i = rdict.ll_dict_lookup(d, llkey, llkey.gethash())
+    hash = ll_strhash(llkey)
+    i = rdict.ll_dict_lookup(d, llkey, hash)
     if d.entries.everused(i):
         # If the entry was ever used, clean up its key and value.
         # We don't store a NULL value, but a dead weakref, because

Modified: pypy/trunk/pypy/rlib/test/test_objectmodel.py
==============================================================================
--- pypy/trunk/pypy/rlib/test/test_objectmodel.py	(original)
+++ pypy/trunk/pypy/rlib/test/test_objectmodel.py	Fri Oct 16 15:11:23 2009
@@ -141,6 +141,51 @@
     py.test.raises(TypeError, "s1 < s2")
     py.test.raises(TypeError, "hash(s1)")
 
+def test_compute_hash():
+    from pypy.rlib.objectmodel import _hash_string, _hash_float, _hash_tuple
+    assert compute_hash("Hello") == _hash_string("Hello")
+    assert compute_hash(7) == 7
+    assert compute_hash(-3.5) == _hash_float(-3.5)
+    assert compute_hash(None) == 0
+    assert compute_hash(("world", None, 7)) == _hash_tuple(("world", None, 7))
+    #
+    class Foo(object):
+        def __hash__(self):
+            return 42
+    foo = Foo()
+    h = compute_hash(foo)
+    assert h == object.__hash__(foo)
+    assert h == getattr(foo, '__precomputed_identity_hash')
+    assert compute_hash(None) == 0
+
+def test_compute_hash_float():
+    from pypy.rlib.rarithmetic import INFINITY, NAN
+    assert compute_hash(INFINITY) == 314159
+    assert compute_hash(-INFINITY) == -271828
+    assert compute_hash(NAN) == 0
+
+def test_compute_identity_hash():
+    class Foo(object):
+        def __hash__(self):
+            return 42
+    foo = Foo()
+    h = compute_identity_hash(foo)
+    assert h == object.__hash__(foo)
+    assert h == getattr(foo, '__precomputed_identity_hash')
+
+def test_compute_unique_id():
+    class Foo(object):
+        pass
+    foo = Foo()
+    assert compute_unique_id(foo) == id(foo)
+
+def test_current_object_addr_as_int():
+    from pypy.rlib.rarithmetic import intmask
+    class Foo(object):
+        pass
+    foo = Foo()
+    assert current_object_addr_as_int(foo) == intmask(id(foo))
+
 class BaseTestObjectModel(BaseRtypingTest):
 
     def test_we_are_translated(self):
@@ -270,6 +315,31 @@
         res = self.interpret(g, [3])
         assert res == 77
 
+    def test_compute_hash(self):
+        class Foo(object):
+            pass
+        def f(i):
+            assert compute_hash(i) == compute_hash(42)
+            assert compute_hash(i+1.0) == compute_hash(43.0)
+            assert compute_hash("Hello" + str(i)) == compute_hash("Hello42")
+            if i == 42:
+                p = None
+            else:
+                p = Foo()
+            assert compute_hash(p) == compute_hash(None)
+            assert (compute_hash(("world", None, i, 7.5)) ==
+                    compute_hash(("world", None, 42, 7.5)))
+            q = Foo()
+            assert compute_hash(q) == compute_identity_hash(q)
+            from pypy.rlib.rarithmetic import INFINITY, NAN
+            assert compute_hash(INFINITY) == 314159
+            assert compute_hash(-INFINITY) == -271828
+            assert compute_hash(NAN) == 0
+            return i*2
+        res = self.interpret(f, [42])
+        assert res == 84
+
+
 class TestLLtype(BaseTestObjectModel, LLRtypeMixin):
 
     def test_rtype_keepalive(self):
@@ -283,6 +353,36 @@
         res = self.interpret(f, [])
         assert res == 1
 
+    def test_compute_hash_across_translation(self):
+        class Foo(object):
+            pass
+        q = Foo()
+
+        def f(i):
+            assert compute_hash(None) == 0
+            assert compute_hash(i) == h_42
+            assert compute_hash(i+1.0) == h_43_dot_0
+            assert compute_hash((i+3)/6.0) == h_7_dot_5
+            assert compute_hash("Hello" + str(i)) == h_Hello42
+            if i == 42:
+                p = None
+            else:
+                p = Foo()
+            assert compute_hash(p) == h_None
+            assert compute_hash(("world", None, i, 7.5)) == h_tuple
+            assert compute_hash(q) == h_q
+            return i*2
+        h_42       = compute_hash(42)
+        h_43_dot_0 = compute_hash(43.0)
+        h_7_dot_5  = compute_hash(7.5)
+        h_Hello42  = compute_hash("Hello42")
+        h_None     = compute_hash(None)
+        h_tuple    = compute_hash(("world", None, 42, 7.5))
+        h_q        = compute_hash(q)
+        
+        res = self.interpret(f, [42])
+        assert res == 84
+
 
 class TestOOtype(BaseTestObjectModel, OORtypeMixin):
     pass

Modified: pypy/trunk/pypy/rpython/llinterp.py
==============================================================================
--- pypy/trunk/pypy/rpython/llinterp.py	(original)
+++ pypy/trunk/pypy/rpython/llinterp.py	Fri Oct 16 15:11:23 2009
@@ -824,6 +824,9 @@
     def op_gc_assume_young_pointers(self, addr):
         raise NotImplementedError
 
+    def op_gc_obtain_free_space(self, size):
+        raise NotImplementedError
+
     def op_gc_can_move(self, ptr):
         addr = llmemory.cast_ptr_to_adr(ptr)
         return self.heap.can_move(addr)
@@ -881,8 +884,16 @@
             self.setvar(v_ptr, p)
     op_gc_reload_possibly_moved.specialform = True
 
-    def op_gc_id(self, v_ptr):
-        return self.heap.gc_id(v_ptr)
+    def op_gc_identityhash(self, obj):
+        return lltype.identityhash(obj)
+
+    def op_gc_id(self, ptr):
+        PTR = lltype.typeOf(ptr)
+        if isinstance(PTR, lltype.Ptr):
+            return self.heap.gc_id(ptr)
+        elif isinstance(PTR, ootype.OOType):
+            return ootype.identityhash(ptr)     # XXX imprecise
+        raise NotImplementedError("gc_id on %r" % (PTR,))
 
     def op_gc_set_max_heap_size(self, maxsize):
         raise NotImplementedError("gc_set_max_heap_size")
@@ -1186,9 +1197,6 @@
             raise RuntimeError("calling abstract method %r" % (m,))
         return self.perform_call(m, (lltype.typeOf(inst),)+lltype.typeOf(m).ARGS, [inst]+args)
 
-    def op_ooidentityhash(self, inst):
-        return ootype.ooidentityhash(inst)
-
     def op_oostring(self, obj, base):
         return ootype.oostring(obj, base)
 
@@ -1210,13 +1218,10 @@
         except ValueError:
             self.make_llexception()
 
-    def op_oohash(self, s):
-        return ootype.oohash(s)
-
 class Tracer(object):
     Counter = 0
     file = None
-    TRACE = False
+    TRACE = int(os.getenv('PYPY_TRACE') or '0')
 
     HEADER = """<html><head>
         <script language=javascript type='text/javascript'>

Modified: pypy/trunk/pypy/rpython/lltypesystem/lloperation.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/lloperation.py	(original)
+++ pypy/trunk/pypy/rpython/lltypesystem/lloperation.py	Fri Oct 16 15:11:23 2009
@@ -439,10 +439,18 @@
     'gc_push_alive_pyobj':  LLOp(),
     'gc_pop_alive_pyobj':   LLOp(),
     'gc_reload_possibly_moved': LLOp(),
+    # see rlib/objectmodel for gc_identityhash and gc_id
+    'gc_identityhash':      LLOp(canraise=(MemoryError,), sideeffects=False,
+                                 canunwindgc=True),
     'gc_id':                LLOp(canraise=(MemoryError,), sideeffects=False),
+                                 # ^^^ but canunwindgc=False, as it is
+                                 # allocating non-GC structures only
+    'gc_obtain_free_space': LLOp(),
     'gc_set_max_heap_size': LLOp(),
     'gc_can_move'         : LLOp(sideeffects=False),
     'gc_thread_prepare'   : LLOp(canraise=(MemoryError,)),
+                                 # ^^^ but canunwindgc=False, as it is
+                                 # allocating non-GC structures only
     'gc_thread_run'       : LLOp(),
     'gc_thread_die'       : LLOp(),
     'gc_assume_young_pointers': LLOp(),
@@ -538,11 +546,9 @@
     'instanceof':           LLOp(oo=True, canfold=True),
     'classof':              LLOp(oo=True, canfold=True),
     'subclassof':           LLOp(oo=True, canfold=True),
-    'ooidentityhash':       LLOp(oo=True, sideeffects=False),  # not an id()!
     'oostring':             LLOp(oo=True, sideeffects=False),
     'ooparse_int':          LLOp(oo=True, canraise=(ValueError,)),
     'ooparse_float':        LLOp(oo=True, canraise=(ValueError,)),
-    'oohash':               LLOp(oo=True, sideeffects=False),
     'oounicode':            LLOp(oo=True, canraise=(UnicodeDecodeError,)),
 
     # _____ read frame var support ___

Modified: pypy/trunk/pypy/rpython/lltypesystem/lltype.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/lltype.py	(original)
+++ pypy/trunk/pypy/rpython/lltypesystem/lltype.py	Fri Oct 16 15:11:23 2009
@@ -1119,6 +1119,19 @@
             return callb(*args)
         raise TypeError("%r instance is not a function" % (self._T,))
 
+    def _identityhash(self, cache=True):
+        p = normalizeptr(self)
+        try:
+            return p._obj._hash_cache_
+        except AttributeError:
+            result = hash(p._obj)
+            if cache:
+                try:
+                    p._obj._hash_cache_ = result
+                except AttributeError:
+                    pass
+            return result
+
 class _ptr(_abstract_ptr):
     __slots__ = ('_TYPE', 
                  '_weak', '_solid',
@@ -1390,7 +1403,7 @@
 class _struct(_parentable):
     _kind = "structure"
 
-    __slots__ = ()
+    __slots__ = ('_hash_cache_',)
 
     def __new__(self, TYPE, n=None, initialization=None, parent=None, parentindex=None):
         my_variety = _struct_variety(TYPE._names)
@@ -1840,6 +1853,33 @@
                                  "should have been: %s" % (p, result2, result))
     return result
 
+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_identity_hash() for more
+    information about the RPython-level meaning of this.
+    """
+    assert p
+    return p._identityhash()
+
+def identityhash_nocache(p):
+    """Version of identityhash() to use from backends that don't care about
+    caching."""
+    assert p
+    return p._identityhash(cache=False)
+
+def init_identity_hash(p, value):
+    """For a prebuilt object p, initialize its hash value to 'value'."""
+    assert isinstance(typeOf(p), Ptr)
+    p = normalizeptr(p)
+    if not p:
+        raise ValueError("cannot change hash(NULL)!")
+    if hasattr(p._obj, '_hash_cache_'):
+        raise ValueError("the hash of %r was already computed" % (p,))
+    if typeOf(p).TO._is_varsize():
+        raise ValueError("init_identity_hash(): not for varsized types")
+    p._obj._hash_cache_ = intmask(value)
+
 def isCompatibleType(TYPE1, TYPE2):
     return TYPE1._is_compatible(TYPE2)
 

Modified: pypy/trunk/pypy/rpython/lltypesystem/rclass.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/rclass.py	(original)
+++ pypy/trunk/pypy/rpython/lltypesystem/rclass.py	Fri Oct 16 15:11:23 2009
@@ -20,6 +20,7 @@
 from pypy.rpython.extregistry import ExtRegistryEntry
 from pypy.annotation import model as annmodel
 from pypy.rlib.rarithmetic import intmask
+from pypy.rlib import objectmodel
 
 #
 #  There is one "vtable" per user class, with the following structure:
@@ -331,12 +332,6 @@
                     mangled_name = 'inst_' + name
                     fields[name] = mangled_name, r
                     llfields.append((mangled_name, r.lowleveltype))
-            #
-            # hash() support
-            if self.rtyper.needs_hash_support(self.classdef):
-                from pypy.rpython import rint
-                fields['_hash_cache_'] = 'hash_cache', rint.signed_repr
-                llfields.append(('hash_cache', Signed))
 
             self.rbase = getinstancerepr(self.rtyper, self.classdef.basedef,
                                          self.gcflavor)
@@ -348,10 +343,6 @@
             if hints is None:
                 hints = {}
             hints = self._check_for_immutable_hints(hints)
-            if ('_hash_cache_' in fields or
-                '_hash_cache_' in self.rbase.allinstancefields):
-                adtmeths = adtmeths.copy()
-                adtmeths['gethash'] = self.get_ll_hash_function()
             object_type = MkStruct(self.classdef.name,
                                    ('super', self.rbase.object_type),
                                    hints=hints,
@@ -406,21 +397,6 @@
     def create_instance(self):
         return malloc(self.object_type, flavor=self.gcflavor)
 
-    def get_ll_hash_function(self):
-        if self.classdef is None:
-            raise TyperError, 'missing hash support flag in classdef'
-        if self.rtyper.needs_hash_support(self.classdef):
-            try:
-                return self._ll_hash_function
-            except AttributeError:
-                INSPTR = self.lowleveltype
-                def _ll_hash_function(ins):
-                    return ll_inst_hash(cast_pointer(INSPTR, ins))
-                self._ll_hash_function = _ll_hash_function
-                return _ll_hash_function
-        else:
-            return self.rbase.get_ll_hash_function()
-
     def initialize_prebuilt_data(self, value, classdef, result):
         if self.classdef is not None:
             # recursively build the parent part of the instance
@@ -429,8 +405,6 @@
             for name, (mangled_name, r) in self.fields.items():
                 if r.lowleveltype is Void:
                     llattrvalue = None
-                elif name == '_hash_cache_': # hash() support
-                    continue   # already done by initialize_prebuilt_hash()
                 else:
                     try:
                         attrvalue = getattr(value, name)
@@ -451,12 +425,9 @@
             result.typeptr = rclass.getvtable()
 
     def initialize_prebuilt_hash(self, value, result):
-        if self.classdef is not None:
-            self.rbase.initialize_prebuilt_hash(value, result.super)
-            if '_hash_cache_' in self.fields:
-                mangled_name, r = self.fields['_hash_cache_']
-                llattrvalue = hash(value)
-                setattr(result, mangled_name, llattrvalue)
+        llattrvalue = getattr(value, '__precomputed_identity_hash', None)
+        if llattrvalue is not None:
+            lltype.init_identity_hash(result, llattrvalue)
 
     def getfieldrepr(self, attr):
         """Return the repr used for the given attribute."""
@@ -523,10 +494,7 @@
                 mangled_name, r = self.allinstancefields[fldname]
                 if r.lowleveltype is Void:
                     continue
-                if fldname == '_hash_cache_':
-                    value = Constant(0, Signed)
-                else:
-                    value = self.classdef.classdesc.read_attribute(fldname, None)
+                value = self.classdef.classdesc.read_attribute(fldname, None)
                 if value is not None:
                     cvalue = inputconst(r.lowleveltype,
                                         r.convert_desc_or_const(value))
@@ -696,18 +664,6 @@
 def ll_runtime_type_info(obj):
     return obj.typeptr.rtti
 
-def ll_inst_hash(ins):
-    if not ins:
-        return 0    # for None
-    cached = ins.hash_cache
-    if cached == 0:
-        # XXX this should ideally be done in a GC-dependent way: we only
-        # need a hash_cache for moving GCs, and we only need the '~' to
-        # avoid Boehm keeping the object alive if the value is passed
-        # around
-       cached = ins.hash_cache = ~cast_ptr_to_int(ins)
-    return cached
-
 def ll_inst_type(obj):
     if obj:
         return obj.typeptr

Modified: pypy/trunk/pypy/rpython/lltypesystem/rstr.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/rstr.py	(original)
+++ pypy/trunk/pypy/rpython/lltypesystem/rstr.py	Fri Oct 16 15:11:23 2009
@@ -2,10 +2,10 @@
 from pypy.tool.pairtype import pairtype
 from pypy.rpython.error import TyperError
 from pypy.rlib.objectmodel import malloc_zero_filled, we_are_translated
+from pypy.rlib.objectmodel import _hash_string
 from pypy.rlib.debug import ll_assert
 from pypy.rlib.jit import purefunction
 from pypy.rpython.robject import PyObjRepr, pyobj_repr
-from pypy.rlib.rarithmetic import _hash_string
 from pypy.rpython.rmodel import inputconst, IntegerRepr
 from pypy.rpython.rstr import AbstractStringRepr,AbstractCharRepr,\
      AbstractUniCharRepr, AbstractStringIteratorRepr,\
@@ -287,6 +287,8 @@
         x = s.hash
         if x == 0:
             x = _hash_string(s.chars)
+            if x == 0:
+                x = 29872897
             s.hash = x
         return x
     ll_strhash._pure_function_ = True # it's pure but it does not look like it

Modified: pypy/trunk/pypy/rpython/lltypesystem/test/test_lltype.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/test/test_lltype.py	(original)
+++ pypy/trunk/pypy/rpython/lltypesystem/test/test_lltype.py	Fri Oct 16 15:11:23 2009
@@ -739,3 +739,39 @@
     del ptr
     import gc; gc.collect(); gc.collect()
     ptr2[0] = 5    # crashes if the array was deallocated
+
+def test_identityhash():
+    S = GcStruct('S', ('x', Signed))
+    S2 = GcStruct('S2', ('super', S))
+    S3 = GcStruct('S3', ('super', S2))
+
+    py.test.raises(AssertionError, identityhash, nullptr(S2))
+
+    s3 = malloc(S3)
+    hash3 = identityhash(s3.super)
+    assert hash3 == identityhash(s3)
+    assert hash3 == identityhash(s3.super)
+    assert hash3 == identityhash(s3.super.super)
+    py.test.raises(ValueError, init_identity_hash, s3, hash3^1)
+    py.test.raises(ValueError, init_identity_hash, s3.super, hash3^4)
+    py.test.raises(ValueError, init_identity_hash, s3.super.super, hash3^9)
+
+    s3 = malloc(S3)
+    init_identity_hash(s3.super, -123)
+    assert -123 == identityhash(s3)
+    assert -123 == identityhash(s3.super)
+    assert -123 == identityhash(s3.super.super)
+    py.test.raises(ValueError, init_identity_hash, s3, 4313)
+    py.test.raises(ValueError, init_identity_hash, s3.super, 0)
+    py.test.raises(ValueError, init_identity_hash, s3.super.super, -124)
+
+    from pypy.rpython.lltypesystem import llmemory
+    p3 = cast_opaque_ptr(llmemory.GCREF, s3)
+    assert -123 == identityhash(p3)
+
+    A = GcArray(Signed)
+    a = malloc(A, 3)
+    hash1 = identityhash(a)
+    assert hash1 == identityhash(a)
+    p = cast_opaque_ptr(llmemory.GCREF, a)
+    assert hash1 == identityhash(p)

Modified: pypy/trunk/pypy/rpython/memory/gc/generation.py
==============================================================================
--- pypy/trunk/pypy/rpython/memory/gc/generation.py	(original)
+++ pypy/trunk/pypy/rpython/memory/gc/generation.py	Fri Oct 16 15:11:23 2009
@@ -1,6 +1,7 @@
 import sys
 from pypy.rpython.memory.gc.semispace import SemiSpaceGC
 from pypy.rpython.memory.gc.semispace import GCFLAG_EXTERNAL, GCFLAG_FORWARDED
+from pypy.rpython.memory.gc.semispace import GCFLAG_HASHTAKEN
 from pypy.rpython.lltypesystem.llmemory import NULL, raw_malloc_usage
 from pypy.rpython.lltypesystem import lltype, llmemory, llarena
 from pypy.rpython.memory.support import DEFAULT_CHUNK_SIZE
@@ -220,7 +221,8 @@
     # flags exposed for the HybridGC subclass
     GCFLAGS_FOR_NEW_YOUNG_OBJECTS = 0   # NO_YOUNG_PTRS never set on young objs
     GCFLAGS_FOR_NEW_EXTERNAL_OBJECTS = (GCFLAG_EXTERNAL | GCFLAG_FORWARDED |
-                                        GCFLAG_NO_YOUNG_PTRS)
+                                        GCFLAG_NO_YOUNG_PTRS |
+                                        GCFLAG_HASHTAKEN)
 
     # ____________________________________________________________
     # Support code for full collections
@@ -243,11 +245,11 @@
             llop.debug_print(lltype.Void, "percent survived", float(self.free - self.tospace) / self.space_size)
 
     def make_a_copy(self, obj, objsize):
-        newobj = SemiSpaceGC.make_a_copy(self, obj, objsize)
+        tid = self.header(obj).tid
         # During a full collect, all copied objects might implicitly come
         # from the nursery.  In case they do, we must add this flag:
-        self.header(newobj).tid |= GCFLAG_NO_YOUNG_PTRS
-        return newobj
+        tid |= GCFLAG_NO_YOUNG_PTRS
+        return self._make_a_copy_with_tid(obj, objsize, tid)
         # history: this was missing and caused an object to become old but without the
         # flag set.  Such an object is bogus in the sense that the write_barrier doesn't
         # work on it.  So it can eventually contain a ptr to a young object but we didn't
@@ -386,7 +388,7 @@
         while scan < self.free:
             curr = scan + self.size_gc_header()
             self.trace_and_drag_out_of_nursery(curr)
-            scan += self.size_gc_header() + self.get_size(curr)
+            scan += self.size_gc_header() + self.get_size_incl_hash(curr)
         return scan
 
     def trace_and_drag_out_of_nursery(self, obj):

Modified: pypy/trunk/pypy/rpython/memory/gc/hybrid.py
==============================================================================
--- pypy/trunk/pypy/rpython/memory/gc/hybrid.py	(original)
+++ pypy/trunk/pypy/rpython/memory/gc/hybrid.py	Fri Oct 16 15:11:23 2009
@@ -2,6 +2,7 @@
 from pypy.rpython.memory.gc.semispace import SemiSpaceGC
 from pypy.rpython.memory.gc.generation import GenerationGC
 from pypy.rpython.memory.gc.semispace import GCFLAG_EXTERNAL, GCFLAG_FORWARDED
+from pypy.rpython.memory.gc.semispace import GCFLAG_HASHTAKEN, GCFLAG_HASHFIELD
 from pypy.rpython.memory.gc.generation import GCFLAG_NO_YOUNG_PTRS
 from pypy.rpython.memory.gc.generation import GCFLAG_NO_HEAP_PTRS
 from pypy.rpython.lltypesystem import lltype, llmemory, llarena
@@ -222,6 +223,8 @@
     def realloc(self, ptr, newlength, fixedsize, itemsize, lengthofs, grow):
         size_gc_header = self.size_gc_header()
         addr = llmemory.cast_ptr_to_adr(ptr)
+        ll_assert(self.header(addr).tid & GCFLAG_EXTERNAL,
+                  "realloc() on a non-external object")
         nonvarsize = size_gc_header + fixedsize
         try:
             varsize = ovfcheck(itemsize * newlength)
@@ -401,15 +404,18 @@
             tid &= ~GCFLAG_AGE_MASK
         # skip GenerationGC.make_a_copy() as we already did the right
         # thing about GCFLAG_NO_YOUNG_PTRS
-        newobj = SemiSpaceGC.make_a_copy(self, obj, objsize)
-        self.header(newobj).tid = tid
-        return newobj
+        return self._make_a_copy_with_tid(obj, objsize, tid)
 
     def make_a_nonmoving_copy(self, obj, objsize):
         # NB. the object can have a finalizer or be a weakref, but
         # it's not an issue.
         totalsize = self.size_gc_header() + objsize
-        newaddr = self.allocate_external_object(totalsize)
+        tid = self.header(obj).tid
+        if tid & (GCFLAG_HASHTAKEN|GCFLAG_HASHFIELD):
+            totalsize_incl_hash = totalsize + llmemory.sizeof(lltype.Signed)
+        else:
+            totalsize_incl_hash = totalsize
+        newaddr = self.allocate_external_object(totalsize_incl_hash)
         if not newaddr:
             return llmemory.NULL   # can't raise MemoryError during a collect()
         if self.config.gcconfig.debugprint:
@@ -417,13 +423,22 @@
             self._nonmoving_copy_size += raw_malloc_usage(totalsize)
 
         llmemory.raw_memcopy(obj - self.size_gc_header(), newaddr, totalsize)
-        newobj = newaddr + self.size_gc_header()
-        hdr = self.header(newobj)
-        hdr.tid |= self.GCFLAGS_FOR_NEW_EXTERNAL_OBJECTS
+        # check if we need to write a hash value at the end of the new obj
+        if tid & (GCFLAG_HASHTAKEN|GCFLAG_HASHFIELD):
+            if tid & GCFLAG_HASHFIELD:
+                hash = (obj + objsize).signed[0]
+            else:
+                hash = llmemory.cast_adr_to_int(obj)
+                tid |= GCFLAG_HASHFIELD
+            (newaddr + totalsize).signed[0] = hash
+        #
         # GCFLAG_UNVISITED is not set
         # GCFLAG_NO_HEAP_PTRS is not set either, conservatively.  It may be
         # set by the next collection's collect_last_generation_roots().
         # This old object is immediately put at generation 3.
+        newobj = newaddr + self.size_gc_header()
+        hdr = self.header(newobj)
+        hdr.tid = tid | self.GCFLAGS_FOR_NEW_EXTERNAL_OBJECTS
         ll_assert(self.is_last_generation(newobj),
                   "make_a_nonmoving_copy: object too young")
         self.gen3_rawmalloced_objects.append(newobj)
@@ -503,13 +518,13 @@
             if tid & GCFLAG_UNVISITED:
                 if self.config.gcconfig.debugprint:
                     dead_count+=1
-                    dead_size+=raw_malloc_usage(self.get_size(obj))
+                    dead_size+=raw_malloc_usage(self.get_size_incl_hash(obj))
                 addr = obj - self.gcheaderbuilder.size_gc_header
                 llmemory.raw_free(addr)
             else:
                 if self.config.gcconfig.debugprint:
                     alive_count+=1
-                    alive_size+=raw_malloc_usage(self.get_size(obj))
+                    alive_size+=raw_malloc_usage(self.get_size_incl_hash(obj))
                 if generation == 3:
                     surviving_objects.append(obj)
                 elif generation == 2:
@@ -591,6 +606,8 @@
         tid = self.header(obj).tid
         ll_assert(bool(tid & GCFLAG_EXTERNAL),
                   "gen2: missing GCFLAG_EXTERNAL")
+        ll_assert(bool(tid & GCFLAG_HASHTAKEN),
+                  "gen2: missing GCFLAG_HASHTAKEN")
         ll_assert(bool(tid & GCFLAG_UNVISITED),
                   "gen2: missing GCFLAG_UNVISITED")
         ll_assert((tid & GCFLAG_AGE_MASK) < GCFLAG_AGE_MAX,
@@ -599,6 +616,8 @@
         tid = self.header(obj).tid
         ll_assert(bool(tid & GCFLAG_EXTERNAL),
                   "gen3: missing GCFLAG_EXTERNAL")
+        ll_assert(bool(tid & GCFLAG_HASHTAKEN),
+                  "gen3: missing GCFLAG_HASHTAKEN")
         ll_assert(not (tid & GCFLAG_UNVISITED),
                   "gen3: unexpected GCFLAG_UNVISITED")
         ll_assert((tid & GCFLAG_AGE_MASK) == GCFLAG_AGE_MAX,

Modified: pypy/trunk/pypy/rpython/memory/gc/markcompact.py
==============================================================================
--- pypy/trunk/pypy/rpython/memory/gc/markcompact.py	(original)
+++ pypy/trunk/pypy/rpython/memory/gc/markcompact.py	Fri Oct 16 15:11:23 2009
@@ -16,6 +16,8 @@
 
 first_gcflag = 1 << 16
 GCFLAG_MARKBIT = first_gcflag << 0
+GCFLAG_HASHTAKEN = first_gcflag << 1      # someone already asked for the hash
+GCFLAG_HASHFIELD = first_gcflag << 2      # we have an extra hash field
 
 memoryError = MemoryError()
 
@@ -71,6 +73,9 @@
 class MarkCompactGC(MovingGCBase):
     HDR = lltype.Struct('header', ('tid', lltype.Signed))
     typeid_is_in_field = 'tid'
+    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).
     TID_BACKUP = lltype.Array(TID_TYPE, hints={'nolength':True})
     WEAKREF_OFFSETS = lltype.Array(lltype.Signed)
 
@@ -80,11 +85,12 @@
     malloc_zero_filled = True
     inline_simple_malloc = True
     inline_simple_malloc_varsize = True
-    first_unused_gcflag = first_gcflag << 1
+    first_unused_gcflag = first_gcflag << 3
     total_collection_time = 0.0
     total_collection_count = 0
 
     def __init__(self, config, chunk_size=DEFAULT_CHUNK_SIZE, space_size=4096):
+        import py; py.test.skip("Disabled for now, sorry")
         MovingGCBase.__init__(self, config, chunk_size)
         self.space_size = space_size
         self.next_collect_after = space_size/2 # whatever...
@@ -107,6 +113,7 @@
 
     def init_gc_object_immortal(self, addr, typeid16, flags=0):
         hdr = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(self.HDR))
+        flags |= GCFLAG_HASHTAKEN
         hdr.tid = self.combine(typeid16, flags)
         # XXX we can store forward_ptr to itself, if we fix C backend
         # so that get_forwarding_address(obj) returns
@@ -176,19 +183,28 @@
 
     def compute_alive_objects(self):
         fromaddr = self.space
-        totalsize = 0
+        addraftercollect = self.space
         num = 1
         while fromaddr < self.free:
             size_gc_header = self.gcheaderbuilder.size_gc_header
-            hdr = llmemory.cast_adr_to_ptr(fromaddr, lltype.Ptr(self.HDR))
+            tid = llmemory.cast_adr_to_ptr(fromaddr, lltype.Ptr(self.HDR)).tid
             obj = fromaddr + size_gc_header
             objsize = self.get_size(obj)
             objtotalsize = size_gc_header + objsize
             if self.marked(obj):
-                totalsize += raw_malloc_usage(objtotalsize)
+                copy_has_hash_field = ((tid & GCFLAG_HASHFIELD) != 0 or
+                                       ((tid & GCFLAG_HASHTAKEN) != 0 and
+                                        addraftercollect < fromaddr))
+                addraftercollect += raw_malloc_usage(objtotalsize)
+                if copy_has_hash_field:
+                    addraftercollect += llmemory.sizeof(lltype.Signed)
             num += 1
             fromaddr += objtotalsize
-        self.totalsize_of_objs = totalsize
+            if tid & GCFLAG_HASHFIELD:
+                fromaddr += llmemory.sizeof(lltype.Signed)
+        ll_assert(addraftercollect <= fromaddr,
+                  "markcompactcollect() is trying to increase memory usage")
+        self.totalsize_of_objs = addraftercollect - self.space
         return num
 
     def collect(self, gen=0):
@@ -346,6 +362,8 @@
         self._trace_and_mark()
 
     def _trace_and_mark(self):
+        # XXX depth-first tracing... it can consume a lot of rawmalloced
+        # memory for very long stacks in some cases
         while self.to_see.non_empty():
             obj = self.to_see.pop()
             self.trace(obj, self._mark_obj, None)
@@ -592,3 +610,30 @@
         self.objects_with_weakrefs.delete()
         self.objects_with_weakrefs = new_with_weakref
         lltype.free(weakref_offsets, flavor='raw')
+
+    def get_size_incl_hash(self, obj):
+        size = self.get_size(obj)
+        hdr = self.header(obj)
+        if hdr.tid & GCFLAG_HASHFIELD:
+            size += llmemory.sizeof(lltype.Signed)
+        return size
+
+    def identityhash(self, gcobj):
+        # Unlike SemiSpaceGC.identityhash(), this function does not have
+        # to care about reducing top_of_space.  The reason is as
+        # follows.  When we collect, each object either moves to the
+        # left or stays where it is.  If it moves to the left (and if it
+        # has GCFLAG_HASHTAKEN), we can give it a hash field, and the
+        # end of the new object cannot move to the right of the end of
+        # the old object.  If it stays where it is, then we don't need
+        # to add the hash field.  So collecting can never actually grow
+        # the consumed size.
+        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]
+        #
+        hdr.tid |= GCFLAG_HASHTAKEN
+        return llmemory.cast_adr_to_int(obj)  # direct case

Modified: pypy/trunk/pypy/rpython/memory/gc/marksweep.py
==============================================================================
--- pypy/trunk/pypy/rpython/memory/gc/marksweep.py	(original)
+++ pypy/trunk/pypy/rpython/memory/gc/marksweep.py	Fri Oct 16 15:11:23 2009
@@ -19,6 +19,9 @@
                                        ('pool',        X_POOL_PTR))
 X_CLONE_PTR = lltype.Ptr(X_CLONE)
 
+FL_WITHHASH = 0x01
+FL_CURPOOL  = 0x02
+
 memoryError = MemoryError()
 class MarkSweepGC(GCBase):
     HDR = lltype.ForwardReference()
@@ -27,9 +30,10 @@
     # systems allocator and can't walk the heap
     HDR.become(lltype.Struct('header', ('typeid16', rffi.USHORT),
                                        ('mark', lltype.Bool),
-                                       ('curpool_flag', lltype.Bool),
+                                       ('flags', lltype.Char),
                                        ('next', HDRPTR)))
     typeid_is_in_field = 'typeid16'
+    withhash_flag_is_in_field = 'flags', FL_WITHHASH
 
     POOL = lltype.GcStruct('gc_pool')
     POOLPTR = lltype.Ptr(POOL)
@@ -102,7 +106,7 @@
         hdr = llmemory.cast_adr_to_ptr(result, self.HDRPTR)
         hdr.typeid16 = typeid16
         hdr.mark = False
-        hdr.curpool_flag = False
+        hdr.flags = '\x00'
         if has_finalizer:
             hdr.next = self.malloced_objects_with_finalizer
             self.malloced_objects_with_finalizer = hdr
@@ -139,7 +143,7 @@
         hdr = llmemory.cast_adr_to_ptr(result, self.HDRPTR)
         hdr.typeid16 = typeid16
         hdr.mark = False
-        hdr.curpool_flag = False
+        hdr.flags = '\x00'
         if has_finalizer:
             hdr.next = self.malloced_objects_with_finalizer
             self.malloced_objects_with_finalizer = hdr
@@ -178,7 +182,7 @@
         hdr = llmemory.cast_adr_to_ptr(result, self.HDRPTR)
         hdr.typeid16 = typeid16
         hdr.mark = False
-        hdr.curpool_flag = False
+        hdr.flags = '\x00'
         hdr.next = self.malloced_objects
         self.malloced_objects = hdr
         self.bytes_malloced = bytes_malloced
@@ -213,7 +217,7 @@
         hdr = llmemory.cast_adr_to_ptr(result, self.HDRPTR)
         hdr.typeid16 = typeid16
         hdr.mark = False
-        hdr.curpool_flag = False
+        hdr.flags = '\x00'
         hdr.next = self.malloced_objects
         self.malloced_objects = hdr
         self.bytes_malloced = bytes_malloced
@@ -516,7 +520,7 @@
         hdr = llmemory.cast_adr_to_ptr(addr, self.HDRPTR)
         hdr.typeid16 = typeid
         hdr.mark = False
-        hdr.curpool_flag = False
+        hdr.flags = '\x00'
 
     def init_gc_object_immortal(self, addr, typeid, flags=0):
         # prebuilt gc structures always have the mark bit set
@@ -524,7 +528,7 @@
         hdr = llmemory.cast_adr_to_ptr(addr, self.HDRPTR)
         hdr.typeid16 = typeid
         hdr.mark = True
-        hdr.curpool_flag = False
+        hdr.flags = '\x00'
 
     # experimental support for thread cloning
     def x_swap_pool(self, newpool):
@@ -596,7 +600,8 @@
         hdr = hdr.next   # skip the POOL object itself
         while hdr:
             next = hdr.next
-            hdr.curpool_flag = True   # mark all objects from malloced_list
+            # mark all objects from malloced_list
+            hdr.flags = chr(ord(hdr.flags) | FL_CURPOOL)
             hdr.next = lltype.nullptr(self.HDR)  # abused to point to the copy
             oldobjects.append(llmemory.cast_ptr_to_adr(hdr))
             hdr = next
@@ -613,7 +618,7 @@
                 continue   # pointer is NULL
             oldhdr = llmemory.cast_adr_to_ptr(oldobj_addr - size_gc_header,
                                               self.HDRPTR)
-            if not oldhdr.curpool_flag:
+            if not (ord(oldhdr.flags) & FL_CURPOOL):
                 continue   # ignore objects that were not in the malloced_list
             newhdr = oldhdr.next      # abused to point to the copy
             if not newhdr:
@@ -645,13 +650,13 @@
 
                 saved_id   = newhdr.typeid16  # XXX hack needed for genc
                 saved_flg1 = newhdr.mark
-                saved_flg2 = newhdr.curpool_flag
+                saved_flg2 = newhdr.flags
                 saved_next = newhdr.next      # where size_gc_header == 0
                 raw_memcopy(oldobj_addr, newobj_addr, size)
-                newhdr.typeid16     = saved_id
-                newhdr.mark         = saved_flg1
-                newhdr.curpool_flag = saved_flg2
-                newhdr.next         = saved_next
+                newhdr.typeid16 = saved_id
+                newhdr.mark     = saved_flg1
+                newhdr.flags    = saved_flg2
+                newhdr.next     = saved_next
 
                 offsets = self.offsets_to_gc_pointers(typeid)
                 i = 0
@@ -685,7 +690,7 @@
         next = lltype.nullptr(self.HDR)
         while oldobjects.non_empty():
             hdr = llmemory.cast_adr_to_ptr(oldobjects.pop(), self.HDRPTR)
-            hdr.curpool_flag = False   # reset the flag
+            hdr.flags = chr(ord(hdr.flags) &~ FL_CURPOOL)  # reset the flag
             hdr.next = next
             next = hdr
         oldobjects.delete()
@@ -700,6 +705,15 @@
         # reinstall the pool that was current at the beginning of x_clone()
         clonedata.pool = self.x_swap_pool(curpool)
 
+    def identityhash(self, obj):
+        obj = llmemory.cast_ptr_to_adr(obj)
+        hdr = self.header(obj)
+        if ord(hdr.flags) & FL_WITHHASH:
+            obj += self.get_size(obj)
+            return obj.signed[0]
+        else:
+            return llmemory.cast_adr_to_int(obj)
+
 
 class PrintingMarkSweepGC(MarkSweepGC):
     _alloc_flavor_ = "raw"

Modified: pypy/trunk/pypy/rpython/memory/gc/semispace.py
==============================================================================
--- pypy/trunk/pypy/rpython/memory/gc/semispace.py	(original)
+++ pypy/trunk/pypy/rpython/memory/gc/semispace.py	Fri Oct 16 15:11:23 2009
@@ -19,6 +19,8 @@
 # either immortal objects or (for HybridGC) externally raw_malloc'ed
 GCFLAG_EXTERNAL = first_gcflag << 1
 GCFLAG_FINALIZATION_ORDERING = first_gcflag << 2
+GCFLAG_HASHTAKEN = first_gcflag << 3      # someone already asked for the hash
+GCFLAG_HASHFIELD = first_gcflag << 4      # we have an extra hash field
 
 memoryError = MemoryError()
 
@@ -28,12 +30,15 @@
     inline_simple_malloc = True
     inline_simple_malloc_varsize = True
     malloc_zero_filled = True
-    first_unused_gcflag = first_gcflag << 3
+    first_unused_gcflag = first_gcflag << 5
     total_collection_time = 0.0
     total_collection_count = 0
 
     HDR = lltype.Struct('header', ('tid', lltype.Signed))   # XXX or rffi.INT?
     typeid_is_in_field = 'tid'
+    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)
@@ -297,11 +302,18 @@
             if free_after_collection < self.space_size // 5:
                 self.red_zone += 1
 
+    def get_size_incl_hash(self, obj):
+        size = self.get_size(obj)
+        hdr = self.header(obj)
+        if hdr.tid & GCFLAG_HASHFIELD:
+            size += llmemory.sizeof(lltype.Signed)
+        return size
+
     def scan_copied(self, scan):
         while scan < self.free:
             curr = scan + self.size_gc_header()
             self.trace_and_copy(curr)
-            scan += self.size_gc_header() + self.get_size(curr)
+            scan += self.size_gc_header() + self.get_size_incl_hash(curr)
         return scan
 
     def collect_roots(self):
@@ -328,15 +340,32 @@
             self.set_forwarding_address(obj, newobj, objsize)
             return newobj
 
-    def make_a_copy(self, obj, objsize):
+    def _make_a_copy_with_tid(self, obj, objsize, tid):
         totalsize = self.size_gc_header() + objsize
         newaddr = self.free
-        self.free += totalsize
         llarena.arena_reserve(newaddr, totalsize)
         raw_memcopy(obj - self.size_gc_header(), newaddr, totalsize)
+        #
+        # check if we need to write a hash value at the end of the new obj
+        if tid & (GCFLAG_HASHTAKEN|GCFLAG_HASHFIELD):
+            if tid & GCFLAG_HASHFIELD:
+                hash = (obj + objsize).signed[0]
+            else:
+                hash = llmemory.cast_adr_to_int(obj)
+                tid |= GCFLAG_HASHFIELD
+            (newaddr + totalsize).signed[0] = hash
+            totalsize += llmemory.sizeof(lltype.Signed)
+        #
+        self.free += totalsize
+        newhdr = llmemory.cast_adr_to_ptr(newaddr, lltype.Ptr(self.HDR))
+        newhdr.tid = tid
         newobj = newaddr + self.size_gc_header()
         return newobj
 
+    def make_a_copy(self, obj, objsize):
+        tid = self.header(obj).tid
+        return self._make_a_copy_with_tid(obj, objsize, tid)
+
     def trace_and_copy(self, obj):
         self.trace(obj, self._trace_copy, None)
 
@@ -407,7 +436,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().
@@ -570,3 +599,33 @@
 
     STATISTICS_NUMBERS = 0
 
+    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/trunk/pypy/rpython/memory/gctransform/boehm.py
==============================================================================
--- pypy/trunk/pypy/rpython/memory/gctransform/boehm.py	(original)
+++ pypy/trunk/pypy/rpython/memory/gctransform/boehm.py	Fri Oct 16 15:11:23 2009
@@ -10,6 +10,7 @@
 class BoehmGCTransformer(GCTransformer):
     malloc_zero_filled = True
     FINALIZER_PTR = lltype.Ptr(lltype.FuncType([llmemory.Address], lltype.Void))
+    HDR = lltype.Struct("header", ("hash", lltype.Signed))
 
     def __init__(self, translator, inline=False):
         super(BoehmGCTransformer, self).__init__(translator, inline=inline)
@@ -34,6 +35,15 @@
 
         ll_realloc = mh.ll_realloc
 
+        HDRPTR = lltype.Ptr(self.HDR)
+
+        def ll_identityhash(addr):
+            obj = llmemory.cast_adr_to_ptr(addr, HDRPTR)
+            h = obj.hash
+            if h == 0:
+                obj.hash = h = ~llmemory.cast_adr_to_int(addr)
+            return h
+
         if self.translator:
             self.malloc_fixedsize_ptr = self.inittime_helper(
                 ll_malloc_fixedsize, [lltype.Signed], llmemory.Address)
@@ -51,6 +61,9 @@
             self.realloc_ptr = self.inittime_helper(
                 ll_realloc, [llmemory.Address] + [lltype.Signed] * 4,
                 llmemory.Address)
+            self.identityhash_ptr = self.inittime_helper(
+                ll_identityhash, [llmemory.Address], lltype.Signed,
+                inline=False)
             self.mixlevelannotator.finish()   # for now
             self.mixlevelannotator.backend_optimize()
 
@@ -154,6 +167,13 @@
                            resulttype=llmemory.Address)
         hop.cast_result(v_addr)
 
+    def gct_gc_identityhash(self, hop):
+        v_obj = hop.spaceop.args[0]
+        v_adr = hop.genop("cast_ptr_to_adr", [v_obj],
+                          resulttype=llmemory.Address)
+        hop.genop("direct_call", [self.identityhash_ptr, v_adr],
+                  resultvar=hop.spaceop.result)
+
     def gct_gc_id(self, hop):
         # this is the logic from the HIDE_POINTER macro in <gc/gc.h>
         v_int = hop.genop('cast_ptr_to_int', [hop.spaceop.args[0]],

Modified: pypy/trunk/pypy/rpython/memory/gctransform/framework.py
==============================================================================
--- pypy/trunk/pypy/rpython/memory/gctransform/framework.py	(original)
+++ pypy/trunk/pypy/rpython/memory/gctransform/framework.py	Fri Oct 16 15:11:23 2009
@@ -337,6 +337,15 @@
                 [annmodel.SomeBool()],
                 s_gcref)
 
+        self.identityhash_ptr = getfn(GCClass.identityhash.im_func,
+                                      [s_gc, s_gcref],
+                                      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,
                                 [s_gc, s_gcref], annmodel.SomeInteger(),
@@ -432,10 +441,28 @@
     def gc_fields(self):
         return self._gc_fields
 
-    def gc_field_values_for(self, obj):
+    def gc_field_values_for(self, obj, needs_hash=False):
         hdr = self.gcdata.gc.gcheaderbuilder.header_of_object(obj)
         HDR = self._gc_HDR
-        return [getattr(hdr, fldname) for fldname in HDR._names]
+        withhash, flag = self.gcdata.gc.withhash_flag_is_in_field
+        result = []
+        for fldname in HDR._names:
+            x = getattr(hdr, fldname)
+            if fldname == withhash:
+                TYPE = lltype.typeOf(x)
+                x = lltype.cast_primitive(lltype.Signed, x)
+                if needs_hash:
+                    x |= flag       # set the flag in the header
+                else:
+                    x &= ~flag      # clear the flag in the header
+                x = lltype.cast_primitive(TYPE, x)
+            result.append(x)
+        return result
+
+    def get_hash_offset(self, T):
+        type_id = self.get_type_id(T)
+        assert not self.gcdata.q_is_varsize(type_id)
+        return self.gcdata.q_fixed_size(type_id)
 
     def finish_tables(self):
         group = self.layoutbuilder.close_table()
@@ -735,6 +762,16 @@
                            resulttype=llmemory.Address)
         hop.cast_result(v_addr)
 
+    def gct_gc_identityhash(self, hop):
+        livevars = self.push_roots(hop)
+        [v_ptr] = hop.spaceop.args
+        v_adr = hop.genop("cast_ptr_to_adr", [v_ptr],
+                          resulttype=llmemory.Address)
+        hop.genop("direct_call",
+                  [self.identityhash_ptr, self.c_const_gc, v_adr],
+                  resultvar=hop.spaceop.result)
+        self.pop_roots(hop, livevars)
+
     def gct_gc_id(self, hop):
         if self.id_ptr is not None:
             livevars = self.push_roots(hop)
@@ -747,6 +784,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/trunk/pypy/rpython/memory/gctransform/refcounting.py
==============================================================================
--- pypy/trunk/pypy/rpython/memory/gctransform/refcounting.py	(original)
+++ pypy/trunk/pypy/rpython/memory/gctransform/refcounting.py	Fri Oct 16 15:11:23 2009
@@ -34,7 +34,8 @@
 class RefcountingGCTransformer(GCTransformer):
     malloc_zero_filled = True
 
-    HDR = lltype.Struct("header", ("refcount", lltype.Signed))
+    HDR = lltype.Struct("header", ("refcount", lltype.Signed),
+                                  ("hash", lltype.Signed))
 
     def __init__(self, translator):
         super(RefcountingGCTransformer, self).__init__(translator, inline=True)
@@ -91,6 +92,13 @@
         mh.ll_malloc_varsize_no_length = ll_malloc_varsize_no_length
         ll_malloc_varsize = mh.ll_malloc_varsize
 
+        def ll_identityhash(addr):
+            obj = llmemory.cast_adr_to_ptr(addr, HDRPTR)
+            h = obj.hash
+            if h == 0:
+                obj.hash = h = llmemory.cast_adr_to_int(addr)
+            return h
+
         if self.translator:
             self.increfptr = self.inittime_helper(
                 ll_incref, [llmemory.Address], lltype.Void)
@@ -107,6 +115,9 @@
                 ll_malloc_varsize_no_length, [lltype.Signed]*3, llmemory.Address)
             self.malloc_varsize_ptr = self.inittime_helper(
                 ll_malloc_varsize, [lltype.Signed]*4, llmemory.Address)
+            self.identityhash_ptr = self.inittime_helper(
+                ll_identityhash, [llmemory.Address], lltype.Signed,
+                inline=False)
             self.mixlevelannotator.finish()
             self.mixlevelannotator.backend_optimize()
         # cache graphs:
@@ -183,6 +194,7 @@
             if not self.gcheaderbuilder.get_header(p):
                 hdr = self.gcheaderbuilder.new_header(p)
                 hdr.refcount = sys.maxint // 2
+                hdr.hash = lltype.identityhash_nocache(p)
 
     def static_deallocation_funcptr_for_type(self, TYPE):
         if TYPE in self.static_deallocator_funcptrs:
@@ -286,4 +298,9 @@
         self.queryptr2dynamic_deallocator_funcptr[queryptr._obj] = fptr
         return fptr
 
-
+    def gct_gc_identityhash(self, hop):
+        v_obj = hop.spaceop.args[0]
+        v_adr = hop.genop("cast_ptr_to_adr", [v_obj],
+                          resulttype=llmemory.Address)
+        hop.genop("direct_call", [self.identityhash_ptr, v_adr],
+                  resultvar=hop.spaceop.result)

Modified: pypy/trunk/pypy/rpython/memory/gctransform/transform.py
==============================================================================
--- pypy/trunk/pypy/rpython/memory/gctransform/transform.py	(original)
+++ pypy/trunk/pypy/rpython/memory/gctransform/transform.py	Fri Oct 16 15:11:23 2009
@@ -380,6 +380,10 @@
     def gct_zero_gc_pointers_inside(self, hop):
         pass
 
+    def gct_gc_identityhash(self, hop):
+        # must be implemented in the various GCs
+        raise NotImplementedError
+
     def gct_gc_id(self, hop):
         # this assumes a non-moving GC.  Moving GCs need to override this
         hop.rename('cast_ptr_to_int')

Modified: pypy/trunk/pypy/rpython/ootypesystem/ooregistry.py
==============================================================================
--- pypy/trunk/pypy/rpython/ootypesystem/ooregistry.py	(original)
+++ pypy/trunk/pypy/rpython/ootypesystem/ooregistry.py	Fri Oct 16 15:11:23 2009
@@ -81,21 +81,3 @@
         hop.has_implicit_exception(ValueError)
         hop.exception_is_here()
         return hop.genop('ooparse_float', vlist, resulttype = ootype.Float)
-
-
-class Entry_oohash(ExtRegistryEntry):
-    _about_ = ootype.oohash
-
-    def compute_result_annotation(self, str_s):
-        if not (isinstance(str_s, annmodel.SomeOOInstance)
-                and (str_s.ootype is ootype.String or
-                     str_s.ootype is ootype.Unicode)):
-            return annmodel.s_ImpossibleValue
-        return annmodel.SomeInteger()
-
-    def specialize_call(self, hop):
-        assert isinstance(hop.args_s[0], annmodel.SomeOOInstance)\
-               and (hop.args_s[0].ootype is ootype.String or
-                    hop.args_s[0].ootype is ootype.Unicode)
-        vlist = hop.inputargs(hop.args_r[0])
-        return hop.genop('oohash', vlist, resulttype=ootype.Signed)

Modified: pypy/trunk/pypy/rpython/ootypesystem/ootype.py
==============================================================================
--- pypy/trunk/pypy/rpython/ootypesystem/ootype.py	(original)
+++ pypy/trunk/pypy/rpython/ootypesystem/ootype.py	Fri Oct 16 15:11:23 2009
@@ -4,6 +4,7 @@
 from pypy.rpython.lltypesystem.lltype import Bool, Void, UniChar, typeOf, \
         Primitive, isCompatibleType, enforce, saferecursive, SignedLongLong, UnsignedLongLong
 from pypy.rpython.lltypesystem.lltype import frozendict, isCompatibleType
+from pypy.rpython.lltypesystem.lltype import identityhash
 from pypy.rlib.rarithmetic import intmask
 from pypy.rlib import objectmodel
 from pypy.tool.uid import uid
@@ -331,9 +332,14 @@
     # can treat them polymorphically, if they choose to do so.
 
     def __init__(self, fields, _hints={}):
+        if isinstance(fields, dict):
+            fields = fields.items()    # random order in that case
         self._fields = frozendict()
-        for name, ITEMTYPE in fields.items():
+        fields_in_order = []
+        for name, ITEMTYPE in fields:
             self._fields[name] = ITEMTYPE, ITEMTYPE._defl()
+            fields_in_order.append(name)
+        self._fields_in_order = tuple(fields_in_order)
         self._null = _null_record(self)
         self._hints = frozendict(_hints)
 
@@ -361,8 +367,8 @@
             return self, None
 
     def __str__(self):
-        item_str = ["%s: %s" % (str(name), str(ITEMTYPE))
-                    for name, (ITEMTYPE, _) in self._fields.items()]
+        item_str = ["%s: %s" % (str(name), str(self._fields[name][0]))
+                    for name in self._fields_in_order]
         return '%s(%s)' % (self.__class__.__name__, ", ".join(item_str))
 
 class BuiltinADTType(BuiltinType):
@@ -411,6 +417,7 @@
 
         generic_types = { self.SELFTYPE_T: self }
         self._GENERIC_METHODS = frozendict({
+            "ll_hash": Meth([], Signed),
             "ll_stritem_nonneg": Meth([Signed], self.CHAR),
             "ll_strlen": Meth([], Signed),
             "ll_strconcat": Meth([self.SELFTYPE_T], self.SELFTYPE_T),
@@ -881,13 +888,10 @@
         return hash(self.obj)
 
     def _identityhash(self):
-        if self:
-            try:
-                return self.obj._identityhash()
-            except AttributeError:
-                return intmask(id(self.obj))
-        else:
-            return 0 # for all null objects
+        try:
+            return self.obj._identityhash()
+        except AttributeError:
+            return hash(self.obj)
 
     def _cast_to_object(self):
         return self
@@ -986,10 +990,7 @@
         return self
 
     def _identityhash(self):
-        if self:
-            return intmask(id(self))
-        else:
-            return 0   # for all null instances
+        return hash(self)
 
     def _cast_to_object(self):
         return make_object(ooupcast(ROOT, self))
@@ -1386,6 +1387,12 @@
         else:
             assert False, 'Unknown type %s' % self._TYPE
 
+    def ll_hash(self):
+        # NOT_RPYTHON
+        # hopefully, ll_hash() should not be called on NULL
+        assert self._str is not None
+        return objectmodel._hash_string(self._str)
+
     def ll_stritem_nonneg(self, i):
         # NOT_RPYTHON
         s = self._str
@@ -1622,10 +1629,7 @@
         self._array[index] = item
 
     def _identityhash(self):
-        if self:
-            return intmask(id(self))
-        else:
-            return 0   # for all null arrays
+        return hash(self)
 
 class _null_array(_null_mixin(_array), _array):
 
@@ -1772,13 +1776,16 @@
             self.__dict__[name] = value
 
     def _identityhash(self):
-        if self:
-            return intmask(id(self))
-        else:
-            return 0 # for all null tuples
+        return hash(self)
+
+    def _items_in_order(self):
+        return [self._items[name] for name in self._TYPE._fields_in_order]
+
+    def _ll_hash(self):
+        return objectmodel._ll_hash_tuple(self._items_in_order())
 
     def __hash__(self):
-        key = tuple(self._items.keys()), tuple(self._items.values())
+        key = tuple(self._items_in_order())
         return hash(key)
 
     def __eq__(self, other):
@@ -1898,16 +1905,6 @@
     assert typeOf(obj) is Object
     return obj._cast_to(EXPECTED_TYPE)
 
-def ooidentityhash(inst):
-    T = typeOf(inst)
-    assert T is Object or isinstance(T, (Instance, Record, Array))
-    return inst._identityhash()
-
-def oohash(inst):
-    assert typeOf(inst) is String or typeOf(inst) is Unicode
-    # for now only strings and unicode are supported
-    return hash(inst._str)
-
 def oostring(obj, base):
     """
     Convert char, int, float, instances and str to str.

Modified: pypy/trunk/pypy/rpython/ootypesystem/rbuiltin.py
==============================================================================
--- pypy/trunk/pypy/rpython/ootypesystem/rbuiltin.py	(original)
+++ pypy/trunk/pypy/rpython/ootypesystem/rbuiltin.py	Fri Oct 16 15:11:23 2009
@@ -48,12 +48,6 @@
     return hop.genop('runtimenew', vlist,
                      resulttype = hop.r_result.lowleveltype)
 
-def rtype_ooidentityhash(hop):
-    assert isinstance(hop.args_s[0], (annmodel.SomeOOInstance, annmodel.SomeOOObject))
-    vlist = hop.inputargs(hop.args_r[0])
-    return hop.genop('ooidentityhash', vlist,
-                     resulttype = ootype.Signed)
-
 def rtype_ooupcast(hop):
     assert isinstance(hop.args_s[0].const, ootype.Instance)
     assert isinstance(hop.args_s[1], annmodel.SomeOOInstance)
@@ -132,7 +126,6 @@
 BUILTIN_TYPER[ootype.subclassof] = rtype_subclassof
 BUILTIN_TYPER[ootype.instanceof] = rtype_instanceof
 BUILTIN_TYPER[ootype.runtimenew] = rtype_runtimenew
-BUILTIN_TYPER[ootype.ooidentityhash] = rtype_ooidentityhash
 BUILTIN_TYPER[ootype.ooupcast] = rtype_ooupcast
 BUILTIN_TYPER[ootype.oodowncast] = rtype_oodowncast
 BUILTIN_TYPER[ootype.cast_from_object] = rtype_cast_from_object

Modified: pypy/trunk/pypy/rpython/ootypesystem/rclass.py
==============================================================================
--- pypy/trunk/pypy/rpython/ootypesystem/rclass.py	(original)
+++ pypy/trunk/pypy/rpython/ootypesystem/rclass.py	Fri Oct 16 15:11:23 2009
@@ -152,7 +152,7 @@
     if config.translation.ootype.mangle:
         return 'o' + name
     else:
-        not_allowed = ('_hash_cache_', 'meta', 'class_')
+        not_allowed = ('meta', 'class_')
         assert name not in not_allowed, "%s is a reserved name" % name
         return name
 
@@ -253,13 +253,6 @@
                     allmethods[mangled] = meth_name, s_meth
                 # else: it's the __init__ of a builtin exception
 
-        #
-        # hash() support
-        if self.rtyper.needs_hash_support(self.classdef):
-            from pypy.rpython import rint
-            allfields['_hash_cache_'] = rint.signed_repr
-            fields['_hash_cache_'] = ootype.Signed
-
         ootype.addFields(self.lowleveltype, fields)
 
         self.rbase = getinstancerepr(self.rtyper, self.classdef.basedef)
@@ -413,9 +406,6 @@
                         graph=graph)
         ootype.addMethods(self.lowleveltype, {mangled: m})
 
-    def get_ll_hash_function(self):
-        return ll_inst_hash
-
     def rtype_getattr(self, hop):
         if hop.s_result.is_constant():
             return hop.inputconst(hop.r_result, hop.s_result.const)
@@ -526,8 +516,6 @@
                 llattrvalue = None
             elif mangled == 'meta':
                 llattrvalue = classrepr.get_meta_instance()
-            elif mangled == '_hash_cache_': # hash() support
-                continue   # already done by initialize_prebuilt_hash()
             else:
                 name = unmangle(mangled, self.rtyper.getconfig())
                 try:
@@ -544,8 +532,7 @@
             setattr(result, mangled, llattrvalue)
 
     def initialize_prebuilt_hash(self, value, result):
-        if '_hash_cache_' in self.lowleveltype._allfields():
-            result._hash_cache_ = hash(value)
+        pass
 
 
 class __extend__(pairtype(InstanceRepr, InstanceRepr)):
@@ -581,15 +568,6 @@
         v = rpair.rtype_eq(hop)
         return hop.genop("bool_not", [v], resulttype=ootype.Bool)
 
-
-def ll_inst_hash(ins):
-    if not ins:
-        return 0
-    cached = ins._hash_cache_
-    if cached == 0:
-        cached = ins._hash_cache_ = ootype.ooidentityhash(ins)
-    return cached
-
 def ll_inst_type(obj):
     if obj:
         return ootype.classof(obj)

Modified: pypy/trunk/pypy/rpython/ootypesystem/rstr.py
==============================================================================
--- pypy/trunk/pypy/rpython/ootypesystem/rstr.py	(original)
+++ pypy/trunk/pypy/rpython/ootypesystem/rstr.py	Fri Oct 16 15:11:23 2009
@@ -113,10 +113,10 @@
         return ootype.oounicode(ch, -1)
 
     def ll_strhash(s):
-        return ootype.oohash(s)
+        return s.ll_hash()
 
     def ll_strfasthash(s):
-        return ootype.oohash(s)
+        return s.ll_hash()
 
     def ll_char_mul(ch, times):
         if times < 0:

Modified: pypy/trunk/pypy/rpython/ootypesystem/rtupletype.py
==============================================================================
--- pypy/trunk/pypy/rpython/ootypesystem/rtupletype.py	(original)
+++ pypy/trunk/pypy/rpython/ootypesystem/rtupletype.py	Fri Oct 16 15:11:23 2009
@@ -12,4 +12,4 @@
         fields = [('item%d' % i, TYPE) for i, TYPE in enumerate(field_lltypes)]
         hints = {'immutable': True,
                  'noidentity': True}
-        return ootype.Record(dict(fields), _hints=hints)
+        return ootype.Record(fields, _hints=hints)

Modified: pypy/trunk/pypy/rpython/ootypesystem/test/test_ooclean.py
==============================================================================
--- pypy/trunk/pypy/rpython/ootypesystem/test/test_ooclean.py	(original)
+++ pypy/trunk/pypy/rpython/ootypesystem/test/test_ooclean.py	Fri Oct 16 15:11:23 2009
@@ -438,21 +438,22 @@
 
 def test_hash_preservation():
     from pypy.rlib.objectmodel import current_object_addr_as_int
+    from pypy.rlib.objectmodel import compute_identity_hash
     class C:
         pass
     class D(C):
         pass
     def f1():
         d2 = D()
-        # xxx we assume that the identityhash doesn't change from
-        #     one line to the next
+        # xxx we assume that current_object_addr_as_int is defined as
+        # simply returning the identity hash
         current_identityhash = current_object_addr_as_int(d2)
-        instance_hash = hash(d2)
-        return ((current_identityhash & sys.maxint) ==
-                (instance_hash & sys.maxint))
+        instance_hash = compute_identity_hash(d2)
+        return current_identityhash == instance_hash
     res = interpret(f1, [])
     assert res is True
 
+    py.test.skip("hash is not preserved during an ootype translation")
     c = C()
     d = D()
     def f2(): return hash(c)

Modified: pypy/trunk/pypy/rpython/ootypesystem/test/test_oorecord.py
==============================================================================
--- pypy/trunk/pypy/rpython/ootypesystem/test/test_oorecord.py	(original)
+++ pypy/trunk/pypy/rpython/ootypesystem/test/test_oorecord.py	Fri Oct 16 15:11:23 2009
@@ -42,12 +42,12 @@
     n2 = null(T)
     assert n == n2
 
-def test_ooidentityhash():
+def test_identityhash():
     T = Record({"a": Signed, "b": Signed})
     t = new(T)
     t.a = 1
     t.b = 2
     t2 = new(T)
-    t.a = 1
-    t.b = 2
-    assert ooidentityhash(t) != ooidentityhash(t2)
+    t2.a = 1
+    t2.b = 2
+    assert identityhash(t) != identityhash(t2)       # xxx???

Modified: pypy/trunk/pypy/rpython/ootypesystem/test/test_oortype.py
==============================================================================
--- pypy/trunk/pypy/rpython/ootypesystem/test/test_oortype.py	(original)
+++ pypy/trunk/pypy/rpython/ootypesystem/test/test_oortype.py	Fri Oct 16 15:11:23 2009
@@ -351,7 +351,7 @@
     res = interpret(fn, [], type_system='ootype')
     assert res == 42
 
-def test_ooidentityhash():
+def test_identityhash():
     L = List(Signed)
 
     def fn():
@@ -359,7 +359,7 @@
         lst2 = new(L)
         obj1 = cast_to_object(lst1)
         obj2 = cast_to_object(lst2)
-        return ooidentityhash(obj1) == ooidentityhash(obj2)
+        return identityhash(obj1) == identityhash(obj2)
 
     res = interpret(fn, [], type_system='ootype')
     assert not res

Modified: pypy/trunk/pypy/rpython/ootypesystem/test/test_ootype.py
==============================================================================
--- pypy/trunk/pypy/rpython/ootypesystem/test/test_ootype.py	(original)
+++ pypy/trunk/pypy/rpython/ootypesystem/test/test_ootype.py	Fri Oct 16 15:11:23 2009
@@ -622,27 +622,27 @@
     cls2 = cast_from_object(Class, obj)
     assert cls is cls2
     
-def test_object_ooidentityhash():
+def test_object_identityhash():
     A = Instance("Foo", ROOT)
     a = new(A)
     obj1 = cast_to_object(a)
     obj2 = cast_to_object(a)
-    assert ooidentityhash(obj1) == ooidentityhash(obj2)
+    assert identityhash(obj1) == identityhash(obj2)
 
-def test_object_ooidentityhash_sm():
+def test_object_identityhash_sm():
     M = StaticMethod([Signed], Signed)
     def m_(x):
        return x
     m = static_meth(M, "m", _callable=m_)
     obj1 = cast_to_object(m)
     obj2 = cast_to_object(m)
-    assert ooidentityhash(obj1) == ooidentityhash(obj2)
+    assert identityhash(obj1) == identityhash(obj2)
 
-def test_ooidentityhash_array():
+def test_identityhash_array():
     A = Array(Signed)
     a = oonewarray(A, 10)
     b = oonewarray(A, 10)
-    assert ooidentityhash(a) != ooidentityhash(b)
+    assert identityhash(a) != identityhash(b)     # likely
 
 def test_bool_class():
     A = Instance("Foo", ROOT)

Modified: pypy/trunk/pypy/rpython/rbuiltin.py
==============================================================================
--- pypy/trunk/pypy/rpython/rbuiltin.py	(original)
+++ pypy/trunk/pypy/rpython/rbuiltin.py	Fri Oct 16 15:11:23 2009
@@ -494,6 +494,10 @@
     return hop.genop('cast_int_to_ptr', [v_input],
                      resulttype = hop.r_result.lowleveltype)
 
+def rtype_identity_hash(hop):
+    vlist = hop.inputargs(hop.args_r[0])
+    return hop.genop('gc_identityhash', vlist, resulttype=lltype.Signed)
+
 def rtype_runtime_type_info(hop):
     assert isinstance(hop.args_r[0], rptr.PtrRepr)
     vlist = hop.inputargs(hop.args_r[0])
@@ -512,6 +516,7 @@
 BUILTIN_TYPER[lltype.cast_int_to_ptr] = rtype_cast_int_to_ptr
 BUILTIN_TYPER[lltype.typeOf] = rtype_const_result
 BUILTIN_TYPER[lltype.nullptr] = rtype_const_result
+BUILTIN_TYPER[lltype.identityhash] = rtype_identity_hash
 BUILTIN_TYPER[lltype.getRuntimeTypeInfo] = rtype_const_result
 BUILTIN_TYPER[lltype.Ptr] = rtype_const_result
 BUILTIN_TYPER[lltype.runtime_type_info] = rtype_runtime_type_info

Modified: pypy/trunk/pypy/rpython/rclass.py
==============================================================================
--- pypy/trunk/pypy/rpython/rclass.py	(original)
+++ pypy/trunk/pypy/rpython/rclass.py	Fri Oct 16 15:11:23 2009
@@ -236,15 +236,18 @@
             self.setup()
             result = self.create_instance()
             self._reusable_prebuilt_instance = result
-            self.initialize_prebuilt_instance(Ellipsis, self.classdef, result)
+            self.initialize_prebuilt_data(Ellipsis, self.classdef, result)
             return result
 
     def initialize_prebuilt_instance(self, value, classdef, result):
-        # must fill in the _hash_cache_ field before the other ones
+        # must fill in the hash cache before the other ones
         # (see test_circular_hash_initialization)
         self.initialize_prebuilt_hash(value, result)
         self.initialize_prebuilt_data(value, classdef, result)
 
+    def get_ll_hash_function(self):
+        return ll_inst_hash
+
     def rtype_type(self, hop):
         raise NotImplementedError
 
@@ -272,6 +275,13 @@
     rinstance = getinstancerepr(rtyper, classdef)
     return rinstance.new_instance(llops, classcallhop)
 
+def ll_inst_hash(ins):
+    if not ins:
+        return 0    # for None
+    else:
+        from pypy.rpython.lltypesystem import lltype
+        return lltype.identityhash(ins)     # also works for ootype
+
 
 _missing = object()
 

Modified: pypy/trunk/pypy/rpython/rdict.py
==============================================================================
--- pypy/trunk/pypy/rpython/rdict.py	(original)
+++ pypy/trunk/pypy/rpython/rdict.py	Fri Oct 16 15:11:23 2009
@@ -46,14 +46,8 @@
         else:
             return self._externalvsinternal(self.rtyper, item_repr)
 
-    def pickkeyrepr(self, key_repr):
-        external, internal = self.pickrepr(key_repr)
-        if external != internal:
-            internal = external
-            while not self.rtyper.needs_hash_support(internal.classdef):
-                internal = internal.rbase
-        return external, internal
-        
+    pickkeyrepr = pickrepr
+
     def compact_repr(self):
         return 'DictR %s %s' % (self.key_repr.compact_repr(), self.value_repr.compact_repr())
 

Modified: pypy/trunk/pypy/rpython/rfloat.py
==============================================================================
--- pypy/trunk/pypy/rpython/rfloat.py	(original)
+++ pypy/trunk/pypy/rpython/rfloat.py	Fri Oct 16 15:11:23 2009
@@ -10,6 +10,7 @@
 from pypy.rpython.rmodel import log
 
 from pypy.rlib.rarithmetic import base_int
+from pypy.rlib.objectmodel import _hash_float
 
 import math
 
@@ -109,7 +110,7 @@
     get_ll_le_function = get_ll_eq_function
 
     def get_ll_hash_function(self):
-        return ll_hash_float
+        return _hash_float
 
     def rtype_is_true(_, hop):
         vlist = hop.inputargs(Float)
@@ -142,23 +143,6 @@
         pass
     ll_str._annspecialcase_ = "specialize:ts('ll_str.ll_float_str')"
 
-
-TAKE_NEXT = float(2**31)
-
-def ll_hash_float(f):
-    """
-    this implementation is identical to the CPython implementation,
-    despite the fact that the integer case is not treated, specially.
-    This should be special-cased in W_FloatObject.
-    In the low-level case, floats cannot be used with ints in dicts, anyway.
-    """
-    from pypy.rlib.rarithmetic import intmask
-    v, expo = math.frexp(f)
-    v *= TAKE_NEXT
-    hipart = int(v)
-    v = (v - float(hipart)) * TAKE_NEXT
-    x = hipart + int(v) + (expo << 15)
-    return intmask(x)
 #
 # _________________________ Conversions _________________________
 

Modified: pypy/trunk/pypy/rpython/rtuple.py
==============================================================================
--- pypy/trunk/pypy/rpython/rtuple.py	(original)
+++ pypy/trunk/pypy/rpython/rtuple.py	Fri Oct 16 15:11:23 2009
@@ -100,14 +100,14 @@
         autounrolling_funclist = unrolling_iterable(enumerate(hash_funcs))
 
         def ll_hash(t):
-            retval = 0x345678
-            mult = 1000003
+            """Must be kept in sync with rlib.objectmodel._hash_tuple()."""
+            x = 0x345678
             for i, hash_func in autounrolling_funclist:
                 attrname = 'item%d' % i
                 item = getattr(t, attrname)
-                retval = intmask((retval ^ hash_func(item)) * intmask(mult))
-                mult = mult + 82520 + 2*len(items_r)
-            return retval
+                y = hash_func(item)
+                x = intmask((1000003 * x) ^ y)
+            return x
 
         _gen_hash_function_cache[key] = ll_hash
         return ll_hash

Modified: pypy/trunk/pypy/rpython/rtyper.py
==============================================================================
--- pypy/trunk/pypy/rpython/rtyper.py	(original)
+++ pypy/trunk/pypy/rpython/rtyper.py	Fri Oct 16 15:11:23 2009
@@ -651,9 +651,6 @@
 
     # __________ utilities __________
 
-    def needs_hash_support(self, clsdef):
-        return clsdef in self.annotator.bookkeeper.needs_hash_support
-
     def needs_wrapper(self, cls):
         return cls in self.classes_with_wrapper
 

Modified: pypy/trunk/pypy/rpython/test/test_rclass.py
==============================================================================
--- pypy/trunk/pypy/rpython/test/test_rclass.py	(original)
+++ pypy/trunk/pypy/rpython/test/test_rclass.py	Fri Oct 16 15:11:23 2009
@@ -425,27 +425,32 @@
 
     def test_hash_preservation(self):
         from pypy.rlib.objectmodel import current_object_addr_as_int
+        from pypy.rlib.objectmodel import compute_identity_hash
         class C:
             pass
         class D(C):
             pass
         c = C()
         d = D()
+        h_c = compute_identity_hash(c)
+        h_d = compute_identity_hash(d)
+        #
         def f():
             d2 = D()
-            return hash(d2), current_object_addr_as_int(d2), hash(c), hash(d)
+            return (compute_identity_hash(d2),
+                    current_object_addr_as_int(d2),
+                    compute_identity_hash(c),
+                    compute_identity_hash(d))
 
         res = self.interpret(f, [])
         # xxx this is too precise, checking the exact implementation
-        if isinstance(self, OORtypeMixin):
-            assert res.item0 == res.item1
-        else:
-            assert res.item0 == ~res.item1
+        assert res.item0 == res.item1
         # the following property is essential on top of the lltypesystem
-        # otherwise prebuilt dictionaries are broken.  It's not that
-        # relevant on top of the ootypesystem though.
-        assert res.item2 == hash(c)
-        assert res.item3 == hash(d)
+        # otherwise prebuilt dictionaries are broken.  It's wrong on
+        # top of the ootypesystem though.
+        if type(self) is TestLLtype:
+            assert res.item2 == h_c
+            assert res.item3 == h_d
 
     def test_circular_hash_initialization(self):
         class B:
@@ -681,6 +686,7 @@
         assert self.interpret(fn, []) == 3 + 8 + 9
 
     def test_hash_of_none(self):
+        from pypy.rlib.objectmodel import compute_hash
         class A:
             pass
         def fn(x):
@@ -688,14 +694,15 @@
                 obj = A()
             else:
                 obj = None
-            return hash(obj)
+            return compute_hash(obj)
         res = self.interpret(fn, [0])
         assert res == 0
 
     def test_hash_of_only_none(self):
+        from pypy.rlib.objectmodel import compute_hash
         def fn():
             obj = None
-            return hash(obj)
+            return compute_hash(obj)
         res = self.interpret(fn, [])
         assert res == 0
 
@@ -754,7 +761,7 @@
                f_summary == {"oosetfield": 2} # for ootype
 
 
-class TestLltype(BaseTestRclass, LLRtypeMixin):
+class TestLLtype(BaseTestRclass, LLRtypeMixin):
 
     def test__del__(self):
         class A(object):
@@ -843,15 +850,16 @@
         from pypy.annotation import model as annmodel
         from pypy.rpython import extregistry
         from pypy.rpython.annlowlevel import cast_object_to_ptr
-        class X(object): pass
-        class Y(X): pass
-        class Z(Y): pass
+        from pypy.rlib.objectmodel import compute_identity_hash
+
+        class Z(object):
+            pass
 
         def my_gethash(z):
             not_implemented
 
         def ll_my_gethash(ptr):
-            return ptr.gethash()
+            return identityhash(ptr)    # from lltype
 
         class MyGetHashEntry(extregistry.ExtRegistryEntry):
             _about_ = my_gethash
@@ -862,13 +870,9 @@
                 return hop.gendirectcall(ll_my_gethash, v_instance)
 
         def f(n):
-            if n > 10:
-                z = Y()
-                got = -1    # path never used
-            else:
-                z = Z()
-                got = my_gethash(z)
-            expected = hash(z)     # put the _hash_cache_ in the class Y
+            z = Z()
+            got = my_gethash(z)
+            expected = compute_identity_hash(z)
             return got - expected
 
         res = self.interpret(f, [5])

Modified: pypy/trunk/pypy/rpython/test/test_rfloat.py
==============================================================================
--- pypy/trunk/pypy/rpython/test/test_rfloat.py	(original)
+++ pypy/trunk/pypy/rpython/test/test_rfloat.py	Fri Oct 16 15:11:23 2009
@@ -4,6 +4,7 @@
 from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
 from pypy.rlib.rarithmetic import r_int, r_uint, r_longlong, r_singlefloat,\
      isnan, isinf
+from pypy.rlib.objectmodel import compute_hash
 
 class TestSnippet(object):
 
@@ -145,9 +146,9 @@
 
     def test_hash(self):
         def fn(f):
-            return hash(f)
+            return compute_hash(f)
         res = self.interpret(fn, [1.5])
-        assert res == hash(1.5)
+        assert res == compute_hash(1.5)
 
 
 class TestOOtype(BaseTestRfloat, OORtypeMixin):

Modified: pypy/trunk/pypy/rpython/test/test_rstr.py
==============================================================================
--- pypy/trunk/pypy/rpython/test/test_rstr.py	(original)
+++ pypy/trunk/pypy/rpython/test/test_rstr.py	Fri Oct 16 15:11:23 2009
@@ -693,14 +693,17 @@
         res = self.interpret(f, [const('a'), 0])
         assert self.ll_to_string(res) == ""
 
+    EMPTY_STRING_HASH = -1     # unless overridden
+
     def test_hash(self):
+        from pypy.rlib.objectmodel import compute_hash
         const = self.const
         def fn(i):
             if i == 0:
                 s = const('')
             else:
                 s = const("xxx")
-            return hash(s)
+            return compute_hash(s)
         res = self.interpret(fn, [0])
         assert res == self.EMPTY_STRING_HASH
         res = self.interpret(fn, [1])
@@ -883,8 +886,6 @@
 
 class TestLLtype(BaseTestRstr, LLRtypeMixin):
 
-    EMPTY_STRING_HASH = -1
-
     def test_ll_find_rfind(self):
         llstr = self.string_to_ll
         
@@ -899,18 +900,18 @@
             assert res == s1.rfind(s2)
 
     def test_hash_via_type(self):
+        from pypy.rlib.objectmodel import compute_hash
+
         def f(n):
             s = malloc(STR, n)
             s.hash = 0
             for i in range(n):
                 s.chars[i] = chr(i)
-            return s.gethash() - hash('\x00\x01\x02\x03\x04')
+            return s.gethash() - compute_hash('\x00\x01\x02\x03\x04')
 
         res = self.interpret(f, [5])
         assert res == 0
 
 
 class TestOOtype(BaseTestRstr, OORtypeMixin):
-
-    EMPTY_STRING_HASH = 0
-        
+    pass

Modified: pypy/trunk/pypy/rpython/test/test_rtuple.py
==============================================================================
--- pypy/trunk/pypy/rpython/test/test_rtuple.py	(original)
+++ pypy/trunk/pypy/rpython/test/test_rtuple.py	Fri Oct 16 15:11:23 2009
@@ -172,8 +172,9 @@
         assert r_AB_tup.lowleveltype == r_BA_tup.lowleveltype
 
     def test_tuple_hash(self):
+        from pypy.rlib.objectmodel import compute_hash
         def f(i, j):
-            return hash((i, j))
+            return compute_hash((i, j))
 
         res1 = self.interpret(f, [12, 27])
         res2 = self.interpret(f, [27, 12])
@@ -333,9 +334,10 @@
             yield test_le, a,b,c,d, resu
             yield test_ge, a,b,c,d, resu
 
-    def test_tuple_hash(self):
+    def test_tuple_hash_2(self):
+        from pypy.rlib.objectmodel import compute_hash
         def f(n):
-            return hash((n, 6)) == hash((3, n*2))
+            return compute_hash((n, 6)) == compute_hash((3, n*2))
         res = self.interpret(f, [3])
         assert res is True
 

Modified: pypy/trunk/pypy/rpython/test/test_runicode.py
==============================================================================
--- pypy/trunk/pypy/rpython/test/test_runicode.py	(original)
+++ pypy/trunk/pypy/rpython/test/test_runicode.py	Fri Oct 16 15:11:23 2009
@@ -214,18 +214,19 @@
     test_hlstr = unsupported
 
 class TestLLtype(BaseTestRUnicode, LLRtypeMixin):
-    EMPTY_STRING_HASH = -1
 
     def test_hash_via_type(self):
+        from pypy.rlib.objectmodel import compute_hash
+
         def f(n):
             s = malloc(UNICODE, n)
             s.hash = 0
             for i in range(n):
                 s.chars[i] = unichr(ord('A') + i)
-            return s.gethash() - hash(u'ABCDE')
+            return s.gethash() - compute_hash(u'ABCDE')
 
         res = self.interpret(f, [5])
         assert res == 0
 
 class TestOOtype(BaseTestRUnicode, OORtypeMixin):
-    EMPTY_STRING_HASH = 0
+    pass

Modified: pypy/trunk/pypy/translator/c/gc.py
==============================================================================
--- pypy/trunk/pypy/translator/c/gc.py	(original)
+++ pypy/trunk/pypy/translator/c/gc.py	Fri Oct 16 15:11:23 2009
@@ -12,16 +12,24 @@
 
 class BasicGcPolicy(object):
     requires_stackless = False
+    stores_hash_at_the_end = False
 
     def __init__(self, db, thread_enabled=False):
         self.db = db
         self.thread_enabled = thread_enabled
 
     def common_gcheader_definition(self, defnode):
-        return []
+        if defnode.db.gctransformer is not None:
+            HDR = defnode.db.gctransformer.HDR
+            return [(name, HDR._flds[name]) for name in HDR._names]
+        else:
+            return []
 
     def common_gcheader_initdata(self, defnode):
-        return []
+        if defnode.db.gctransformer is not None:
+            raise NotImplementedError
+        else:
+            return []
 
     def struct_gcheader_definition(self, defnode):
         return self.common_gcheader_definition(defnode)
@@ -35,9 +43,6 @@
     def array_gcheader_initdata(self, defnode):
         return self.common_gcheader_initdata(defnode)
 
-    def struct_after_definition(self, defnode):
-        return []
-
     def compilation_info(self):
         if not self.db:
             return ExternalCompilationInfo()
@@ -50,6 +55,9 @@
             post_include_bits=['typedef void *GC_hidden_pointer;']
             )
 
+    def get_prebuilt_hash(self, obj):
+        return None
+
     def need_no_typeptr(self):
         return False
 
@@ -102,13 +110,6 @@
 class RefcountingGcPolicy(BasicGcPolicy):
     transformerclass = refcounting.RefcountingGCTransformer
 
-    def common_gcheader_definition(self, defnode):
-        if defnode.db.gctransformer is not None:
-            HDR = defnode.db.gctransformer.HDR
-            return [(name, HDR._flds[name]) for name in HDR._names]
-        else:
-            return []
-
     def common_gcheader_initdata(self, defnode):
         if defnode.db.gctransformer is not None:
             gct = defnode.db.gctransformer
@@ -193,6 +194,12 @@
 class BoehmGcPolicy(BasicGcPolicy):
     transformerclass = boehm.BoehmGCTransformer
 
+    def common_gcheader_initdata(self, defnode):
+        if defnode.db.gctransformer is not None:
+            return [lltype.identityhash_nocache(defnode.obj._as_ptr())]
+        else:
+            return []
+
     def array_setup(self, arraydefnode):
         pass
 
@@ -281,6 +288,7 @@
 
 class FrameworkGcPolicy(BasicGcPolicy):
     transformerclass = framework.FrameworkGCTransformer
+    stores_hash_at_the_end = True
 
     def struct_setup(self, structdefnode, rtti):
         if rtti is not None and hasattr(rtti._obj, 'destructor_funcptr'):
@@ -327,7 +335,20 @@
 
     def common_gcheader_initdata(self, defnode):
         o = top_container(defnode.obj)
-        return defnode.db.gctransformer.gc_field_values_for(o)
+        needs_hash = self.get_prebuilt_hash(o) is not None
+        return defnode.db.gctransformer.gc_field_values_for(o, needs_hash)
+
+    def get_prebuilt_hash(self, obj):
+        # for prebuilt objects that need to have their hash stored and
+        # restored.  Note that only structures that are StructNodes all
+        # the way have their hash stored (and not e.g. structs with var-
+        # sized arrays at the end).  'obj' must be the top_container.
+        TYPE = typeOf(obj)
+        if not isinstance(TYPE, lltype.GcStruct):
+            return None
+        if TYPE._is_varsize():
+            return None
+        return getattr(obj, '_hash_cache_', None)
 
     def need_no_typeptr(self):
         config = self.db.translator.config

Modified: pypy/trunk/pypy/translator/c/node.py
==============================================================================
--- pypy/trunk/pypy/translator/c/node.py	(original)
+++ pypy/trunk/pypy/translator/c/node.py	Fri Oct 16 15:11:23 2009
@@ -9,7 +9,7 @@
 from pypy.translator.c.support import USESLOTS # set to False if necessary while refactoring
 from pypy.translator.c.support import cdecl, forward_cdecl, somelettersfrom
 from pypy.translator.c.support import c_char_array_constant, barebonearray
-from pypy.translator.c.primitive import PrimitiveType
+from pypy.translator.c.primitive import PrimitiveType, name_signed
 from pypy.rlib.rarithmetic import isinf, isnan
 from pypy.translator.c import extfunc
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
@@ -149,8 +149,6 @@
         if is_empty:
             yield '\t' + 'char _dummy; /* this struct is empty */'
         yield '};'
-        for line in self.db.gcpolicy.struct_after_definition(self):
-            yield line
 
     def visitor_lines(self, prefix, on_field):
         for name in self.fieldnames:
@@ -547,6 +545,49 @@
 
 assert not USESLOTS or '__dict__' not in dir(StructNode)
 
+class GcStructNodeWithHash(StructNode):
+    # for the outermost level of nested structures, if it has a _hash_cache_.
+    nodekind = 'struct'
+    if USESLOTS:
+        __slots__ = ()
+
+    def get_hash_typename(self):
+        return 'struct _hashT_%s @' % self.name
+
+    def forward_declaration(self):
+        hash_typename = self.get_hash_typename()
+        hash_offset = self.db.gctransformer.get_hash_offset(self.T)
+        yield '%s {' % cdecl(hash_typename, '')
+        yield '\tunion {'
+        yield '\t\t%s;' % cdecl(self.implementationtypename, 'head')
+        yield '\t\tchar pad[%s];' % name_signed(hash_offset, self.db)
+        yield '\t} u;'
+        yield '\tlong hash;'
+        yield '};'
+        yield '%s;' % (
+            forward_cdecl(hash_typename, '_hash_' + self.name,
+                          self.db.standalone, self.is_thread_local()),)
+        yield '#define %s _hash_%s.u.head' % (self.name, self.name)
+
+    def implementation(self):
+        hash_typename = self.get_hash_typename()
+        hash = self.db.gcpolicy.get_prebuilt_hash(self.obj)
+        assert hash is not None
+        lines = list(self.initializationexpr())
+        lines.insert(0, '%s = { {' % (
+            cdecl(hash_typename, '_hash_' + self.name,
+                  self.is_thread_local()),))
+        lines.append('}, %s /* hash */ };' % name_signed(hash, self.db))
+        return lines
+
+def gcstructnode_factory(db, T, obj):
+    if db.gcpolicy.get_prebuilt_hash(obj) is not None:
+        cls = GcStructNodeWithHash
+    else:
+        cls = StructNode
+    return cls(db, T, obj)
+
+
 class ArrayNode(ContainerNode):
     nodekind = 'array'
     if USESLOTS:
@@ -970,7 +1011,7 @@
 
 ContainerNodeFactory = {
     Struct:       StructNode,
-    GcStruct:     StructNode,
+    GcStruct:     gcstructnode_factory,
     Array:        ArrayNode,
     GcArray:      ArrayNode,
     FixedSizeArray: FixedSizeArrayNode,

Modified: pypy/trunk/pypy/translator/c/src/g_prerequisite.h
==============================================================================
--- pypy/trunk/pypy/translator/c/src/g_prerequisite.h	(original)
+++ pypy/trunk/pypy/translator/c/src/g_prerequisite.h	Fri Oct 16 15:11:23 2009
@@ -27,3 +27,6 @@
 #else
 typedef unsigned char bool_t;
 #endif
+
+
+#include "src/align.h"

Modified: pypy/trunk/pypy/translator/c/src/mem.h
==============================================================================
--- pypy/trunk/pypy/translator/c/src/mem.h	(original)
+++ pypy/trunk/pypy/translator/c/src/mem.h	Fri Oct 16 15:11:23 2009
@@ -2,22 +2,6 @@
 /************************************************************/
  /***  C header subsection: operations on LowLevelTypes    ***/
 
-/* alignment for arena-based garbage collectors: the following line
-   enforces an alignment that should be enough for any structure
-   containing pointers and 'double' fields. */
-struct rpy_memory_alignment_test1 {
-  double d;
-  void* p;
-};
-struct rpy_memory_alignment_test2 {
-  char c;
-  struct rpy_memory_alignment_test1 s;
-};
-#define MEMORY_ALIGNMENT	offsetof(struct rpy_memory_alignment_test2, s)
-#define ROUND_UP_FOR_ALLOCATION(x, minsize)  \
-  ((((x)>=(minsize)?(x):(minsize))           \
-               + (MEMORY_ALIGNMENT-1)) & ~(MEMORY_ALIGNMENT-1))
-
 extern char __gcmapstart;
 extern char __gcmapend;
 extern char __gccallshapes;

Modified: pypy/trunk/pypy/translator/c/test/test_boehm.py
==============================================================================
--- pypy/trunk/pypy/translator/c/test/test_boehm.py	(original)
+++ pypy/trunk/pypy/translator/c/test/test_boehm.py	Fri Oct 16 15:11:23 2009
@@ -422,3 +422,31 @@
             return True
         run = self.getcompiled(f)
         assert run() == True        
+
+    def test_hash_preservation(self):
+        from pypy.rlib.objectmodel import compute_hash
+        from pypy.rlib.objectmodel import current_object_addr_as_int
+        class C:
+            pass
+        class D(C):
+            pass
+        c = C()
+        d = D()
+        compute_hash(d)     # force to be cached on 'd', but not on 'c'
+        #
+        def fn():
+            d2 = D()
+            return (compute_hash(d2),
+                    current_object_addr_as_int(d2),
+                    compute_hash(c),
+                    compute_hash(d),
+                    compute_hash(("Hi", None, (7.5, 2, d))))
+        
+        f = self.getcompiled(fn)
+        res = f()
+
+        # xxx the next line is too precise, checking the exact implementation
+        assert res[0] == ~res[1]
+        assert res[2] != compute_hash(c)     # likely
+        assert res[3] == compute_hash(d)
+        assert res[4] == compute_hash(("Hi", None, (7.5, 2, d)))

Modified: pypy/trunk/pypy/translator/c/test/test_lltyped.py
==============================================================================
--- pypy/trunk/pypy/translator/c/test/test_lltyped.py	(original)
+++ pypy/trunk/pypy/translator/c/test/test_lltyped.py	Fri Oct 16 15:11:23 2009
@@ -249,6 +249,7 @@
         assert res1 == res2
 
     def test_null_padding(self):
+        py.test.skip("we no longer pad our RPython strings with a final NUL")
         from pypy.rpython.lltypesystem import llmemory
         from pypy.rpython.lltypesystem import rstr
         chars_offset = llmemory.FieldOffset(rstr.STR, 'chars') + \

Modified: pypy/trunk/pypy/translator/c/test/test_newgc.py
==============================================================================
--- pypy/trunk/pypy/translator/c/test/test_newgc.py	(original)
+++ pypy/trunk/pypy/translator/c/test/test_newgc.py	Fri Oct 16 15:11:23 2009
@@ -26,6 +26,7 @@
     def _makefunc2(cls, f):
         t = Translation(f, [int, int], gc=cls.gcpolicy,
                         policy=annpolicy.StrictAnnotatorPolicy())
+        t.config.translation.gcconfig.debugprint = True
         t.config.translation.gcconfig.removetypeptr = cls.removetypeptr
         t.disable(['backendopt'])
         t.set_backend_extra_options(c_isolated=True, c_debug_defines=True)
@@ -88,6 +89,8 @@
         if not args:
             args = (-1, )
         num = self.name_to_func[name]
+        print
+        print 'Running %r (test number %d)' % (name, num)
         res = self.c_allfuncs(num, *args)
         if self.funcsstr[num]:
             return res
@@ -689,6 +692,130 @@
     def test_resizable_buffer(self):
         assert self.run('resizable_buffer')
 
+    def define_hash_preservation(cls):
+        from pypy.rlib.objectmodel import compute_hash
+        from pypy.rlib.objectmodel import compute_identity_hash
+        from pypy.rlib.objectmodel import current_object_addr_as_int
+        class C:
+            pass
+        class D(C):
+            pass
+        c = C()
+        d = D()
+        h_d = compute_hash(d)     # force to be cached on 'd', but not on 'c'
+        h_t = compute_hash(("Hi", None, (7.5, 2, d)))
+        S = lltype.GcStruct('S', ('x', lltype.Signed),
+                                 ('a', lltype.Array(lltype.Signed)))
+        s = lltype.malloc(S, 15, zero=True)
+        h_s = compute_identity_hash(s)   # varsized: hash not saved/restored
+        #
+        def f():
+            if compute_hash(c) != compute_identity_hash(c): return 12
+            if compute_hash(d) != h_d: return 13
+            if compute_hash(("Hi", None, (7.5, 2, d))) != h_t: return 14
+            c2 = C()
+            h_c2 = compute_hash(c2)
+            if compute_hash(c2) != h_c2: return 15
+            if compute_identity_hash(s) == h_s: return 16   # unlikely
+            i = 0
+            while i < 6:
+                rgc.collect()
+                if compute_hash(c2) != h_c2: return i
+                i += 1
+            return 42
+        return f
+
+    def test_hash_preservation(self):
+        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):
+            "Make a chain of n objects."
+            x1 = None
+            i = 0
+            while i < n:
+                x2 = X()
+                x2.prev = x1
+                x1 = x2
+                i += 1
+            return x1
+
+        def build(xr, n):
+            "Build the identity hashes of all n objects of the chain."
+            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
+
+        def check(xr, n, step):
+            "Check that the identity hashes are still correct."
+            i = 0
+            while i < n:
+                if xr.hash != compute_identity_hash(xr):
+                    os.write(2, "wrong hash! i=%d, n=%d, step=%d\n" % (i, n,
+                                                                       step))
+                    raise ValueError
+                xr = xr.prev
+                i += 1
+            assert xr is None
+
+        def h(n):
+            x3 = g(3)
+            x4 = g(3)
+            x1 = g(n)
+            build(x1, n)       # can collect!
+            check(x1, n, 1)
+            build(x3, 3)
+            x2 = g(n//2)       # allocate more and try again
+            build(x2, n//2)
+            check(x1, n, 11)
+            check(x2, n//2, 12)
+            build(x4, 3)
+            check(x3, 3, 13)   # check these old objects too
+            check(x4, 3, 14)   # check these old objects too
+            rgc.collect()
+            check(x1, n, 21)
+            check(x2, n//2, 22)
+            check(x3, 3, 23)
+            check(x4, 3, 24)
+
+        def f():
+            # numbers optimized for a 8MB space
+            for n in [100000, 225000, 250000, 300000, 380000,
+                      460000, 570000, 800000]:
+                os.write(2, 'case %d\n' % n)
+                rgc.collect()
+                h(n)
+            return -42
+
+        return f
+
+    def test_hash_overflow(self):
+        res = self.run('hash_overflow')
+        assert res == -42
+
+    def define_hash_varsized(self):
+        S = lltype.GcStruct('S', ('abc', lltype.Signed),
+                                 ('def', lltype.Array(lltype.Signed)))
+        s = lltype.malloc(S, 3, zero=True)
+        h_s = lltype.identityhash(s)
+        def f():
+            return lltype.identityhash(s) - h_s    # != 0 (so far),
+                                # because S is a varsized structure.
+        return f
+
+    def test_hash_varsized(self):
+        res = self.run('hash_varsized')
+        assert res != 0
+
 class TestSemiSpaceGC(TestUsingFramework, snippet.SemiSpaceGCTestDefines):
     gcpolicy = "semispace"
     should_be_moving = True
@@ -805,6 +932,9 @@
     gcpolicy = "markcompact"
     should_be_moving = True
 
+    def setup_class(cls):
+        py.test.skip("Disabled for now")
+
     def test_gc_set_max_heap_size(self):
         py.test.skip("not implemented")
 

Modified: pypy/trunk/pypy/translator/c/test/test_typed.py
==============================================================================
--- pypy/trunk/pypy/translator/c/test/test_typed.py	(original)
+++ pypy/trunk/pypy/translator/c/test/test_typed.py	Fri Oct 16 15:11:23 2009
@@ -563,6 +563,7 @@
         assert f(255) == 255
 
     def test_hash_preservation(self):
+        from pypy.rlib.objectmodel import compute_hash
         from pypy.rlib.objectmodel import current_object_addr_as_int
         class C:
             pass
@@ -570,18 +571,24 @@
             pass
         c = C()
         d = D()
+        compute_hash(d)     # force to be cached on 'd', but not on 'c'
+        #
         def fn():
             d2 = D()
-            return hash(d2), current_object_addr_as_int(d2), hash(c), hash(d)
+            return (compute_hash(d2),
+                    current_object_addr_as_int(d2),
+                    compute_hash(c),
+                    compute_hash(d),
+                    compute_hash(("Hi", None, (7.5, 2, d))))
         
         f = self.getcompiled(fn)
-
         res = f()
 
-        # xxx this is too precise, checking the exact implementation
-        assert res[0] == ~res[1]
-        assert res[2] == hash(c)
-        assert res[3] == hash(d)
+        # xxx the next line is too precise, checking the exact implementation
+        assert res[0] == res[1]
+        assert res[2] != compute_hash(c)     # likely
+        assert res[3] == compute_hash(d)
+        assert res[4] == compute_hash(("Hi", None, (7.5, 2, d)))
 
     def test_list_basic_ops(self):
         def list_basic_ops(i, j):

Modified: pypy/trunk/pypy/translator/cli/opcodes.py
==============================================================================
--- pypy/trunk/pypy/translator/cli/opcodes.py	(original)
+++ pypy/trunk/pypy/translator/cli/opcodes.py	Fri Oct 16 15:11:23 2009
@@ -58,8 +58,8 @@
     'classof':                  [PushAllArgs, 'callvirt instance class [mscorlib]System.Type object::GetType()'],
     'instanceof':               [CastTo, 'ldnull', 'cgt.un'],
     'subclassof':               [PushAllArgs, 'call bool [pypylib]pypy.runtime.Utils::SubclassOf(class [mscorlib]System.Type, class[mscorlib]System.Type)'],
-    'ooidentityhash':           [PushAllArgs, 'callvirt instance int32 object::GetHashCode()'],
-    'oohash':                   [PushAllArgs, 'callvirt instance int32 object::GetHashCode()'],    
+    'gc_id':                    [PushAllArgs, 'call int32 [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetHashCode(object)'],   # XXX not implemented
+    'gc_identityhash':          [PushAllArgs, 'call int32 [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetHashCode(object)'],
     'oostring':                 [OOString],
     'oounicode':                [OOUnicode],
     'ooparse_int':              [PushAllArgs, 'call int32 [pypylib]pypy.runtime.Utils::OOParseInt(string, int32)'],

Modified: pypy/trunk/pypy/translator/cli/src/pypylib.cs
==============================================================================
--- pypy/trunk/pypy/translator/cli/src/pypylib.cs	(original)
+++ pypy/trunk/pypy/translator/cli/src/pypylib.cs	Fri Oct 16 15:11:23 2009
@@ -584,6 +584,10 @@
 
     public class String
     {
+        public static int ll_hash(string s)
+        {
+            return s.GetHashCode();
+        }
         public static char ll_stritem_nonneg(string s, int index)
         {
             return s[index];

Modified: pypy/trunk/pypy/translator/cli/test/test_dotnet.py
==============================================================================
--- pypy/trunk/pypy/translator/cli/test/test_dotnet.py	(original)
+++ pypy/trunk/pypy/translator/cli/test/test_dotnet.py	Fri Oct 16 15:11:23 2009
@@ -681,13 +681,13 @@
         A = ootype.Instance("A", ootype.ROOT, {})
         def fn():
             a = ootype.new(A)
-            ahash = ootype.ooidentityhash(a)
+            ahash = ootype.identityhash(a)
             obj = ootype.cast_to_object(a)
             native = cast_to_native_object(obj)
             name = native.GetType().get_Name()
             obj2 = cast_from_native_object(native)
             a2 = ootype.cast_from_object(A, obj2)
-            a2hash = ootype.ooidentityhash(a2)
+            a2hash = ootype.identityhash(a2)
             return name, ahash == a2hash
         res = self.ll_to_tuple(self.interpret(fn, []))
         assert res == ('A', True)

Modified: pypy/trunk/pypy/translator/jvm/builtin.py
==============================================================================
--- pypy/trunk/pypy/translator/jvm/builtin.py	(original)
+++ pypy/trunk/pypy/translator/jvm/builtin.py	Fri Oct 16 15:11:23 2009
@@ -84,6 +84,9 @@
     (ootype.StringBuilder.__class__, "ll_build"):
     jvm.Method.v(jStringBuilder, "toString", (), jString),
 
+    (ootype.String.__class__, "ll_hash"):
+    jvm.Method.v(jString, "hashCode", (), jInt),
+
     (ootype.String.__class__, "ll_streq"):
     jvm.Method.v(jString, "equals", (jObject,), jBool),
 

Modified: pypy/trunk/pypy/translator/jvm/opcodes.py
==============================================================================
--- pypy/trunk/pypy/translator/jvm/opcodes.py	(original)
+++ pypy/trunk/pypy/translator/jvm/opcodes.py	Fri Oct 16 15:11:23 2009
@@ -82,8 +82,8 @@
     'instanceof':               [CastTo, StoreResult],
     'subclassof':               [PushAllArgs, jvm.SWAP, jvm.CLASSISASSIGNABLEFROM, StoreResult],
     'classof':                  [PushAllArgs, jvm.OBJECTGETCLASS, StoreResult],
-    'ooidentityhash':           [PushAllArgs, jvm.OBJHASHCODE, StoreResult], 
-    'oohash':                   [PushAllArgs, jvm.OBJHASHCODE, StoreResult], 
+    'gc_id':                    [PushAllArgs, jvm.SYSTEMIDENTITYHASH, StoreResult],   # XXX not implemented
+    'gc_identityhash':          [PushAllArgs, jvm.SYSTEMIDENTITYHASH, StoreResult], 
     'oostring':                 [OOString, StoreResult],
     'oounicode':                [OOUnicode, StoreResult],
     'ooparse_float':            jvm.PYPYOOPARSEFLOAT,

Modified: pypy/trunk/pypy/translator/jvm/typesystem.py
==============================================================================
--- pypy/trunk/pypy/translator/jvm/typesystem.py	(original)
+++ pypy/trunk/pypy/translator/jvm/typesystem.py	Fri Oct 16 15:11:23 2009
@@ -888,6 +888,7 @@
 OBJHASHCODE =           Method.v(jObject, 'hashCode', (), jInt)
 OBJTOSTRING =           Method.v(jObject, 'toString', (), jString)
 OBJEQUALS =             Method.v(jObject, 'equals', (jObject,), jBool)
+SYSTEMIDENTITYHASH =    Method.s(jSystem, 'identityHashCode', (jObject,), jInt)
 SYSTEMGC =              Method.s(jSystem, 'gc', (), jVoid)
 INTTOSTRINGI =          Method.s(jIntegerClass, 'toString', (jInt,), jString)
 LONGTOSTRINGL =         Method.s(jLongClass, 'toString', (jLong,), jString)

Modified: pypy/trunk/pypy/translator/oosupport/test_template/string.py
==============================================================================
--- pypy/trunk/pypy/translator/oosupport/test_template/string.py	(original)
+++ pypy/trunk/pypy/translator/oosupport/test_template/string.py	Fri Oct 16 15:11:23 2009
@@ -1,5 +1,6 @@
 import py
 from pypy.rpython.test.test_rstr import BaseTestRstr
+from pypy.rlib.objectmodel import compute_hash
 
 class BaseTestString(BaseTestRstr):
 
@@ -22,11 +23,11 @@
         pass # it doesn't make sense here
 
     def test_hash_value(self):
-        # make that hash are computed by value and not by reference
+        # make sure that hash are computed by value and not by reference
         def fn(x, y):
             s1 = ''.join([x, 'e', 'l', 'l', 'o'])
             s2 = ''.join([y, 'e', 'l', 'l', 'o'])
-            return (hash(s1) == hash(s2)) and (s1 is not s2)
+            return (compute_hash(s1) == compute_hash(s2)) and (s1 is not s2)
         assert self.interpret(fn, ['h', 'h']) == True
 
     def test_int_formatting(self):



More information about the Pypy-commit mailing list