Author: Ronan Lamy <ronan.l...@gmail.com>
Branch: py3.5
Changeset: r91408:66108bb2353d
Date: 2017-05-24 17:35 +0100
http://bitbucket.org/pypy/pypy/changeset/66108bb2353d/

Log:    hg merge default

diff too long, truncating to 2000 out of 4286 lines

diff --git a/pypy/doc/test/test_whatsnew.py b/pypy/doc/test/test_whatsnew.py
--- a/pypy/doc/test/test_whatsnew.py
+++ b/pypy/doc/test/test_whatsnew.py
@@ -102,6 +102,8 @@
     assert not not_documented
     if branch == 'py3k':
         assert not not_merged
+    else:
+        assert branch in documented, 'Please document this branch before 
merging: %s' % branch
 
 def test_startrev_on_default():
     doc = ROOT.join('pypy', 'doc')
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -56,3 +56,15 @@
 
 Remove faulty fastpath from ctypes
 
+.. branch: sockopt_zero
+
+Passing a buffersize of 0 to socket.getsockopt
+
+.. branch: better-test-whatsnew
+
+.. branch: faster-rstruct-2
+
+Improve the performance of struct.pack and struct.pack_into by using raw_store
+or gc_store_indexed whenever possible. Moreover, enable the existing
+struct.unpack fast path to all the existing buffer types, whereas previously
+it was enabled only for strings
diff --git a/pypy/doc/windows.rst b/pypy/doc/windows.rst
--- a/pypy/doc/windows.rst
+++ b/pypy/doc/windows.rst
@@ -23,7 +23,7 @@
 Installing Visual Compiler v9 (for Python 2.7)
 ----------------------------------------------
 
-This compiler, while the standard one for Python 2.7, is depricated. Microsoft 
has
+This compiler, while the standard one for Python 2.7, is deprecated. Microsoft 
has
 made it available as the `Microsoft Visual C++ Compiler for Python 2.7`_ (the 
link
 was checked in Nov 2016). Note that the compiler suite will be installed in
 ``C:\Users\<user name>\AppData\Local\Programs\Common\Microsoft\Visual C++ for 
Python``.
diff --git a/pypy/interpreter/buffer.py b/pypy/interpreter/buffer.py
--- a/pypy/interpreter/buffer.py
+++ b/pypy/interpreter/buffer.py
@@ -1,5 +1,6 @@
 from rpython.rlib.rstruct.error import StructError
 from rpython.rlib.buffer import StringBuffer, SubBuffer
+from rpython.rlib.mutbuffer import MutableStringBuffer
 
 from pypy.interpreter.error import oefmt
 
@@ -72,14 +73,15 @@
     def bytes_from_value(self, space, w_val):
         from pypy.module.struct.formatiterator import PackFormatIterator
         itemsize = self.getitemsize()
-        fmtiter = PackFormatIterator(space, [w_val], itemsize)
+        buf = MutableStringBuffer(itemsize)
+        fmtiter = PackFormatIterator(space, buf, [w_val])
         try:
             fmtiter.interpret(self.getformat())
         except StructError as e:
             raise oefmt(space.w_TypeError,
                         "memoryview: invalid type for format '%s'",
                         self.getformat())
-        return fmtiter.result.build()
+        return buf.finish()
 
     def _copy_buffer(self):
         if self.getndim() == 0:
diff --git a/pypy/module/_cffi_backend/cbuffer.py 
b/pypy/module/_cffi_backend/cbuffer.py
--- a/pypy/module/_cffi_backend/cbuffer.py
+++ b/pypy/module/_cffi_backend/cbuffer.py
@@ -6,13 +6,13 @@
 from pypy.module._cffi_backend import ctypestruct
 from pypy.interpreter.buffer import SimpleView
 
-from rpython.rlib.buffer import Buffer
+from rpython.rlib.buffer import RawBuffer
 from rpython.rtyper.annlowlevel import llstr
 from rpython.rtyper.lltypesystem import rffi
 from rpython.rtyper.lltypesystem.rstr import copy_string_to_raw
 
 
-class LLBuffer(Buffer):
+class LLBuffer(RawBuffer):
     _immutable_ = True
 
     def __init__(self, raw_cdata, size):
@@ -35,7 +35,7 @@
     def getslice(self, start, stop, step, size):
         if step == 1:
             return rffi.charpsize2str(rffi.ptradd(self.raw_cdata, start), size)
-        return Buffer.getslice(self, start, stop, step, size)
+        return RawBuffer.getslice(self, start, stop, step, size)
 
     def setslice(self, start, string):
         raw_cdata = rffi.ptradd(self.raw_cdata, start)
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,11 +1,10 @@
 from rpython.rtyper.lltypesystem import rffi
-
-from rpython.rlib.buffer import Buffer
+from rpython.rlib.buffer import RawBuffer
 
 # XXX not the most efficient implementation
 
 
-class RawFFIBuffer(Buffer):
+class RawFFIBuffer(RawBuffer):
     _immutable_ = True
 
     def __init__(self, datainstance):
diff --git a/pypy/module/_socket/interp_socket.py 
b/pypy/module/_socket/interp_socket.py
--- a/pypy/module/_socket/interp_socket.py
+++ b/pypy/module/_socket/interp_socket.py
@@ -367,20 +367,19 @@
         except SocketError as e:
             raise converted_error(space, e)
 
-    @unwrap_spec(level=int, optname=int)
-    def getsockopt_w(self, space, level, optname, w_buflen=None):
+    @unwrap_spec(level=int, optname=int, buflen=int)
+    def getsockopt_w(self, space, level, optname, buflen=0):
         """getsockopt(level, option[, buffersize]) -> value
 
         Get a socket option.  See the Unix manual for level and option.
         If a nonzero buffersize argument is given, the return value is a
         string of that length; otherwise it is an integer.
         """
-        if w_buflen is None:
+        if buflen == 0:
             try:
                 return space.newint(self.sock.getsockopt_int(level, optname))
             except SocketError as e:
                 raise converted_error(space, e)
-        buflen = space.int_w(w_buflen)
         return space.newbytes(self.sock.getsockopt(level, optname, buflen))
 
     def gettimeout_w(self, space):
diff --git a/pypy/module/_socket/test/test_sock_app.py 
b/pypy/module/_socket/test/test_sock_app.py
--- a/pypy/module/_socket/test/test_sock_app.py
+++ b/pypy/module/_socket/test/test_sock_app.py
@@ -514,6 +514,19 @@
         (reuse,) = struct.unpack('i', reusestr)
         assert reuse != 0
 
+    def test_getsetsockopt_zero(self):
+        # related to issue #2561: when specifying the buffer size param:
+        # if 0 or None, should return the setted value,
+        # otherwise an empty buffer of the specified size
+        import _socket
+        s = _socket.socket()
+        assert s.getsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, 0) == 0
+        assert s.getsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, 2) == 
b'\x00\x00'
+        s.setsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, True)
+        assert s.getsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, 0) == 1
+        s.setsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, 1)
+        assert s.getsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, 0) == 1
+
     def test_socket_ioctl(self):
         import _socket, sys
         if sys.platform != 'win32':
diff --git a/pypy/module/array/interp_array.py 
b/pypy/module/array/interp_array.py
--- a/pypy/module/array/interp_array.py
+++ b/pypy/module/array/interp_array.py
@@ -1,11 +1,11 @@
 from rpython.rlib import jit, rgc
+from rpython.rlib.buffer import RawBuffer
 from rpython.rlib.objectmodel import keepalive_until_here
 from rpython.rlib.rarithmetic import ovfcheck, widen
 from rpython.rlib.unroll import unrolling_iterable
 from rpython.rtyper.annlowlevel import llstr
 from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.rtyper.lltypesystem.rstr import copy_string_to_raw
-from rpython.rlib.buffer import Buffer
 
 from pypy.interpreter.buffer import BufferView
 from pypy.interpreter.baseobjspace import W_Root
@@ -848,7 +848,7 @@
     v.typecode = k
 unroll_typecodes = unrolling_iterable(types.keys())
 
-class ArrayData(Buffer):
+class ArrayData(RawBuffer):
     _immutable_ = True
     readonly = False
     def __init__(self, w_array):
diff --git a/pypy/module/bz2/interp_bz2.py b/pypy/module/bz2/interp_bz2.py
--- a/pypy/module/bz2/interp_bz2.py
+++ b/pypy/module/bz2/interp_bz2.py
@@ -324,6 +324,7 @@
         to compress, call the flush() method to finish the compression process,
         and return what is left in the internal buffers."""
 
+        assert data is not None
         try:
             self.lock()
             datasize = len(data)
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
@@ -22,8 +22,7 @@
 from pypy.interpreter.buffer import BufferView
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.argument import Arguments
-
-from rpython.rlib.buffer import Buffer
+from rpython.rlib.buffer import RawBuffer
 from rpython.rlib.unroll import unrolling_iterable
 from rpython.rlib.objectmodel import specialize, not_rpython
 from rpython.tool.sourcetools import func_renamer
@@ -428,7 +427,7 @@
 fq = FQ()
 
 
-class CBuffer(Buffer):
+class CBuffer(RawBuffer):
     _immutable_ = True
     def __init__(self, view):
         self.view = view
diff --git a/pypy/module/micronumpy/concrete.py 
b/pypy/module/micronumpy/concrete.py
--- a/pypy/module/micronumpy/concrete.py
+++ b/pypy/module/micronumpy/concrete.py
@@ -3,7 +3,7 @@
 from rpython.rlib import jit, rgc
 from rpython.rlib.rarithmetic import ovfcheck
 from rpython.rlib.listsort import make_timsort_class
-from rpython.rlib.buffer import Buffer
+from rpython.rlib.buffer import RawBuffer
 from rpython.rlib.debug import make_sure_not_resized
 from rpython.rlib.rstring import StringBuilder
 from rpython.rlib.rawstorage import alloc_raw_storage, free_raw_storage, \
@@ -702,7 +702,8 @@
     def __del__(self):
         free_raw_storage(self.storage)
 
-class ArrayData(Buffer):
+
+class ArrayData(RawBuffer):
     _immutable_ = True
     def __init__(self, impl, readonly):
         self.impl = impl
diff --git a/pypy/module/mmap/interp_mmap.py b/pypy/module/mmap/interp_mmap.py
--- a/pypy/module/mmap/interp_mmap.py
+++ b/pypy/module/mmap/interp_mmap.py
@@ -4,8 +4,8 @@
 from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
 from pypy.interpreter.buffer import SimpleView
 
-from rpython.rlib.buffer import Buffer
 from rpython.rlib import rmmap, rarithmetic, objectmodel
+from rpython.rlib.buffer import RawBuffer
 from rpython.rlib.rmmap import RValueError, RTypeError, RMMapError
 from rpython.rlib.rstring import StringBuilder
 
@@ -309,7 +309,7 @@
         return OperationError(space.w_SystemError, space.newtext('%s' % e))
 
 
-class MMapBuffer(Buffer):
+class MMapBuffer(RawBuffer):
     _immutable_ = True
 
     def __init__(self, space, mmap, readonly):
@@ -329,7 +329,7 @@
         if step == 1:
             return self.mmap.getslice(start, size)
         else:
-            return Buffer.getslice(self, start, stop, step, size)
+            return RawBuffer.getslice(self, start, stop, step, size)
 
     def setitem(self, index, char):
         self.check_valid_writeable()
diff --git a/pypy/module/pypyjit/test_pypy_c/test_buffers.py 
b/pypy/module/pypyjit/test_pypy_c/test_buffers.py
--- a/pypy/module/pypyjit/test_pypy_c/test_buffers.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_buffers.py
@@ -34,29 +34,13 @@
             i = 0
             while i < n:
                 i += 1
-                struct.unpack('<i', a)  # ID: unpack
+                struct.unpack('i', a)  # ID: unpack
             return i
         log = self.run(main, [1000])
         assert log.result == 1000
         loop, = log.loops_by_filename(self.filepath)
         assert loop.match_by_id('unpack', """
             guard_not_invalidated(descr=...)
