[pypy-commit] pypy default: Merge the virtual-raw-mallocs branch which, as the name suggests, makes it

antocuni noreply at buildbot.pypy.org
Thu Apr 11 13:53:27 CEST 2013


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: 
Changeset: r63227:0a0a7ff924c2
Date: 2013-04-11 12:51 +0100
http://bitbucket.org/pypy/pypy/changeset/0a0a7ff924c2/

Log:	Merge the virtual-raw-mallocs branch which, as the name suggests,
	makes it possible to make raw-malloced blocks of memory as virtual
	during the optimizeopt step. This is especially useful for cffi
	calls, because it removes all the overhead of writing the parameters
	into the temp buffer and then immediately reading them, before doing
	the actuall ffi call.

diff too long, truncating to 2000 out of 2422 lines

diff --git a/pypy/module/_cffi_backend/ccallback.py b/pypy/module/_cffi_backend/ccallback.py
--- a/pypy/module/_cffi_backend/ccallback.py
+++ b/pypy/module/_cffi_backend/ccallback.py
@@ -134,7 +134,7 @@
             # W_CTypePrimitiveSigned.convert_from_object() in order
             # to write a whole 'ffi_arg'.
             value = misc.as_long(space, w_res)
-            misc.write_raw_integer_data(ll_res, value, SIZE_OF_FFI_ARG)
+            misc.write_raw_signed_data(ll_res, value, SIZE_OF_FFI_ARG)
             return
         else:
             # zero extension: fill the '*result' with zeros, and (on big-
diff --git a/pypy/module/_cffi_backend/cdataobj.py b/pypy/module/_cffi_backend/cdataobj.py
--- a/pypy/module/_cffi_backend/cdataobj.py
+++ b/pypy/module/_cffi_backend/cdataobj.py
@@ -280,8 +280,13 @@
         return self.ctype.iter(self)
 
     @specialize.argtype(1)
-    def write_raw_integer_data(self, source):
-        misc.write_raw_integer_data(self._cdata, source, self.ctype.size)
+    def write_raw_signed_data(self, source):
+        misc.write_raw_signed_data(self._cdata, source, self.ctype.size)
+        keepalive_until_here(self)
+
+    @specialize.argtype(1)
+    def write_raw_unsigned_data(self, source):
+        misc.write_raw_unsigned_data(self._cdata, source, self.ctype.size)
         keepalive_until_here(self)
 
     def write_raw_float_data(self, source):
diff --git a/pypy/module/_cffi_backend/ctypeprim.py b/pypy/module/_cffi_backend/ctypeprim.py
--- a/pypy/module/_cffi_backend/ctypeprim.py
+++ b/pypy/module/_cffi_backend/ctypeprim.py
@@ -63,7 +63,7 @@
         else:
             value = self._cast_generic(w_ob)
         w_cdata = cdataobj.W_CDataMem(space, self.size, self)
-        w_cdata.write_raw_integer_data(value)
+        self.write_raw_integer_data(w_cdata, value)
         return w_cdata
 
     def _cast_result(self, intvalue):
@@ -94,6 +94,9 @@
         from pypy.module._cffi_backend import newtype
         return newtype.new_primitive_type(self.space, "int")
 
+    def write_raw_integer_data(self, w_cdata, value):
+        w_cdata.write_raw_unsigned_data(value)
+
 
 class W_CTypePrimitiveChar(W_CTypePrimitiveCharOrUniChar):
     _attrs_ = []
@@ -185,10 +188,10 @@
             if self.size < rffi.sizeof(lltype.Signed):
                 if r_uint(value) - self.vmin > self.vrangemax:
                     self._overflow(w_ob)
-            misc.write_raw_integer_data(cdata, value, self.size)
+            misc.write_raw_signed_data(cdata, value, self.size)
         else:
             value = misc.as_long_long(self.space, w_ob)
-            misc.write_raw_integer_data(cdata, value, self.size)
+            misc.write_raw_signed_data(cdata, value, self.size)
 
     def get_vararg_type(self):
         if self.size < rffi.sizeof(rffi.INT):
@@ -196,6 +199,9 @@
             return newtype.new_primitive_type(self.space, "int")
         return self
 
+    def write_raw_integer_data(self, w_cdata, value):
+        w_cdata.write_raw_signed_data(value)
+
 
 class W_CTypePrimitiveUnsigned(W_CTypePrimitive):
     _attrs_            = ['value_fits_long', 'value_fits_ulong', 'vrangemax']
@@ -222,10 +228,10 @@
             if self.value_fits_long:
                 if value > self.vrangemax:
                     self._overflow(w_ob)
-            misc.write_raw_integer_data(cdata, value, self.size)
+            misc.write_raw_unsigned_data(cdata, value, self.size)
         else:
             value = misc.as_unsigned_long_long(self.space, w_ob, strict=True)
-            misc.write_raw_integer_data(cdata, value, self.size)
+            misc.write_raw_unsigned_data(cdata, value, self.size)
 
     def convert_to_object(self, cdata):
         if self.value_fits_ulong:
@@ -244,6 +250,9 @@
             return newtype.new_primitive_type(self.space, "int")
         return self
 
+    def write_raw_integer_data(self, w_cdata, value):
+        w_cdata.write_raw_unsigned_data(value)
+
 
 class W_CTypePrimitiveBool(W_CTypePrimitiveUnsigned):
     _attrs_ = []
diff --git a/pypy/module/_cffi_backend/ctypestruct.py b/pypy/module/_cffi_backend/ctypestruct.py
--- a/pypy/module/_cffi_backend/ctypestruct.py
+++ b/pypy/module/_cffi_backend/ctypestruct.py
@@ -242,11 +242,13 @@
         #
         value = misc.as_long_long(space, w_ob)
         if isinstance(ctype, ctypeprim.W_CTypePrimitiveSigned):
+            is_signed = True
             fmin = -(r_longlong(1) << (self.bitsize - 1))
             fmax = (r_longlong(1) << (self.bitsize - 1)) - 1
             if fmax == 0:
                 fmax = 1      # special case to let "int x:1" receive "1"
         else:
+            is_signed = False
             fmin = r_longlong(0)
             fmax = r_longlong((r_ulonglong(1) << self.bitsize) - 1)
         if value < fmin or value > fmax:
@@ -258,7 +260,10 @@
         rawvalue = r_ulonglong(value) << self.bitshift
         rawfielddata = misc.read_raw_unsigned_data(cdata, ctype.size)
         rawfielddata = (rawfielddata & ~rawmask) | (rawvalue & rawmask)
