[pypy-commit] pypy optresult: merge default (badly)

fijal noreply at buildbot.pypy.org
Wed Dec 31 08:42:13 CET 2014


Author: Maciej Fijalkowski <fijall at gmail.com>
Branch: optresult
Changeset: r75187:7bdcf543fd1c
Date: 2014-12-31 09:42 +0200
http://bitbucket.org/pypy/pypy/changeset/7bdcf543fd1c/

Log:	merge default (badly)

diff too long, truncating to 2000 out of 5605 lines

diff --git a/lib-python/2.7/test/test_xml_etree.py b/lib-python/2.7/test/test_xml_etree.py
--- a/lib-python/2.7/test/test_xml_etree.py
+++ b/lib-python/2.7/test/test_xml_etree.py
@@ -225,9 +225,9 @@
     >>> element.remove(subelement)
     >>> serialize(element) # 5
     '<tag key="value" />'
-    >>> element.remove(subelement)
+    >>> element.remove(subelement)    # doctest: +ELLIPSIS
     Traceback (most recent call last):
-    ValueError: list.remove(x): x not in list
+    ValueError: list.remove(...
     >>> serialize(element) # 6
     '<tag key="value" />'
     >>> element[0:0] = [subelement, subelement, subelement]
diff --git a/lib_pypy/readline.py b/lib_pypy/readline.py
--- a/lib_pypy/readline.py
+++ b/lib_pypy/readline.py
@@ -6,4 +6,11 @@
 are only stubs at the moment.
 """
 
-from pyrepl.readline import *
+try:
+    from pyrepl.readline import *
+except ImportError:
+    import sys
+    if sys.platform == 'win32':
+        raise ImportError("the 'readline' module is not available on Windows"
+                          " (on either PyPy or CPython)")
+    raise
diff --git a/pypy/interpreter/astcompiler/optimize.py b/pypy/interpreter/astcompiler/optimize.py
--- a/pypy/interpreter/astcompiler/optimize.py
+++ b/pypy/interpreter/astcompiler/optimize.py
@@ -83,17 +83,16 @@
 
 class __extend__(ast.BoolOp):
 
-    def _accept_jump_if_any_is(self, gen, condition, target):
-        self.values[0].accept_jump_if(gen, condition, target)
-        for i in range(1, len(self.values)):
+    def _accept_jump_if_any_is(self, gen, condition, target, skip_last=0):
+        for i in range(len(self.values) - skip_last):
             self.values[i].accept_jump_if(gen, condition, target)
 
     def accept_jump_if(self, gen, condition, target):
         if condition and self.op == ast.And or \
                 (not condition and self.op == ast.Or):
             end = gen.new_block()
-            self._accept_jump_if_any_is(gen, not condition, end)
-            gen.emit_jump(ops.JUMP_FORWARD, target)
+            self._accept_jump_if_any_is(gen, not condition, end, skip_last=1)
+            self.values[-1].accept_jump_if(gen, condition, target)
             gen.use_next_block(end)
         else:
             self._accept_jump_if_any_is(gen, condition, target)
diff --git a/pypy/module/_cffi_backend/newtype.py b/pypy/module/_cffi_backend/newtype.py
--- a/pypy/module/_cffi_backend/newtype.py
+++ b/pypy/module/_cffi_backend/newtype.py
@@ -62,10 +62,54 @@
 
 eptype("intptr_t",  rffi.INTPTR_T,  ctypeprim.W_CTypePrimitiveSigned)
 eptype("uintptr_t", rffi.UINTPTR_T, ctypeprim.W_CTypePrimitiveUnsigned)
-eptype("ptrdiff_t", rffi.INTPTR_T,  ctypeprim.W_CTypePrimitiveSigned) # <-xxx
 eptype("size_t",    rffi.SIZE_T,    ctypeprim.W_CTypePrimitiveUnsigned)
 eptype("ssize_t",   rffi.SSIZE_T,   ctypeprim.W_CTypePrimitiveSigned)
 
+_WCTSigned = ctypeprim.W_CTypePrimitiveSigned
+_WCTUnsign = ctypeprim.W_CTypePrimitiveUnsigned
+
+eptype("ptrdiff_t", getattr(rffi, 'PTRDIFF_T', rffi.INTPTR_T), _WCTSigned)
+eptype("intmax_t",  getattr(rffi, 'INTMAX_T',  rffi.LONGLONG), _WCTSigned)
+eptype("uintmax_t", getattr(rffi, 'UINTMAX_T', rffi.LONGLONG), _WCTUnsign)
+
+if hasattr(rffi, 'INT_LEAST8_T'):
+    eptype("int_least8_t",  rffi.INT_LEAST8_T,  _WCTSigned)
+    eptype("int_least16_t", rffi.INT_LEAST16_T, _WCTSigned)
+    eptype("int_least32_t", rffi.INT_LEAST32_T, _WCTSigned)
+    eptype("int_least64_t", rffi.INT_LEAST64_T, _WCTSigned)
+    eptype("uint_least8_t", rffi.UINT_LEAST8_T,  _WCTUnsign)
+    eptype("uint_least16_t",rffi.UINT_LEAST16_T, _WCTUnsign)
+    eptype("uint_least32_t",rffi.UINT_LEAST32_T, _WCTUnsign)
+    eptype("uint_least64_t",rffi.UINT_LEAST64_T, _WCTUnsign)
+else:
+    eptypesize("int_least8_t",   1, _WCTSigned)
+    eptypesize("uint_least8_t",  1, _WCTUnsign)
+    eptypesize("int_least16_t",  2, _WCTSigned)
+    eptypesize("uint_least16_t", 2, _WCTUnsign)
+    eptypesize("int_least32_t",  4, _WCTSigned)
+    eptypesize("uint_least32_t", 4, _WCTUnsign)
+    eptypesize("int_least64_t",  8, _WCTSigned)
+    eptypesize("uint_least64_t", 8, _WCTUnsign)
+
+if hasattr(rffi, 'INT_FAST8_T'):
+    eptype("int_fast8_t",  rffi.INT_FAST8_T,  _WCTSigned)
+    eptype("int_fast16_t", rffi.INT_FAST16_T, _WCTSigned)
+    eptype("int_fast32_t", rffi.INT_FAST32_T, _WCTSigned)
+    eptype("int_fast64_t", rffi.INT_FAST64_T, _WCTSigned)
+    eptype("uint_fast8_t", rffi.UINT_FAST8_T,  _WCTUnsign)
+    eptype("uint_fast16_t",rffi.UINT_FAST16_T, _WCTUnsign)
+    eptype("uint_fast32_t",rffi.UINT_FAST32_T, _WCTUnsign)
+    eptype("uint_fast64_t",rffi.UINT_FAST64_T, _WCTUnsign)
+else:
+    eptypesize("int_fast8_t",   1, _WCTSigned)
+    eptypesize("uint_fast8_t",  1, _WCTUnsign)
+    eptypesize("int_fast16_t",  2, _WCTSigned)
+    eptypesize("uint_fast16_t", 2, _WCTUnsign)
+    eptypesize("int_fast32_t",  4, _WCTSigned)
+    eptypesize("uint_fast32_t", 4, _WCTUnsign)
+    eptypesize("int_fast64_t",  8, _WCTSigned)
+    eptypesize("uint_fast64_t", 8, _WCTUnsign)
+
 @unwrap_spec(name=str)
 def new_primitive_type(space, name):
     try:
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -397,7 +397,7 @@
 def test_invalid_indexing():
     p = new_primitive_type("int")
     x = cast(p, 42)
-    py.test.raises(TypeError, "p[0]")
+    py.test.raises(TypeError, "x[0]")
 
 def test_default_str():
     BChar = new_primitive_type("char")
@@ -2718,7 +2718,16 @@
 def test_nonstandard_integer_types():
     for typename in ['int8_t', 'uint8_t', 'int16_t', 'uint16_t', 'int32_t',
                      'uint32_t', 'int64_t', 'uint64_t', 'intptr_t',
-                     'uintptr_t', 'ptrdiff_t', 'size_t', 'ssize_t']:
+                     'uintptr_t', 'ptrdiff_t', 'size_t', 'ssize_t',
+                     'int_least8_t',  'uint_least8_t',
+                     'int_least16_t', 'uint_least16_t',
+                     'int_least32_t', 'uint_least32_t',
+                     'int_least64_t', 'uint_least64_t',
+                     'int_fast8_t',  'uint_fast8_t',
+                     'int_fast16_t', 'uint_fast16_t',
+                     'int_fast32_t', 'uint_fast32_t',
+                     'int_fast64_t', 'uint_fast64_t',
+                     'intmax_t', 'uintmax_t']:
         new_primitive_type(typename)    # works
 
 def test_cannot_convert_unicode_to_charp():
diff --git a/pypy/module/_rawffi/buffer.py b/pypy/module/_rawffi/buffer.py
--- a/pypy/module/_rawffi/buffer.py
+++ b/pypy/module/_rawffi/buffer.py
@@ -1,4 +1,5 @@
 from rpython.rlib.buffer import Buffer
+from rpython.rtyper.lltypesystem import rffi
 
 # XXX not the most efficient implementation
 
@@ -20,3 +21,7 @@
     def setitem(self, index, char):
         ll_buffer = self.datainstance.ll_buffer
         ll_buffer[index] = char
+
+    def get_raw_address(self):
+        ll_buffer = self.datainstance.ll_buffer
+        return rffi.cast(rffi.CCHARP, ll_buffer)
diff --git a/pypy/module/_rawffi/test/test__rawffi.py b/pypy/module/_rawffi/test/test__rawffi.py
--- a/pypy/module/_rawffi/test/test__rawffi.py
+++ b/pypy/module/_rawffi/test/test__rawffi.py
@@ -1144,6 +1144,15 @@
         b[3] = b'x'
         assert b[3] == b'x'
 
+    def test_pypy_raw_address(self):
+        import _rawffi
+        S = _rawffi.Structure((40, 1))
+        s = S(autofree=True)
+        addr = buffer(s)._pypy_raw_address()
+        assert type(addr) is int
+        assert buffer(s)._pypy_raw_address() == addr
+        assert buffer(s, 10)._pypy_raw_address() == addr + 10
+
     def test_union(self):
         import _rawffi
         longsize = _rawffi.sizeof('l')
diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py
--- a/pypy/module/cpyext/slotdefs.py
+++ b/pypy/module/cpyext/slotdefs.py
@@ -244,6 +244,9 @@
     def getitem(self, index):
         return self.ptr[index]
 
+    def get_raw_address(self):
+        return rffi.cast(rffi.CCHARP, self.ptr)
+
 def wrap_getreadbuffer(space, w_self, w_args, func):
     func_target = rffi.cast(readbufferproc, func)
     with lltype.scoped_alloc(rffi.VOIDPP.TO, 1) as ptr:
diff --git a/pypy/module/gc/__init__.py b/pypy/module/gc/__init__.py
--- a/pypy/module/gc/__init__.py
+++ b/pypy/module/gc/__init__.py
@@ -30,6 +30,7 @@
                 'get_referrers': 'referents.get_referrers',
                 '_dump_rpy_heap': 'referents._dump_rpy_heap',
                 'get_typeids_z': 'referents.get_typeids_z',
+                'get_typeids_list': 'referents.get_typeids_list',
                 'GcRef': 'referents.W_GcRef',
                 })
         MixedModule.__init__(self, space, w_name)
diff --git a/pypy/module/gc/app_referents.py b/pypy/module/gc/app_referents.py
--- a/pypy/module/gc/app_referents.py
+++ b/pypy/module/gc/app_referents.py
@@ -16,7 +16,8 @@
     [0][0][0][-1] inserted after all GC roots, before all non-roots.
 
     If the argument is a filename and the 'zlib' module is available,
-    we also write a 'typeids.txt' in the same directory, if none exists.
+    we also write 'typeids.txt' and 'typeids.lst' in the same directory,
+    if they don't already exist.
     """
     if isinstance(file, str):
         f = open(file, 'wb')
@@ -30,7 +31,13 @@
             filename2 = os.path.join(os.path.dirname(file), 'typeids.txt')
             if not os.path.exists(filename2):
                 data = zlib.decompress(gc.get_typeids_z())
-                f = open(filename2, 'wb')
+                f = open(filename2, 'w')
+                f.write(data)
+                f.close()
+            filename2 = os.path.join(os.path.dirname(file), 'typeids.lst')
+            if not os.path.exists(filename2):
+                data = ''.join(['%d\n' % n for n in gc.get_typeids_list()])
+                f = open(filename2, 'w')
                 f.write(data)
                 f.close()
     else:
diff --git a/pypy/module/gc/referents.py b/pypy/module/gc/referents.py
--- a/pypy/module/gc/referents.py
+++ b/pypy/module/gc/referents.py
@@ -228,3 +228,8 @@
     a = rgc.get_typeids_z()
     s = ''.join([a[i] for i in range(len(a))])
     return space.wrap(s)
+
+def get_typeids_list(space):
+    l = rgc.get_typeids_list()
+    list_w = [space.wrap(l[i]) for i in range(len(l))]
+    return space.newlist(list_w)
diff --git a/pypy/module/micronumpy/descriptor.py b/pypy/module/micronumpy/descriptor.py
--- a/pypy/module/micronumpy/descriptor.py
+++ b/pypy/module/micronumpy/descriptor.py
@@ -392,7 +392,7 @@
         alignment = space.int_w(space.getitem(w_data, space.wrap(6)))
 
         if (w_names == space.w_None) != (w_fields == space.w_None):
-            raise oefmt(space.w_ValueError, "inconsistent fields and names")
+            raise oefmt(space.w_ValueError, "inconsistent fields and names in Numpy dtype unpickling")
 
         self.byteorder = endian
         self.shape = []
diff --git a/pypy/module/micronumpy/test/test_complex.py b/pypy/module/micronumpy/test/test_complex.py
--- a/pypy/module/micronumpy/test/test_complex.py
+++ b/pypy/module/micronumpy/test/test_complex.py
@@ -478,6 +478,15 @@
         for i in range(4):
             assert c[i] == max(a[i], b[i])
 
+
+    def test_abs_overflow(self):
+        from numpy import array, absolute, isinf
+        a = array(complex(1.5e308,1.5e308))
+        # Prints a RuntimeWarning, but does not raise
+        b = absolute(a)
+        assert isinf(b)
+        
+
     def test_basic(self):
         import sys
         from numpy import (dtype, add, array, dtype,
diff --git a/pypy/module/micronumpy/test/test_dtypes.py b/pypy/module/micronumpy/test/test_dtypes.py
--- a/pypy/module/micronumpy/test/test_dtypes.py
+++ b/pypy/module/micronumpy/test/test_dtypes.py
@@ -665,6 +665,7 @@
 
         assert numpy.int64(9223372036854775807) == 9223372036854775807
         assert numpy.int64(9223372036854775807) == 9223372036854775807
+        assert numpy.int64(-9223372036854775807) == -9223372036854775807
         raises(OverflowError, numpy.int64, 9223372036854775808)
         raises(OverflowError, numpy.int64, 9223372036854775808L)
 
@@ -1233,7 +1234,8 @@
 
         d = np.dtype(('<f8', 2))
         exc = raises(ValueError, "d.__setstate__((3, '|', None, ('f0', 'f1'), None, 16, 1, 0))")
-        assert exc.value[0] == 'inconsistent fields and names'
+        inconsistent = 'inconsistent fields and names in Numpy dtype unpickling'
+        assert exc.value[0] == inconsistent
         assert d.fields is None
         assert d.shape == (2,)
         assert d.subdtype is not None
@@ -1241,7 +1243,7 @@
 
         d = np.dtype(('<f8', 2))
         exc = raises(ValueError, "d.__setstate__((3, '|', None, None, {'f0': (np.dtype('float64'), 0), 'f1': (np.dtype('float64'), 8)}, 16, 1, 0))")
-        assert exc.value[0] == 'inconsistent fields and names'
+        assert exc.value[0] == inconsistent
         assert d.fields is None
         assert d.shape == (2,)
         assert d.subdtype is not None
@@ -1282,7 +1284,11 @@
         from cPickle import loads, dumps
 
         d = dtype([("x", "int32"), ("y", "int32"), ("z", "int32"), ("value", float)])
-        assert d.__reduce__() == (dtype, ('V20', 0, 1), (3, '|', None, ('x', 'y', 'z', 'value'), {'y': (dtype('int32'), 4), 'x': (dtype('int32'), 0), 'z': (dtype('int32'), 8), 'value': (dtype('float64'), 12)}, 20, 1, 0))
+        assert d.__reduce__() == (dtype, ('V20', 0, 1), (3, '|', None, 
+                     ('x', 'y', 'z', 'value'),
+                     {'y': (dtype('int32'), 4), 'x': (dtype('int32'), 0),
+                      'z': (dtype('int32'), 8), 'value': (dtype('float64'), 12),
+                      }, 20, 1, 0))
 
         new_d = loads(dumps(d))
 
diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py
--- a/pypy/module/micronumpy/types.py
+++ b/pypy/module/micronumpy/types.py
@@ -1195,7 +1195,11 @@
 
     @complex_to_real_unary_op
     def abs(self, v):
-        return rcomplex.c_abs(v[0], v[1])
+        try:
+            return rcomplex.c_abs(v[0], v[1])
+        except OverflowError:
+            # warning ...
+            return rfloat.INFINITY
 
     @raw_unary_op
     def isnan(self, v):
diff --git a/pypy/module/micronumpy/ufuncs.py b/pypy/module/micronumpy/ufuncs.py
--- a/pypy/module/micronumpy/ufuncs.py
+++ b/pypy/module/micronumpy/ufuncs.py
@@ -619,6 +619,9 @@
             space.int_w(w_obj)
         except OperationError, e:
             if e.match(space, space.w_OverflowError):
+                if space.is_true(space.le(w_obj, space.wrap(0))):
+                    return find_binop_result_dtype(space, int64_dtype,
+                                               current_guess)
                 return find_binop_result_dtype(space, uint64_dtype,
                                                current_guess)
             raise
diff --git a/pypy/module/select/test/test_select.py b/pypy/module/select/test/test_select.py
--- a/pypy/module/select/test/test_select.py
+++ b/pypy/module/select/test/test_select.py
@@ -286,7 +286,7 @@
 
             t = thread.start_new_thread(pollster.poll, ())
             try:
-                time.sleep(0.1)
+                time.sleep(0.3)
                 for i in range(5): print '',  # to release GIL untranslated
                 # trigger ufds array reallocation
                 for fd in rfds:
@@ -297,7 +297,7 @@
             finally:
                 # and make the call to poll() from the thread return
                 os.write(w, b'spam')
-                time.sleep(0.1)
+                time.sleep(0.3)
                 for i in range(5): print '',  # to release GIL untranslated
         finally:
             os.close(r)
diff --git a/pypy/module/thread/gil.py b/pypy/module/thread/gil.py
--- a/pypy/module/thread/gil.py
+++ b/pypy/module/thread/gil.py
@@ -7,7 +7,7 @@
 # all but one will be blocked.  The other threads get a chance to run
 # from time to time, using the periodic action GILReleaseAction.
 
-from rpython.rlib import rthread, rgil
+from rpython.rlib import rthread, rgil, rwin32
 from pypy.module.thread.error import wrap_thread_error
 from pypy.interpreter.executioncontext import PeriodicAsyncAction
 from pypy.module.thread.threadlocals import OSThreadLocals
@@ -76,9 +76,14 @@
 
 def after_external_call():
     e = get_errno()
+    e2 = 0
+    if rwin32.WIN32:
+        e2 = rwin32.GetLastError()
     rgil.gil_acquire()
     rthread.gc_thread_run()
     after_thread_switch()
+    if rwin32.WIN32:
+        rwin32.SetLastError(e2)
     set_errno(e)
 after_external_call._gctransformer_hint_cannot_collect_ = True
 after_external_call._dont_reach_me_in_del_ = True
diff --git a/pypy/objspace/std/bufferobject.py b/pypy/objspace/std/bufferobject.py
--- a/pypy/objspace/std/bufferobject.py
+++ b/pypy/objspace/std/bufferobject.py
@@ -5,7 +5,7 @@
 from rpython.rlib.objectmodel import compute_hash
 
 from pypy.interpreter.baseobjspace import W_Root
-from pypy.interpreter.error import oefmt
+from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.gateway import interp2app, unwrap_spec
 from pypy.interpreter.typedef import TypeDef
 
@@ -123,6 +123,17 @@
         return space.wrap("<%s for 0x%s, size %d>" %
                           (info, addrstring, self.buf.getlength()))
 
+    def descr_pypy_raw_address(self, space):
+        from rpython.rtyper.lltypesystem import lltype, rffi
+        try:
+            ptr = self.buf.get_raw_address()
+        except ValueError:
+            # report the error using the RPython-level internal repr of self.buf
+            msg = ("cannot find the underlying address of buffer that "
+                   "is internally %r" % (self.buf,))
+            raise OperationError(space.w_ValueError, space.wrap(msg))
+        return space.wrap(rffi.cast(lltype.Signed, ptr))
+
 W_Buffer.typedef = TypeDef(
     "buffer",
     __doc__ = """\
@@ -149,5 +160,6 @@
     __mul__ = interp2app(W_Buffer.descr_mul),
     __rmul__ = interp2app(W_Buffer.descr_mul),
     __repr__ = interp2app(W_Buffer.descr_repr),
+    _pypy_raw_address = interp2app(W_Buffer.descr_pypy_raw_address),
 )
 W_Buffer.typedef.acceptable_as_base_class = False
diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py
--- a/pypy/objspace/std/listobject.py
+++ b/pypy/objspace/std/listobject.py
@@ -618,8 +618,8 @@
         try:
             i = self.find(w_value, 0, sys.maxint)
         except ValueError:
-            raise OperationError(space.w_ValueError,
-                                 space.wrap("list.remove(x): x not in list"))
+            raise oefmt(space.w_ValueError,
+                        "list.remove(): %R is not in list", w_value)
         if i < self.length():  # otherwise list was mutated
             self.pop(i)
 
@@ -633,8 +633,7 @@
         try:
             i = self.find(w_value, i, stop)
         except ValueError:
-            raise OperationError(space.w_ValueError,
-                                 space.wrap("list.index(x): x not in list"))
+            raise oefmt(space.w_ValueError, "%R is not in list", w_value)
         return space.wrap(i)
 
     @unwrap_spec(reverse=bool)
diff --git a/pypy/objspace/std/test/test_bufferobject.py b/pypy/objspace/std/test/test_bufferobject.py
--- a/pypy/objspace/std/test/test_bufferobject.py
+++ b/pypy/objspace/std/test/test_bufferobject.py
@@ -197,3 +197,9 @@
         buf = buffer('hello world')
         raises(TypeError, "buf[MyInt(0)]")
         raises(TypeError, "buf[MyInt(0):MyInt(5)]")
+
+    def test_pypy_raw_address_base(self):
+        raises(ValueError, buffer("foobar")._pypy_raw_address)
+        raises(ValueError, buffer(u"foobar")._pypy_raw_address)
+        e = raises(ValueError, buffer(bytearray("foobar"))._pypy_raw_address)
+        assert 'BytearrayBuffer' in str(e.value)
diff --git a/pypy/objspace/std/test/test_dictproxy.py b/pypy/objspace/std/test/test_dictproxy.py
--- a/pypy/objspace/std/test/test_dictproxy.py
+++ b/pypy/objspace/std/test/test_dictproxy.py
@@ -15,14 +15,15 @@
         assert NotEmpty.__dict__.get("b") is None
         raises(TypeError, 'NotEmpty.__dict__[15] = "y"')
         raises(KeyError, 'del NotEmpty.__dict__[15]')
+
+        key, value = NotEmpty.__dict__.popitem()
+        assert (key == 'a' and value == 1) or (key == 'b' and value == 4)
+
         assert NotEmpty.__dict__.setdefault("string", 1) == 1
         assert NotEmpty.__dict__.setdefault("string", 2) == 1
         assert NotEmpty.string == 1
         raises(TypeError, 'NotEmpty.__dict__.setdefault(15, 1)')
 
-        key, value = NotEmpty.__dict__.popitem()
-        assert (key == 'a' and value == 1) or (key == 'b' and value == 4)
-
     def test_dictproxy_getitem(self):
         class NotEmpty(object):
             a = 1
diff --git a/pypy/objspace/std/test/test_listobject.py b/pypy/objspace/std/test/test_listobject.py
--- a/pypy/objspace/std/test/test_listobject.py
+++ b/pypy/objspace/std/test/test_listobject.py
@@ -976,7 +976,10 @@
 
         c = [0.0, 2.2, 4.4]
         assert c.index(0) == 0.0
-        raises(ValueError, c.index, 3)
+        e = raises(ValueError, c.index, 3)
+        import sys
+        if sys.version_info[:2] == (2, 7):     # CPython 2.7, PyPy
+            assert str(e.value) == '3 is not in list'
 
     def test_index_cpython_bug(self):
         if self.on_cpython:
@@ -1228,7 +1231,9 @@
         assert l == [0.0, 1.1, 3.3, 4.4]
         l = [0.0, 3.3, 5.5]
         raises(ValueError, c.remove, 2)
-        raises(ValueError, c.remove, 2.2)
+        e = raises(ValueError, c.remove, 2.2)
+        if not self.on_cpython:
+            assert str(e.value) == 'list.remove(): 2.2 is not in list'
 
     def test_reverse(self):
         c = list('hello world')
diff --git a/rpython/annotator/model.py b/rpython/annotator/model.py
--- a/rpython/annotator/model.py
+++ b/rpython/annotator/model.py
@@ -254,7 +254,10 @@
         return self.__class__(can_be_None=False, no_nul=self.no_nul)
 
     def nonnulify(self):
-        return self.__class__(can_be_None=self.can_be_None, no_nul=True)
+        if self.can_be_None:
+            return self.__class__(can_be_None=True, no_nul=True)
+        else:
+            return self.__class__(no_nul=True)
 
 
 class SomeString(SomeStringOrUnicode):
diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py
--- a/rpython/annotator/test/test_annrpython.py
+++ b/rpython/annotator/test/test_annrpython.py
@@ -4326,6 +4326,13 @@
         assert isinstance(s, annmodel.SomeString)
         assert not s.can_be_none()
 
+    def test_nonnulify(self):
+        s = annmodel.SomeString(can_be_None=True).nonnulify()
+        assert s.can_be_None is True
+        assert s.no_nul is True
+        s = annmodel.SomeChar().nonnulify()
+        assert s.no_nul is True
+
 
 def g(n):
     return [0, 1, 2, n]
diff --git a/rpython/jit/backend/arm/assembler.py b/rpython/jit/backend/arm/assembler.py
--- a/rpython/jit/backend/arm/assembler.py
+++ b/rpython/jit/backend/arm/assembler.py
@@ -901,7 +901,7 @@
             descr = tok.faildescr
             assert isinstance(descr, AbstractFailDescr)
             failure_recovery_pos = block_start + tok.pos_recovery_stub
-            descr._arm_failure_recovery_block = failure_recovery_pos
+            descr.adr_jump_offset = failure_recovery_pos
             relative_offset = tok.pos_recovery_stub - tok.offset
             guard_pos = block_start + tok.offset
             if not tok.is_guard_not_invalidated:
@@ -968,11 +968,11 @@
 
     def patch_trace(self, faildescr, looptoken, bridge_addr, regalloc):
         b = InstrBuilder(self.cpu.cpuinfo.arch_version)
-        patch_addr = faildescr._arm_failure_recovery_block
+        patch_addr = faildescr.adr_jump_offset
         assert patch_addr != 0
         b.B(bridge_addr)
         b.copy_to_raw_memory(patch_addr)
-        faildescr._arm_failure_recovery_block = 0
+        faildescr.adr_jump_offset = 0
 
     # regalloc support
     def load(self, loc, value):
diff --git a/rpython/jit/backend/llsupport/assembler.py b/rpython/jit/backend/llsupport/assembler.py
--- a/rpython/jit/backend/llsupport/assembler.py
+++ b/rpython/jit/backend/llsupport/assembler.py
@@ -192,8 +192,6 @@
             positions[i] = rffi.cast(rffi.USHORT, position)
         # write down the positions of locs
         guardtok.faildescr.rd_locs = positions
-        # we want the descr to keep alive
-        guardtok.faildescr.rd_loop_token = self.current_clt
         return fail_descr, target
 
     def call_assembler(self, op, guard_op, argloc, vloc, result_loc, tmploc):
diff --git a/rpython/jit/backend/x86/assembler.py b/rpython/jit/backend/x86/assembler.py
--- a/rpython/jit/backend/x86/assembler.py
+++ b/rpython/jit/backend/x86/assembler.py
@@ -572,13 +572,13 @@
 
     def patch_pending_failure_recoveries(self, rawstart):
         # after we wrote the assembler to raw memory, set up
-        # tok.faildescr._x86_adr_jump_offset to contain the raw address of
+        # tok.faildescr.adr_jump_offset to contain the raw address of
         # the 4-byte target field in the JMP/Jcond instruction, and patch
         # the field in question to point (initially) to the recovery stub
         clt = self.current_clt
         for tok in self.pending_guard_tokens:
             addr = rawstart + tok.pos_jump_offset
-            tok.faildescr._x86_adr_jump_offset = addr
+            tok.faildescr.adr_jump_offset = addr
             relative_target = tok.pos_recovery_stub - (tok.pos_jump_offset + 4)
             assert rx86.fits_in_32bits(relative_target)
             #
@@ -685,7 +685,7 @@
                                    self.cpu.gc_ll_descr.gcrootmap)
 
     def patch_jump_for_descr(self, faildescr, adr_new_target):
-        adr_jump_offset = faildescr._x86_adr_jump_offset
+        adr_jump_offset = faildescr.adr_jump_offset
         assert adr_jump_offset != 0
         offset = adr_new_target - (adr_jump_offset + 4)
         # If the new target fits within a rel32 of the jump, just patch
@@ -705,7 +705,7 @@
             p = rffi.cast(rffi.INTP, adr_jump_offset)
             adr_target = adr_jump_offset + 4 + rffi.cast(lltype.Signed, p[0])
             mc.copy_to_raw_memory(adr_target)
-        faildescr._x86_adr_jump_offset = 0    # means "patched"
+        faildescr.adr_jump_offset = 0    # means "patched"
 
     def fixup_target_tokens(self, rawstart):
         for targettoken in self.target_tokens_currently_compiling:
diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py
--- a/rpython/jit/metainterp/blackhole.py
+++ b/rpython/jit/metainterp/blackhole.py
@@ -1479,15 +1479,15 @@
             assert kind == 'v'
         return lltype.nullptr(rclass.OBJECTPTR.TO)
 
-    def _prepare_resume_from_failure(self, opnum, dont_change_position,
-                                     deadframe):
+    def _prepare_resume_from_failure(self, opnum, deadframe):
         from rpython.jit.metainterp.resoperation import rop
         #
-        if opnum == rop.GUARD_TRUE:
+        if opnum == rop.GUARD_FUTURE_CONDITION:
+            pass
+        elif opnum == rop.GUARD_TRUE:
             # Produced directly by some goto_if_not_xxx() opcode that did not
             # jump, but which must now jump.  The pc is just after the opcode.
-            if not dont_change_position:
-                self.position = self.jitcode.follow_jump(self.position)
+            self.position = self.jitcode.follow_jump(self.position)
         #
         elif opnum == rop.GUARD_FALSE:
             # Produced directly by some goto_if_not_xxx() opcode that jumped,
@@ -1517,8 +1517,7 @@
         elif opnum == rop.GUARD_NO_OVERFLOW:
             # Produced by int_xxx_ovf().  The pc is just after the opcode.
             # We get here because it did not used to overflow, but now it does.
-            if not dont_change_position:
-                return get_llexception(self.cpu, OverflowError())
+            return get_llexception(self.cpu, OverflowError())
         #
         elif opnum == rop.GUARD_OVERFLOW:
             # Produced by int_xxx_ovf().  The pc is just after the opcode.
@@ -1649,13 +1648,9 @@
         resumedescr,
         deadframe,
         all_virtuals)
-    if isinstance(resumedescr, ResumeAtPositionDescr):
-        dont_change_position = True
-    else:
-        dont_change_position = False
 
     current_exc = blackholeinterp._prepare_resume_from_failure(
-        resumedescr.guard_opnum, dont_change_position, deadframe)
+        resumedescr.guard_opnum, deadframe)
 
     _run_forever(blackholeinterp, current_exc)
 
diff --git a/rpython/jit/metainterp/compile.py b/rpython/jit/metainterp/compile.py
--- a/rpython/jit/metainterp/compile.py
+++ b/rpython/jit/metainterp/compile.py
@@ -61,14 +61,18 @@
     if metainterp_sd.warmrunnerdesc is not None:    # for tests
         assert original_jitcell_token.generation > 0     # has been registered with memmgr
     wref = weakref.ref(original_jitcell_token)
+    clt = original_jitcell_token.compiled_loop_token
+    clt.loop_token_wref = wref
     for op in loop.operations:
         descr = op.getdescr()
+        # not sure what descr.index is about
         if isinstance(descr, ResumeDescr):
-            descr.wref_original_loop_token = wref   # stick it there
-            n = descr.index
-            if n >= 0:       # we also record the resumedescr number
-                original_jitcell_token.compiled_loop_token.record_faildescr_index(n)
-        elif isinstance(descr, JitCellToken):
+            descr.rd_loop_token = clt   # stick it there
+            #n = descr.index
+            #if n >= 0:       # we also record the resumedescr number
+            #    original_jitcell_token.compiled_loop_token.record_faildescr_index(n)
+        #    pass
+        if isinstance(descr, JitCellToken):
             # for a CALL_ASSEMBLER: record it as a potential jump.
             if descr is not original_jitcell_token:
                 original_jitcell_token.record_jump_to(descr)
@@ -135,15 +139,14 @@
     part = create_empty_loop(metainterp)
     part.inputargs = inputargs[:]
     h_ops = history.operations
-    memo = Memo()
-    part.operations = [ResOperation(rop.LABEL, inputargs, descr=TargetToken(jitcell_token))] + \
-                      [h_ops[i].clone(memo) for i in range(start, len(h_ops))]
-    jumpargs = [memo.get(box, box) for box in jumpargs]
-    part.operations.append(ResOperation(rop.LABEL, jumpargs, descr=jitcell_token))
+    label = ResOperation(rop.LABEL, inputargs, None,
+                         descr=TargetToken(jitcell_token))
+    end_label = ResOperation(rop.LABEL, jumpargs, None, descr=jitcell_token)
+    part.operations = [label] + h_ops[start:] + [end_label]
 
     try:
-        start_state = optimize_trace(metainterp_sd, part, enable_opts,
-                                     export_state=True)
+        start_state = optimize_trace(metainterp_sd, jitdriver_sd, part,
+                                     enable_opts, export_state=True)
     except InvalidLoop:
         return None
     target_token = part.operations[0].getdescr()
@@ -170,7 +173,7 @@
         jumpargs = part.operations[-1].getarglist()
 
         try:
-            optimize_trace(metainterp_sd, part, enable_opts,
+            optimize_trace(metainterp_sd, jitdriver_sd, part, enable_opts,
                            start_state=start_state, export_state=False)
         except InvalidLoop:
             return None
@@ -218,13 +221,14 @@
     h_ops = history.operations
 
     part.operations = [partial_trace.operations[-1]] + \
-                      [h_ops[i].clone() for i in range(start, len(h_ops))] + \
+                      h_ops[start:] + \
                       [ResOperation(rop.JUMP, jumpargs, None, descr=loop_jitcell_token)]
     label = part.operations[0]
     orignial_label = label.clone()
     assert label.getopnum() == rop.LABEL
     try:
-        optimize_trace(metainterp_sd, part, jitdriver_sd.warmstate.enable_opts,
+        optimize_trace(metainterp_sd, jitdriver_sd, part,
+                       jitdriver_sd.warmstate.enable_opts,
                        start_state=start_state, export_state=False)
     except InvalidLoop:
         # Fall back on jumping to preamble
@@ -234,7 +238,7 @@
                           [ResOperation(rop.JUMP, inputargs[:],
                                         None, descr=loop_jitcell_token)]
         try:
-            optimize_trace(metainterp_sd, part,
+            optimize_trace(metainterp_sd, jitdriver_sd, part,
                            jitdriver_sd.warmstate.enable_opts,
                            inline_short_preamble=False, start_state=start_state,
                            export_state=False)
@@ -494,22 +498,31 @@
     return d
 
 class ResumeDescr(AbstractFailDescr):
-    pass
+    _attrs_ = ()
 
 class ResumeGuardDescr(ResumeDescr):
-    # this class also gets the following attributes stored by resume.py code
-    # XXX move all of unused stuff to guard_op, now that we can have
-    #     a separate class, so it does not survive that long
-    rd_snapshot = None
-    rd_frame_info_list = None
+    _attrs_ = ('rd_numb', 'rd_count', 'rd_consts', 'rd_virtuals',
+               'rd_frame_info_list', 'rd_pendingfields', 'status')
+    
     rd_numb = lltype.nullptr(NUMBERING)
     rd_count = 0
     rd_consts = None
     rd_virtuals = None
+    rd_frame_info_list = None
     rd_pendingfields = lltype.nullptr(PENDINGFIELDSP.TO)
 
     status = r_uint(0)
 
+    def copy_all_attributes_from(self, other):
+        assert isinstance(other, ResumeGuardDescr)
+        self.rd_count = other.rd_count
+        self.rd_consts = other.rd_consts
+        self.rd_frame_info_list = other.rd_frame_info_list
+        self.rd_pendingfields = other.rd_pendingfields
+        self.rd_virtuals = other.rd_virtuals
+        self.rd_numb = other.rd_numb
+        # we don't copy status
+
     ST_BUSY_FLAG    = 0x01     # if set, busy tracing from the guard
     ST_TYPE_MASK    = 0x06     # mask for the type (TY_xxx)
     ST_SHIFT        = 3        # in "status >> ST_SHIFT" is stored:
@@ -524,31 +537,12 @@
     def store_final_boxes(self, guard_op, boxes, metainterp_sd):
         guard_op.setfailargs(boxes)
         self.rd_count = len(boxes)
-        self.guard_opnum = guard_op.getopnum()
         #
         if metainterp_sd.warmrunnerdesc is not None:   # for tests
             jitcounter = metainterp_sd.warmrunnerdesc.jitcounter
             hash = jitcounter.fetch_next_hash()
             self.status = hash & self.ST_SHIFT_MASK
 
-    def make_a_counter_per_value(self, guard_value_op):
-        assert guard_value_op.getopnum() == rop.GUARD_VALUE
-        box = guard_value_op.getarg(0)
-        try:
-            i = guard_value_op.getfailargs().index(box)
-        except ValueError:
-            return     # xxx probably very rare
-        else:
-            if box.type == history.INT:
-                ty = self.TY_INT
-            elif box.type == history.REF:
-                ty = self.TY_REF
-            elif box.type == history.FLOAT:
-                ty = self.TY_FLOAT
-            else:
-                assert 0, box.type
-            self.status = ty | (r_uint(i) << self.ST_SHIFT)
-
     def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
         if self.must_compile(deadframe, metainterp_sd, jitdriver_sd):
             self.start_compiling()
@@ -647,32 +641,62 @@
                                self, inputargs, new_loop.operations,
                                new_loop.original_jitcell_token)
 
-    def copy_all_attributes_into(self, res, memo):
-        # XXX a bit ugly to have to list them all here
-        res.rd_snapshot = self.rd_snapshot.copy(memo)
-        res.rd_frame_info_list = self.rd_frame_info_list
-        res.rd_numb = self.rd_numb
-        res.rd_consts = self.rd_consts
-        res.rd_virtuals = self.rd_virtuals
-        res.rd_pendingfields = self.rd_pendingfields
-        res.rd_count = self.rd_count
+    def make_a_counter_per_value(self, guard_value_op):
+        assert guard_value_op.getopnum() == rop.GUARD_VALUE
+        box = guard_value_op.getarg(0)
+        try:
+            i = guard_value_op.getfailargs().index(box)
+        except ValueError:
+            return     # xxx probably very rare
+        else:
+            if box.type == history.INT:
+                ty = self.TY_INT
+            elif box.type == history.REF:
+                ty = self.TY_REF
+            elif box.type == history.FLOAT:
+                ty = self.TY_FLOAT
+            else:
+                assert 0, box.type
+            self.status = ty | (r_uint(i) << self.ST_SHIFT)
 
-    def _clone_if_mutable(self, memo):
-        res = ResumeGuardDescr()
-        self.copy_all_attributes_into(res, memo)
-        return res
+class ResumeGuardNonnullDescr(ResumeGuardDescr):
+    guard_opnum = rop.GUARD_NONNULL
+
+class ResumeGuardIsnullDescr(ResumeGuardDescr):
+    guard_opnum = rop.GUARD_ISNULL
+
+class ResumeGuardClassDescr(ResumeGuardDescr):
+    guard_opnum = rop.GUARD_CLASS
+
+class ResumeGuardTrueDescr(ResumeGuardDescr):
+    guard_opnum = rop.GUARD_TRUE
+
+class ResumeGuardFalseDescr(ResumeGuardDescr):
+    guard_opnum = rop.GUARD_FALSE
+
+class ResumeGuardNonnullClassDescr(ResumeGuardDescr):
+    guard_opnum = rop.GUARD_NONNULL_CLASS
+
+class ResumeGuardExceptionDescr(ResumeGuardDescr):
+    guard_opnum = rop.GUARD_EXCEPTION
+
+class ResumeGuardNoExceptionDescr(ResumeGuardDescr):
+    guard_opnum = rop.GUARD_NO_EXCEPTION
+
+class ResumeGuardOverflowDescr(ResumeGuardDescr):
+    guard_opnum = rop.GUARD_OVERFLOW
+
+class ResumeGuardNoOverflowDescr(ResumeGuardDescr):
+    guard_opnum = rop.GUARD_NO_OVERFLOW
+
+class ResumeGuardValueDescr(ResumeGuardDescr):
+    guard_opnum = rop.GUARD_VALUE
 
 class ResumeGuardNotInvalidated(ResumeGuardDescr):
-    def _clone_if_mutable(self, memo):
-        res = ResumeGuardNotInvalidated()
-        self.copy_all_attributes_into(res, memo)
-        return res
+    guard_opnum = rop.GUARD_NOT_INVALIDATED
 
 class ResumeAtPositionDescr(ResumeGuardDescr):
-    def _clone_if_mutable(self, memo):
-        res = ResumeAtPositionDescr()
-        self.copy_all_attributes_into(res, memo)
-        return res
+    guard_opnum = rop.GUARD_FUTURE_CONDITION
 
 class AllVirtuals:
     llopaque = True
@@ -693,8 +717,10 @@
 
 
 class ResumeGuardForcedDescr(ResumeGuardDescr):
+    guard_opnum = rop.GUARD_NOT_FORCED
 
-    def __init__(self, metainterp_sd, jitdriver_sd):
+    def _init(self, metainterp_sd, jitdriver_sd):
+        # to please the annotator
         self.metainterp_sd = metainterp_sd
         self.jitdriver_sd = jitdriver_sd
 
@@ -755,12 +781,39 @@
         hidden_all_virtuals = obj.hide(metainterp_sd.cpu)
         metainterp_sd.cpu.set_savedata_ref(deadframe, hidden_all_virtuals)
 
-    def _clone_if_mutable(self, memo):
-        res = ResumeGuardForcedDescr(self.metainterp_sd,
-                                     self.jitdriver_sd)
-        self.copy_all_attributes_into(res, memo)
-        return res
-
+def invent_fail_descr_for_op(opnum, optimizer):
+    if opnum == rop.GUARD_NOT_FORCED or opnum == rop.GUARD_NOT_FORCED_2:
+        resumedescr = ResumeGuardForcedDescr()
+        resumedescr._init(optimizer.metainterp_sd, optimizer.jitdriver_sd)
+    elif opnum == rop.GUARD_NOT_INVALIDATED:
+        resumedescr = ResumeGuardNotInvalidated()
+    elif opnum == rop.GUARD_FUTURE_CONDITION:
+        resumedescr = ResumeAtPositionDescr()
+    elif opnum == rop.GUARD_VALUE:
+        resumedescr = ResumeGuardValueDescr()
+    elif opnum == rop.GUARD_NONNULL:
+        resumedescr = ResumeGuardNonnullDescr()
+    elif opnum == rop.GUARD_ISNULL:
+        resumedescr = ResumeGuardIsnullDescr()
+    elif opnum == rop.GUARD_NONNULL_CLASS:
+        resumedescr = ResumeGuardNonnullClassDescr()
+    elif opnum == rop.GUARD_CLASS:
+        resumedescr = ResumeGuardClassDescr()
+    elif opnum == rop.GUARD_TRUE:
+        resumedescr = ResumeGuardTrueDescr()
+    elif opnum == rop.GUARD_FALSE:
+        resumedescr = ResumeGuardFalseDescr()
+    elif opnum == rop.GUARD_EXCEPTION:
+        resumedescr = ResumeGuardExceptionDescr()
+    elif opnum == rop.GUARD_NO_EXCEPTION:
+        resumedescr = ResumeGuardNoExceptionDescr()
+    elif opnum == rop.GUARD_OVERFLOW:
+        resumedescr = ResumeGuardOverflowDescr()
+    elif opnum == rop.GUARD_NO_OVERFLOW:
+        resumedescr = ResumeGuardNoOverflowDescr()
+    else:
+        assert False
+    return resumedescr
 
 class ResumeFromInterpDescr(ResumeDescr):
     def __init__(self, original_greenkey):
@@ -796,19 +849,18 @@
     # it does not work -- i.e. none of the existing old_loop_tokens match.
     new_trace = create_empty_loop(metainterp)
     new_trace.inputargs = metainterp.history.inputargs[:]
-    # clone ops, as optimize_bridge can mutate the ops
 
-    memo = Memo()
-    new_trace.operations = [op.clone(memo) for op in
-                            metainterp.history.operations]
+    new_trace.operations = metainterp.history.operations[:]
     metainterp_sd = metainterp.staticdata
-    state = metainterp.jitdriver_sd.warmstate
+    jitdriver_sd = metainterp.jitdriver_sd
+    state = jitdriver_sd.warmstate
     if isinstance(resumekey, ResumeAtPositionDescr):
         inline_short_preamble = False
     else:
         inline_short_preamble = True
     try:
-        state = optimize_trace(metainterp_sd, new_trace, state.enable_opts,
+        state = optimize_trace(metainterp_sd, jitdriver_sd, new_trace,
+                               state.enable_opts,
                                inline_short_preamble, export_state=True)
     except InvalidLoop:
         debug_print("compile_new_bridge: got an InvalidLoop")
diff --git a/rpython/jit/metainterp/history.py b/rpython/jit/metainterp/history.py
--- a/rpython/jit/metainterp/history.py
+++ b/rpython/jit/metainterp/history.py
@@ -124,14 +124,6 @@
     def repr_of_descr(self):
         return '%r' % (self,)
 
-    def _clone_if_mutable(self, memo):
-        return self
-    def clone_if_mutable(self, memo):
-        clone = self._clone_if_mutable(memo)
-        if not we_are_translated():
-            assert clone.__class__ is self.__class__
-        return clone
-
     def hide(self, cpu):
         descr_ptr = cpu.ts.cast_instance_to_base_ref(self)
         return cpu.ts.cast_to_ref(descr_ptr)
@@ -149,6 +141,8 @@
     index = -1
     final_descr = False
 
+    _attrs_ = ('adr_jump_offset', 'rd_locs', 'rd_loop_token')
+
     def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
         raise NotImplementedError
     def compile_and_attach(self, metainterp, new_loop):
diff --git a/rpython/jit/metainterp/optimizeopt/__init__.py b/rpython/jit/metainterp/optimizeopt/__init__.py
--- a/rpython/jit/metainterp/optimizeopt/__init__.py
+++ b/rpython/jit/metainterp/optimizeopt/__init__.py
@@ -47,7 +47,7 @@
 
     return optimizations, unroll
 
-def optimize_trace(metainterp_sd, loop, enable_opts,
+def optimize_trace(metainterp_sd, jitdriver_sd, loop, enable_opts,
                    inline_short_preamble=True, start_state=None,
                    export_state=True):
     """Optimize loop.operations to remove internal overheadish operations.
@@ -59,11 +59,13 @@
                                                           loop.operations)
         optimizations, unroll = build_opt_chain(metainterp_sd, enable_opts)
         if unroll:
-            return optimize_unroll(metainterp_sd, loop, optimizations,
+            return optimize_unroll(metainterp_sd, jitdriver_sd, loop,
+                                   optimizations,
                                    inline_short_preamble, start_state,
                                    export_state)
         else:
-            optimizer = Optimizer(metainterp_sd, loop, optimizations)
+            optimizer = Optimizer(metainterp_sd, jitdriver_sd, loop,
+                                  optimizations)
             optimizer.propagate_all_forward()
     finally:
         debug_stop("jit-optimize")
diff --git a/rpython/jit/metainterp/optimizeopt/generalize.py b/rpython/jit/metainterp/optimizeopt/generalize.py
--- a/rpython/jit/metainterp/optimizeopt/generalize.py
+++ b/rpython/jit/metainterp/optimizeopt/generalize.py
@@ -1,4 +1,5 @@
-from rpython.jit.metainterp.optimizeopt.optimizer import MININT, MAXINT
+from rpython.jit.metainterp.optimizeopt.optimizer import MININT, MAXINT,\
+     IntOptValue
 
 
 class GeneralizationStrategy(object):
@@ -14,7 +15,8 @@
         for v in self.optimizer.values.values():
             if v.is_constant():
                 continue
-            if v.intbound.lower < MININT / 2:
-                v.intbound.lower = MININT
-            if v.intbound.upper > MAXINT / 2:
-                v.intbound.upper = MAXINT
+            if isinstance(v, IntOptValue):
+                if v.intbound.lower < MININT / 2:
+                    v.intbound.lower = MININT
+                if v.intbound.upper > MAXINT / 2:
+                    v.intbound.upper = MAXINT
diff --git a/rpython/jit/metainterp/optimizeopt/heap.py b/rpython/jit/metainterp/optimizeopt/heap.py
--- a/rpython/jit/metainterp/optimizeopt/heap.py
+++ b/rpython/jit/metainterp/optimizeopt/heap.py
@@ -4,7 +4,8 @@
 from rpython.jit.metainterp.optimizeopt.util import args_dict
 from rpython.jit.metainterp.history import Const
 from rpython.jit.metainterp.jitexc import JitException
-from rpython.jit.metainterp.optimizeopt.optimizer import Optimization, MODE_ARRAY, LEVEL_KNOWNCLASS, REMOVED
+from rpython.jit.metainterp.optimizeopt.optimizer import Optimization,\
+     MODE_ARRAY, LEVEL_KNOWNCLASS, REMOVED
 from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
 from rpython.jit.metainterp.optimize import InvalidLoop
 from rpython.jit.metainterp.resoperation import rop, ResOperation, OpHelpers
@@ -63,6 +64,17 @@
             # cancelling its previous effects with no side effect.
             self._lazy_setfield = None
 
+    def value_updated(self, oldvalue, newvalue):
+        try:
+            fieldvalue = self._cached_fields[oldvalue]
+        except KeyError:
+            pass
+        else:
+            self._cached_fields[newvalue] = fieldvalue
+            op = self._cached_fields_getfield_op[oldvalue].clone()
+            op.setarg(0, newvalue.box)
+            self._cached_fields_getfield_op[newvalue] = op
+
     def possible_aliasing(self, optheap, structvalue):
         # If lazy_setfield is set and contains a setfield on a different
         # structvalue, then we are annoyed, because it may point to either
@@ -82,10 +94,12 @@
         else:
             return self._cached_fields.get(structvalue, None)
 
-    def remember_field_value(self, structvalue, fieldvalue, getfield_op=None):
+    def remember_field_value(self, structvalue, fieldvalue, op=None,
+                             optimizer=None):
         assert self._lazy_setfield is None
         self._cached_fields[structvalue] = fieldvalue
-        self._cached_fields_getfield_op[structvalue] = getfield_op
+        op = optimizer.get_op_replacement(op)
+        self._cached_fields_getfield_op[structvalue] = op
 
     def force_lazy_setfield(self, optheap, can_cache=True):
         op = self._lazy_setfield
@@ -109,7 +123,8 @@
             # field.
             structvalue = optheap.getvalue(op.getarg(0))
             fieldvalue = optheap.getvalue(op.getarglist()[-1])
-            self.remember_field_value(structvalue, fieldvalue, op)
+            self.remember_field_value(structvalue, fieldvalue, op,
+                                      optheap.optimizer)
         elif not can_cache:
             self.clear()
 
@@ -117,18 +132,6 @@
         self._cached_fields.clear()
         self._cached_fields_getfield_op.clear()
 
-    def turned_constant(self, newvalue, value):
-        if newvalue not in self._cached_fields and value in self._cached_fields:
-            self._cached_fields[newvalue] = self._cached_fields[value]
-            op = self._cached_fields_getfield_op[value].clone()
-            constbox = value.box
-            assert isinstance(constbox, Const)
-            op.setarg(0, constbox)
-            self._cached_fields_getfield_op[newvalue] = op
-        for structvalue in self._cached_fields.keys():
-            if self._cached_fields[structvalue] is value:
-                self._cached_fields[structvalue] = newvalue
-
     def produce_potential_short_preamble_ops(self, optimizer, shortboxes, descr):
         if self._lazy_setfield is not None:
             return
@@ -138,7 +141,7 @@
                 continue
             value = optimizer.getvalue(op.getarg(0))
             if value in optimizer.opaque_pointers:
-                if value.level < LEVEL_KNOWNCLASS:
+                if value.getlevel() < LEVEL_KNOWNCLASS:
                     continue
                 if op.getopnum() != rop.SETFIELD_GC and op.getopnum() != rop.GETFIELD_GC:
                     continue
@@ -191,6 +194,17 @@
         self._seen_guard_not_invalidated = False
         self.postponed_op = None
 
+    def setup(self):
+        self.optimizer.optheap = self
+
+    def value_updated(self, oldvalue, newvalue):
+        # XXXX very unhappy about that
+        for cf in self.cached_fields.itervalues():
+            cf.value_updated(oldvalue, newvalue)
+        for submap in self.cached_arrayitems.itervalues():
+            for cf in submap.itervalues():
+                cf.value_updated(oldvalue, newvalue)
+
     def force_at_end_of_preamble(self):
         self.cached_dict_reads.clear()
         self.corresponding_array_descrs.clear()
@@ -363,16 +377,6 @@
             # ^^^ we only need to force this field; the other fields
             # of virtualref_info and virtualizable_info are not gcptrs.
 
-    def turned_constant(self, value):
-        assert value.is_constant()
-        newvalue = self.getvalue(value.box)
-        if value is not newvalue:
-            for cf in self.cached_fields.itervalues():
-                cf.turned_constant(newvalue, value)
-            for submap in self.cached_arrayitems.itervalues():
-                for cf in submap.itervalues():
-                    cf.turned_constant(newvalue, value)
-
     def force_lazy_setfield(self, descr, can_cache=True):
         try:
             cf = self.cached_fields[descr]
@@ -386,7 +390,7 @@
         except KeyError:
             return
         for idx, cf in submap.iteritems():
-            if indexvalue is None or indexvalue.intbound.contains(idx):
+            if indexvalue is None or indexvalue.getintbound().contains(idx):
                 cf.force_lazy_setfield(self, can_cache)
 
     def _assert_valid_cf(self, cf):
@@ -448,7 +452,7 @@
         self.emit_operation(op)
         # then remember the result of reading the field
         fieldvalue = self.getvalue(op)
-        cf.remember_field_value(structvalue, fieldvalue, op)
+        cf.remember_field_value(structvalue, fieldvalue, op, self.optimizer)
     optimize_GETFIELD_GC_R = optimize_GETFIELD_GC_I
     optimize_GETFIELD_GC_F = optimize_GETFIELD_GC_I
 
@@ -497,7 +501,7 @@
         # the remember the result of reading the array item
         if cf is not None:
             fieldvalue = self.getvalue(op)
-            cf.remember_field_value(arrayvalue, fieldvalue, op)
+            cf.remember_field_value(arrayvalue, fieldvalue, op, self.optimizer)
     optimize_GETARRAYITEM_GC_R = optimize_GETARRAYITEM_GC_I
     optimize_GETARRAYITEM_GC_F = optimize_GETARRAYITEM_GC_I
 
diff --git a/rpython/jit/metainterp/optimizeopt/intbounds.py b/rpython/jit/metainterp/optimizeopt/intbounds.py
--- a/rpython/jit/metainterp/optimizeopt/intbounds.py
+++ b/rpython/jit/metainterp/optimizeopt/intbounds.py
@@ -4,7 +4,7 @@
 from rpython.jit.metainterp.optimizeopt.intutils import (IntBound, IntLowerBound,
     IntUpperBound)
 from rpython.jit.metainterp.optimizeopt.optimizer import (Optimization, CONST_1,
-    CONST_0, MODE_ARRAY, MODE_STR, MODE_UNICODE)
+    CONST_0, MODE_ARRAY, MODE_STR, MODE_UNICODE, IntOptValue)
 from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
 from rpython.jit.metainterp.resoperation import rop, AbstractResOp
 from rpython.jit.backend.llsupport import symbolic
@@ -53,7 +53,9 @@
         # FIXME: This takes care of the instruction where box is the reuslt
         #        but the bounds produced by all instructions where box is
         #        an argument might also be tighten
-        b = v.intbound
+        b = v.getintbound()
+        if b is None:
+            return # pointer
         if b.has_lower and b.has_upper and b.lower == b.upper:
             v.make_constant(ConstInt(b.lower))
 
@@ -78,11 +80,13 @@
                 self.make_constant_int(op, 0)
             return
         self.emit_operation(op)
-        if v1.intbound.known_ge(IntBound(0, 0)) and \
-           v2.intbound.known_ge(IntBound(0, 0)):
-            r = self.getvalue(op)
-            mostsignificant = v1.intbound.upper | v2.intbound.upper
-            r.intbound.intersect(IntBound(0, next_pow2_m1(mostsignificant)))
+        bound1 = v1.getintbound()
+        bound2 = v2.getintbound()
+        if bound1.known_ge(IntBound(0, 0)) and \
+           bound2.known_ge(IntBound(0, 0)):
+            r = self.getvalue(op).getintbound()
+            mostsignificant = bound1.upper | bound2.upper
+            r.intersect(IntBound(0, next_pow2_m1(mostsignificant)))
 
     optimize_INT_OR = optimize_INT_OR_or_XOR
     optimize_INT_XOR = optimize_INT_OR_or_XOR
@@ -96,55 +100,55 @@
         if v2.is_constant():
             val = v2.box.getint()
             if val >= 0:
-                r.intbound.intersect(IntBound(0, val))
+                r.getintbound().intersect(IntBound(0, val))
         elif v1.is_constant():
             val = v1.box.getint()
             if val >= 0:
-                r.intbound.intersect(IntBound(0, val))
-        elif v1.intbound.known_ge(IntBound(0, 0)) and \
-          v2.intbound.known_ge(IntBound(0, 0)):
-            lesser = min(v1.intbound.upper, v2.intbound.upper)
-            r.intbound.intersect(IntBound(0, next_pow2_m1(lesser)))
+                r.getintbound().intersect(IntBound(0, val))
+        elif v1.getintbound().known_ge(IntBound(0, 0)) and \
+          v2.getintbound().known_ge(IntBound(0, 0)):
+            lesser = min(v1.getintbound().upper, v2.getintbound().upper)
+            r.getintbound().intersect(IntBound(0, next_pow2_m1(lesser)))
 
     def optimize_INT_SUB(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
         self.emit_operation(op)
         r = self.getvalue(op)
-        b = v1.intbound.sub_bound(v2.intbound)
+        b = v1.getintbound().sub_bound(v2.getintbound())
         if b.bounded():
-            r.intbound.intersect(b)
+            r.getintbound().intersect(b)
 
     def optimize_INT_ADD(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
         self.emit_operation(op)
         r = self.getvalue(op)
-        b = v1.intbound.add_bound(v2.intbound)
+        b = v1.getintbound().add_bound(v2.getintbound())
         if b.bounded():
-            r.intbound.intersect(b)
+            r.getintbound().intersect(b)
 
     def optimize_INT_MUL(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
         self.emit_operation(op)
         r = self.getvalue(op)
-        b = v1.intbound.mul_bound(v2.intbound)
+        b = v1.getintbound().mul_bound(v2.getintbound())
         if b.bounded():
-            r.intbound.intersect(b)
+            r.getintbound().intersect(b)
 
     def optimize_INT_FLOORDIV(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
         self.emit_operation(op)
         r = self.getvalue(op)
-        r.intbound.intersect(v1.intbound.div_bound(v2.intbound))
+        r.getintbound().intersect(v1.getintbound().div_bound(v2.getintbound()))
 
     def optimize_INT_MOD(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
-        known_nonneg = (v1.intbound.known_ge(IntBound(0, 0)) and
-                        v2.intbound.known_ge(IntBound(0, 0)))
+        known_nonneg = (v1.getintbound().known_ge(IntBound(0, 0)) and
+                        v2.getintbound().known_ge(IntBound(0, 0)))
         if known_nonneg and v2.is_constant():
             val = v2.box.getint()
             if (val & (val-1)) == 0:
@@ -162,18 +166,18 @@
                     return     # give up
                 val = -val
             if known_nonneg:
-                r.intbound.make_ge(IntBound(0, 0))
+                r.getintbound().make_ge(IntBound(0, 0))
             else:
-                r.intbound.make_gt(IntBound(-val, -val))
-            r.intbound.make_lt(IntBound(val, val))
+                r.getintbound().make_gt(IntBound(-val, -val))
+            r.getintbound().make_lt(IntBound(val, val))
 
     def optimize_INT_LSHIFT(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
         self.emit_operation(op)
         r = self.getvalue(op)
-        b = v1.intbound.lshift_bound(v2.intbound)
-        r.intbound.intersect(b)
+        b = v1.getintbound().lshift_bound(v2.getintbound())
+        r.getintbound().intersect(b)
         # intbound.lshift_bound checks for an overflow and if the
         # lshift can be proven not to overflow sets b.has_upper and
         # b.has_lower
@@ -184,14 +188,14 @@
     def optimize_INT_RSHIFT(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
-        b = v1.intbound.rshift_bound(v2.intbound)
+        b = v1.getintbound().rshift_bound(v2.getintbound())
         if b.has_lower and b.has_upper and b.lower == b.upper:
             # constant result (likely 0, for rshifts that kill all bits)
             self.make_constant_int(op, b.lower)
         else:
             self.emit_operation(op)
             r = self.getvalue(op)
-            r.intbound.intersect(b)
+            r.getintbound().intersect(b)
 
     def optimize_GUARD_NO_OVERFLOW(self, op):
         lastop = self.last_emitted_operation
@@ -236,17 +240,16 @@
     def optimize_INT_ADD_OVF(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
-        resbound = v1.intbound.add_bound(v2.intbound)
+        resbound = v1.getintbound().add_bound(v2.getintbound())
         if resbound.bounded():
             # Transform into INT_ADD.  The following guard will be killed
             # by optimize_GUARD_NO_OVERFLOW; if we see instead an
             # optimize_GUARD_OVERFLOW, then InvalidLoop.
-            newop = self.replace_op_with(op, rop.INT_ADD)
-        else:
-            newop = op
-        self.emit_operation(newop) # emit the op
+            xxx
+            op = op.copy_and_change(rop.INT_ADD)
+        self.emit_operation(op) # emit the op
         r = self.getvalue(op)
-        r.intbound.intersect(resbound)
+        r.getintbound().intersect(resbound)
 
     def optimize_INT_SUB_OVF(self, op):
         v1 = self.getvalue(op.getarg(0))
@@ -254,31 +257,30 @@
         if v1 is v2:
             self.make_constant_int(op, 0)
             return
-        resbound = v1.intbound.sub_bound(v2.intbound)
+        resbound = v1.getintbound().sub_bound(v2.getintbound())
         if resbound.bounded():
-            newop = self.replace_op_with(op, rop.INT_SUB)
-        else:
-            newop = op
-        self.emit_operation(newop) # emit the op
+            xxx
+            op = op.copy_and_change(rop.INT_SUB)
+        self.emit_operation(op) # emit the op
         r = self.getvalue(op)
-        r.intbound.intersect(resbound)
+        r.getintbound().intersect(resbound)
 
     def optimize_INT_MUL_OVF(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
-        resbound = v1.intbound.mul_bound(v2.intbound)
+        resbound = v1.getintbound().mul_bound(v2.getintbound())
         if resbound.bounded():
             op = self.replace_op_with(op, rop.INT_MUL)
         self.emit_operation(op)
         r = self.getvalue(op)
-        r.intbound.intersect(resbound)
+        r.getintbound().intersect(resbound)
 
     def optimize_INT_LT(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
-        if v1.intbound.known_lt(v2.intbound):
+        if v1.getintbound().known_lt(v2.getintbound()):
             self.make_constant_int(op, 1)
-        elif v1.intbound.known_ge(v2.intbound) or v1 is v2:
+        elif v1.getintbound().known_ge(v2.getintbound()) or v1 is v2:
             self.make_constant_int(op, 0)
         else:
             self.emit_operation(op)
@@ -286,9 +288,9 @@
     def optimize_INT_GT(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
-        if v1.intbound.known_gt(v2.intbound):
+        if v1.getintbound().known_gt(v2.getintbound()):
             self.make_constant_int(op, 1)
-        elif v1.intbound.known_le(v2.intbound) or v1 is v2:
+        elif v1.getintbound().known_le(v2.getintbound()) or v1 is v2:
             self.make_constant_int(op, 0)
         else:
             self.emit_operation(op)
@@ -296,9 +298,9 @@
     def optimize_INT_LE(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
-        if v1.intbound.known_le(v2.intbound) or v1 is v2:
+        if v1.getintbound().known_le(v2.getintbound()) or v1 is v2:
             self.make_constant_int(op, 1)
-        elif v1.intbound.known_gt(v2.intbound):
+        elif v1.getintbound().known_gt(v2.getintbound()):
             self.make_constant_int(op, 0)
         else:
             self.emit_operation(op)
@@ -306,9 +308,9 @@
     def optimize_INT_GE(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
-        if v1.intbound.known_ge(v2.intbound) or v1 is v2:
+        if v1.getintbound().known_ge(v2.getintbound()) or v1 is v2:
             self.make_constant_int(op, 1)
-        elif v1.intbound.known_lt(v2.intbound):
+        elif v1.getintbound().known_lt(v2.getintbound()):
             self.make_constant_int(op, 0)
         else:
             self.emit_operation(op)
@@ -316,9 +318,9 @@
     def optimize_INT_EQ(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
-        if v1.intbound.known_gt(v2.intbound):
+        if v1.getintbound().known_gt(v2.getintbound()):
             self.make_constant_int(op, 0)
-        elif v1.intbound.known_lt(v2.intbound):
+        elif v1.getintbound().known_lt(v2.getintbound()):
             self.make_constant_int(op, 0)
         elif v1 is v2:
             self.make_constant_int(op, 1)
@@ -328,9 +330,9 @@
     def optimize_INT_NE(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
-        if v1.intbound.known_gt(v2.intbound):
+        if v1.getintbound().known_gt(v2.getintbound()):
             self.make_constant_int(op, 1)
-        elif v1.intbound.known_lt(v2.intbound):
+        elif v1.getintbound().known_lt(v2.getintbound()):
             self.make_constant_int(op, 1)
         elif v1 is v2:
             self.make_constant_int(op, 0)
@@ -339,7 +341,7 @@
 
     def optimize_INT_FORCE_GE_ZERO(self, op):
         value = self.getvalue(op.getarg(0))
-        if value.intbound.known_ge(IntBound(0, 0)):
+        if value.getintbound().known_ge(IntBound(0, 0)):
             self.make_equal_to(op, value)
         else:
             self.emit_operation(op)
@@ -350,50 +352,53 @@
         start = -(1 << (numbits - 1))
         stop = 1 << (numbits - 1)
         bounds = IntBound(start, stop - 1)
-        if bounds.contains_bound(value.intbound):
+        if bounds.contains_bound(value.getintbound()):
             self.make_equal_to(op, value)
         else:
             self.emit_operation(op)
             vres = self.getvalue(op)
-            vres.intbound.intersect(bounds)
+            vres.getintbound().intersect(bounds)
 
     def optimize_ARRAYLEN_GC(self, op):
         self.emit_operation(op)
         array = self.getvalue(op.getarg(0))
         result = self.getvalue(op)
         array.make_len_gt(MODE_ARRAY, op.getdescr(), -1)
-        array.lenbound.bound.intersect(result.intbound)
-        result.intbound = array.lenbound.bound
+        array.getlenbound().bound.intersect(result.getintbound())
+        assert isinstance(result, IntOptValue)
+        result.intbound = array.getlenbound().bound
 
     def optimize_STRLEN(self, op):
         self.emit_operation(op)
         array = self.getvalue(op.getarg(0))
         result = self.getvalue(op)
         array.make_len_gt(MODE_STR, op.getdescr(), -1)
-        array.lenbound.bound.intersect(result.intbound)
-        result.intbound = array.lenbound.bound
+        array.getlenbound().bound.intersect(result.getintbound())
+        assert isinstance(result, IntOptValue)
+        result.intbound = array.getlenbound().bound
 
     def optimize_UNICODELEN(self, op):
         self.emit_operation(op)
         array = self.getvalue(op.getarg(0))
         result = self.getvalue(op)
         array.make_len_gt(MODE_UNICODE, op.getdescr(), -1)
-        array.lenbound.bound.intersect(result.intbound)
-        result.intbound = array.lenbound.bound
+        array.getlenbound().bound.intersect(result.getintbound())
+        assert isinstance(result, IntOptValue)        
+        result.intbound = array.getlenbound().bound
 
     def optimize_STRGETITEM(self, op):
         self.emit_operation(op)
         v1 = self.getvalue(op)
-        v1.intbound.make_ge(IntLowerBound(0))
-        v1.intbound.make_lt(IntUpperBound(256))
+        v1.getintbound().make_ge(IntLowerBound(0))
+        v1.getintbound().make_lt(IntUpperBound(256))
 
     def optimize_GETFIELD_RAW_I(self, op):
         self.emit_operation(op)
         descr = op.getdescr()
         if descr.is_integer_bounded():
             v1 = self.getvalue(op)
-            v1.intbound.make_ge(IntLowerBound(descr.get_integer_min()))
-            v1.intbound.make_le(IntUpperBound(descr.get_integer_max()))
+            v1.getintbound().make_ge(IntLowerBound(descr.get_integer_min()))
+            v1.getintbound().make_le(IntUpperBound(descr.get_integer_max()))
 
     optimize_GETFIELD_RAW_F = optimize_GETFIELD_RAW_I
     optimize_GETFIELD_GC_I = optimize_GETFIELD_RAW_I
@@ -408,9 +413,9 @@
         self.emit_operation(op)
         descr = op.getdescr()
         if descr and descr.is_item_integer_bounded():
-            v1 = self.getvalue(op)
-            v1.intbound.make_ge(IntLowerBound(descr.get_item_integer_min()))
-            v1.intbound.make_le(IntUpperBound(descr.get_item_integer_max()))
+            intbound = self.getvalue(op).getintbound()
+            intbound.make_ge(IntLowerBound(descr.get_item_integer_min()))
+            intbound.make_le(IntUpperBound(descr.get_item_integer_max()))
 
     optimize_GETARRAYITEM_RAW_F = optimize_GETARRAYITEM_RAW_I
     optimize_GETARRAYITEM_GC_I = optimize_GETARRAYITEM_RAW_I
@@ -420,22 +425,22 @@
     def optimize_UNICODEGETITEM(self, op):
         self.emit_operation(op)
         v1 = self.getvalue(op)
-        v1.intbound.make_ge(IntLowerBound(0))
+        v1.getintbound().make_ge(IntLowerBound(0))
 
     def make_int_lt(self, box1, box2):
         v1 = self.getvalue(box1)
         v2 = self.getvalue(box2)
-        if v1.intbound.make_lt(v2.intbound):
+        if v1.getintbound().make_lt(v2.getintbound()):
             self.propagate_bounds_backward(box1, v1)
-        if v2.intbound.make_gt(v1.intbound):
+        if v2.getintbound().make_gt(v1.getintbound()):
             self.propagate_bounds_backward(box2, v2)
 
     def make_int_le(self, box1, box2):
         v1 = self.getvalue(box1)
         v2 = self.getvalue(box2)
-        if v1.intbound.make_le(v2.intbound):
+        if v1.getintbound().make_le(v2.getintbound()):
             self.propagate_bounds_backward(box1, v1)
-        if v2.intbound.make_ge(v1.intbound):
+        if v2.getintbound().make_ge(v1.getintbound()):
             self.propagate_bounds_backward(box2, v2)
 
     def make_int_gt(self, box1, box2):
@@ -482,9 +487,9 @@
             if r.box.same_constant(CONST_1):
                 v1 = self.getvalue(op.getarg(0))
                 v2 = self.getvalue(op.getarg(1))
-                if v1.intbound.intersect(v2.intbound):
+                if v1.getintbound().intersect(v2.getintbound()):
                     self.propagate_bounds_backward(op.getarg(0), v1)
-                if v2.intbound.intersect(v1.intbound):
+                if v2.getintbound().intersect(v1.getintbound()):
                     self.propagate_bounds_backward(op.getarg(1), v2)
 
     def propagate_bounds_INT_NE(self, op):
@@ -493,9 +498,9 @@
             if r.box.same_constant(CONST_0):
                 v1 = self.getvalue(op.getarg(0))
                 v2 = self.getvalue(op.getarg(1))
-                if v1.intbound.intersect(v2.intbound):
+                if v1.getintbound().intersect(v2.getintbound()):
                     self.propagate_bounds_backward(op.getarg(0), v1)
-                if v2.intbound.intersect(v1.intbound):
+                if v2.getintbound().intersect(v1.getintbound()):
                     self.propagate_bounds_backward(op.getarg(1), v2)
 
     def propagate_bounds_INT_IS_TRUE(self, op):
@@ -503,8 +508,8 @@
         if r.is_constant():
             if r.box.same_constant(CONST_1):
                 v1 = self.getvalue(op.getarg(0))
-                if v1.intbound.known_ge(IntBound(0, 0)):
-                    v1.intbound.make_gt(IntBound(0, 0))
+                if v1.getintbound().known_ge(IntBound(0, 0)):
+                    v1.getintbound().make_gt(IntBound(0, 0))
                     self.propagate_bounds_backward(op.getarg(0), v1)
 
     def propagate_bounds_INT_IS_ZERO(self, op):
@@ -515,50 +520,50 @@
                 # Clever hack, we can't use self.make_constant_int yet because
                 # the args aren't in the values dictionary yet so it runs into
                 # an assert, this is a clever way of expressing the same thing.
-                v1.intbound.make_ge(IntBound(0, 0))
-                v1.intbound.make_lt(IntBound(1, 1))
+                v1.getintbound().make_ge(IntBound(0, 0))
+                v1.getintbound().make_lt(IntBound(1, 1))
                 self.propagate_bounds_backward(op.getarg(0), v1)
 
     def propagate_bounds_INT_ADD(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
-        r = self.getvalue(op)
-        b = r.intbound.sub_bound(v2.intbound)
-        if v1.intbound.intersect(b):
+        r = self.getvalue(op.result)
+        b = r.getintbound().sub_bound(v2.getintbound())
+        if v1.getintbound().intersect(b):
             self.propagate_bounds_backward(op.getarg(0), v1)
-        b = r.intbound.sub_bound(v1.intbound)
-        if v2.intbound.intersect(b):
+        b = r.getintbound().sub_bound(v1.getintbound())
+        if v2.getintbound().intersect(b):
             self.propagate_bounds_backward(op.getarg(1), v2)
 
     def propagate_bounds_INT_SUB(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
-        r = self.getvalue(op)
-        b = r.intbound.add_bound(v2.intbound)
-        if v1.intbound.intersect(b):
+        r = self.getvalue(op.result)
+        b = r.getintbound().add_bound(v2.getintbound())
+        if v1.getintbound().intersect(b):
             self.propagate_bounds_backward(op.getarg(0), v1)
-        b = r.intbound.sub_bound(v1.intbound).mul(-1)
-        if v2.intbound.intersect(b):
+        b = r.getintbound().sub_bound(v1.getintbound()).mul(-1)
+        if v2.getintbound().intersect(b):
             self.propagate_bounds_backward(op.getarg(1), v2)
 
     def propagate_bounds_INT_MUL(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
-        r = self.getvalue(op)
-        b = r.intbound.div_bound(v2.intbound)
-        if v1.intbound.intersect(b):
+        r = self.getvalue(op.result)
+        b = r.getintbound().div_bound(v2.getintbound())
+        if v1.getintbound().intersect(b):
             self.propagate_bounds_backward(op.getarg(0), v1)
-        b = r.intbound.div_bound(v1.intbound)
-        if v2.intbound.intersect(b):
+        b = r.getintbound().div_bound(v1.getintbound())
+        if v2.getintbound().intersect(b):
             self.propagate_bounds_backward(op.getarg(1), v2)
 
     def propagate_bounds_INT_LSHIFT(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
         r = self.getvalue(op)
-        b = r.intbound.rshift_bound(v2.intbound)
-        if v1.intbound.intersect(b):
-            self.propagate_bounds_backward(op.getarg(0))
+        b = r.getintbound().rshift_bound(v2.getintbound())
+        if v1.getintbound().intersect(b):
+            self.propagate_bounds_backward(op.getarg(0), v1)
 
     propagate_bounds_INT_ADD_OVF = propagate_bounds_INT_ADD
     propagate_bounds_INT_SUB_OVF = propagate_bounds_INT_SUB
diff --git a/rpython/jit/metainterp/optimizeopt/optimizer.py b/rpython/jit/metainterp/optimizeopt/optimizer.py
--- a/rpython/jit/metainterp/optimizeopt/optimizer.py
+++ b/rpython/jit/metainterp/optimizeopt/optimizer.py
@@ -4,20 +4,26 @@
 from rpython.jit.metainterp.history import Const, ConstInt, REF
 from rpython.jit.metainterp.optimizeopt.intutils import IntBound, IntUnbounded, \
                                                      ImmutableIntUnbounded, \
-                                                     IntLowerBound, MININT, MAXINT
+                                                     IntLowerBound, MININT,\
+                                                     MAXINT
 from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
 from rpython.jit.metainterp.resoperation import rop, ResOperation,\
-     AbstractResOp, AbstractInputArg, DONT_CHANGE
+     AbstractResOp, AbstractInputArg, DONT_CHANGE, GuardResOp
 from rpython.jit.metainterp.typesystem import llhelper
 from rpython.tool.pairtype import extendabletype
 from rpython.rlib.debug import debug_print
 from rpython.rlib.objectmodel import specialize
 
+""" The tag field on OptValue has a following meaning:
 
-LEVEL_UNKNOWN    = '\x00'
-LEVEL_NONNULL    = '\x01'
-LEVEL_KNOWNCLASS = '\x02'     # might also mean KNOWNARRAYDESCR, for arrays
-LEVEL_CONSTANT   = '\x03'
+lower two bits are LEVEL
+next 16 bits is the position in the original list, 0 if unknown or a constant
+"""
+
+LEVEL_UNKNOWN    = 0
+LEVEL_NONNULL    = 1
+LEVEL_KNOWNCLASS = 2     # might also mean KNOWNARRAYDESCR, for arrays
+LEVEL_CONSTANT   = 3
 
 MODE_ARRAY   = '\x00'
 MODE_STR     = '\x01'
@@ -41,92 +47,49 @@
 
 class OptValue(object):
     __metaclass__ = extendabletype
-    _attrs_ = ('box', 'known_class', 'last_guard', 'level', 'intbound', 'lenbound')
-    last_guard = None
+    _attrs_ = ('box', '_tag')
 
-    level = LEVEL_UNKNOWN
-    known_class = None
-    intbound = ImmutableIntUnbounded()
-    lenbound = None
+    _tag = 0
 
     def __init__(self, box, level=None, known_class=None, intbound=None):
         self.box = box
         if level is not None:
-            self.level = level
-        self.known_class = known_class
-        if intbound:
-            self.intbound = intbound
-        else:
-            if box is not None and box.type == 'i':
-                self.intbound = IntBound(MININT, MAXINT)
-            else:
-                self.intbound = IntUnbounded()
+            self._tag = level
 
         if isinstance(box, Const):
             self.make_constant(box)
         # invariant: box is a Const if and only if level == LEVEL_CONSTANT
 
-    def make_len_gt(self, mode, descr, val):
-        if self.lenbound:
-            assert self.lenbound.mode == mode
-            assert self.lenbound.descr == descr
-            self.lenbound.bound.make_gt(IntBound(val, val))
+    def getlevel(self):
+        return self._tag & 0x3
+
+    def setlevel(self, level):
+        self._tag = (self._tag & (~0x3)) | level
+
+    def import_from(self, other, optimizer):
+        if self.getlevel() == LEVEL_CONSTANT:
+            assert other.getlevel() == LEVEL_CONSTANT
+            assert other.box.same_constant(self.box)
+            return
+        assert self.getlevel() <= LEVEL_NONNULL
+        if other.getlevel() == LEVEL_CONSTANT:
+            self.make_constant(other.get_key_box())
+        elif other.getlevel() == LEVEL_KNOWNCLASS:
+            self.make_constant_class(None, other.get_known_class())
         else:
-            self.lenbound = LenBound(mode, descr, IntLowerBound(val + 1))
+            if other.getlevel() == LEVEL_NONNULL:
+                self.ensure_nonnull()
 
     def make_guards(self, box):
-        guards = []
-        if self.level == LEVEL_CONSTANT:
+        if self.getlevel() == LEVEL_CONSTANT:
             op = ResOperation(rop.GUARD_VALUE, [box, self.box], None)
-            guards.append(op)
-        elif self.level == LEVEL_KNOWNCLASS:
-            op = ResOperation(rop.GUARD_NONNULL, [box], None)
-            guards.append(op)
-            op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
-            guards.append(op)
-        else:
-            if self.level == LEVEL_NONNULL:
-                op = ResOperation(rop.GUARD_NONNULL, [box], None)
-                guards.append(op)
-            self.intbound.make_guards(box, guards)
-            if self.lenbound:
-                lenbox = BoxInt()
-                if self.lenbound.mode == MODE_ARRAY:
-                    op = ResOperation(rop.ARRAYLEN_GC, [box], lenbox, self.lenbound.descr)
-                elif self.lenbound.mode == MODE_STR:
-                    op = ResOperation(rop.STRLEN, [box], lenbox, self.lenbound.descr)
-                elif self.lenbound.mode == MODE_UNICODE:
-                    op = ResOperation(rop.UNICODELEN, [box], lenbox, self.lenbound.descr)
-                else:
-                    debug_print("Unknown lenbound mode")
-                    assert False
-                guards.append(op)
-                self.lenbound.bound.make_guards(lenbox, guards)
-        return guards
+            return [op]
+        return []
 
-    def import_from(self, other, optimizer):
-        if self.level == LEVEL_CONSTANT:
-            assert other.level == LEVEL_CONSTANT
-            assert other.box.same_constant(self.box)
-            return
-        assert self.level <= LEVEL_NONNULL
-        if other.level == LEVEL_CONSTANT:
-            self.make_constant(other.get_key_box())
-            optimizer.turned_constant(self)
-        elif other.level == LEVEL_KNOWNCLASS:
-            self.make_constant_class(other.known_class, None)
-        else:
-            if other.level == LEVEL_NONNULL:
-                self.ensure_nonnull()
-            self.intbound.intersect(other.intbound)
-            if other.lenbound:
-                if self.lenbound:
-                    assert other.lenbound.mode == self.lenbound.mode
-                    assert other.lenbound.descr == self.lenbound.descr
-                    self.lenbound.bound.intersect(other.lenbound.bound)
-                else:
-                    self.lenbound = other.lenbound.clone()
-
+    def copy_from(self, other_value):
+        assert isinstance(other_value, OptValue)
+        self.box = other_value.box
+        self._tag = other_value._tag
 
     def force_box(self, optforce):
         return self.box
@@ -154,7 +117,7 @@
         assert 0, "unreachable"
 
     def is_constant(self):
-        return self.level == LEVEL_CONSTANT
+        return self.getlevel() == LEVEL_CONSTANT
 
     def is_null(self):
         if self.is_constant():
@@ -170,58 +133,20 @@
             return self.box.same_constant(other.box)
         return self is other
 
-    def make_constant(self, constbox):
-        """Replace 'self.box' with a Const box."""
-        assert isinstance(constbox, Const)
-        self.box = constbox
-        self.level = LEVEL_CONSTANT
-
-        if isinstance(constbox, ConstInt):
-            val = constbox.getint()
-            self.intbound = IntBound(val, val)
-        else:
-            self.intbound = IntUnbounded()
-
-    def get_constant_class(self, cpu):
-        level = self.level
-        if level == LEVEL_KNOWNCLASS:
-            return self.known_class
-        elif level == LEVEL_CONSTANT:
-            return cpu.ts.cls_of_box(self.box)
-        else:
-            return None
-
-    def make_constant_class(self, classbox, guardop):
-        assert self.level < LEVEL_KNOWNCLASS
-        self.known_class = classbox
-        self.level = LEVEL_KNOWNCLASS
-        self.last_guard = guardop
-
-    def make_nonnull(self, guardop):
-        assert self.level < LEVEL_NONNULL
-        self.level = LEVEL_NONNULL
-        self.last_guard = guardop
-
     def is_nonnull(self):
-        level = self.level
+        level = self.getlevel()
         if level == LEVEL_NONNULL or level == LEVEL_KNOWNCLASS:
             return True
         elif level == LEVEL_CONSTANT:
             box = self.box
             assert isinstance(box, Const)
             return box.nonnull()
-        elif self.intbound:
-            if self.intbound.known_gt(IntBound(0, 0)) or \
-               self.intbound.known_lt(IntBound(0, 0)):
-                return True
-            else:
-                return False
         else:
             return False
 
     def ensure_nonnull(self):
-        if self.level < LEVEL_NONNULL:
-            self.level = LEVEL_NONNULL
+        if self.getlevel() < LEVEL_NONNULL:
+            self.setlevel(LEVEL_NONNULL)
 
     def is_virtual(self):
         # Don't check this with 'isinstance(_, VirtualValue)'!
@@ -262,8 +187,224 @@
     def get_missing_null_value(self):
         raise NotImplementedError    # only for VArrayValue
 
+    def make_constant(self, constbox):
+        """Replace 'self.box' with a Const box."""
+        assert isinstance(constbox, Const)
+        self.box = constbox
+        self.setlevel(LEVEL_CONSTANT)
 
-class ConstantValue(OptValue):
+    def get_last_guard(self, optimizer):
+        return None
+
+    def get_known_class(self):
+        return None
+
+    def getlenbound(self):
+        return None
+
+    def getintbound(self):
+        return None
+
+    def get_constant_class(self, cpu):
+        return None
+
+class PtrOptValue(OptValue):
+    _attrs_ = ('known_class', 'last_guard_pos', 'lenbound')
+
+    known_class = None
+    last_guard_pos = -1
+    lenbound = None
+
+    def __init__(self, box, level=None, known_class=None, intbound=None):
+        OptValue.__init__(self, box, level, None, intbound)
+        if not isinstance(box, Const):
+            self.known_class = known_class
+
+    def copy_from(self, other_value):
+        assert isinstance(other_value, PtrOptValue)
+        self.box = other_value.box
+        self.known_class = other_value.known_class
+        self._tag = other_value._tag
+        self.last_guard_pos = other_value.last_guard_pos
+        self.lenbound = other_value.lenbound
+
+    def make_len_gt(self, mode, descr, val):
+        if self.lenbound:
+            assert self.lenbound.mode == mode
+            assert self.lenbound.descr == descr
+            self.lenbound.bound.make_gt(IntBound(val, val))
+        else:
+            self.lenbound = LenBound(mode, descr, IntLowerBound(val + 1))


More information about the pypy-commit mailing list