-            p90 = newstr(4)
-            call_n(ConstClass(copy_raw_to_string), i55, p90, 0, 4, 
descr=<Callv 0 irii EF=5>)
-            guard_no_exception(descr=...)
-            i91 = strgetitem(p90, 0)
-            i92 = strgetitem(p90, 1)
-            i93 = int_lshift(i92, 8)
-            i94 = int_or(i91, i93)
-            i95 = strgetitem(p90, 2)
-            i96 = int_lshift(i95, 16)
-            i97 = int_or(i94, i96)
-            i98 = strgetitem(p90, 3)
-            i99 = int_ge(i98, 128)
-            guard_false(i99, descr=...)
-            i100 = int_lshift(i98, 24)
-            i101 = int_or(i97, i100)
-            i102 = getfield_raw_i(#, descr=<FieldS pypysig_long_struct.c_value 
0>)
-            i103 = int_lt(i102, 0)
-            guard_false(i103, descr=...)
+            i66 = raw_load_i(i53, 0, descr=<ArrayS 4>)
+            --TICK--
         """)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_ffi.py 
b/pypy/module/pypyjit/test_pypy_c/test_ffi.py
--- a/pypy/module/pypyjit/test_pypy_c/test_ffi.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_ffi.py
@@ -110,38 +110,6 @@
         loops = log.loops_by_id('sleep')
         assert len(loops) == 1 # make sure that we actually JITted the loop
 
-    def test_ctypes_call(self):
-        from rpython.rlib.test.test_clibffi import get_libm_name
-        def main(libm_name):
-            import ctypes
-            libm = ctypes.CDLL(libm_name)
-            fabs = libm.fabs
-            fabs.argtypes = [ctypes.c_double]
-            fabs.restype = ctypes.c_double
-            x = -4
-            i = 0
-            while i < 300:
-                x = fabs(x)
-                x = x - 100
-                i += 1
-            return fabs._ptr.getaddr(), x
-
-        libm_name = get_libm_name(sys.platform)
-        log = self.run(main, [libm_name], import_site=True)
-        fabs_addr, res = log.result
-        assert res == -4.0
-        loop, = log.loops_by_filename(self.filepath)
-        ops = loop.allops()
-        opnames = log.opnames(ops)
-        assert opnames.count('new_with_vtable') == 1 # only the virtualref
-        py.test.skip("XXX re-optimize _ffi for the JIT?")
-        assert opnames.count('call_release_gil') == 1
-        idx = opnames.index('call_release_gil')
-        call = ops[idx]
-        assert (call.args[0] == 'ConstClass(fabs)' or    # e.g. OS/X
-                int(call.args[0]) == fabs_addr)
-
-
     def test__ffi_struct(self):
         def main():
             from _rawffi.alt import _StructDescr, Field, types
diff --git a/pypy/module/pypyjit/test_pypy_c/test_struct.py 
b/pypy/module/pypyjit/test_pypy_c/test_struct.py
--- a/pypy/module/pypyjit/test_pypy_c/test_struct.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_struct.py
@@ -30,32 +30,33 @@
         loop, = log.loops_by_filename(self.filepath)
         # This could, of course stand some improvement, to remove all these
         # arithmatic ops, but we've removed all the core overhead.
-        assert loop.match_by_id("pack", """
-            guard_not_invalidated(descr=...)
-            # struct.pack
-            %s
-            i11 = int_and(i4, 255)
-            i13 = int_rshift(i4, 8)
-            i14 = int_and(i13, 255)
-            i16 = int_rshift(i13, 8)
-            i17 = int_and(i16, 255)
-            i19 = int_rshift(i16, 8)
-            i20 = int_and(i19, 255)
-        """ % extra)
+        if sys.byteorder == 'little':
+            # on little endian machines, we take the fast path and store the
+            # value using gc_store_indexed
+            assert loop.match_by_id("pack", """
+                guard_not_invalidated(descr=...)
+                # struct.pack
+                %s
+                p75 = newstr(4)
+                gc_store_indexed(p75, 0, _, 1, _, 4, descr=...)
+            """ % extra)
+        else:
+            assert loop.match_by_id("pack", """
+                guard_not_invalidated(descr=...)
+                # struct.pack
+                %s
+                i11 = int_and(i4, 255)
+                i13 = int_rshift(i4, 8)
+                i14 = int_and(i13, 255)
+                i16 = int_rshift(i13, 8)
+                i17 = int_and(i16, 255)
+                i19 = int_rshift(i16, 8)
+                i20 = int_and(i19, 255)
+            """ % extra)
 
         if sys.byteorder == 'little':
-            # the newstr and the strsetitems are because the string is forced,
-            # which is in turn because the optimizer doesn't know how to 
handle a
-            # gc_load_indexed_i on a virtual string. It could be improved, but 
it
-            # is also true that in real life cases struct.unpack is called on
-            # strings which come from the outside, so it's a minor issue.
             assert loop.match_by_id("unpack", """
                 # struct.unpack
-                p88 = newstr(4)
-                strsetitem(p88, 0, i11)
-                strsetitem(p88, 1, i14)
-                strsetitem(p88, 2, i17)
-                strsetitem(p88, 3, i20)
                 i91 = gc_load_indexed_i(p88, 0, 1, _, -4)
             """)
         else:
@@ -93,27 +94,106 @@
             assert loop.match_by_id('pack', """
                 guard_not_invalidated(descr=...)
                 # struct.pack
+                p85 = newstr(8)
+                gc_store_indexed(p85, 0, -1, 1, _, 4, descr=...)
                 %s
-                i11 = int_and(i4, 255)
-                i13 = int_rshift(i4, 8)
-                i14 = int_and(i13, 255)
-                i16 = int_rshift(i13, 8)
-                i17 = int_and(i16, 255)
-                i19 = int_rshift(i16, 8)
-                i20 = int_and(i19, 255)
+                gc_store_indexed(p85, 4, _, 1, _, 4, descr=...)
             """ % extra)
 
             assert loop.match_by_id('unpack', """
                 # struct.unpack