-        misc.write_raw_integer_data(cdata, rawfielddata, ctype.size)
+        if is_signed:
+            misc.write_raw_signed_data(cdata, rawfielddata, ctype.size)
+        else:
+            misc.write_raw_unsigned_data(cdata, rawfielddata, ctype.size)
 
 
 W_CField.typedef = TypeDef(
diff --git a/pypy/module/_cffi_backend/misc.py b/pypy/module/_cffi_backend/misc.py
--- a/pypy/module/_cffi_backend/misc.py
+++ b/pypy/module/_cffi_backend/misc.py
@@ -9,6 +9,7 @@
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 from rpython.translator.tool.cbuild import ExternalCompilationInfo
 
+
 # ____________________________________________________________
 
 _prim_signed_types = unrolling_iterable([
@@ -65,19 +66,22 @@
     return rffi.cast(rffi.LONGDOUBLEP, target)[0]
 
 @specialize.argtype(1)
-def write_raw_integer_data(target, source, size):
-    if is_signed_integer_type(lltype.typeOf(source)):
-        for TP, TPP in _prim_signed_types:
-            if size == rffi.sizeof(TP):
-                rffi.cast(TPP, target)[0] = rffi.cast(TP, source)
-                return
-    else:
-        for TP, TPP in _prim_unsigned_types:
-            if size == rffi.sizeof(TP):
-                rffi.cast(TPP, target)[0] = rffi.cast(TP, source)
-                return
+def write_raw_unsigned_data(target, source, size):
+    for TP, TPP in _prim_unsigned_types:
+        if size == rffi.sizeof(TP):
+            rffi.cast(TPP, target)[0] = rffi.cast(TP, source)
+            return
     raise NotImplementedError("bad integer size")
 
+ at specialize.argtype(1)
+def write_raw_signed_data(target, source, size):
+    for TP, TPP in _prim_signed_types:
+        if size == rffi.sizeof(TP):
+            rffi.cast(TPP, target)[0] = rffi.cast(TP, source)
+            return
+    raise NotImplementedError("bad integer size")
+
+
 def write_raw_float_data(target, source, size):
     for TP, TPP in _prim_float_types:
         if size == rffi.sizeof(TP):
diff --git a/pypy/module/pypyjit/test_pypy_c/test__ffi.py b/pypy/module/pypyjit/test_pypy_c/test_ffi.py
rename from pypy/module/pypyjit/test_pypy_c/test__ffi.py
rename to 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
@@ -181,32 +181,39 @@
 
             libm = _cffi_backend.load_library(libm_name)
             BDouble = _cffi_backend.new_primitive_type("double")
-            BPow = _cffi_backend.new_function_type([BDouble, BDouble], BDouble)
-            pow = libm.load_function(BPow, 'pow')
+            BInt = _cffi_backend.new_primitive_type("int")
+            BPow = _cffi_backend.new_function_type([BDouble, BInt], BDouble)
+            ldexp = libm.load_function(BPow, 'ldexp')
             i = 0
             res = 0
             while i < 300:
-                tmp = pow(2, 3)   # ID: cfficall
+                tmp = ldexp(1, 3)   # ID: cfficall
                 res += tmp
                 i += 1
             BLong = _cffi_backend.new_primitive_type("long")
-            pow_addr = int(_cffi_backend.cast(BLong, pow))
-            return pow_addr, res
+            ldexp_addr = int(_cffi_backend.cast(BLong, ldexp))
+            return ldexp_addr, res
         #
         libm_name = get_libm_name(sys.platform)
         log = self.run(main, [libm_name])
-        pow_addr, res = log.result
+        ldexp_addr, res = log.result
         assert res == 8.0 * 300
         loop, = log.loops_by_filename(self.filepath)
-        if 'ConstClass(pow)' in repr(loop):   # e.g. OS/X
-            pow_addr = 'ConstClass(pow)'
+        if 'ConstClass(ldexp)' in repr(loop):   # e.g. OS/X
+            ldexp_addr = 'ConstClass(ldexp)'
         assert loop.match_by_id('cfficall', """
             ...
-            f1 = call_release_gil(..., descr=<Callf 8 ff EF=6 OS=62>)
+            f1 = call_release_gil(..., descr=<Callf 8 fi EF=6 OS=62>)
             ...
         """)
+        ops = loop.ops_by_id('cfficall')
+        assert 'raw_malloc' not in str(ops)
+        assert 'raw_free' not in str(ops)
+        assert 'getarrayitem_raw' not in log.opnames(ops)
+        assert 'setarrayitem_raw' not in log.opnames(ops)
         # so far just check that call_release_gil() is produced.
         # later, also check that the arguments to call_release_gil()
+        # are constants
         # are constants, and that the numerous raw_mallocs are removed
 
     def test_cffi_call_guard_not_forced_fails(self):
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
@@ -484,6 +484,15 @@
         else:
             return self.bh_raw_load_i(struct, offset, descr)
 
+    def unpack_arraydescr_size(self, arraydescr):
+        from rpython.jit.backend.llsupport.symbolic import get_array_token
+        from rpython.jit.backend.llsupport.descr import get_type_flag, FLAG_SIGNED
+        assert isinstance(arraydescr, ArrayDescr)
+        basesize, itemsize, _ = get_array_token(arraydescr.A, False)
+        flag = get_type_flag(arraydescr.A.OF)
+        is_signed = (flag == FLAG_SIGNED)
+        return basesize, itemsize, is_signed
+
     def bh_raw_store_i(self, struct, offset, newvalue, descr):
         ll_p = rffi.cast(rffi.CCHARP, struct)
         ll_p = rffi.cast(lltype.Ptr(descr.A), rffi.ptradd(ll_p, offset))
@@ -566,10 +575,14 @@
     def bh_read_timestamp(self):
         return read_timestamp()
 
+    def bh_new_raw_buffer(self, size):
+        return lltype.malloc(rffi.CCHARP.TO, size, flavor='raw')
+
     def store_fail_descr(self, deadframe, descr):
         pass # I *think*
 
 
+
 class LLDeadFrame(object):
     _TYPE = llmemory.GCREF
 
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
@@ -742,6 +742,9 @@
             as_array[self.vtable_offset/WORD] = vtable
         return res
 
+    def bh_new_raw_buffer(self, size):
+        return lltype.malloc(rffi.CCHARP.TO, size, flavor='raw')
+
     def bh_classof(self, struct):
         struct = lltype.cast_opaque_ptr(rclass.OBJECTPTR, struct)
         result_adr = llmemory.cast_ptr_to_adr(struct.typeptr)
diff --git a/rpython/jit/backend/model.py b/rpython/jit/backend/model.py
--- a/rpython/jit/backend/model.py
+++ b/rpython/jit/backend/model.py
@@ -194,11 +194,18 @@
     def typedescrof(self, TYPE):
         raise NotImplementedError
 
+    def unpack_arraydescr_size(self, arraydescr):
+        """
+        Return basesize, itemsize, is_signed
+        """
+        raise NotImplementedError
+
     @staticmethod
     def cast_int_to_ptr(x, TYPE):
         x = llmemory.cast_int_to_adr(x)
         return llmemory.cast_adr_to_ptr(x, TYPE)
 
+
     # ---------- the backend-dependent operations ----------
 
     # lltype specific operations
@@ -235,6 +242,8 @@
         raise NotImplementedError
     def bh_newunicode(self, length):
         raise NotImplementedError
+    def bh_new_raw_buffer(self, size):
+        raise NotImplementedError
 
     def bh_arraylen_gc(self, array, arraydescr):
         raise NotImplementedError
diff --git a/rpython/jit/backend/x86/test/test_zrpy_gc_boehm.py b/rpython/jit/backend/x86/test/test_zrpy_gc_boehm.py
--- a/rpython/jit/backend/x86/test/test_zrpy_gc_boehm.py
+++ b/rpython/jit/backend/x86/test/test_zrpy_gc_boehm.py
@@ -2,6 +2,7 @@
 import weakref
 from rpython.rlib.jit import JitDriver, dont_look_inside
 from rpython.jit.backend.x86.test.test_zrpy_gc import run, get_entry, compile
+from rpython.jit.backend.x86.test.test_ztranslation import fix_annotator_for_vrawbuffer
 
 class X(object):
     def __init__(self, x=0):
@@ -31,7 +32,8 @@
     g._dont_inline_ = True
     return g
 
-def test_compile_boehm():
+def test_compile_boehm(monkeypatch):
+    fix_annotator_for_vrawbuffer(monkeypatch)
     myjitdriver = JitDriver(greens = [], reds = ['n', 'x'])
     @dont_look_inside
     def see(lst, n):
diff --git a/rpython/jit/backend/x86/test/test_ztranslation.py b/rpython/jit/backend/x86/test/test_ztranslation.py
--- a/rpython/jit/backend/x86/test/test_ztranslation.py
+++ b/rpython/jit/backend/x86/test/test_ztranslation.py
@@ -13,6 +13,18 @@
 from rpython.config.translationoption import DEFL_GC
 from rpython.rlib import rgc
 
+def fix_annotator_for_vrawbuffer(monkeypatch):
+    from rpython.rlib.nonconst import NonConstant
+    from rpython.jit.metainterp.optimizeopt.virtualize import VRawBufferValue
+    from rpython.jit.metainterp import warmspot
+
+    def my_hook_for_tests(cpu):
+        # this is needed so that the annotator can see it
+        if NonConstant(False):
+            v = VRawBufferValue(cpu, None, -1, None, None)
+    monkeypatch.setattr(warmspot, 'hook_for_tests', my_hook_for_tests)
+
+
 class TestTranslationX86(CCompiledMixin):
     CPUClass = getcpuclass()
 
@@ -22,7 +34,159 @@
         assert '-msse2' in cbuilder.eci.compile_extra
         assert '-mfpmath=sse' in cbuilder.eci.compile_extra
 
-    def test_jit_get_stats(self):
+    def test_stuff_translates(self, monkeypatch):
+        fix_annotator_for_vrawbuffer(monkeypatch)
+        # this is a basic test that tries to hit a number of features and their
+        # translation:
+        # - jitting of loops and bridges
+        # - virtualizables
+        # - set_param interface
+        # - profiler
+        # - full optimizer
+        # - floats neg and abs
+
+        class Frame(object):
+            _virtualizable2_ = ['i']
+
+            def __init__(self, i):
+                self.i = i
+
+        @dont_look_inside
+        def myabs(x):
+            return abs(x)
+
+        jitdriver = JitDriver(greens = [],
+                              reds = ['total', 'frame', 'j'],
+                              virtualizables = ['frame'])
+        def f(i, j):
+            for param, _ in unroll_parameters:
+                defl = PARAMETERS[param]
+                set_param(jitdriver, param, defl)
+            set_param(jitdriver, "threshold", 3)
+            set_param(jitdriver, "trace_eagerness", 2)
+            total = 0
+            frame = Frame(i)
+            j = float(j)
+            while frame.i > 3:
+                jitdriver.can_enter_jit(frame=frame, total=total, j=j)
+                jitdriver.jit_merge_point(frame=frame, total=total, j=j)
+                total += frame.i
+                if frame.i >= 20:
+                    frame.i -= 2
+                frame.i -= 1
+                j *= -0.712
+                if j + (-j):    raise ValueError
+                k = myabs(j)
+                if k - abs(j):  raise ValueError
+                if k - abs(-j): raise ValueError
+            return chr(total % 253)
+        #
+        from rpython.rtyper.lltypesystem import lltype, rffi
+        from rpython.rlib.libffi import types, CDLL, ArgChain
+        from rpython.rlib.test.test_clibffi import get_libm_name
+        libm_name = get_libm_name(sys.platform)
+        jitdriver2 = JitDriver(greens=[], reds = ['i', 'func', 'res', 'x'])
+        def libffi_stuff(i, j):
+            lib = CDLL(libm_name)
+            func = lib.getpointer('fabs', [types.double], types.double)
+            res = 0.0
+            x = float(j)
+            while i > 0:
+                jitdriver2.jit_merge_point(i=i, res=res, func=func, x=x)
+                promote(func)
+                argchain = ArgChain()
+                argchain.arg(x)
+                res = func.call(argchain, rffi.DOUBLE)
+                i -= 1
+            return res
+        #
+        def main(i, j):
+            a_char = f(i, j)
+            a_float = libffi_stuff(i, j)
+            return ord(a_char) * 10 + int(a_float)
+        expected = main(40, -49)
+        res = self.meta_interp(main, [40, -49])
+        assert res == expected
+
+    def test_direct_assembler_call_translates(self, monkeypatch):
+        """Test CALL_ASSEMBLER and the recursion limit"""
+        from rpython.rlib.rstackovf import StackOverflow
+        fix_annotator_for_vrawbuffer(monkeypatch)
+
+        class Thing(object):
+            def __init__(self, val):
+                self.val = val
+
+        class Frame(object):
+            _virtualizable2_ = ['thing']
+
+        driver = JitDriver(greens = ['codeno'], reds = ['i', 'frame'],
+                           virtualizables = ['frame'],
+                           get_printable_location = lambda codeno: str(codeno))
+        class SomewhereElse(object):
+            pass
+
+        somewhere_else = SomewhereElse()
+
+        def change(newthing):
+            somewhere_else.frame.thing = newthing
+
+        def main(codeno):
+            frame = Frame()
+            somewhere_else.frame = frame
+            frame.thing = Thing(0)
+            portal(codeno, frame)
+            return frame.thing.val
+
+        def portal(codeno, frame):
+            i = 0
+            while i < 10:
+                driver.can_enter_jit(frame=frame, codeno=codeno, i=i)
+                driver.jit_merge_point(frame=frame, codeno=codeno, i=i)
+                nextval = frame.thing.val
+                if codeno == 0:
+                    subframe = Frame()
+                    subframe.thing = Thing(nextval)
+                    nextval = portal(1, subframe)
+                elif frame.thing.val > 40:
+                    change(Thing(13))
+                    nextval = 13
+                frame.thing = Thing(nextval + 1)
+                i += 1
+            return frame.thing.val
+
+        driver2 = JitDriver(greens = [], reds = ['n'])
+
+        def main2(bound):
+            try:
+                while portal2(bound) == -bound+1:
+                    bound *= 2
+            except StackOverflow:
+                pass
+            return bound
+
+        def portal2(n):
+            while True:
+                driver2.jit_merge_point(n=n)
+                n -= 1
+                if n <= 0:
+                    return n
+                n = portal2(n)
+        assert portal2(10) == -9
+
+        def mainall(codeno, bound):
+            return main(codeno) + main2(bound)
+
+        res = self.meta_interp(mainall, [0, 1], inline=True,
+                               policy=StopAtXPolicy(change))
+        print hex(res)
+        assert res & 255 == main(0)
+        bound = res & ~255
+        assert 1024 <= bound <= 131072
+        assert bound & (bound-1) == 0       # a power of two
+
+    def test_jit_get_stats(self, monkeypatch):
+        fix_annotator_for_vrawbuffer(monkeypatch)
         driver = JitDriver(greens = [], reds = ['i'])
         
         def f():
@@ -40,3 +204,88 @@
         res = self.meta_interp(main, [])
         assert res == 3
         # one for loop, one for entry point and one for the prologue
+
+class TestTranslationRemoveTypePtrX86(CCompiledMixin):
+    CPUClass = getcpuclass()
+
+    def _get_TranslationContext(self):
+        t = TranslationContext()
+        t.config.translation.gc = DEFL_GC   # 'hybrid' or 'minimark'
+        t.config.translation.gcrootfinder = 'asmgcc'
+        t.config.translation.list_comprehension_operations = True
+        t.config.translation.gcremovetypeptr = True
+        return t
+
+    def test_external_exception_handling_translates(self, monkeypatch):
+        fix_annotator_for_vrawbuffer(monkeypatch)
+        jitdriver = JitDriver(greens = [], reds = ['n', 'total'])
+
+        class ImDone(Exception):
+            def __init__(self, resvalue):
+                self.resvalue = resvalue
+
+        @dont_look_inside
+        def f(x, total):
+            if x <= 30:
+                raise ImDone(total * 10)
+            if x > 200:
+                return 2
+            raise ValueError
+        @dont_look_inside
+        def g(x):
+            if x > 150:
+                raise ValueError
+            return 2
+        class Base:
+            def meth(self):
+                return 2
+        class Sub(Base):
+            def meth(self):
+                return 1
+        @dont_look_inside
+        def h(x):
+            if x < 20000:
+                return Sub()
+            else:
+                return Base()
+        def myportal(i):
+            set_param(jitdriver, "threshold", 3)
+            set_param(jitdriver, "trace_eagerness", 2)
+            total = 0
+            n = i
+            while True:
+                jitdriver.can_enter_jit(n=n, total=total)
+                jitdriver.jit_merge_point(n=n, total=total)
+                try:
+                    total += f(n, total)
+                except ValueError:
+                    total += 1
+                try:
+                    total += g(n)
+                except ValueError:
+                    total -= 1
+                n -= h(n).meth()   # this is to force a GUARD_CLASS
+        def main(i):
+            try:
+                myportal(i)
+            except ImDone, e:
+                return e.resvalue
+
+        # XXX custom fishing, depends on the exact env var and format
+        logfile = udir.join('test_ztranslation.log')
+        os.environ['PYPYLOG'] = 'jit-log-opt:%s' % (logfile,)
+        try:
+            res = self.meta_interp(main, [400])
+            assert res == main(400)
+        finally:
+            del os.environ['PYPYLOG']
+
+        guard_class = 0
+        for line in open(str(logfile)):
+            if 'guard_class' in line:
+                guard_class += 1
+        # if we get many more guard_classes, it means that we generate
+        # guards that always fail (the following assert's original purpose
+        # is to catch the following case: each GUARD_CLASS is misgenerated
+        # and always fails with "gcremovetypeptr")
+        assert 0 < guard_class < 10
diff --git a/rpython/jit/backend/x86/test/test_ztranslation_basic.py b/rpython/jit/backend/x86/test/test_ztranslation_basic.py
--- a/rpython/jit/backend/x86/test/test_ztranslation_basic.py
+++ b/rpython/jit/backend/x86/test/test_ztranslation_basic.py
@@ -12,6 +12,7 @@
 from rpython.jit.backend.x86.arch import IS_X86_32, IS_X86_64
 from rpython.config.translationoption import DEFL_GC
 from rpython.rlib import rgc
+from rpython.jit.backend.x86.test.test_ztranslation import fix_annotator_for_vrawbuffer
 
 class TestTranslationX86(CCompiledMixin):
     CPUClass = getcpuclass()
@@ -22,7 +23,7 @@
         assert '-msse2' in cbuilder.eci.compile_extra
         assert '-mfpmath=sse' in cbuilder.eci.compile_extra
 
-    def test_stuff_translates(self):
+    def test_stuff_translates(self, monkeypatch):
         # this is a basic test that tries to hit a number of features and their
         # translation:
         # - jitting of loops and bridges
@@ -31,6 +32,7 @@
         # - profiler
         # - full optimizer
         # - floats neg and abs
+        fix_annotator_for_vrawbuffer(monkeypatch)
 
         class Frame(object):
             _virtualizable2_ = ['i']
diff --git a/rpython/jit/backend/x86/test/test_ztranslation_call_assembler.py b/rpython/jit/backend/x86/test/test_ztranslation_call_assembler.py
--- a/rpython/jit/backend/x86/test/test_ztranslation_call_assembler.py
+++ b/rpython/jit/backend/x86/test/test_ztranslation_call_assembler.py
@@ -12,6 +12,7 @@
 from rpython.jit.backend.x86.arch import IS_X86_32, IS_X86_64
 from rpython.config.translationoption import DEFL_GC
 from rpython.rlib import rgc
+from rpython.jit.backend.x86.test.test_ztranslation import fix_annotator_for_vrawbuffer
 
 class TestTranslationX86(CCompiledMixin):
     CPUClass = getcpuclass()
@@ -22,9 +23,10 @@
         assert '-msse2' in cbuilder.eci.compile_extra
         assert '-mfpmath=sse' in cbuilder.eci.compile_extra
 
-    def test_direct_assembler_call_translates(self):
+    def test_direct_assembler_call_translates(self, monkeypatch):
         """Test CALL_ASSEMBLER and the recursion limit"""
         from rpython.rlib.rstackovf import StackOverflow
+        fix_annotator_for_vrawbuffer(monkeypatch)
 
         class Thing(object):
             def __init__(self, val):
diff --git a/rpython/jit/backend/x86/test/test_ztranslation_external_exception.py b/rpython/jit/backend/x86/test/test_ztranslation_external_exception.py
--- a/rpython/jit/backend/x86/test/test_ztranslation_external_exception.py
+++ b/rpython/jit/backend/x86/test/test_ztranslation_external_exception.py
@@ -12,7 +12,7 @@
 from rpython.jit.backend.x86.arch import IS_X86_32, IS_X86_64
 from rpython.config.translationoption import DEFL_GC
 from rpython.rlib import rgc
-
+from rpython.jit.backend.x86.test.test_ztranslation import fix_annotator_for_vrawbuffer
 
 class TestTranslationRemoveTypePtrX86(CCompiledMixin):
     CPUClass = getcpuclass()
@@ -25,7 +25,9 @@
         t.config.translation.gcremovetypeptr = True
         return t
 
-    def test_external_exception_handling_translates(self):
+    def test_external_exception_handling_translates(self, monkeypatch):
+        fix_annotator_for_vrawbuffer(monkeypatch)
+        
         jitdriver = JitDriver(greens = [], reds = ['n', 'total'])
 
         class ImDone(Exception):
diff --git a/rpython/jit/codewriter/effectinfo.py b/rpython/jit/codewriter/effectinfo.py
--- a/rpython/jit/codewriter/effectinfo.py
+++ b/rpython/jit/codewriter/effectinfo.py
@@ -76,14 +76,14 @@
     #
     OS_MATH_SQRT                = 100
     #
-    OS_RAW_MALLOC_VARSIZE       = 110
+    OS_RAW_MALLOC_VARSIZE_CHAR  = 110
     OS_RAW_FREE                 = 111
 
     OS_JIT_FORCE_VIRTUAL        = 120
 
     # for debugging:
     _OS_CANRAISE = set([OS_NONE, OS_STR2UNICODE, OS_LIBFFI_CALL,
-                        OS_RAW_MALLOC_VARSIZE, OS_JIT_FORCE_VIRTUAL])
+                        OS_RAW_MALLOC_VARSIZE_CHAR, OS_JIT_FORCE_VIRTUAL])
 
     def __new__(cls, readonly_descrs_fields, readonly_descrs_arrays,
                 write_descrs_fields, write_descrs_arrays,
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
@@ -539,9 +539,11 @@
             name += '_no_track_allocation'
         op1 = self.prepare_builtin_call(op, name, args, (TYPE,), TYPE)
         if name == 'raw_malloc_varsize':
-            return self._handle_oopspec_call(op1, args,
-                                             EffectInfo.OS_RAW_MALLOC_VARSIZE,
-                                             EffectInfo.EF_CAN_RAISE)
+            ITEMTYPE = op.args[0].value.OF
+            if ITEMTYPE == lltype.Char:
+                return self._handle_oopspec_call(op1, args,
+                                                 EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR,
+                                                 EffectInfo.EF_CAN_RAISE)
         return self.rewrite_op_direct_call(op1)
 
     def rewrite_op_malloc_varsize(self, op):
diff --git a/rpython/jit/codewriter/test/test_jtransform.py b/rpython/jit/codewriter/test/test_jtransform.py
--- a/rpython/jit/codewriter/test/test_jtransform.py
+++ b/rpython/jit/codewriter/test/test_jtransform.py
@@ -126,7 +126,7 @@
             INT = lltype.Signed
             UNICHAR = lltype.UniChar
             FLOAT = lltype.Float
-            ARRAYPTR = rffi.CArrayPtr(lltype.Signed)
+            ARRAYPTR = rffi.CArrayPtr(lltype.Char)
             argtypes = {
              EI.OS_MATH_SQRT:  ([FLOAT], FLOAT),
              EI.OS_STR2UNICODE:([PSTR], PUNICODE),
@@ -143,7 +143,7 @@
              EI.OS_UNIEQ_NONNULL_CHAR:   ([PUNICODE, UNICHAR], INT),
              EI.OS_UNIEQ_CHECKNULL_CHAR: ([PUNICODE, UNICHAR], INT),
              EI.OS_UNIEQ_LENGTHOK:       ([PUNICODE, PUNICODE], INT),
-             EI.OS_RAW_MALLOC_VARSIZE:   ([INT], ARRAYPTR),
+             EI.OS_RAW_MALLOC_VARSIZE_CHAR: ([INT], ARRAYPTR),
              EI.OS_RAW_FREE:             ([ARRAYPTR], lltype.Void),
             }
             argtypes = argtypes[oopspecindex]
@@ -151,7 +151,7 @@
             assert argtypes[1] == op.result.concretetype
             if oopspecindex == EI.OS_STR2UNICODE:
                 assert extraeffect == EI.EF_ELIDABLE_CAN_RAISE
-            elif oopspecindex == EI.OS_RAW_MALLOC_VARSIZE:
+            elif oopspecindex == EI.OS_RAW_MALLOC_VARSIZE_CHAR:
                 assert extraeffect == EI.EF_CAN_RAISE
             elif oopspecindex == EI.OS_RAW_FREE:
                 assert extraeffect == EI.EF_CANNOT_RAISE
@@ -161,7 +161,7 @@
 
     def calldescr_canraise(self, calldescr):
         EI = effectinfo.EffectInfo
-        if calldescr == 'calldescr-%d' % EI.OS_RAW_MALLOC_VARSIZE:
+        if calldescr == 'calldescr-%d' % EI.OS_RAW_MALLOC_VARSIZE_CHAR:
             return True
         return False
 
@@ -555,7 +555,7 @@
     assert op1.args == []
 
 def test_raw_malloc():
-    S = rffi.CArray(lltype.Signed)
+    S = rffi.CArray(lltype.Char)
     v1 = varoftype(lltype.Signed)
     v = varoftype(lltype.Ptr(S))
     flags = Constant({'flavor': 'raw'}, lltype.Void)
@@ -566,7 +566,7 @@
     assert op0.opname == 'residual_call_ir_i'
     assert op0.args[0].value == 'raw_malloc_varsize' # pseudo-function as a str
     assert (op0.args[-1] == 'calldescr-%d' %
-            effectinfo.EffectInfo.OS_RAW_MALLOC_VARSIZE)
+            effectinfo.EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR)
 
     assert op1.opname == '-live-'
     assert op1.args == []
@@ -608,7 +608,7 @@
     assert op1.args == []
 
 def test_raw_free():
-    S = rffi.CArray(lltype.Signed)
+    S = rffi.CArray(lltype.Char)
     flags = Constant({'flavor': 'raw', 'track_allocation': True},
                      lltype.Void)
     op = SpaceOperation('free', [varoftype(lltype.Ptr(S)), flags],
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
@@ -15,7 +15,7 @@
 from rpython.jit.metainterp import history, resume
 from rpython.jit.metainterp.optimize import InvalidLoop
 from rpython.jit.metainterp.inliner import Inliner
-from rpython.jit.metainterp.resume import NUMBERING, PENDINGFIELDSP
+from rpython.jit.metainterp.resume import NUMBERING, PENDINGFIELDSP, ResumeDataDirectReader
 from rpython.jit.codewriter import heaptracker, longlong
 
 def giveup():
@@ -656,9 +656,9 @@
 
 class AllVirtuals:
     llopaque = True
-    list = [resume.ResumeDataDirectReader.virtual_default]   # annotation hack
-    def __init__(self, list):
-        self.list = list
+    cache = None
+    def __init__(self, cache):
+        self.cache = cache
     def hide(self, cpu):
         ptr = cpu.ts.cast_instance_to_base_ref(self)
         return cpu.ts.cast_to_ref(ptr)
@@ -682,9 +682,9 @@
         from rpython.jit.metainterp.blackhole import resume_in_blackhole
         hidden_all_virtuals = metainterp_sd.cpu.get_savedata_ref(deadframe)
         obj = AllVirtuals.show(metainterp_sd.cpu, hidden_all_virtuals)
-        all_virtuals = obj.list
+        all_virtuals = obj.cache
         if all_virtuals is None:
-            all_virtuals = []
+            all_virtuals = ResumeDataDirectReader.VirtualCache([], [])
         assert jitdriver_sd is self.jitdriver_sd
         resume_in_blackhole(metainterp_sd, jitdriver_sd, self, deadframe,
                             all_virtuals)
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
@@ -191,7 +191,6 @@
     def get_jitcode_for_class(self, oocls):
         return self.jitcodes[oocls]
 
-
 class Const(AbstractValue):
     __slots__ = ()
 
diff --git a/rpython/jit/metainterp/optimizeopt/earlyforce.py b/rpython/jit/metainterp/optimizeopt/earlyforce.py
--- a/rpython/jit/metainterp/optimizeopt/earlyforce.py
+++ b/rpython/jit/metainterp/optimizeopt/earlyforce.py
@@ -1,15 +1,26 @@
+from rpython.jit.codewriter.effectinfo import EffectInfo
 from rpython.jit.metainterp.optimizeopt.optimizer import Optimization
 from rpython.jit.metainterp.optimizeopt.vstring import VAbstractStringValue
 from rpython.jit.metainterp.resoperation import rop, ResOperation
 
+def is_raw_free(op, opnum):
+    if opnum != rop.CALL:
+        return False
+    einfo = op.getdescr().get_extra_info()
+    return einfo.oopspecindex == EffectInfo.OS_RAW_FREE
+
+
 class OptEarlyForce(Optimization):
     def propagate_forward(self, op):
         opnum = op.getopnum()
+
         if (opnum != rop.SETFIELD_GC and 
             opnum != rop.SETARRAYITEM_GC and
+            opnum != rop.SETARRAYITEM_RAW and
             opnum != rop.QUASIIMMUT_FIELD and
             opnum != rop.SAME_AS and
-            opnum != rop.MARK_OPAQUE_PTR):
+            opnum != rop.MARK_OPAQUE_PTR and
+            not is_raw_free(op, opnum)):
                
             for arg in op.getarglist():
                 if arg in self.optimizer.values:
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
@@ -231,6 +231,12 @@
     def setitem(self, index, value):
         raise NotImplementedError
 
+    def getitem_raw(self, offset, length, descr):
+        raise NotImplementedError
+
+    def setitem_raw(self, offset, length, descr, value):
+        raise NotImplementedError
+
     def getinteriorfield(self, index, ofs, default):
         raise NotImplementedError
 
diff --git a/rpython/jit/metainterp/optimizeopt/rawbuffer.py b/rpython/jit/metainterp/optimizeopt/rawbuffer.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/metainterp/optimizeopt/rawbuffer.py
@@ -0,0 +1,134 @@
+from rpython.rlib.debug import debug_start, debug_stop, debug_print
+from rpython.rlib.objectmodel import compute_unique_id, we_are_translated
+
+class InvalidRawOperation(Exception):
+    pass
+
+class InvalidRawWrite(InvalidRawOperation):
+    pass
+
+class InvalidRawRead(InvalidRawOperation):
+    pass
+
+class RawBuffer(object):
+    def __init__(self, cpu, logops=None):
+        # the following lists represents the writes in the buffer: values[i]
+        # is the value of length lengths[i] stored at offset[i].
+        #
+        # the invariant is that they are ordered by offset, and that
+        # offset[i]+length[i] <= offset[i+1], i.e. that the writes never
+        # overlaps
+        self.cpu = cpu
+        self.logops = logops
+        self.offsets = []
+        self.lengths = []
+        self.descrs = []
+        self.values = []
+
+    def _get_memory(self):
+        """
+        NOT_RPYTHON
+        for testing only
+        """
+        return zip(self.offsets, self.lengths, self.descrs, self.values)
+
+    def _repr_of_descr(self, descr):
+        if self.logops:
+            s = self.logops.repr_of_descr(descr)
+        else:
+            s = str(descr)
+        s += " at %d" % compute_unique_id(descr)
+        return s
+
+    def _repr_of_value(self, value):
+        if not we_are_translated() and isinstance(value, str):
+            return value # for tests
+        if self.logops:
+            s = self.logops.repr_of_arg(value.box)
+        else:
+            s = str(value.box)
+        s += " at %d" % compute_unique_id(value.box)
+        return s
+
+    def _dump_to_log(self):
+        debug_print("RawBuffer state")
+        debug_print("offset, length, descr, box")
+        debug_print("(box == None means that the value is still virtual)")
+        for i in range(len(self.offsets)):
+            descr = self._repr_of_descr(self.descrs[i])
+            box = self._repr_of_value(self.values[i])
+            debug_print("%d, %d, %s, %s" % (self.offsets[i], self.lengths[i], descr, box))
+
+    def _invalid_write(self, message, offset, length, descr, value):
+        debug_start('jit-log-rawbuffer')
+        debug_print('Invalid write: %s' % message)
+        debug_print("  offset: %d" % offset)
+        debug_print("  length: %d" % length)
+        debug_print("  descr:  %s" % self._repr_of_descr(descr))
+        debug_print("  value:  %s" % self._repr_of_value(value))
+        self._dump_to_log()
+        debug_stop('jit-log-rawbuffer')
+        raise InvalidRawWrite
+
+    def _invalid_read(self, message, offset, length, descr):
+        debug_start('jit-log-rawbuffer')
+        debug_print('Invalid read: %s' % message)
+        debug_print("  offset: %d" % offset)
+        debug_print("  length: %d" % length)
+        debug_print("  descr:  %s" % self._repr_of_descr(descr))
+        self._dump_to_log()
+        debug_stop('jit-log-rawbuffer')
+        raise InvalidRawRead
+
+    def _descrs_are_compatible(self, d1, d2):
+        # two arraydescrs are compatible if they have the same basesize,
+        # itemsize and sign, even if they are not identical
+        unpack = self.cpu.unpack_arraydescr_size
+        return unpack(d1) == unpack(d2)
+
+    def write_value(self, offset, length, descr, value):
+        i = 0
+        N = len(self.offsets)
+        while i < N:
+            if self.offsets[i] == offset:
+                if (length != self.lengths[i] or not
+                    self._descrs_are_compatible(descr, self.descrs[i])):
+                    # in theory we could add support for the cases in which
+                    # the length or descr is different, but I don't think we
+                    # need it in practice
+                    self._invalid_write('length or descr not compatible',
+                                        offset, length, descr, value)
+                # update the value at this offset
+                self.values[i] = value
+                return
+            elif self.offsets[i] > offset:
+                break
+            i += 1
+        #
+        if i < len(self.offsets) and offset+length > self.offsets[i]:
+            self._invalid_write("overlap with next bytes",
+                                offset, length, descr, value)
+        if i > 0 and self.offsets[i-1]+self.lengths[i-1] > offset:
+            self._invalid_write("overlap with previous bytes",
+                                offset, length, descr, value)
+        # insert a new value at offset
+        self.offsets.insert(i, offset)
+        self.lengths.insert(i, length)
+        self.descrs.insert(i, descr)
+        self.values.insert(i, value)
+
+    def read_value(self, offset, length, descr):
+        i = 0
+        N = len(self.offsets)
+        while i < N:
+            if self.offsets[i] == offset:
+                if (length != self.lengths[i] or
+                    not self._descrs_are_compatible(descr, self.descrs[i])):
+                    self._invalid_read('length or descr not compatible',
+                                       offset, length, descr)
+                return self.values[i]
+            i += 1
+        # memory location not found: this means we are reading from
+        # uninitialized memory, give up the optimization
+        self._invalid_read('uninitialized memory',
+                           offset, length, descr)
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -770,8 +770,6 @@
         """
         self.optimize_loop(ops, expected)
 
-
-
     def test_p123_simple(self):
         ops = """
         [i1, p2, p3]
@@ -1730,6 +1728,175 @@
         # We cannot track virtuals that survive for more than two iterations.
         self.optimize_loop(ops, expected, preamble)
 
+    def test_virtual_raw_malloc(self):
+        ops = """
+        [i1]
+        i2 = call('malloc', 10, descr=raw_malloc_descr)
+        setarrayitem_raw(i2, 0, i1, descr=rawarraydescr)
+        i3 = getarrayitem_raw(i2, 0, descr=rawarraydescr)
+        call('free', i2, descr=raw_free_descr)
+        jump(i3)
+        """
+        expected = """
+        [i1]
+        jump(i1)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_virtual_raw_malloc_force(self):
+        ops = """
+        [i1]
+        i2 = call('malloc', 10, descr=raw_malloc_descr)
+        setarrayitem_raw(i2, 0, i1, descr=rawarraydescr)
+        setarrayitem_raw(i2, 2, 456, descr=rawarraydescr)
+        setarrayitem_raw(i2, 1, 123, descr=rawarraydescr)
+        label('foo') # we expect the buffer to be forced *after* the label
+        escape(i2)
+        call('free', i2, descr=raw_free_descr)
+        jump(i1)
+        """
+        expected = """
+        [i1]
+        label('foo')
+        i2 = call('malloc', 10, descr=raw_malloc_descr)
+        setarrayitem_raw(i2, 0, i1, descr=rawarraydescr)
+        i3 = int_add(i2, 8)
+        setarrayitem_raw(i3, 0, 123, descr=rawarraydescr)
+        i4 = int_add(i2, 16)
+        setarrayitem_raw(i4, 0, 456, descr=rawarraydescr)
+        escape(i2)
+        call('free', i2, descr=raw_free_descr)
+        jump(i1)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_virtual_raw_malloc_invalid_write_force(self):
+        ops = """
+        [i1]
+        i2 = call('malloc', 10, descr=raw_malloc_descr)
+        setarrayitem_raw(i2, 0, i1, descr=rawarraydescr)
+        label('foo') # we expect the buffer to be forced *after* the label
+        setarrayitem_raw(i2, 2, 456, descr=rawarraydescr_char) # overlap!
+        call('free', i2, descr=raw_free_descr)
+        jump(i1)
+        """
+        expected = """
+        [i1]
+        label('foo')
+        i2 = call('malloc', 10, descr=raw_malloc_descr)
+        setarrayitem_raw(i2, 0, i1, descr=rawarraydescr)
+        setarrayitem_raw(i2, 2, 456, descr=rawarraydescr_char)
+        call('free', i2, descr=raw_free_descr)
+        jump(i1)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_virtual_raw_malloc_invalid_read_force(self):
+        ops = """
+        [i1]
+        i2 = call('malloc', 10, descr=raw_malloc_descr)
+        setarrayitem_raw(i2, 0, i1, descr=rawarraydescr)
+        label('foo') # we expect the buffer to be forced *after* the label
+        i3 = getarrayitem_raw(i2, 0, descr=rawarraydescr_char)
+        call('free', i2, descr=raw_free_descr)
+        jump(i1)
+        """
+        expected = """
+        [i1]
+        label('foo')
+        i2 = call('malloc', 10, descr=raw_malloc_descr)
+        setarrayitem_raw(i2, 0, i1, descr=rawarraydescr)
+        i3 = getarrayitem_raw(i2, 0, descr=rawarraydescr_char)
+        call('free', i2, descr=raw_free_descr)
+        jump(i1)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_virtual_raw_slice(self):
+        ops = """
+        [i0, i1]
+        i2 = call('malloc', 10, descr=raw_malloc_descr)
+        setarrayitem_raw(i2, 0, 42, descr=rawarraydescr_char)
+        i3 = int_add(i2, 1) # get a slice of the original buffer
+        setarrayitem_raw(i3, 0, 4242, descr=rawarraydescr) # write to the slice
+        i4 = getarrayitem_raw(i2, 0, descr=rawarraydescr_char)
+        i5 = int_add(i2, 1)
+        i6 = getarrayitem_raw(i5, 0, descr=rawarraydescr)
+        call('free', i2, descr=raw_free_descr)
+        jump(i0, i1)
+        """
+        expected = """
+        [i0, i1]
+        jump(i0, i1)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_virtual_raw_slice_of_a_raw_slice(self):
+        ops = """
+        [i0, i1]
+        i2 = call('malloc', 10, descr=raw_malloc_descr)
+        i3 = int_add(i2, 1) # get a slice of the original buffer
+        i4 = int_add(i3, 1) # get a slice of a slice
+        setarrayitem_raw(i4, 0, i1, descr=rawarraydescr_char) # write to the slice
+        i5 = getarrayitem_raw(i2, 2, descr=rawarraydescr_char)
+        call('free', i2, descr=raw_free_descr)
+        jump(i0, i5)
+        """
+        expected = """
+        [i0, i1]
+        jump(i0, i1)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_virtual_raw_slice_force(self):
+        ops = """
+        [i0, i1]
+        i2 = call('malloc', 10, descr=raw_malloc_descr)
+        setarrayitem_raw(i2, 0, 42, descr=rawarraydescr_char)
+        i3 = int_add(i2, 1) # get a slice of the original buffer
+        setarrayitem_raw(i3, 4, 4242, descr=rawarraydescr_char) # write to the slice
+        label('foo')
+        escape(i3)
+        jump(i0, i1)
+        """
+        expected = """
+        [i0, i1]
+        label('foo')
+        # these ops are generated by VirtualRawBufferValue._really_force
+        i2 = call('malloc', 10, descr=raw_malloc_descr)
+        setarrayitem_raw(i2, 0, 42, descr=rawarraydescr_char)
+        i3 = int_add(i2, 5) # 1+4*sizeof(char)
+        setarrayitem_raw(i3, 0, 4242, descr=rawarraydescr_char)
+        # this is generated by VirtualRawSliceValue._really_force
+        i4 = int_add(i2, 1)
+        escape(i4)
+        jump(i0, i1)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_virtual_raw_malloc_virtualstate(self):
+        ops = """
+        [i0]
+        i1 = getarrayitem_raw(i0, 0, descr=rawarraydescr)
+        i2 = int_add(i1, 1)
+        call('free', i0, descr=raw_free_descr)
+        i3 = call('malloc', 10, descr=raw_malloc_descr)
+        setarrayitem_raw(i3, 0, i2, descr=rawarraydescr)
+        label('foo')
+        jump(i3)
+        """
+        expected = """
+        [i0]
+        i1 = getarrayitem_raw(i0, 0, descr=rawarraydescr)
+        i2 = int_add(i1, 1)
+        call('free', i0, descr=raw_free_descr)
+        label('foo')
+        i3 = call('malloc', 10, descr=raw_malloc_descr)
+        setarrayitem_raw(i3, 0, i2, descr=rawarraydescr)
+        jump(i3)
+        """
+        self.optimize_loop(ops, expected)
+
     def test_duplicate_getfield_1(self):
         ops = """
         [p1, p2]
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_rawbuffer.py b/rpython/jit/metainterp/optimizeopt/test/test_rawbuffer.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/metainterp/optimizeopt/test/test_rawbuffer.py
@@ -0,0 +1,90 @@
+import py
+from rpython.jit.metainterp.optimizeopt.rawbuffer import (InvalidRawWrite,
+                                                          InvalidRawRead, RawBuffer)
+
+class FakeCPU(object):
+    def unpack_arraydescr_size(self, descr):
+        return descr, 'foo', 'bar'
+
+def test_write_value():
+    buf = RawBuffer(FakeCPU())
+    buf.write_value(8, 4, 'descr3', 'three')
+    buf.write_value(0, 4, 'descr1', 'one')
+    buf.write_value(4, 2, 'descr2', 'two')
+    buf.write_value(12, 2, 'descr4', 'four')
+    assert buf._get_memory() == [
+        ( 0, 4, 'descr1', 'one'),
+        ( 4, 2, 'descr2', 'two'),
+        ( 8, 4, 'descr3', 'three'),
+        (12, 2, 'descr4', 'four'),
+        ]
+    #
+
+def test_write_value_update():
+    buf = RawBuffer(FakeCPU())
+    buf.write_value(0, 4, 'descr', 'one')
+    buf.write_value(4, 2, 'descr', 'two')
+    buf.write_value(0, 4, 'descr', 'ONE')
+    assert buf._get_memory() == [
+        ( 0, 4, 'descr', 'ONE'),
+        ( 4, 2, 'descr', 'two'),
+        ]
+
+def test_write_value_invalid_length():
+    buf = RawBuffer(FakeCPU())
+    buf.write_value(0, 4, 'descr1', 'one')
+    with py.test.raises(InvalidRawWrite):
+        buf.write_value(0, 5, 'descr1', 'two')
+    with py.test.raises(InvalidRawWrite):
+        buf.write_value(0, 4, 'descr2', 'two')
+
+    
+def test_write_value_overlapping_next():
+    buf = RawBuffer(FakeCPU())
+    buf.write_value(0, 4, 'descr', 'one')
+    buf.write_value(6, 4, 'descr', 'two')
+    with py.test.raises(InvalidRawWrite):
+        buf.write_value(4, 4, 'descr', 'three')
+
+def test_write_value_overlapping_prev():
+    buf = RawBuffer(FakeCPU())
+    buf.write_value(0, 4, 'descr', 'one')
+    with py.test.raises(InvalidRawWrite):
+        buf.write_value(2, 1, 'descr', 'two')
+
+def test_read_value():
+    buf = RawBuffer(FakeCPU())
+    buf.write_value(0, 4, 'descr', 'one')
+    buf.write_value(4, 4, 'descr', 'two')
+    assert buf.read_value(0, 4, 'descr') == 'one'
+    assert buf.read_value(4, 4, 'descr') == 'two'
+    with py.test.raises(InvalidRawRead):
+        buf.read_value(0, 2, 'descr')
+    with py.test.raises(InvalidRawRead):
+        buf.read_value(8, 2, 'descr')
+    with py.test.raises(InvalidRawRead):
+        buf.read_value(0, 4, 'another descr')
+
+def test_unpack_descrs():
+    ArrayS_8_1 = object()
+    ArrayS_8_2 = object()
+    ArrayU_8 = object()
+
+    class FakeCPU(object):
+        def unpack_arraydescr_size(self, descr):
+            if descr in (ArrayS_8_1, ArrayS_8_2):
+                return 0, 8, True
+            return 0, 8, False
+
+    buf = RawBuffer(FakeCPU())
+    buf.write_value(0, 4, ArrayS_8_1, 'one')
+    assert buf.read_value(0, 4, ArrayS_8_1) == 'one'
+    assert buf.read_value(0, 4, ArrayS_8_2) == 'one' # with a non-identical descr
+    #
+    buf.write_value(0, 4, ArrayS_8_2, 'two') # with a non-identical descr
+    assert buf.read_value(0, 4, ArrayS_8_1) == 'two'
+    #
+    with py.test.raises(InvalidRawRead):
+        buf.read_value(0, 4, ArrayU_8)
+    with py.test.raises(InvalidRawWrite):
+        buf.write_value(0, 4, ArrayU_8, 'three')
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
@@ -196,6 +196,15 @@
                         EffectInfo.EF_CANNOT_RAISE,
                         oopspecindex=EffectInfo.OS_ARRAYCOPY))
 