-                p88 = newstr(8)
-                strsetitem(p88, 0, 255)
-                strsetitem(p88, 1, 255)
-                strsetitem(p88, 2, 255)
-                strsetitem(p88, 3, 255)
-                strsetitem(p88, 4, i11)
-                strsetitem(p88, 5, i14)
-                strsetitem(p88, 6, i17)
-                strsetitem(p88, 7, i20)
                 i90 = gc_load_indexed_i(p88, 0, 1, _, -4)
                 i91 = gc_load_indexed_i(p88, 4, 1, _, -4)
             """)
+
+    def test_unpack_raw_buffer(self):
+        def main(n):
+            import array
+            import struct
+            buf = struct.pack('H', 0x1234)
+            buf = array.array('b', buf)
+            i = 1
+            res = 0
+            while i < n:
+                val = struct.unpack("h", buf)[0]     # ID: unpack
+                res += val
+                i += 1
+            return res
+        log = self.run(main, [1000])
+        assert log.result == main(1000)
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match_by_id('unpack', """
+            guard_not_invalidated(descr=...)
+            i65 = raw_load_i(i49, 0, descr=<ArrayS 2>)
+        """)
+
+    def test_unpack_bytearray(self):
+        def main(n):
+            import struct
+            buf = struct.pack('H', 0x1234)
+            buf = bytearray(buf)
+            i = 1
+            res = 0
+            while i < n:
+                val = struct.unpack("h", buf)[0]     # ID: unpack
+                res += val
+                i += 1
+            return res
+        log = self.run(main, [1000])
+        assert log.result == main(1000)
+        loop, = log.loops_by_filename(self.filepath)
+        # the offset of gc_load_indexed_i used to be the constant 0. However,
+        # now it is 'i46' because we need to add 0 to
+        # W_BytearrayObject._offset
+        assert loop.match_by_id('unpack', """
+            guard_not_invalidated(descr=...)
+            i70 = gc_load_indexed_i(p48, i46, 1, _, -2)
+        """)
+
+    def test_pack_into_raw_buffer(self):
+        def main(n):
+            import array
+            import struct
+            buf = array.array('b', '\x00'*8)
+            i = 1
+            while i < n:
+                struct.pack_into("h", buf, 4, i)     # ID: pack_into
+                i += 1
+            return i
+        log = self.run(main, [1000])
+        assert log.result == main(1000)
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match_by_id('pack_into', """\
+            guard_not_invalidated(descr=...)
+            i65 = int_le(i58, 32767)
+            guard_true(i65, descr=...)
+            raw_store(i55, 4, i58, descr=<ArrayS 2>)
+        """)
+
+    def test_pack_into_bytearray(self):
+        def main(n):
+            import struct
+            buf = bytearray(8)
+            i = 1
+            while i < n:
+                struct.pack_into("h", buf, 4, i)     # ID: pack_into
+                i += 1
+            return i
+        log = self.run(main, [1000])
+        assert log.result == main(1000)
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match_by_id('pack_into', """\
+            guard_not_invalidated(descr=...)
+            p68 = getfield_gc_r(p14, descr=<FieldP 
pypy.objspace.std.bytearrayobject.W_BytearrayObject.inst__data \d+>)
+            i69 = getfield_gc_i(p68, descr=<FieldS list.length \d+>)
+            i70 = getfield_gc_i(p14, descr=<FieldS 
pypy.objspace.std.bytearrayobject.W_BytearrayObject.inst__offset \d+>)
+            i71 = int_sub(i69, i70)
+            i73 = int_sub(i71, 4)
+            i75 = int_lt(i73, 2)
+            guard_false(i75, descr=...)
+            i77 = int_le(i62, 32767)
+            guard_true(i77, descr=...)
+            p78 = getfield_gc_r(p68, descr=<FieldP list.items \d+>)
+            i81 = int_add(4, i70)
+            gc_store_indexed(p78, i81, i62, 1, _, 2, descr=<ArrayS 2>)
+        """)
diff --git a/pypy/module/struct/formatiterator.py 
b/pypy/module/struct/formatiterator.py
--- a/pypy/module/struct/formatiterator.py
+++ b/pypy/module/struct/formatiterator.py
@@ -2,7 +2,6 @@
                                       maxint, intmask)
 from rpython.rlib import jit
 from rpython.rlib.objectmodel import specialize
-from rpython.rlib.rstring import StringBuilder
 from rpython.rlib.rstruct.error import StructError
 from rpython.rlib.rstruct.formatiterator import FormatIterator
 
@@ -10,11 +9,15 @@
 
 
 class PackFormatIterator(FormatIterator):
-    def __init__(self, space, args_w, size):
+    def __init__(self, space, wbuf, args_w):
         self.space = space
         self.args_w = args_w
         self.args_index = 0
-        self.result = StringBuilder(size)
+        self.pos = 0
+        self.wbuf = wbuf
+
+    def advance(self, count):
+        self.pos += count
 
     # This *should* be always unroll safe, the only way to get here is by
     # unroll the interpret function, which means the fmt is const, and thus
@@ -31,8 +34,10 @@
 
     @jit.unroll_safe
     def align(self, mask):
-        pad = (-self.result.getlength()) & mask
-        self.result.append_multiple_char('\x00', pad)
+        pad = (-self.pos) & mask
+        for i in range(self.pos, self.pos+pad):
+            self.wbuf.setitem(i, '\x00')
+        self.advance(pad)
 
     def finished(self):
         if self.args_index != len(self.args_w):
@@ -135,15 +140,22 @@
         if value != self.length:
             raise StructError("unpack str size too long for format")
 
-    def read(self, count):
+    def can_advance(self, count):
         if self.strides:
             count = self.strides[0]
         end = self.pos + count
-        if end > self.length:
+        return end <= self.length
+
+    def advance(self, count):
+        if not self.can_advance(count):
             raise StructError("unpack str size too short for format")
-        s = self.buf.getslice(self.pos, end, 1, count)
-        self.pos = end
-        return s
+        self.pos += count
+
+    def read(self, count):
+        curpos = self.pos
+        end = curpos + count
+        self.advance(count) # raise if we are out of bound
+        return self.buf.getslice(curpos, end, 1, count)
 
     @specialize.argtype(1)
     def appendobj(self, value):
@@ -162,9 +174,8 @@
     def get_pos(self):
         return self.pos
 
-    def get_buffer_as_string_maybe(self):
-        string, pos = self.buf.as_str_and_offset_maybe()
-        return string, pos+self.pos
+    def get_buffer_and_pos(self):
+        return self.buf, self.pos
 
     def skip(self, count):
         # assumption: UnpackFormatIterator only iterates over
diff --git a/pypy/module/struct/interp_struct.py 
b/pypy/module/struct/interp_struct.py
--- a/pypy/module/struct/interp_struct.py
+++ b/pypy/module/struct/interp_struct.py
@@ -1,5 +1,6 @@
 from rpython.rlib import jit
 from rpython.rlib.buffer import SubBuffer
+from rpython.rlib.mutbuffer import MutableStringBuffer
 from rpython.rlib.rstruct.error import StructError, StructOverflowError
 from rpython.rlib.rstruct.formatiterator import CalcSizeFormatIterator
 
@@ -48,18 +49,17 @@
 
 def _pack(space, format, args_w):
     """Return string containing values v1, v2, ... packed according to fmt."""
-    if jit.isconstant(format):
-        size = _calcsize(space, format)
-    else:
-        size = 8
-    fmtiter = PackFormatIterator(space, args_w, size)
+    size = _calcsize(space, format)
+    wbuf = MutableStringBuffer(size)
+    fmtiter = PackFormatIterator(space, wbuf, args_w)
     try:
         fmtiter.interpret(format)
     except StructOverflowError as e:
         raise OperationError(space.w_OverflowError, space.newtext(e.msg))
     except StructError as e:
         raise OperationError(get_error(space), space.newtext(e.msg))
-    return fmtiter.result.build()
+    assert fmtiter.pos == wbuf.getlength(), 'missing .advance() or wrong 
calcsize()'
+    return wbuf.finish()
 
 
 def pack(space, w_format, args_w):
@@ -79,21 +79,27 @@
     format = text_or_bytes_w(space, w_format)
     return do_pack_into(space, format, w_buffer, offset, args_w)
 
-# XXX inefficient
 def do_pack_into(space, format, w_buffer, offset, args_w):
     """ Pack the values v1, v2, ... according to fmt.
 Write the packed bytes into the writable buffer buf starting at offset
     """
-    res = _pack(space, format, args_w)
+    size = _calcsize(space, format)
     buf = space.writebuf_w(w_buffer)
     if offset < 0:
         offset += buf.getlength()
-    size = len(res)
     if offset < 0 or (buf.getlength() - offset) < size:
         raise oefmt(get_error(space),
                     "pack_into requires a buffer of at least %d bytes",
                     size)
-    buf.setslice(offset, res)
+    #
+    wbuf = SubBuffer(buf, offset, size)
+    fmtiter = PackFormatIterator(space, wbuf, args_w)
+    try:
+        fmtiter.interpret(format)
+    except StructOverflowError as e:
+        raise OperationError(space.w_OverflowError, space.newtext(e.msg))
+    except StructError as e:
+        raise OperationError(get_error(space), space.newtext(e.msg))
 
 
 def _unpack(space, format, buf):
diff --git a/pypy/module/struct/test/test_struct.py 
b/pypy/module/struct/test/test_struct.py
--- a/pypy/module/struct/test/test_struct.py
+++ b/pypy/module/struct/test/test_struct.py
@@ -591,7 +591,7 @@
 
 
 class AppTestFastPath(object):
-    spaceconfig = dict(usemodules=['struct', '__pypy__'])
+    spaceconfig = dict(usemodules=['array', 'struct', '__pypy__'])
 
     def setup_class(cls):
         from rpython.rlib.rstruct import standardfmttable
@@ -610,7 +610,43 @@
         from rpython.rlib.rstruct import standardfmttable
         standardfmttable.ALLOW_SLOWPATH = True
 
+    def test_unpack_simple(self):
+        buf = self.struct.pack("iii", 0, 42, 43)
+        assert self.struct.unpack("iii", buf) == (0, 42, 43)
+
     def test_unpack_from(self):
         buf = self.struct.pack("iii", 0, 42, 43)
         offset = self.struct.calcsize("i")
         assert self.struct.unpack_from("ii", buf, offset) == (42, 43)
+
+    def test_unpack_bytearray(self):
+        data = self.struct.pack("iii", 0, 42, 43)
+        buf = bytearray(data)
+        assert self.struct.unpack("iii", buf) == (0, 42, 43)
+
+    def test_unpack_array(self):
+        import array
+        data = self.struct.pack("iii", 0, 42, 43)
+        buf = array.array('c', data)
+        assert self.struct.unpack("iii", buf) == (0, 42, 43)
+
+    def test_pack_into_bytearray(self):
+        expected = self.struct.pack("ii", 42, 43)
+        buf = bytearray(len(expected))
+        self.struct.pack_into("ii", buf, 0, 42, 43)
+        assert buf == expected
+
+    def test_pack_into_bytearray_padding(self):
+        expected = self.struct.pack("xxi", 42)
+        buf = bytearray(len(expected))
+        self.struct.pack_into("xxi", buf, 0, 42)
+        assert buf == expected
+
+    def test_pack_into_bytearray_delete(self):
+        expected = self.struct.pack("i", 42)
+        # force W_BytearrayObject._delete_from_start
+        buf = bytearray(64)
+        del buf[:8]
+        self.struct.pack_into("i", buf, 0, 42)
+        buf = buf[:len(expected)]
+        assert buf == expected
diff --git a/pypy/objspace/std/bytearrayobject.py 
b/pypy/objspace/std/bytearrayobject.py
--- a/pypy/objspace/std/bytearrayobject.py
+++ b/pypy/objspace/std/bytearrayobject.py
@@ -8,10 +8,11 @@
 from rpython.rlib.debug import check_list_of_chars, check_nonneg
 from rpython.rtyper.lltypesystem import rffi
 from rpython.rlib.rgc import (resizable_list_supporting_raw_ptr,
-        nonmoving_raw_ptr_for_resizable_list)
+                              nonmoving_raw_ptr_for_resizable_list)
 from rpython.rlib import jit
-from rpython.rlib.buffer import Buffer
-
+from rpython.rlib.buffer import (GCBuffer,
+                                 get_gc_data_for_list_of_chars,
+                                 get_gc_data_offset_for_list_of_chars)
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.objspace.std.bytesobject import makebytesdata_w, newbytesdata_w
@@ -1274,7 +1275,8 @@
         start += step
 
 
-class BytearrayBuffer(Buffer):
+@GCBuffer.decorate
+class BytearrayBuffer(GCBuffer):
     _immutable_ = True
 
     def __init__(self, ba, readonly=False):
@@ -1304,7 +1306,7 @@
             if start != 0 or stop != len(data):
                 data = data[start:stop]
             return "".join(data)
-        return Buffer.getslice(self, start, stop, step, size)
+        return GCBuffer.getslice(self, start, stop, step, size)
 
     def setslice(self, start, string):
         # No bounds checks.
@@ -1319,6 +1321,16 @@
         p = rffi.ptradd(p, ba._offset)
         return p
 
+    @staticmethod
+    def _get_gc_data_offset():
+        return get_gc_data_offset_for_list_of_chars()
+
+    def _get_gc_data_extra_offset(self):
+        return self.ba._offset
+
+    def _get_gc_data(self):
+        return get_gc_data_for_list_of_chars(self.ba._data)
+
 
 @specialize.argtype(1)
 def _memcmp(selfvalue, buffer, length):
diff --git a/pypy/objspace/std/marshal_impl.py 
b/pypy/objspace/std/marshal_impl.py
--- a/pypy/objspace/std/marshal_impl.py
+++ b/pypy/objspace/std/marshal_impl.py
@@ -1,5 +1,6 @@
 from rpython.rlib.rarithmetic import LONG_BIT, r_longlong, r_uint
-from rpython.rlib.rstring import StringBuilder, assert_str0
+from rpython.rlib.rstring import assert_str0
+from rpython.rlib.mutbuffer import MutableStringBuffer
 from rpython.rlib.rstruct import ieee
 from rpython.rlib.unroll import unrolling_iterable
 from rpython.rlib import objectmodel
@@ -213,9 +214,9 @@
 
 
 def pack_float(f):
-    result = StringBuilder(8)
-    ieee.pack_float(result, f, 8, False)
-    return result.build()
+    buf = MutableStringBuffer(8)
+    ieee.pack_float(buf, 0, f, 8, False)
+    return buf.finish()
 
 def unpack_float(s):
     return ieee.unpack_float(s, False)
@@ -408,7 +409,7 @@
     stacksize   = u.get_int()
     flags       = u.get_int()
     code        = space.bytes_w(u.get_w_obj())
-    consts_w    = _unmarshal_tuple_w(u)   
+    consts_w    = _unmarshal_tuple_w(u)
     names       = _unmarshal_strlist(u)
     varnames    = _unmarshal_strlist(u)
     freevars    = _unmarshal_strlist(u)
diff --git a/rpython/annotator/binaryop.py b/rpython/annotator/binaryop.py
--- a/rpython/annotator/binaryop.py
+++ b/rpython/annotator/binaryop.py
@@ -514,13 +514,13 @@
     ne = eq
 
     def lt((tup1, tup2)):
-        raise Exception("unsupported: (...) < (...)")
+        raise AnnotatorError("unsupported: (...) < (...)")
     def le((tup1, tup2)):
-        raise Exception("unsupported: (...) <= (...)")
+        raise AnnotatorError("unsupported: (...) <= (...)")
     def gt((tup1, tup2)):
-        raise Exception("unsupported: (...) > (...)")
+        raise AnnotatorError("unsupported: (...) > (...)")
     def ge((tup1, tup2)):
-        raise Exception("unsupported: (...) >= (...)")
+        raise AnnotatorError("unsupported: (...) >= (...)")
 
 
 class __extend__(pairtype(SomeDict, SomeDict)):
diff --git a/rpython/annotator/bookkeeper.py b/rpython/annotator/bookkeeper.py
--- a/rpython/annotator/bookkeeper.py
+++ b/rpython/annotator/bookkeeper.py
@@ -15,7 +15,8 @@
     SomeDict, SomeBuiltin, SomePBC, SomeInteger, TLS, SomeUnicodeCodePoint,
     s_None, s_ImpossibleValue, SomeBool, SomeTuple, SomeException,
     SomeImpossibleValue, SomeUnicodeString, SomeList, HarmlesslyBlocked,
-    SomeWeakRef, SomeByteArray, SomeConstantType, SomeProperty)
+    SomeWeakRef, SomeByteArray, SomeConstantType, SomeProperty,
+    AnnotatorError)
 from rpython.annotator.classdesc import ClassDef, ClassDesc
 from rpython.annotator.listdef import ListDef, ListItem
 from rpython.annotator.dictdef import DictDef
@@ -343,7 +344,7 @@
         elif x is None:
             return s_None
         else:
-            raise Exception("Don't know how to represent %r" % (x,))
+            raise AnnotatorError("Don't know how to represent %r" % (x,))
         result.const = x
         return result
 
@@ -363,7 +364,7 @@
                 result = self.newfuncdesc(pyobj)
             elif isinstance(pyobj, (type, types.ClassType)):
                 if pyobj is object:
-                    raise Exception("ClassDesc for object not supported")
+                    raise AnnotatorError("ClassDesc for object not supported")
                 if pyobj.__module__ == '__builtin__': # avoid making classdefs 
for builtin types
                     result = self.getfrozen(pyobj)
                 else:
@@ -400,7 +401,7 @@
                         msg = "object with a __call__ is not RPython"
                     else:
                         msg = "unexpected prebuilt constant"
-                    raise Exception("%s: %r" % (msg, pyobj))
+                    raise AnnotatorError("%s: %r" % (msg, pyobj))
                 result = self.getfrozen(pyobj)
             self.descs[obj_key] = result
             return result
@@ -589,7 +590,7 @@
         for name, value in dict.iteritems():
             if value is func:
                 return cls, name
-    raise Exception("could not match bound-method to attribute name: %r" % 
(boundmeth,))
+    raise AnnotatorError("could not match bound-method to attribute name: %r" 
% (boundmeth,))
 
 def ishashable(x):
     try:
diff --git a/rpython/annotator/classdesc.py b/rpython/annotator/classdesc.py
--- a/rpython/annotator/classdesc.py
+++ b/rpython/annotator/classdesc.py
@@ -550,8 +550,8 @@
                                         "with _mixin_: %r" % (cls,))
                 base = b1
         if mixins_before and mixins_after:
-            raise Exception("unsupported: class %r has mixin bases both"
-                            " before and after the regular base" % (self,))
+            raise AnnotatorError("unsupported: class %r has mixin bases both"
+                                 " before and after the regular base" % 
(self,))
         self.add_mixins(mixins_after, check_not_in=base)
         self.add_mixins(mixins_before)
         self.add_sources_for_class(cls)
@@ -569,8 +569,8 @@
                 attrs.update(decl)
             if self.basedesc is not None:
                 if self.basedesc.all_enforced_attrs is None:
-                    raise Exception("%r has slots or _attrs_, "
-                                    "but not its base class" % (cls,))
+                    raise AnnotatorError("%r has slots or _attrs_, "
+                                         "but not its base class" % (cls,))
                 attrs.update(self.basedesc.all_enforced_attrs)
             self.all_enforced_attrs = attrs
 
@@ -714,8 +714,8 @@
                 try:
                     args.fixedunpack(0)
                 except ValueError:
-                    raise Exception("default __init__ takes no argument"
-                                    " (class %s)" % (self.name,))
+                    raise AnnotatorError("default __init__ takes no argument"
+                                         " (class %s)" % (self.name,))
             elif self.pyobj is Exception:
                 # check explicitly against "raise Exception, x" where x
                 # is a low-level exception pointer
@@ -824,8 +824,8 @@
         # actual copy in the rtyper). Tested in rpython.rtyper.test.test_rlist,
         # test_immutable_list_out_of_instance.
         if self._detect_invalid_attrs and attr in self._detect_invalid_attrs:
-            raise Exception("field %r was migrated to %r from a subclass in "
-                            "which it was declared as _immutable_fields_" %
+            raise AnnotatorError("field %r was migrated to %r from a subclass 
in "
+                                 "which it was declared as _immutable_fields_" 
%
                             (attr, self.pyobj))
         search1 = '%s[*]' % (attr,)
         search2 = '%s?[*]' % (attr,)
@@ -858,7 +858,7 @@
             # call to a single class, look at the result annotation
             # in case it was specialized
             if not isinstance(s_result, SomeInstance):
-                raise Exception("calling a class didn't return an instance??")
+                raise AnnotatorError("calling a class didn't return an 
instance??")
             classdefs = [s_result.classdef]
         else:
             # call to multiple classes: specialization not supported
diff --git a/rpython/annotator/description.py b/rpython/annotator/description.py
--- a/rpython/annotator/description.py
+++ b/rpython/annotator/description.py
@@ -314,8 +314,8 @@
         enforceargs = getattr(self.pyobj, '_annenforceargs_', None)
         signature = getattr(self.pyobj, '_signature_', None)
         if enforceargs and signature:
-            raise Exception("%r: signature and enforceargs cannot both be "
-                            "used" % (self,))
+            raise AnnotatorError("%r: signature and enforceargs cannot both be 
"
+                                 "used" % (self,))
         if enforceargs:
             if not callable(enforceargs):
                 from rpython.annotator.signature import Sig
@@ -432,7 +432,7 @@
     def func_args(self, args):
         from rpython.annotator.model import SomeInstance
         if self.selfclassdef is None:
-            raise Exception("calling %r" % (self,))
+            raise AnnotatorError("calling %r" % (self,))
         s_instance = SomeInstance(self.selfclassdef, flags=self.flags)
         return args.prepend(s_instance)
 
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
@@ -4584,7 +4584,7 @@
             return Ellipsis
         a = self.RPythonAnnotator()
         e = py.test.raises(Exception, a.build_types, f, [])
-        assert str(e.value) == "Don't know how to represent Ellipsis"
+        assert "Don't know how to represent Ellipsis" in str(e.value)
 
     def test_must_be_light_finalizer(self):
         from rpython.rlib import rgc
diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py
--- a/rpython/annotator/unaryop.py
+++ b/rpython/annotator/unaryop.py
@@ -185,8 +185,8 @@
         return SomeString()
 
     def id(self):
-        raise Exception("cannot use id() in RPython; "
-                        "see objectmodel.compute_xxx()")
+        raise AnnotatorError("cannot use id() in RPython; "
+                             "see objectmodel.compute_xxx()")
 
     def int(self):
         return SomeInteger()
@@ -421,7 +421,7 @@
     def setslice(self, s_start, s_stop, s_iterable):
         check_negative_slice(s_start, s_stop)
         if not isinstance(s_iterable, SomeList):
-            raise Exception("list[start:stop] = x: x must be a list")
+            raise AnnotatorError("list[start:stop] = x: x must be a list")
         self.listdef.mutate()
         self.listdef.agree(getbookkeeper(), s_iterable.listdef)
         self.listdef.resize()
diff --git a/rpython/jit/backend/arm/test/test_llop.py 
b/rpython/jit/backend/arm/test/test_llop.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/backend/arm/test/test_llop.py
@@ -0,0 +1,9 @@
+from rpython.jit.backend.arm.test.support import JitARMMixin
+from rpython.jit.metainterp.test.test_llop import TestLLOp as _TestLLOp
+
+
+class TestLLOp(JitARMMixin, _TestLLOp):
+    # for the individual tests see
+    # ====> ../../../metainterp/test/test_llop.py
+    pass
+
diff --git a/rpython/jit/backend/llgraph/runner.py 
b/rpython/jit/backend/llgraph/runner.py
--- a/rpython/jit/backend/llgraph/runner.py
+++ b/rpython/jit/backend/llgraph/runner.py
@@ -716,16 +716,28 @@
         else:
             return self.bh_raw_load_i(struct, offset, descr)
 
+    def _get_int_type_from_size(self, size):
+        if   size == 1:
+            return rffi.UCHAR
+        elif size == 2:
+            return rffi.USHORT
+        elif size == 4:
+            return rffi.UINT
+        elif size == 8:
+            return rffi.ULONGLONG
+        elif size == -1:
+            return rffi.SIGNEDCHAR
+        elif size == -2:
+            return rffi.SHORT
+        elif size == -4:
+            return rffi.INT
+        elif size == -8:
+            return rffi.LONGLONG
+        else:
+            raise NotImplementedError(size)
+    
     def bh_gc_load_indexed_i(self, struct, index, scale, base_ofs, bytes):
-        if   bytes == 1: T = rffi.UCHAR
-        elif bytes == 2: T = rffi.USHORT
-        elif bytes == 4: T = rffi.UINT
-        elif bytes == 8: T = rffi.ULONGLONG
-        elif bytes == -1: T = rffi.SIGNEDCHAR
-        elif bytes == -2: T = rffi.SHORT
-        elif bytes == -4: T = rffi.INT
-        elif bytes == -8: T = rffi.LONGLONG
-        else: raise NotImplementedError(bytes)
+        T = self._get_int_type_from_size(bytes)
         x = llop.gc_load_indexed(T, struct, index, scale, base_ofs)
         return lltype.cast_primitive(lltype.Signed, x)
 
@@ -735,6 +747,30 @@
         return llop.gc_load_indexed(longlong.FLOATSTORAGE,
                                     struct, index, scale, base_ofs)
 
+    def bh_gc_store_indexed_i(self, struct, index, val, scale, base_ofs, bytes,
+                              descr):
+        T = self._get_int_type_from_size(bytes)
+        val = lltype.cast_primitive(T, val)
+        if descr.A.OF == lltype.SingleFloat:
+            val = longlong.int2singlefloat(val)
+        llop.gc_store_indexed(lltype.Void, struct, index, val, scale, base_ofs)
+
+    def bh_gc_store_indexed_f(self, struct, index, val, scale, base_ofs, bytes,
+                              descr):
+        if bytes != 8:
+            raise Exception("gc_store_indexed_f is only for 'double'!")
+        val = longlong.getrealfloat(val)
+        llop.gc_store_indexed(lltype.Void, struct, index, val, scale, base_ofs)
+
+    def bh_gc_store_indexed(self, struct, index, val, scale, base_ofs, bytes,
+                            descr):
+        if descr.A.OF == lltype.Float:
+            self.bh_gc_store_indexed_f(struct, index, val, scale, base_ofs,
+                                       bytes, descr)
+        else:
+            self.bh_gc_store_indexed_i(struct, index, val, scale, base_ofs,
+                                       bytes, descr)
+
     def bh_increment_debug_counter(self, addr):
         p = rffi.cast(rffi.CArrayPtr(lltype.Signed), addr)
         p[0] += 1
diff --git a/rpython/jit/backend/llsupport/llmodel.py 
b/rpython/jit/backend/llsupport/llmodel.py
--- a/rpython/jit/backend/llsupport/llmodel.py
+++ b/rpython/jit/backend/llsupport/llmodel.py
@@ -496,6 +496,7 @@
     @specialize.argtype(1)
     def write_float_at_mem(self, gcref, ofs, newvalue):
         llop.raw_store(lltype.Void, gcref, ofs, newvalue)
+    write_float_at_mem._annenforceargs_ = [None, None, None, 
longlong.r_float_storage]
 
     # ____________________________________________________________
 
@@ -754,6 +755,16 @@
         offset = base_ofs + scale * index
         return self.read_float_at_mem(addr, offset)
 
+    def bh_gc_store_indexed_i(self, addr, index, val, scale, base_ofs, bytes,
+                              descr):
+        offset = base_ofs + scale * index
+        self.write_int_at_mem(addr, offset, bytes, val)
+
+    def bh_gc_store_indexed_f(self, addr, index, val, scale, base_ofs, bytes,
+                              descr):
+        offset = base_ofs + scale * index
+        self.write_float_at_mem(addr, offset, val)
+
     def bh_new(self, sizedescr):
         return self.gc_ll_descr.gc_malloc(sizedescr)
 
diff --git a/rpython/jit/backend/x86/test/test_llop.py 
b/rpython/jit/backend/x86/test/test_llop.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/backend/x86/test/test_llop.py
@@ -0,0 +1,14 @@
+from rpython.jit.backend.x86.test.test_basic import Jit386Mixin
+from rpython.jit.metainterp.test.test_llop import TestLLOp as _TestLLOp
+
+
+class TestLLOp(Jit386Mixin, _TestLLOp):
+    # for the individual tests see
+    # ====> ../../../metainterp/test/test_llop.py
+
+    # do NOT test the blackhole implementation of gc_store_indexed. It cannot
+    # work inside tests because llmodel.py:bh_gc_store_indexed_* receive a
+    # symbolic as the offset. It is not a problem because it is tested anyway
+    # by the same test in test_metainterp.py
+    TEST_BLACKHOLE = False
+
diff --git a/rpython/jit/backend/x86/test/test_strstorage.py 
b/rpython/jit/backend/x86/test/test_strstorage.py
deleted file mode 100644
--- a/rpython/jit/backend/x86/test/test_strstorage.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from rpython.jit.backend.x86.test.test_basic import Jit386Mixin
-from rpython.jit.metainterp.test.test_strstorage import TestStrStorage as 
_TestStrStorage
-
-
-class TestStrStorage(Jit386Mixin, _TestStrStorage):
-    # for the individual tests see
-    # ====> ../../../metainterp/test/test_strstorage.py
-    pass
diff --git a/rpython/jit/codewriter/jtransform.py 
b/rpython/jit/codewriter/jtransform.py
--- a/rpython/jit/codewriter/jtransform.py
+++ b/rpython/jit/codewriter/jtransform.py
@@ -1131,6 +1131,26 @@
                               [op.args[0], op.args[1],
                                op.args[2], op.args[3], c_bytes], op.result)
 
+    def rewrite_op_gc_store_indexed(self, op):
+        T = op.args[2].concretetype
+        kind = getkind(T)[0]
+        assert kind != 'r'
+        descr = self.cpu.arraydescrof(rffi.CArray(T))
+        if (not isinstance(op.args[3], Constant) or
+            not isinstance(op.args[4], Constant)):
+            raise NotImplementedError("gc_store_indexed: 'scale' and 
'base_ofs'"
+                                      " should be constants")
+        # According to the comment in resoperation.py, "itemsize is not signed
+        # (always > 0)", so we don't need the "bytes = -bytes" line which is
+        # in rewrite_op_gc_load_indexed
+        bytes = descr.get_item_size_in_bytes()
+        c_bytes = Constant(bytes, lltype.Signed)
+        return SpaceOperation('gc_store_indexed_%s' % kind,
+                              [op.args[0], op.args[1], op.args[2],
+                               op.args[3], op.args[4], c_bytes, descr], None)
+
+
+
     def _rewrite_equality(self, op, opname):
         arg0, arg1 = op.args
         if isinstance(arg0, Constant) and not arg0.value:
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
@@ -1478,6 +1478,18 @@
     def bhimpl_gc_load_indexed_f(cpu, addr, index, scale, base_ofs, bytes):
         return cpu.bh_gc_load_indexed_f(addr, index,scale,base_ofs, bytes)
 
+    @arguments("cpu", "r", "i", "i", "i", "i", "i", "d")
+    def bhimpl_gc_store_indexed_i(cpu, addr, index, val, scale, base_ofs, 
bytes,
+                                  arraydescr):
+        return cpu.bh_gc_store_indexed_i(addr, index, val, scale,base_ofs, 
bytes,
+                                         arraydescr)
+
+    @arguments("cpu", "r", "i", "f", "i", "i", "i", "d")
+    def bhimpl_gc_store_indexed_f(cpu, addr, index, val, scale, base_ofs, 
bytes,
+                                  arraydescr):
+        return cpu.bh_gc_store_indexed_f(addr, index, val, scale,base_ofs, 
bytes,
+                                         arraydescr)
+
     @arguments("r", "d", "d")
     def bhimpl_record_quasiimmut_field(struct, fielddescr, mutatefielddescr):
         pass
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
@@ -110,7 +110,7 @@
     """
     log_noopt = False
 