+    raw_malloc_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+             EffectInfo([], [], [], [],
+                        EffectInfo.EF_CAN_RAISE,
+                        oopspecindex=EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR))
+    raw_free_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+             EffectInfo([], [], [], [],
+                        EffectInfo.EF_CANNOT_RAISE,
+                        oopspecindex=EffectInfo.OS_RAW_FREE))
+
 
     # array of structs (complex data)
     complexarray = lltype.GcArray(
@@ -208,6 +217,12 @@
     complexrealdescr = cpu.interiorfielddescrof(complexarray, "real")
     compleximagdescr = cpu.interiorfielddescrof(complexarray, "imag")
 
+    rawarraydescr = cpu.arraydescrof(lltype.Array(lltype.Signed,
+                                                  hints={'nolength': True}))
+    rawarraydescr_char = cpu.arraydescrof(lltype.Array(lltype.Char,
+                                                       hints={'nolength': True}))
+
+
     for _name, _os in [
         ('strconcatdescr',               'OS_STR_CONCAT'),
         ('strslicedescr',                'OS_STR_SLICE'),
diff --git a/rpython/jit/metainterp/optimizeopt/virtualize.py b/rpython/jit/metainterp/optimizeopt/virtualize.py
--- a/rpython/jit/metainterp/optimizeopt/virtualize.py
+++ b/rpython/jit/metainterp/optimizeopt/virtualize.py
@@ -6,7 +6,9 @@
 from rpython.jit.metainterp.optimizeopt import optimizer
 from rpython.jit.metainterp.optimizeopt.optimizer import OptValue, REMOVED
 from rpython.jit.metainterp.optimizeopt.util import (make_dispatcher_method,
-    descrlist_dict, sort_descrs)
+                                                     descrlist_dict, sort_descrs)
+
+from rpython.jit.metainterp.optimizeopt.rawbuffer import RawBuffer, InvalidRawOperation
 from rpython.jit.metainterp.resoperation import rop, ResOperation
 from rpython.rlib.objectmodel import we_are_translated
 
@@ -236,8 +238,36 @@
     def _get_descr(self):
         return self.structdescr
 
+class AbstractVArrayValue(AbstractVirtualValue):
+    """
+    Base class for VArrayValue (for normal GC arrays) and VRawBufferValue (for
+    malloc()ed memory)
+    """
 
-class VArrayValue(AbstractVirtualValue):
+    def getlength(self):
+        return len(self._items)
+
+    def get_item_value(self, i):
+        raise NotImplementedError
+
+    def set_item_value(self, i, newval):
+        raise NotImplementedError
+
+    def get_args_for_fail(self, modifier):
+        if self.box is None and not modifier.already_seen_virtual(self.keybox):
+            # checks for recursion: it is False unless
+            # we have already seen the very same keybox
+            itemboxes = []
+            for i in range(self.getlength()):
+                itemvalue = self.get_item_value(i)
+                itemboxes.append(itemvalue.get_key_box())
+            modifier.register_virtual_fields(self.keybox, itemboxes)
+            for i in range(self.getlength()):
+                itemvalue = self.get_item_value(i)
+                itemvalue.get_args_for_fail(modifier)
+
+
+class VArrayValue(AbstractVArrayValue):
 
     def __init__(self, arraydescr, constvalue, size, keybox, source_op=None):
         AbstractVirtualValue.__init__(self, keybox, source_op)
@@ -248,6 +278,12 @@
     def getlength(self):
         return len(self._items)
 
+    def get_item_value(self, i):
+        return self._items[i]
+
+    def set_item_value(self, i, newval):
+        self._items[i] = newval
+
     def getitem(self, index):
         res = self._items[index]
         return res
@@ -257,11 +293,16 @@
         self._items[index] = itemvalue
 
     def force_at_end_of_preamble(self, already_forced, optforce):
+        # note that this method is on VArrayValue instead of
+        # AbstractVArrayValue because we do not want to support virtualstate
+        # for rawbuffers for now
         if self in already_forced:
             return self
         already_forced[self] = self
-        for index in range(len(self._items)):
-            self._items[index] = self._items[index].force_at_end_of_preamble(already_forced, optforce)
+        for index in range(self.getlength()):
+            itemval = self.get_item_value(index)
+            itemval = itemval.force_at_end_of_preamble(already_forced, optforce)
+            self.set_item_value(index, itemval)
         return self
 
     def _really_force(self, optforce):
@@ -281,29 +322,16 @@
                                   descr=self.arraydescr)
                 optforce.emit_operation(op)
 