-    def __init__(self, trace, celltoken, state, runtime_boxes,
+    def __init__(self, trace, celltoken, state,
                  call_pure_results=None, enable_opts=None,
                  inline_short_preamble=True):
         self.trace = trace
@@ -119,8 +119,6 @@
         self.state = state
         self.call_pure_results = call_pure_results
         self.inline_short_preamble = inline_short_preamble
-        assert runtime_boxes is not None
-        self.runtime_boxes = runtime_boxes
 
     def optimize(self, metainterp_sd, jitdriver_sd, optimizations, unroll):
         from rpython.jit.metainterp.optimizeopt.unroll import UnrollOptimizer
@@ -128,11 +126,7 @@
         assert unroll # we should not be here if it's disabled
         opt = UnrollOptimizer(metainterp_sd, jitdriver_sd, optimizations)
         return opt.optimize_peeled_loop(self.trace, self.celltoken, self.state,
-            self.runtime_boxes, self.call_pure_results, 
self.inline_short_preamble)
-
-    def forget_optimization_info(self):
-        self.state.forget_optimization_info()
-        CompileData.forget_optimization_info(self)
+            self.call_pure_results, self.inline_short_preamble)
 
 def show_procedures(metainterp_sd, procedure=None, error=None):
     # debugging
@@ -298,7 +292,7 @@
     start_descr = TargetToken(jitcell_token,
                               original_jitcell_token=jitcell_token)
     jitcell_token.target_tokens = [start_descr]
-    loop_data = UnrolledLoopData(trace, jitcell_token, start_state, jumpargs,
+    loop_data = UnrolledLoopData(trace, jitcell_token, start_state,
                                  call_pure_results=call_pure_results,
                                  enable_opts=enable_opts)
     try:
@@ -368,7 +362,7 @@
     history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token)
     enable_opts = jitdriver_sd.warmstate.enable_opts
     call_pure_results = metainterp.call_pure_results
-    loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state, 
jumpargs,
+    loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state,
                                  call_pure_results=call_pure_results,
                                  enable_opts=enable_opts)
     try:
@@ -380,7 +374,6 @@
         history.cut(cut)
         history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token)
         loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state,
-                                     jumpargs,
                                      call_pure_results=call_pure_results,
                                      enable_opts=enable_opts,
                                      inline_short_preamble=False)
@@ -525,7 +518,7 @@
     for item in lst:
         item.set_forwarded(None)
         # XXX we should really do it, but we need to remember the values
-        #     somehow for ContinueRunningNormally
+        #     somehoe for ContinueRunningNormally
         if reset_values:
             item.reset_value()
 
diff --git a/rpython/jit/metainterp/executor.py 
b/rpython/jit/metainterp/executor.py
--- a/rpython/jit/metainterp/executor.py
+++ b/rpython/jit/metainterp/executor.py
@@ -251,6 +251,25 @@
     else:
         return BoxInt(cpu.bh_raw_load_i(addr, offset, arraydescr))
 