-    def get_args_for_fail(self, modifier):
-        if self.box is None and not modifier.already_seen_virtual(self.keybox):
-            # checks for recursion: it is False unless
-            # we have already seen the very same keybox
-            itemboxes = []
-            for itemvalue in self._items:
-                itemboxes.append(itemvalue.get_key_box())
-            modifier.register_virtual_fields(self.keybox, itemboxes)
-            for itemvalue in self._items:
-                itemvalue.get_args_for_fail(modifier)
-
     def _make_virtual(self, modifier):
         return modifier.make_varray(self.arraydescr)
 
+
 class VArrayStructValue(AbstractVirtualValue):
     def __init__(self, arraydescr, size, keybox, source_op=None):
         AbstractVirtualValue.__init__(self, keybox, source_op)
         self.arraydescr = arraydescr
         self._items = [{} for _ in xrange(size)]
 
-    def getlength(self):
-        return len(self._items)
-
     def getinteriorfield(self, index, ofs, default):
         return self._items[index].get(ofs, default)
 
@@ -363,6 +391,90 @@
         return modifier.make_varraystruct(self.arraydescr, self._get_list_of_descrs())
 
 
+class VRawBufferValue(AbstractVArrayValue):
+
+    def __init__(self, cpu, logops, size, keybox, source_op):
+        AbstractVirtualValue.__init__(self, keybox, source_op)
+        # note that size is unused, because we assume that the buffer is big
+        # enough to write/read everything we need. If it's not, it's undefined
+        # behavior anyway, although in theory we could probably detect such
+        # cases here
+        self.size = size
+        self.buffer = RawBuffer(cpu, logops)
+
+    def getlength(self):
+        return len(self.buffer.values)
+
+    def get_item_value(self, i):
+        return self.buffer.values[i]
+
+    def set_item_value(self, i, newval):
+        self.buffer.values[i] = newval
+
+    def getitem_raw(self, offset, length, descr):
+        return self.buffer.read_value(offset, length, descr)
+
+    def setitem_raw(self, offset, length, descr, value):
+        self.buffer.write_value(offset, length, descr, value)
+
+    def _really_force(self, optforce):
+        op = self.source_op
+        assert op is not None
+        if not we_are_translated():
+            op.name = 'FORCE ' + self.source_op.name
+        optforce.emit_operation(self.source_op)
+        self.box = box = self.source_op.result
+        for i in range(len(self.buffer.offsets)):
+            # get a pointer to self.box+offset
+            offset = self.buffer.offsets[i]
+            if offset == 0:
+                arraybox = self.box
+            else:
+                arraybox = BoxInt()
+                op = ResOperation(rop.INT_ADD,
+                                  [self.box, ConstInt(offset)], arraybox)
+                optforce.emit_operation(op)
+            #
+            # write the value
+            descr = self.buffer.descrs[i]
+            itemvalue = self.buffer.values[i]
+            itembox = itemvalue.force_box(optforce)
+            op = ResOperation(rop.SETARRAYITEM_RAW,
+                              [arraybox, ConstInt(0), itembox], None,
+                              descr=descr)
+            optforce.emit_operation(op)
+
+    def _make_virtual(self, modifier):
+        # I *think* we need to make a copy of offsets and descrs because we
+        # want a snapshot of the virtual state right now: if we grow more
+        # elements later, we don't want them to go in this virtual state
+        return modifier.make_vrawbuffer(self.size,
+                                        self.buffer.offsets[:],
+                                        self.buffer.descrs[:])
+
+
+class VRawSliceValue(AbstractVirtualValue):
+
+    def __init__(self, rawbuffer_value, offset, keybox, source_op):
+        AbstractVirtualValue.__init__(self, keybox, source_op)
+        self.rawbuffer_value = rawbuffer_value
+        self.offset = offset
+
+    def _really_force(self, optforce):
+        op = self.source_op
+        assert op is not None
+        if not we_are_translated():
+            op.name = 'FORCE ' + self.source_op.name
+        self.box = self.source_op.result
+        self.rawbuffer_value.force_box(optforce)
+        optforce.emit_operation(op)
+
+    def setitem_raw(self, offset, length, descr, value):
+        self.rawbuffer_value.setitem_raw(self.offset+offset, length, descr, value)
+
+    def getitem_raw(self, offset, length, descr):
+        return self.rawbuffer_value.getitem_raw(self.offset+offset, length, descr)
+
 class OptVirtualize(optimizer.Optimization):
     "Virtualize objects until they escape."
 