+def do_gc_store_indexed(cpu, _, addrbox, indexbox, valuebox, scalebox,
+                        base_ofsbox, bytesbox, arraydescr):
+    addr = addrbox.getref_base()
+    index = indexbox.getint()
+    scale = scalebox.getint()
+    base_ofs = base_ofsbox.getint()
+    bytes = bytesbox.getint()
+    if arraydescr.is_array_of_pointers():
+        raise AssertionError("cannot store GC pointers in gc_store_indexed for 
now")
+    elif arraydescr.is_array_of_floats():
+        floatval = valuebox.getfloatstorage()
+        cpu.bh_gc_store_indexed_f(addr, index, floatval, scale, base_ofs, 
bytes,
+                                  arraydescr)
+    else:
+        intval = valuebox.getint()
+        cpu.bh_gc_store_indexed_i(addr, index, intval, scale, base_ofs, bytes,
+                                  arraydescr)
+
+
 def exec_new_with_vtable(cpu, descr):
     return cpu.bh_new_with_vtable(descr)
 
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
@@ -12,7 +12,7 @@
 from rpython.jit.metainterp.optimize import InvalidLoop
 from rpython.jit.metainterp.resoperation import rop, ResOperation, OpHelpers,\
      AbstractResOp, GuardResOp
-from rpython.rlib.objectmodel import we_are_translated, we_are_debug
+from rpython.rlib.objectmodel import we_are_translated
 from rpython.jit.metainterp.optimizeopt import info
         
 
@@ -173,7 +173,7 @@
 
     def _getfield(self, opinfo, descr, optheap, true_force=True):
         res = opinfo.getfield(descr, optheap)
-        if we_are_debug() and res:
+        if not we_are_translated() and res:
             if isinstance(opinfo, info.AbstractStructPtrInfo):
                 assert opinfo in self.cached_infos
         if isinstance(res, PreambleOp):
@@ -203,7 +203,7 @@
 
     def _getfield(self, opinfo, descr, optheap, true_force=True):
         res = opinfo.getitem(descr, self.index, optheap)
-        if we_are_debug() and res:
+        if not we_are_translated() and res:
             if isinstance(opinfo, info.ArrayPtrInfo):
                 assert opinfo in self.cached_infos
         if (isinstance(res, PreambleOp) and
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
@@ -24,20 +24,9 @@
 llhelper.CONST_NULLREF = llhelper.CONST_NULL
 REMOVED = AbstractResOp()
 
-def check_no_forwarding(lsts):
-    for lst in lsts:
-        for op in lst:
-            assert op.get_forwarded() is None
-
 class LoopInfo(object):
     label_op = None
 
-    def _check_no_forwarding(self):
-        pass
-
-    def forget_optimization_info(self):
-        pass
-
 class BasicLoopInfo(LoopInfo):
     def __init__(self, inputargs, quasi_immutable_deps, jump_op):
         self.inputargs = inputargs
@@ -567,8 +556,7 @@
         return (BasicLoopInfo(trace.inputargs, self.quasi_immutable_deps, 
last_op),
                 self._newoperations)
 
-    @staticmethod
-    def _clean_optimization_info(lst):
+    def _clean_optimization_info(self, lst):
         for op in lst:
             if op.get_forwarded() is not None:
                 op.set_forwarded(None)
diff --git a/rpython/jit/metainterp/optimizeopt/shortpreamble.py 
b/rpython/jit/metainterp/optimizeopt/shortpreamble.py
--- a/rpython/jit/metainterp/optimizeopt/shortpreamble.py
+++ b/rpython/jit/metainterp/optimizeopt/shortpreamble.py
@@ -5,7 +5,6 @@
      rop, AbstractResOp, AbstractInputArg
 from rpython.jit.metainterp.history import Const, make_hashable_int,\
      TreeLoop
-from rpython.jit.metainterp.optimize import InvalidLoop
 from rpython.jit.metainterp.optimizeopt import info
 
 class PreambleOp(AbstractResOp):
@@ -19,7 +18,7 @@
     See force_op_from_preamble for details how the extra things are put.
     """
     op = None
-
+    
     def __init__(self, op, preamble_op, invented_name):
         self.op = op
         self.preamble_op = preamble_op
@@ -52,13 +51,7 @@
 class AbstractShortOp(object):
     """ An operation that is potentially produced by the short preamble
     """
-    res = None
-
-    def _check_no_forwarding(self):
-        assert self.res.get_forwarded() is None
-
-    def forget_optimization_info(self):
-        self.res.clear_forwarded()
+    pass
 
 class HeapOp(AbstractShortOp):
     def __init__(self, res, getfield_op):
@@ -108,14 +101,6 @@
                                        descr=sop.getdescr())
         return ProducedShortOp(self, preamble_op)
 
-    def _check_no_forwarding(self):
-        AbstractShortOp._check_no_forwarding(self)
-        assert self.getfield_op.get_forwarded() is None
-
-    def forget_optimization_info(self):
-        AbstractShortOp.forget_optimization_info(self)
-        self.getfield_op.clear_forwarded()
-
     def __repr__(self):
         return "HeapOp(%r)" % (self.res,)
 
@@ -208,16 +193,6 @@
                 l.append(pop)
         return l
 
-    def _check_no_forwarding(self):
-        AbstractShortOp._check_no_forwarding(self)
-        self.one._check_no_forwarding()
-        self.two._check_no_forwarding()
-
-    def forget_optimization_info(self):
-        AbstractShortOp.forget_optimization_info(self)
-        self.one.forget_optimization_info()
-        self.two.forget_optimization_info()
-
     def repr(self, memo):
         return "CompoundOp(%s, %s, %s)" % (self.res.repr(memo),
                                            self.one.repr(memo),
@@ -228,7 +203,7 @@
 
 class ProducedShortOp(AbstractProducedShortOp):
     invented_name = False
-
+    
     def __init__(self, short_op, preamble_op):
         self.short_op = short_op
         self.preamble_op = preamble_op
@@ -240,14 +215,6 @@
     def repr(self, memo):
         return self.short_op.repr(memo)
 
-    def _check_no_forwarding(self):
-        self.short_op._check_no_forwarding()
-        assert self.preamble_op.get_forwarded() is None
-
-    def forget_optimization_info(self):
-        self.short_op.forget_optimization_info()
-        self.preamble_op.clear_forwarded()
-
     def __repr__(self):
         return "%r -> %r" % (self.short_op, self.preamble_op)
 
@@ -268,14 +235,6 @@
     def repr(self, memo):
         return "INP(%s)" % (self.res.repr(memo),)
 
-    def _check_no_forwarding(self):
-        AbstractShortOp._check_no_forwarding(self)
-        assert self.preamble_op.get_forwarded() is None
-
-    def forget_optimization_info(self):
-        AbstractShortOp.forget_optimization_info(self)
-        self.preamble_op.clear_forwarded()
-
     def __repr__(self):
         return "INP(%r -> %r)" % (self.res, self.preamble_op)
 
@@ -495,23 +454,16 @@
         self.sb = sb
         self.extra_same_as = self.sb.extra_same_as
         self.target_token = target_token
-        self.build_inplace = False
 
     def setup(self, jump_args, short, label_args):
         self.jump_args = jump_args
         self.short = short
         self.label_args = label_args
-        self.build_inplace = True
 
     def add_preamble_op(self, preamble_op):
         """ Notice that we're actually using the preamble_op, add it to
         label and jump
         """
-        # Could this be considered a speculative error?
-        # This check should only fail when trying to jump to an existing trace
-        # by forcing portions of the virtualstate.
-        if not self.build_inplace:
-            raise InvalidLoop("Forcing boxes would modify an existing short 
preamble")
         op = preamble_op.op.get_box_replacement()
         if preamble_op.invented_name:
             self.extra_same_as.append(op)
@@ -519,8 +471,6 @@
         self.jump_args.append(preamble_op.preamble_op)
 
     def use_box(self, box, preamble_op, optimizer=None):
-        if not self.build_inplace:
-            raise InvalidLoop("Forcing boxes would modify an existing short 
preamble")
         jump_op = self.short.pop()
         AbstractShortPreambleBuilder.use_box(self, box, preamble_op, optimizer)
         self.short.append(jump_op)
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py 
b/rpython/jit/metainterp/optimizeopt/test/test_util.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_util.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py
@@ -577,7 +577,6 @@
         #
         compile_data.enable_opts = self.enable_opts
         state = optimize_trace(metainterp_sd, None, compile_data)
-        state[0]._check_no_forwarding()
         return state
 
     def _convert_call_pure_results(self, d):
@@ -626,7 +625,7 @@
         start_state, preamble_ops = self._do_optimize_loop(preamble_data)
         preamble_data.forget_optimization_info()
         loop_data = compile.UnrolledLoopData(preamble_data.trace,
-            celltoken, start_state, runtime_boxes, call_pure_results)
+            celltoken, start_state, call_pure_results)
         loop_info, ops = self._do_optimize_loop(loop_data)
         preamble = TreeLoop('preamble')
         preamble.inputargs = start_state.renamed_inputargs
diff --git a/rpython/jit/metainterp/optimizeopt/unroll.py 
b/rpython/jit/metainterp/optimizeopt/unroll.py
--- a/rpython/jit/metainterp/optimizeopt/unroll.py
+++ b/rpython/jit/metainterp/optimizeopt/unroll.py
@@ -6,7 +6,7 @@
 from rpython.jit.metainterp.optimizeopt import info, intutils
 from rpython.jit.metainterp.optimize import InvalidLoop, SpeculativeError
 from rpython.jit.metainterp.optimizeopt.optimizer import Optimizer,\
-     Optimization, LoopInfo, MININT, MAXINT, BasicLoopInfo, check_no_forwarding
+     Optimization, LoopInfo, MININT, MAXINT, BasicLoopInfo
 from rpython.jit.metainterp.optimizeopt.vstring import StrPtrInfo
 from rpython.jit.metainterp.optimizeopt.virtualstate import (
     VirtualStateConstructor, VirtualStatesCantMatch)
@@ -35,7 +35,7 @@
 
     def setinfo_from_preamble_list(self, lst, infos):
         for item in lst:
-            if item is None or isinstance(item, Const):
+            if item is None:
                 continue
             i = infos.get(item, None)
             if i is not None:
@@ -99,6 +99,7 @@
         elif isinstance(preamble_info, info.FloatConstInfo):
             op.set_forwarded(preamble_info._const)
 
+
 class UnrollOptimizer(Optimization):
     """Unroll the loop into two iterations. The first one will
     become the preamble or entry bridge (don't think there is a
@@ -116,22 +117,26 @@
         return modifier.get_virtual_state(args)
 
     def _check_no_forwarding(self, lsts, check_newops=True):
-        check_no_forwarding(lsts)
+        for lst in lsts:
+            for op in lst:
+                assert op.get_forwarded() is None
         if check_newops:
             assert not self.optimizer._newoperations
 
+
     def optimize_preamble(self, trace, runtime_boxes, call_pure_results, memo):
         info, newops = self.optimizer.propagate_all_forward(
             trace.get_iter(), call_pure_results, flush=False)
         exported_state = self.export_state(info.jump_op.getarglist(),
-                                           info.inputargs, memo)
+                                           info.inputargs,
+                                           runtime_boxes, memo)
         exported_state.quasi_immutable_deps = info.quasi_immutable_deps
         # we need to absolutely make sure that we've cleaned up all
         # the optimization info
         self.optimizer._clean_optimization_info(self.optimizer._newoperations)
         return exported_state, self.optimizer._newoperations
 
-    def optimize_peeled_loop(self, trace, celltoken, state, runtime_boxes,
+    def optimize_peeled_loop(self, trace, celltoken, state,
                              call_pure_results, inline_short_preamble=True):
         trace = trace.get_iter()
         try:
@@ -183,7 +188,7 @@
 
         try:
             new_virtual_state = self.jump_to_existing_trace(
-                    end_jump, label_op, runtime_boxes, force_boxes=False)
+                    end_jump, label_op, state.runtime_boxes, force_boxes=False)
         except InvalidLoop:
             # inlining short preamble failed, jump to preamble
             self.jump_to_preamble(celltoken, end_jump, info)
@@ -196,7 +201,7 @@
             # to the preamble.
             try:
                 new_virtual_state = self.jump_to_existing_trace(
-                        end_jump, label_op, runtime_boxes, force_boxes=True)
+                        end_jump, label_op, state.runtime_boxes, 
force_boxes=True)
             except InvalidLoop:
                 pass
 
@@ -279,7 +284,8 @@
             debug_print("Retrace count reached, jumping to preamble")
             return self.jump_to_preamble(cell_token, jump_op, info)
         exported_state = self.export_state(info.jump_op.getarglist(),
-                                           info.inputargs, box_names_memo)
+                                           info.inputargs, runtime_boxes,
+                                           box_names_memo)
         exported_state.quasi_immutable_deps = 
self.optimizer.quasi_immutable_deps
         self.optimizer._clean_optimization_info(self.optimizer._newoperations)
         return exported_state, self.optimizer._newoperations
@@ -443,7 +449,8 @@
                 continue
             self._expand_info(item, infos)
 
-    def export_state(self, original_label_args, renamed_inputargs, memo):
+    def export_state(self, original_label_args, renamed_inputargs,
+                     runtime_boxes, memo):
         end_args = [self.optimizer.force_box_for_end_of_preamble(a)
                     for a in original_label_args]
         self.optimizer.flush()
@@ -464,17 +471,16 @@
             op = produced_op.short_op.res
             if not isinstance(op, Const):
                 self._expand_info(op, infos)
+        self.optimizer._clean_optimization_info(end_args)
         return ExportedState(label_args, end_args, virtual_state, infos,
                              short_boxes, renamed_inputargs,
-                             short_inputargs, memo)
+                             short_inputargs, runtime_boxes, memo)
 
     def import_state(self, targetargs, exported_state):
         # the mapping between input args (from old label) and what we need
         # to actually emit. Update the info
         assert (len(exported_state.next_iteration_args) ==
                 len(targetargs))
-        self._check_no_forwarding([targetargs])
-        exported_state._check_no_forwarding()
         for i, target in enumerate(exported_state.next_iteration_args):
             source = targetargs[i]
             assert source is not target
@@ -530,11 +536,13 @@
     * renamed_inputargs - the start label arguments in optimized version
     * short_inputargs - the renamed inputargs for short preamble
     * quasi_immutable_deps - for tracking quasi immutables