@@ -388,6 +500,17 @@
         self.make_equal_to(box, vvalue)
         return vvalue
 
+    def make_virtual_raw_memory(self, size, box, source_op):
+        logops = self.optimizer.loop.logops
+        vvalue = VRawBufferValue(self.optimizer.cpu, logops, size, box, source_op)
+        self.make_equal_to(box, vvalue)
+        return vvalue
+
+    def make_virtual_raw_slice(self, rawbuffer_value, offset, box, source_op):
+        vvalue = VRawSliceValue(rawbuffer_value, offset, box, source_op)
+        self.make_equal_to(box, vvalue)
+        return vvalue
+
     def optimize_GUARD_NO_EXCEPTION(self, op):
         if self.last_emitted_operation is REMOVED:
             return
@@ -524,6 +647,43 @@
             self.getvalue(op.result).ensure_nonnull()
             self.emit_operation(op)
 
+    def optimize_CALL(self, op):
+        effectinfo = op.getdescr().get_extra_info()
+        if effectinfo.oopspecindex == EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR:
+            self.do_RAW_MALLOC_VARSIZE_CHAR(op)
+        elif effectinfo.oopspecindex == EffectInfo.OS_RAW_FREE:
+            self.do_RAW_FREE(op)
+        else:
+            self.emit_operation(op)
+
+    def do_RAW_MALLOC_VARSIZE_CHAR(self, op):
+        sizebox = op.getarg(1)
+        if not isinstance(sizebox, ConstInt):
+            self.emit_operation(op)
+            return
+        size = sizebox.value
+        self.make_virtual_raw_memory(size, op.result, op)
+
+    def do_RAW_FREE(self, op):
+        value = self.getvalue(op.getarg(1))
+        if value.is_virtual():
+            return
+        self.emit_operation(op)
+
+    def optimize_INT_ADD(self, op):
+        value = self.getvalue(op.getarg(0))
+        offsetbox = self.get_constant_box(op.getarg(1))
+        if value.is_virtual() and offsetbox is not None:
+            offset = offsetbox.getint()
+            if isinstance(value, VRawBufferValue):
+                self.make_virtual_raw_slice(value, offset, op.result, op)
+                return
+            elif isinstance(value, VRawSliceValue):
+                offset = offset + value.offset
+                self.make_virtual_raw_slice(value.rawbuffer_value, offset, op.result, op)
+                return
+        self.emit_operation(op)
+
     def optimize_ARRAYLEN_GC(self, op):
         value = self.getvalue(op.getarg(0))
         if value.is_virtual():