+    * runtime_boxes - runtime values for boxes, necessary when generating
+                      guards to jump to
     """
 
     def __init__(self, end_args, next_iteration_args, virtual_state,
                  exported_infos, short_boxes, renamed_inputargs,
-                 short_inputargs, memo):
+                 short_inputargs, runtime_boxes, memo):
         self.end_args = end_args
         self.next_iteration_args = next_iteration_args
         self.virtual_state = virtual_state
@@ -542,8 +550,8 @@
         self.short_boxes = short_boxes
         self.renamed_inputargs = renamed_inputargs
         self.short_inputargs = short_inputargs
+        self.runtime_boxes = runtime_boxes
         self.dump(memo)
-        self.forget_optimization_info()
 
     def dump(self, memo):
         if have_debug_prints():
@@ -553,35 +561,5 @@
                 debug_print("  " + box.repr(memo))
             debug_stop("jit-log-exported-state")
 
-    def _check_no_forwarding(self):
-        """ Ensures that no optimization state is attached to relevant 
operations
-        before importing anything. """
-        # Some of these may be redunant
-        check_no_forwarding([
-            self.end_args,
-            self.next_iteration_args,
-            self.renamed_inputargs,
-            self.short_inputargs,
-            self.exported_infos.keys()])
-        for box in self.short_boxes:
-            box._check_no_forwarding()
-
-    def forget_optimization_info(self):
-        """ Clean up optimization info on all operations stored in the 
ExportedState.
-
-        This function needs to be called when exporting the optimizer state to
-        prevent leaking of optimization information between invocations of the
-        optimizer.
-
-        That includes cleaning up in the event that optimize_peeled_loop() 
fails
-        with an InvalidLoop exception, as optimize_peeled_loop() mutates the
-        contents of ExportedState.
-        """
-        Optimizer._clean_optimization_info(self.renamed_inputargs)
-        for box in self.exported_infos.iterkeys():
-            box.clear_forwarded()
-        for box in self.short_boxes:
-            box.forget_optimization_info()
-
     def final(self):
         return False
diff --git a/rpython/jit/metainterp/pyjitpl.py 
b/rpython/jit/metainterp/pyjitpl.py
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -827,6 +827,21 @@
                             self._remove_symbolics(scalebox),
                             self._remove_symbolics(baseofsbox), bytesbox)
 
+    @arguments("box", "box", "box", "box", "box", "box", "descr")
+    def _opimpl_gc_store_indexed(self, addrbox, indexbox, valuebox,
+                                 scalebox, baseofsbox, bytesbox,
+                                 arraydescr):
+        return self.execute_with_descr(rop.GC_STORE_INDEXED,
+                                       arraydescr,
+                                       addrbox,
+                                       indexbox,
+                                       valuebox,
+                                       self._remove_symbolics(scalebox),
+                                       self._remove_symbolics(baseofsbox),
+                                       bytesbox)
+    opimpl_gc_store_indexed_i = _opimpl_gc_store_indexed
+    opimpl_gc_store_indexed_f = _opimpl_gc_store_indexed
+
     @arguments("box")
     def opimpl_hint_force_virtualizable(self, box):
         self.metainterp.gen_store_back_in_vable(box)
@@ -2024,8 +2039,6 @@
         self.aborted_tracing_greenkey = None
 
     def retrace_needed(self, trace, exported_state):
-        if not we_are_translated():
-            exported_state._check_no_forwarding()
         self.partial_trace = trace
         self.retracing_from = self.potential_retrace_position
         self.exported_state = exported_state
diff --git a/rpython/jit/metainterp/resoperation.py 
b/rpython/jit/metainterp/resoperation.py
--- a/rpython/jit/metainterp/resoperation.py
+++ b/rpython/jit/metainterp/resoperation.py
@@ -52,10 +52,6 @@
         llop.debug_print(lltype.Void, "setting forwarded on:", 
self.__class__.__name__)
         raise SettingForwardedOnAbstractValue()
 
-    def clear_forwarded(self):
-        if self.get_forwarded() is not None:
-            self.set_forwarded(None)
-
     @specialize.arg(1)
     def get_box_replacement(op, not_const=False):
         # Read the chain "op, op._forwarded, op._forwarded._forwarded..."
diff --git a/rpython/jit/metainterp/test/support.py 
b/rpython/jit/metainterp/test/support.py
--- a/rpython/jit/metainterp/test/support.py
+++ b/rpython/jit/metainterp/test/support.py
@@ -19,7 +19,8 @@
                   supports_floats=True,
                   supports_longlong=False,
                   supports_singlefloats=False,
-                  translationoptions={}, **kwds):
+                  translationoptions={},
+                  backendopt_inline_threshold=0, **kwds):
     from rpython.jit.codewriter import support
 
     class FakeJitCell(object):
@@ -59,7 +60,7 @@
         FakeWarmRunnerState.enable_opts = {}
 
     func._jit_unroll_safe_ = True
-    rtyper = support.annotate(func, values,
+    rtyper = support.annotate(func, values, inline=backendopt_inline_threshold,
                               translationoptions=translationoptions)
     graphs = rtyper.annotator.translator.graphs
     testself.all_graphs = graphs
diff --git a/rpython/jit/metainterp/test/test_llop.py 
b/rpython/jit/metainterp/test/test_llop.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/metainterp/test/test_llop.py
@@ -0,0 +1,72 @@
+import py
+import sys
+import struct
+from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rtyper.test.test_llop import (BaseLLOpTest, str_gc_load,
+                                           newlist_and_gc_store)
+from rpython.jit.codewriter import longlong
+from rpython.jit.metainterp.history import getkind
+from rpython.jit.metainterp.test.support import LLJitMixin
+
+
+class TestLLOp(BaseLLOpTest, LLJitMixin):
+
+    # for the individual tests see
+    # ====> ../../../rtyper/test/test_llop.py
+    TEST_BLACKHOLE = True
+
+    def gc_load_from_string(self, TYPE, buf, offset):
+        def f(offset):
+            return str_gc_load(TYPE, buf, offset)
+        res = self.interp_operations(f, [offset], supports_singlefloats=True)
+        #
+        kind = getkind(TYPE)[0] # 'i' or 'f'
+        self.check_operations_history({'gc_load_indexed_%s' % kind: 1,
+                                       'finish': 1})
+        #
+        if TYPE == lltype.SingleFloat:
+            # interp_operations returns the int version of r_singlefloat, but
+            # our tests expects to receive an r_singlefloat: let's convert it
+            # back!
+            return longlong.int2singlefloat(res)
+        return res
+
+    def newlist_and_gc_store(self, TYPE, value, expected):
+        def f(value):
+            lst = newlist_and_gc_store(TYPE, value)
+            got = ''.join(lst)
+            if got != expected:
+                # I'm not sure why, but if I use an assert, the test doesn't 
fail
+                raise ValueError('got != expected')
+            return len(got)
+        #
+        if self.TEST_BLACKHOLE:
+            # we pass a big inline_threshold to ensure that
+            # newlist_and_gc_store is inlined, else the blackhole does not see
+            # (and thus we do not test!) the llop.gc_store_indexed
+            threshold = 33
+        else:
+            threshold = 0
+        return self.interp_operations(f, [value], supports_singlefloats=True,
+                                      backendopt_inline_threshold=threshold)
+
+
+    def test_force_virtual_str_storage(self):
+        byteorder = sys.byteorder
+        size = rffi.sizeof(lltype.Signed)
+        def f(val):
+            if byteorder == 'little':
+                x = chr(val) + '\x00'*(size-1)
+            else:
+                x = '\x00'*(size-1) + chr(val)
+            return str_gc_load(lltype.Signed, x, 0)
+        res = self.interp_operations(f, [42], supports_singlefloats=True)
+        assert res == 42
+        self.check_operations_history({
+            'newstr': 1,              # str forcing
+            'strsetitem': 1,          # str forcing
+            'call_pure_r': 1,         # str forcing (copystrcontent)
+            'guard_no_exception': 1,  # str forcing
+            'gc_load_indexed_i': 1,   # str_storage_getitem
+            'finish': 1
+            })
diff --git a/rpython/jit/metainterp/test/test_strstorage.py 
b/rpython/jit/metainterp/test/test_strstorage.py
deleted file mode 100644
--- a/rpython/jit/metainterp/test/test_strstorage.py
+++ /dev/null
@@ -1,53 +0,0 @@
-import py
-import sys
-import struct
-from rpython.rtyper.lltypesystem import lltype, rffi
-from rpython.rlib.strstorage import str_storage_getitem
-from rpython.rlib.test.test_strstorage import BaseStrStorageTest
-from rpython.jit.codewriter import longlong
-from rpython.jit.metainterp.history import getkind
-from rpython.jit.metainterp.test.support import LLJitMixin
-
-class TestStrStorage(BaseStrStorageTest, LLJitMixin):
-
-    # for the individual tests see
-    # ====> ../../../rlib/test/test_strstorage.py
-
-    def str_storage_getitem(self, TYPE, buf, offset):
-        def f():
-            return str_storage_getitem(TYPE, buf, offset)
-        res = self.interp_operations(f, [], supports_singlefloats=True)
-        #
-        kind = getkind(TYPE)[0] # 'i' or 'f'
-        self.check_operations_history({'gc_load_indexed_%s' % kind: 1,
-                                       'finish': 1})
-        #
-        if TYPE == lltype.SingleFloat:
-            # interp_operations returns the int version of r_singlefloat, but
-            # our tests expects to receive an r_singlefloat: let's convert it
-            # back!
-            return longlong.int2singlefloat(res)
-        return res
-
-    #def str_storage_supported(self, TYPE):
-    #    py.test.skip('this is not a JIT test')
-
-    def test_force_virtual_str_storage(self):
-        byteorder = sys.byteorder
-        size = rffi.sizeof(lltype.Signed)
-        def f(val):
-            if byteorder == 'little':
-                x = chr(val) + '\x00'*(size-1)
-            else:
-                x = '\x00'*(size-1) + chr(val)
-            return str_storage_getitem(lltype.Signed, x, 0)
-        res = self.interp_operations(f, [42], supports_singlefloats=True)
-        assert res == 42
-        self.check_operations_history({
-            'newstr': 1,              # str forcing
-            'strsetitem': 1,          # str forcing
-            'call_pure_r': 1,         # str forcing (copystrcontent)
-            'guard_no_exception': 1,  # str forcing
-            'gc_load_indexed_i': 1,   # str_storage_getitem
-            'finish': 1
-            })
diff --git a/rpython/rlib/buffer.py b/rpython/rlib/buffer.py
--- a/rpython/rlib/buffer.py
+++ b/rpython/rlib/buffer.py
@@ -1,13 +1,55 @@
 """
 Buffer protocol support.
 """
-from rpython.rlib.rgc import (
-    nonmoving_raw_ptr_for_resizable_list, resizable_list_supporting_raw_ptr)
+from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.rtyper.lltypesystem.lloperation import llop
+from rpython.rtyper.lltypesystem.rstr import STR
+from rpython.rtyper.lltypesystem.rlist import LIST_OF
+from rpython.rtyper.annlowlevel import llstr
+from rpython.rlib.objectmodel import specialize
+from rpython.rlib import jit
+from rpython.rlib.rgc import (resizable_list_supporting_raw_ptr,
+                              nonmoving_raw_ptr_for_resizable_list,
+                              ll_for_resizable_list)
 from rpython.rlib.signature import signature
 from rpython.rlib import types
+from rpython.rlib import rawstorage
+
+ALLOW_UNALIGNED_ACCESS = rawstorage.misaligned_is_fine
+
+@specialize.ll()
+def is_alignment_correct(TYPE, index):
+    if ALLOW_UNALIGNED_ACCESS:
+        return True
+    try:
+        rawstorage._check_alignment(TYPE, index)
+    except rawstorage.AlignmentError:
+        return False
+    else:
+        return True
+
+
+class CannotRead(Exception):
+    """
+    Exception raised by Buffer.typed_read in case it is not possible to
+    accomplish the request. This might be because it is not supported by the
+    specific type of buffer, or because of alignment issues.
+    """
+
+class CannotWrite(Exception):
+    """
+    Raised by Buffer.typed_write in case it is not possible to accomplish the
+    request
+    """
 
 class Buffer(object):
-    """Base class for buffers of bytes"""
+    """
+    Base class for buffers of bytes.
+
+    Most probably, you do NOT want to use this as a lone base class, but
+    either inherit from RawBuffer or GCBuffer, so that you automatically get
+    the proper implementation of typed_read and typed_write.
+    """
     _attrs_ = ['readonly']
     _immutable_ = True
 
@@ -25,14 +67,6 @@
         # May be overridden.
         return self.getslice(0, self.getlength(), 1, self.getlength())
 
-    def as_str_and_offset_maybe(self):
-        """
-        If the buffer is backed by a string, return a pair (string, offset),
-        where offset is the offset inside the string where the buffer start.
-        Else, return (None, 0).
-        """
-        return None, 0
-
     def getitem(self, index):
         "Returns the index'th character in the buffer."
         raise NotImplementedError   # Must be overriden.  No bounds checks.
@@ -60,7 +94,141 @@
         for i in range(len(string)):
             self.setitem(start + i, string[i])
 
-class ByteBuffer(Buffer):
+    @jit.look_inside_iff(lambda self, index, count:
+                         jit.isconstant(count) and count <= 8)
+    def setzeros(self, index, count):
+        for i in range(index, index+count):
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to