@@ -558,6 +718,48 @@
         value.ensure_nonnull()
         self.emit_operation(op)
 
+    def _unpack_arrayitem_raw_op(self, op, indexbox):
+        index = indexbox.getint()
+        cpu = self.optimizer.cpu
+        descr = op.getdescr()
+        basesize, itemsize, _ = cpu.unpack_arraydescr_size(descr)
+        offset = basesize + (itemsize*index)
+        return offset, itemsize, descr
+
+    def optimize_GETARRAYITEM_RAW(self, op):
+        value = self.getvalue(op.getarg(0))
+        if value.is_virtual():
+            indexbox = self.get_constant_box(op.getarg(1))
+            if indexbox is not None:
+                offset, itemsize, descr = self._unpack_arrayitem_raw_op(op, indexbox)
+                try:
+                    itemvalue = value.getitem_raw(offset, itemsize, descr)
+                    self.make_equal_to(op.result, itemvalue)
+                except InvalidRawOperation:
+                    box = value.force_box(self)
+                    op.setarg(0, box)
+                    self.emit_operation(op)
+                return
+        value.ensure_nonnull()
+        self.emit_operation(op)
+
+    def optimize_SETARRAYITEM_RAW(self, op):
+        value = self.getvalue(op.getarg(0))
+        if value.is_virtual():
+            indexbox = self.get_constant_box(op.getarg(1))
+            if indexbox is not None:
+                offset, itemsize, descr = self._unpack_arrayitem_raw_op(op, indexbox)
+                itemvalue = self.getvalue(op.getarg(2))
+                try:
+                    value.setitem_raw(offset, itemsize, descr, itemvalue)
+                except InvalidRawOperation:
+                    box = value.force_box(self)
+                    op.setarg(0, box)
+                    self.emit_operation(op)
+                return
+        value.ensure_nonnull()
+        self.emit_operation(op)
+
     def optimize_GETINTERIORFIELD_GC(self, op):
         value = self.getvalue(op.getarg(0))
         if value.is_virtual():
diff --git a/rpython/jit/metainterp/optimizeopt/virtualstate.py b/rpython/jit/metainterp/optimizeopt/virtualstate.py
--- a/rpython/jit/metainterp/optimizeopt/virtualstate.py
+++ b/rpython/jit/metainterp/optimizeopt/virtualstate.py
@@ -158,10 +158,16 @@
     def debug_header(self, indent):
         debug_print(indent + 'VStructStateInfo(%d):' % self.position)
 
+
 class VArrayStateInfo(AbstractVirtualStateInfo):
+
     def __init__(self, arraydescr):
         self.arraydescr = arraydescr
 
+    def _generalization_of(self, other):
+        return (isinstance(other, VArrayStateInfo) and
+            self.arraydescr is other.arraydescr)
+
     def generalization_of(self, other, renum, bad):
         assert self.position != -1
         if self.position in renum:
@@ -187,10 +193,6 @@
                 return False
         return True
 
-    def _generalization_of(self, other):
-        return (isinstance(other, VArrayStateInfo) and
-            self.arraydescr is other.arraydescr)
-
     def enum_forced_boxes(self, boxes, value, optimizer):
         if not isinstance(value, virtualize.VArrayValue):
             raise BadVirtualState
@@ -198,7 +200,7 @@
             raise BadVirtualState
         for i in range(len(self.fieldstate)):
             try:
-                v = value._items[i]
+                v = value.get_item_value(i)
             except IndexError:
                 raise BadVirtualState
             s = self.fieldstate[i]
@@ -212,6 +214,8 @@
     def debug_header(self, indent):
         debug_print(indent + 'VArrayStateInfo(%d):' % self.position)
 
+
+
 class VArrayStructStateInfo(AbstractVirtualStateInfo):
     def __init__(self, arraydescr, fielddescrs):
         self.arraydescr = arraydescr
@@ -287,6 +291,7 @@
         debug_print(indent + 'VArrayStructStateInfo(%d):' % self.position)
 
 
+
 class NotVirtualStateInfo(AbstractVirtualStateInfo):
     def __init__(self, value, is_opaque=False):
         self.is_opaque = is_opaque
@@ -579,6 +584,9 @@
     def make_varraystruct(self, arraydescr, fielddescrs):
         return VArrayStructStateInfo(arraydescr, fielddescrs)
 
+    def make_vrawbuffer(self, size, offsets, descrs):
+        raise NotImplementedError
+
 class BoxNotProducable(Exception):
     pass
 
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
@@ -2595,6 +2595,7 @@
         box_exchange_buffer = op.getarg(3)
         self.history.operations.pop()
         arg_boxes = []
+
         for i in range(cif_description.nargs):
             kind, descr, itemsize = get_arg_descr(self.cpu,
                                                   cif_description.atypes[i])
diff --git a/rpython/jit/metainterp/resume.py b/rpython/jit/metainterp/resume.py
--- a/rpython/jit/metainterp/resume.py
+++ b/rpython/jit/metainterp/resume.py
@@ -275,6 +275,9 @@
     def make_varraystruct(self, arraydescr, fielddescrs):
         return VArrayStructInfo(arraydescr, fielddescrs)
 
+    def make_vrawbuffer(self, size, offsets, descrs):
+        return VRawBufferStateInfo(size, offsets, descrs)
+
     def make_vstrplain(self, is_unicode=False):
         if is_unicode:
             return VUniPlainInfo()
@@ -446,8 +449,8 @@
                 return self.liveboxes_from_env[box]
             return self.liveboxes[box]
 
-
 class AbstractVirtualInfo(object):
+    kind = REF
     #def allocate(self, decoder, index):
     #    raise NotImplementedError
     def equals(self, fieldnums):
@@ -458,6 +461,7 @@
 
     def debug_prints(self):
         raise NotImplementedError
+        
 
 class AbstractVirtualStructInfo(AbstractVirtualInfo):
     def __init__(self, fielddescrs):
@@ -486,7 +490,7 @@
     @specialize.argtype(1)
     def allocate(self, decoder, index):
         struct = decoder.allocate_with_vtable(self.known_class)
-        decoder.virtuals_cache[index] = struct
+        decoder.virtuals_cache.set_ptr(index, struct)
         return self.setfields(decoder, struct)
 
     def debug_prints(self):
@@ -502,7 +506,7 @@
     @specialize.argtype(1)
     def allocate(self, decoder, index):
         struct = decoder.allocate_struct(self.typedescr)
-        decoder.virtuals_cache[index] = struct
+        decoder.virtuals_cache.set_ptr(index, struct)
         return self.setfields(decoder, struct)
 
     def debug_prints(self):
@@ -519,7 +523,7 @@
         length = len(self.fieldnums)
         arraydescr = self.arraydescr
         array = decoder.allocate_array(length, arraydescr)
-        decoder.virtuals_cache[index] = array
+        decoder.virtuals_cache.set_ptr(index, array)
         # NB. the check for the kind of array elements is moved out of the loop
         if arraydescr.is_array_of_pointers():
             for i in range(length):
@@ -541,6 +545,31 @@
             debug_print("\t\t", str(untag(i)))
 
 
+class VRawBufferStateInfo(AbstractVirtualInfo):
+    kind = INT
+    
+    def __init__(self, size, offsets, descrs):
+        self.size = size
+        self.offsets = offsets
+        self.descrs = descrs
+
+    @specialize.argtype(1)
+    def allocate_int(self, decoder, index):
+        length = len(self.fieldnums)
+        buffer = decoder.allocate_raw_buffer(self.size)
+        decoder.virtuals_cache.set_int(index, buffer)
+        for i in range(len(self.offsets)):
+            offset = self.offsets[i]
+            descr = self.descrs[i]
+            decoder.setrawbuffer_item(buffer, self.fieldnums[i], offset, descr)
+        return buffer
+
+    def debug_prints(self):
+        debug_print("\tvrawbufferinfo", " at ",  compute_unique_id(self))
+        for i in self.fieldnums:
+            debug_print("\t\t", str(untag(i)))
+
+
 class VArrayStructInfo(AbstractVirtualInfo):
     def __init__(self, arraydescr, fielddescrs):
         self.arraydescr = arraydescr
@@ -554,7 +583,7 @@
     @specialize.argtype(1)
     def allocate(self, decoder, index):
         array = decoder.allocate_array(len(self.fielddescrs), self.arraydescr)
-        decoder.virtuals_cache[index] = array
+        decoder.virtuals_cache.set_ptr(index, array)
         p = 0
         for i in range(len(self.fielddescrs)):
             for j in range(len(self.fielddescrs[i])):
@@ -571,7 +600,7 @@
     def allocate(self, decoder, index):
         length = len(self.fieldnums)
         string = decoder.allocate_string(length)
-        decoder.virtuals_cache[index] = string
+        decoder.virtuals_cache.set_ptr(index, string)
         for i in range(length):
             charnum = self.fieldnums[i]
             if not tagged_eq(charnum, UNINITIALIZED):
@@ -593,7 +622,7 @@
         # efficient.  Not sure we care.
         left, right = self.fieldnums
         string = decoder.concat_strings(left, right)
-        decoder.virtuals_cache[index] = string
+        decoder.virtuals_cache.set_ptr(index, string)
         return string
 
     def debug_prints(self):
@@ -609,7 +638,7 @@
     def allocate(self, decoder, index):
         largerstr, start, length = self.fieldnums
         string = decoder.slice_string(largerstr, start, length)
-        decoder.virtuals_cache[index] = string
+        decoder.virtuals_cache.set_ptr(index, string)
         return string
 
     def debug_prints(self):
@@ -626,7 +655,7 @@
     def allocate(self, decoder, index):
         length = len(self.fieldnums)
         string = decoder.allocate_unicode(length)
-        decoder.virtuals_cache[index] = string
+        decoder.virtuals_cache.set_ptr(index, string)
         for i in range(length):
             charnum = self.fieldnums[i]
             if not tagged_eq(charnum, UNINITIALIZED):
@@ -648,7 +677,7 @@
         # efficient.  Not sure we care.
         left, right = self.fieldnums
         string = decoder.concat_unicodes(left, right)
-        decoder.virtuals_cache[index] = string
+        decoder.virtuals_cache.set_ptr(index, string)
         return string
 
     def debug_prints(self):
@@ -665,7 +694,7 @@
     def allocate(self, decoder, index):
         largerstr, start, length = self.fieldnums
         string = decoder.slice_unicode(largerstr, start, length)
-        decoder.virtuals_cache[index] = string
+        decoder.virtuals_cache.set_ptr(index, string)
         return string
 
     def debug_prints(self):
@@ -675,6 +704,33 @@
 
 # ____________________________________________________________
 
+class AbstractVirtualCache(object):
+    pass
+
+def get_VirtualCache_class(suffix):
+    # we need to create two copy of this class, because virtuals_*_cache will
+    # be lists of different types (one for ResumeDataDirectReader and one for
+    # ResumeDataBoxReader)
+    class VirtualCache(AbstractVirtualCache):
+        def __init__(self, virtuals_ptr_cache, virtuals_int_cache):
+            self.virtuals_ptr_cache = virtuals_ptr_cache
+            self.virtuals_int_cache = virtuals_int_cache
+
+        def get_ptr(self, i):
+            return self.virtuals_ptr_cache[i]
+
+        def get_int(self, i):
+            return self.virtuals_int_cache[i]
+
+        def set_ptr(self, i, v):
+            self.virtuals_ptr_cache[i] = v
+
+        def set_int(self, i, v):
+            self.virtuals_int_cache[i] = v
+
+    VirtualCache.__name__ += suffix
+    return VirtualCache
+
 class AbstractResumeDataReader(object):
     """A base mixin containing the logic to reconstruct virtuals out of
     guard failure.  There are two implementations of this mixin:
@@ -685,7 +741,9 @@
     _mixin_ = True
     rd_virtuals = None
     virtuals_cache = None
-    virtual_default = None
+    virtual_ptr_default = None
+    virtual_int_default = None
+
 
     def _init(self, cpu, storage):
         self.cpu = cpu
@@ -697,31 +755,50 @@
         self._prepare_virtuals(storage.rd_virtuals)
         self._prepare_pendingfields(storage.rd_pendingfields)
 
-    def getvirtual(self, index):
+    def getvirtual_ptr(self, index):
         # Returns the index'th virtual, building it lazily if needed.
         # Note that this may be called recursively; that's why the
         # allocate() methods must fill in the cache as soon as they
         # have the object, before they fill its fields.
         assert self.virtuals_cache is not None
-        v = self.virtuals_cache[index]
+        v = self.virtuals_cache.get_ptr(index)
         if not v:
             assert self.rd_virtuals is not None
             v = self.rd_virtuals[index].allocate(self, index)
-            ll_assert(v == self.virtuals_cache[index], "resume.py: bad cache")
+            ll_assert(v == self.virtuals_cache.get_ptr(index), "resume.py: bad cache")
+        return v
+
+    def getvirtual_int(self, index):
+        assert self.virtuals_cache is not None
+        v = self.virtuals_cache.get_int(index)
+        if not v:
+            v = self.rd_virtuals[index].allocate_int(self, index)
+            ll_assert(v == self.virtuals_cache.get_int(index), "resume.py: bad cache")
         return v
 
     def force_all_virtuals(self):
         rd_virtuals = self.rd_virtuals
         if rd_virtuals:
             for i in range(len(rd_virtuals)):
-                if rd_virtuals[i] is not None:
-                    self.getvirtual(i)
+                rd_virtual = rd_virtuals[i]
+                if rd_virtual is not None:
+                    if rd_virtual.kind == REF:
+                        self.getvirtual_ptr(i)
+                    elif rd_virtual.kind == INT:
+                        self.getvirtual_int(i)
+                    else:
+                        assert False
         return self.virtuals_cache
 
     def _prepare_virtuals(self, virtuals):
         if virtuals:
             self.rd_virtuals = virtuals
-            self.virtuals_cache = [self.virtual_default] * len(virtuals)
+            # XXX: this is suboptimal, because we are creating two lists, one
+            # for REFs and one for INTs: but for each index, we are using
+            # either one or the other, so we should think of a way to
+            # "compact" them
+            self.virtuals_cache = self.VirtualCache([self.virtual_ptr_default] * len(virtuals),
+                                                    [self.virtual_int_default] * len(virtuals))
 
     def _prepare_pendingfields(self, pendingfields):
         if pendingfields:
@@ -791,6 +868,7 @@
 
 class ResumeDataBoxReader(AbstractResumeDataReader):
     unique_id = lambda: None
+    VirtualCache = get_VirtualCache_class('BoxReader')
 
     def __init__(self, storage, deadframe, metainterp):
         self._init(metainterp.cpu, storage)
@@ -848,6 +926,12 @@
         return self.metainterp.execute_and_record(rop.NEW_ARRAY,
                                                   arraydescr, ConstInt(length))
 
+    def allocate_raw_buffer(self, size):
+        cic = self.metainterp.staticdata.callinfocollection
+        calldescr, func = cic.callinfo_for_oopspec(EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR)
+        return self.metainterp.execute_and_record_varargs(
+            rop.CALL, [ConstInt(func), ConstInt(size)], calldescr)
+
     def allocate_string(self, length):
         return self.metainterp.execute_and_record(rop.NEWSTR,
                                                   None, ConstInt(length))
@@ -941,6 +1025,17 @@
                                            arraydescr, arraybox,
                                            ConstInt(index), itembox)
 


More information about the pypy-commit mailing list