[pypy-commit] pypy length-hint: merge default

pjenvey noreply at buildbot.pypy.org
Fri Aug 24 01:28:42 CEST 2012


Author: Philip Jenvey <pjenvey at underboss.org>
Branch: length-hint
Changeset: r56833:741b3e3ee863
Date: 2012-08-23 16:27 -0700
http://bitbucket.org/pypy/pypy/changeset/741b3e3ee863/

Log:	merge default

diff too long, truncating to 10000 out of 22145 lines

diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py
--- a/lib_pypy/_ctypes/basics.py
+++ b/lib_pypy/_ctypes/basics.py
@@ -59,7 +59,8 @@
         'resbuffer' is a _rawffi array of length 1 containing the value,
         and this returns a general Python object that corresponds.
         """
-        res = self.__new__(self)
+        res = object.__new__(self)
+        res.__class__ = self
         res.__dict__['_buffer'] = resbuffer
         res.__dict__['_base'] = base
         res.__dict__['_index'] = index
diff --git a/lib_pypy/_marshal.py b/lib_pypy/_marshal.py
--- a/lib_pypy/_marshal.py
+++ b/lib_pypy/_marshal.py
@@ -430,6 +430,7 @@
 def _read(self, n):
     pos = self.bufpos
     newpos = pos + n
+    if newpos > len(self.bufstr): raise EOFError
     ret = self.bufstr[pos : newpos]
     self.bufpos = newpos
     return ret
diff --git a/lib_pypy/greenlet.py b/lib_pypy/greenlet.py
--- a/lib_pypy/greenlet.py
+++ b/lib_pypy/greenlet.py
@@ -77,8 +77,6 @@
         try:
             unbound_method = getattr(_continulet, methodname)
             args = unbound_method(current, *args, to=target)
-        except GreenletExit, e:
-            args = (e,)
         finally:
             _tls.current = current
         #
@@ -132,6 +130,8 @@
     _tls.current = greenlet
     try:
         res = greenlet.run(*args)
+    except GreenletExit, e:
+        res = e
     finally:
         _continuation.permute(greenlet, greenlet.parent)
     return (res,)
diff --git a/lib_pypy/pypy_test/test_marshal_extra.py b/lib_pypy/pypy_test/test_marshal_extra.py
--- a/lib_pypy/pypy_test/test_marshal_extra.py
+++ b/lib_pypy/pypy_test/test_marshal_extra.py
@@ -142,4 +142,6 @@
         f2.close()
     assert obj == case
 
-
+def test_load_truncated_string():
+    s = '(\x02\x00\x00\x00i\x03\x00\x00\x00sB\xf9\x00\x00\nabcd'
+    py.test.raises(EOFError, marshal.loads, s)
diff --git a/lib_pypy/pyrepl/readline.py b/lib_pypy/pyrepl/readline.py
--- a/lib_pypy/pyrepl/readline.py
+++ b/lib_pypy/pyrepl/readline.py
@@ -194,7 +194,7 @@
         except _error:
             return _old_raw_input(prompt)
         reader.ps1 = prompt
-        return reader.readline(reader, startup_hook=self.startup_hook)
+        return reader.readline(startup_hook=self.startup_hook)
 
     def multiline_input(self, more_lines, ps1, ps2, returns_unicode=False):
         """Read an input on possibly multiple lines, asking for more
diff --git a/pypy/annotation/binaryop.py b/pypy/annotation/binaryop.py
--- a/pypy/annotation/binaryop.py
+++ b/pypy/annotation/binaryop.py
@@ -7,7 +7,7 @@
 from pypy.tool.pairtype import pair, pairtype
 from pypy.annotation.model import SomeObject, SomeInteger, SomeBool, s_Bool
 from pypy.annotation.model import SomeString, SomeChar, SomeList, SomeDict
-from pypy.annotation.model import SomeUnicodeCodePoint
+from pypy.annotation.model import SomeUnicodeCodePoint, SomeStringOrUnicode
 from pypy.annotation.model import SomeTuple, SomeImpossibleValue, s_ImpossibleValue
 from pypy.annotation.model import SomeInstance, SomeBuiltin, SomeIterator
 from pypy.annotation.model import SomePBC, SomeFloat, s_None
@@ -470,30 +470,37 @@
             "string formatting mixing strings and unicode not supported")
 
 
-class __extend__(pairtype(SomeString, SomeTuple)):
-    def mod((str, s_tuple)):
+class __extend__(pairtype(SomeString, SomeTuple),
+                 pairtype(SomeUnicodeString, SomeTuple)):
+    def mod((s_string, s_tuple)):
+        is_string = isinstance(s_string, SomeString)
+        is_unicode = isinstance(s_string, SomeUnicodeString)
+        assert is_string or is_unicode
         for s_item in s_tuple.items:
-            if isinstance(s_item, (SomeUnicodeCodePoint, SomeUnicodeString)):
+            if (is_unicode and isinstance(s_item, (SomeChar, SomeString)) or
+                is_string and isinstance(s_item, (SomeUnicodeCodePoint,
+                                                  SomeUnicodeString))):
                 raise NotImplementedError(
                     "string formatting mixing strings and unicode not supported")
-        getbookkeeper().count('strformat', str, s_tuple)
-        no_nul = str.no_nul
+        getbookkeeper().count('strformat', s_string, s_tuple)
+        no_nul = s_string.no_nul
         for s_item in s_tuple.items:
             if isinstance(s_item, SomeFloat):
                 pass   # or s_item is a subclass, like SomeInteger
-            elif isinstance(s_item, SomeString) and s_item.no_nul:
+            elif isinstance(s_item, SomeStringOrUnicode) and s_item.no_nul:
                 pass
             else:
                 no_nul = False
                 break
-        return SomeString(no_nul=no_nul)
+        return s_string.__class__(no_nul=no_nul)
 
 
-class __extend__(pairtype(SomeString, SomeObject)):
+class __extend__(pairtype(SomeString, SomeObject),
+                 pairtype(SomeUnicodeString, SomeObject)):
 
-    def mod((str, args)):
-        getbookkeeper().count('strformat', str, args)
-        return SomeString()
+    def mod((s_string, args)):
+        getbookkeeper().count('strformat', s_string, args)
+        return s_string.__class__()
 
 class __extend__(pairtype(SomeFloat, SomeFloat)):
     
diff --git a/pypy/annotation/description.py b/pypy/annotation/description.py
--- a/pypy/annotation/description.py
+++ b/pypy/annotation/description.py
@@ -450,6 +450,12 @@
                     attrs.update(self.basedesc.all_enforced_attrs)
                 self.all_enforced_attrs = attrs
 
+            if (self.is_builtin_exception_class() and
+                self.all_enforced_attrs is None):
+                from pypy.annotation import classdef
+                if self.pyobj not in classdef.FORCE_ATTRIBUTES_INTO_CLASSES:
+                    self.all_enforced_attrs = []    # no attribute allowed
+
     def add_source_attribute(self, name, value, mixin=False):
         if isinstance(value, types.FunctionType):
             # for debugging
diff --git a/pypy/annotation/test/test_annrpython.py b/pypy/annotation/test/test_annrpython.py
--- a/pypy/annotation/test/test_annrpython.py
+++ b/pypy/annotation/test/test_annrpython.py
@@ -3389,6 +3389,22 @@
         s = a.build_types(f, [str])
         assert isinstance(s, annmodel.SomeString)
 
+    def test_unicodeformatting(self):
+        def f(x):
+            return u'%s' % x
+
+        a = self.RPythonAnnotator()
+        s = a.build_types(f, [unicode])
+        assert isinstance(s, annmodel.SomeUnicodeString)
+
+    def test_unicodeformatting_tuple(self):
+        def f(x):
+            return u'%s' % (x,)
+
+        a = self.RPythonAnnotator()
+        s = a.build_types(f, [unicode])
+        assert isinstance(s, annmodel.SomeUnicodeString)
+
 
     def test_negative_slice(self):
         def f(s, e):
@@ -3813,7 +3829,7 @@
 
             def next(self):
                 return 1
-        
+
         def fn():
             s = 0
             for x in A():
@@ -3825,6 +3841,24 @@
         assert len(a.translator.graphs) == 3 # fn, __iter__, next
         assert isinstance(s, annmodel.SomeInteger)
 
+    def test_next_function(self):
+        def fn(n):
+            x = [0, 1, n]
+            i = iter(x)
+            return next(i) + next(i)
+
+        a = self.RPythonAnnotator()
+        s = a.build_types(fn, [int])
+        assert isinstance(s, annmodel.SomeInteger)
+
+    def test_no_attr_on_common_exception_classes(self):
+        for cls in [ValueError, Exception]:
+            def fn():
+                e = cls()
+                e.foo = "bar"
+            a = self.RPythonAnnotator()
+            py.test.raises(Exception, a.build_types, fn, [])
+
 def g(n):
     return [0,1,2,n]
 
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -34,13 +34,14 @@
      "thread", "itertools", "pyexpat", "_ssl", "cpyext", "array",
      "_bisect", "binascii", "_multiprocessing", '_warnings',
      "_collections", "_multibytecodec", "micronumpy", "_ffi",
-     "_continuation"]
+     "_continuation", "_cffi_backend"]
 ))
 
 translation_modules = default_modules.copy()
 translation_modules.update(dict.fromkeys(
     ["fcntl", "rctime", "select", "signal", "_rawffi", "zlib",
      "struct", "_md5", "cStringIO", "array", "_ffi",
+     "binascii",
      # the following are needed for pyrepl (and hence for the
      # interactive prompt/pdb)
      "termios", "_minimal_curses",
@@ -88,7 +89,6 @@
     "_rawffi": [("objspace.usemodules.struct", True)],
     "cpyext": [("translation.secondaryentrypoints", "cpyext"),
                ("translation.shared", sys.platform == "win32")],
-    "_ffi":    [("translation.jit_ffi", True)],
 }
 
 module_import_dependencies = {
diff --git a/pypy/config/test/test_pypyoption.py b/pypy/config/test/test_pypyoption.py
--- a/pypy/config/test/test_pypyoption.py
+++ b/pypy/config/test/test_pypyoption.py
@@ -72,8 +72,3 @@
         for path in c.getpaths(include_groups=True):
             fn = prefix + "." + path + ".txt"
             yield fn, check_file_exists, fn
-
-def test__ffi_opt():
-    config = get_pypy_config(translating=True)
-    config.objspace.usemodules._ffi = True
-    assert config.translation.jit_ffi
diff --git a/pypy/config/translationoption.py b/pypy/config/translationoption.py
--- a/pypy/config/translationoption.py
+++ b/pypy/config/translationoption.py
@@ -122,8 +122,6 @@
     ChoiceOption("jit_profiler", "integrate profiler support into the JIT",
                  ["off", "oprofile"],
                  default="off"),
-    # jit_ffi is automatically turned on by withmod-_ffi (which is enabled by default)
-    BoolOption("jit_ffi", "optimize libffi calls", default=False, cmdline=None),
     BoolOption("check_str_without_nul",
                "Forbid NUL chars in strings in some external function calls",
                default=False, cmdline=None),
diff --git a/pypy/conftest.py b/pypy/conftest.py
--- a/pypy/conftest.py
+++ b/pypy/conftest.py
@@ -186,6 +186,9 @@
     def delslice(self, obj, *args):
         obj.__delslice__(*args)
 
+    def is_w(self, obj1, obj2):
+        return obj1 is obj2
+
 def translation_test_so_skip_if_appdirect():
     if option.runappdirect:
         py.test.skip("translation test, skipped for appdirect")
diff --git a/pypy/doc/coding-guide.rst b/pypy/doc/coding-guide.rst
--- a/pypy/doc/coding-guide.rst
+++ b/pypy/doc/coding-guide.rst
@@ -255,7 +255,12 @@
   code if the translator can prove that they are non-negative.  When
   slicing a string it is necessary to prove that the slice start and
   stop indexes are non-negative. There is no implicit str-to-unicode cast
-  anywhere.
+  anywhere. Simple string formatting using the ``%`` operator works, as long
+  as the format string is known at translation time; the only supported
+  formatting specifiers are ``%s``, ``%d``, ``%x``, ``%o``, ``%f``, plus
+  ``%r`` but only for user-defined instances. Modifiers such as conversion
+  flags, precision, length etc. are not supported. Moreover, it is forbidden
+  to mix unicode and strings when formatting.
 
 **tuples**
 
diff --git a/pypy/doc/config/objspace.usemodules._cffi_backend.txt b/pypy/doc/config/objspace.usemodules._cffi_backend.txt
new file mode 100644
--- /dev/null
+++ b/pypy/doc/config/objspace.usemodules._cffi_backend.txt
@@ -0,0 +1,1 @@
+Core of CFFI (http://cffi.readthedocs.org)
diff --git a/pypy/doc/cppyy.rst b/pypy/doc/cppyy.rst
--- a/pypy/doc/cppyy.rst
+++ b/pypy/doc/cppyy.rst
@@ -153,6 +153,7 @@
 
 Automatic class loader
 ======================
+
 There is one big problem in the code above, that prevents its use in a (large
 scale) production setting: the explicit loading of the reflection library.
 Clearly, if explicit load statements such as these show up in code downstream
@@ -164,7 +165,9 @@
 The class loader makes use of so-called rootmap files, which ``genreflex``
 can produce.
 These files contain the list of available C++ classes and specify the library
-that needs to be loaded for their use.
+that needs to be loaded for their use (as an aside, this listing allows for a
+cross-check to see whether reflection info is generated for all classes that
+you expect).
 By convention, the rootmap files should be located next to the reflection info
 libraries, so that they can be found through the normal shared library search
 path.
@@ -198,6 +201,7 @@
 
 Advanced example
 ================
+
 The following snippet of C++ is very contrived, to allow showing that such
 pathological code can be handled and to show how certain features play out in
 practice::
@@ -253,6 +257,9 @@
 With the aid of a selection file, a large project can be easily managed:
 simply ``#include`` all relevant headers into a single header file that is
 handed to ``genreflex``.
+In fact, if you hand multiple header files to ``genreflex``, then a selection
+file is almost obligatory: without it, only classes from the last header will
+be selected.
 Then, apply a selection file to pick up all the relevant classes.
 For our purposes, the following rather straightforward selection will do
 (the name ``lcgdict`` for the root is historical, but required)::
@@ -325,15 +332,43 @@
 (active memory management is one such case), but by and large, if the use of a
 feature does not strike you as obvious, it is more likely to simply be a bug.
 That is a strong statement to make, but also a worthy goal.
+For the C++ side of the examples, refer to this `example code`_, which was
+bound using::
+
+    $ genreflex example.h --deep --rootmap=libexampleDict.rootmap --rootmap-lib=libexampleDict.so
+    $ g++ -fPIC -rdynamic -O2 -shared -I$ROOTSYS/include example_rflx.cpp -o libexampleDict.so -L$ROOTSYS/lib -lReflex
+
+.. _`example code`: cppyy_example.html
 
 * **abstract classes**: Are represented as python classes, since they are
   needed to complete the inheritance hierarchies, but will raise an exception
   if an attempt is made to instantiate from them.
+  Example::
+
+    >>>> from cppyy.gbl import AbstractClass, ConcreteClass
+    >>>> a = AbstractClass()
+    Traceback (most recent call last):
+      File "<console>", line 1, in <module>
+    TypeError: cannot instantiate abstract class 'AbstractClass'
+    >>>> issubclass(ConcreteClass, AbstractClass)
+    True
+    >>>> c = ConcreteClass()
+    >>>> isinstance(c, AbstractClass)
+    True
+    >>>>
 
 * **arrays**: Supported for builtin data types only, as used from module
   ``array``.
   Out-of-bounds checking is limited to those cases where the size is known at
   compile time (and hence part of the reflection info).
+  Example::
+
+    >>>> from cppyy.gbl import ConcreteClass
+    >>>> from array import array
+    >>>> c = ConcreteClass()
+    >>>> c.array_method(array('d', [1., 2., 3., 4.]), 4)
+    1 2 3 4
+    >>>> 
 
 * **builtin data types**: Map onto the expected equivalent python types, with
   the caveat that there may be size differences, and thus it is possible that
@@ -344,23 +379,77 @@
   in the hierarchy of the object being returned.
   This is important to preserve object identity as well as to make casting,
   a pure C++ feature after all, superfluous.
+  Example::
+
+    >>>> from cppyy.gbl import AbstractClass, ConcreteClass
+    >>>> c = ConcreteClass()
+    >>>> ConcreteClass.show_autocast.__doc__
+    'AbstractClass* ConcreteClass::show_autocast()'
+    >>>> d = c.show_autocast()
+    >>>> type(d)
+    <class '__main__.ConcreteClass'>
+    >>>>
+
+  However, if need be, you can perform C++-style reinterpret_casts (i.e.
+  without taking offsets into account), by taking and rebinding the address
+  of an object::
+
+    >>>> from cppyy import addressof, bind_object
+    >>>> e = bind_object(addressof(d), AbstractClass)
+    >>>> type(e)
+    <class '__main__.AbstractClass'>
+    >>>>
 
 * **classes and structs**: Get mapped onto python classes, where they can be
   instantiated as expected.
   If classes are inner classes or live in a namespace, their naming and
   location will reflect that.
+  Example::
+
+    >>>> from cppyy.gbl import ConcreteClass, Namespace
+    >>>> ConcreteClass == Namespace.ConcreteClass
+    False
+    >>>> n = Namespace.ConcreteClass.NestedClass()
+    >>>> type(n)
+    <class '__main__.Namespace::ConcreteClass::NestedClass'>
+    >>>> 
 
 * **data members**: Public data members are represented as python properties
   and provide read and write access on instances as expected.
+  Private and protected data members are not accessible.
+  Example::
+
+    >>>> from cppyy.gbl import ConcreteClass
+    >>>> c = ConcreteClass()
+    >>>> c.m_int
+    42
+    >>>>
 
 * **default arguments**: C++ default arguments work as expected, but python
   keywords are not supported.
   It is technically possible to support keywords, but for the C++ interface,
   the formal argument names have no meaning and are not considered part of the
   API, hence it is not a good idea to use keywords.
+  Example::
+
+    >>>> from cppyy.gbl import ConcreteClass
+    >>>> c = ConcreteClass()       # uses default argument
+    >>>> c.m_int
+    42
+    >>>> c = ConcreteClass(13)
+    >>>> c.m_int
+    13
+    >>>>
 
 * **doc strings**: The doc string of a method or function contains the C++
   arguments and return types of all overloads of that name, as applicable.
+  Example::
+
+    >>>> from cppyy.gbl import ConcreteClass
+    >>>> print ConcreteClass.array_method.__doc__
+    void ConcreteClass::array_method(int*, int)
+    void ConcreteClass::array_method(double*, int)
+    >>>> 
 
 * **enums**: Are translated as ints with no further checking.
 
@@ -375,11 +464,40 @@
   This is a current, not a fundamental, limitation.
   The C++ side will not see any overridden methods on the python side, as
   cross-inheritance is planned but not yet supported.
+  Example::
+
+    >>>> from cppyy.gbl import ConcreteClass
+    >>>> help(ConcreteClass)
+    Help on class ConcreteClass in module __main__:
+
+    class ConcreteClass(AbstractClass)
+     |  Method resolution order:
+     |      ConcreteClass
+     |      AbstractClass
+     |      cppyy.CPPObject
+     |      __builtin__.CPPInstance
+     |      __builtin__.object
+     |  
+     |  Methods defined here:
+     |  
+     |  ConcreteClass(self, *args)
+     |      ConcreteClass::ConcreteClass(const ConcreteClass&)
+     |      ConcreteClass::ConcreteClass(int)
+     |      ConcreteClass::ConcreteClass()
+     |
+     etc. ....
 
 * **memory**: C++ instances created by calling their constructor from python
   are owned by python.
   You can check/change the ownership with the _python_owns flag that every
   bound instance carries.
+  Example::
+
+    >>>> from cppyy.gbl import ConcreteClass
+    >>>> c = ConcreteClass()
+    >>>> c._python_owns            # True: object created in Python
+    True
+    >>>> 
 
 * **methods**: Are represented as python methods and work as expected.
   They are first class objects and can be bound to an instance.
@@ -395,23 +513,34 @@
   Namespaces are more open-ended than classes, so sometimes initial access may
   result in updates as data and functions are looked up and constructed
   lazily.
-  Thus the result of ``dir()`` on a namespace should not be relied upon: it
-  only shows the already accessed members. (TODO: to be fixed by implementing
-  __dir__.)
+  Thus the result of ``dir()`` on a namespace shows the classes available,
+  even if they may not have been created yet.
+  It does not show classes that could potentially be loaded by the class
+  loader.
+  Once created, namespaces are registered as modules, to allow importing from
+  them.
+  Namespace currently do not work with the class loader.
+  Fixing these bootstrap problems is on the TODO list.
   The global namespace is ``cppyy.gbl``.
 
 * **operator conversions**: If defined in the C++ class and a python
   equivalent exists (i.e. all builtin integer and floating point types, as well
   as ``bool``), it will map onto that python conversion.
   Note that ``char*`` is mapped onto ``__str__``.
+  Example::
+
+    >>>> from cppyy.gbl import ConcreteClass
+    >>>> print ConcreteClass()
+    Hello operator const char*!
+    >>>> 
 
 * **operator overloads**: If defined in the C++ class and if a python
   equivalent is available (not always the case, think e.g. of ``operator||``),
   then they work as expected.
   Special care needs to be taken for global operator overloads in C++: first,
   make sure that they are actually reflected, especially for the global
-  overloads for ``operator==`` and ``operator!=`` of STL iterators in the case
-  of gcc.
+  overloads for ``operator==`` and ``operator!=`` of STL vector iterators in
+  the case of gcc (note that they are not needed to iterator over a vector).
   Second, make sure that reflection info is loaded in the proper order.
   I.e. that these global overloads are available before use.
 
@@ -441,17 +570,30 @@
   will be returned if the return type is ``const char*``.
 
 * **templated classes**: Are represented in a meta-class style in python.
-  This looks a little bit confusing, but conceptually is rather natural.
+  This may look a little bit confusing, but conceptually is rather natural.
   For example, given the class ``std::vector<int>``, the meta-class part would
-  be ``std.vector`` in python.
+  be ``std.vector``.
   Then, to get the instantiation on ``int``, do ``std.vector(int)`` and to
-  create an instance of that class, do ``std.vector(int)()``.
+  create an instance of that class, do ``std.vector(int)()``::
+
+    >>>> import cppyy
+    >>>> cppyy.load_reflection_info('libexampleDict.so')
+    >>>> cppyy.gbl.std.vector                # template metatype
+    <cppyy.CppyyTemplateType object at 0x00007fcdd330f1a0>
+    >>>> cppyy.gbl.std.vector(int)           # instantiates template -> class
+    <class '__main__.std::vector<int>'>
+    >>>> cppyy.gbl.std.vector(int)()         # instantiates class -> object
+    <__main__.std::vector<int> object at 0x00007fe480ba4bc0>
+    >>>> 
+
   Note that templates can be build up by handing actual types to the class
   instantiation (as done in this vector example), or by passing in the list of
   template arguments as a string.
   The former is a lot easier to work with if you have template instantiations
-  using classes that themselves are templates (etc.) in the arguments.
-  All classes must already exist in the loaded reflection info.
+  using classes that themselves are templates in  the arguments (think e.g a
+  vector of vectors).
+  All template classes must already exist in the loaded reflection info, they
+  do not work (yet) with the class loader.
 
 * **typedefs**: Are simple python references to the actual classes to which
   they refer.
@@ -502,11 +644,19 @@
 
 If you know for certain that all symbols will be linked in from other sources,
 you can also declare the explicit template instantiation ``extern``.
+An alternative is to add an object to an unnamed namespace::
 
-Unfortunately, this is not enough for gcc.
-The iterators, if they are going to be used, need to be instantiated as well,
-as do the comparison operators on those iterators, as these live in an
-internal namespace, rather than in the iterator classes.
+    namespace {
+        std::vector<MyClass> vmc;
+    } // unnamed namespace
+
+Unfortunately, this is not always enough for gcc.
+The iterators of vectors, if they are going to be used, need to be
+instantiated as well, as do the comparison operators on those iterators, as
+these live in an internal namespace, rather than in the iterator classes.
+Note that you do NOT need this iterators to iterator over a vector.
+You only need them if you plan to explicitly call e.g. ``begin`` and ``end``
+methods, and do comparisons of iterators.
 One way to handle this, is to deal with this once in a macro, then reuse that
 macro for all ``vector`` classes.
 Thus, the header above needs this (again protected with
@@ -533,8 +683,6 @@
     <lcgdict>
         <class pattern="std::vector<*>" />
         <class pattern="std::vector<*>::iterator" />
-        <class pattern="std::_Vector_base<*>" />
-        <class pattern="std::_Vector_base<*>::_Vector_impl" />
         <function name="__gnu_cxx::operator=="/>
         <function name="__gnu_cxx::operator!="/>
 
@@ -549,7 +697,7 @@
 Note: this is a dirty corner that clearly could do with some automation,
 even if the macro already helps.
 Such automation is planned.
-In fact, in the cling world, the backend can perform the template
+In fact, in the Cling world, the backend can perform the template
 instantations and generate the reflection info on the fly, and none of the
 above will any longer be necessary.
 
@@ -568,7 +716,8 @@
     1 2 3
     >>>>
 
-Other templates work similarly.
+Other templates work similarly, but are typically simpler, as there are no
+similar issues with iterators for e.g. ``std::list``.
 The arguments to the template instantiation can either be a string with the
 full list of arguments, or the explicit classes.
 The latter makes for easier code writing if the classes passed to the
@@ -655,3 +804,15 @@
 In that wrapper script you can rename methods exactly the way you need it.
 
 In the cling world, all these differences will be resolved.
+
+
+Python3
+=======
+
+To change versions of CPython (to Python3, another version of Python, or later
+to the `Py3k`_ version of PyPy), the only part that requires recompilation is
+the bindings module, be it ``cppyy`` or ``libPyROOT.so`` (in PyCintex).
+Although ``genreflex`` is indeed a Python tool, the generated reflection
+information is completely independent of Python.
+
+.. _`Py3k`: https://bitbucket.org/pypy/pypy/src/py3k
diff --git a/pypy/doc/cppyy_example.rst b/pypy/doc/cppyy_example.rst
new file mode 100644
--- /dev/null
+++ b/pypy/doc/cppyy_example.rst
@@ -0,0 +1,56 @@
+// File: example.h::
+
+    #include <iostream>
+    #include <vector>
+
+    class AbstractClass {
+    public:
+        virtual ~AbstractClass() {}
+        virtual void abstract_method() = 0;
+    };
+
+    class ConcreteClass : AbstractClass {
+    public:
+        ConcreteClass(int n=42) : m_int(n) {}
+        ~ConcreteClass() {}
+
+        virtual void abstract_method() {
+            std::cout << "called concrete method" << std::endl;
+        }
+
+        void array_method(int* ad, int size) {
+            for (int i=0; i < size; ++i)
+                std::cout << ad[i] << ' ';
+            std::cout << std::endl;
+        }
+
+        void array_method(double* ad, int size) {
+            for (int i=0; i < size; ++i)
+                std::cout << ad[i] << ' ';
+            std::cout << std::endl;
+        }
+
+        AbstractClass* show_autocast() {
+            return this;
+        }
+
+        operator const char*() {
+            return "Hello operator const char*!";
+        }
+
+    public:
+        int m_int;
+    };
+
+    namespace Namespace {
+
+       class ConcreteClass {
+       public:
+          class NestedClass {
+          public:
+             std::vector<int> m_v;
+          };
+
+       };
+
+    } // namespace Namespace
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
@@ -14,5 +14,24 @@
 .. branch: nupypy-axis-arg-check
 Check that axis arg is valid in _numpypy
 
+.. branch: iterator-in-rpython
+.. branch: numpypy_count_nonzero
+.. branch: even-more-jit-hooks
+Implement better JIT hooks
+.. branch: virtual-arguments
+Improve handling of **kwds greatly, making them virtual sometimes.
+.. branch: improve-rbigint
+Introduce __int128 on systems where it's supported and improve the speed of
+rlib/rbigint.py greatly.
+.. branch: translation-cleanup
+Start to clean up a bit the flow object space.
+.. branch: ffi-backend
+Support CFFI.  http://morepypy.blogspot.ch/2012/08/cffi-release-03.html
+.. branch: speedup-unpackiterable
+
+
 .. "uninteresting" branches that we should just ignore for the whatsnew:
 .. branch: slightly-shorter-c
+.. branch: better-enforceargs
+.. branch: rpython-unicode-formatting
+.. branch: jit-opaque-licm
diff --git a/pypy/doc/windows.rst b/pypy/doc/windows.rst
--- a/pypy/doc/windows.rst
+++ b/pypy/doc/windows.rst
@@ -135,6 +135,10 @@
 the name of a valid gcc-derivative compiler, i.e. x86_64-w64-mingw32-gcc for the 64 bit
 compiler creating a 64 bit target.
 
+You probably want to set the CPATH, LIBRARY_PATH, and PATH environment variable to
+the header files, lib or dlls, and dlls respectively of the locally installed packages 
+if they are not in the mingw directory heirarchy. 
+
 libffi for the mingw compiler
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -175,7 +179,7 @@
 Since hacking on Pypy means running tests, you will need a way to specify
 the mingw compiler when hacking (as opposed to translating). As of
 March 2012, --cc is not a valid option for pytest.py. However if you set an
-environment variable CC it will allow you to choose a compiler.
+environment variable CC to the compliter exe, testing will use it.
 
 .. _'mingw32 build': http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Automated%20Builds
 .. _`mingw64 build`: http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Automated%20Builds
diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py
--- a/pypy/interpreter/argument.py
+++ b/pypy/interpreter/argument.py
@@ -110,12 +110,10 @@
             make_sure_not_resized(self.keywords_w)
 
         make_sure_not_resized(self.arguments_w)
-        if w_stararg is not None:
-            self._combine_starargs_wrapped(w_stararg)
-        # if we have a call where **args are used at the callsite
-        # we shouldn't let the JIT see the argument matching
-        self._dont_jit = (w_starstararg is not None and
-                          self._combine_starstarargs_wrapped(w_starstararg))
+        self._combine_wrapped(w_stararg, w_starstararg)
+        # a flag that specifies whether the JIT can unroll loops that operate
+        # on the keywords
+        self._jit_few_keywords = self.keywords is None or jit.isconstant(len(self.keywords))
 
     def __repr__(self):
         """ NOT_RPYTHON """
@@ -129,7 +127,7 @@
 
     ###  Manipulation  ###
 
-    @jit.look_inside_iff(lambda self: not self._dont_jit)
+    @jit.look_inside_iff(lambda self: self._jit_few_keywords)
     def unpack(self): # slowish
         "Return a ([w1,w2...], {'kw':w3...}) pair."
         kwds_w = {}
@@ -176,13 +174,14 @@
         keywords, values_w = space.view_as_kwargs(w_starstararg)
         if keywords is not None: # this path also taken for empty dicts
             if self.keywords is None:
-                self.keywords = keywords[:] # copy to make non-resizable
-                self.keywords_w = values_w[:]
+                self.keywords = keywords
+                self.keywords_w = values_w
             else:
-                self._check_not_duplicate_kwargs(keywords, values_w)
+                _check_not_duplicate_kwargs(
+                    self.space, self.keywords, keywords, values_w)
                 self.keywords = self.keywords + keywords
                 self.keywords_w = self.keywords_w + values_w
-            return not jit.isconstant(len(self.keywords))
+            return
         if space.isinstance_w(w_starstararg, space.w_dict):
             keys_w = space.unpackiterable(w_starstararg)
         else:
@@ -198,57 +197,17 @@
                                    "a mapping, not %s" % (typename,)))
                 raise
             keys_w = space.unpackiterable(w_keys)
-        self._do_combine_starstarargs_wrapped(keys_w, w_starstararg)
-        return True
-
-    def _do_combine_starstarargs_wrapped(self, keys_w, w_starstararg):
-        space = self.space
         keywords_w = [None] * len(keys_w)
         keywords = [None] * len(keys_w)
-        i = 0
-        for w_key in keys_w:
-            try:
-                key = space.str_w(w_key)
-            except OperationError, e:
-                if e.match(space, space.w_TypeError):
-                    raise OperationError(
-                        space.w_TypeError,
-                        space.wrap("keywords must be strings"))
-                if e.match(space, space.w_UnicodeEncodeError):
-                    # Allow this to pass through
-                    key = None
-                else:
-                    raise
-            else:
-                if self.keywords and key in self.keywords:
-                    raise operationerrfmt(self.space.w_TypeError,
-                                          "got multiple values "
-                                          "for keyword argument "
-                                          "'%s'", key)
-            keywords[i] = key
-            keywords_w[i] = space.getitem(w_starstararg, w_key)
-            i += 1
+        _do_combine_starstarargs_wrapped(space, keys_w, w_starstararg, keywords, keywords_w, self.keywords)
+        self.keyword_names_w = keys_w
         if self.keywords is None:
             self.keywords = keywords
             self.keywords_w = keywords_w
         else:
             self.keywords = self.keywords + keywords
             self.keywords_w = self.keywords_w + keywords_w
-        self.keyword_names_w = keys_w
 
-    @jit.look_inside_iff(lambda self, keywords, keywords_w:
-            jit.isconstant(len(keywords) and
-            jit.isconstant(self.keywords)))
-    def _check_not_duplicate_kwargs(self, keywords, keywords_w):
-        # looks quadratic, but the JIT should remove all of it nicely.
-        # Also, all the lists should be small
-        for key in keywords:
-            for otherkey in self.keywords:
-                if otherkey == key:
-                    raise operationerrfmt(self.space.w_TypeError,
-                                          "got multiple values "
-                                          "for keyword argument "
-                                          "'%s'", key)
 
     def fixedunpack(self, argcount):
         """The simplest argument parsing: get the 'argcount' arguments,
@@ -269,34 +228,14 @@
 
     ###  Parsing for function calls  ###
 
-    # XXX: this should be @jit.look_inside_iff, but we need key word arguments,
-    # and it doesn't support them for now.
+    @jit.unroll_safe
     def _match_signature(self, w_firstarg, scope_w, signature, defaults_w=None,
                          blindargs=0):
         """Parse args and kwargs according to the signature of a code object,
         or raise an ArgErr in case of failure.
-        Return the number of arguments filled in.
         """
-        if jit.we_are_jitted() and self._dont_jit:
-            return self._match_signature_jit_opaque(w_firstarg, scope_w,
-                                                    signature, defaults_w,
-                                                    blindargs)
-        return self._really_match_signature(w_firstarg, scope_w, signature,
-                                            defaults_w, blindargs)
-
-    @jit.dont_look_inside
-    def _match_signature_jit_opaque(self, w_firstarg, scope_w, signature,
-                                    defaults_w, blindargs):
-        return self._really_match_signature(w_firstarg, scope_w, signature,
-                                            defaults_w, blindargs)
-
-    @jit.unroll_safe
-    def _really_match_signature(self, w_firstarg, scope_w, signature,
-                                defaults_w=None, blindargs=0):
-        #
+        #   w_firstarg = a first argument to be inserted (e.g. self) or None
         #   args_w = list of the normal actual parameters, wrapped
-        #   kwds_w = real dictionary {'keyword': wrapped parameter}
-        #   argnames = list of formal parameter names
         #   scope_w = resulting list of wrapped values
         #
 
@@ -304,38 +243,29 @@
         # so all values coming from there can be assumed constant. It assumes
         # that the length of the defaults_w does not vary too much.
         co_argcount = signature.num_argnames() # expected formal arguments, without */**
-        has_vararg = signature.has_vararg()
-        has_kwarg = signature.has_kwarg()
-        extravarargs = None
-        input_argcount =  0
 
+        # put the special w_firstarg into the scope, if it exists
         if w_firstarg is not None:
             upfront = 1
             if co_argcount > 0:
                 scope_w[0] = w_firstarg
-                input_argcount = 1
-            else:
-                extravarargs = [w_firstarg]
         else:
             upfront = 0
 
         args_w = self.arguments_w
         num_args = len(args_w)
+        avail = num_args + upfront
 
         keywords = self.keywords
-        keywords_w = self.keywords_w
         num_kwds = 0
         if keywords is not None:
             num_kwds = len(keywords)
 
-        avail = num_args + upfront
 
+        # put as many positional input arguments into place as available
+        input_argcount = upfront
         if input_argcount < co_argcount:
-            # put as many positional input arguments into place as available
-            if avail > co_argcount:
-                take = co_argcount - input_argcount
-            else:
-                take = num_args
+            take = min(num_args, co_argcount - upfront)
 
             # letting the JIT unroll this loop is safe, because take is always
             # smaller than co_argcount
@@ -344,11 +274,10 @@
             input_argcount += take
 
         # collect extra positional arguments into the *vararg
-        if has_vararg:
+        if signature.has_vararg():
             args_left = co_argcount - upfront
             if args_left < 0:  # check required by rpython
-                assert extravarargs is not None
-                starargs_w = extravarargs
+                starargs_w = [w_firstarg]
                 if num_args:
                     starargs_w = starargs_w + args_w
             elif num_args > args_left:
@@ -357,86 +286,65 @@
                 starargs_w = []
             scope_w[co_argcount] = self.space.newtuple(starargs_w)
         elif avail > co_argcount:
-            raise ArgErrCount(avail, num_kwds,
-                              co_argcount, has_vararg, has_kwarg,
-                              defaults_w, 0)
+            raise ArgErrCount(avail, num_kwds, signature, defaults_w, 0)
 
-        # the code assumes that keywords can potentially be large, but that
-        # argnames is typically not too large
-        num_remainingkwds = num_kwds
-        used_keywords = None
-        if keywords:
-            # letting JIT unroll the loop is *only* safe if the callsite didn't
-            # use **args because num_kwds can be arbitrarily large otherwise.
-            used_keywords = [False] * num_kwds
-            for i in range(num_kwds):
-                name = keywords[i]
-                # If name was not encoded as a string, it could be None. In that
-                # case, it's definitely not going to be in the signature.
-                if name is None:
-                    continue
-                j = signature.find_argname(name)
-                if j < 0:
-                    continue
-                elif j < input_argcount:
-                    # check that no keyword argument conflicts with these. note
-                    # that for this purpose we ignore the first blindargs,
-                    # which were put into place by prepend().  This way,
-                    # keywords do not conflict with the hidden extra argument
-                    # bound by methods.
-                    if blindargs <= j:
-                        raise ArgErrMultipleValues(name)
+        # if a **kwargs argument is needed, create the dict
+        w_kwds = None
+        if signature.has_kwarg():
+            w_kwds = self.space.newdict(kwargs=True)
+            scope_w[co_argcount + signature.has_vararg()] = w_kwds
+
+        # handle keyword arguments
+        num_remainingkwds = 0
+        keywords_w = self.keywords_w
+        kwds_mapping = None
+        if num_kwds:
+            # kwds_mapping maps target indexes in the scope (minus input_argcount)
+            # to positions in the keywords_w list
+            kwds_mapping = [0] * (co_argcount - input_argcount)
+            # initialize manually, for the JIT :-(
+            for i in range(len(kwds_mapping)):
+                kwds_mapping[i] = -1
+            # match the keywords given at the call site to the argument names
+            # the called function takes
+            # this function must not take a scope_w, to make the scope not
+            # escape
+            num_remainingkwds = _match_keywords(
+                    signature, blindargs, input_argcount, keywords,
+                    kwds_mapping, self._jit_few_keywords)
+            if num_remainingkwds:
+                if w_kwds is not None:
+                    # collect extra keyword arguments into the **kwarg
+                    _collect_keyword_args(
+                            self.space, keywords, keywords_w, w_kwds,
+                            kwds_mapping, self.keyword_names_w, self._jit_few_keywords)
                 else:
-                    assert scope_w[j] is None
-                    scope_w[j] = keywords_w[i]
-                    used_keywords[i] = True # mark as used
-                    num_remainingkwds -= 1
+                    if co_argcount == 0:
+                        raise ArgErrCount(avail, num_kwds, signature, defaults_w, 0)
+                    raise ArgErrUnknownKwds(self.space, num_remainingkwds, keywords,
+                                            kwds_mapping, self.keyword_names_w)
+
+        # check for missing arguments and fill them from the kwds,
+        # or with defaults, if available
         missing = 0
         if input_argcount < co_argcount:
             def_first = co_argcount - (0 if defaults_w is None else len(defaults_w))
+            j = 0
+            kwds_index = -1
             for i in range(input_argcount, co_argcount):
-                if scope_w[i] is not None:
-                    continue
+                if kwds_mapping is not None:
+                    kwds_index = kwds_mapping[j]
+                    j += 1
+                    if kwds_index >= 0:
+                        scope_w[i] = keywords_w[kwds_index]
+                        continue
                 defnum = i - def_first
                 if defnum >= 0:
                     scope_w[i] = defaults_w[defnum]
                 else:
-                    # error: not enough arguments.  Don't signal it immediately
-                    # because it might be related to a problem with */** or
-                    # keyword arguments, which will be checked for below.
                     missing += 1
-
-        # collect extra keyword arguments into the **kwarg
-        if has_kwarg:
-            w_kwds = self.space.newdict(kwargs=True)
-            if num_remainingkwds:
-                #
-                limit = len(keywords)
-                if self.keyword_names_w is not None:
-                    limit -= len(self.keyword_names_w)
-                for i in range(len(keywords)):
-                    if not used_keywords[i]:
-                        if i < limit:
-                            w_key = self.space.wrap(keywords[i])
-                        else:
-                            w_key = self.keyword_names_w[i - limit]
-                        self.space.setitem(w_kwds, w_key, keywords_w[i])
-                #
-            scope_w[co_argcount + has_vararg] = w_kwds
-        elif num_remainingkwds:
-            if co_argcount == 0:
-                raise ArgErrCount(avail, num_kwds,
-                              co_argcount, has_vararg, has_kwarg,
-                              defaults_w, missing)
-            raise ArgErrUnknownKwds(self.space, num_remainingkwds, keywords,
-                                    used_keywords, self.keyword_names_w)
-
-        if missing:
-            raise ArgErrCount(avail, num_kwds,
-                              co_argcount, has_vararg, has_kwarg,
-                              defaults_w, missing)
-
-        return co_argcount + has_vararg + has_kwarg
+            if missing:
+                raise ArgErrCount(avail, num_kwds, signature, defaults_w, missing)
 
 
 
@@ -448,11 +356,12 @@
         scope_w must be big enough for signature.
         """
         try:
-            return self._match_signature(w_firstarg,
-                                         scope_w, signature, defaults_w, 0)
+            self._match_signature(w_firstarg,
+                                  scope_w, signature, defaults_w, 0)
         except ArgErr, e:
             raise operationerrfmt(self.space.w_TypeError,
                                   "%s() %s", fnname, e.getmsg())
+        return signature.scope_length()
 
     def _parse(self, w_firstarg, signature, defaults_w, blindargs=0):
         """Parse args and kwargs according to the signature of a code object,
@@ -499,6 +408,102 @@
                 space.setitem(w_kwds, w_key, self.keywords_w[i])
         return w_args, w_kwds
 
+# JIT helper functions
+# these functions contain functionality that the JIT is not always supposed to
+# look at. They should not get a self arguments, which makes the amount of
+# arguments annoying :-(
+
+ at jit.look_inside_iff(lambda space, existingkeywords, keywords, keywords_w:
+        jit.isconstant(len(keywords) and
+        jit.isconstant(existingkeywords)))
+def _check_not_duplicate_kwargs(space, existingkeywords, keywords, keywords_w):
+    # looks quadratic, but the JIT should remove all of it nicely.
+    # Also, all the lists should be small
+    for key in keywords:
+        for otherkey in existingkeywords:
+            if otherkey == key:
+                raise operationerrfmt(space.w_TypeError,
+                                      "got multiple values "
+                                      "for keyword argument "
+                                      "'%s'", key)
+
+def _do_combine_starstarargs_wrapped(space, keys_w, w_starstararg, keywords,
+        keywords_w, existingkeywords):
+    i = 0
+    for w_key in keys_w:
+        try:
+            key = space.str_w(w_key)
+        except OperationError, e:
+            if e.match(space, space.w_TypeError):
+                raise OperationError(
+                    space.w_TypeError,
+                    space.wrap("keywords must be strings"))
+            if e.match(space, space.w_UnicodeEncodeError):
+                # Allow this to pass through
+                key = None
+            else:
+                raise
+        else:
+            if existingkeywords and key in existingkeywords:
+                raise operationerrfmt(space.w_TypeError,
+                                      "got multiple values "
+                                      "for keyword argument "
+                                      "'%s'", key)
+        keywords[i] = key
+        keywords_w[i] = space.getitem(w_starstararg, w_key)
+        i += 1
+
+ at jit.look_inside_iff(
+    lambda signature, blindargs, input_argcount,
+           keywords, kwds_mapping, jiton: jiton)
+def _match_keywords(signature, blindargs, input_argcount,
+                    keywords, kwds_mapping, _):
+    # letting JIT unroll the loop is *only* safe if the callsite didn't
+    # use **args because num_kwds can be arbitrarily large otherwise.
+    num_kwds = num_remainingkwds = len(keywords)
+    for i in range(num_kwds):
+        name = keywords[i]
+        # If name was not encoded as a string, it could be None. In that
+        # case, it's definitely not going to be in the signature.
+        if name is None:
+            continue
+        j = signature.find_argname(name)
+        # if j == -1 nothing happens, because j < input_argcount and
+        # blindargs > j
+        if j < input_argcount:
+            # check that no keyword argument conflicts with these. note
+            # that for this purpose we ignore the first blindargs,
+            # which were put into place by prepend().  This way,
+            # keywords do not conflict with the hidden extra argument
+            # bound by methods.
+            if blindargs <= j:
+                raise ArgErrMultipleValues(name)
+        else:
+            kwds_mapping[j - input_argcount] = i # map to the right index
+            num_remainingkwds -= 1
+    return num_remainingkwds
+
+ at jit.look_inside_iff(
+    lambda space, keywords, keywords_w, w_kwds, kwds_mapping,
+        keyword_names_w, jiton: jiton)
+def _collect_keyword_args(space, keywords, keywords_w, w_kwds, kwds_mapping,
+                          keyword_names_w, _):
+    limit = len(keywords)
+    if keyword_names_w is not None:
+        limit -= len(keyword_names_w)
+    for i in range(len(keywords)):
+        # again a dangerous-looking loop that either the JIT unrolls
+        # or that is not too bad, because len(kwds_mapping) is small
+        for j in kwds_mapping:
+            if i == j:
+                break
+        else:
+            if i < limit:
+                w_key = space.wrap(keywords[i])
+            else:
+                w_key = keyword_names_w[i - limit]
+            space.setitem(w_kwds, w_key, keywords_w[i])
+
 class ArgumentsForTranslation(Arguments):
     def __init__(self, space, args_w, keywords=None, keywords_w=None,
                  w_stararg=None, w_starstararg=None):
@@ -654,11 +659,9 @@
 
 class ArgErrCount(ArgErr):
 
-    def __init__(self, got_nargs, nkwds, expected_nargs, has_vararg, has_kwarg,
+    def __init__(self, got_nargs, nkwds, signature,
                  defaults_w, missing_args):
-        self.expected_nargs = expected_nargs
-        self.has_vararg = has_vararg
-        self.has_kwarg = has_kwarg
+        self.signature = signature
 
         self.num_defaults = 0 if defaults_w is None else len(defaults_w)
         self.missing_args = missing_args
@@ -666,16 +669,16 @@
         self.num_kwds = nkwds
 
     def getmsg(self):
-        n = self.expected_nargs
+        n = self.signature.num_argnames()
         if n == 0:
             msg = "takes no arguments (%d given)" % (
                 self.num_args + self.num_kwds)
         else:
             defcount = self.num_defaults
-            has_kwarg = self.has_kwarg
+            has_kwarg = self.signature.has_kwarg()
             num_args = self.num_args
             num_kwds = self.num_kwds
-            if defcount == 0 and not self.has_vararg:
+            if defcount == 0 and not self.signature.has_vararg():
                 msg1 = "exactly"
                 if not has_kwarg:
                     num_args += num_kwds
@@ -714,13 +717,13 @@
 
 class ArgErrUnknownKwds(ArgErr):
 
-    def __init__(self, space, num_remainingkwds, keywords, used_keywords,
+    def __init__(self, space, num_remainingkwds, keywords, kwds_mapping,
                  keyword_names_w):
         name = ''
         self.num_kwds = num_remainingkwds
         if num_remainingkwds == 1:
             for i in range(len(keywords)):
-                if not used_keywords[i]:
+                if i not in kwds_mapping:
                     name = keywords[i]
                     if name is None:
                         # We'll assume it's unicode. Encode it.
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -20,6 +20,9 @@
 
 UINT_MAX_32_BITS = r_uint(4294967295)
 
+unpackiterable_driver = jit.JitDriver(name = 'unpackiterable',
+                                      greens = ['tp'],
+                                      reds = ['items', 'w_iterator'])
 
 class W_Root(object):
     """This is the abstract root class of all wrapped objects that live
@@ -224,6 +227,23 @@
     def __spacebind__(self, space):
         return self
 
+class W_InterpIterable(W_Root):
+    def __init__(self, space, w_iterable):
+        self.w_iter = space.iter(w_iterable)
+        self.space = space
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        space = self.space
+        try:
+            return space.next(self.w_iter)
+        except OperationError, e:
+            if not e.match(space, space.w_StopIteration):
+                raise
+            raise StopIteration
+
 class InternalSpaceCache(Cache):
     """A generic cache for an object space.  Arbitrary information can
     be attached to the space by defining a function or class 'f' which
@@ -831,6 +851,9 @@
                                                       expected_length)
             return lst_w[:]     # make the resulting list resizable
 
+    def iteriterable(self, w_iterable):
+        return W_InterpIterable(self, w_iterable)
+
     @jit.dont_look_inside
     def _unpackiterable_unknown_length(self, w_iterator, w_iterable):
         # Unpack a variable-size list of unknown length.
@@ -844,7 +867,11 @@
         except MemoryError:
             items = [] # it might have lied
 
+        tp = self.type(w_iterator)
         while True:
+            unpackiterable_driver.jit_merge_point(tp=tp,
+                                                  w_iterator=w_iterator,
+                                                  items=items)
             try:
                 w_item = self.next(w_iterator)
             except OperationError, e:
@@ -1026,6 +1053,10 @@
         w_meth = self.getattr(w_obj, self.wrap(methname))
         return self.call_function(w_meth, *arg_w)
 
+    def raise_key_error(self, w_key):
+        e = self.call_function(self.w_KeyError, w_key)
+        raise OperationError(self.w_KeyError, e)
+
     def lookup(self, w_obj, name):
         w_type = self.type(w_obj)
         w_mro = self.getattr(w_type, self.wrap("__mro__"))
diff --git a/pypy/interpreter/test/test_argument.py b/pypy/interpreter/test/test_argument.py
--- a/pypy/interpreter/test/test_argument.py
+++ b/pypy/interpreter/test/test_argument.py
@@ -57,6 +57,9 @@
     def __nonzero__(self):
         raise NotImplementedError
 
+class kwargsdict(dict):
+    pass
+
 class DummySpace(object):
     def newtuple(self, items):
         return tuple(items)
@@ -76,9 +79,13 @@
         return list(it)
 
     def view_as_kwargs(self, x):
+        if len(x) == 0:
+            return [], []
         return None, None
 
     def newdict(self, kwargs=False):
+        if kwargs:
+            return kwargsdict()
         return {}
 
     def newlist(self, l=[]):
@@ -299,6 +306,22 @@
             args._match_signature(None, l, Signature(["a", "b", "c"], None, "**"))
             assert l == [1, 2, 3, {'d': 4}]
 
+    def test_match_kwds_creates_kwdict(self):
+        space = DummySpace()
+        kwds = [("c", 3), ('d', 4)]
+        for i in range(4):
+            kwds_w = dict(kwds[:i])
+            keywords = kwds_w.keys()
+            keywords_w = kwds_w.values()
+            w_kwds = dummy_wrapped_dict(kwds[i:])
+            if i == 3:
+                w_kwds = None
+            args = Arguments(space, [1, 2], keywords, keywords_w, w_starstararg=w_kwds)
+            l = [None, None, None, None]
+            args._match_signature(None, l, Signature(["a", "b", "c"], None, "**"))
+            assert l == [1, 2, 3, {'d': 4}]
+            assert isinstance(l[-1], kwargsdict)
+
     def test_duplicate_kwds(self):
         space = DummySpace()
         excinfo = py.test.raises(OperationError, Arguments, space, [], ["a"],
@@ -546,34 +569,47 @@
     def test_missing_args(self):
         # got_nargs, nkwds, expected_nargs, has_vararg, has_kwarg,
         # defaults_w, missing_args
-        err = ArgErrCount(1, 0, 0, False, False, None, 0)
+        sig = Signature([], None, None)
+        err = ArgErrCount(1, 0, sig, None, 0)
         s = err.getmsg()
         assert s == "takes no arguments (1 given)"
-        err = ArgErrCount(0, 0, 1, False, False, [], 1)
+
+        sig = Signature(['a'], None, None)
+        err = ArgErrCount(0, 0, sig, [], 1)
         s = err.getmsg()
         assert s == "takes exactly 1 argument (0 given)"
-        err = ArgErrCount(3, 0, 2, False, False, [], 0)
+
+        sig = Signature(['a', 'b'], None, None)
+        err = ArgErrCount(3, 0, sig, [], 0)
         s = err.getmsg()
         assert s == "takes exactly 2 arguments (3 given)"
-        err = ArgErrCount(3, 0, 2, False, False, ['a'], 0)
+        err = ArgErrCount(3, 0, sig, ['a'], 0)
         s = err.getmsg()
         assert s == "takes at most 2 arguments (3 given)"
-        err = ArgErrCount(1, 0, 2, True, False, [], 1)
+
+        sig = Signature(['a', 'b'], '*', None)
+        err = ArgErrCount(1, 0, sig, [], 1)
         s = err.getmsg()
         assert s == "takes at least 2 arguments (1 given)"
-        err = ArgErrCount(0, 1, 2, True, False, ['a'], 1)
+        err = ArgErrCount(0, 1, sig, ['a'], 1)
         s = err.getmsg()
         assert s == "takes at least 1 non-keyword argument (0 given)"
-        err = ArgErrCount(2, 1, 1, False, True, [], 0)
+
+        sig = Signature(['a'], None, '**')
+        err = ArgErrCount(2, 1, sig, [], 0)
         s = err.getmsg()
         assert s == "takes exactly 1 non-keyword argument (2 given)"
-        err = ArgErrCount(0, 1, 1, False, True, [], 1)
+        err = ArgErrCount(0, 1, sig, [], 1)
         s = err.getmsg()
         assert s == "takes exactly 1 non-keyword argument (0 given)"
-        err = ArgErrCount(0, 1, 1, True, True, [], 1)
+
+        sig = Signature(['a'], '*', '**')
+        err = ArgErrCount(0, 1, sig, [], 1)
         s = err.getmsg()
         assert s == "takes at least 1 non-keyword argument (0 given)"
-        err = ArgErrCount(2, 1, 1, False, True, ['a'], 0)
+
+        sig = Signature(['a'], None, '**')
+        err = ArgErrCount(2, 1, sig, ['a'], 0)
         s = err.getmsg()
         assert s == "takes at most 1 non-keyword argument (2 given)"
 
@@ -596,11 +632,14 @@
 
     def test_unknown_keywords(self):
         space = DummySpace()
-        err = ArgErrUnknownKwds(space, 1, ['a', 'b'], [True, False], None)
+        err = ArgErrUnknownKwds(space, 1, ['a', 'b'], [0], None)
         s = err.getmsg()
         assert s == "got an unexpected keyword argument 'b'"
+        err = ArgErrUnknownKwds(space, 1, ['a', 'b'], [1], None)
+        s = err.getmsg()
+        assert s == "got an unexpected keyword argument 'a'"
         err = ArgErrUnknownKwds(space, 2, ['a', 'b', 'c'],
-                                [True, False, False], None)
+                                [0], None)
         s = err.getmsg()
         assert s == "got 2 unexpected keyword arguments"
 
@@ -610,7 +649,7 @@
                 defaultencoding = 'utf-8'
         space = DummySpaceUnicode()
         err = ArgErrUnknownKwds(space, 1, ['a', None, 'b', 'c'],
-                                [True, False, True, True],
+                                [0, 3, 2],
                                 [unichr(0x1234), u'b', u'c'])
         s = err.getmsg()
         assert s == "got an unexpected keyword argument '\xe1\x88\xb4'"
diff --git a/pypy/interpreter/test/test_function.py b/pypy/interpreter/test/test_function.py
--- a/pypy/interpreter/test/test_function.py
+++ b/pypy/interpreter/test/test_function.py
@@ -16,6 +16,7 @@
         assert f.func_defaults == None
         assert f.func_dict == {}
         assert type(f.func_globals) == dict
+        assert f.func_globals is f.__globals__
         assert f.func_closure is None
         assert f.func_doc == None
         assert f.func_name == 'f'
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -37,7 +37,7 @@
         assert __total_ordering__ in (None, 'auto'), "Unknown value for __total_ordering"
         if __total_ordering__ == 'auto':
             self.auto_total_ordering()
-    
+
     def add_entries(self, **rawdict):
         # xxx fix the names of the methods to match what app-level expects
         for key, value in rawdict.items():
@@ -228,7 +228,7 @@
 
     def add(Proto):
         for key, value in Proto.__dict__.items():
-            if (not key.startswith('__') and not key.startswith('_mixin_') 
+            if (not key.startswith('__') and not key.startswith('_mixin_')
                     or key == '__del__'):
                 if hasattr(value, "func_name"):
                     value = func_with_new_name(value, value.func_name)
@@ -315,10 +315,10 @@
         class Proto(object):
             def getdict(self, space):
                 return self.w__dict__
-            
+
             def setdict(self, space, w_dict):
                 self.w__dict__ = check_new_dictionary(space, w_dict)
-            
+
             def user_setup(self, space, w_subtype):
                 self.w__dict__ = space.newdict(
                     instance=True)
@@ -383,7 +383,7 @@
             return %(name)s(%(args)s, %(extra)s)
         """
         miniglobals[cls_name] = cls
-    
+
     name = func.__name__
     extra = ', '.join(extraargs)
     from pypy.interpreter import pycode
@@ -503,7 +503,7 @@
                 space, '__delattr__',
                 self.reqcls, Arguments(space, [w_obj,
                                                space.wrap(self.name)]))
-    
+
     def descr_get_objclass(space, property):
         return property.objclass_getter(space)
 
@@ -521,7 +521,7 @@
             return space.w_None
         else:
             return w_value
-    
+
     return GetSetProperty(fget, cls=cls, doc=doc)
 
 GetSetProperty.typedef = TypeDef(
@@ -543,7 +543,7 @@
         self.index = index
         self.name = name
         self.w_cls = w_cls
-    
+
     def typecheck(self, space, w_obj):
         if not space.is_true(space.isinstance(w_obj, self.w_cls)):
             raise operationerrfmt(space.w_TypeError,
@@ -552,7 +552,7 @@
                                   self.name,
                                   self.w_cls.name,
                                   space.type(w_obj).getname(space))
-    
+
     def descr_member_get(self, space, w_obj, w_w_cls=None):
         """member.__get__(obj[, type]) -> value
         Read the slot 'member' of the given 'obj'."""
@@ -565,13 +565,13 @@
                 raise OperationError(space.w_AttributeError,
                                      space.wrap(self.name)) # XXX better message
             return w_result
-    
+
     def descr_member_set(self, space, w_obj, w_value):
         """member.__set__(obj, value)
         Write into the slot 'member' of the given 'obj'."""
         self.typecheck(space, w_obj)
         w_obj.setslotvalue(self.index, w_value)
-    
+
     def descr_member_del(self, space, w_obj):
         """member.__delete__(obj)
         Delete the value of the slot 'member' from the given 'obj'."""
@@ -803,15 +803,16 @@
     func_dict = getset_func_dict,
     func_defaults = getset_func_defaults,
     func_globals = interp_attrproperty_w('w_func_globals', cls=Function),
-    func_closure = GetSetProperty( Function.fget_func_closure ),
+    func_closure = GetSetProperty(Function.fget_func_closure),
     __code__ = getset_func_code,
     __doc__ = getset_func_doc,
     __name__ = getset_func_name,
     __dict__ = getset_func_dict,
     __defaults__ = getset_func_defaults,
+    __globals__ = interp_attrproperty_w('w_func_globals', cls=Function),
     __module__ = getset___module__,
     __weakref__ = make_weakref_descr(Function),
-    )
+)
 Function.typedef.acceptable_as_base_class = False
 
 Method.typedef = TypeDef(
diff --git a/pypy/jit/backend/llgraph/llimpl.py b/pypy/jit/backend/llgraph/llimpl.py
--- a/pypy/jit/backend/llgraph/llimpl.py
+++ b/pypy/jit/backend/llgraph/llimpl.py
@@ -21,7 +21,6 @@
 from pypy.jit.backend.llgraph import symbolic
 from pypy.jit.codewriter import longlong
 
-from pypy.rlib import libffi, clibffi
 from pypy.rlib.objectmodel import ComputedIntSymbolic, we_are_translated
 from pypy.rlib.rarithmetic import ovfcheck
 from pypy.rlib.rarithmetic import r_longlong, r_ulonglong, r_uint
@@ -64,7 +63,8 @@
 
 FLOAT_ARRAY_TP = lltype.Ptr(lltype.Array(lltype.Float, hints={"nolength": True}))
 def maybe_uncast(TP, array):
-    if array._TYPE.TO._hints.get("uncast_on_llgraph"):
+    if array._TYPE.TO.OF != lltype.Float:
+        # array._TYPE.TO._hints.get("uncast_on_llgraph"):
         array = rffi.cast(TP, array)
     return array
 
@@ -96,6 +96,7 @@
     'int_add_ovf'     : (('int', 'int'), 'int'),
     'int_sub_ovf'     : (('int', 'int'), 'int'),
     'int_mul_ovf'     : (('int', 'int'), 'int'),
+    'int_force_ge_zero':(('int',), 'int'),
     'uint_add'        : (('int', 'int'), 'int'),
     'uint_sub'        : (('int', 'int'), 'int'),
     'uint_mul'        : (('int', 'int'), 'int'),
@@ -802,7 +803,7 @@
         if arraydescr.typeinfo == REF:
             raise NotImplementedError("getarrayitem_raw -> gcref")
         elif arraydescr.typeinfo == INT:
-            return do_getarrayitem_raw_int(array, index)
+            return do_getarrayitem_raw_int(array, index, arraydescr.ofs)
         elif arraydescr.typeinfo == FLOAT:
             return do_getarrayitem_raw_float(array, index)
         else:
@@ -823,9 +824,7 @@
     op_getfield_gc_pure = op_getfield_gc
 
     def op_getfield_raw(self, fielddescr, struct):
-        if fielddescr.arg_types == 'dynamic': # abuse of .arg_types
-            return do_getfield_raw_dynamic(struct, fielddescr)
-        elif fielddescr.typeinfo == REF:
+        if fielddescr.typeinfo == REF:
             return do_getfield_raw_ptr(struct, fielddescr.ofs)
         elif fielddescr.typeinfo == INT:
             return do_getfield_raw_int(struct, fielddescr.ofs)
@@ -836,6 +835,26 @@
 
     op_getfield_raw_pure = op_getfield_raw
 
+    def op_raw_store(self, arraydescr, addr, offset, value):
+        if arraydescr.typeinfo == REF:
+            raise AssertionError("cannot store GC pointer in raw storage")
+        elif arraydescr.typeinfo == INT:
+            do_raw_store_int(addr, offset, arraydescr.ofs, value)
+        elif arraydescr.typeinfo == FLOAT:
+            do_raw_store_float(addr, offset, value)
+        else:
+            raise NotImplementedError
+
+    def op_raw_load(self, arraydescr, addr, offset):
+        if arraydescr.typeinfo == REF: 
+            raise AssertionError("cannot store GC pointer in raw storage")
+        elif arraydescr.typeinfo == INT:
+            return do_raw_load_int(addr, offset, arraydescr.ofs)
+        elif arraydescr.typeinfo == FLOAT:
+            return do_raw_load_float(addr, offset)
+        else:
+            raise NotImplementedError
+
     def op_new(self, size):
         return do_new(size.ofs)
 
@@ -861,7 +880,7 @@
         if arraydescr.typeinfo == REF:
             raise NotImplementedError("setarrayitem_raw <- gcref")
         elif arraydescr.typeinfo == INT:
-            do_setarrayitem_raw_int(array, index, newvalue)
+            do_setarrayitem_raw_int(array, index, newvalue, arraydescr.ofs)
         elif arraydescr.typeinfo == FLOAT:
             do_setarrayitem_raw_float(array, index, newvalue)
         else:
@@ -921,9 +940,7 @@
             raise NotImplementedError
 
     def op_setfield_raw(self, fielddescr, struct, newvalue):
-        if fielddescr.arg_types == 'dynamic': # abuse of .arg_types
-            do_setfield_raw_dynamic(struct, fielddescr, newvalue)
-        elif fielddescr.typeinfo == REF:
+        if fielddescr.typeinfo == REF:
             do_setfield_raw_ptr(struct, fielddescr.ofs, newvalue)
         elif fielddescr.typeinfo == INT:
             do_setfield_raw_int(struct, fielddescr.ofs, newvalue)
@@ -1432,9 +1449,13 @@
     array = array._obj.container
     return cast_to_int(array.getitem(index))
 
-def do_getarrayitem_raw_int(array, index):
-    array = array.adr.ptr._obj
-    return cast_to_int(array.getitem(index))
+def do_getarrayitem_raw_int(array, index, itemsize):
+    array = array.adr.ptr
+    ITEMTYPE = lltype.typeOf(array).TO.OF
+    TYPE = symbolic.Size2Type[itemsize]
+    if TYPE.OF != ITEMTYPE:
+        array = rffi.cast(lltype.Ptr(TYPE), array)
+    return cast_to_int(array._obj.getitem(index))
 
 def do_getarrayitem_gc_float(array, index):
     array = array._obj.container
@@ -1478,18 +1499,6 @@
     struct = array._obj.container.getitem(index)
     return cast_to_ptr(_getinteriorfield_gc(struct, fieldnum))
 
-def _getinteriorfield_raw(ffitype, array, index, width, ofs):
-    addr = rffi.cast(rffi.VOIDP, array)
-    return libffi.array_getitem(ffitype, width, addr, index, ofs)
-
-def do_getinteriorfield_raw_int(array, index, width, ofs):
-    res = _getinteriorfield_raw(libffi.types.slong, array, index, width, ofs)
-    return res
-
-def do_getinteriorfield_raw_float(array, index, width, ofs):
-    res = _getinteriorfield_raw(libffi.types.double, array, index, width, ofs)
-    return res
-
 def _getfield_raw(struct, fieldnum):
     STRUCT, fieldname = symbolic.TokenToField[fieldnum]
     ptr = cast_from_int(lltype.Ptr(STRUCT), struct)
@@ -1504,16 +1513,31 @@
 def do_getfield_raw_ptr(struct, fieldnum):
     return cast_to_ptr(_getfield_raw(struct, fieldnum))
 
-def do_getfield_raw_dynamic(struct, fielddescr):
-    from pypy.rlib import libffi
-    addr = cast_from_int(rffi.VOIDP, struct)
-    ofs = fielddescr.ofs
-    if fielddescr.is_pointer_field():
-        assert False, 'fixme'
-    elif fielddescr.is_float_field():
-        assert False, 'fixme'
-    else:
-        return libffi._struct_getfield(lltype.Signed, addr, ofs)
+def do_raw_load_int(struct, offset, descrofs):
+    TYPE = symbolic.Size2Type[descrofs]
+    ll_p = rffi.cast(rffi.CCHARP, struct)
+    ll_p = rffi.cast(lltype.Ptr(TYPE), rffi.ptradd(ll_p, offset))
+    value = ll_p[0]
+    return rffi.cast(lltype.Signed, value)
+
+def do_raw_load_float(struct, offset):
+    ll_p = rffi.cast(rffi.CCHARP, struct)
+    ll_p = rffi.cast(rffi.CArrayPtr(longlong.FLOATSTORAGE),
+                     rffi.ptradd(ll_p, offset))
+    value = ll_p[0]
+    return value
+
+def do_raw_store_int(struct, offset, descrofs, value):
+    TYPE = symbolic.Size2Type[descrofs]
+    ll_p = rffi.cast(rffi.CCHARP, struct)
+    ll_p = rffi.cast(lltype.Ptr(TYPE), rffi.ptradd(ll_p, offset))
+    ll_p[0] = rffi.cast(TYPE.OF, value)
+
+def do_raw_store_float(struct, offset, value):
+    ll_p = rffi.cast(rffi.CCHARP, struct)
+    ll_p = rffi.cast(rffi.CArrayPtr(longlong.FLOATSTORAGE),
+                     rffi.ptradd(ll_p, offset))
+    ll_p[0] = value
 
 def do_new(size):
     TYPE = symbolic.Size2Type[size]
@@ -1522,6 +1546,7 @@
 
 def do_new_array(arraynum, count):
     TYPE = symbolic.Size2Type[arraynum]
+    assert count >= 0 # explode if it's not
     x = lltype.malloc(TYPE, count, zero=True)
     return cast_to_ptr(x)
 
@@ -1531,10 +1556,13 @@
     newvalue = cast_from_int(ITEMTYPE, newvalue)
     array.setitem(index, newvalue)
 
-def do_setarrayitem_raw_int(array, index, newvalue):
+def do_setarrayitem_raw_int(array, index, newvalue, itemsize):
     array = array.adr.ptr
     ITEMTYPE = lltype.typeOf(array).TO.OF
-    newvalue = cast_from_int(ITEMTYPE, newvalue)
+    TYPE = symbolic.Size2Type[itemsize]
+    if TYPE.OF != ITEMTYPE:
+        array = rffi.cast(lltype.Ptr(TYPE), array)
+    newvalue = cast_from_int(TYPE.OF, newvalue)
     array._obj.setitem(index, newvalue)
 
 def do_setarrayitem_gc_float(array, index, newvalue):
@@ -1579,18 +1607,6 @@
 do_setinteriorfield_gc_float = new_setinteriorfield_gc(cast_from_floatstorage)
 do_setinteriorfield_gc_ptr = new_setinteriorfield_gc(cast_from_ptr)
 
-def new_setinteriorfield_raw(cast_func, ffitype):
-    def do_setinteriorfield_raw(array, index, newvalue, width, ofs):
-        addr = rffi.cast(rffi.VOIDP, array)
-        for TYPE, ffitype2 in clibffi.ffitype_map:
-            if ffitype2 is ffitype:
-                newvalue = cast_func(TYPE, newvalue)
-                break
-        return libffi.array_setitem(ffitype, width, addr, index, ofs, newvalue)
-    return do_setinteriorfield_raw
-do_setinteriorfield_raw_int = new_setinteriorfield_raw(cast_from_int, libffi.types.slong)
-do_setinteriorfield_raw_float = new_setinteriorfield_raw(cast_from_floatstorage, libffi.types.double)
-
 def do_setfield_raw_int(struct, fieldnum, newvalue):
     STRUCT, fieldname = symbolic.TokenToField[fieldnum]
     ptr = cast_from_int(lltype.Ptr(STRUCT), struct)
@@ -1612,17 +1628,6 @@
     newvalue = cast_from_ptr(FIELDTYPE, newvalue)
     setattr(ptr, fieldname, newvalue)
 
-def do_setfield_raw_dynamic(struct, fielddescr, newvalue):
-    from pypy.rlib import libffi
-    addr = cast_from_int(rffi.VOIDP, struct)
-    ofs = fielddescr.ofs
-    if fielddescr.is_pointer_field():
-        assert False, 'fixme'
-    elif fielddescr.is_float_field():
-        assert False, 'fixme'
-    else:
-        libffi._struct_setfield(lltype.Signed, addr, ofs, newvalue)
-
 def do_newstr(length):
     x = rstr.mallocstr(length)
     return cast_to_ptr(x)
@@ -1921,6 +1926,7 @@
 setannotation(do_getinteriorfield_gc_int, annmodel.SomeInteger())
 setannotation(do_getinteriorfield_gc_ptr, annmodel.SomePtr(llmemory.GCREF))
 setannotation(do_getinteriorfield_gc_float, s_FloatStorage)
+setannotation(do_raw_load_int, annmodel.SomeInteger())
 setannotation(do_new, annmodel.SomePtr(llmemory.GCREF))
 setannotation(do_new_array, annmodel.SomePtr(llmemory.GCREF))
 setannotation(do_setarrayitem_gc_int, annmodel.s_None)
@@ -1937,6 +1943,7 @@
 setannotation(do_setinteriorfield_gc_int, annmodel.s_None)
 setannotation(do_setinteriorfield_gc_ptr, annmodel.s_None)
 setannotation(do_setinteriorfield_gc_float, annmodel.s_None)
+setannotation(do_raw_store_int, annmodel.s_None)
 setannotation(do_newstr, annmodel.SomePtr(llmemory.GCREF))
 setannotation(do_strsetitem, annmodel.s_None)
 setannotation(do_newunicode, annmodel.SomePtr(llmemory.GCREF))
diff --git a/pypy/jit/backend/llgraph/runner.py b/pypy/jit/backend/llgraph/runner.py
--- a/pypy/jit/backend/llgraph/runner.py
+++ b/pypy/jit/backend/llgraph/runner.py
@@ -339,16 +339,6 @@
         token = history.getkind(getattr(S, fieldname))
         return self.getdescr(ofs, token[0], name=fieldname)
 
-    def fielddescrof_dynamic(self, offset, fieldsize, is_pointer, is_float, is_signed):
-        if is_pointer:
-            typeinfo = REF
-        elif is_float:
-            typeinfo = FLOAT
-        else:
-            typeinfo = INT
-        # we abuse the arg_types field to distinguish dynamic and static descrs
-        return self.getdescr(offset, typeinfo, arg_types='dynamic', name='<dynamic field>')
-
     def interiorfielddescrof(self, A, fieldname):
         S = A.OF
         width = symbolic.get_size(A)
@@ -356,18 +346,6 @@
         token = history.getkind(getattr(S, fieldname))
         return self.getdescr(ofs, token[0], name=fieldname, width=width)
 
-    def interiorfielddescrof_dynamic(self, offset, width, fieldsize,
-        is_pointer, is_float, is_signed):
-
-        if is_pointer:
-            typeinfo = REF
-        elif is_float:
-            typeinfo = FLOAT
-        else:
-            typeinfo = INT
-        # we abuse the arg_types field to distinguish dynamic and static descrs
-        return Descr(offset, typeinfo, arg_types='dynamic', name='<dynamic interior field>', width=width)
-
     def calldescrof(self, FUNC, ARGS, RESULT, extrainfo):
         arg_types = []
         for ARG in ARGS:
@@ -382,22 +360,27 @@
         return self.getdescr(0, token[0], extrainfo=extrainfo,
                              arg_types=''.join(arg_types))
 
-    def calldescrof_dynamic(self, ffi_args, ffi_result, extrainfo, ffi_flags):
+    def calldescrof_dynamic(self, cif_description, extrainfo):
         from pypy.jit.backend.llsupport.ffisupport import get_ffi_type_kind
         from pypy.jit.backend.llsupport.ffisupport import UnsupportedKind
         arg_types = []
         try:
-            for arg in ffi_args:
+            for arg in cif_description.atypes:
                 kind = get_ffi_type_kind(self, arg)
                 if kind != history.VOID:
                     arg_types.append(kind)
-            reskind = get_ffi_type_kind(self, ffi_result)
+            reskind = get_ffi_type_kind(self, cif_description.rtype)
         except UnsupportedKind:
             return None
         return self.getdescr(0, reskind, extrainfo=extrainfo,
                              arg_types=''.join(arg_types),
-                             ffi_flags=ffi_flags)
+                             ffi_flags=cif_description.abi)
 
+    def _calldescr_dynamic_for_tests(self, atypes, rtype,
+                                     abiname='FFI_DEFAULT_ABI'):
+        from pypy.jit.backend.llsupport import ffisupport
+        return ffisupport.calldescr_dynamic_for_tests(self, atypes, rtype,
+                                                      abiname)
 
     def grab_exc_value(self):
         return llimpl.grab_exc_value()
@@ -433,7 +416,7 @@
         return llimpl.do_getarrayitem_gc_int(array, index)
     def bh_getarrayitem_raw_i(self, arraydescr, array, index):
         assert isinstance(arraydescr, Descr)
-        return llimpl.do_getarrayitem_raw_int(array, index)
+        return llimpl.do_getarrayitem_raw_int(array, index, arraydescr.ofs)
     def bh_getarrayitem_gc_r(self, arraydescr, array, index):
         assert isinstance(arraydescr, Descr)
         return llimpl.do_getarrayitem_gc_ptr(array, index)
@@ -487,6 +470,19 @@
         return llimpl.do_setinteriorfield_gc_float(array, index, descr.ofs,
                                                    value)
 
+    def bh_raw_store_i(self, struct, offset, descr, newvalue):
+        assert isinstance(descr, Descr)
+        return llimpl.do_raw_store_int(struct, offset, descr.ofs, newvalue)
+    def bh_raw_store_f(self, struct, offset, descr, newvalue):
+        assert isinstance(descr, Descr)
+        return llimpl.do_raw_store_float(struct, offset, newvalue)
+    def bh_raw_load_i(self, struct, offset, descr):
+        assert isinstance(descr, Descr)
+        return llimpl.do_raw_load_int(struct, offset, descr.ofs)
+    def bh_raw_load_f(self, struct, offset, descr):
+        assert isinstance(descr, Descr)
+        return llimpl.do_raw_load_float(struct, offset)
+
     def bh_new(self, sizedescr):
         assert isinstance(sizedescr, Descr)
         return llimpl.do_new(sizedescr.ofs)
@@ -516,7 +512,7 @@
 
     def bh_setarrayitem_raw_i(self, arraydescr, array, index, newvalue):
         assert isinstance(arraydescr, Descr)
-        llimpl.do_setarrayitem_raw_int(array, index, newvalue)
+        llimpl.do_setarrayitem_raw_int(array, index, newvalue, arraydescr.ofs)
 
     def bh_setarrayitem_gc_r(self, arraydescr, array, index, newvalue):
         assert isinstance(arraydescr, Descr)
diff --git a/pypy/jit/backend/llgraph/symbolic.py b/pypy/jit/backend/llgraph/symbolic.py
--- a/pypy/jit/backend/llgraph/symbolic.py
+++ b/pypy/jit/backend/llgraph/symbolic.py
@@ -1,8 +1,7 @@
-import ctypes
 from pypy.rpython.lltypesystem import lltype, rffi, rclass
 
 
-Size2Type = [None]
+Size2Type = [None] * 100
 Type2Size = {}
 
 def get_size(TYPE):
@@ -14,7 +13,7 @@
         Type2Size[TYPE] = size
         return size
 
-TokenToField = [None]
+TokenToField = [None] * 100
 FieldToToken = {}
 
 def get_field_token(STRUCT, fieldname):
@@ -26,21 +25,3 @@
         FieldToToken[STRUCT, fieldname] = token
         return token
 get_field_token(rclass.OBJECT, 'typeptr')     # force the index 1 for this
-
-def get_array_token(T):
-    # T can be an array or a var-sized structure
-    if isinstance(T, lltype.Struct):
-        assert T._arrayfld is not None, "%r is not variable-sized" % (T,)
-        cstruct = ll2ctypes.get_ctypes_type(T)
-        cfield = getattr(cstruct, T._arrayfld)
-        before_array_part = cfield.offset
-        T = getattr(T, T._arrayfld)
-    else:
-        before_array_part = 0
-    carray = ll2ctypes.get_ctypes_type(T)
-    assert carray.length.size == 4
-    ofs_length = before_array_part + carray.length.offset
-    basesize = before_array_part + carray.items.offset
-    carrayitem = ll2ctypes.get_ctypes_type(T.OF)
-    itemsize = ctypes.sizeof(carrayitem)
-    return basesize, itemsize, ofs_length
diff --git a/pypy/jit/backend/llsupport/descr.py b/pypy/jit/backend/llsupport/descr.py
--- a/pypy/jit/backend/llsupport/descr.py
+++ b/pypy/jit/backend/llsupport/descr.py
@@ -237,29 +237,6 @@
         cache[(ARRAY, name)] = descr
         return descr
 
-def compute_flag(is_pointer, is_float, is_signed):
-    if is_pointer:
-        assert not is_float
-        return FLAG_POINTER
-    elif is_float:
-        return FLAG_FLOAT
-    elif is_signed:
-        return FLAG_SIGNED
-    else:
-        return FLAG_UNSIGNED
-
-def get_dynamic_field_descr(offset, fieldsize, is_pointer, is_float, is_signed):
-    flag = compute_flag(is_pointer, is_float, is_signed)
-    return FieldDescr('dynamic', offset, fieldsize, flag)
-
-def get_dynamic_interiorfield_descr(gc_ll_descr, offset, width, fieldsize,
-                                    is_pointer, is_float, is_signed):
-    arraydescr = ArrayDescr(0, width, None, FLAG_STRUCT)
-    flag = compute_flag(is_pointer, is_float, is_signed)
-    fielddescr = FieldDescr('dynamic', offset, fieldsize, flag)
-    return InteriorFieldDescr(arraydescr, fielddescr)
-
-
 # ____________________________________________________________
 # CallDescrs
 
diff --git a/pypy/jit/backend/llsupport/ffisupport.py b/pypy/jit/backend/llsupport/ffisupport.py
--- a/pypy/jit/backend/llsupport/ffisupport.py
+++ b/pypy/jit/backend/llsupport/ffisupport.py
@@ -1,43 +1,97 @@
 from pypy.rlib.rarithmetic import intmask
-from pypy.jit.metainterp import history
-from pypy.rpython.lltypesystem import rffi
+from pypy.rlib.objectmodel import specialize
+from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.jit.backend.llsupport.descr import CallDescr
 
 class UnsupportedKind(Exception):
     pass
 
-def get_call_descr_dynamic(cpu, ffi_args, ffi_result, extrainfo, ffi_flags):
-    """Get a call descr: the types of result and args are represented by
-    rlib.libffi.types.*"""
+def get_call_descr_dynamic(cpu, cif_description, extrainfo):
+    """Get a call descr from the given CIF_DESCRIPTION"""
+    ffi_result = cif_description.rtype
     try:
         reskind = get_ffi_type_kind(cpu, ffi_result)
-        argkinds = [get_ffi_type_kind(cpu, arg) for arg in ffi_args]
+        argkinds = [get_ffi_type_kind(cpu, cif_description.atypes[i])
+                    for i in range(cif_description.nargs)]
     except UnsupportedKind:
         return None
-    if reskind == history.VOID:
+    if reskind == 'v':
         result_size = 0
     else:
         result_size = intmask(ffi_result.c_size)
     argkinds = ''.join(argkinds)
     return CallDescr(argkinds, reskind, is_ffi_type_signed(ffi_result),
-                     result_size, extrainfo, ffi_flags=ffi_flags)
+                     result_size, extrainfo, ffi_flags=cif_description.abi)
 
 def get_ffi_type_kind(cpu, ffi_type):
-    from pypy.rlib.libffi import types
+    from pypy.rlib.jit_libffi import types
+    kind = types.getkind(ffi_type)
+    if ((not cpu.supports_floats and kind == 'f') or
+        (not cpu.supports_longlong and kind == 'L') or
+        (not cpu.supports_singlefloats and kind == 'S') or
+        kind == '*' or kind == '?'):
+        raise UnsupportedKind("Unsupported kind '%s'" % kind)
+    if kind == 'u':
+        kind = 'i'
+    return kind
+
+def is_ffi_type_signed(ffi_type):
+    from pypy.rlib.jit_libffi import types
+    kind = types.getkind(ffi_type)
+    return kind != 'u'
+
+ at specialize.memo()
+def _get_ffi2descr_dict(cpu):
+    d = {('v', 0): ('v', None)}
+    if cpu.supports_floats:
+        d[('f', 0)] = ('f', cpu.arraydescrof(rffi.CArray(lltype.Float)))
+    if cpu.supports_singlefloats:
+        d[('S', 0)] = ('i', cpu.arraydescrof(rffi.CArray(lltype.SingleFloat)))
+    for SIGNED_TYPE in [rffi.SIGNEDCHAR,
+                        rffi.SHORT,
+                        rffi.INT,
+                        rffi.LONG,
+                        rffi.LONGLONG]:
+        key = ('i', rffi.sizeof(SIGNED_TYPE))
+        kind = 'i'
+        if key[1] > rffi.sizeof(lltype.Signed):
+            if not cpu.supports_longlong:
+                continue
+            key = ('L', 0)
+            kind = 'f'
+        d[key] = (kind, cpu.arraydescrof(rffi.CArray(SIGNED_TYPE)))
+    for UNSIGNED_TYPE in [rffi.UCHAR,
+                          rffi.USHORT,
+                          rffi.UINT,
+                          rffi.ULONG,
+                          rffi.ULONGLONG]:
+        key = ('u', rffi.sizeof(UNSIGNED_TYPE))
+        if key[1] > rffi.sizeof(lltype.Signed):
+            continue
+        d[key] = ('i', cpu.arraydescrof(rffi.CArray(UNSIGNED_TYPE)))
+    return d
+
+def get_arg_descr(cpu, ffi_type):
+    from pypy.rlib.jit_libffi import types
     kind = types.getkind(ffi_type)
     if kind == 'i' or kind == 'u':
-        return history.INT
-    elif cpu.supports_floats and kind == 'f':
-        return history.FLOAT
-    elif kind == 'v':
-        return history.VOID
-    elif cpu.supports_longlong and (kind == 'I' or kind == 'U'):     # longlong
-        return 'L'
-    elif cpu.supports_singlefloats and kind == 's':    # singlefloat
-        return 'S'
-    raise UnsupportedKind("Unsupported kind '%s'" % kind)
+        size = rffi.getintfield(ffi_type, 'c_size')
+    else:
+        size = 0
+    return _get_ffi2descr_dict(cpu)[kind, size]
 
-def is_ffi_type_signed(ffi_type):
-    from pypy.rlib.libffi import types
-    kind = types.getkind(ffi_type)
-    return kind != 'u'
+def calldescr_dynamic_for_tests(cpu, atypes, rtype, abiname='FFI_DEFAULT_ABI'):
+    from pypy.rlib import clibffi
+    from pypy.rlib.jit_libffi import CIF_DESCRIPTION, FFI_TYPE_PP
+    from pypy.jit.codewriter.effectinfo import EffectInfo
+    #
+    p = lltype.malloc(CIF_DESCRIPTION, len(atypes),
+                      flavor='raw', immortal=True)
+    p.abi = getattr(clibffi, abiname)
+    p.nargs = len(atypes)
+    p.rtype = rtype
+    p.atypes = lltype.malloc(FFI_TYPE_PP.TO, len(atypes),
+                             flavor='raw', immortal=True)
+    for i in range(len(atypes)):
+        p.atypes[i] = atypes[i]
+    return cpu.calldescrof_dynamic(p, EffectInfo.MOST_GENERAL)
diff --git a/pypy/jit/backend/llsupport/llmodel.py b/pypy/jit/backend/llsupport/llmodel.py
--- a/pypy/jit/backend/llsupport/llmodel.py
+++ b/pypy/jit/backend/llsupport/llmodel.py
@@ -10,8 +10,8 @@
 from pypy.jit.backend.llsupport.symbolic import WORD, unroll_basic_sizes
 from pypy.jit.backend.llsupport.descr import (
     get_size_descr, get_field_descr, get_array_descr,
-    get_call_descr, get_interiorfield_descr, get_dynamic_interiorfield_descr,
-    FieldDescr, ArrayDescr, CallDescr, InteriorFieldDescr, get_dynamic_field_descr)
+    get_call_descr, get_interiorfield_descr,
+    FieldDescr, ArrayDescr, CallDescr, InteriorFieldDescr)
 from pypy.jit.backend.llsupport.asmmemmgr import AsmMemoryManager
 
 
@@ -245,9 +245,6 @@
     def fielddescrof(self, STRUCT, fieldname):
         return get_field_descr(self.gc_ll_descr, STRUCT, fieldname)
 
-    def fielddescrof_dynamic(self, offset, fieldsize, is_pointer, is_float, is_signed):
-        return get_dynamic_field_descr(offset, fieldsize, is_pointer, is_float, is_signed)
-
     def unpack_fielddescr(self, fielddescr):
         assert isinstance(fielddescr, FieldDescr)
         return fielddescr.offset
@@ -267,12 +264,6 @@
     def interiorfielddescrof(self, A, fieldname):
         return get_interiorfield_descr(self.gc_ll_descr, A, fieldname)
 
-    def interiorfielddescrof_dynamic(self, offset, width, fieldsize,
-                                     is_pointer, is_float, is_signed):
-        return get_dynamic_interiorfield_descr(self.gc_ll_descr,
-                                               offset, width, fieldsize,
-                                               is_pointer, is_float, is_signed)
-
     def unpack_arraydescr(self, arraydescr):
         assert isinstance(arraydescr, ArrayDescr)
         return arraydescr.basesize
@@ -289,10 +280,16 @@
     def calldescrof(self, FUNC, ARGS, RESULT, extrainfo):
         return get_call_descr(self.gc_ll_descr, ARGS, RESULT, extrainfo)
 
-    def calldescrof_dynamic(self, ffi_args, ffi_result, extrainfo, ffi_flags):
+    def calldescrof_dynamic(self, cif_description, extrainfo):
         from pypy.jit.backend.llsupport import ffisupport
-        return ffisupport.get_call_descr_dynamic(self, ffi_args, ffi_result,
-                                                 extrainfo, ffi_flags)
+        return ffisupport.get_call_descr_dynamic(self, cif_description,
+                                                 extrainfo)
+
+    def _calldescr_dynamic_for_tests(self, atypes, rtype,
+                                     abiname='FFI_DEFAULT_ABI'):
+        from pypy.jit.backend.llsupport import ffisupport
+        return ffisupport.calldescr_dynamic_for_tests(self, atypes, rtype,
+                                                      abiname)
 
     def get_overflow_error(self):
         ovf_vtable = self.cast_adr_to_int(self._ovf_error_vtable)
@@ -589,6 +586,32 @@
     bh_setfield_raw_r = _base_do_setfield_r
     bh_setfield_raw_f = _base_do_setfield_f
 
+    def bh_raw_store_i(self, addr, offset, descr, newvalue):
+        ofs, size, sign = self.unpack_arraydescr_size(descr)
+        items = addr + offset
+        for TYPE, _, itemsize in unroll_basic_sizes:
+            if size == itemsize:
+                items = rffi.cast(rffi.CArrayPtr(TYPE), items)
+                items[0] = rffi.cast(TYPE, newvalue)
+                break
+
+    def bh_raw_store_f(self, addr, offset, descr, newvalue):
+        items = rffi.cast(rffi.CArrayPtr(longlong.FLOATSTORAGE), addr + offset)
+        items[0] = newvalue
+
+    def bh_raw_load_i(self, addr, offset, descr):
+        ofs, size, sign = self.unpack_arraydescr_size(descr)
+        items = addr + offset
+        for TYPE, _, itemsize in unroll_basic_sizes:
+            if size == itemsize:
+                items = rffi.cast(rffi.CArrayPtr(TYPE), items)
+                return rffi.cast(lltype.Signed, items[0])
+        assert False # unreachable code
+
+    def bh_raw_load_f(self, addr, offset, descr):
+        items = rffi.cast(rffi.CArrayPtr(longlong.FLOATSTORAGE), addr + offset)
+        return items[0]
+
     def bh_new(self, sizedescr):
         return self.gc_ll_descr.gc_malloc(sizedescr)
 
diff --git a/pypy/jit/backend/llsupport/test/test_ffisupport.py b/pypy/jit/backend/llsupport/test/test_ffisupport.py
--- a/pypy/jit/backend/llsupport/test/test_ffisupport.py
+++ b/pypy/jit/backend/llsupport/test/test_ffisupport.py
@@ -1,4 +1,6 @@
-from pypy.rlib.libffi import types
+from pypy.rlib.jit_libffi import types, CIF_DESCRIPTION, FFI_TYPE_PP
+from pypy.rlib.clibffi import FFI_DEFAULT_ABI
+from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.jit.codewriter.longlong import is_64_bit
 from pypy.jit.backend.llsupport.descr import *
 from pypy.jit.backend.llsupport.ffisupport import *
@@ -11,56 +13,55 @@
         self.supports_floats = supports_floats
         self.supports_longlong = supports_longlong
         self.supports_singlefloats = supports_singlefloats
-
+    def calldescrof_dynamic(self, cif_descr, effectinfo):
+        return get_call_descr_dynamic(self, cif_descr, effectinfo)
 
 def test_call_descr_dynamic():
     args = [types.sint, types.pointer]
-    descr = get_call_descr_dynamic(FakeCPU(), args, types.sint, None,
-                                   ffi_flags=42)
+    descr = calldescr_dynamic_for_tests(FakeCPU(), args, types.sint)
     assert isinstance(descr, CallDescr)
     assert descr.result_type == 'i'
     assert descr.result_flag == FLAG_SIGNED
     assert descr.arg_classes == 'ii'
-    assert descr.get_ffi_flags() == 42
+    assert descr.get_ffi_flags() == FFI_DEFAULT_ABI
 
     args = [types.sint, types.double, types.pointer]
-    descr = get_call_descr_dynamic(FakeCPU(), args, types.void, None, 42)
+    descr = calldescr_dynamic_for_tests(FakeCPU(), args, types.void)
     assert descr is None    # missing floats
-    descr = get_call_descr_dynamic(FakeCPU(supports_floats=True),
-                                   args, types.void, None, ffi_flags=43)
+    descr = calldescr_dynamic_for_tests(FakeCPU(supports_floats=True),
+                                        args, types.void)
     assert descr.result_type == 'v'
     assert descr.result_flag == FLAG_VOID
     assert descr.arg_classes == 'ifi'
-    assert descr.get_ffi_flags() == 43
+    assert descr.get_ffi_flags() == FFI_DEFAULT_ABI
 
-    descr = get_call_descr_dynamic(FakeCPU(), [], types.sint8, None, 42)
+    descr = calldescr_dynamic_for_tests(FakeCPU(), [], types.sint8)
     assert descr.get_result_size() == 1
     assert descr.result_flag == FLAG_SIGNED
     assert descr.is_result_signed() == True
 
-    descr = get_call_descr_dynamic(FakeCPU(), [], types.uint8, None, 42)
+    descr = calldescr_dynamic_for_tests(FakeCPU(), [], types.uint8)
     assert isinstance(descr, CallDescr)
     assert descr.get_result_size() == 1
     assert descr.result_flag == FLAG_UNSIGNED
     assert descr.is_result_signed() == False
 
     if not is_64_bit or is_emulated_long:
-        descr = get_call_descr_dynamic(FakeCPU(), [], types.slonglong,
-                                       None, 42)
+        descr = calldescr_dynamic_for_tests(FakeCPU(), [], types.slonglong)
         assert descr is None   # missing longlongs
-        descr = get_call_descr_dynamic(FakeCPU(supports_longlong=True),
-                                       [], types.slonglong, None, ffi_flags=43)
+        descr = calldescr_dynamic_for_tests(FakeCPU(supports_longlong=True),
+                                            [], types.slonglong)
         assert isinstance(descr, CallDescr)
         assert descr.result_flag == FLAG_FLOAT
         assert descr.result_type == 'L'
-        assert descr.get_ffi_flags() == 43
+        assert descr.get_ffi_flags() == FFI_DEFAULT_ABI
     else:
         assert types.slonglong is types.slong
 
-    descr = get_call_descr_dynamic(FakeCPU(), [], types.float, None, 42)
+    descr = calldescr_dynamic_for_tests(FakeCPU(), [], types.float)
     assert descr is None   # missing singlefloats
-    descr = get_call_descr_dynamic(FakeCPU(supports_singlefloats=True),
-                                   [], types.float, None, ffi_flags=44)
+    descr = calldescr_dynamic_for_tests(FakeCPU(supports_singlefloats=True),
+                                        [], types.float)
     assert descr.result_flag == FLAG_UNSIGNED
     assert descr.result_type == 'S'
-    assert descr.get_ffi_flags() == 44
+    assert descr.get_ffi_flags() == FFI_DEFAULT_ABI
diff --git a/pypy/jit/backend/model.py b/pypy/jit/backend/model.py
--- a/pypy/jit/backend/model.py
+++ b/pypy/jit/backend/model.py
@@ -208,10 +208,6 @@
     def interiorfielddescrof(self, A, fieldname):
         raise NotImplementedError
 
-    def interiorfielddescrof_dynamic(self, offset, width, fieldsize, is_pointer,
-        is_float, is_signed):
-        raise NotImplementedError
-
     def arraydescrof(self, A):
         raise NotImplementedError
 
diff --git a/pypy/jit/backend/test/calling_convention_test.py b/pypy/jit/backend/test/calling_convention_test.py
--- a/pypy/jit/backend/test/calling_convention_test.py
+++ b/pypy/jit/backend/test/calling_convention_test.py
@@ -59,7 +59,6 @@
         return ConstInt(heaptracker.adr2int(addr))
 
     def test_call_aligned_with_spilled_values(self):
-        from pypy.rlib.libffi import types
         cpu = self.cpu
         if not cpu.supports_floats:
             py.test.skip('requires floats')
@@ -118,7 +117,6 @@
             assert abs(x - expected_result) < 0.0001
 
     def test_call_aligned_with_imm_values(self):
-        from pypy.rlib.libffi import types
         cpu = self.cpu
         if not cpu.supports_floats:
             py.test.skip('requires floats')
@@ -161,7 +159,6 @@
             assert abs(res.getfloat() - result) < 0.0001
 
     def test_call_aligned_with_args_on_the_stack(self):
-        from pypy.rlib.libffi import types
         cpu = self.cpu
         if not cpu.supports_floats:
             py.test.skip('requires floats')
@@ -204,7 +201,6 @@
             assert abs(res.getfloat() - result) < 0.0001
 
     def test_call_alignment_call_assembler(self):
-        from pypy.rlib.libffi import types
         cpu = self.cpu
         if not cpu.supports_floats:
             py.test.skip('requires floats')
@@ -303,7 +299,6 @@
             py.test.skip('requires floats and singlefloats')
 
         import random
-        from pypy.rlib.libffi import types
         from pypy.rlib.rarithmetic import r_singlefloat
 
         def func(*args):
@@ -315,9 +310,9 @@
         F = lltype.Float
         S = lltype.SingleFloat
         I = lltype.Signed
-        floats = [random.random() - 0.5 for i in range(8)]
-        singlefloats = [r_singlefloat(random.random() - 0.5) for i in range(8)]
-        ints = [random.randrange(-99, 99) for i in range(8)]
+        floats = [random.random() - 0.5 for i in range(20)]
+        singlefloats = [r_singlefloat(random.random() - 0.5) for i in range(20)]
+        ints = [random.randrange(-99, 99) for i in range(20)]
         for repeat in range(100):
             args = []
             argvalues = []
@@ -325,20 +320,23 @@
             local_floats = list(floats)
             local_singlefloats = list(singlefloats)
             local_ints = list(ints)
-            for i in range(8):
-                case = random.randrange(0, 3)
-                if case == 0:
+            for i in range(random.randrange(4, 20)):
+                case = random.randrange(0, 6)
+                if case & 1: boxme = BoxInt
+                else:        boxme = ConstInt
+                if case < 2:
                     args.append(F)
-                    arg = local_floats.pop()
-                    argslist.append(boxfloat(arg))
-                elif case == 1:
+                    arg = arg1 = local_floats.pop()
+                    if case & 1: boxme = boxfloat
+                    else:        boxme = constfloat
+                elif case < 4:
                     args.append(S)
                     arg = local_singlefloats.pop()
-                    argslist.append(BoxInt(longlong.singlefloat2int(arg)))
+                    arg1 = longlong.singlefloat2int(arg)
                 else:
                     args.append(I)
-                    arg = local_ints.pop()
-                    argslist.append(BoxInt(arg))
+                    arg = arg1 = local_ints.pop()
+                argslist.append(boxme(arg1))
                 argvalues.append(arg)
             FUNC = self.FuncType(args, F)
             FPTR = self.Ptr(FUNC)
diff --git a/pypy/jit/backend/test/runner_test.py b/pypy/jit/backend/test/runner_test.py
--- a/pypy/jit/backend/test/runner_test.py
+++ b/pypy/jit/backend/test/runner_test.py
@@ -515,7 +515,7 @@
             assert longlong.getrealfloat(x) == 3.5 - 42
 
     def test_call(self):
-        from pypy.rlib.libffi import types, FUNCFLAG_CDECL
+        from pypy.rlib.jit_libffi import types
 
         def func_int(a, b):
             return a + b
@@ -543,9 +543,8 @@
                                          'int', descr=calldescr)
             assert res.value == 2 * num
             # then, try it with the dynamic calldescr
-            dyn_calldescr = cpu.calldescrof_dynamic([ffi_type, ffi_type], ffi_type,
-                                                    EffectInfo.MOST_GENERAL,
-                                                    ffi_flags=FUNCFLAG_CDECL)
+            dyn_calldescr = cpu._calldescr_dynamic_for_tests(
+                [ffi_type, ffi_type], ffi_type)
             res = self.execute_operation(rop.CALL,
                                          [funcbox, BoxInt(num), BoxInt(num)],
                                          'int', descr=dyn_calldescr)
@@ -1733,39 +1732,6 @@
         assert s.x == chr(190)
         assert s.y == chr(150)
 
-    def test_fielddescrof_dynamic(self):
-        S = lltype.Struct('S',
-                          ('x', lltype.Signed),
-                          ('y', lltype.Signed),
-                          )
-        longsize = rffi.sizeof(lltype.Signed)
-        y_ofs = longsize
-        s = lltype.malloc(S, flavor='raw')
-        sa = llmemory.cast_ptr_to_adr(s)
-        s_box = BoxInt(heaptracker.adr2int(sa))
-        #
-        field = self.cpu.fielddescrof(S, 'y')
-        field_dyn = self.cpu.fielddescrof_dynamic(offset=y_ofs,
-                                                  fieldsize=longsize,
-                                                  is_pointer=False,
-                                                  is_float=False,
-                                                  is_signed=True)
-        assert field.is_pointer_field() == field_dyn.is_pointer_field()
-        assert field.is_float_field()   == field_dyn.is_float_field()
-        if 'llgraph' not in str(self.cpu):
-            assert field.is_field_signed()  == field_dyn.is_field_signed()
-
-        #
-        for get_op, set_op in ((rop.GETFIELD_RAW, rop.SETFIELD_RAW),
-                               (rop.GETFIELD_RAW_PURE, rop.SETFIELD_RAW)):
-            for descr in (field, field_dyn):
-                self.execute_operation(set_op, [s_box, BoxInt(32)], 'void',
-                                       descr=descr)
-                res = self.execute_operation(get_op, [s_box], 'int', descr=descr)
-                assert res.getint()  == 32
-
-        lltype.free(s, flavor='raw')
-
     def test_new_with_vtable(self):
         cpu = self.cpu
         t_box, T_box = self.alloc_instance(self.T)
@@ -2200,9 +2166,7 @@
         cpu = self.cpu
         func_adr = llmemory.cast_ptr_to_adr(c_tolower.funcsym)
         funcbox = ConstInt(heaptracker.adr2int(func_adr))
-        calldescr = cpu.calldescrof_dynamic([types.uchar], types.sint,
-                                            EffectInfo.MOST_GENERAL,
-                                            ffi_flags=FUNCFLAG_CDECL)
+        calldescr = cpu._calldescr_dynamic_for_tests([types.uchar], types.sint)
         i1 = BoxInt()
         i2 = BoxInt()
         tok = BoxInt()
@@ -2255,11 +2219,9 @@
         cpu = self.cpu
         func_adr = llmemory.cast_ptr_to_adr(c_qsort.funcsym)
         funcbox = ConstInt(heaptracker.adr2int(func_adr))
-        calldescr = cpu.calldescrof_dynamic([types.pointer, types_size_t,
-                                             types_size_t, types.pointer],
-                                            types.void,
-                                            EffectInfo.MOST_GENERAL,
-                                            ffi_flags=clibffi.FUNCFLAG_CDECL)
+        calldescr = cpu._calldescr_dynamic_for_tests(
+            [types.pointer, types_size_t, types_size_t, types.pointer],
+            types.void)
         i0 = BoxInt()
         i1 = BoxInt()
         i2 = BoxInt()
@@ -2308,10 +2270,10 @@
         cpu = self.cpu
         func_adr = llmemory.cast_ptr_to_adr(c_GetCurrentDir.funcsym)
         funcbox = ConstInt(heaptracker.adr2int(func_adr))
-        calldescr = cpu.calldescrof_dynamic([types.ulong, types.pointer],
-                                            types.ulong,
-                                            EffectInfo.MOST_GENERAL,
-                                            ffi_flags=FUNCFLAG_STDCALL)
+        calldescr = cpu._calldescr_dynamic_for_tests(
+            [types.ulong, types.pointer],
+            types.ulong,
+            abiname='FFI_STDCALL')
         i1 = BoxInt()
         i2 = BoxInt()
         faildescr = BasicFailDescr(1)
@@ -2565,13 +2527,14 @@
         assert str.chars[4] == '/'
 
     def test_sorting_of_fields(self):
-        S = self.S
+        S = lltype.GcStruct('S', ('parent', rclass.OBJECT),
+                                  ('value', lltype.Signed),
+                                  ('chr1', lltype.Char),
+                                  ('chr2', lltype.Char))
+        chr1 = self.cpu.fielddescrof(S, 'chr1').sort_key()
         value = self.cpu.fielddescrof(S, 'value').sort_key()
-        chr1 = self.cpu.fielddescrof(S, 'chr1').sort_key()
         chr2 = self.cpu.fielddescrof(S, 'chr2').sort_key()
-        assert (sorted([chr2, chr1, value]) ==
-                [value, chr1, chr2])
-        assert len(dict.fromkeys([value, chr1, chr2]).keys()) == 3
+        assert len(set([value, chr1, chr2])) == 3
 
     def test_guards_nongc(self):
         x = lltype.malloc(lltype.Struct('x'), flavor='raw')
@@ -3206,6 +3169,20 @@
         res = self.cpu.get_latest_value_int(0)
         assert res == -10
 
+    def test_int_force_ge_zero(self):
+        ops = """
+        [i0]
+        i1 = int_force_ge_zero(i0)    # but forced to be in a register
+        finish(i1, descr=1)
+        """
+        loop = parse(ops, self.cpu, namespace=locals())
+        descr = loop.operations[-1].getdescr()
+        looptoken = JitCellToken()
+        self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+        for inp, outp in [(2,2), (-3, 0)]:
+            self.cpu.execute_token(looptoken, inp)
+            assert outp == self.cpu.get_latest_value_int(0)
+
     def test_compile_asmlen(self):
         from pypy.jit.backend.llsupport.llmodel import AbstractLLCPU
         if not isinstance(self.cpu, AbstractLLCPU):
@@ -3340,6 +3317,107 @@
         fail = self.cpu.execute_token(looptoken2, -9)
         assert fail.identifier == 42
 
+    def test_raw_load_int(self):
+        from pypy.rlib import rawstorage
+        for T in [rffi.UCHAR, rffi.SIGNEDCHAR,
+                  rffi.USHORT, rffi.SHORT,
+                  rffi.UINT, rffi.INT,
+                  rffi.ULONG, rffi.LONG]:
+            ops = """
+            [i0, i1]
+            i2 = raw_load(i0, i1, descr=arraydescr)
+            finish(i2)
+            """
+            arraydescr = self.cpu.arraydescrof(rffi.CArray(T))
+            p = rawstorage.alloc_raw_storage(31)
+            for i in range(31):
+                p[i] = '\xDD'
+            value = rffi.cast(T, 0x4243444546474849)
+            rawstorage.raw_storage_setitem(p, 16, value)
+            loop = parse(ops, self.cpu, namespace=locals())
+            looptoken = JitCellToken()
+            self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+            self.cpu.execute_token(looptoken,
+                                   rffi.cast(lltype.Signed, p), 16)
+            result = self.cpu.get_latest_value_int(0)
+            assert result == rffi.cast(lltype.Signed, value)
+            rawstorage.free_raw_storage(p)
+
+    def test_raw_load_float(self):
+        if not self.cpu.supports_floats:
+            py.test.skip("requires floats")
+        from pypy.rlib import rawstorage
+        for T in [rffi.DOUBLE]:
+            ops = """
+            [i0, i1]
+            f2 = raw_load(i0, i1, descr=arraydescr)
+            finish(f2)
+            """
+            arraydescr = self.cpu.arraydescrof(rffi.CArray(T))
+            p = rawstorage.alloc_raw_storage(31)
+            for i in range(31):
+                p[i] = '\xDD'
+            value = rffi.cast(T, 1.12e20)
+            rawstorage.raw_storage_setitem(p, 16, value)
+            loop = parse(ops, self.cpu, namespace=locals())
+            looptoken = JitCellToken()
+            self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+            self.cpu.execute_token(looptoken,
+                                   rffi.cast(lltype.Signed, p), 16)
+            result = self.cpu.get_latest_value_float(0)
+            result = longlong.getrealfloat(result)
+            assert result == rffi.cast(lltype.Float, value)
+            rawstorage.free_raw_storage(p)
+
+    def test_raw_store_int(self):
+        from pypy.rlib import rawstorage
+        for T in [rffi.UCHAR, rffi.SIGNEDCHAR,
+                  rffi.USHORT, rffi.SHORT,
+                  rffi.UINT, rffi.INT,
+                  rffi.ULONG, rffi.LONG]:
+            ops = """
+            [i0, i1, i2]
+            raw_store(i0, i1, i2, descr=arraydescr)
+            finish()
+            """
+            arraydescr = self.cpu.arraydescrof(rffi.CArray(T))
+            p = rawstorage.alloc_raw_storage(31)
+            for i in range(31):
+                p[i] = '\xDD'
+            value = 0x4243444546474849 & sys.maxint
+            loop = parse(ops, self.cpu, namespace=locals())
+            looptoken = JitCellToken()
+            self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+            self.cpu.execute_token(looptoken,
+                                   rffi.cast(lltype.Signed, p), 16, value)
+            result = rawstorage.raw_storage_getitem(T, p, 16)
+            assert result == rffi.cast(T, value)
+            rawstorage.free_raw_storage(p)
+
+    def test_raw_store_float(self):
+        if not self.cpu.supports_floats:
+            py.test.skip("requires floats")
+        from pypy.rlib import rawstorage
+        for T in [rffi.DOUBLE]:
+            ops = """
+            [i0, i1, f2]
+            raw_store(i0, i1, f2, descr=arraydescr)
+            finish()
+            """
+            arraydescr = self.cpu.arraydescrof(rffi.CArray(T))
+            p = rawstorage.alloc_raw_storage(31)
+            for i in range(31):
+                p[i] = '\xDD'
+            value = 1.23e20
+            loop = parse(ops, self.cpu, namespace=locals())
+            looptoken = JitCellToken()
+            self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+            self.cpu.execute_token(looptoken,
+                                   rffi.cast(lltype.Signed, p), 16,
+                                   longlong.getfloatstorage(value))
+            result = rawstorage.raw_storage_getitem(T, p, 16)
+            assert result == rffi.cast(T, value)
+            rawstorage.free_raw_storage(p)
 
 class OOtypeBackendTest(BaseBackendTest):
 
diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py
--- a/pypy/jit/backend/x86/assembler.py
+++ b/pypy/jit/backend/x86/assembler.py
@@ -127,9 +127,13 @@
         self._build_stack_check_slowpath()
         if gc_ll_descr.gcrootmap:
             self._build_release_gil(gc_ll_descr.gcrootmap)
-        debug_start('jit-backend-counts')
-        self.set_debug(have_debug_prints())
-        debug_stop('jit-backend-counts')
+        if not self._debug:
+            # if self._debug is already set it means that someone called
+            # set_debug by hand before initializing the assembler. Leave it
+            # as it is
+            debug_start('jit-backend-counts')
+            self.set_debug(have_debug_prints())
+            debug_stop('jit-backend-counts')
 
     def setup(self, looptoken):
         assert self.memcpy_addr != 0, "setup_once() not called?"
@@ -998,6 +1002,24 @@
             getattr(self.mc, asmop)(arglocs[0], arglocs[1])
         return genop_binary
 
+    def _binaryop_or_lea(asmop, is_add):
+        def genop_binary_or_lea(self, op, arglocs, result_loc):
+            # use a regular ADD or SUB if result_loc is arglocs[0],
+            # and a LEA only if different.
+            if result_loc is arglocs[0]:
+                getattr(self.mc, asmop)(arglocs[0], arglocs[1])
+            else:
+                loc = arglocs[0]
+                argloc = arglocs[1]
+                assert isinstance(loc, RegLoc)
+                assert isinstance(argloc, ImmedLoc)
+                assert isinstance(result_loc, RegLoc)
+                delta = argloc.value
+                if not is_add:    # subtraction
+                    delta = -delta
+                self.mc.LEA_rm(result_loc.value, (loc.value, delta))
+        return genop_binary_or_lea
+
     def _cmpop(cond, rev_cond):
         def genop_cmp(self, op, arglocs, result_loc):
             rl = result_loc.lowest8bits()
@@ -1149,11 +1171,13 @@
                     xmm_dst_locs.append(unused_xmm.pop())
                 else:
                     pass_on_stack.append(loc)
-            elif (argtypes is not None and argtypes[i-start] == 'S' and
-                  len(unused_xmm) > 0):
+            elif argtypes is not None and argtypes[i-start] == 'S':
                 # Singlefloat argument
-                if singlefloats is None: singlefloats = []
-                singlefloats.append((loc, unused_xmm.pop()))
+                if len(unused_xmm) > 0:
+                    if singlefloats is None: singlefloats = []
+                    singlefloats.append((loc, unused_xmm.pop()))
+                else:
+                    pass_on_stack.append(loc)
             else:
                 if len(unused_gpr) > 0:
                     src_locs.append(loc)
@@ -1187,6 +1211,9 @@
         # Load the singlefloat arguments from main regs or stack to xmm regs
         if singlefloats is not None:
             for src, dst in singlefloats:
+                if isinstance(src, ImmedLoc):
+                    self.mc.MOV(X86_64_SCRATCH_REG, src)
+                    src = X86_64_SCRATCH_REG
                 self.mc.MOVD(dst, src)
         # Finally remap the arguments in the main regs
         # If x is a register and is in dst_locs, then oups, it needs to
@@ -1224,8 +1251,8 @@
 
     genop_int_neg = _unaryop("NEG")
     genop_int_invert = _unaryop("NOT")
-    genop_int_add = _binaryop("ADD", True)
-    genop_int_sub = _binaryop("SUB")
+    genop_int_add = _binaryop_or_lea("ADD", True)
+    genop_int_sub = _binaryop_or_lea("SUB", False)
     genop_int_mul = _binaryop("IMUL", True)
     genop_int_and = _binaryop("AND", True)
     genop_int_or  = _binaryop("OR", True)
@@ -1375,6 +1402,11 @@
     genop_cast_ptr_to_int = genop_same_as
     genop_cast_int_to_ptr = genop_same_as
 
+    def genop_int_force_ge_zero(self, op, arglocs, resloc):
+        self.mc.TEST(arglocs[0], arglocs[0])
+        self.mov(imm0, resloc)
+        self.mc.CMOVNS(resloc, arglocs[0])
+
     def genop_int_mod(self, op, arglocs, resloc):
         if IS_X86_32:
             self.mc.CDQ()
@@ -1545,6 +1577,13 @@
 
     genop_getarrayitem_gc_pure = genop_getarrayitem_gc
     genop_getarrayitem_raw = genop_getarrayitem_gc
+    genop_getarrayitem_raw_pure = genop_getarrayitem_gc
+
+    def genop_raw_load(self, op, arglocs, resloc):
+        base_loc, ofs_loc, size_loc, ofs, sign_loc = arglocs
+        assert isinstance(ofs, ImmedLoc)
+        src_addr = addr_add(base_loc, ofs_loc, ofs.value, 0)
+        self.load_from_mem(resloc, src_addr, size_loc, sign_loc)
 
     def _get_interiorfield_addr(self, temp_loc, index_loc, itemsize_loc,
                                 base_loc, ofs_loc):
@@ -1571,9 +1610,6 @@
                                                 ofs_loc)
         self.load_from_mem(resloc, src_addr, fieldsize_loc, sign_loc)
 
-    genop_getinteriorfield_raw = genop_getinteriorfield_gc
-
-
     def genop_discard_setfield_gc(self, op, arglocs):
         base_loc, ofs_loc, size_loc, value_loc = arglocs
         assert isinstance(size_loc, ImmedLoc)
@@ -1598,6 +1634,12 @@
         dest_addr = AddressLoc(base_loc, ofs_loc, scale, baseofs.value)
         self.save_into_mem(dest_addr, value_loc, size_loc)
 
+    def genop_discard_raw_store(self, op, arglocs):
+        base_loc, ofs_loc, value_loc, size_loc, baseofs = arglocs
+        assert isinstance(baseofs, ImmedLoc)
+        dest_addr = AddressLoc(base_loc, ofs_loc, 0, baseofs.value)
+        self.save_into_mem(dest_addr, value_loc, size_loc)
+
     def genop_discard_strsetitem(self, op, arglocs):
         base_loc, ofs_loc, val_loc = arglocs
         basesize, itemsize, ofs_length = symbolic.get_array_token(rstr.STR,
@@ -1706,15 +1748,15 @@
                             guard_op.getopname())
 
     def genop_guard_int_add_ovf(self, op, guard_op, guard_token, arglocs, result_loc):
-        self.genop_int_add(op, arglocs, result_loc)
+        self.mc.ADD(arglocs[0], arglocs[1])
         return self._gen_guard_overflow(guard_op, guard_token)
 
     def genop_guard_int_sub_ovf(self, op, guard_op, guard_token, arglocs, result_loc):
-        self.genop_int_sub(op, arglocs, result_loc)
+        self.mc.SUB(arglocs[0], arglocs[1])
         return self._gen_guard_overflow(guard_op, guard_token)
 
     def genop_guard_int_mul_ovf(self, op, guard_op, guard_token, arglocs, result_loc):
-        self.genop_int_mul(op, arglocs, result_loc)
+        self.mc.IMUL(arglocs[0], arglocs[1])
         return self._gen_guard_overflow(guard_op, guard_token)
 
     def genop_guard_guard_false(self, ign_1, guard_op, guard_token, locs, ign_2):
@@ -2630,13 +2672,13 @@
     return AddressLoc(reg_or_imm1, reg_or_imm2, scale, offset)
 
 def addr_add_const(reg_or_imm1, offset):
-    return AddressLoc(reg_or_imm1, ImmedLoc(0), 0, offset)
+    return AddressLoc(reg_or_imm1, imm0, 0, offset)
 
 def mem(loc, offset):
-    return AddressLoc(loc, ImmedLoc(0), 0, offset)
+    return AddressLoc(loc, imm0, 0, offset)
 
 def heap(addr):
-    return AddressLoc(ImmedLoc(addr), ImmedLoc(0), 0, 0)
+    return AddressLoc(ImmedLoc(addr), imm0, 0, 0)
 
 def not_implemented(msg):
     os.write(2, '[x86/asm] %s\n' % msg)
diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py
--- a/pypy/jit/backend/x86/regalloc.py
+++ b/pypy/jit/backend/x86/regalloc.py
@@ -23,6 +23,7 @@
      TempBox
 from pypy.jit.backend.x86.arch import WORD, FRAME_FIXED_SIZE
 from pypy.jit.backend.x86.arch import IS_X86_32, IS_X86_64, MY_COPY_OF_REGS
+from pypy.jit.backend.x86 import rx86
 from pypy.rlib.rarithmetic import r_longlong
 
 class X86RegisterManager(RegisterManager):
@@ -610,9 +611,31 @@
         loc, argloc = self._consider_binop_part(op)
         self.Perform(op, [loc, argloc], loc)
 
-    consider_int_add = _consider_binop
+    def _consider_lea(self, op, loc):
+        argloc = self.loc(op.getarg(1))
+        self.rm.possibly_free_var(op.getarg(0))
+        resloc = self.force_allocate_reg(op.result)
+        self.Perform(op, [loc, argloc], resloc)
+
+    def consider_int_add(self, op):
+        loc = self.loc(op.getarg(0))
+        y = op.getarg(1)
+        if (isinstance(loc, RegLoc) and
+            isinstance(y, ConstInt) and rx86.fits_in_32bits(y.value)):
+            self._consider_lea(op, loc)
+        else:
+            self._consider_binop(op)
+
+    def consider_int_sub(self, op):
+        loc = self.loc(op.getarg(0))
+        y = op.getarg(1)
+        if (isinstance(loc, RegLoc) and
+            isinstance(y, ConstInt) and rx86.fits_in_32bits(-y.value)):
+            self._consider_lea(op, loc)
+        else:
+            self._consider_binop(op)
+
     consider_int_mul = _consider_binop
-    consider_int_sub = _consider_binop
     consider_int_and = _consider_binop
     consider_int_or  = _consider_binop
     consider_int_xor = _consider_binop
@@ -1102,6 +1125,7 @@
                                  imm(itemsize), imm(ofs)])
 
     consider_setarrayitem_raw = consider_setarrayitem_gc
+    consider_raw_store = consider_setarrayitem_gc
 
     def consider_getfield_gc(self, op):
         ofs_loc, size_loc, sign = self._unpack_fielddescr(op.getdescr())
@@ -1135,6 +1159,8 @@
 
     consider_getarrayitem_raw = consider_getarrayitem_gc
     consider_getarrayitem_gc_pure = consider_getarrayitem_gc
+    consider_getarrayitem_raw_pure = consider_getarrayitem_gc
+    consider_raw_load = consider_getarrayitem_gc
 
     def consider_getinteriorfield_gc(self, op):
         t = self._unpack_interiorfielddescr(op.getdescr())
@@ -1166,8 +1192,6 @@
         self.Perform(op, [base_loc, ofs, itemsize, fieldsize,
                           index_loc, temp_loc, sign_loc], result_loc)
 
-    consider_getinteriorfield_raw = consider_getinteriorfield_gc
-
     def consider_int_is_true(self, op, guard_op):
         # doesn't need arg to be in a register
         argloc = self.loc(op.getarg(0))
@@ -1188,6 +1212,12 @@
     consider_cast_ptr_to_int = consider_same_as
     consider_cast_int_to_ptr = consider_same_as
 
+    def consider_int_force_ge_zero(self, op):
+        argloc = self.make_sure_var_in_reg(op.getarg(0))
+        resloc = self.force_allocate_reg(op.result, [op.getarg(0)])
+        self.possibly_free_var(op.getarg(0))
+        self.Perform(op, [argloc], resloc)
+
     def consider_strlen(self, op):
         args = op.getarglist()
         base_loc = self.rm.make_sure_var_in_reg(op.getarg(0), args)
diff --git a/pypy/jit/backend/x86/regloc.py b/pypy/jit/backend/x86/regloc.py
--- a/pypy/jit/backend/x86/regloc.py
+++ b/pypy/jit/backend/x86/regloc.py
@@ -548,6 +548,7 @@
     # Avoid XCHG because it always implies atomic semantics, which is
     # slower and does not pair well for dispatch.
     #XCHG = _binaryop('XCHG')
+    CMOVNS = _binaryop('CMOVNS')
 
     PUSH = _unaryop('PUSH')
     POP = _unaryop('POP')
diff --git a/pypy/jit/backend/x86/rx86.py b/pypy/jit/backend/x86/rx86.py
--- a/pypy/jit/backend/x86/rx86.py
+++ b/pypy/jit/backend/x86/rx86.py
@@ -530,6 +530,8 @@
     NOT_r = insn(rex_w, '\xF7', register(1), '\xD0')
     NOT_b = insn(rex_w, '\xF7', orbyte(2<<3), stack_bp(1))
 
+    CMOVNS_rr = insn(rex_w, '\x0F\x49', register(1, 8), register(2), '\xC0')
+
     # ------------------------------ Misc stuff ------------------------------
 
     NOP = insn('\x90')
diff --git a/pypy/jit/backend/x86/test/test_fficall.py b/pypy/jit/backend/x86/test/test_fficall.py
--- a/pypy/jit/backend/x86/test/test_fficall.py
+++ b/pypy/jit/backend/x86/test/test_fficall.py
@@ -2,7 +2,7 @@
 from pypy.jit.metainterp.test import test_fficall
 from pypy.jit.backend.x86.test.test_basic import Jit386Mixin
 
-class TestFfiLookups(Jit386Mixin, test_fficall.FfiLookupTests):
+class TestFfiCall(Jit386Mixin, test_fficall.FfiCallTests):
     # for the individual tests see
     # ====> ../../../metainterp/test/test_fficall.py
-    supports_all = True
+    pass
diff --git a/pypy/jit/backend/x86/test/test_rawmem.py b/pypy/jit/backend/x86/test/test_rawmem.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/backend/x86/test/test_rawmem.py
@@ -0,0 +1,9 @@
+
+from pypy.jit.backend.x86.test.test_basic import Jit386Mixin
+from pypy.jit.metainterp.test.test_rawmem import RawMemTests
+
+
+class TestRawMem(Jit386Mixin, RawMemTests):
+    # for the individual tests see
+    # ====> ../../../metainterp/test/test_rawmem.py
+    pass
diff --git a/pypy/jit/backend/x86/test/test_runner.py b/pypy/jit/backend/x86/test/test_runner.py
--- a/pypy/jit/backend/x86/test/test_runner.py
+++ b/pypy/jit/backend/x86/test/test_runner.py
@@ -458,10 +458,8 @@
                 mc.RET16_i(40)
             rawstart = mc.materialize(cpu.asmmemmgr, [])
             #
-            calldescr = cpu.calldescrof_dynamic([types.slong] * 10,
-                                                types.slong,
-                                                EffectInfo.MOST_GENERAL,
-                                                ffi_flags=-1)
+            calldescr = cpu._calldescr_dynamic_for_tests([types.slong] * 10,
+                                                         types.slong)
             calldescr.get_call_conv = lambda: ffi      # <==== hack
             # ^^^ we patch get_call_conv() so that the test also makes sense
             #     on Linux, because clibffi.get_call_conv() would always
diff --git a/pypy/jit/backend/x86/test/test_ztranslation.py b/pypy/jit/backend/x86/test/test_ztranslation.py
--- a/pypy/jit/backend/x86/test/test_ztranslation.py
+++ b/pypy/jit/backend/x86/test/test_ztranslation.py
@@ -181,12 +181,14 @@
                 i += 1
 
         def main():
+            jit_hooks.stats_set_debug(None, True)
             f()
             ll_times = jit_hooks.stats_get_loop_run_times(None)
             return len(ll_times)
 
         res = self.meta_interp(main, [])
-        assert res == 1
+        assert res == 3
+        # one for loop, one for entry point and one for the prologue
 
 class TestTranslationRemoveTypePtrX86(CCompiledMixin):
     CPUClass = getcpuclass()
diff --git a/pypy/jit/backend/x86/tool/test/test_viewcode.py b/pypy/jit/backend/x86/tool/test/test_viewcode.py
--- a/pypy/jit/backend/x86/tool/test/test_viewcode.py
+++ b/pypy/jit/backend/x86/tool/test/test_viewcode.py
@@ -1,5 +1,10 @@
 from cStringIO import StringIO
 from pypy.jit.backend.x86.tool.viewcode import format_code_dump_with_labels
+from pypy.jit.backend.x86.tool.viewcode import find_objdump
+import os
+import py
+import tempfile
+from pypy.tool.udir import udir
 
 def test_format_code_dump_with_labels():
     lines = StringIO("""
@@ -53,3 +58,16 @@
     lines = format_code_dump_with_labels(0xAA00, lines, label_list=None)
     out = ''.join(lines)
     assert out.strip() == input
+
+def test_find_objdump():
+    old = os.environ['PATH']
+    os.environ['PATH'] = ''
+    py.test.raises(find_objdump)
+
+    #
+    path = udir.join('objdump')
+    print >>path, 'hello world'
+    os.environ['PATH'] = path.dirname
+    assert find_objdump() == 'objdump'
+    #
+    os.environ['PATH'] = old
diff --git a/pypy/jit/backend/x86/tool/viewcode.py b/pypy/jit/backend/x86/tool/viewcode.py
--- a/pypy/jit/backend/x86/tool/viewcode.py
+++ b/pypy/jit/backend/x86/tool/viewcode.py
@@ -8,9 +8,9 @@
     ./viewcode.py log               # also includes a pygame viewer
 """
 
-import autopath
 import new
 import operator
+import os
 import py
 import re
 import sys
@@ -36,6 +36,17 @@
 if sys.platform == "win32":
     pass   # lots more in Psyco
 
+def find_objdump():
+    exe = ('objdump', 'gobjdump')
+    path = os.environ['PATH'].split(os.pathsep)
+    for e in exe:
+        for p in path:
+            path_to = os.path.join(p, e)
+            if not os.path.exists(path_to):
+                continue
+            return e
+    raise AssertionError('(g)objdump was not found in PATH')
+
 def machine_code_dump(data, originaddr, backend_name, label_list=None):
     objdump_backend_option = {
         'x86': 'i386',
@@ -43,7 +54,8 @@
         'x86_64': 'x86-64',
         'i386': 'i386',
     }
-    objdump = ('objdump -M %(backend)s -b binary -m i386 '
+    cmd = find_objdump()
+    objdump = ('%(command)s -M %(backend)s -b binary -m i386 '
                '--disassembler-options=intel-mnemonics '
                '--adjust-vma=%(origin)d -D %(file)s')
     #
@@ -51,6 +63,7 @@
     f.write(data)
     f.close()
     p = subprocess.Popen(objdump % {
+        'command': cmd,
         'file': tmpfile,
         'origin': originaddr,
         'backend': objdump_backend_option[backend_name],
diff --git a/pypy/jit/codewriter/call.py b/pypy/jit/codewriter/call.py
--- a/pypy/jit/codewriter/call.py
+++ b/pypy/jit/codewriter/call.py
@@ -16,6 +16,7 @@
 
 class CallControl(object):
     virtualref_info = None     # optionally set from outside
+    has_libffi_call = False    # default value
 
     def __init__(self, cpu=None, jitdrivers_sd=[]):
         assert isinstance(jitdrivers_sd, list)   # debugging
diff --git a/pypy/jit/codewriter/effectinfo.py b/pypy/jit/codewriter/effectinfo.py
--- a/pypy/jit/codewriter/effectinfo.py
+++ b/pypy/jit/codewriter/effectinfo.py
@@ -45,13 +45,7 @@
     OS_UNIEQ_LENGTHOK           = 51   #
     _OS_offset_uni              = OS_UNI_CONCAT - OS_STR_CONCAT
     #
-    OS_LIBFFI_PREPARE           = 60
-    OS_LIBFFI_PUSH_ARG          = 61
     OS_LIBFFI_CALL              = 62
-    OS_LIBFFI_STRUCT_GETFIELD   = 63
-    OS_LIBFFI_STRUCT_SETFIELD   = 64
-    OS_LIBFFI_GETARRAYITEM      = 65
-    OS_LIBFFI_SETARRAYITEM      = 66
     #
     OS_LLONG_INVERT             = 69
     OS_LLONG_ADD                = 70
@@ -81,9 +75,13 @@
     OS_LLONG_U_TO_FLOAT         = 94
     #
     OS_MATH_SQRT                = 100
+    #
+    OS_RAW_MALLOC_VARSIZE       = 110
+    OS_RAW_FREE                 = 111
 
     # for debugging:
-    _OS_CANRAISE = set([OS_NONE, OS_STR2UNICODE, OS_LIBFFI_CALL])
+    _OS_CANRAISE = set([OS_NONE, OS_STR2UNICODE, OS_LIBFFI_CALL,
+                        OS_RAW_MALLOC_VARSIZE])
 
     def __new__(cls, readonly_descrs_fields, readonly_descrs_arrays,
                 write_descrs_fields, write_descrs_arrays,
diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -11,6 +11,7 @@
 from pypy.objspace.flow.model import SpaceOperation, Variable, Constant, c_last_exception
 from pypy.rlib import objectmodel
 from pypy.rlib.jit import _we_are_jitted
+from pypy.rlib.rgc import lltype_is_gc
 from pypy.rpython.lltypesystem import lltype, llmemory, rstr, rclass, rffi
 from pypy.rpython.rclass import IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY
 from pypy.translator.simplify import get_funcobj
@@ -208,6 +209,10 @@
         if op.args[0] in self.vable_array_vars:
             self.vable_array_vars[op.result]= self.vable_array_vars[op.args[0]]
 
+    def rewrite_op_cast_ptr_to_adr(self, op):
+        if lltype_is_gc(op.args[0].concretetype):
+            raise Exception("cast_ptr_to_adr for GC types unsupported")
+
     def rewrite_op_cast_pointer(self, op):
         newop = self.rewrite_op_same_as(op)
         assert newop is None
@@ -223,6 +228,9 @@
                 return [None, # hack, do the right renaming from op.args[0] to op.result
                         SpaceOperation("record_known_class", [op.args[0], const_vtable], None)]
 
+    def rewrite_op_raw_malloc_usage(self, op):
+        pass
+
     def rewrite_op_jit_record_known_class(self, op):
         return SpaceOperation("record_known_class", [op.args[0], op.args[1]], None)
 
@@ -520,9 +528,12 @@
             name += '_add_memory_pressure'
         if not track_allocation:
             name += '_no_track_allocation'
-        return self._do_builtin_call(op, name, args,
-                                     extra = (TYPE,),
-                                     extrakey = TYPE)
+        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)
+        return self.rewrite_op_direct_call(op1)
 
     def rewrite_op_malloc_varsize(self, op):
         if op.args[1].value['flavor'] == 'raw':
@@ -550,8 +561,13 @@
         name = 'raw_free'
         if not track_allocation:
             name += '_no_track_allocation'
-        return self._do_builtin_call(op, name, [op.args[0]],
-                                     extra = (STRUCT,), extrakey = STRUCT)
+        op1 = self.prepare_builtin_call(op, name, [op.args[0]], (STRUCT,),
+                                        STRUCT)
+        if name == 'raw_free':
+            return self._handle_oopspec_call(op1, [op.args[0]],
+                                             EffectInfo.OS_RAW_FREE,
+                                             EffectInfo.EF_CANNOT_RAISE)
+        return self.rewrite_op_direct_call(op1)
 
     def rewrite_op_getarrayitem(self, op):
         ARRAY = op.args[0].concretetype.TO
@@ -566,9 +582,14 @@
                                    [v_base, arrayfielddescr, arraydescr,
                                     op.args[1]], op.result)]
         # normal case follows
+        pure = ''
+        immut = ARRAY._immutable_field(None)
+        if immut:
+            pure = '_pure'
         arraydescr = self.cpu.arraydescrof(ARRAY)
         kind = getkind(op.result.concretetype)
-        return SpaceOperation('getarrayitem_%s_%s' % (ARRAY._gckind, kind[0]),
+        return SpaceOperation('getarrayitem_%s_%s%s' % (ARRAY._gckind,
+                                                        kind[0], pure),
                               [op.args[0], arraydescr, op.args[1]],
                               op.result)
 
@@ -691,6 +712,16 @@
                               [v_inst, descr, v_value],
                               None)
 
+    def rewrite_op_getsubstruct(self, op):
+        STRUCT = op.args[0].concretetype.TO
+        argname = getattr(STRUCT, '_gckind', 'gc')
+        if argname != 'raw':
+            raise Exception("%r: only supported for gckind=raw" % (op,))
+        ofs = llmemory.offsetof(STRUCT, op.args[1].value)
+        return SpaceOperation('int_add',
+                              [op.args[0], Constant(ofs, lltype.Signed)],
+                              op.result)
+
     def is_typeptr_getset(self, op):
         return (op.args[1].value == 'typeptr' and
                 op.args[0].concretetype.TO._hints.get('typeptr'))
@@ -840,6 +871,23 @@
             return SpaceOperation('setinteriorfield_gc_%s' % kind, args,
                                   op.result)
 
+    def rewrite_op_raw_store(self, op):
+        T = op.args[2].concretetype
+        kind = getkind(T)[0]
+        assert kind != 'r'
+        descr = self.cpu.arraydescrof(rffi.CArray(T))
+        return SpaceOperation('raw_store_%s' % kind,
+                              [op.args[0], op.args[1], descr, op.args[2]],
+                              None)
+
+    def rewrite_op_raw_load(self, op):
+        T = op.result.concretetype
+        kind = getkind(T)[0]
+        assert kind != 'r'
+        descr = self.cpu.arraydescrof(rffi.CArray(T))
+        return SpaceOperation('raw_load_%s' % kind,
+                              [op.args[0], op.args[1], descr], op.result)
+
     def _rewrite_equality(self, op, opname):
         arg0, arg1 = op.args
         if isinstance(arg0, Constant) and not arg0.value:
@@ -850,7 +898,7 @@
             return self._rewrite_symmetric(op)
 
     def _is_gc(self, v):
-        return getattr(getattr(v.concretetype, "TO", None), "_gckind", "?") == 'gc'
+        return lltype_is_gc(v.concretetype)
 
     def _is_rclass_instance(self, v):
         return lltype._castdepth(v.concretetype.TO, rclass.OBJECT) >= 0
@@ -1228,6 +1276,8 @@
                        ('uint_or', 'int_or'),
                        ('uint_lshift', 'int_lshift'),
                        ('uint_xor', 'int_xor'),
+
+                       ('adr_add', 'int_add'),
                        ]:
         assert _old not in locals()
         exec py.code.Source('''
@@ -1430,7 +1480,19 @@
 
     def do_fixed_newlist(self, op, args, arraydescr):
         v_length = self._get_initial_newlist_length(op, args)
-        return SpaceOperation('new_array', [arraydescr, v_length], op.result)
+        assert v_length.concretetype is lltype.Signed
+        ops = []
+        if isinstance(v_length, Constant):
+            if v_length.value >= 0:
+                v = v_length
+            else:
+                v = Constant(0, lltype.Signed)
+        else:
+            v = Variable('new_length')
+            v.concretetype = lltype.Signed
+            ops.append(SpaceOperation('int_force_ge_zero', [v_length], v))
+        ops.append(SpaceOperation('new_array', [arraydescr, v], op.result))
+        return ops
 
     def do_fixed_list_len(self, op, args, arraydescr):
         if args[0] in self.vable_array_vars:     # virtualizable array
@@ -1457,7 +1519,7 @@
                                                      'check_neg_index')
         extra = getkind(op.result.concretetype)[0]
         if pure:
-            extra = 'pure_' + extra
+            extra += '_pure'
         op = SpaceOperation('getarrayitem_gc_%s' % extra,
                             [args[0], arraydescr, v_index], op.result)
         return extraop + [op]
@@ -1666,27 +1728,10 @@
     # rlib.libffi
 
     def _handle_libffi_call(self, op, oopspec_name, args):
-        if oopspec_name == 'libffi_prepare_call':
-            oopspecindex = EffectInfo.OS_LIBFFI_PREPARE
-            extraeffect = EffectInfo.EF_CANNOT_RAISE
-        elif oopspec_name.startswith('libffi_push_'):
-            oopspecindex = EffectInfo.OS_LIBFFI_PUSH_ARG
-            extraeffect = EffectInfo.EF_CANNOT_RAISE
-        elif oopspec_name.startswith('libffi_call_'):
+        if oopspec_name == 'libffi_call':
             oopspecindex = EffectInfo.OS_LIBFFI_CALL
             extraeffect = EffectInfo.EF_RANDOM_EFFECTS
-        elif oopspec_name == 'libffi_struct_getfield':
-            oopspecindex = EffectInfo.OS_LIBFFI_STRUCT_GETFIELD
-            extraeffect = EffectInfo.EF_CANNOT_RAISE
-        elif oopspec_name == 'libffi_struct_setfield':
-            oopspecindex = EffectInfo.OS_LIBFFI_STRUCT_SETFIELD
-            extraeffect = EffectInfo.EF_CANNOT_RAISE
-        elif oopspec_name == 'libffi_array_getitem':
-            oopspecindex = EffectInfo.OS_LIBFFI_GETARRAYITEM
-            extraeffect = EffectInfo.EF_CANNOT_RAISE
-        elif oopspec_name == 'libffi_array_setitem':
-            oopspecindex = EffectInfo.OS_LIBFFI_SETARRAYITEM
-            extraeffect = EffectInfo.EF_CANNOT_RAISE
+            self.callcontrol.has_libffi_call = True
         else:
             assert False, 'unsupported oopspec: %s' % oopspec_name
         return self._handle_oopspec_call(op, args, oopspecindex, extraeffect)
diff --git a/pypy/jit/codewriter/policy.py b/pypy/jit/codewriter/policy.py
--- a/pypy/jit/codewriter/policy.py
+++ b/pypy/jit/codewriter/policy.py
@@ -63,11 +63,10 @@
             contains_loop = contains_loop and not getattr(
                     func, '_jit_unroll_safe_', False)
 
-        unsupported = contains_unsupported_variable_type(graph,
+        res = see_function and not contains_unsupported_variable_type(graph,
                             self.supports_floats,
                             self.supports_longlong,
                             self.supports_singlefloats)
-        res = see_function and not unsupported
         if res and contains_loop:
             self.unsafe_loopy_graphs.add(graph)
         res = res and not contains_loop
diff --git a/pypy/jit/codewriter/support.py b/pypy/jit/codewriter/support.py
--- a/pypy/jit/codewriter/support.py
+++ b/pypy/jit/codewriter/support.py
@@ -431,31 +431,6 @@
     return llop.uint_mod(lltype.Unsigned, xll, yll)
 
 
-# libffi support
-# --------------
-
-def func(llfunc):
-    from pypy.rlib.libffi import Func
-    return cast_base_ptr_to_instance(Func, llfunc)
-
-def _ll_1_libffi_prepare_call(llfunc):
-    return func(llfunc)._prepare()
-
-def _ll_4_libffi_push_int(llfunc, value, ll_args, i):
-    return func(llfunc)._push_int(value, ll_args, i)
-
-def _ll_4_libffi_push_float(llfunc, value, ll_args, i):
-    return func(llfunc)._push_float(value, ll_args, i)
-
-def _ll_3_libffi_call_int(llfunc, funcsym, ll_args):
-    return func(llfunc)._do_call(funcsym, ll_args, rffi.LONG)
-
-def _ll_3_libffi_call_float(llfunc, funcsym, ll_args):
-    return func(llfunc)._do_call(funcsym, ll_args, rffi.DOUBLE)
-
-def _ll_3_libffi_call_void(llfunc, funcsym, ll_args):
-    return func(llfunc)._do_call(funcsym, ll_args, lltype.Void)
-
 # in the following calls to builtins, the JIT is allowed to look inside:
 inline_calls_to = [
     ('int_floordiv_ovf_zer', [lltype.Signed, lltype.Signed], lltype.Signed),
diff --git a/pypy/jit/codewriter/test/test_codewriter.py b/pypy/jit/codewriter/test/test_codewriter.py
--- a/pypy/jit/codewriter/test/test_codewriter.py
+++ b/pypy/jit/codewriter/test/test_codewriter.py
@@ -221,3 +221,17 @@
     assert 'setarrayitem_raw_i' in s
     assert 'getarrayitem_raw_i' in s
     assert 'residual_call_ir_v $<* fn _ll_1_raw_free__arrayPtr>' in s
+
+def test_newlist_negativ():
+    def f(n):
+        l = [0] * n
+        return len(l)
+
+    rtyper = support.annotate(f, [-1])
+    jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
+    cw = CodeWriter(FakeCPU(rtyper), [jitdriver_sd])
+    cw.find_all_graphs(FakePolicy())
+    cw.make_jitcodes(verbose=True)
+    s = jitdriver_sd.mainjitcode.dump()
+    assert 'int_force_ge_zero' in s
+    assert 'new_array' in s
diff --git a/pypy/jit/codewriter/test/test_jtransform.py b/pypy/jit/codewriter/test/test_jtransform.py
--- a/pypy/jit/codewriter/test/test_jtransform.py
+++ b/pypy/jit/codewriter/test/test_jtransform.py
@@ -123,6 +123,7 @@
             INT = lltype.Signed
             UNICHAR = lltype.UniChar
             FLOAT = lltype.Float
+            ARRAYPTR = rffi.CArrayPtr(lltype.Signed)
             argtypes = {
              EI.OS_MATH_SQRT:  ([FLOAT], FLOAT),
              EI.OS_STR2UNICODE:([PSTR], PUNICODE),
@@ -139,16 +140,26 @@
              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_FREE:             ([ARRAYPTR], lltype.Void),
             }
             argtypes = argtypes[oopspecindex]
             assert argtypes[0] == [v.concretetype for v in op.args[1:]]
             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:
+                assert extraeffect == EI.EF_CAN_RAISE
+            elif oopspecindex == EI.OS_RAW_FREE:
+                assert extraeffect == EI.EF_CANNOT_RAISE
             else:
                 assert extraeffect == EI.EF_ELIDABLE_CANNOT_RAISE
         return 'calldescr-%d' % oopspecindex
+
     def calldescr_canraise(self, calldescr):
+        EI = effectinfo.EffectInfo
+        if calldescr == 'calldescr-%d' % EI.OS_RAW_MALLOC_VARSIZE:
+            return True
         return False
 
 
@@ -547,10 +558,13 @@
     flags = Constant({'flavor': 'raw'}, lltype.Void)
     op = SpaceOperation('malloc_varsize', [Constant(S, lltype.Void), flags,
                                            v1], v)
-    tr = Transformer(FakeCPU(), FakeResidualCallControl())
+    tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
     op0, op1 = tr.rewrite_operation(op)
     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)
+
     assert op1.opname == '-live-'
     assert op1.args == []
 
@@ -591,21 +605,28 @@
     assert op1.args == []
 
 def test_raw_free():
-    S = lltype.Struct('dummy', ('x', lltype.Signed))
-    for flag in [True, False]:
-        flags = Constant({'flavor': 'raw', 'track_allocation': flag},
-                         lltype.Void)
-        op = SpaceOperation('free', [varoftype(lltype.Ptr(S)), flags],
-                            varoftype(lltype.Void))
-        tr = Transformer(FakeCPU(), FakeResidualCallControl())
-        op0, op1 = tr.rewrite_operation(op)
-        assert op0.opname == 'residual_call_ir_v'
-        if flag:
-            pseudo_op_name = 'raw_free'
-        else:
-            pseudo_op_name = 'raw_free_no_track_allocation'
-        assert op0.args[0].value == pseudo_op_name   # pseudo-function as a str
-        assert op1.opname == '-live-'
+    S = rffi.CArray(lltype.Signed)
+    flags = Constant({'flavor': 'raw', 'track_allocation': True},
+                     lltype.Void)
+    op = SpaceOperation('free', [varoftype(lltype.Ptr(S)), flags],
+                        varoftype(lltype.Void))
+    tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
+    op0 = tr.rewrite_operation(op)
+    assert op0.opname == 'residual_call_ir_v'
+    assert op0.args[0].value == 'raw_free'
+    assert op0.args[1] == 'calldescr-%d' % effectinfo.EffectInfo.OS_RAW_FREE
+
+def test_raw_free_no_track_allocation():
+    S = rffi.CArray(lltype.Signed)
+    flags = Constant({'flavor': 'raw', 'track_allocation': False},
+                     lltype.Void)
+    op = SpaceOperation('free', [varoftype(lltype.Ptr(S)), flags],
+                        varoftype(lltype.Void))
+    tr = Transformer(FakeCPU(), FakeResidualCallControl())
+    op0, op1 = tr.rewrite_operation(op)
+    assert op0.opname == 'residual_call_ir_v'
+    assert op0.args[0].value == 'raw_free_no_track_allocation'
+    assert op1.opname == '-live-'
 
 def test_rename_on_links():
     v1 = Variable()
@@ -621,6 +642,13 @@
     assert block.exits[0].target is block2
     assert block.exits[0].args == [v1]
 
+def test_cast_ptr_to_adr():
+    t = Transformer(FakeCPU(), None)
+    v = varoftype(lltype.Ptr(lltype.Array()))
+    v2 = varoftype(llmemory.Address)
+    op1 = t.rewrite_operation(SpaceOperation('cast_ptr_to_adr', [v], v2))
+    assert op1 is None
+
 def test_int_eq():
     v1 = varoftype(lltype.Signed)
     v2 = varoftype(lltype.Signed)
@@ -830,6 +858,30 @@
     op1 = Transformer(FakeCPU()).rewrite_operation(op)
     assert not op1
 
+def test_raw_store():
+    v_storage = varoftype(llmemory.Address)
+    v_index = varoftype(lltype.Signed)
+    v_item = varoftype(lltype.Signed) # for example
+    op = SpaceOperation('raw_store', [v_storage, v_index, v_item], None)
+    op1 = Transformer(FakeCPU()).rewrite_operation(op)
+    assert op1.opname == 'raw_store_i'
+    assert op1.args[0] == v_storage
+    assert op1.args[1] == v_index
+    assert op1.args[2] == ('arraydescr', rffi.CArray(lltype.Signed))
+    assert op1.args[3] == v_item
+
+def test_raw_load():
+    v_storage = varoftype(llmemory.Address)
+    v_index = varoftype(lltype.Signed)
+    v_res = varoftype(lltype.Signed) # for example
+    op = SpaceOperation('raw_load', [v_storage, v_index], v_res)
+    op1 = Transformer(FakeCPU()).rewrite_operation(op)
+    assert op1.opname == 'raw_load_i'
+    assert op1.args[0] == v_storage
+    assert op1.args[1] == v_index
+    assert op1.args[2] == ('arraydescr', rffi.CArray(lltype.Signed))
+    assert op1.result == v_res
+
 def test_promote_1():
     v1 = varoftype(lltype.Signed)
     v2 = varoftype(lltype.Signed)
diff --git a/pypy/jit/codewriter/test/test_list.py b/pypy/jit/codewriter/test/test_list.py
--- a/pypy/jit/codewriter/test/test_list.py
+++ b/pypy/jit/codewriter/test/test_list.py
@@ -85,8 +85,11 @@
                  """new_array <ArrayDescr>, $0 -> %r0""")
     builtin_test('newlist', [Constant(5, lltype.Signed)], FIXEDLIST,
                  """new_array <ArrayDescr>, $5 -> %r0""")
+    builtin_test('newlist', [Constant(-2, lltype.Signed)], FIXEDLIST,
+                 """new_array <ArrayDescr>, $0 -> %r0""")
     builtin_test('newlist', [varoftype(lltype.Signed)], FIXEDLIST,
-                 """new_array <ArrayDescr>, %i0 -> %r0""")
+                 """int_force_ge_zero %i0 -> %i1\n"""
+                 """new_array <ArrayDescr>, %i1 -> %r0""")
     builtin_test('newlist', [Constant(5, lltype.Signed),
                              Constant(0, lltype.Signed)], FIXEDLIST,
                  """new_array <ArrayDescr>, $5 -> %r0""")
@@ -126,14 +129,14 @@
     builtin_test('list.getitem_foldable/NONNEG',
                  [varoftype(FIXEDLIST), varoftype(lltype.Signed)],
                  lltype.Signed, """
-                     getarrayitem_gc_pure_i %r0, <ArrayDescr>, %i0 -> %i1
+                     getarrayitem_gc_i_pure %r0, <ArrayDescr>, %i0 -> %i1
                  """)
     builtin_test('list.getitem_foldable/NEG',
                  [varoftype(FIXEDLIST), varoftype(lltype.Signed)],
                  lltype.Signed, """
                      -live-
                      check_neg_index %r0, <ArrayDescr>, %i0 -> %i1
-                     getarrayitem_gc_pure_i %r0, <ArrayDescr>, %i1 -> %i2
+                     getarrayitem_gc_i_pure %r0, <ArrayDescr>, %i1 -> %i2
                  """)
 
 def test_fixed_setitem():
diff --git a/pypy/jit/metainterp/blackhole.py b/pypy/jit/metainterp/blackhole.py
--- a/pypy/jit/metainterp/blackhole.py
+++ b/pypy/jit/metainterp/blackhole.py
@@ -477,6 +477,11 @@
     @arguments("i", "i", "i", returns="i")
     def bhimpl_int_between(a, b, c):
         return a <= b < c
+    @arguments("i", returns="i")
+    def bhimpl_int_force_ge_zero(i):
+        if i < 0:
+            return 0
+        return i
 
     @arguments("i", "i", returns="i")
     def bhimpl_uint_lt(a, b):
@@ -1124,9 +1129,9 @@
     def bhimpl_getarrayitem_gc_f(cpu, array, arraydescr, index):
         return cpu.bh_getarrayitem_gc_f(arraydescr, array, index)
 
-    bhimpl_getarrayitem_gc_pure_i = bhimpl_getarrayitem_gc_i
-    bhimpl_getarrayitem_gc_pure_r = bhimpl_getarrayitem_gc_r
-    bhimpl_getarrayitem_gc_pure_f = bhimpl_getarrayitem_gc_f
+    bhimpl_getarrayitem_gc_i_pure = bhimpl_getarrayitem_gc_i
+    bhimpl_getarrayitem_gc_r_pure = bhimpl_getarrayitem_gc_r
+    bhimpl_getarrayitem_gc_f_pure = bhimpl_getarrayitem_gc_f
 
     @arguments("cpu", "i", "d", "i", returns="i")
     def bhimpl_getarrayitem_raw_i(cpu, array, arraydescr, index):
@@ -1135,6 +1140,9 @@
     def bhimpl_getarrayitem_raw_f(cpu, array, arraydescr, index):
         return cpu.bh_getarrayitem_raw_f(arraydescr, array, index)
 
+    bhimpl_getarrayitem_raw_i_pure = bhimpl_getarrayitem_raw_i
+    bhimpl_getarrayitem_raw_f_pure = bhimpl_getarrayitem_raw_f
+
     @arguments("cpu", "r", "d", "i", "i")
     def bhimpl_setarrayitem_gc_i(cpu, array, arraydescr, index, newvalue):
         cpu.bh_setarrayitem_gc_i(arraydescr, array, index, newvalue)
@@ -1269,6 +1277,20 @@
     def bhimpl_setfield_raw_f(cpu, struct, fielddescr, newvalue):
         cpu.bh_setfield_raw_f(struct, fielddescr, newvalue)
 
+    @arguments("cpu", "i", "i", "d", "i")
+    def bhimpl_raw_store_i(cpu, addr, offset, arraydescr, newvalue):
+        cpu.bh_raw_store_i(addr, offset, arraydescr, newvalue)
+    @arguments("cpu", "i", "i", "d", "f")
+    def bhimpl_raw_store_f(cpu, addr, offset, arraydescr, newvalue):
+        cpu.bh_raw_store_f(addr, offset, arraydescr, newvalue)
+
+    @arguments("cpu", "i", "i", "d", returns="i")
+    def bhimpl_raw_load_i(cpu, addr, offset, arraydescr):
+        return cpu.bh_raw_load_i(addr, offset, arraydescr)
+    @arguments("cpu", "i", "i", "d", returns="f")
+    def bhimpl_raw_load_f(cpu, addr, offset, arraydescr):
+        return cpu.bh_raw_load_f(addr, offset, arraydescr)
+
     @arguments("r", "d", "d")
     def bhimpl_record_quasiimmut_field(struct, fielddescr, mutatefielddescr):
         pass
diff --git a/pypy/jit/metainterp/compile.py b/pypy/jit/metainterp/compile.py
--- a/pypy/jit/metainterp/compile.py
+++ b/pypy/jit/metainterp/compile.py
@@ -225,6 +225,8 @@
     assert isinstance(target_token, TargetToken)
     assert loop_jitcell_token.target_tokens
     loop_jitcell_token.target_tokens.append(target_token)
+    if target_token.short_preamble:
+        metainterp_sd.logger_ops.log_short_preamble([], target_token.short_preamble)
 
     loop = partial_trace
     loop.operations = loop.operations[:-1] + part.operations
diff --git a/pypy/jit/metainterp/executor.py b/pypy/jit/metainterp/executor.py
--- a/pypy/jit/metainterp/executor.py
+++ b/pypy/jit/metainterp/executor.py
@@ -180,6 +180,26 @@
     else:
         cpu.bh_setfield_raw_i(struct, fielddescr, itembox.getint())
 
+def do_raw_store(cpu, _, addrbox, offsetbox, valuebox, arraydescr):
+    addr = addrbox.getint()
+    offset = offsetbox.getint()
+    if arraydescr.is_array_of_pointers():
+        raise AssertionError("cannot store GC pointers in raw store")
+    elif arraydescr.is_array_of_floats():
+        cpu.bh_raw_store_f(addr, offset, arraydescr,valuebox.getfloatstorage())
+    else:
+        cpu.bh_raw_store_i(addr, offset, arraydescr, valuebox.getint())
+
+def do_raw_load(cpu, _, addrbox, offsetbox, arraydescr):
+    addr = addrbox.getint()
+    offset = offsetbox.getint()
+    if arraydescr.is_array_of_pointers():
+        raise AssertionError("cannot store GC pointers in raw store")
+    elif arraydescr.is_array_of_floats():
+        return BoxFloat(cpu.bh_raw_load_f(addr, offset, arraydescr))
+    else:
+        return BoxInt(cpu.bh_raw_load_i(addr, offset, arraydescr))
+
 def exec_new_with_vtable(cpu, clsbox):
     from pypy.jit.codewriter import heaptracker
     vtable = clsbox.getint()
@@ -277,19 +297,6 @@
 
 
 def _make_execute_list():
-    if 0:     # enable this to trace calls to do_xxx
-        def wrap(fn):
-            def myfn(*args):
-                print '<<<', fn.__name__
-                try:
-                    return fn(*args)
-                finally:
-                    print fn.__name__, '>>>'
-            return myfn
-    else:
-        def wrap(fn):
-            return fn
-    #
     execute_by_num_args = {}
     for key, value in rop.__dict__.items():
         if not key.startswith('_'):
@@ -343,7 +350,6 @@
                          rop.DEBUG_MERGE_POINT,
                          rop.JIT_DEBUG,
                          rop.SETARRAYITEM_RAW,
-                         rop.GETINTERIORFIELD_RAW,
                          rop.SETINTERIORFIELD_RAW,
                          rop.CALL_RELEASE_GIL,
                          rop.QUASIIMMUT_FIELD,
diff --git a/pypy/jit/metainterp/history.py b/pypy/jit/metainterp/history.py
--- a/pypy/jit/metainterp/history.py
+++ b/pypy/jit/metainterp/history.py
@@ -39,7 +39,7 @@
         # XXX fix this for oo...
         if (TYPE != llmemory.Address and
             rffi.sizeof(TYPE) > rffi.sizeof(lltype.Signed)):
-            if supports_longlong:
+            if supports_longlong and TYPE is not lltype.LongFloat:
                 assert rffi.sizeof(TYPE) == 8
                 return 'float'
             raise NotImplementedError("type %s is too large" % TYPE)
@@ -706,6 +706,7 @@
 
         self.virtual_state = None
         self.exported_state = None
+        self.short_preamble = None
 
     def repr_of_descr(self):
         return 'TargetToken(%d)' % compute_unique_id(self)
diff --git a/pypy/jit/metainterp/optimizeopt/__init__.py b/pypy/jit/metainterp/optimizeopt/__init__.py
--- a/pypy/jit/metainterp/optimizeopt/__init__.py
+++ b/pypy/jit/metainterp/optimizeopt/__init__.py
@@ -5,7 +5,6 @@
 from pypy.jit.metainterp.optimizeopt.heap import OptHeap
 from pypy.jit.metainterp.optimizeopt.vstring import OptString
 from pypy.jit.metainterp.optimizeopt.unroll import optimize_unroll
-from pypy.jit.metainterp.optimizeopt.fficall import OptFfiCall
 from pypy.jit.metainterp.optimizeopt.simplify import OptSimplify
 from pypy.jit.metainterp.optimizeopt.pure import OptPure
 from pypy.jit.metainterp.optimizeopt.earlyforce import OptEarlyForce
@@ -21,7 +20,6 @@
             ('earlyforce', OptEarlyForce),
             ('pure', OptPure),
             ('heap', OptHeap),
-            ('ffi', None),
             ('unroll', None)]
 # no direct instantiation of unroll
 unroll_all_opts = unrolling_iterable(ALL_OPTS)
@@ -42,11 +40,6 @@
             if opt is not None:
                 o = opt()
                 optimizations.append(o)
-            elif name == 'ffi' and config.translation.jit_ffi:
-                # we cannot put the class directly in the unrolling_iterable,
-                # because we do not want it to be seen at all (to avoid to
-                # introduce a dependency on libffi in case we do not need it)
-                optimizations.append(OptFfiCall())
 
     if ('rewrite' not in enable_opts or 'virtualize' not in enable_opts
         or 'heap' not in enable_opts or 'unroll' not in enable_opts
diff --git a/pypy/jit/metainterp/optimizeopt/fficall.py b/pypy/jit/metainterp/optimizeopt/fficall.py
deleted file mode 100644
--- a/pypy/jit/metainterp/optimizeopt/fficall.py
+++ /dev/null
@@ -1,307 +0,0 @@
-from pypy.jit.codewriter.effectinfo import EffectInfo
-from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
-from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
-from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.rlib import clibffi, libffi
-from pypy.rlib.debug import debug_print
-from pypy.rlib.libffi import Func
-from pypy.rlib.objectmodel import we_are_translated
-from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
-from pypy.rpython.lltypesystem import lltype, llmemory, rffi
-from pypy.rlib.objectmodel import we_are_translated
-from pypy.rlib.rarithmetic import intmask
-
-
-class FuncInfo(object):
-
-    argtypes = None
-    restype = None
-    descr = None
-    prepare_op = None
-
-    def __init__(self, funcval, cpu, prepare_op):
-        self.funcval = funcval
-        self.opargs = []
-        argtypes, restype, flags = self._get_signature(funcval)
-        self.descr = cpu.calldescrof_dynamic(argtypes, restype,
-                                             EffectInfo.MOST_GENERAL,
-                                             ffi_flags=flags)
-        # ^^^ may be None if unsupported
-        self.prepare_op = prepare_op
-        self.delayed_ops = []
-
-    def _get_signature(self, funcval):
-        """
-        given the funcval, return a tuple (argtypes, restype, flags), where
-        the actuall types are libffi.types.*
-
-        The implementation is tricky because we have three possible cases:
-
-        - translated: the easiest case, we can just cast back the pointer to
-          the original Func instance and read .argtypes, .restype and .flags
-
-        - completely untranslated: this is what we get from test_optimizeopt
-          tests. funcval contains a FakeLLObject whose _fake_class is Func,
-          and we can just get .argtypes, .restype and .flags
-
-        - partially translated: this happens when running metainterp tests:
-          funcval contains the low-level equivalent of a Func, and thus we
-          have to fish inst_argtypes and inst_restype by hand.  Note that
-          inst_argtypes is actually a low-level array, but we can use it
-          directly since the only thing we do with it is to read its items
-        """
-
-        llfunc = funcval.box.getref_base()
-        if we_are_translated():
-            func = cast_base_ptr_to_instance(Func, llfunc)
-            return func.argtypes, func.restype, func.flags
-        elif getattr(llfunc, '_fake_class', None) is Func:
-            # untranslated
-            return llfunc.argtypes, llfunc.restype, llfunc.flags
-        else:
-            # partially translated
-            # llfunc contains an opaque pointer to something like the following:
-            # <GcStruct pypy.rlib.libffi.Func { super, inst_argtypes, inst_funcptr,
-            #                                   inst_funcsym, inst_restype }>
-            #
-            # Unfortunately, we cannot use the proper lltype.cast_opaque_ptr,
-            # because we don't have the exact TYPE to cast to.  Instead, we
-            # just fish it manually :-(
-            f = llfunc._obj.container
-            return f.inst_argtypes, f.inst_restype, f.inst_flags
-
-
-class OptFfiCall(Optimization):
-
-    def setup(self):
-        self.funcinfo = None
-        if self.optimizer.loop is not None:
-            self.logops = self.optimizer.loop.logops
-        else:
-            self.logops = None
-
-    def new(self):
-        return OptFfiCall()
-
-    def begin_optimization(self, funcval, op):
-        self.rollback_maybe('begin_optimization', op)
-        self.funcinfo = FuncInfo(funcval, self.optimizer.cpu, op)
-
-    def commit_optimization(self):
-        self.funcinfo = None
-
-    def rollback_maybe(self, msg, op):
-        if self.funcinfo is None:
-            return # nothing to rollback
-        #
-        # we immediately set funcinfo to None to prevent recursion when
-        # calling emit_op
-        if self.logops is not None:
-            debug_print('rollback: ' + msg + ': ', self.logops.repr_of_resop(op))
-        funcinfo = self.funcinfo
-        self.funcinfo = None
-        self.emit_operation(funcinfo.prepare_op)
-        for op in funcinfo.opargs:
-            self.emit_operation(op)
-        for delayed_op in funcinfo.delayed_ops:
-            self.emit_operation(delayed_op)
-
-    def emit_operation(self, op):
-        # we cannot emit any operation during the optimization
-        self.rollback_maybe('invalid op', op)
-        Optimization.emit_operation(self, op)
-
-    def optimize_CALL(self, op):
-        oopspec = self._get_oopspec(op)
-        ops = [op]
-        if oopspec == EffectInfo.OS_LIBFFI_PREPARE:
-            ops = self.do_prepare_call(op)
-        elif oopspec == EffectInfo.OS_LIBFFI_PUSH_ARG:
-            ops = self.do_push_arg(op)
-        elif oopspec == EffectInfo.OS_LIBFFI_CALL:
-            ops = self.do_call(op)
-        elif (oopspec == EffectInfo.OS_LIBFFI_STRUCT_GETFIELD or
-              oopspec == EffectInfo.OS_LIBFFI_STRUCT_SETFIELD):
-            ops = self.do_struct_getsetfield(op, oopspec)
-        elif (oopspec == EffectInfo.OS_LIBFFI_GETARRAYITEM or
-            oopspec == EffectInfo.OS_LIBFFI_SETARRAYITEM):
-            ops = self.do_getsetarrayitem(op, oopspec)
-        #
-        for op in ops:
-            self.emit_operation(op)
-
-    optimize_CALL_MAY_FORCE = optimize_CALL
-
-    def optimize_FORCE_TOKEN(self, op):
-        # The handling of force_token needs a bit of explanation.
-        # The original trace which is getting optimized looks like this:
-        #    i1 = force_token()
-        #    setfield_gc(p0, i1, ...)
-        #    call_may_force(...)
-        #
-        # In theory, fficall should take care of both force_token and
-        # setfield_gc.  However, the lazy setfield optimization in heap.py
-        # delays the setfield_gc, with the effect that fficall.py sees them in
-        # this order:
-        #    i1 = force_token()
-        #    call_may_force(...)
-        #    setfield_gc(p0, i1, ...)
-        #
-        # This means that see the setfield_gc only the call_may_force, when
-        # the optimization has already been done, and thus we need to take
-        # special care just of force_token.
-        #
-        # Finally, the method force_lazy_setfield in heap.py reorders the
-        # call_may_force and the setfield_gc, so the final result we get is
-        # again force_token/setfield_gc/call_may_force.
-        #
-        # However, note that nowadays we also allow to have any setfield_gc
-        # between libffi_prepare and libffi_call, so while the comment above
-        # it's a bit superfluous, it has been left there for future reference.
-        if self.funcinfo is None:
-            self.emit_operation(op)
-        else:
-            self.funcinfo.delayed_ops.append(op)
-
-    optimize_SETFIELD_GC = optimize_FORCE_TOKEN
-
-    def do_prepare_call(self, op):
-        self.rollback_maybe('prepare call', op)
-        funcval = self._get_funcval(op)
-        if not funcval.is_constant():
-            return [op] # cannot optimize
-        self.begin_optimization(funcval, op)
-        return []
-
-    def do_push_arg(self, op):
-        funcval = self._get_funcval(op)
-        if not self.funcinfo or self.funcinfo.funcval is not funcval:
-            return [op] # cannot optimize
-        self.funcinfo.opargs.append(op)
-        return []
-
-    def do_call(self, op):
-        funcval = self._get_funcval(op)
-        funcinfo = self.funcinfo
-        if (not funcinfo or funcinfo.funcval is not funcval or
-            funcinfo.descr is None):
-            return [op] # cannot optimize
-        funcsymval = self.getvalue(op.getarg(2))
-        arglist = [funcsymval.get_key_box()]
-        for push_op in funcinfo.opargs:
-            argval = self.getvalue(push_op.getarg(2))
-            arglist.append(argval.get_key_box())
-        newop = ResOperation(rop.CALL_RELEASE_GIL, arglist, op.result,
-                             descr=funcinfo.descr)
-        self.commit_optimization()
-        ops = []
-        for delayed_op in funcinfo.delayed_ops:
-            ops.append(delayed_op)
-        ops.append(newop)
-        return ops
-
-    def do_struct_getsetfield(self, op, oopspec):
-        ffitypeval = self.getvalue(op.getarg(1))
-        addrval = self.getvalue(op.getarg(2))
-        offsetval = self.getvalue(op.getarg(3))
-        if not ffitypeval.is_constant() or not offsetval.is_constant():
-            return [op]
-        #
-        ffitypeaddr = ffitypeval.box.getaddr()
-        ffitype = llmemory.cast_adr_to_ptr(ffitypeaddr, clibffi.FFI_TYPE_P)
-        offset = offsetval.box.getint()
-        descr = self._get_field_descr(ffitype, offset)
-        #
-        arglist = [addrval.force_box(self.optimizer)]
-        if oopspec == EffectInfo.OS_LIBFFI_STRUCT_GETFIELD:
-            opnum = rop.GETFIELD_RAW
-        else:
-            opnum = rop.SETFIELD_RAW
-            newval = self.getvalue(op.getarg(4))
-            arglist.append(newval.force_box(self.optimizer))
-        #
-        newop = ResOperation(opnum, arglist, op.result, descr=descr)
-        return [newop]
-
-    def _get_field_descr(self, ffitype, offset):
-        kind = libffi.types.getkind(ffitype)
-        is_pointer = is_float = is_signed = False
-        if ffitype is libffi.types.pointer:
-            is_pointer = True
-        elif kind == 'i':
-            is_signed = True
-        elif kind == 'f' or kind == 'I' or kind == 'U':
-            # longlongs are treated as floats, see e.g. llsupport/descr.py:getDescrClass
-            is_float = True
-        else:
-            assert False, "unsupported ffitype or kind"
-        #
-        fieldsize = intmask(ffitype.c_size)
-        return self.optimizer.cpu.fielddescrof_dynamic(offset, fieldsize,
-                                                       is_pointer, is_float, is_signed)
-    
-    def do_getsetarrayitem(self, op, oopspec):
-        ffitypeval = self.getvalue(op.getarg(1))
-        widthval = self.getvalue(op.getarg(2))
-        offsetval = self.getvalue(op.getarg(5))
-        if not ffitypeval.is_constant() or not widthval.is_constant() or not offsetval.is_constant():
-            return [op]
-
-        ffitypeaddr = ffitypeval.box.getaddr()
-        ffitype = llmemory.cast_adr_to_ptr(ffitypeaddr, clibffi.FFI_TYPE_P)
-        offset = offsetval.box.getint()
-        width = widthval.box.getint()
-        descr = self._get_interior_descr(ffitype, width, offset)
-
-        arglist = [
-            self.getvalue(op.getarg(3)).force_box(self.optimizer),
-            self.getvalue(op.getarg(4)).force_box(self.optimizer),
-        ]
-        if oopspec == EffectInfo.OS_LIBFFI_GETARRAYITEM:
-            opnum = rop.GETINTERIORFIELD_RAW
-        elif oopspec == EffectInfo.OS_LIBFFI_SETARRAYITEM:
-            opnum = rop.SETINTERIORFIELD_RAW
-            arglist.append(self.getvalue(op.getarg(6)).force_box(self.optimizer))
-        else:
-            assert False
-        return [
-            ResOperation(opnum, arglist, op.result, descr=descr),
-        ]
-
-    def _get_interior_descr(self, ffitype, width, offset):
-        kind = libffi.types.getkind(ffitype)
-        is_pointer = is_float = is_signed = False
-        if ffitype is libffi.types.pointer:
-            is_pointer = True
-        elif kind == 'i':
-            is_signed = True
-        elif kind == 'f' or kind == 'I' or kind == 'U':
-            # longlongs are treated as floats, see
-            # e.g. llsupport/descr.py:getDescrClass
-            is_float = True
-        elif kind == 'u' or kind == 's':
-            # they're all False
-            pass
-        else:
-            raise NotImplementedError("unsupported ffitype or kind: %s" % kind)
-        #
-        fieldsize = rffi.getintfield(ffitype, 'c_size')
-        return self.optimizer.cpu.interiorfielddescrof_dynamic(
-            offset, width, fieldsize, is_pointer, is_float, is_signed
-        )
-
-
-    def propagate_forward(self, op):
-        if self.logops is not None:
-            debug_print(self.logops.repr_of_resop(op))
-        dispatch_opt(self, op)
-
-    def _get_oopspec(self, op):
-        effectinfo = op.getdescr().get_extra_info()
-        return effectinfo.oopspecindex
-
-    def _get_funcval(self, op):
-        return self.getvalue(op.getarg(1))
-
-dispatch_opt = make_dispatcher_method(OptFfiCall, 'optimize_',
-        default=OptFfiCall.emit_operation)
diff --git a/pypy/jit/metainterp/optimizeopt/heap.py b/pypy/jit/metainterp/optimizeopt/heap.py
--- a/pypy/jit/metainterp/optimizeopt/heap.py
+++ b/pypy/jit/metainterp/optimizeopt/heap.py
@@ -1,7 +1,7 @@
 import os
 
 from pypy.jit.metainterp.jitexc import JitException
-from pypy.jit.metainterp.optimizeopt.optimizer import Optimization, MODE_ARRAY
+from pypy.jit.metainterp.optimizeopt.optimizer import Optimization, MODE_ARRAY, LEVEL_KNOWNCLASS
 from pypy.jit.metainterp.history import ConstInt, Const
 from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
 from pypy.jit.metainterp.resoperation import rop, ResOperation
@@ -128,8 +128,12 @@
             op = self._cached_fields_getfield_op[structvalue]
             if not op:
                 continue
-            if optimizer.getvalue(op.getarg(0)) in optimizer.opaque_pointers:
-                continue
+            value = optimizer.getvalue(op.getarg(0))
+            if value in optimizer.opaque_pointers:
+                if value.level < LEVEL_KNOWNCLASS:
+                    continue
+                if op.getopnum() != rop.SETFIELD_GC and op.getopnum() != rop.GETFIELD_GC:
+                    continue
             if structvalue in self._cached_fields:
                 if op.getopnum() == rop.SETFIELD_GC:
                     result = op.getarg(1)
@@ -251,6 +255,7 @@
             opnum == rop.SETARRAYITEM_GC or      # handled specially
             opnum == rop.SETARRAYITEM_RAW or     # no effect on GC struct
             opnum == rop.SETINTERIORFIELD_RAW or # no effect on GC struct
+            opnum == rop.RAW_STORE or            # no effect on GC struct
             opnum == rop.STRSETITEM or           # no effect on GC struct/array
             opnum == rop.UNICODESETITEM or       # no effect on GC struct/array
             opnum == rop.DEBUG_MERGE_POINT or    # no effect whatsoever
diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py b/pypy/jit/metainterp/optimizeopt/rewrite.py
--- a/pypy/jit/metainterp/optimizeopt/rewrite.py
+++ b/pypy/jit/metainterp/optimizeopt/rewrite.py
@@ -241,6 +241,16 @@
             # guard_nonnull_class on this value, which is rather silly.
             # replace the original guard with a guard_value
             old_guard_op = value.last_guard
+            if old_guard_op.getopnum() != rop.GUARD_NONNULL:
+                # This is only safe if the class of the guard_value matches the
+                # class of the guard_*_class, otherwise the intermediate ops might
+                # be executed with wrong classes.
+                previous_classbox = value.get_constant_class(self.optimizer.cpu)            
+                expected_classbox = self.optimizer.cpu.ts.cls_of_box(op.getarg(1))
+                assert previous_classbox is not None
+                assert expected_classbox is not None
+                if not previous_classbox.same_constant(expected_classbox):
+                    raise InvalidLoop('A GUARD_VALUE was proven to always fail')
             op = old_guard_op.copy_and_change(rop.GUARD_VALUE,
                                       args = [old_guard_op.getarg(0), op.getarg(1)])
             self.optimizer.replaces_guard[op] = old_guard_op
@@ -251,6 +261,8 @@
             assert isinstance(descr, compile.ResumeGuardDescr)
             descr.guard_opnum = rop.GUARD_VALUE
             descr.make_a_counter_per_value(op)
+            # to be safe
+            value.last_guard = None
         constbox = op.getarg(1)
         assert isinstance(constbox, Const)
         self.optimize_guard(op, constbox)
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_multilabel.py b/pypy/jit/metainterp/optimizeopt/test/test_multilabel.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_multilabel.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_multilabel.py
@@ -431,7 +431,53 @@
         jump(i55, i81)
         """
         self.optimize_loop(ops, expected)
-        
+
+    def test_boxed_opaque_unknown_class(self):
+        ops = """
+        [p1]
+        p2 = getfield_gc(p1, descr=nextdescr) 
+        mark_opaque_ptr(p2)        
+        i3 = getfield_gc(p2, descr=otherdescr)
+        label(p1)
+        i4 = getfield_gc(p1, descr=otherdescr)
+        label(p1)
+        p5 = getfield_gc(p1, descr=nextdescr) 
+        mark_opaque_ptr(p5)        
+        i6 = getfield_gc(p5, descr=otherdescr)
+        i7 = call(i6, descr=nonwritedescr)
+        """
+        expected = """
+        [p1]
+        p2 = getfield_gc(p1, descr=nextdescr) 
+        i3 = getfield_gc(p2, descr=otherdescr)
+        label(p1)
+        i4 = getfield_gc(p1, descr=otherdescr)
+        label(p1)
+        p5 = getfield_gc(p1, descr=nextdescr) 
+        i6 = getfield_gc(p5, descr=otherdescr)
+        i7 = call(i6, descr=nonwritedescr)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_opaque_pointer_fails_to_close_loop(self):
+        ops = """
+        [p1, p11]
+        p2 = getfield_gc(p1, descr=nextdescr) 
+        guard_class(p2, ConstClass(node_vtable)) []
+        mark_opaque_ptr(p2)        
+        i3 = getfield_gc(p2, descr=otherdescr)
+        label(p1, p11)
+        p12 = getfield_gc(p1, descr=nextdescr) 
+        i13 = getfield_gc(p2, descr=otherdescr)
+        i14 = call(i13, descr=nonwritedescr)        
+        jump(p11, p1)
+        """
+        with raises(InvalidLoop):
+            self.optimize_loop(ops, ops)
+
+            
+
+
 class OptRenameStrlen(Optimization):
     def propagate_forward(self, op):
         dispatch_opt(self, op)
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
deleted file mode 100644
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py
+++ /dev/null
@@ -1,315 +0,0 @@
-from pypy.rpython.lltypesystem import llmemory
-from pypy.rlib.libffi import Func, types
-from pypy.jit.metainterp.history import AbstractDescr
-from pypy.jit.codewriter.effectinfo import EffectInfo
-from pypy.jit.metainterp.optimizeopt.test.test_optimizebasic import BaseTestBasic
-from pypy.jit.metainterp.optimizeopt.test.test_optimizebasic import LLtypeMixin
-
-class MyCallDescr(AbstractDescr):
-    """
-    Fake calldescr to be used inside the tests.
-
-    The particularity is that it provides an __eq__ method, so that it
-    comparses by value by comparing the arg_types and typeinfo fields, so you
-    can check that the signature of a call is really what you want.
-    """
-
-    def __init__(self, arg_types, typeinfo, flags):
-        self.arg_types = arg_types
-        self.typeinfo = typeinfo   # return type
-        self.flags = flags
-
-    def __eq__(self, other):
-        return (self.arg_types == other.arg_types and
-                self.typeinfo == other.typeinfo and
-                self.flags == other.get_ffi_flags())
-
-class FakeLLObject(object):
-
-    def __init__(self, **kwds):
-        self.__dict__.update(kwds)
-        self._TYPE = llmemory.GCREF
-
-    def _identityhash(self):
-        return id(self)
-
-
-class TestFfiCall(BaseTestBasic, LLtypeMixin):
-
-    enable_opts = "intbounds:rewrite:virtualize:string:pure:earlyforce:heap:ffi"
-
-    class namespace:
-        cpu = LLtypeMixin.cpu
-        FUNC = LLtypeMixin.FUNC
-        vable_token_descr = LLtypeMixin.valuedescr
-        valuedescr = LLtypeMixin.valuedescr
-
-        int_float__int_42 = MyCallDescr('if', 'i', 42)
-        int_float__int_43 = MyCallDescr('if', 'i', 43)
-        funcptr = FakeLLObject()
-        func = FakeLLObject(_fake_class=Func,
-                            argtypes=[types.sint, types.double],
-                            restype=types.sint,
-                            flags=42)
-        func2 = FakeLLObject(_fake_class=Func,
-                             argtypes=[types.sint, types.double],
-                             restype=types.sint,
-                             flags=43)
-        #
-        ffi_slong = types.slong
-        dyn_123_field = cpu.fielddescrof_dynamic(offset=123,
-                                                 fieldsize=types.slong.c_size,
-                                                 is_pointer=False,
-                                                 is_float=False,
-                                                 is_signed=True)
-        #
-        def calldescr(cpu, FUNC, oopspecindex, extraeffect=None):
-            if extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
-                f = None   # means "can force all" really
-            else:
-                f = []
-            einfo = EffectInfo(f, f, f, f, oopspecindex=oopspecindex,
-                               extraeffect=extraeffect)
-            return cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, einfo)
-        #
-        libffi_prepare =  calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_PREPARE)
-        libffi_push_arg = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_PUSH_ARG)
-        libffi_call =     calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_CALL,
-                                    EffectInfo.EF_RANDOM_EFFECTS)
-        libffi_struct_getfield = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_STRUCT_GETFIELD)
-        libffi_struct_setfield = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_STRUCT_SETFIELD)
-    
-    namespace = namespace.__dict__
-
-    # ----------------------------------------------------------------------
-    # this group of tests is the most important, as they represent the "real"
-    # cases you actually get when using rlib.libffi
-    
-    def test_ffi_call_opt(self):
-        ops = """
-        [i0, f1]
-        call(0, ConstPtr(func),                       descr=libffi_prepare)
-        call(0, ConstPtr(func), i0,                   descr=libffi_push_arg)
-        call(0, ConstPtr(func), f1,                   descr=libffi_push_arg)
-        i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
-        guard_not_forced() []
-        guard_no_exception() []
-        jump(i3, f1)
-        """
-        expected = """
-        [i0, f1]
-        i3 = call_release_gil(12345, i0, f1, descr=int_float__int_42)
-        guard_not_forced() []
-        guard_no_exception() []
-        jump(i3, f1)
-        """
-        loop = self.optimize_loop(ops, expected)
-
-    def test_ffi_call_nonconst(self):
-        ops = """
-        [i0, f1, p2]
-        call(0, p2,                       descr=libffi_prepare)
-        call(0, p2, i0,                   descr=libffi_push_arg)
-        call(0, p2, f1,                   descr=libffi_push_arg)
-        i3 = call_may_force(0, p2, 12345, descr=libffi_call)
-        guard_not_forced() []
-        guard_no_exception() []
-        jump(i3, f1, p2)
-        """
-        expected = ops
-        loop = self.optimize_loop(ops, expected)
-
-    def test_handle_virtualizables(self):
-        # this test needs an explanation to understand what goes on: see the
-        # comment in optimize_FORCE_TOKEN
-        ops = """
-        [i0, f1, p2]
-        call(0, ConstPtr(func),                       descr=libffi_prepare)
-        call(0, ConstPtr(func), i0,                   descr=libffi_push_arg)
-        call(0, ConstPtr(func), f1,                   descr=libffi_push_arg)
-        i4 = force_token()
-        setfield_gc(p2, i4, descr=vable_token_descr)
-        i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
-        guard_not_forced() [p2]
-        guard_no_exception() [p2]
-        jump(i3, f1, p2)
-        """
-        expected = """
-        [i0, f1, p2]
-        i4 = force_token()
-        setfield_gc(p2, i4, descr=vable_token_descr)
-        i3 = call_release_gil(12345, i0, f1, descr=int_float__int_42)
-        guard_not_forced() [p2]
-        guard_no_exception() [p2]
-        jump(i3, f1, p2)
-        """
-        loop = self.optimize_loop(ops, expected)
-
-    # ----------------------------------------------------------------------
-    # in pratice, the situations described in these tests should never happen,
-    # but we still want to ensure correctness
-
-    def test_rollback_if_op_in_between(self):
-        ops = """
-        [i0, f1]
-        call(0, ConstPtr(func),                       descr=libffi_prepare)
-        call(0, ConstPtr(func), i0,                   descr=libffi_push_arg)
-        i1 = int_add(i0, 1)
-        call(0, ConstPtr(func), f1,                   descr=libffi_push_arg)
-        i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
-        guard_not_forced() []
-        guard_no_exception() []
-        jump(i3, f1)
-        """
-        expected = ops
-        loop = self.optimize_loop(ops, expected)
-
-    def test_rollback_multiple_calls(self):
-        ops = """
-        [i0, i2, f1]
-        call(0, ConstPtr(func),                        descr=libffi_prepare)
-        call(0, ConstPtr(func),  i0,                   descr=libffi_push_arg)
-        #
-        # this is the culprit!
-        call(0, ConstPtr(func2),                       descr=libffi_prepare)
-        #
-        call(0, ConstPtr(func),  f1,                   descr=libffi_push_arg)
-        i3 = call_may_force(0, ConstPtr(func),  12345, descr=libffi_call)
-        guard_not_forced() []
-        guard_no_exception() []
-        call(0, ConstPtr(func2), i0,                   descr=libffi_push_arg)
-        call(0, ConstPtr(func2), f1,                   descr=libffi_push_arg)
-        i4 = call_may_force(0, ConstPtr(func2), 67890, descr=libffi_call)
-        guard_not_forced() []
-        guard_no_exception() []
-        jump(i3, i4, f1)
-        """
-        expected = ops
-        loop = self.optimize_loop(ops, expected)
-
-    def test_rollback_multiple_prepare(self):
-        ops = """
-        [i0, i2, f1]
-        call(0, ConstPtr(func),                        descr=libffi_prepare)
-        #
-        # this is the culprit!
-        call(0, ConstPtr(func2),                       descr=libffi_prepare)
-        #
-        call(0, ConstPtr(func),  i0,                   descr=libffi_push_arg)
-        call(0, ConstPtr(func),  f1,                   descr=libffi_push_arg)
-        i3 = call_may_force(0, ConstPtr(func),  12345, descr=libffi_call)
-        guard_not_forced() []
-        guard_no_exception() []
-        call(0, ConstPtr(func2), i0,                   descr=libffi_push_arg)
-        call(0, ConstPtr(func2), f1,                   descr=libffi_push_arg)
-        i4 = call_may_force(0, ConstPtr(func2), 67890, descr=libffi_call)
-        guard_not_forced() []
-        guard_no_exception() []
-        jump(i3, i4, f1)
-        """
-        expected = ops
-        loop = self.optimize_loop(ops, expected)
-
-    def test_optimize_nested_call(self):
-        ops = """
-        [i0, i2, f1]
-        call(0, ConstPtr(func),                        descr=libffi_prepare)
-        #
-        # this "nested" call is nicely optimized
-        call(0, ConstPtr(func2),                       descr=libffi_prepare)
-        call(0, ConstPtr(func2), i0,                   descr=libffi_push_arg)
-        call(0, ConstPtr(func2), f1,                   descr=libffi_push_arg)
-        i4 = call_may_force(0, ConstPtr(func2), 67890, descr=libffi_call)
-        guard_not_forced() []
-        guard_no_exception() []
-        #
-        call(0, ConstPtr(func),  i0,                   descr=libffi_push_arg)
-        call(0, ConstPtr(func),  f1,                   descr=libffi_push_arg)
-        i3 = call_may_force(0, ConstPtr(func),  12345, descr=libffi_call)
-        guard_not_forced() []
-        guard_no_exception() []
-        jump(i3, i4, f1)
-        """
-        expected = """
-        [i0, i2, f1]
-        call(0, ConstPtr(func),                        descr=libffi_prepare)
-        #
-        # this "nested" call is nicely optimized
-        i4 = call_release_gil(67890, i0, f1, descr=int_float__int_43)
-        guard_not_forced() []
-        guard_no_exception() []
-        #
-        call(0, ConstPtr(func),  i0,                   descr=libffi_push_arg)
-        call(0, ConstPtr(func),  f1,                   descr=libffi_push_arg)
-        i3 = call_may_force(0, ConstPtr(func),  12345, descr=libffi_call)
-        guard_not_forced() []
-        guard_no_exception() []
-        jump(i3, i4, f1)
-        """
-        loop = self.optimize_loop(ops, expected)
-
-    def test_rollback_force_token(self):
-        ops = """
-        [i0, f1, p2]
-        call(0, ConstPtr(func),                       descr=libffi_prepare)
-        call(0, ConstPtr(func), i0,                   descr=libffi_push_arg)
-        call(0, ConstPtr(func), f1,                   descr=libffi_push_arg)
-        i4 = force_token()
-        i5 = int_add(i0, 1) # culprit!
-        setfield_gc(p2, i4, descr=vable_token_descr)
-        i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
-        guard_not_forced() [p2]
-        guard_no_exception() [p2]
-        jump(i3, f1, p2)
-        """
-        expected = ops
-        loop = self.optimize_loop(ops, expected)
-
-    def test_allow_setfields_in_between(self):
-        ops = """
-        [i0, f1, p2]
-        call(0, ConstPtr(func),                       descr=libffi_prepare)
-        call(0, ConstPtr(func), i0,                   descr=libffi_push_arg)
-        call(0, ConstPtr(func), f1,                   descr=libffi_push_arg)
-        setfield_gc(p2, i0,                           descr=valuedescr)
-        i3 = call_may_force(0, ConstPtr(func), 12345, descr=libffi_call)
-        guard_not_forced() []
-        guard_no_exception() []
-        jump(i3, f1, p2)
-        """
-        expected = """
-        [i0, f1, p2]
-        setfield_gc(p2, i0, descr=valuedescr)
-        i3 = call_release_gil(12345, i0, f1, descr=int_float__int_42)
-        guard_not_forced() []
-        guard_no_exception() []
-        jump(i3, f1, p2)
-        """
-        loop = self.optimize_loop(ops, expected)
-
-    def test_ffi_struct_fields(self):
-        ops = """
-        [i0]
-        i1 = call(0, ConstClass(ffi_slong), i0, 123, descr=libffi_struct_getfield)
-        i2 = int_add(i1, 1)
-        call(0, ConstClass(ffi_slong), i0, 123, i2, descr=libffi_struct_setfield)
-        jump(i1)
-        """
-        expected = """
-        [i0]
-        i1 = getfield_raw(i0, descr=dyn_123_field)
-        i2 = int_add(i1, 1)
-        setfield_raw(i0, i2, descr=dyn_123_field)
-        jump(i1)
-        """
-        loop = self.optimize_loop(ops, expected)
-
-    def test_ffi_struct_fields_nonconst(self):
-        ops = """
-        [i0, i1]
-        i2 = call(0, ConstClass(ffi_slong), i0, i1,  descr=libffi_struct_getfield)
-        i3 = call(0, i1                   , i0, 123, descr=libffi_struct_getfield)
-        jump(i1)
-        """
-        expected = ops
-        loop = self.optimize_loop(ops, expected)
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -41,14 +41,6 @@
     #
     chain, _ = build_opt_chain(metainterp_sd, "aaa:bbb")
     check(chain, ["OptSimplify"])
-    #
-    chain, _ = build_opt_chain(metainterp_sd, "ffi")
-    check(chain, ["OptFfiCall", "OptSimplify"])
-    #
-    metainterp_sd.config = get_pypy_config(translating=True)
-    assert not metainterp_sd.config.translation.jit_ffi
-    chain, _ = build_opt_chain(metainterp_sd, "ffi")
-    check(chain, ["OptSimplify"])
 
 
 # ____________________________________________________________
@@ -7862,6 +7854,84 @@
         """
         self.optimize_loop(ops, expected)
 
+    def test_only_strengthen_guard_if_class_matches(self):
+        ops = """
+        [p1]
+        guard_class(p1, ConstClass(node_vtable2)) []
+        guard_value(p1, ConstPtr(myptr)) []
+        jump(p1)
+        """
+        self.raises(InvalidLoop, self.optimize_loop,
+                       ops, ops)
+
+    def test_licm_boxed_opaque_getitem(self):
+        ops = """
+        [p1]
+        p2 = getfield_gc(p1, descr=nextdescr) 
+        mark_opaque_ptr(p2)        
+        guard_class(p2,  ConstClass(node_vtable)) []
+        i3 = getfield_gc(p2, descr=otherdescr)
+        i4 = call(i3, descr=nonwritedescr)
+        jump(p1)
+        """
+        expected = """
+        [p1, i3]
+        i4 = call(i3, descr=nonwritedescr)
+        jump(p1, i3)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_licm_boxed_opaque_getitem_unknown_class(self):
+        ops = """
+        [p1]
+        p2 = getfield_gc(p1, descr=nextdescr) 
+        mark_opaque_ptr(p2)        
+        i3 = getfield_gc(p2, descr=otherdescr)
+        i4 = call(i3, descr=nonwritedescr)
+        jump(p1)
+        """
+        expected = """
+        [p1, p2]
+        i3 = getfield_gc(p2, descr=otherdescr)
+        i4 = call(i3, descr=nonwritedescr)
+        jump(p1, p2)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_licm_unboxed_opaque_getitem(self):
+        ops = """
+        [p2]
+        mark_opaque_ptr(p2)        
+        guard_class(p2,  ConstClass(node_vtable)) []
+        i3 = getfield_gc(p2, descr=otherdescr)
+        i4 = call(i3, descr=nonwritedescr)
+        jump(p2)
+        """
+        expected = """
+        [p1, i3]
+        i4 = call(i3, descr=nonwritedescr)
+        jump(p1, i3)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_licm_unboxed_opaque_getitem_unknown_class(self):
+        ops = """
+        [p2]
+        mark_opaque_ptr(p2)        
+        i3 = getfield_gc(p2, descr=otherdescr)
+        i4 = call(i3, descr=nonwritedescr)
+        jump(p2)
+        """
+        expected = """
+        [p2]
+        i3 = getfield_gc(p2, descr=otherdescr) 
+        i4 = call(i3, descr=nonwritedescr)
+        jump(p2)
+        """
+        self.optimize_loop(ops, expected)
+
+
+
 class TestLLtype(OptimizeOptTest, LLtypeMixin):
     pass
 
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_util.py b/pypy/jit/metainterp/optimizeopt/test/test_util.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_util.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_util.py
@@ -346,7 +346,6 @@
         self.options = Fake()
         self.globaldata = Fake()
         self.config = get_pypy_config(translating=True)
-        self.config.translation.jit_ffi = True
 
     class logger_noopt:
         @classmethod
diff --git a/pypy/jit/metainterp/optimizeopt/unroll.py b/pypy/jit/metainterp/optimizeopt/unroll.py
--- a/pypy/jit/metainterp/optimizeopt/unroll.py
+++ b/pypy/jit/metainterp/optimizeopt/unroll.py
@@ -120,9 +120,9 @@
                 limit = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.retrace_limit
                 if cell_token.retraced_count < limit:
                     cell_token.retraced_count += 1
-                    #debug_print('Retracing (%d/%d)' % (cell_token.retraced_count, limit))
+                    debug_print('Retracing (%d/%d)' % (cell_token.retraced_count, limit))
                 else:
-                    #debug_print("Retrace count reached, jumping to preamble")
+                    debug_print("Retrace count reached, jumping to preamble")
                     assert cell_token.target_tokens[0].virtual_state is None
                     jumpop.setdescr(cell_token.target_tokens[0])
                     self.optimizer.send_extra_operation(jumpop)
@@ -341,6 +341,12 @@
             op = self.short[i]
             newop = self.short_inliner.inline_op(op)
             self.optimizer.send_extra_operation(newop)
+            if op.result in self.short_boxes.assumed_classes:
+                classbox = self.getvalue(newop.result).get_constant_class(self.optimizer.cpu)
+                assumed_classbox = self.short_boxes.assumed_classes[op.result]
+                if not classbox or not classbox.same_constant(assumed_classbox):
+                    raise InvalidLoop('Class of opaque pointer needed in short ' +
+                                      'preamble unknown at end of loop')
             i += 1
 
         # Import boxes produced in the preamble but used in the loop
@@ -432,9 +438,13 @@
                 newargs[i] = a.clonebox()
                 boxmap[a] = newargs[i]
         inliner = Inliner(short_inputargs, newargs)
+        target_token.assumed_classes = {}
         for i in range(len(short)):
-            short[i] = inliner.inline_op(short[i])
-
+            op = short[i]
+            newop = inliner.inline_op(op)
+            if op.result and op.result in self.short_boxes.assumed_classes:
+                target_token.assumed_classes[newop.result] = self.short_boxes.assumed_classes[op.result]
+            short[i] = newop
         target_token.resume_at_jump_descr = target_token.resume_at_jump_descr.clone_if_mutable()
         inliner.inline_descr_inplace(target_token.resume_at_jump_descr)
 
@@ -588,6 +598,12 @@
                     for shop in target.short_preamble[1:]:
                         newop = inliner.inline_op(shop)
                         self.optimizer.send_extra_operation(newop)
+                        if shop.result in target.assumed_classes:
+                            classbox = self.getvalue(newop.result).get_constant_class(self.optimizer.cpu)
+                            if not classbox or not classbox.same_constant(target.assumed_classes[shop.result]):
+                                raise InvalidLoop('The class of an opaque pointer at the end ' +
+                                                  'of the bridge does not mach the class ' + 
+                                                  'it has at the start of the target loop')
                 except InvalidLoop:
                     #debug_print("Inlining failed unexpectedly",
                     #            "jumping to preamble instead")
diff --git a/pypy/jit/metainterp/optimizeopt/virtualstate.py b/pypy/jit/metainterp/optimizeopt/virtualstate.py
--- a/pypy/jit/metainterp/optimizeopt/virtualstate.py
+++ b/pypy/jit/metainterp/optimizeopt/virtualstate.py
@@ -288,7 +288,8 @@
 
 
 class NotVirtualStateInfo(AbstractVirtualStateInfo):
-    def __init__(self, value):
+    def __init__(self, value, is_opaque=False):
+        self.is_opaque = is_opaque
         self.known_class = value.known_class
         self.level = value.level
         if value.intbound is None:
@@ -357,6 +358,9 @@
         if self.lenbound or other.lenbound:
             raise InvalidLoop('The array length bounds does not match.')
 
+        if self.is_opaque:
+            raise InvalidLoop('Generating guards for opaque pointers is not safe')
+
         if self.level == LEVEL_KNOWNCLASS and \
            box.nonnull() and \
            self.known_class.same_constant(cpu.ts.cls_of_box(box)):
@@ -560,7 +564,8 @@
         return VirtualState([self.state(box) for box in jump_args])
 
     def make_not_virtual(self, value):
-        return NotVirtualStateInfo(value)
+        is_opaque = value in self.optimizer.opaque_pointers
+        return NotVirtualStateInfo(value, is_opaque)
 
     def make_virtual(self, known_class, fielddescrs):
         return VirtualStateInfo(known_class, fielddescrs)
@@ -585,6 +590,7 @@
         self.rename = {}
         self.optimizer = optimizer
         self.availible_boxes = availible_boxes
+        self.assumed_classes = {}
 
         if surviving_boxes is not None:
             for box in surviving_boxes:
@@ -678,6 +684,12 @@
             raise BoxNotProducable
 
     def add_potential(self, op, synthetic=False):
+        if op.result and op.result in self.optimizer.values:
+            value = self.optimizer.values[op.result]
+            if value in self.optimizer.opaque_pointers:
+                classbox = value.get_constant_class(self.optimizer.cpu)
+                if classbox:
+                    self.assumed_classes[op.result] = classbox
         if op.result not in self.potential_ops:
             self.potential_ops[op.result] = op
         else:
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -222,7 +222,7 @@
                     'float_neg', 'float_abs',
                     'cast_ptr_to_int', 'cast_int_to_ptr',
                     'convert_float_bytes_to_longlong',
-                    'convert_longlong_bytes_to_float',
+                    'convert_longlong_bytes_to_float', 'int_force_ge_zero',
                     ]:
         exec py.code.Source('''
             @arguments("box")
@@ -451,12 +451,27 @@
     opimpl_getarrayitem_raw_f = _opimpl_getarrayitem_raw_any
 
     @arguments("box", "descr", "box")
+    def _opimpl_getarrayitem_raw_pure_any(self, arraybox,arraydescr, indexbox):
+        return self.execute_with_descr(rop.GETARRAYITEM_RAW_PURE,
+                                       arraydescr, arraybox, indexbox)
+
+    opimpl_getarrayitem_raw_i_pure = _opimpl_getarrayitem_raw_pure_any
+    opimpl_getarrayitem_raw_f_pure = _opimpl_getarrayitem_raw_pure_any
+
+    @arguments("box", "descr", "box")
     def _opimpl_getarrayitem_gc_pure_any(self, arraybox, arraydescr, indexbox):
+        if isinstance(arraybox, ConstPtr) and isinstance(indexbox, ConstInt):
+            # if the arguments are directly constants, bypass the heapcache
+            # completely
+            resbox = executor.execute(self.metainterp.cpu, self.metainterp,
+                                      rop.GETARRAYITEM_GC_PURE, arraydescr,
+                                      arraybox, indexbox)
+            return resbox.constbox()
         return self._do_getarrayitem_gc_any(rop.GETARRAYITEM_GC_PURE, arraybox, arraydescr, indexbox)
 
-    opimpl_getarrayitem_gc_pure_i = _opimpl_getarrayitem_gc_pure_any
-    opimpl_getarrayitem_gc_pure_r = _opimpl_getarrayitem_gc_pure_any
-    opimpl_getarrayitem_gc_pure_f = _opimpl_getarrayitem_gc_pure_any
+    opimpl_getarrayitem_gc_i_pure = _opimpl_getarrayitem_gc_pure_any
+    opimpl_getarrayitem_gc_r_pure = _opimpl_getarrayitem_gc_pure_any
+    opimpl_getarrayitem_gc_f_pure = _opimpl_getarrayitem_gc_pure_any
 
     @arguments("box", "descr", "box", "box")
     def _opimpl_setarrayitem_gc_any(self, arraybox, arraydescr,
@@ -563,6 +578,11 @@
 
     @arguments("box", "descr")
     def _opimpl_getfield_gc_pure_any(self, box, fielddescr):
+        if isinstance(box, ConstPtr):
+            # if 'box' is directly a ConstPtr, bypass the heapcache completely
+            resbox = executor.execute(self.metainterp.cpu, self.metainterp,
+                                      rop.GETFIELD_GC_PURE, fielddescr, box)
+            return resbox.constbox()
         return self._opimpl_getfield_gc_any_pureornot(
                 rop.GETFIELD_GC_PURE, box, fielddescr)
     opimpl_getfield_gc_i_pure = _opimpl_getfield_gc_pure_any
@@ -647,6 +667,20 @@
     opimpl_setfield_raw_r = _opimpl_setfield_raw_any
     opimpl_setfield_raw_f = _opimpl_setfield_raw_any
 
+    @arguments("box", "box", "descr", "box")
+    def _opimpl_raw_store(self, addrbox, offsetbox, arraydescr, valuebox):
+        self.execute_with_descr(rop.RAW_STORE, arraydescr,
+                                addrbox, offsetbox, valuebox)
+    opimpl_raw_store_i = _opimpl_raw_store
+    opimpl_raw_store_f = _opimpl_raw_store
+
+    @arguments("box", "box", "descr")
+    def _opimpl_raw_load(self, addrbox, offsetbox, arraydescr):
+        return self.execute_with_descr(rop.RAW_LOAD, arraydescr,
+                                       addrbox, offsetbox)
+    opimpl_raw_load_i = _opimpl_raw_load
+    opimpl_raw_load_f = _opimpl_raw_load
+
     @arguments("box", "descr", "descr", "orgpc")
     def opimpl_record_quasiimmut_field(self, box, fielddescr,
                                        mutatefielddescr, orgpc):
@@ -1368,6 +1402,8 @@
             if vablebox is not None:
                 self.metainterp.history.record(rop.KEEPALIVE, [vablebox], None)
             self.metainterp.handle_possible_exception()
+            if effectinfo.oopspecindex == effectinfo.OS_LIBFFI_CALL:
+                self.metainterp.direct_libffi_call()
             return resbox
         else:
             effect = effectinfo.extraeffect
@@ -1462,6 +1498,7 @@
         self.jitdrivers_sd = codewriter.callcontrol.jitdrivers_sd
         self.virtualref_info = codewriter.callcontrol.virtualref_info
         self.callinfocollection = codewriter.callcontrol.callinfocollection
+        self.has_libffi_call = codewriter.callcontrol.has_libffi_call
         #
         # store this information for fastpath of call_assembler
         # (only the paths that can actually be taken)
@@ -2511,6 +2548,89 @@
         else:
             return None
 
+    def direct_libffi_call(self):
+        """Generate a direct call to C code, patching the CALL_MAY_FORCE
+        to jit_ffi_call() that occurred just now.
+        """
+        # an 'assert' that constant-folds away the rest of this function
+        # if the codewriter didn't produce any OS_LIBFFI_CALL at all.
+        assert self.staticdata.has_libffi_call
+        #
+        from pypy.rpython.lltypesystem import llmemory
+        from pypy.rlib.jit_libffi import CIF_DESCRIPTION_P
+        from pypy.jit.backend.llsupport.ffisupport import get_arg_descr
+        #
+        num_extra_guards = 0
+        while True:
+            op = self.history.operations[-1-num_extra_guards]
+            if op.getopnum() == rop.CALL_MAY_FORCE:
+                break
+            assert op.is_guard()
+            num_extra_guards += 1
+        #
+        box_cif_description = op.getarg(1)
+        if not isinstance(box_cif_description, ConstInt):
+            return
+        cif_description = box_cif_description.getint()
+        cif_description = llmemory.cast_int_to_adr(cif_description)
+        cif_description = llmemory.cast_adr_to_ptr(cif_description,
+                                                   CIF_DESCRIPTION_P)
+        extrainfo = op.getdescr().get_extra_info()
+        calldescr = self.cpu.calldescrof_dynamic(cif_description, extrainfo)
+        if calldescr is None:
+            return
+        #
+        extra_guards = []
+        for i in range(num_extra_guards):
+            extra_guards.append(self.history.operations.pop())
+        extra_guards.reverse()
+        #
+        box_exchange_buffer = op.getarg(3)
+        self.history.operations.pop()
+        arg_boxes = []
+        for i in range(cif_description.nargs):
+            kind, descr = get_arg_descr(self.cpu, cif_description.atypes[i])
+            if kind == 'i':
+                box_arg = history.BoxInt()
+            elif kind == 'f':
+                box_arg = history.BoxFloat()
+            else:
+                assert kind == 'v'
+                continue
+            ofs = cif_description.exchange_args[i]
+            box_argpos = history.BoxInt()
+            self.history.record(rop.INT_ADD,
+                                [box_exchange_buffer, ConstInt(ofs)],
+                                box_argpos)
+            self.history.record(rop.GETARRAYITEM_RAW,
+                                [box_argpos, ConstInt(0)],
+                                box_arg, descr)
+            arg_boxes.append(box_arg)
+        #
+        kind, descr = get_arg_descr(self.cpu, cif_description.rtype)
+        if kind == 'i':
+            box_result = history.BoxInt()
+        elif kind == 'f':
+            box_result = history.BoxFloat()
+        else:
+            assert kind == 'v'
+            box_result = None
+        self.history.record(rop.CALL_RELEASE_GIL,
+                            [op.getarg(2)] + arg_boxes,
+                            box_result, calldescr)
+        #
+        self.history.operations.extend(extra_guards)
+        #
+        if box_result is not None:
+            ofs = cif_description.exchange_result
+            box_resultpos = history.BoxInt()
+            self.history.record(rop.INT_ADD,
+                                [box_exchange_buffer, ConstInt(ofs)],
+                                box_resultpos)
+            self.history.record(rop.SETARRAYITEM_RAW,
+                                [box_resultpos, ConstInt(0), box_result],
+                                None, descr)
+
 # ____________________________________________________________
 
 class ChangeFrame(JitException):
diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py
--- a/pypy/jit/metainterp/resoperation.py
+++ b/pypy/jit/metainterp/resoperation.py
@@ -443,6 +443,7 @@
     'INT_IS_TRUE/1b',
     'INT_NEG/1',
     'INT_INVERT/1',
+    'INT_FORCE_GE_ZERO/1',
     #
     'SAME_AS/1',      # gets a Const or a Box, turns it into another Box
     'CAST_PTR_TO_INT/1',
@@ -459,6 +460,7 @@
     'GETFIELD_GC_PURE/1d',
     'GETFIELD_RAW_PURE/1d',
     'GETARRAYITEM_GC_PURE/2d',
+    'GETARRAYITEM_RAW_PURE/2d',
     'UNICODELEN/1',
     'UNICODEGETITEM/2',
     #
@@ -471,7 +473,7 @@
     'GETARRAYITEM_GC/2d',
     'GETARRAYITEM_RAW/2d',
     'GETINTERIORFIELD_GC/2d',
-    'GETINTERIORFIELD_RAW/2d',
+    'RAW_LOAD/2d',
     'GETFIELD_GC/1d',
     'GETFIELD_RAW/1d',
     '_MALLOC_FIRST',
@@ -490,7 +492,8 @@
     'SETARRAYITEM_GC/3d',
     'SETARRAYITEM_RAW/3d',
     'SETINTERIORFIELD_GC/3d',
-    'SETINTERIORFIELD_RAW/3d',
+    'SETINTERIORFIELD_RAW/3d',    # only used by llsupport/rewrite.py
+    'RAW_STORE/3d',
     'SETFIELD_GC/2d',
     'SETFIELD_RAW/2d',
     'STRSETITEM/3',
diff --git a/pypy/jit/metainterp/resume.py b/pypy/jit/metainterp/resume.py
--- a/pypy/jit/metainterp/resume.py
+++ b/pypy/jit/metainterp/resume.py
@@ -10,6 +10,7 @@
 from pypy.rpython import annlowlevel
 from pypy.rlib import rarithmetic, rstack
 from pypy.rlib.objectmodel import we_are_translated, specialize
+from pypy.rlib.objectmodel import compute_unique_id
 from pypy.rlib.debug import have_debug_prints, ll_assert
 from pypy.rlib.debug import debug_start, debug_stop, debug_print
 from pypy.jit.metainterp.optimize import InvalidLoop
@@ -493,7 +494,7 @@
         return self.setfields(decoder, struct)
 
     def debug_prints(self):
-        debug_print("\tvirtualinfo", self.known_class.repr_rpython())
+        debug_print("\tvirtualinfo", self.known_class.repr_rpython(), " at ",  compute_unique_id(self))
         AbstractVirtualStructInfo.debug_prints(self)
 
 
@@ -509,7 +510,7 @@
         return self.setfields(decoder, struct)
 
     def debug_prints(self):
-        debug_print("\tvstructinfo", self.typedescr.repr_rpython())
+        debug_print("\tvstructinfo", self.typedescr.repr_rpython(), " at ",  compute_unique_id(self))
         AbstractVirtualStructInfo.debug_prints(self)
 
 class VArrayInfo(AbstractVirtualInfo):
@@ -539,7 +540,7 @@
         return array
 
     def debug_prints(self):
-        debug_print("\tvarrayinfo", self.arraydescr)
+        debug_print("\tvarrayinfo", self.arraydescr, " at ",  compute_unique_id(self))
         for i in self.fieldnums:
             debug_print("\t\t", str(untag(i)))
 
@@ -550,7 +551,7 @@
         self.fielddescrs = fielddescrs
 
     def debug_prints(self):
-        debug_print("\tvarraystructinfo", self.arraydescr)
+        debug_print("\tvarraystructinfo", self.arraydescr, " at ",  compute_unique_id(self))
         for i in self.fieldnums:
             debug_print("\t\t", str(untag(i)))
 
@@ -581,7 +582,7 @@
         return string
 
     def debug_prints(self):
-        debug_print("\tvstrplaininfo length", len(self.fieldnums))
+        debug_print("\tvstrplaininfo length", len(self.fieldnums), " at ",  compute_unique_id(self))
 
 
 class VStrConcatInfo(AbstractVirtualInfo):
@@ -599,7 +600,7 @@
         return string
 
     def debug_prints(self):
-        debug_print("\tvstrconcatinfo")
+        debug_print("\tvstrconcatinfo at ",  compute_unique_id(self))
         for i in self.fieldnums:
             debug_print("\t\t", str(untag(i)))
 
@@ -615,7 +616,7 @@
         return string
 
     def debug_prints(self):
-        debug_print("\tvstrsliceinfo")
+        debug_print("\tvstrsliceinfo at ",  compute_unique_id(self))
         for i in self.fieldnums:
             debug_print("\t\t", str(untag(i)))
 
@@ -636,7 +637,7 @@
         return string
 
     def debug_prints(self):
-        debug_print("\tvuniplaininfo length", len(self.fieldnums))
+        debug_print("\tvuniplaininfo length", len(self.fieldnums), " at ",  compute_unique_id(self))
 
 
 class VUniConcatInfo(AbstractVirtualInfo):
@@ -654,7 +655,7 @@
         return string
 
     def debug_prints(self):
-        debug_print("\tvuniconcatinfo")
+        debug_print("\tvuniconcatinfo at ",  compute_unique_id(self))
         for i in self.fieldnums:
             debug_print("\t\t", str(untag(i)))
 
@@ -671,7 +672,7 @@
         return string
 
     def debug_prints(self):
-        debug_print("\tvunisliceinfo")
+        debug_print("\tvunisliceinfo at ",  compute_unique_id(self))
         for i in self.fieldnums:
             debug_print("\t\t", str(untag(i)))
 
@@ -1280,7 +1281,6 @@
 
 def dump_storage(storage, liveboxes):
     "For profiling only."
-    from pypy.rlib.objectmodel import compute_unique_id
     debug_start("jit-resume")
     if have_debug_prints():
         debug_print('Log storage', compute_unique_id(storage))
@@ -1313,4 +1313,13 @@
                     debug_print('\t\t', 'None')
                 else:
                     virtual.debug_prints()
+        if storage.rd_pendingfields:
+            debug_print('\tpending setfields')
+            for i in range(len(storage.rd_pendingfields)):
+                lldescr  = storage.rd_pendingfields[i].lldescr
+                num      = storage.rd_pendingfields[i].num
+                fieldnum = storage.rd_pendingfields[i].fieldnum
+                itemindex= storage.rd_pendingfields[i].itemindex
+                debug_print("\t\t", str(lldescr), str(untag(num)), str(untag(fieldnum)), itemindex)
+
     debug_stop("jit-resume")
diff --git a/pypy/jit/metainterp/test/support.py b/pypy/jit/metainterp/test/support.py
--- a/pypy/jit/metainterp/test/support.py
+++ b/pypy/jit/metainterp/test/support.py
@@ -42,6 +42,9 @@
         trace_limit = sys.maxint
         enable_opts = ALL_OPTS_DICT
 
+    if kwds.pop('disable_optimizations', False):
+        FakeWarmRunnerState.enable_opts = {}
+
     func._jit_unroll_safe_ = True
     rtyper = support.annotate(func, values, type_system=type_system,
                               translationoptions=translationoptions)
diff --git a/pypy/jit/metainterp/test/test_ajit.py b/pypy/jit/metainterp/test/test_ajit.py
--- a/pypy/jit/metainterp/test/test_ajit.py
+++ b/pypy/jit/metainterp/test/test_ajit.py
@@ -3797,6 +3797,7 @@
         assert res == 3
 
     def test_float_bytes(self):
+        from pypy.rlib.rfloat import isnan
         def f(n):
             ll = float2longlong(n)
             return longlong2float(ll)
@@ -3804,7 +3805,7 @@
         for x in [2.5, float("nan"), -2.5, float("inf")]:
             # There are tests elsewhere to verify the correctness of this.
             res = self.interp_operations(f, [x])
-            assert res == x or math.isnan(x) and math.isnan(res)
+            assert res == x or isnan(x) and isnan(res)
 
 
 class TestLLtype(BaseLLtypeTests, LLJitMixin):
diff --git a/pypy/jit/metainterp/test/test_dict.py b/pypy/jit/metainterp/test/test_dict.py
--- a/pypy/jit/metainterp/test/test_dict.py
+++ b/pypy/jit/metainterp/test/test_dict.py
@@ -161,6 +161,22 @@
                            'guard_no_exception': 8, 'new': 2,
                            'guard_false': 2, 'int_is_true': 2})
 
+    def test_unrolling_of_dict_iter(self):
+        driver = JitDriver(greens = [], reds = ['n'])
+        
+        def f(n):
+            while n > 0:
+                driver.jit_merge_point(n=n)
+                d = {1: 1}
+                for elem in d:
+                    n -= elem
+            return n
+
+        res = self.meta_interp(f, [10], listops=True)
+        assert res == 0
+        self.check_simple_loop({'int_sub': 1, 'int_gt': 1, 'guard_true': 1,
+                                'jump': 1})
+
 
 class TestOOtype(DictTests, OOJitMixin):
     pass
diff --git a/pypy/jit/metainterp/test/test_fficall.py b/pypy/jit/metainterp/test/test_fficall.py
--- a/pypy/jit/metainterp/test/test_fficall.py
+++ b/pypy/jit/metainterp/test/test_fficall.py
@@ -1,210 +1,106 @@
-from __future__ import with_statement
 import py
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.jit.metainterp.test.support import LLJitMixin
+from pypy.rlib import jit
+from pypy.rlib.jit_libffi import types, CIF_DESCRIPTION, FFI_TYPE_PP
+from pypy.rlib.unroll import unrolling_iterable
 
-from pypy.jit.metainterp.test.support import LLJitMixin
-from pypy.rlib.jit import JitDriver, promote, dont_look_inside
-from pypy.rlib.libffi import (ArgChain, IS_32_BIT, array_getitem, array_setitem,
-    types, struct_setfield_int, struct_getfield_int)
-from pypy.rlib.objectmodel import specialize
-from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong
-from pypy.rlib.test.test_libffi import TestLibffiCall as _TestLibffiCall
-from pypy.rlib.unroll import unrolling_iterable
-from pypy.rpython.lltypesystem import lltype, rffi
-from pypy.tool.sourcetools import func_with_new_name
 
+def get_description(atypes, rtype):
+    p = lltype.malloc(CIF_DESCRIPTION, len(atypes),
+                      flavor='raw', immortal=True)
+    p.abi = 42
+    p.nargs = len(atypes)
+    p.rtype = rtype
+    p.atypes = lltype.malloc(FFI_TYPE_PP.TO, len(atypes),
+                             flavor='raw', immortal=True)
+    for i in range(len(atypes)):
+        p.atypes[i] = atypes[i]
+    return p
 
-class FfiCallTests(_TestLibffiCall):
-    # ===> ../../../rlib/test/test_libffi.py
 
-    def call(self, funcspec, args, RESULT, is_struct=False, jitif=[]):
-        """
-        Call the function specified by funcspec in a loop, and let the jit to
-        see and optimize it.
-        """
-        #
-        lib, name, argtypes, restype = funcspec
-        method_and_args = []
-        for argval in args:
-            if isinstance(argval, tuple):
-                method_name, argval = argval
+class FfiCallTests(object):
+
+    def _run(self, atypes, rtype, avalues, rvalue):
+        cif_description = get_description(atypes, rtype)
+
+        def verify(*args):
+            assert args == tuple(avalues)
+            return rvalue
+        FUNC = lltype.FuncType([lltype.typeOf(avalue) for avalue in avalues],
+                               lltype.typeOf(rvalue))
+        func = lltype.functionptr(FUNC, 'verify', _callable=verify)
+        func_addr = rffi.cast(rffi.VOIDP, func)
+
+        for i in range(len(avalues)):
+            cif_description.exchange_args[i] = (i+1) * 16
+        cif_description.exchange_result = (len(avalues)+1) * 16
+
+        unroll_avalues = unrolling_iterable(avalues)
+
+        @jit.oopspec("libffi_call(cif_description,func_addr,exchange_buffer)")
+        def fake_call(cif_description, func_addr, exchange_buffer):
+            ofs = 16
+            for avalue in unroll_avalues:
+                TYPE = rffi.CArray(lltype.typeOf(avalue))
+                data = rffi.ptradd(exchange_buffer, ofs)
+                assert rffi.cast(lltype.Ptr(TYPE), data)[0] == avalue
+                ofs += 16
+            if rvalue is not None:
+                write_rvalue = rvalue
             else:
-                method_name = 'arg'
-            method_and_args.append((method_name, argval))
-        method_and_args = unrolling_iterable(method_and_args)
-        #
-        reds = ['n', 'res', 'func']
-        if (RESULT is rffi.DOUBLE or
-            IS_32_BIT and RESULT in [rffi.LONGLONG, rffi.ULONGLONG]):
-            reds = ['n', 'func', 'res'] # 'double' floats must be *after* refs
-        driver = JitDriver(reds=reds, greens=[])
-        init_result = rffi.cast(RESULT, 0)
-        #
-        def g(func):
-            # a different function, which is marked as "dont_look_inside"
-            # in case it uses an unsupported argument
-            argchain = ArgChain()
-            # this loop is unrolled
-            for method_name, argval in method_and_args:
-                getattr(argchain, method_name)(argval)
-            return func.call(argchain, RESULT, is_struct=is_struct)
-        #
-        def f(n):
-            func = lib.getpointer(name, argtypes, restype)
-            res = init_result
-            while n < 10:
-                driver.jit_merge_point(n=n, res=res, func=func)
-                promote(func)
-                res = g(func)
-                n += 1
+                write_rvalue = 12923  # ignored
+            TYPE = rffi.CArray(lltype.typeOf(write_rvalue))
+            data = rffi.ptradd(exchange_buffer, ofs)
+            rffi.cast(lltype.Ptr(TYPE), data)[0] = write_rvalue
+
+        def f():
+            exbuf = lltype.malloc(rffi.CCHARP.TO, (len(avalues)+2) * 16,
+                                  flavor='raw', zero=True)
+            ofs = 16
+            for avalue in unroll_avalues:
+                TYPE = rffi.CArray(lltype.typeOf(avalue))
+                data = rffi.ptradd(exbuf, ofs)
+                rffi.cast(lltype.Ptr(TYPE), data)[0] = avalue
+                ofs += 16
+
+            fake_call(cif_description, func_addr, exbuf)
+
+            if rvalue is None:
+                res = 654321
+            else:
+                TYPE = rffi.CArray(lltype.typeOf(rvalue))
+                data = rffi.ptradd(exbuf, ofs)
+                res = rffi.cast(lltype.Ptr(TYPE), data)[0]
+            lltype.free(exbuf, flavor='raw')
             return res
-        #
-        res = self.meta_interp(f, [0], backendopt=True,
-                               supports_floats       = self.supports_all,
-                               supports_longlong     = self.supports_all,
-                               supports_singlefloats = self.supports_all)
-        d = {'floats': self.supports_all,
-             'longlong': self.supports_all or not IS_32_BIT,
-             'singlefloats': self.supports_all,
-             'byval': False}
-        supported = all(d[check] for check in jitif)
-        if supported:
-            self.check_resops(
-                call_release_gil=2,   # a CALL_RELEASE_GIL, and no other CALLs
-                call=0,
-                call_may_force=0,
-                guard_no_exception=2,
-                guard_not_forced=2,
-                int_add=2,
-                int_lt=2,
-                guard_true=2,
-                jump=1)
-        else:
-            self.check_resops(
-                call_release_gil=0,   # no CALL_RELEASE_GIL
-                int_add=2,
-                int_lt=2,
-                guard_true=2,
-                jump=1)
-        return res
 
-    def test_byval_result(self):
-        _TestLibffiCall.test_byval_result(self)
-    test_byval_result.__doc__ = _TestLibffiCall.test_byval_result.__doc__
-    test_byval_result.dont_track_allocations = True
+        res = f()
+        assert res == rvalue or (res, rvalue) == (654321, None)
+        res = self.interp_operations(f, [])
+        assert res == rvalue or (res, rvalue) == (654321, None)
+        self.check_operations_history(call_may_force=0,
+                                      call_release_gil=1)
 
-class FfiLookupTests(object):
-    def test_array_fields(self):
-        myjitdriver = JitDriver(
-            greens = [],
-            reds = ["n", "i", "points", "result_point"],
-        )
+    def test_simple_call(self):
+        self._run([types.signed] * 2, types.signed, [456, 789], -42)
 
-        POINT = lltype.Struct("POINT",
-            ("x", lltype.Signed),
-            ("y", lltype.Signed),
-        )
-        def f(points, result_point, n):
-            i = 0
-            while i < n:
-                myjitdriver.jit_merge_point(i=i, points=points, n=n,
-                                            result_point=result_point)
-                x = array_getitem(
-                    types.slong, rffi.sizeof(lltype.Signed) * 2, points, i, 0
-                )
-                y = array_getitem(
-                    types.slong, rffi.sizeof(lltype.Signed) * 2, points, i, rffi.sizeof(lltype.Signed)
-                )
+    def test_many_arguments(self):
+        for i in [0, 6, 20]:
+            self._run([types.signed] * i, types.signed,
+                      [-123456*j for j in range(i)],
+                      -42434445)
 
-                cur_x = array_getitem(
-                    types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, 0
-                )
-                cur_y = array_getitem(
-                    types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, rffi.sizeof(lltype.Signed)
-                )
+    def test_simple_call_float(self):
+        self._run([types.double] * 2, types.double, [45.6, 78.9], -4.2)
 
-                array_setitem(
-                    types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, 0, cur_x + x
-                )
-                array_setitem(
-                    types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, rffi.sizeof(lltype.Signed), cur_y + y
-                )
-                i += 1
+    def test_returns_none(self):
+        self._run([types.signed] * 2, types.void, [456, 789], None)
 
-        def main(n):
-            with lltype.scoped_alloc(rffi.CArray(POINT), n) as points:
-                with lltype.scoped_alloc(rffi.CArray(POINT), 1) as result_point:
-                    for i in xrange(n):
-                        points[i].x = i * 2
-                        points[i].y = i * 2 + 1
-                    points = rffi.cast(rffi.CArrayPtr(lltype.Char), points)
-                    result_point[0].x = 0
-                    result_point[0].y = 0
-                    result_point = rffi.cast(rffi.CArrayPtr(lltype.Char), result_point)
-                    f(points, result_point, n)
-                    result_point = rffi.cast(rffi.CArrayPtr(POINT), result_point)
-                    return result_point[0].x * result_point[0].y
-
-        assert self.meta_interp(main, [10]) == main(10) == 9000
-        self.check_resops({'jump': 1, 'int_lt': 2, 'setinteriorfield_raw': 4,
-                           'getinteriorfield_raw': 8, 'int_add': 6, 'guard_true': 2})
-
-    def _test_getitem_type(self, TYPE, ffitype, COMPUTE_TYPE):
-        reds = ["n", "i", "s", "data"]
-        if COMPUTE_TYPE is lltype.Float:
-            # Move the float var to the back.
-            reds.remove("s")
-            reds.append("s")
-        myjitdriver = JitDriver(
-            greens = [],
-            reds = reds,
-        )
-        def f(data, n):
-            i = 0
-            s = rffi.cast(COMPUTE_TYPE, 0)
-            while i < n:
-                myjitdriver.jit_merge_point(n=n, i=i, s=s, data=data)
-                s += rffi.cast(COMPUTE_TYPE, array_getitem(ffitype, rffi.sizeof(TYPE), data, 0, 0))
-                i += 1
-            return s
-        def main(n):
-            with lltype.scoped_alloc(rffi.CArray(TYPE), 1) as data:
-                data[0] = rffi.cast(TYPE, 200)
-                return f(data, n)
-        assert self.meta_interp(main, [10]) == 2000
-
-    def test_array_getitem_uint8(self):
-        self._test_getitem_type(rffi.UCHAR, types.uchar, lltype.Signed)
-        self.check_resops({'jump': 1, 'int_lt': 2, 'getinteriorfield_raw': 2,
-                           'guard_true': 2, 'int_add': 4})
-
-    def test_array_getitem_float(self):
-        self._test_getitem_type(rffi.FLOAT, types.float, lltype.Float)
+    def test_returns_signedchar(self):
+        self._run([types.signed], types.sint8, [456],
+                  rffi.cast(rffi.SIGNEDCHAR, -42))
 
 
 class TestFfiCall(FfiCallTests, LLJitMixin):
-    supports_all = False
-
-class TestFfiCallSupportAll(FfiCallTests, LLJitMixin):
-    supports_all = True     # supports_{floats,longlong,singlefloats}
-
-    def test_struct_getfield(self):
-        myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'addr'])
-
-        def f(n):
-            i = 0
-            addr = lltype.malloc(rffi.VOIDP.TO, 10, flavor='raw')
-            while i < n:
-                myjitdriver.jit_merge_point(n=n, i=i, addr=addr)
-                struct_setfield_int(types.slong, addr, 0, 1)
-                i += struct_getfield_int(types.slong, addr, 0)
-            lltype.free(addr, flavor='raw')
-            return i
-        assert self.meta_interp(f, [20]) == f(20)
-        self.check_resops(
-            setfield_raw=2,
-            getfield_raw=2,
-            call=0)
-
-
-class TestFfiLookup(FfiLookupTests, LLJitMixin):
     pass
diff --git a/pypy/jit/metainterp/test/test_immutable.py b/pypy/jit/metainterp/test/test_immutable.py
--- a/pypy/jit/metainterp/test/test_immutable.py
+++ b/pypy/jit/metainterp/test/test_immutable.py
@@ -89,6 +89,92 @@
                             int_add=3)
 
 
+    def test_raw_field_and_array(self):
+        from pypy.rpython.lltypesystem import lltype
+        X = lltype.Struct('X',
+            ('a', lltype.Signed),
+            ('b', lltype.Array(lltype.Signed,
+                               hints={'nolength': True, 'immutable': True})),
+            hints={'immutable': True})
+
+        x = lltype.malloc(X, 4, flavor='raw', immortal=True)
+        x.a = 6
+        x.b[2] = 7
+        xlist = [x, lltype.nullptr(X)]
+        def g(num):
+            if num < 0:
+                num = 0
+            return num
+        g._dont_inline_ = True
+        def f(num):
+            num = g(num)
+            x = xlist[num]
+            return x.a * x.b[2]
+        #
+        res = self.interp_operations(f, [0], disable_optimizations=True)
+        assert res == 42
+        self.check_operations_history(getfield_raw_pure=1,
+                                      getarrayitem_raw_pure=1,
+                                      int_mul=1)
+        #
+        # second try, in which we get num=0 constant-folded through f()
+        res = self.interp_operations(f, [-1], disable_optimizations=True)
+        assert res == 42
+        self.check_operations_history(getfield_raw_pure=0,
+                                      getarrayitem_raw_pure=0,
+                                      int_mul=0)
+
+    def test_read_on_promoted(self):
+        # this test used to fail because the n = f.n was staying alive
+        # in a box (not a const, as it was read before promote), and
+        # thus the second f.n was returning the same box, although it
+        # could now return a const.
+        class Foo(object):
+            _immutable_fields_ = ['n']
+            def __init__(self, n):
+                self.n = n
+        f1 = Foo(42); f2 = Foo(43)
+        @jit.dont_look_inside
+        def some(m):
+            return [f1, f2][m]
+        @jit.dont_look_inside
+        def do_stuff_with(n):
+            print n
+        def main(m):
+            f = some(m)
+            n = f.n
+            f = jit.hint(f, promote=True)
+            res = f.n * 6
+            do_stuff_with(n)
+            return res
+        res = self.interp_operations(main, [1])
+        assert res == 43 * 6
+        self.check_operations_history(int_mul=0)   # constant-folded
+
+    def test_read_on_promoted_array(self):
+        class Foo(object):
+            _immutable_fields_ = ['lst[*]']
+            def __init__(self, lst):
+                self.lst = lst
+        f1 = Foo([42]); f2 = Foo([43])
+        @jit.dont_look_inside
+        def some(m):
+            return [f1, f2][m]
+        @jit.dont_look_inside
+        def do_stuff_with(n):
+            print n
+        def main(m):
+            f = some(m)
+            n = f.lst[0]
+            f = jit.hint(f, promote=True)
+            res = f.lst[0] * 6
+            do_stuff_with(n)
+            return res
+        res = self.interp_operations(main, [1])
+        assert res == 43 * 6
+        self.check_operations_history(int_mul=0)   # constant-folded
+
+
 class TestLLtypeImmutableFieldsTests(ImmutableFieldsTests, LLJitMixin):
     pass
 
diff --git a/pypy/jit/metainterp/test/test_list.py b/pypy/jit/metainterp/test/test_list.py
--- a/pypy/jit/metainterp/test/test_list.py
+++ b/pypy/jit/metainterp/test/test_list.py
@@ -251,6 +251,16 @@
         self.meta_interp(f, [10], listops=True)
         self.check_resops(new_array=0, call=0)
 
+    def test_list_mul(self):
+        def f(i):
+            l = [0] * i
+            return len(l)
+
+        r = self.interp_operations(f, [3])
+        assert r == 3
+        r = self.interp_operations(f, [-1])
+        assert r == 0
+
 class TestOOtype(ListTests, OOJitMixin):
     pass
 
diff --git a/pypy/jit/metainterp/test/test_loop.py b/pypy/jit/metainterp/test/test_loop.py
--- a/pypy/jit/metainterp/test/test_loop.py
+++ b/pypy/jit/metainterp/test/test_loop.py
@@ -871,6 +871,42 @@
         res = self.meta_interp(f, [20, 10, 1])
         assert res == f(20, 10, 1)
 
+    def test_boxed_unerased_pointers_in_short_preamble(self):
+        from pypy.rlib.rerased import new_erasing_pair
+        from pypy.rpython.lltypesystem import lltype
+        class A(object):
+            def __init__(self, val):
+                self.val = val
+            def tst(self):
+                return self.val
+
+        class Box(object):
+            def __init__(self, val):
+                self.val = val
+
+        erase_A, unerase_A = new_erasing_pair('A')
+        erase_TP, unerase_TP = new_erasing_pair('TP')
+        TP = lltype.GcArray(lltype.Signed)
+        myjitdriver = JitDriver(greens = [], reds = ['n', 'm', 'i', 'sa', 'p'])
+        def f(n, m):
+            i = sa = 0
+            p = Box(erase_A(A(7)))
+            while i < n:
+                myjitdriver.jit_merge_point(n=n, m=m, i=i, sa=sa, p=p)
+                if i < m:
+                    sa += unerase_A(p.val).tst()
+                elif i == m:
+                    a = lltype.malloc(TP, 5)
+                    a[0] = 42
+                    p = Box(erase_TP(a))
+                else:
+                    sa += unerase_TP(p.val)[0]
+                sa -= A(i).val
+                i += 1
+            return sa
+        res = self.meta_interp(f, [20, 10])
+        assert res == f(20, 10)
+
 class TestOOtype(LoopTest, OOJitMixin):
     pass
 
diff --git a/pypy/jit/metainterp/test/test_rawmem.py b/pypy/jit/metainterp/test/test_rawmem.py
--- a/pypy/jit/metainterp/test/test_rawmem.py
+++ b/pypy/jit/metainterp/test/test_rawmem.py
@@ -1,8 +1,9 @@
 from pypy.jit.metainterp.test.support import LLJitMixin
 from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib.rawstorage import (alloc_raw_storage, raw_storage_setitem,
+                                  free_raw_storage, raw_storage_getitem)
 
-
-class TestJITRawMem(LLJitMixin):
+class RawMemTests(object):
     def test_cast_void_ptr(self):
         TP = lltype.Array(lltype.Float, hints={"nolength": True})
         VOID_TP = lltype.Array(lltype.Void, hints={"nolength": True, "uncast_on_llgraph": True})
@@ -18,7 +19,7 @@
             s += rffi.cast(lltype.Ptr(TP), a.storage)[0]
             lltype.free(x, flavor="raw")
             return s
-        res = self.interp_operations(f, [10])
+        self.interp_operations(f, [10])
 
     def test_fixed_size_malloc(self):
         TIMEVAL = lltype.Struct('dummy', ('tv_sec', rffi.LONG), ('tv_usec', rffi.LONG))
@@ -30,3 +31,32 @@
         assert res == 42
         self.check_operations_history({'call': 2, 'guard_no_exception': 1,
                                        'finish': 1})
+
+    def test_raw_storage_int(self):
+        def f():
+            p = alloc_raw_storage(15)
+            raw_storage_setitem(p, 3, 24)
+            res = raw_storage_getitem(lltype.Signed, p, 3)
+            free_raw_storage(p)
+            return res
+        res = self.interp_operations(f, [])
+        assert res == 24
+        self.check_operations_history({'call': 2, 'guard_no_exception': 1,
+                                       'raw_store': 1, 'raw_load': 1,
+                                       'finish': 1})
+
+    def test_raw_storage_float(self):
+        def f():
+            p = alloc_raw_storage(15)
+            raw_storage_setitem(p, 3, 2.4e15)
+            res = raw_storage_getitem(lltype.Float, p, 3)
+            free_raw_storage(p)
+            return res
+        res = self.interp_operations(f, [])
+        assert res == 2.4e15
+        self.check_operations_history({'call': 2, 'guard_no_exception': 1,
+                                       'raw_store': 1, 'raw_load': 1,
+                                       'finish': 1})
+
+class TestRawMem(RawMemTests, LLJitMixin):
+    pass
diff --git a/pypy/jit/metainterp/test/test_virtualstate.py b/pypy/jit/metainterp/test/test_virtualstate.py
--- a/pypy/jit/metainterp/test/test_virtualstate.py
+++ b/pypy/jit/metainterp/test/test_virtualstate.py
@@ -908,6 +908,141 @@
         """
         self.optimize_bridge(loop, bridge, expected, p5=self.myptr, p6=self.myptr2)
 
+    def test_licm_boxed_opaque_getitem(self):
+        loop = """
+        [p1]
+        p2 = getfield_gc(p1, descr=nextdescr) 
+        mark_opaque_ptr(p2)        
+        guard_class(p2,  ConstClass(node_vtable)) []
+        i3 = getfield_gc(p2, descr=otherdescr)
+        i4 = call(i3, descr=nonwritedescr)
+        jump(p1)
+        """
+        bridge = """
+        [p1]
+        guard_nonnull(p1) []
+        jump(p1)
+        """
+        expected = """
+        [p1]
+        guard_nonnull(p1) []
+        p2 = getfield_gc(p1, descr=nextdescr)
+        jump(p1)
+        """        
+        self.optimize_bridge(loop, bridge, expected, 'Preamble')
+        
+        bridge = """
+        [p1]
+        p2 = getfield_gc(p1, descr=nextdescr) 
+        guard_class(p2,  ConstClass(node_vtable2)) []
+        jump(p1)
+        """
+        expected = """
+        [p1]
+        p2 = getfield_gc(p1, descr=nextdescr) 
+        guard_class(p2,  ConstClass(node_vtable2)) []
+        jump(p1)
+        """
+        self.optimize_bridge(loop, bridge, expected, 'Preamble')
+
+        bridge = """
+        [p1]
+        p2 = getfield_gc(p1, descr=nextdescr) 
+        guard_class(p2,  ConstClass(node_vtable)) []
+        jump(p1)
+        """
+        expected = """
+        [p1]
+        p2 = getfield_gc(p1, descr=nextdescr) 
+        guard_class(p2,  ConstClass(node_vtable)) []
+        i3 = getfield_gc(p2, descr=otherdescr)
+        jump(p1, i3)
+        """
+        self.optimize_bridge(loop, bridge, expected, 'Loop')
+
+    def test_licm_unboxed_opaque_getitem(self):
+        loop = """
+        [p2]
+        mark_opaque_ptr(p2)        
+        guard_class(p2,  ConstClass(node_vtable)) []
+        i3 = getfield_gc(p2, descr=otherdescr)
+        i4 = call(i3, descr=nonwritedescr)
+        jump(p2)
+        """
+        bridge = """
+        [p1]
+        guard_nonnull(p1) []
+        jump(p1)
+        """
+        self.optimize_bridge(loop, bridge, 'RETRACE', p1=self.myptr)
+        self.optimize_bridge(loop, bridge, 'RETRACE', p1=self.myptr2)
+        
+        bridge = """
+        [p2]
+        guard_class(p2,  ConstClass(node_vtable2)) []
+        jump(p2)
+        """
+        self.optimize_bridge(loop, bridge, 'RETRACE')
+
+        bridge = """
+        [p2]
+        guard_class(p2,  ConstClass(node_vtable)) []
+        jump(p2)
+        """
+        expected = """
+        [p2]
+        guard_class(p2,  ConstClass(node_vtable)) []
+        i3 = getfield_gc(p2, descr=otherdescr)
+        jump(p2, i3)
+        """
+        self.optimize_bridge(loop, bridge, expected, 'Loop')
+
+    def test_licm_virtual_opaque_getitem(self):
+        loop = """
+        [p1]
+        p2 = getfield_gc(p1, descr=nextdescr) 
+        mark_opaque_ptr(p2)        
+        guard_class(p2,  ConstClass(node_vtable)) []
+        i3 = getfield_gc(p2, descr=otherdescr)
+        i4 = call(i3, descr=nonwritedescr)
+        p3 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p3, p2, descr=nextdescr)
+        jump(p3)
+        """
+        bridge = """
+        [p1]
+        p3 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p3, p1, descr=nextdescr)
+        jump(p3)
+        """
+        self.optimize_bridge(loop, bridge, 'RETRACE', p1=self.myptr)
+        self.optimize_bridge(loop, bridge, 'RETRACE', p1=self.myptr2)
+
+        bridge = """
+        [p1]
+        p3 = new_with_vtable(ConstClass(node_vtable))
+        guard_class(p1,  ConstClass(node_vtable2)) []
+        setfield_gc(p3, p1, descr=nextdescr)
+        jump(p3)
+        """
+        self.optimize_bridge(loop, bridge, 'RETRACE')
+
+        bridge = """
+        [p1]
+        p3 = new_with_vtable(ConstClass(node_vtable))
+        guard_class(p1,  ConstClass(node_vtable)) []
+        setfield_gc(p3, p1, descr=nextdescr)
+        jump(p3)
+        """
+        expected = """
+        [p1]
+        guard_class(p1,  ConstClass(node_vtable)) []
+        i3 = getfield_gc(p1, descr=otherdescr)
+        jump(p1, i3)
+        """
+        self.optimize_bridge(loop, bridge, expected)
+
+
 class TestLLtypeGuards(BaseTestGenerateGuards, LLtypeMixin):
     pass
 
@@ -915,6 +1050,9 @@
     pass
 
 class FakeOptimizer:
+    def __init__(self):
+        self.opaque_pointers = {}
+        self.values = {}
     def make_equal_to(*args):
         pass
     def getvalue(*args):
diff --git a/pypy/jit/metainterp/test/test_warmspot.py b/pypy/jit/metainterp/test/test_warmspot.py
--- a/pypy/jit/metainterp/test/test_warmspot.py
+++ b/pypy/jit/metainterp/test/test_warmspot.py
@@ -260,6 +260,33 @@
                     pass   # other case
         self.meta_interp(f1, [18])
 
+    def test_bug_constant_int(self):
+        py.test.skip("crashes because a is a constant")
+        from pypy.rpython.lltypesystem import lltype, rffi
+        mydriver = JitDriver(greens=['a'], reds=['m'])
+        def f1(m, a):
+            while m > 0:
+                mydriver.jit_merge_point(a=a, m=m)
+                m = m - 1
+        def entry(m):
+            f1(m, 42)
+        self.meta_interp(entry, [18])
+
+    def test_bug_constant_instance(self):
+        py.test.skip("crashes because a is a constant")
+        from pypy.rpython.lltypesystem import lltype, rffi
+        mydriver = JitDriver(greens=['a'], reds=['m'])
+        class A(object):
+            pass
+        a1 = A()
+        def f1(m, a):
+            while m > 0:
+                mydriver.jit_merge_point(a=a, m=m)
+                m = m - 1
+        def entry(m):
+            f1(m, a1)
+        self.meta_interp(entry, [18])
+
     def test_bug_constant_rawptrs(self):
         py.test.skip("crashes because a is a constant")
         from pypy.rpython.lltypesystem import lltype, rffi
diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py
--- a/pypy/jit/metainterp/warmspot.py
+++ b/pypy/jit/metainterp/warmspot.py
@@ -14,6 +14,7 @@
 from pypy.rlib.debug import fatalerror
 from pypy.rlib.rstackovf import StackOverflow
 from pypy.translator.simplify import get_functype
+from pypy.translator.backendopt import removenoops
 from pypy.translator.unsimplify import call_final_function
 
 from pypy.jit.metainterp import history, pyjitpl, gc, memmgr
@@ -79,10 +80,6 @@
         translator.config.translation.list_comprehension_operations = True
     except ConfigError:
         pass
-    try:
-        translator.config.translation.jit_ffi = True
-    except ConfigError:
-        pass
     warmrunnerdesc = WarmRunnerDesc(translator, backendopt=backendopt, **kwds)
     for jd in warmrunnerdesc.jitdrivers_sd:
         jd.warmstate.set_param_threshold(3)          # for tests
@@ -264,6 +261,10 @@
         graph = copygraph(graph)
         [jmpp] = find_jit_merge_points([graph])
         graph.startblock = support.split_before_jit_merge_point(*jmpp)
+        # XXX this is incredibly obscure, but this is sometiems necessary
+        #     so we don't explode in checkgraph. for reasons unknown this
+        #     is not contanied within simplify_graph
+        removenoops.remove_same_as(graph)
         # a crash in the following checkgraph() means that you forgot
         # to list some variable in greens=[] or reds=[] in JitDriver,
         # or that a jit_merge_point() takes a constant as an argument.
diff --git a/pypy/jit/tl/pypyjit_demo.py b/pypy/jit/tl/pypyjit_demo.py
--- a/pypy/jit/tl/pypyjit_demo.py
+++ b/pypy/jit/tl/pypyjit_demo.py
@@ -1,19 +1,27 @@
 import pypyjit
 pypyjit.set_param(threshold=200)
 
+kwargs = {"z": 1}
 
-def g(*args):
-    return len(args)
+def f(*args, **kwargs):
+    result = g(1, *args, **kwargs)
+    return result + 2
 
-def f(n):
-    s = 0
-    for i in range(n):
-        l = [i, n, 2]
-        s += g(*l)
-    return s
+def g(x, y, z=2):
+    return x - y + z
+
+def main():
+    res = 0
+    i = 0
+    while i < 10000:
+        res = f(res, z=i)
+        g(1, res, **kwargs)
+        i += 1
+    return res
+
 
 try:
-    print f(301)
+    print main()
 
 except Exception, e:
     print "Exception: ", type(e)
diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py
--- a/pypy/module/__pypy__/__init__.py
+++ b/pypy/module/__pypy__/__init__.py
@@ -43,6 +43,8 @@
         'do_what_I_mean'            : 'interp_magic.do_what_I_mean',
         'list_strategy'             : 'interp_magic.list_strategy',
         'validate_fd'               : 'interp_magic.validate_fd',
+        'newdict'                   : 'interp_dict.newdict',
+        'dictstrategy'              : 'interp_dict.dictstrategy',
     }
     if sys.platform == 'win32':
         interpleveldefs['get_console_cp'] = 'interp_magic.get_console_cp'
diff --git a/pypy/module/__pypy__/interp_dict.py b/pypy/module/__pypy__/interp_dict.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/__pypy__/interp_dict.py
@@ -0,0 +1,24 @@
+
+from pypy.interpreter.gateway import unwrap_spec
+from pypy.interpreter.error import operationerrfmt, OperationError
+from pypy.objspace.std.dictmultiobject import W_DictMultiObject
+
+ at unwrap_spec(type=str)
+def newdict(space, type):
+    if type == 'module':
+        return space.newdict(module=True)
+    elif type == 'instance':
+        return space.newdict(instance=True)
+    elif type == 'kwargs':
+        return space.newdict(kwargs=True)
+    elif type == 'strdict':
+        return space.newdict(strdict=True)
+    else:
+        raise operationerrfmt(space.w_TypeError, "unknown type of dict %s",
+                              type)
+
+def dictstrategy(space, w_obj):
+    if not isinstance(w_obj, W_DictMultiObject):
+        raise OperationError(space.w_TypeError,
+                             space.wrap("expecting dict object"))
+    return space.wrap('%r' % (w_obj.strategy,))
diff --git a/pypy/module/__pypy__/interp_time.py b/pypy/module/__pypy__/interp_time.py
--- a/pypy/module/__pypy__/interp_time.py
+++ b/pypy/module/__pypy__/interp_time.py
@@ -1,5 +1,5 @@
 from __future__ import with_statement
-import os
+import sys
 
 from pypy.interpreter.error import exception_from_errno
 from pypy.interpreter.gateway import unwrap_spec
@@ -7,10 +7,11 @@
 from pypy.rpython.tool import rffi_platform
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
 
-if os.name == 'nt':
+if sys.platform == 'linux2':
+    libraries = ["rt"]
+else:
     libraries = []
-else:
-    libraries = ["rt"]
+
 
 class CConfig:
     _compilation_info_ = ExternalCompilationInfo(
diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -0,0 +1,42 @@
+from pypy.interpreter.mixedmodule import MixedModule
+
+class Module(MixedModule):
+
+    appleveldefs = {
+        }
+    interpleveldefs = {
+        '__version__': 'space.wrap("0.3")',
+
+        'nonstandard_integer_types': 'misc.nonstandard_integer_types',
+
+        'load_library': 'libraryobj.load_library',
+
+        'new_primitive_type': 'newtype.new_primitive_type',
+        'new_pointer_type': 'newtype.new_pointer_type',
+        'new_array_type': 'newtype.new_array_type',
+        'new_struct_type': 'newtype.new_struct_type',
+        'new_union_type': 'newtype.new_union_type',
+        'complete_struct_or_union': 'newtype.complete_struct_or_union',
+        'new_void_type': 'newtype.new_void_type',
+        'new_enum_type': 'newtype.new_enum_type',
+        'new_function_type': 'newtype.new_function_type',
+
+        'newp': 'func.newp',
+        'cast': 'func.cast',
+        'callback': 'func.callback',
+        'alignof': 'func.alignof',
+        'sizeof': 'func.sizeof',
+        'typeof': 'func.typeof',
+        'offsetof': 'func.offsetof',
+        '_getfields': 'func._getfields',
+        'getcname': 'func.getcname',
+
+        'string': 'func.string',
+        'buffer': 'cbuffer.buffer',
+
+        'get_errno': 'cerrno.get_errno',
+        'set_errno': 'cerrno.set_errno',
+
+        'FFI_DEFAULT_ABI': 'ctypefunc._get_abi(space, "FFI_DEFAULT_ABI")',
+        'FFI_CDECL': 'ctypefunc._get_abi(space,"FFI_DEFAULT_ABI")',#win32 name
+        }
diff --git a/pypy/module/_cffi_backend/cbuffer.py b/pypy/module/_cffi_backend/cbuffer.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/cbuffer.py
@@ -0,0 +1,55 @@
+from pypy.interpreter.error import operationerrfmt
+from pypy.interpreter.buffer import RWBuffer
+from pypy.interpreter.gateway import unwrap_spec
+from pypy.rpython.lltypesystem import rffi
+from pypy.module._cffi_backend import cdataobj, ctypeptr, ctypearray
+
+
+class LLBuffer(RWBuffer):
+    _immutable_ = True
+
+    def __init__(self, raw_cdata, size):
+        self.raw_cdata = raw_cdata
+        self.size = size
+
+    def getlength(self):
+        return self.size
+
+    def getitem(self, index):
+        return self.raw_cdata[index]
+
+    def setitem(self, index, char):
+        self.raw_cdata[index] = char
+
+    def get_raw_address(self):
+        return self.raw_cdata
+
+    def getslice(self, start, stop, step, size):
+        if step == 1:
+            return rffi.charpsize2str(rffi.ptradd(self.raw_cdata, start), size)
+        return RWBuffer.getslice(self, start, stop, step, size)
+
+    def setslice(self, start, string):
+        raw_cdata = rffi.ptradd(self.raw_cdata, start)
+        for i in range(len(string)):
+            raw_cdata[i] = string[i]
+
+
+ at unwrap_spec(cdata=cdataobj.W_CData, size=int)
+def buffer(space, cdata, size=-1):
+    ctype = cdata.ctype
+    if isinstance(ctype, ctypeptr.W_CTypePointer):
+        if size < 0:
+            size = ctype.ctitem.size
+    elif isinstance(ctype, ctypearray.W_CTypeArray):
+        if size < 0:
+            size = cdata._sizeof()
+    else:
+        raise operationerrfmt(space.w_TypeError,
+                              "expected a pointer or array cdata, got '%s'",
+                              ctype.name)
+    if size < 0:
+        raise operationerrfmt(space.w_TypeError,
+                              "don't know the size pointed to by '%s'",
+                              ctype.name)
+    return space.wrap(LLBuffer(cdata._cdata, size))
diff --git a/pypy/module/_cffi_backend/ccallback.py b/pypy/module/_cffi_backend/ccallback.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ccallback.py
@@ -0,0 +1,200 @@
+"""
+Callbacks.
+"""
+import os
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rlib.objectmodel import compute_unique_id, keepalive_until_here
+from pypy.rlib import clibffi, rweakref, rgc
+from pypy.rlib.rarithmetic import r_ulonglong
+
+from pypy.module._cffi_backend.cdataobj import W_CData
+from pypy.module._cffi_backend.ctypefunc import SIZE_OF_FFI_ARG, BIG_ENDIAN
+from pypy.module._cffi_backend.ctypefunc import W_CTypeFunc
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveSigned
+from pypy.module._cffi_backend.ctypevoid import W_CTypeVoid
+from pypy.module._cffi_backend import cerrno, misc
+
+# ____________________________________________________________
+
+
+class W_CDataCallback(W_CData):
+    #_immutable_fields_ = ...
+    ll_error = lltype.nullptr(rffi.CCHARP.TO)
+
+    def __init__(self, space, ctype, w_callable, w_error):
+        raw_closure = rffi.cast(rffi.CCHARP, clibffi.closureHeap.alloc())
+        W_CData.__init__(self, space, raw_closure, ctype)
+        #
+        if not space.is_true(space.callable(w_callable)):
+            raise operationerrfmt(space.w_TypeError,
+                                  "expected a callable object, not %s",
+                                  space.type(w_callable).getname(space))
+        self.w_callable = w_callable
+        self.w_error = w_error
+        #
+        fresult = self.getfunctype().ctitem
+        size = fresult.size
+        if size > 0:
+            if fresult.is_primitive_integer and size < SIZE_OF_FFI_ARG:
+                size = SIZE_OF_FFI_ARG
+            self.ll_error = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw',
+                                          zero=True)
+        if not space.is_w(w_error, space.w_None):
+            convert_from_object_fficallback(fresult, self.ll_error, w_error)
+        #
+        self.unique_id = compute_unique_id(self)
+        global_callback_mapping.set(self.unique_id, self)
+        #
+        cif_descr = self.getfunctype().cif_descr
+        if not cif_descr:
+            raise OperationError(space.w_NotImplementedError,
+                                 space.wrap("callbacks with '...'"))
+        res = clibffi.c_ffi_prep_closure(self.get_closure(), cif_descr.cif,
+                                         invoke_callback,
+                                         rffi.cast(rffi.VOIDP, self.unique_id))
+        if rffi.cast(lltype.Signed, res) != clibffi.FFI_OK:
+            raise OperationError(space.w_SystemError,
+                space.wrap("libffi failed to build this callback"))
+
+    def get_closure(self):
+        return rffi.cast(clibffi.FFI_CLOSUREP, self._cdata)
+
+    #@rgc.must_be_light_finalizer
+    def __del__(self):
+        clibffi.closureHeap.free(self.get_closure())
+        if self.ll_error:
+            lltype.free(self.ll_error, flavor='raw')
+
+    def _repr_extra(self):
+        space = self.space
+        return 'calling ' + space.str_w(space.repr(self.w_callable))
+
+    def getfunctype(self):
+        ctype = self.ctype
+        if not isinstance(ctype, W_CTypeFunc):
+            space = self.space
+            raise OperationError(space.w_TypeError,
+                                 space.wrap("expected a function ctype"))
+        return ctype
+
+    def invoke(self, ll_args, ll_res):
+        space = self.space
+        ctype = self.getfunctype()
+        args_w = []
+        for i, farg in enumerate(ctype.fargs):
+            ll_arg = rffi.cast(rffi.CCHARP, ll_args[i])
+            args_w.append(farg.convert_to_object(ll_arg))
+        fresult = ctype.ctitem
+        #
+        w_res = space.call(self.w_callable, space.newtuple(args_w))
+        #
+        convert_from_object_fficallback(fresult, ll_res, w_res)
+
+    def print_error(self, operr):
+        space = self.space
+        operr.write_unraisable(space, "cffi callback", self.w_callable)
+
+    def write_error_return_value(self, ll_res):
+        fresult = self.getfunctype().ctitem
+        if fresult.size > 0:
+            misc._raw_memcopy(self.ll_error, ll_res, fresult.size)
+            keepalive_until_here(self)
+
+
+global_callback_mapping = rweakref.RWeakValueDictionary(int, W_CDataCallback)
+
+
+def convert_from_object_fficallback(fresult, ll_res, w_res):
+    space = fresult.space
+    small_result = fresult.size < SIZE_OF_FFI_ARG
+    if small_result and isinstance(fresult, W_CTypeVoid):
+        if not space.is_w(w_res, space.w_None):
+            raise OperationError(space.w_TypeError,
+                    space.wrap("callback with the return type 'void'"
+                               " must return None"))
+        return
+    #
+    if small_result and fresult.is_primitive_integer:
+        # work work work around a libffi irregularity: for integer return
+        # types we have to fill at least a complete 'ffi_arg'-sized result
+        # buffer.
+        if type(fresult) is W_CTypePrimitiveSigned:
+            # It's probably fine to always zero-extend, but you never
+            # know: maybe some code somewhere expects a negative
+            # 'short' result to be returned into EAX as a 32-bit
+            # negative number.  Better safe than sorry.  This code
+            # is about that case.  Let's ignore this for enums.
+            #
+            # do a first conversion only to detect overflows.  This
+            # conversion produces stuff that is otherwise ignored.
+            fresult.convert_from_object(ll_res, w_res)
+            #
+            # manual inlining and tweaking of
+            # W_CTypePrimitiveSigned.convert_from_object() in order
+            # to write a whole 'ffi_arg'.
+            value = misc.as_long_long(space, w_res)
+            value = r_ulonglong(value)
+            misc.write_raw_integer_data(ll_res, value, SIZE_OF_FFI_ARG)
+            return
+        else:
+            # zero extension: fill the '*result' with zeros, and (on big-
+            # endian machines) correct the 'result' pointer to write to
+            misc._raw_memclear(ll_res, SIZE_OF_FFI_ARG)
+            if BIG_ENDIAN:
+                diff = SIZE_OF_FFI_ARG - fresult.size
+                ll_res = rffi.ptradd(ll_res, diff)
+    #
+    fresult.convert_from_object(ll_res, w_res)
+
+
+# ____________________________________________________________
+
+STDERR = 2
+
+def invoke_callback(ffi_cif, ll_res, ll_args, ll_userdata):
+    """ Callback specification.
+    ffi_cif - something ffi specific, don't care
+    ll_args - rffi.VOIDPP - pointer to array of pointers to args
+    ll_restype - rffi.VOIDP - pointer to result
+    ll_userdata - a special structure which holds necessary information
+                  (what the real callback is for example), casted to VOIDP
+    """
+    e = cerrno.get_real_errno()
+    ll_res = rffi.cast(rffi.CCHARP, ll_res)
+    unique_id = rffi.cast(lltype.Signed, ll_userdata)
+    callback = global_callback_mapping.get(unique_id)
+    if callback is None:
+        # oups!
+        try:
+            os.write(STDERR, "SystemError: invoking a callback "
+                             "that was already freed\n")
+        except OSError:
+            pass
+        # In this case, we don't even know how big ll_res is.  Let's assume
+        # it is just a 'ffi_arg', and store 0 there.
+        misc._raw_memclear(ll_res, SIZE_OF_FFI_ARG)
+        return
+    #
+    ec = None
+    try:
+        ec = cerrno.get_errno_container(callback.space)
+        cerrno.save_errno_into(ec, e)
+        try:
+            callback.invoke(ll_args, ll_res)
+        except OperationError, e:
+            # got an app-level exception
+            callback.print_error(e)
+            callback.write_error_return_value(ll_res)
+        #
+    except Exception, e:
+        # oups! last-level attempt to recover.
+        try:
+            os.write(STDERR, "SystemError: callback raised ")
+            os.write(STDERR, str(e))
+            os.write(STDERR, "\n")
+        except OSError:
+            pass
+        callback.write_error_return_value(ll_res)
+    if ec is not None:
+        cerrno.restore_errno_from(ec)
diff --git a/pypy/module/_cffi_backend/cdataobj.py b/pypy/module/_cffi_backend/cdataobj.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/cdataobj.py
@@ -0,0 +1,309 @@
+import operator
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.typedef import TypeDef, make_weakref_descr
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib.objectmodel import keepalive_until_here
+from pypy.rlib import objectmodel, rgc
+from pypy.tool.sourcetools import func_with_new_name
+
+from pypy.module._cffi_backend import misc
+
+
+class W_CData(Wrappable):
+    _attrs_ = ['space', '_cdata', 'ctype', '_lifeline_']
+    _immutable_fields_ = ['_cdata', 'ctype']
+    _cdata = lltype.nullptr(rffi.CCHARP.TO)
+
+    def __init__(self, space, cdata, ctype):
+        from pypy.module._cffi_backend import ctypeprim
+        assert lltype.typeOf(cdata) == rffi.CCHARP
+        assert isinstance(ctype, ctypeprim.W_CType)
+        self.space = space
+        self._cdata = cdata    # don't forget keepalive_until_here!
+        self.ctype = ctype
+
+    def _repr_extra(self):
+        extra = self.ctype.extra_repr(self._cdata)
+        keepalive_until_here(self)
+        return extra
+
+    def _repr_extra_owning(self):
+        from pypy.module._cffi_backend.ctypeptr import W_CTypePointer
+        ctype = self.ctype
+        if isinstance(ctype, W_CTypePointer):
+            num_bytes = ctype.ctitem.size
+        else:
+            num_bytes = self._sizeof()
+        return 'owning %d bytes' % num_bytes
+
+    def repr(self):
+        extra2 = self._repr_extra()
+        extra1 = ''
+        if not isinstance(self, W_CDataNewOwning):
+            # it's slightly confusing to get "<cdata 'struct foo' 0x...>"
+            # because the struct foo is not owned.  Trying to make it
+            # clearer, write in this case "<cdata 'struct foo &' 0x...>".
+            from pypy.module._cffi_backend import ctypestruct
+            if isinstance(self.ctype, ctypestruct.W_CTypeStructOrUnion):
+                extra1 = ' &'
+        return self.space.wrap("<cdata '%s%s' %s>" % (
+            self.ctype.name, extra1, extra2))
+
+    def nonzero(self):
+        return self.space.wrap(bool(self._cdata))
+
+    def int(self):
+        w_result = self.ctype.int(self._cdata)
+        keepalive_until_here(self)
+        return w_result
+
+    def long(self):
+        w_result = self.int()
+        space = self.space
+        if space.is_w(space.type(w_result), space.w_int):
+            w_result = space.newlong(space.int_w(w_result))
+        return w_result
+
+    def float(self):
+        w_result = self.ctype.float(self._cdata)
+        keepalive_until_here(self)
+        return w_result
+
+    def len(self):
+        from pypy.module._cffi_backend import ctypearray
+        space = self.space
+        if isinstance(self.ctype, ctypearray.W_CTypeArray):
+            return space.wrap(self.get_array_length())
+        raise operationerrfmt(space.w_TypeError,
+                              "cdata of type '%s' has no len()",
+                              self.ctype.name)
+
+    def _make_comparison(name):
+        op = getattr(operator, name)
+        requires_ordering = name not in ('eq', 'ne')
+        #
+        def _cmp(self, w_other):
+            from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitive
+            space = self.space
+            cdata1 = self._cdata
+            other = space.interpclass_w(w_other)
+            if isinstance(other, W_CData):
+                cdata2 = other._cdata
+            else:
+                return space.w_NotImplemented
+
+            if requires_ordering:
+                if (isinstance(self.ctype, W_CTypePrimitive) or
+                    isinstance(other.ctype, W_CTypePrimitive)):
+                    raise OperationError(space.w_TypeError,
+                        space.wrap("cannot do comparison on a primitive cdata"))
+                cdata1 = rffi.cast(lltype.Unsigned, cdata1)
+                cdata2 = rffi.cast(lltype.Unsigned, cdata2)
+            return space.newbool(op(cdata1, cdata2))
+        #
+        return func_with_new_name(_cmp, name)
+
+    lt = _make_comparison('lt')
+    le = _make_comparison('le')
+    eq = _make_comparison('eq')
+    ne = _make_comparison('ne')
+    gt = _make_comparison('gt')
+    ge = _make_comparison('ge')
+
+    def hash(self):
+        h = (objectmodel.compute_identity_hash(self.ctype) ^
+             rffi.cast(lltype.Signed, self._cdata))
+        return self.space.wrap(h)
+
+    def getitem(self, w_index):
+        space = self.space
+        i = space.getindex_w(w_index, space.w_IndexError)
+        ctype = self.ctype._check_subscript_index(self, i)
+        w_o = self._do_getitem(ctype, i)
+        keepalive_until_here(self)
+        return w_o
+
+    def _do_getitem(self, ctype, i):
+        ctitem = ctype.ctitem
+        return ctitem.convert_to_object(
+            rffi.ptradd(self._cdata, i * ctitem.size))
+
+    def setitem(self, w_index, w_value):
+        space = self.space
+        i = space.getindex_w(w_index, space.w_IndexError)
+        ctype = self.ctype._check_subscript_index(self, i)
+        ctitem = ctype.ctitem
+        ctitem.convert_from_object(
+            rffi.ptradd(self._cdata, i * ctitem.size),
+            w_value)
+        keepalive_until_here(self)
+
+    def _add_or_sub(self, w_other, sign):
+        space = self.space
+        i = sign * space.getindex_w(w_other, space.w_OverflowError)
+        return self.ctype.add(self._cdata, i)
+
+    def add(self, w_other):
+        return self._add_or_sub(w_other, +1)
+
+    def sub(self, w_other):
+        space = self.space
+        ob = space.interpclass_w(w_other)
+        if isinstance(ob, W_CData):
+            from pypy.module._cffi_backend import ctypeptr, ctypearray
+            ct = ob.ctype
+            if isinstance(ct, ctypearray.W_CTypeArray):
+                ct = ct.ctptr
+            #
+            if (ct is not self.ctype or
+                   not isinstance(ct, ctypeptr.W_CTypePointer) or
+                   ct.ctitem.size <= 0):
+                raise operationerrfmt(space.w_TypeError,
+                    "cannot subtract cdata '%s' and cdata '%s'",
+                    self.ctype.name, ct.name)
+            #
+            diff = (rffi.cast(lltype.Signed, self._cdata) -
+                    rffi.cast(lltype.Signed, ob._cdata)) // ct.ctitem.size
+            return space.wrap(diff)
+        #
+        return self._add_or_sub(w_other, -1)
+
+    def getcfield(self, w_attr):
+        return self.ctype.getcfield(self.space.str_w(w_attr))
+
+    def getattr(self, w_attr):
+        w_res = self.getcfield(w_attr).read(self._cdata)
+        keepalive_until_here(self)
+        return w_res
+
+    def setattr(self, w_attr, w_value):
+        self.getcfield(w_attr).write(self._cdata, w_value)
+        keepalive_until_here(self)
+
+    def call(self, args_w):
+        w_result = self.ctype.call(self._cdata, args_w)
+        keepalive_until_here(self)
+        return w_result
+
+    def iter(self):
+        return self.ctype.iter(self)
+
+    def write_raw_integer_data(self, source):
+        misc.write_raw_integer_data(self._cdata, source, self.ctype.size)
+        keepalive_until_here(self)
+
+    def write_raw_float_data(self, source):
+        misc.write_raw_float_data(self._cdata, source, self.ctype.size)
+        keepalive_until_here(self)
+
+    def convert_to_object(self):
+        w_obj = self.ctype.convert_to_object(self._cdata)
+        keepalive_until_here(self)
+        return w_obj
+
+    def get_array_length(self):
+        from pypy.module._cffi_backend import ctypearray
+        ctype = self.ctype
+        assert isinstance(ctype, ctypearray.W_CTypeArray)
+        length = ctype.length
+        assert length >= 0
+        return length
+
+    def _sizeof(self):
+        return self.ctype.size
+
+
+class W_CDataMem(W_CData):
+    """This is the base class used for cdata objects that own and free
+    their memory.  Used directly by the results of cffi.cast('int', x)
+    or other primitive explicitly-casted types.  It is further subclassed
+    by W_CDataNewOwning."""
+    _attrs_ = []
+
+    def __init__(self, space, size, ctype):
+        cdata = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw', zero=True)
+        W_CData.__init__(self, space, cdata, ctype)
+
+    @rgc.must_be_light_finalizer
+    def __del__(self):
+        lltype.free(self._cdata, flavor='raw')
+
+
+class W_CDataNewOwning(W_CDataMem):
+    """This is the class used for the cata objects created by newp()."""
+    _attrs_ = []
+
+    def _repr_extra(self):
+        return self._repr_extra_owning()
+
+
+class W_CDataNewOwningLength(W_CDataNewOwning):
+    """Subclass with an explicit length, for allocated instances of
+    the C type 'foo[]'."""
+    _attrs_ = ['length']
+    _immutable_fields_ = ['length']
+
+    def __init__(self, space, size, ctype, length):
+        W_CDataNewOwning.__init__(self, space, size, ctype)
+        self.length = length
+
+    def _sizeof(self):
+        from pypy.module._cffi_backend import ctypearray
+        ctype = self.ctype
+        assert isinstance(ctype, ctypearray.W_CTypeArray)
+        return self.length * ctype.ctitem.size
+
+    def get_array_length(self):
+        return self.length
+
+
+class W_CDataPtrToStructOrUnion(W_CData):
+    """This subclass is used for the pointer returned by new('struct foo').
+    It has a strong reference to a W_CDataNewOwning that really owns the
+    struct, which is the object returned by the app-level expression 'p[0]'.
+    But it is not itself owning any memory, although its repr says so;
+    it is merely a co-owner."""
+    _attrs_ = ['structobj']
+    _immutable_fields_ = ['structobj']
+
+    def __init__(self, space, cdata, ctype, structobj):
+        W_CData.__init__(self, space, cdata, ctype)
+        self.structobj = structobj
+
+    def _repr_extra(self):
+        return self._repr_extra_owning()
+
+    def _do_getitem(self, ctype, i):
+        assert i == 0
+        return self.structobj
+
+
+W_CData.typedef = TypeDef(
+    'CData',
+    __module__ = '_cffi_backend',
+    __repr__ = interp2app(W_CData.repr),
+    __nonzero__ = interp2app(W_CData.nonzero),
+    __int__ = interp2app(W_CData.int),
+    __long__ = interp2app(W_CData.long),
+    __float__ = interp2app(W_CData.float),
+    __len__ = interp2app(W_CData.len),
+    __lt__ = interp2app(W_CData.lt),
+    __le__ = interp2app(W_CData.le),
+    __eq__ = interp2app(W_CData.eq),
+    __ne__ = interp2app(W_CData.ne),
+    __gt__ = interp2app(W_CData.gt),
+    __ge__ = interp2app(W_CData.ge),
+    __hash__ = interp2app(W_CData.hash),
+    __getitem__ = interp2app(W_CData.getitem),
+    __setitem__ = interp2app(W_CData.setitem),
+    __add__ = interp2app(W_CData.add),
+    __sub__ = interp2app(W_CData.sub),
+    __getattr__ = interp2app(W_CData.getattr),
+    __setattr__ = interp2app(W_CData.setattr),
+    __call__ = interp2app(W_CData.call),
+    __iter__ = interp2app(W_CData.iter),
+    __weakref__ = make_weakref_descr(W_CData),
+    )
+W_CData.typedef.acceptable_as_base_class = False
diff --git a/pypy/module/_cffi_backend/cerrno.py b/pypy/module/_cffi_backend/cerrno.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/cerrno.py
@@ -0,0 +1,29 @@
+from pypy.rlib import rposix
+from pypy.interpreter.executioncontext import ExecutionContext
+from pypy.interpreter.gateway import unwrap_spec
+
+
+ExecutionContext._cffi_saved_errno = 0
+
+
+def get_errno_container(space):
+    return space.getexecutioncontext()
+
+get_real_errno = rposix.get_errno
+
+
+def restore_errno_from(ec):
+    rposix.set_errno(ec._cffi_saved_errno)
+
+def save_errno_into(ec, errno):
+    ec._cffi_saved_errno = errno
+
+
+def get_errno(space):
+    ec = get_errno_container(space)
+    return space.wrap(ec._cffi_saved_errno)
+
+ at unwrap_spec(errno=int)
+def set_errno(space, errno):
+    ec = get_errno_container(space)
+    ec._cffi_saved_errno = errno
diff --git a/pypy/module/_cffi_backend/ctypearray.py b/pypy/module/_cffi_backend/ctypearray.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypearray.py
@@ -0,0 +1,128 @@
+"""
+Arrays.
+"""
+
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.gateway import interp2app
+from pypy.interpreter.typedef import TypeDef
+from pypy.rpython.lltypesystem import rffi
+from pypy.rlib.objectmodel import keepalive_until_here
+from pypy.rlib.rarithmetic import ovfcheck
+
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveChar
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveUniChar
+from pypy.module._cffi_backend.ctypeptr import W_CTypePtrOrArray
+from pypy.module._cffi_backend import cdataobj
+
+
+class W_CTypeArray(W_CTypePtrOrArray):
+    _attrs_            = ['ctptr']
+    _immutable_fields_ = ['ctptr']
+
+    def __init__(self, space, ctptr, length, arraysize, extra):
+        W_CTypePtrOrArray.__init__(self, space, arraysize, extra, 0,
+                                   ctptr.ctitem)
+        self.length = length
+        self.ctptr = ctptr
+
+    def _alignof(self):
+        return self.ctitem.alignof()
+
+    def newp(self, w_init):
+        space = self.space
+        datasize = self.size
+        #
+        if datasize < 0:
+            if (space.isinstance_w(w_init, space.w_list) or
+                space.isinstance_w(w_init, space.w_tuple)):
+                length = space.int_w(space.len(w_init))
+            elif space.isinstance_w(w_init, space.w_basestring):
+                # from a string, we add the null terminator
+                length = space.int_w(space.len(w_init)) + 1
+            else:
+                length = space.getindex_w(w_init, space.w_OverflowError)
+                if length < 0:
+                    raise OperationError(space.w_ValueError,
+                                         space.wrap("negative array length"))
+                w_init = space.w_None
+            #
+            try:
+                datasize = ovfcheck(length * self.ctitem.size)
+            except OverflowError:
+                raise OperationError(space.w_OverflowError,
+                    space.wrap("array size would overflow a ssize_t"))
+            #
+            cdata = cdataobj.W_CDataNewOwningLength(space, datasize,
+                                                    self, length)
+        #
+        else:
+            cdata = cdataobj.W_CDataNewOwning(space, datasize, self)
+        #
+        if not space.is_w(w_init, space.w_None):
+            self.convert_from_object(cdata._cdata, w_init)
+            keepalive_until_here(cdata)
+        return cdata
+
+    def _check_subscript_index(self, w_cdata, i):
+        space = self.space
+        if i < 0:
+            raise OperationError(space.w_IndexError,
+                                 space.wrap("negative index not supported"))
+        if i >= w_cdata.get_array_length():
+            raise operationerrfmt(space.w_IndexError,
+                "index too large for cdata '%s' (expected %d < %d)",
+                self.name, i, w_cdata.get_array_length())
+        return self
+
+    def convert_from_object(self, cdata, w_ob):
+        self.convert_array_from_object(cdata, w_ob)
+
+    def convert_to_object(self, cdata):
+        if self.length < 0:
+            # we can't return a <cdata 'int[]'> here, because we don't
+            # know the length to give it.  As a compromize, returns
+            # <cdata 'int *'> in this case.
+            self = self.ctptr
+        #
+        return cdataobj.W_CData(self.space, cdata, self)
+
+    def add(self, cdata, i):
+        p = rffi.ptradd(cdata, i * self.ctitem.size)
+        return cdataobj.W_CData(self.space, p, self.ctptr)
+
+    def iter(self, cdata):
+        return W_CDataIter(self.space, self.ctitem, cdata)
+
+    def get_vararg_type(self):
+        return self.ctptr
+
+
+class W_CDataIter(Wrappable):
+    _immutable_fields_ = ['ctitem', 'cdata', '_stop']    # but not '_next'
+
+    def __init__(self, space, ctitem, cdata):
+        self.space = space
+        self.ctitem = ctitem
+        self.cdata = cdata
+        length = cdata.get_array_length()
+        self._next = cdata._cdata
+        self._stop = rffi.ptradd(cdata._cdata, length * ctitem.size)
+
+    def iter_w(self):
+        return self.space.wrap(self)
+
+    def next_w(self):
+        result = self._next
+        if result == self._stop:
+            raise OperationError(self.space.w_StopIteration, self.space.w_None)
+        self._next = rffi.ptradd(result, self.ctitem.size)
+        return self.ctitem.convert_to_object(result)
+
+W_CDataIter.typedef = TypeDef(
+    'CDataIter',
+    __module__ = '_cffi_backend',
+    __iter__ = interp2app(W_CDataIter.iter_w),
+    next = interp2app(W_CDataIter.next_w),
+    )
+W_CDataIter.typedef.acceptable_as_base_class = False
diff --git a/pypy/module/_cffi_backend/ctypeenum.py b/pypy/module/_cffi_backend/ctypeenum.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypeenum.py
@@ -0,0 +1,88 @@
+"""
+Enums.
+"""
+
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.rpython.lltypesystem import rffi
+from pypy.rlib.rarithmetic import intmask, r_ulonglong
+from pypy.rlib.objectmodel import keepalive_until_here
+
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveSigned
+from pypy.module._cffi_backend import misc
+
+
+class W_CTypeEnum(W_CTypePrimitiveSigned):
+    _attrs_            = ['enumerators2values', 'enumvalues2erators']
+    _immutable_fields_ = ['enumerators2values', 'enumvalues2erators']
+
+    def __init__(self, space, name, enumerators, enumvalues):
+        from pypy.module._cffi_backend.newtype import alignment
+        name = "enum " + name
+        size = rffi.sizeof(rffi.INT)
+        align = alignment(rffi.INT)
+        W_CTypePrimitiveSigned.__init__(self, space, size,
+                                        name, len(name), align)
+        self.enumerators2values = {}   # str -> int
+        self.enumvalues2erators = {}   # int -> str
+        for i in range(len(enumerators)-1, -1, -1):
+            self.enumerators2values[enumerators[i]] = enumvalues[i]
+            self.enumvalues2erators[enumvalues[i]] = enumerators[i]
+
+    def _getfields(self):
+        space = self.space
+        lst = []
+        for enumerator in self.enumerators2values:
+            enumvalue = self.enumerators2values[enumerator]
+            lst.append(space.newtuple([space.wrap(enumvalue),
+                                       space.wrap(enumerator)]))
+        w_lst = space.newlist(lst)
+        space.call_method(w_lst, 'sort')
+        return w_lst
+
+    def string(self, cdataobj, maxlen):
+        w_result = self.convert_to_object(cdataobj._cdata)
+        keepalive_until_here(cdataobj)
+        return w_result
+
+    def convert_to_object(self, cdata):
+        value = intmask(misc.read_raw_signed_data(cdata, self.size))
+        try:
+            enumerator = self.enumvalues2erators[value]
+        except KeyError:
+            enumerator = '#%d' % (value,)
+        return self.space.wrap(enumerator)
+
+    def convert_from_object(self, cdata, w_ob):
+        space = self.space
+        try:
+            return W_CTypePrimitiveSigned.convert_from_object(self, cdata,
+                                                              w_ob)
+        except OperationError, e:
+            if not e.match(space, space.w_TypeError):
+                raise
+        if space.isinstance_w(w_ob, space.w_str):
+            value = self.convert_enum_string_to_int(space.str_w(w_ob))
+            value = r_ulonglong(value)
+            misc.write_raw_integer_data(cdata, value, self.size)
+        else:
+            raise self._convert_error("str or int", w_ob)
+
+    def cast_str(self, w_ob):
+        space = self.space
+        return self.convert_enum_string_to_int(space.str_w(w_ob))
+
+    def convert_enum_string_to_int(self, s):
+        space = self.space
+        if s.startswith('#'):
+            try:
+                return int(s[1:])     # xxx is it RPython?
+            except ValueError:
+                raise OperationError(space.w_ValueError,
+                                     space.wrap("invalid literal after '#'"))
+        else:
+            try:
+                return self.enumerators2values[s]
+            except KeyError:
+                raise operationerrfmt(space.w_ValueError,
+                                      "'%s' is not an enumerator for %s",
+                                      s, self.name)
diff --git a/pypy/module/_cffi_backend/ctypefunc.py b/pypy/module/_cffi_backend/ctypefunc.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypefunc.py
@@ -0,0 +1,422 @@
+"""
+Function pointers.
+"""
+
+import sys
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rlib import jit, clibffi, jit_libffi
+from pypy.rlib.jit_libffi import CIF_DESCRIPTION, CIF_DESCRIPTION_P
+from pypy.rlib.jit_libffi import FFI_TYPE, FFI_TYPE_P, FFI_TYPE_PP
+from pypy.rlib.jit_libffi import SIZE_OF_FFI_ARG
+from pypy.rlib.objectmodel import we_are_translated, instantiate
+from pypy.rlib.objectmodel import keepalive_until_here
+
+from pypy.module._cffi_backend.ctypeobj import W_CType
+from pypy.module._cffi_backend.ctypeptr import W_CTypePtrBase, W_CTypePointer
+from pypy.module._cffi_backend.ctypevoid import W_CTypeVoid
+from pypy.module._cffi_backend.ctypestruct import W_CTypeStruct
+from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveSigned
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveUnsigned
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveCharOrUniChar
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveFloat
+from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveLongDouble
+from pypy.module._cffi_backend import ctypearray, cdataobj, cerrno
+
+
+class W_CTypeFunc(W_CTypePtrBase):
+    _attrs_            = ['fargs', 'ellipsis', 'cif_descr']
+    _immutable_fields_ = ['fargs[*]', 'ellipsis', 'cif_descr']
+
+    def __init__(self, space, fargs, fresult, ellipsis):
+        extra = self._compute_extra_text(fargs, fresult, ellipsis)
+        size = rffi.sizeof(rffi.VOIDP)
+        W_CTypePtrBase.__init__(self, space, size, extra, 2, fresult,
+                                could_cast_anything=False)
+        self.fargs = fargs
+        self.ellipsis = bool(ellipsis)
+        # fresult is stored in self.ctitem
+
+        if not ellipsis:
+            # Functions with '...' varargs are stored without a cif_descr
+            # at all.  The cif is computed on every call from the actual
+            # types passed in.  For all other functions, the cif_descr
+            # is computed here.
+            CifDescrBuilder(fargs, fresult).rawallocate(self)
+
+    def new_ctypefunc_completing_argtypes(self, args_w):
+        space = self.space
+        nargs_declared = len(self.fargs)
+        fvarargs = [None] * len(args_w)
+        fvarargs[:nargs_declared] = self.fargs
+        for i in range(nargs_declared, len(args_w)):
+            w_obj = args_w[i]
+            if isinstance(w_obj, cdataobj.W_CData):
+                ct = w_obj.ctype.get_vararg_type()
+            else:
+                raise operationerrfmt(space.w_TypeError,
+                             "argument %d passed in the variadic part "
+                             "needs to be a cdata object (got %s)",
+                             i + 1, space.type(w_obj).getname(space))
+            fvarargs[i] = ct
+        ctypefunc = instantiate(W_CTypeFunc)
+        ctypefunc.space = space
+        ctypefunc.fargs = fvarargs
+        ctypefunc.ctitem = self.ctitem
+        CifDescrBuilder(fvarargs, self.ctitem).rawallocate(ctypefunc)
+        return ctypefunc
+
+    def __del__(self):
+        if self.cif_descr:
+            lltype.free(self.cif_descr, flavor='raw')
+
+    def _compute_extra_text(self, fargs, fresult, ellipsis):
+        argnames = ['(*)(']
+        for i, farg in enumerate(fargs):
+            if i > 0:
+                argnames.append(', ')
+            argnames.append(farg.name)
+        if ellipsis:
+            if len(fargs) > 0:
+                argnames.append(', ')
+            argnames.append('...')
+        argnames.append(')')
+        return ''.join(argnames)
+
+
+    def call(self, funcaddr, args_w):
+        if self.cif_descr:
+            # regular case: this function does not take '...' arguments
+            self = jit.promote(self)
+            nargs_declared = len(self.fargs)
+            if len(args_w) != nargs_declared:
+                space = self.space
+                raise operationerrfmt(space.w_TypeError,
+                                      "'%s' expects %d arguments, got %d",
+                                      self.name, nargs_declared, len(args_w))
+            return self._call(funcaddr, args_w)
+        else:
+            # call of a variadic function
+            return self.call_varargs(funcaddr, args_w)
+
+    @jit.dont_look_inside
+    def call_varargs(self, funcaddr, args_w):
+        nargs_declared = len(self.fargs)
+        if len(args_w) < nargs_declared:
+            space = self.space
+            raise operationerrfmt(space.w_TypeError,
+                                  "'%s' expects at least %d arguments, got %d",
+                                  self.name, nargs_declared, len(args_w))
+        completed = self.new_ctypefunc_completing_argtypes(args_w)
+        return completed._call(funcaddr, args_w)
+
+    # The following is the core of function calls.  It is @unroll_safe,
+    # which means that the JIT is free to unroll the argument handling.
+    # But in case the function takes variable arguments, we don't unroll
+    # this (yet) for better safety: this is handled by @dont_look_inside
+    # in call_varargs.
+    @jit.unroll_safe
+    def _call(self, funcaddr, args_w):
+        space = self.space
+        cif_descr = self.cif_descr
+        size = cif_descr.exchange_size
+        mustfree_max_plus_1 = 0
+        buffer = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw')
+        try:
+            for i in range(len(args_w)):
+                data = rffi.ptradd(buffer, cif_descr.exchange_args[i])
+                w_obj = args_w[i]
+                argtype = self.fargs[i]
+                if argtype.convert_argument_from_object(data, w_obj):
+                    # argtype is a pointer type, and w_obj a list/tuple/str
+                    mustfree_max_plus_1 = i + 1
+
+            ec = cerrno.get_errno_container(space)
+            cerrno.restore_errno_from(ec)
+            jit_libffi.jit_ffi_call(cif_descr,
+                                    rffi.cast(rffi.VOIDP, funcaddr),
+                                    buffer)
+            e = cerrno.get_real_errno()
+            cerrno.save_errno_into(ec, e)
+
+            resultdata = rffi.ptradd(buffer, cif_descr.exchange_result)
+            w_res = self.ctitem.copy_and_convert_to_object(resultdata)
+        finally:
+            for i in range(mustfree_max_plus_1):
+                argtype = self.fargs[i]
+                if isinstance(argtype, W_CTypePointer):
+                    data = rffi.ptradd(buffer, cif_descr.exchange_args[i])
+                    if get_mustfree_flag(data):
+                        raw_string = rffi.cast(rffi.CCHARPP, data)[0]
+                        lltype.free(raw_string, flavor='raw')
+            lltype.free(buffer, flavor='raw')
+        return w_res
+
+def get_mustfree_flag(data):
+    return ord(rffi.ptradd(data, -1)[0])
+
+def set_mustfree_flag(data, flag):
+    rffi.ptradd(data, -1)[0] = chr(flag)
+
+def _get_abi(space, name):
+    abi = getattr(clibffi, name)
+    assert isinstance(abi, int)
+    return space.wrap(abi)
+
+# ____________________________________________________________
+
+
+W_CTypeFunc.cif_descr = lltype.nullptr(CIF_DESCRIPTION)     # default value
+
+BIG_ENDIAN = sys.byteorder == 'big'
+
+
+# ----------
+# We attach to the classes small methods that return a 'ffi_type'
+def _missing_ffi_type(self, cifbuilder):
+    space = self.space
+    if self.size < 0:
+        raise operationerrfmt(space.w_TypeError,
+                              "ctype '%s' has incomplete type",
+                              self.name)
+    raise operationerrfmt(space.w_NotImplementedError,
+                          "ctype '%s' (size %d) not supported as argument"
+                          " or return value",
+                          self.name, self.size)
+
+def _struct_ffi_type(self, cifbuilder):
+    if self.size >= 0:
+        return cifbuilder.fb_struct_ffi_type(self)
+    return _missing_ffi_type(self, cifbuilder)
+
+def _primsigned_ffi_type(self, cifbuilder):
+    size = self.size
+    if   size == 1: return clibffi.ffi_type_sint8
+    elif size == 2: return clibffi.ffi_type_sint16
+    elif size == 4: return clibffi.ffi_type_sint32
+    elif size == 8: return clibffi.ffi_type_sint64
+    return _missing_ffi_type(self, cifbuilder)
+
+def _primunsigned_ffi_type(self, cifbuilder):
+    size = self.size
+    if   size == 1: return clibffi.ffi_type_uint8
+    elif size == 2: return clibffi.ffi_type_uint16
+    elif size == 4: return clibffi.ffi_type_uint32
+    elif size == 8: return clibffi.ffi_type_uint64
+    return _missing_ffi_type(self, cifbuilder)
+
+def _primfloat_ffi_type(self, cifbuilder):
+    size = self.size
+    if   size == 4: return clibffi.ffi_type_float
+    elif size == 8: return clibffi.ffi_type_double
+    return _missing_ffi_type(self, cifbuilder)
+
+def _primlongdouble_ffi_type(self, cifbuilder):
+    return clibffi.ffi_type_longdouble
+
+def _ptr_ffi_type(self, cifbuilder):
+    return clibffi.ffi_type_pointer
+
+def _void_ffi_type(self, cifbuilder):
+    return clibffi.ffi_type_void
+
+W_CType._get_ffi_type                       = _missing_ffi_type
+W_CTypeStruct._get_ffi_type                 = _struct_ffi_type
+W_CTypePrimitiveSigned._get_ffi_type        = _primsigned_ffi_type
+W_CTypePrimitiveCharOrUniChar._get_ffi_type = _primunsigned_ffi_type
+W_CTypePrimitiveUnsigned._get_ffi_type      = _primunsigned_ffi_type
+W_CTypePrimitiveFloat._get_ffi_type         = _primfloat_ffi_type
+W_CTypePrimitiveLongDouble._get_ffi_type    = _primlongdouble_ffi_type
+W_CTypePtrBase._get_ffi_type                = _ptr_ffi_type
+#W_CTypeVoid._get_ffi_type                  = _void_ffi_type -- special-cased
+# ----------
+
+
+class CifDescrBuilder(object):
+    rawmem = lltype.nullptr(rffi.CCHARP.TO)
+
+    def __init__(self, fargs, fresult):
+        self.fargs = fargs
+        self.fresult = fresult
+
+    def fb_alloc(self, size):
+        size = llmemory.raw_malloc_usage(size)
+        if not self.bufferp:
+            self.nb_bytes += size
+            return lltype.nullptr(rffi.CCHARP.TO)
+        else:
+            result = self.bufferp
+            self.bufferp = rffi.ptradd(result, size)
+            return result
+
+
+    def fb_fill_type(self, ctype, is_result_type):
+        if is_result_type and isinstance(ctype, W_CTypeVoid):
+            return clibffi.ffi_type_void
+        return ctype._get_ffi_type(self)
+
+    def fb_struct_ffi_type(self, ctype):
+        # We can't pass a struct that was completed by verify().
+        # Issue: assume verify() is given "struct { long b; ...; }".
+        # Then it will complete it in the same way whether it is actually
+        # "struct { long a, b; }" or "struct { double a; long b; }".
+        # But on 64-bit UNIX, these two structs are passed by value
+        # differently: e.g. on x86-64, "b" ends up in register "rsi" in
+        # the first case and "rdi" in the second case.
+        #
+        # Another reason for 'custom_field_pos' would be anonymous
+        # nested structures: we lost the information about having it
+        # here, so better safe (and forbid it) than sorry (and maybe
+        # crash).
+        space = self.space
+        if ctype.custom_field_pos:
+            raise OperationError(space.w_TypeError,
+                                 space.wrap(
+               "cannot pass as an argument a struct that was completed "
+               "with verify() (see pypy/module/_cffi_backend/ctypefunc.py "
+               "for details)"))
+
+        # allocate an array of (n + 1) ffi_types
+        n = len(ctype.fields_list)
+        elements = self.fb_alloc(rffi.sizeof(FFI_TYPE_P) * (n + 1))
+        elements = rffi.cast(FFI_TYPE_PP, elements)
+
+        # fill it with the ffi types of the fields
+        for i, cf in enumerate(ctype.fields_list):
+            if cf.is_bitfield():
+                raise OperationError(space.w_NotImplementedError,
+                    space.wrap("cannot pass as argument a struct "
+                               "with bit fields"))
+            ffi_subtype = self.fb_fill_type(cf.ctype, False)
+            if elements:
+                elements[i] = ffi_subtype
+
+        # zero-terminate the array
+        if elements:
+            elements[n] = lltype.nullptr(FFI_TYPE_P.TO)
+
+        # allocate and fill an ffi_type for the struct itself
+        ffistruct = self.fb_alloc(rffi.sizeof(FFI_TYPE))
+        ffistruct = rffi.cast(FFI_TYPE_P, ffistruct)
+        if ffistruct:
+            rffi.setintfield(ffistruct, 'c_size', ctype.size)
+            rffi.setintfield(ffistruct, 'c_alignment', ctype.alignof())
+            rffi.setintfield(ffistruct, 'c_type', clibffi.FFI_TYPE_STRUCT)
+            ffistruct.c_elements = elements
+
+        return ffistruct
+
+
+    def fb_build(self):
+        # Build a CIF_DESCRIPTION.  Actually this computes the size and
+        # allocates a larger amount of data.  It starts with a
+        # CIF_DESCRIPTION and continues with data needed for the CIF:
+        #
+        #  - the argument types, as an array of 'ffi_type *'.
+        #
+        #  - optionally, the result's and the arguments' ffi type data
+        #    (this is used only for 'struct' ffi types; in other cases the
+        #    'ffi_type *' just points to static data like 'ffi_type_sint32').
+        #
+        nargs = len(self.fargs)
+
+        # start with a cif_description (cif and exchange_* fields)
+        self.fb_alloc(llmemory.sizeof(CIF_DESCRIPTION, nargs))
+
+        # next comes an array of 'ffi_type*', one per argument
+        atypes = self.fb_alloc(rffi.sizeof(FFI_TYPE_P) * nargs)
+        self.atypes = rffi.cast(FFI_TYPE_PP, atypes)
+
+        # next comes the result type data
+        self.rtype = self.fb_fill_type(self.fresult, True)
+
+        # next comes each argument's type data
+        for i, farg in enumerate(self.fargs):
+            atype = self.fb_fill_type(farg, False)
+            if self.atypes:
+                self.atypes[i] = atype
+
+
+    def align_arg(self, n):
+        return (n + 7) & ~7
+
+    def fb_build_exchange(self, cif_descr):
+        nargs = len(self.fargs)
+
+        # first, enough room for an array of 'nargs' pointers
+        exchange_offset = rffi.sizeof(rffi.CCHARP) * nargs
+        exchange_offset = self.align_arg(exchange_offset)
+        cif_descr.exchange_result = exchange_offset
+        cif_descr.exchange_result_libffi = exchange_offset
+
+        if BIG_ENDIAN and self.fresult.is_primitive_integer:
+            # For results of precisely these types, libffi has a
+            # strange rule that they will be returned as a whole
+            # 'ffi_arg' if they are smaller.  The difference
+            # only matters on big-endian.
+            if self.fresult.size < SIZE_OF_FFI_ARG:
+                diff = SIZE_OF_FFI_ARG - self.fresult.size
+                cif_descr.exchange_result += diff
+
+        # then enough room for the result, rounded up to sizeof(ffi_arg)
+        exchange_offset += max(rffi.getintfield(self.rtype, 'c_size'),
+                               SIZE_OF_FFI_ARG)
+
+        # loop over args
+        for i, farg in enumerate(self.fargs):
+            if isinstance(farg, W_CTypePointer):
+                exchange_offset += 1   # for the "must free" flag
+            exchange_offset = self.align_arg(exchange_offset)
+            cif_descr.exchange_args[i] = exchange_offset
+            exchange_offset += rffi.getintfield(self.atypes[i], 'c_size')
+
+        # store the exchange data size
+        cif_descr.exchange_size = exchange_offset
+
+    def fb_extra_fields(self, cif_descr):
+        cif_descr.abi = clibffi.FFI_DEFAULT_ABI    # XXX
+        cif_descr.nargs = len(self.fargs)
+        cif_descr.rtype = self.rtype
+        cif_descr.atypes = self.atypes
+
+    @jit.dont_look_inside
+    def rawallocate(self, ctypefunc):
+        space = ctypefunc.space
+        self.space = space
+
+        # compute the total size needed in the CIF_DESCRIPTION buffer
+        self.nb_bytes = 0
+        self.bufferp = lltype.nullptr(rffi.CCHARP.TO)
+        self.fb_build()
+
+        # allocate the buffer
+        if we_are_translated():
+            rawmem = lltype.malloc(rffi.CCHARP.TO, self.nb_bytes,
+                                   flavor='raw')
+            rawmem = rffi.cast(CIF_DESCRIPTION_P, rawmem)
+        else:
+            # gross overestimation of the length below, but too bad
+            rawmem = lltype.malloc(CIF_DESCRIPTION_P.TO, self.nb_bytes,
+                                   flavor='raw')
+
+        # the buffer is automatically managed from the W_CTypeFunc instance
+        ctypefunc.cif_descr = rawmem
+
+        # call again fb_build() to really build the libffi data structures
+        self.bufferp = rffi.cast(rffi.CCHARP, rawmem)
+        self.fb_build()
+        assert self.bufferp == rffi.ptradd(rffi.cast(rffi.CCHARP, rawmem),
+                                           self.nb_bytes)
+
+        # fill in the 'exchange_*' fields
+        self.fb_build_exchange(rawmem)
+
+        # fill in the extra fields
+        self.fb_extra_fields(rawmem)
+
+        # call libffi's ffi_prep_cif() function
+        res = jit_libffi.jit_ffi_prep_cif(rawmem)
+        if res != clibffi.FFI_OK:
+            raise OperationError(space.w_SystemError,
+                space.wrap("libffi failed to build this function type"))
diff --git a/pypy/module/_cffi_backend/ctypeobj.py b/pypy/module/_cffi_backend/ctypeobj.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypeobj.py
@@ -0,0 +1,175 @@
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.gateway import interp2app
+from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.typedef import make_weakref_descr
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rlib.objectmodel import we_are_translated
+
+from pypy.module._cffi_backend import cdataobj
+
+
+class W_CType(Wrappable):
+    _attrs_   = ['space', 'size',  'name', 'name_position', '_lifeline_']
+    _immutable_fields_ = ['size?', 'name', 'name_position']
+    # note that 'size' is not strictly immutable, because it can change
+    # from -1 to the real value in the W_CTypeStruct subclass.
+
+    cast_anything = False
+    is_primitive_integer = False
+
+    def __init__(self, space, size, name, name_position):
+        self.space = space
+        self.size = size     # size of instances, or -1 if unknown
+        self.name = name     # the name of the C type as a string
+        self.name_position = name_position
+        # 'name_position' is the index in 'name' where it must be extended,
+        # e.g. with a '*' or a variable name.
+
+    def repr(self):
+        space = self.space
+        return space.wrap("<ctype '%s'>" % (self.name,))
+
+    def extra_repr(self, cdata):
+        if cdata:
+            return '0x%x' % rffi.cast(lltype.Unsigned, cdata)
+        else:
+            return 'NULL'
+
+    def is_char_ptr_or_array(self):
+        return False
+
+    def is_unichar_ptr_or_array(self):
+        return False
+
+    def newp(self, w_init):
+        space = self.space
+        raise operationerrfmt(space.w_TypeError,
+                              "expected a pointer or array ctype, got '%s'",
+                              self.name)
+
+    def cast(self, w_ob):
+        space = self.space
+        raise operationerrfmt(space.w_TypeError,
+                              "cannot cast to '%s'", self.name)
+
+    def int(self, cdata):
+        space = self.space
+        raise operationerrfmt(space.w_TypeError,
+                              "int() not supported on cdata '%s'", self.name)
+
+    def float(self, cdata):
+        space = self.space
+        raise operationerrfmt(space.w_TypeError,
+                              "float() not supported on cdata '%s'", self.name)
+
+    def convert_to_object(self, cdata):
+        space = self.space
+        raise operationerrfmt(space.w_TypeError,
+                              "cannot return a cdata '%s'", self.name)
+
+    def convert_from_object(self, cdata, w_ob):
+        space = self.space
+        raise operationerrfmt(space.w_TypeError,
+                              "cannot initialize cdata '%s'", self.name)
+
+    def convert_argument_from_object(self, cdata, w_ob):
+        self.convert_from_object(cdata, w_ob)
+        return False
+
+    def _convert_error(self, expected, w_got):
+        space = self.space
+        ob = space.interpclass_w(w_got)
+        if isinstance(ob, cdataobj.W_CData):
+            return operationerrfmt(space.w_TypeError,
+                                   "initializer for ctype '%s' must be a %s, "
+                                   "not cdata '%s'", self.name, expected,
+                                   ob.ctype.name)
+        else:
+            return operationerrfmt(space.w_TypeError,
+                                   "initializer for ctype '%s' must be a %s, "
+                                   "not %s", self.name, expected,
+                                   space.type(w_got).getname(space))
+
+    def _check_subscript_index(self, w_cdata, i):
+        space = self.space
+        raise operationerrfmt(space.w_TypeError,
+                              "cdata of type '%s' cannot be indexed",
+                              self.name)
+
+    def string(self, cdataobj, maxlen):
+        space = self.space
+        raise operationerrfmt(space.w_TypeError,
+                              "string(): unexpected cdata '%s' argument",
+                              self.name)
+
+    def add(self, cdata, i):
+        space = self.space
+        raise operationerrfmt(space.w_TypeError,
+                              "cannot add a cdata '%s' and a number",
+                              self.name)
+
+    def insert_name(self, extra, extra_position):
+        name = '%s%s%s' % (self.name[:self.name_position],
+                           extra,
+                           self.name[self.name_position:])
+        name_position = self.name_position + extra_position
+        return name, name_position
+
+    def alignof(self):
+        align = self._alignof()
+        if not we_are_translated():
+            # obscure hack when untranslated, maybe, approximate, don't use
+            if isinstance(align, llmemory.FieldOffset):
+                align = rffi.sizeof(align.TYPE.y)
+        else:
+            # a different hack when translated, to avoid seeing constants
+            # of a symbolic integer type
+            align = llmemory.raw_malloc_usage(align)
+        return align
+
+    def _alignof(self):
+        space = self.space
+        raise operationerrfmt(space.w_TypeError,
+                              "ctype '%s' is of unknown alignment",
+                              self.name)
+
+    def offsetof(self, fieldname):
+        space = self.space
+        raise OperationError(space.w_TypeError,
+                             space.wrap("not a struct or union ctype"))
+
+    def _getfields(self):
+        return None
+
+    def call(self, funcaddr, args_w):
+        space = self.space
+        raise operationerrfmt(space.w_TypeError,
+                              "cdata '%s' is not callable", self.name)
+
+    def iter(self, cdata):
+        space = self.space
+        raise operationerrfmt(space.w_TypeError,
+                              "cdata '%s' does not support iteration",
+                              self.name)
+
+    def get_vararg_type(self):
+        return self
+
+    def getcfield(self, attr):
+        space = self.space
+        raise operationerrfmt(space.w_AttributeError,
+                              "cdata '%s' has no attribute '%s'",
+                              self.name, attr)
+
+    def copy_and_convert_to_object(self, cdata):
+        return self.convert_to_object(cdata)
+
+
+W_CType.typedef = TypeDef(
+    'CTypeDescr',
+    __module__ = '_cffi_backend',
+    __repr__ = interp2app(W_CType.repr),
+    __weakref__ = make_weakref_descr(W_CType),
+    )
+W_CType.typedef.acceptable_as_base_class = False
diff --git a/pypy/module/_cffi_backend/ctypeprim.py b/pypy/module/_cffi_backend/ctypeprim.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypeprim.py
@@ -0,0 +1,332 @@
+"""
+Primitives.
+"""
+
+from pypy.interpreter.error import operationerrfmt
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib.rarithmetic import r_ulonglong
+from pypy.rlib.objectmodel import keepalive_until_here
+from pypy.rlib import jit
+
+from pypy.module._cffi_backend.ctypeobj import W_CType
+from pypy.module._cffi_backend import cdataobj, misc
+
+
+class W_CTypePrimitive(W_CType):
+    _attrs_            = ['align']
+    _immutable_fields_ = ['align']
+
+    def __init__(self, space, size, name, name_position, align):
+        W_CType.__init__(self, space, size, name, name_position)
+        self.align = align
+
+    def extra_repr(self, cdata):
+        w_ob = self.convert_to_object(cdata)
+        return self.space.str_w(self.space.repr(w_ob))
+
+    def _alignof(self):
+        return self.align
+
+    def cast_str(self, w_ob):
+        space = self.space
+        s = space.str_w(w_ob)
+        if len(s) != 1:
+            raise operationerrfmt(space.w_TypeError,
+                              "cannot cast string of length %d to ctype '%s'",
+                                  len(s), self.name)
+        return ord(s[0])
+
+    def cast_unicode(self, w_ob):
+        space = self.space
+        s = space.unicode_w(w_ob)
+        if len(s) != 1:
+            raise operationerrfmt(space.w_TypeError,
+                      "cannot cast unicode string of length %d to ctype '%s'",
+                                  len(s), self.name)
+        return ord(s[0])
+
+    def cast(self, w_ob):
+        from pypy.module._cffi_backend import ctypeptr
+        space = self.space
+        ob = space.interpclass_w(w_ob)
+        if (isinstance(ob, cdataobj.W_CData) and
+               isinstance(ob.ctype, ctypeptr.W_CTypePtrOrArray)):
+            value = rffi.cast(lltype.Signed, ob._cdata)
+            value = r_ulonglong(value)
+        elif space.isinstance_w(w_ob, space.w_str):
+            value = self.cast_str(w_ob)
+            value = r_ulonglong(value)
+        elif space.isinstance_w(w_ob, space.w_unicode):
+            value = self.cast_unicode(w_ob)
+            value = r_ulonglong(value)
+        else:
+            value = misc.as_unsigned_long_long(space, w_ob, strict=False)
+        w_cdata = cdataobj.W_CDataMem(space, self.size, self)
+        w_cdata.write_raw_integer_data(value)
+        return w_cdata
+
+    def _overflow(self, w_ob):
+        space = self.space
+        s = space.str_w(space.str(w_ob))
+        raise operationerrfmt(space.w_OverflowError,
+                              "integer %s does not fit '%s'", s, self.name)
+
+    def string(self, cdataobj, maxlen):
+        if self.size == 1:
+            s = cdataobj._cdata[0]
+            keepalive_until_here(cdataobj)
+            return self.space.wrap(s)
+        return W_CType.string(self, cdataobj, maxlen)
+
+
+class W_CTypePrimitiveCharOrUniChar(W_CTypePrimitive):
+    _attrs_ = []
+    is_primitive_integer = True
+
+    def get_vararg_type(self):
+        from pypy.module._cffi_backend import newtype
+        return newtype.new_primitive_type(self.space, "int")
+
+
+class W_CTypePrimitiveChar(W_CTypePrimitiveCharOrUniChar):
+    _attrs_ = []
+    cast_anything = True
+
+    def int(self, cdata):
+        return self.space.wrap(ord(cdata[0]))
+
+    def convert_to_object(self, cdata):
+        return self.space.wrap(cdata[0])
+
+    def _convert_to_char(self, w_ob):
+        space = self.space
+        if space.isinstance_w(w_ob, space.w_str):
+            s = space.str_w(w_ob)
+            if len(s) == 1:
+                return s[0]
+        ob = space.interpclass_w(w_ob)
+        if (isinstance(ob, cdataobj.W_CData) and
+               isinstance(ob.ctype, W_CTypePrimitiveChar)):
+            return ob._cdata[0]
+        raise self._convert_error("string of length 1", w_ob)
+
+    def convert_from_object(self, cdata, w_ob):
+        value = self._convert_to_char(w_ob)
+        cdata[0] = value
+
+
+class W_CTypePrimitiveUniChar(W_CTypePrimitiveCharOrUniChar):
+    _attrs_ = []
+
+    def int(self, cdata):
+        unichardata = rffi.cast(rffi.CWCHARP, cdata)
+        return self.space.wrap(ord(unichardata[0]))
+
+    def convert_to_object(self, cdata):
+        unichardata = rffi.cast(rffi.CWCHARP, cdata)
+        s = rffi.wcharpsize2unicode(unichardata, 1)
+        return self.space.wrap(s)
+
+    def string(self, cdataobj, maxlen):
+        w_res = self.convert_to_object(cdataobj._cdata)
+        keepalive_until_here(cdataobj)
+        return w_res
+
+    def _convert_to_unichar(self, w_ob):
+        space = self.space
+        if space.isinstance_w(w_ob, space.w_unicode):
+            s = space.unicode_w(w_ob)
+            if len(s) == 1:
+                return s[0]
+        ob = space.interpclass_w(w_ob)
+        if (isinstance(ob, cdataobj.W_CData) and
+               isinstance(ob.ctype, W_CTypePrimitiveUniChar)):
+            return rffi.cast(rffi.CWCHARP, ob._cdata)[0]
+        raise self._convert_error("unicode string of length 1", w_ob)
+
+    def convert_from_object(self, cdata, w_ob):
+        value = self._convert_to_unichar(w_ob)
+        rffi.cast(rffi.CWCHARP, cdata)[0] = value
+
+
+class W_CTypePrimitiveSigned(W_CTypePrimitive):
+    _attrs_            = ['value_fits_long', 'vmin', 'vrangemax']
+    _immutable_fields_ = ['value_fits_long', 'vmin', 'vrangemax']
+    is_primitive_integer = True
+
+    def __init__(self, *args):
+        W_CTypePrimitive.__init__(self, *args)
+        self.value_fits_long = self.size <= rffi.sizeof(lltype.Signed)
+        if self.size < rffi.sizeof(lltype.SignedLongLong):
+            sh = self.size * 8
+            self.vmin = r_ulonglong(-1) << (sh - 1)
+            self.vrangemax = (r_ulonglong(1) << sh) - 1
+
+    def int(self, cdata):
+        if self.value_fits_long:
+            # this case is to handle enums, but also serves as a slight
+            # performance improvement for some other primitive types
+            value = misc.read_raw_long_data(cdata, self.size)
+            return self.space.wrap(value)
+        else:
+            return self.convert_to_object(cdata)
+
+    def convert_to_object(self, cdata):
+        if self.value_fits_long:
+            value = misc.read_raw_long_data(cdata, self.size)
+            return self.space.wrap(value)
+        else:
+            value = misc.read_raw_signed_data(cdata, self.size)
+            return self.space.wrap(value)    # r_longlong => on 32-bit, 'long'
+
+    def convert_from_object(self, cdata, w_ob):
+        value = misc.as_long_long(self.space, w_ob)
+        if self.size < rffi.sizeof(lltype.SignedLongLong):
+            if r_ulonglong(value) - self.vmin > self.vrangemax:
+                self._overflow(w_ob)
+        value = r_ulonglong(value)
+        misc.write_raw_integer_data(cdata, value, self.size)
+
+    def get_vararg_type(self):
+        if self.size < rffi.sizeof(rffi.INT):
+            from pypy.module._cffi_backend import newtype
+            return newtype.new_primitive_type(self.space, "int")
+        return self
+
+
+class W_CTypePrimitiveUnsigned(W_CTypePrimitive):
+    _attrs_            = ['value_fits_long', 'vrangemax']
+    _immutable_fields_ = ['value_fits_long', 'vrangemax']
+    is_primitive_integer = True
+
+    def __init__(self, *args):
+        W_CTypePrimitive.__init__(self, *args)
+        self.value_fits_long = self.size < rffi.sizeof(lltype.Signed)
+        if self.size < rffi.sizeof(lltype.SignedLongLong):
+            sh = self.size * 8
+            self.vrangemax = (r_ulonglong(1) << sh) - 1
+
+    def int(self, cdata):
+        return self.convert_to_object(cdata)
+
+    def convert_from_object(self, cdata, w_ob):
+        value = misc.as_unsigned_long_long(self.space, w_ob, strict=True)
+        if self.size < rffi.sizeof(lltype.SignedLongLong):
+            if value > self.vrangemax:
+                self._overflow(w_ob)
+        misc.write_raw_integer_data(cdata, value, self.size)
+
+    def convert_to_object(self, cdata):
+        if self.value_fits_long:
+            value = misc.read_raw_ulong_data(cdata, self.size)
+            return self.space.wrap(value)
+        else:
+            value = misc.read_raw_unsigned_data(cdata, self.size)
+            return self.space.wrap(value)    # r_ulonglong => 'long' object
+
+    def get_vararg_type(self):
+        if self.size < rffi.sizeof(rffi.INT):
+            from pypy.module._cffi_backend import newtype
+            return newtype.new_primitive_type(self.space, "int")
+        return self
+
+
+class W_CTypePrimitiveFloat(W_CTypePrimitive):
+    _attrs_ = []
+
+    def cast(self, w_ob):
+        space = self.space
+        ob = space.interpclass_w(w_ob)
+        if isinstance(ob, cdataobj.W_CData):
+            if not isinstance(ob.ctype, W_CTypePrimitive):
+                raise operationerrfmt(space.w_TypeError,
+                                      "cannot cast ctype '%s' to ctype '%s'",
+                                      ob.ctype.name, self.name)
+            w_ob = ob.convert_to_object()
+        #
+        if space.isinstance_w(w_ob, space.w_str):
+            value = self.cast_str(w_ob)
+        elif space.isinstance_w(w_ob, space.w_unicode):
+            value = self.cast_unicode(w_ob)
+        else:
+            value = space.float_w(w_ob)
+        w_cdata = cdataobj.W_CDataMem(space, self.size, self)
+        if not isinstance(self, W_CTypePrimitiveLongDouble):
+            w_cdata.write_raw_float_data(value)
+        else:
+            self._to_longdouble_and_write(value, w_cdata._cdata)
+            keepalive_until_here(w_cdata)
+        return w_cdata
+
+    def int(self, cdata):
+        w_value = self.float(cdata)
+        return self.space.int(w_value)
+
+    def float(self, cdata):
+        return self.convert_to_object(cdata)
+
+    def convert_to_object(self, cdata):
+        value = misc.read_raw_float_data(cdata, self.size)
+        return self.space.wrap(value)
+
+    def convert_from_object(self, cdata, w_ob):
+        space = self.space
+        value = space.float_w(space.float(w_ob))
+        misc.write_raw_float_data(cdata, value, self.size)
+
+
+class W_CTypePrimitiveLongDouble(W_CTypePrimitiveFloat):
+    _attrs_ = []
+
+    @jit.dont_look_inside
+    def extra_repr(self, cdata):
+        lvalue = misc.read_raw_longdouble_data(cdata)
+        return misc.longdouble2str(lvalue)
+
+    def cast(self, w_ob):
+        space = self.space
+        ob = space.interpclass_w(w_ob)
+        if (isinstance(ob, cdataobj.W_CData) and
+                isinstance(ob.ctype, W_CTypePrimitiveLongDouble)):
+            w_cdata = self.convert_to_object(ob._cdata)
+            keepalive_until_here(ob)
+            return w_cdata
+        else:
+            return W_CTypePrimitiveFloat.cast(self, w_ob)
+
+    @jit.dont_look_inside
+    def _to_longdouble_and_write(self, value, cdata):
+        lvalue = rffi.cast(rffi.LONGDOUBLE, value)
+        misc.write_raw_longdouble_data(cdata, lvalue)
+
+    @jit.dont_look_inside
+    def _read_from_longdouble(self, cdata):
+        lvalue = misc.read_raw_longdouble_data(cdata)
+        value = rffi.cast(lltype.Float, lvalue)
+        return value
+
+    @jit.dont_look_inside
+    def _copy_longdouble(self, cdatasrc, cdatadst):
+        lvalue = misc.read_raw_longdouble_data(cdatasrc)
+        misc.write_raw_longdouble_data(cdatadst, lvalue)
+
+    def float(self, cdata):
+        value = self._read_from_longdouble(cdata)
+        return self.space.wrap(value)
+
+    def convert_to_object(self, cdata):
+        w_cdata = cdataobj.W_CDataMem(self.space, self.size, self)
+        self._copy_longdouble(cdata, w_cdata._cdata)
+        keepalive_until_here(w_cdata)
+        return w_cdata
+
+    def convert_from_object(self, cdata, w_ob):
+        space = self.space
+        ob = space.interpclass_w(w_ob)
+        if (isinstance(ob, cdataobj.W_CData) and
+                isinstance(ob.ctype, W_CTypePrimitiveLongDouble)):
+            self._copy_longdouble(ob._cdata, cdata)
+            keepalive_until_here(ob)
+        else:
+            value = space.float_w(space.float(w_ob))
+            self._to_longdouble_and_write(value, cdata)
diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypeptr.py
@@ -0,0 +1,291 @@
+"""
+Pointers.
+"""
+
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib.objectmodel import keepalive_until_here
+from pypy.rlib.rarithmetic import ovfcheck
+
+from pypy.module._cffi_backend.ctypeobj import W_CType
+from pypy.module._cffi_backend import cdataobj, misc, ctypeprim
+
+
+class W_CTypePtrOrArray(W_CType):
+    _attrs_            = ['ctitem', 'can_cast_anything', 'is_struct_ptr',
+                          'length']
+    _immutable_fields_ = ['ctitem', 'can_cast_anything', 'is_struct_ptr',
+                          'length']
+    length = -1
+
+    def __init__(self, space, size, extra, extra_position, ctitem,
+                 could_cast_anything=True):
+        from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
+        name, name_position = ctitem.insert_name(extra, extra_position)
+        W_CType.__init__(self, space, size, name, name_position)
+        # this is the "underlying type":
+        #  - for pointers, it is the pointed-to type
+        #  - for arrays, it is the array item type
+        #  - for functions, it is the return type
+        self.ctitem = ctitem
+        self.can_cast_anything = could_cast_anything and ctitem.cast_anything
+        self.is_struct_ptr = isinstance(ctitem, W_CTypeStructOrUnion)
+
+    def is_char_ptr_or_array(self):
+        return isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveChar)
+
+    def is_unichar_ptr_or_array(self):
+        return isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveUniChar)
+
+    def is_char_or_unichar_ptr_or_array(self):
+        return isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveCharOrUniChar)
+
+    def cast(self, w_ob):
+        # cast to a pointer, to a funcptr, or to an array.
+        # Note that casting to an array is an extension to the C language,
+        # which seems to be necessary in order to sanely get a
+        # <cdata 'int[3]'> at some address.
+        if self.size < 0:
+            return W_CType.cast(self, w_ob)
+        space = self.space
+        ob = space.interpclass_w(w_ob)
+        if (isinstance(ob, cdataobj.W_CData) and
+                isinstance(ob.ctype, W_CTypePtrOrArray)):
+            value = ob._cdata
+        else:
+            value = misc.as_unsigned_long_long(space, w_ob, strict=False)
+            value = rffi.cast(rffi.CCHARP, value)
+        return cdataobj.W_CData(space, value, self)
+
+    def convert_array_from_object(self, cdata, w_ob):
+        space = self.space
+        if (space.isinstance_w(w_ob, space.w_list) or
+            space.isinstance_w(w_ob, space.w_tuple)):
+            lst_w = space.listview(w_ob)
+            if self.length >= 0 and len(lst_w) > self.length:
+                raise operationerrfmt(space.w_IndexError,
+                    "too many initializers for '%s' (got %d)",
+                                      self.name, len(lst_w))
+            ctitem = self.ctitem
+            for i in range(len(lst_w)):
+                ctitem.convert_from_object(cdata, lst_w[i])
+                cdata = rffi.ptradd(cdata, ctitem.size)
+        elif isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveChar):
+            try:
+                s = space.str_w(w_ob)
+            except OperationError, e:
+                if not e.match(space, space.w_TypeError):
+                    raise
+                raise self._convert_error("str or list or tuple", w_ob)
+            n = len(s)
+            if self.length >= 0 and n > self.length:
+                raise operationerrfmt(space.w_IndexError,
+                                      "initializer string is too long for '%s'"
+                                      " (got %d characters)",
+                                      self.name, n)
+            for i in range(n):
+                cdata[i] = s[i]
+            if n != self.length:
+                cdata[n] = '\x00'
+        elif isinstance(self.ctitem, ctypeprim.W_CTypePrimitiveUniChar):
+            try:
+                s = space.unicode_w(w_ob)
+            except OperationError, e:
+                if not e.match(space, space.w_TypeError):
+                    raise
+                raise self._convert_error("unicode or list or tuple", w_ob)
+            n = len(s)
+            if self.length >= 0 and n > self.length:
+                raise operationerrfmt(space.w_IndexError,
+                              "initializer unicode string is too long for '%s'"
+                                      " (got %d characters)",
+                                      self.name, n)
+            unichardata = rffi.cast(rffi.CWCHARP, cdata)
+            for i in range(n):
+                unichardata[i] = s[i]
+            if n != self.length:
+                unichardata[n] = u'\x00'
+        else:
+            raise self._convert_error("list or tuple", w_ob)
+
+    def string(self, cdataobj, maxlen):
+        space = self.space
+        if isinstance(self.ctitem, ctypeprim.W_CTypePrimitive):
+            cdata = cdataobj._cdata
+            if not cdata:
+                raise operationerrfmt(space.w_RuntimeError,
+                                      "cannot use string() on %s",
+                                      space.str_w(cdataobj.repr()))
+            #
+            from pypy.module._cffi_backend import ctypearray
+            length = maxlen
+            if length < 0 and isinstance(self, ctypearray.W_CTypeArray):
+                length = cdataobj.get_array_length()
+            #
+            # pointer to a primitive type of size 1: builds and returns a str
+            if self.ctitem.size == rffi.sizeof(lltype.Char):
+                if length < 0:
+                    s = rffi.charp2str(cdata)
+                else:
+                    s = rffi.charp2strn(cdata, length)
+                keepalive_until_here(cdataobj)
+                return space.wrap(s)
+            #
+            # pointer to a wchar_t: builds and returns a unicode
+            if self.is_unichar_ptr_or_array():
+                cdata = rffi.cast(rffi.CWCHARP, cdata)
+                if length < 0:
+                    u = rffi.wcharp2unicode(cdata)
+                else:
+                    u = rffi.wcharp2unicoden(cdata, length)
+                keepalive_until_here(cdataobj)
+                return space.wrap(u)
+        #
+        return W_CType.string(self, cdataobj, maxlen)
+
+
+class W_CTypePtrBase(W_CTypePtrOrArray):
+    # base class for both pointers and pointers-to-functions
+    _attrs_ = []
+
+    def convert_to_object(self, cdata):
+        ptrdata = rffi.cast(rffi.CCHARPP, cdata)[0]
+        return cdataobj.W_CData(self.space, ptrdata, self)
+
+    def convert_from_object(self, cdata, w_ob):
+        space = self.space
+        ob = space.interpclass_w(w_ob)
+        if not isinstance(ob, cdataobj.W_CData):
+            raise self._convert_error("compatible pointer", w_ob)
+        other = ob.ctype
+        if not isinstance(other, W_CTypePtrBase):
+            from pypy.module._cffi_backend import ctypearray
+            if isinstance(other, ctypearray.W_CTypeArray):
+                other = other.ctptr
+            else:
+                raise self._convert_error("compatible pointer", w_ob)
+        if self is not other:
+            if not (self.can_cast_anything or other.can_cast_anything):
+                raise self._convert_error("compatible pointer", w_ob)
+
+        rffi.cast(rffi.CCHARPP, cdata)[0] = ob._cdata
+
+    def _alignof(self):
+        from pypy.module._cffi_backend import newtype
+        return newtype.alignment_of_pointer
+
+
+class W_CTypePointer(W_CTypePtrBase):
+    _attrs_ = []
+
+    def __init__(self, space, ctitem):
+        from pypy.module._cffi_backend import ctypearray
+        size = rffi.sizeof(rffi.VOIDP)
+        if isinstance(ctitem, ctypearray.W_CTypeArray):
+            extra = "(*)"    # obscure case: see test_array_add
+        else:
+            extra = " *"
+        W_CTypePtrBase.__init__(self, space, size, extra, 2, ctitem)
+
+    def newp(self, w_init):
+        space = self.space
+        ctitem = self.ctitem
+        datasize = ctitem.size
+        if datasize < 0:
+            raise operationerrfmt(space.w_TypeError,
+                "cannot instantiate ctype '%s' of unknown size",
+                                  self.name)
+        if self.is_struct_ptr:
+            # 'newp' on a struct-or-union pointer: in this case, we return
+            # a W_CDataPtrToStruct object which has a strong reference
+            # to a W_CDataNewOwning that really contains the structure.
+            cdatastruct = cdataobj.W_CDataNewOwning(space, datasize, ctitem)
+            cdata = cdataobj.W_CDataPtrToStructOrUnion(space,
+                                                       cdatastruct._cdata,
+                                                       self, cdatastruct)
+        else:
+            if self.is_char_or_unichar_ptr_or_array():
+                datasize *= 2       # forcefully add a null character
+            cdata = cdataobj.W_CDataNewOwning(space, datasize, self)
+        #
+        if not space.is_w(w_init, space.w_None):
+            ctitem.convert_from_object(cdata._cdata, w_init)
+            keepalive_until_here(cdata)
+        return cdata
+
+    def _check_subscript_index(self, w_cdata, i):
+        if (isinstance(w_cdata, cdataobj.W_CDataNewOwning) or
+            isinstance(w_cdata, cdataobj.W_CDataPtrToStructOrUnion)):
+            if i != 0:
+                space = self.space
+                raise operationerrfmt(space.w_IndexError,
+                                      "cdata '%s' can only be indexed by 0",
+                                      self.name)
+        return self
+
+    def add(self, cdata, i):
+        space = self.space
+        ctitem = self.ctitem
+        if ctitem.size < 0:
+            raise operationerrfmt(space.w_TypeError,
+                                  "ctype '%s' points to items of unknown size",
+                                  self.name)
+        p = rffi.ptradd(cdata, i * self.ctitem.size)
+        return cdataobj.W_CData(space, p, self)
+
+    def _prepare_pointer_call_argument(self, w_init):
+        space = self.space
+        if (space.isinstance_w(w_init, space.w_list) or
+            space.isinstance_w(w_init, space.w_tuple)):
+            length = space.int_w(space.len(w_init))
+        elif space.isinstance_w(w_init, space.w_basestring):
+            # from a string, we add the null terminator
+            length = space.int_w(space.len(w_init)) + 1
+        else:
+            return lltype.nullptr(rffi.CCHARP.TO)
+        if self.ctitem.size <= 0:
+            return lltype.nullptr(rffi.CCHARP.TO)
+        try:
+            datasize = ovfcheck(length * self.ctitem.size)
+        except OverflowError:
+            raise OperationError(space.w_OverflowError,
+                space.wrap("array size would overflow a ssize_t"))
+        result = lltype.malloc(rffi.CCHARP.TO, datasize,
+                               flavor='raw', zero=True)
+        try:
+            self.convert_array_from_object(result, w_init)
+        except Exception:
+            lltype.free(result, flavor='raw')
+            raise
+        return result
+
+    def convert_argument_from_object(self, cdata, w_ob):
+        from pypy.module._cffi_backend.ctypefunc import set_mustfree_flag
+        space = self.space
+        ob = space.interpclass_w(w_ob)
+        if isinstance(ob, cdataobj.W_CData):
+            buffer = lltype.nullptr(rffi.CCHARP.TO)
+        else:
+            buffer = self._prepare_pointer_call_argument(w_ob)
+        #
+        if buffer:
+            rffi.cast(rffi.CCHARPP, cdata)[0] = buffer
+            set_mustfree_flag(cdata, True)
+            return True
+        else:
+            set_mustfree_flag(cdata, False)
+            try:
+                self.convert_from_object(cdata, w_ob)
+            except OperationError:
+                if (self.is_struct_ptr and isinstance(ob, cdataobj.W_CData)
+                    and ob.ctype is self.ctitem):
+                    # special case to make the life of verifier.py easier:
+                    # if the formal argument type is 'struct foo *' but
+                    # we pass a 'struct foo', then get a pointer to it
+                    rffi.cast(rffi.CCHARPP, cdata)[0] = ob._cdata
+                else:
+                    raise
+            return False
+
+    def getcfield(self, attr):
+        return self.ctitem.getcfield(attr)
diff --git a/pypy/module/_cffi_backend/ctypestruct.py b/pypy/module/_cffi_backend/ctypestruct.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypestruct.py
@@ -0,0 +1,251 @@
+"""
+Struct and unions.
+"""
+
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.rpython.lltypesystem import rffi
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.typedef import TypeDef, interp_attrproperty
+from pypy.rlib.objectmodel import keepalive_until_here
+from pypy.rlib.rarithmetic import r_ulonglong, r_longlong, intmask
+from pypy.rlib import jit
+
+from pypy.module._cffi_backend.ctypeobj import W_CType
+from pypy.module._cffi_backend import cdataobj, ctypeprim, misc
+
+
+class W_CTypeStructOrUnion(W_CType):
+    _immutable_fields_ = ['alignment?', 'fields_list?', 'fields_dict?',
+                          'custom_field_pos?']
+    # fields added by complete_struct_or_union():
+    alignment = -1
+    fields_list = None
+    fields_dict = None
+    custom_field_pos = False
+
+    def __init__(self, space, name):
+        name = '%s %s' % (self.kind, name)
+        W_CType.__init__(self, space, -1, name, len(name))
+
+    def check_complete(self):
+        if self.fields_dict is None:
+            space = self.space
+            raise operationerrfmt(space.w_TypeError,
+                                  "'%s' is not completed yet", self.name)
+
+    def _alignof(self):
+        self.check_complete()
+        return self.alignment
+
+    def _getfields(self):
+        if self.size < 0:
+            return None
+        space = self.space
+        result = [None] * len(self.fields_list)
+        for fname, field in self.fields_dict.iteritems():
+            i = self.fields_list.index(field)
+            result[i] = space.newtuple([space.wrap(fname),
+                                        space.wrap(field)])
+        return space.newlist(result)
+
+    def convert_to_object(self, cdata):
+        space = self.space
+        self.check_complete()
+        return cdataobj.W_CData(space, cdata, self)
+
+    def copy_and_convert_to_object(self, cdata):
+        space = self.space
+        self.check_complete()
+        ob = cdataobj.W_CDataNewOwning(space, self.size, self)
+        misc._raw_memcopy(cdata, ob._cdata, self.size)
+        keepalive_until_here(ob)
+        return ob
+
+    def offsetof(self, fieldname):
+        self.check_complete()
+        try:
+            cfield = self.fields_dict[fieldname]
+        except KeyError:
+            space = self.space
+            raise OperationError(space.w_KeyError, space.wrap(fieldname))
+        return cfield.offset
+
+    def _copy_from_same(self, cdata, w_ob):
+        space = self.space
+        ob = space.interpclass_w(w_ob)
+        if isinstance(ob, cdataobj.W_CData):
+            if ob.ctype is self and self.size >= 0:
+                misc._raw_memcopy(ob._cdata, cdata, self.size)
+                keepalive_until_here(ob)
+                return True
+        return False
+
+    def _check_only_one_argument_for_union(self, w_ob):
+        pass
+
+    def convert_from_object(self, cdata, w_ob):
+        space = self.space
+        if self._copy_from_same(cdata, w_ob):
+            return
+
+        self._check_only_one_argument_for_union(w_ob)
+
+        if (space.isinstance_w(w_ob, space.w_list) or
+            space.isinstance_w(w_ob, space.w_tuple)):
+            lst_w = space.listview(w_ob)
+            if len(lst_w) > len(self.fields_list):
+                raise operationerrfmt(space.w_ValueError,
+                        "too many initializers for '%s' (got %d)",
+                                      self.name, len(lst_w))
+            for i in range(len(lst_w)):
+                self.fields_list[i].write(cdata, lst_w[i])
+
+        elif space.isinstance_w(w_ob, space.w_dict):
+            lst_w = space.fixedview(w_ob)
+            for i in range(len(lst_w)):
+                w_key = lst_w[i]
+                key = space.str_w(w_key)
+                try:
+                    cf = self.fields_dict[key]
+                except KeyError:
+                    space.raise_key_error(w_key)
+                    assert 0
+                cf.write(cdata, space.getitem(w_ob, w_key))
+
+        else:
+            raise self._convert_error("list or tuple or dict or struct-cdata",
+                                      w_ob)
+
+    @jit.elidable
+    def _getcfield_const(self, attr):
+        return self.fields_dict[attr]
+
+    def getcfield(self, attr):
+        if self.fields_dict is not None:
+            self = jit.promote(self)
+            attr = jit.promote_string(attr)
+            try:
+                return self._getcfield_const(attr)
+            except KeyError:
+                pass
+        return W_CType.getcfield(self, attr)
+
+
+class W_CTypeStruct(W_CTypeStructOrUnion):
+    kind = "struct"
+
+class W_CTypeUnion(W_CTypeStructOrUnion):
+    kind = "union"
+
+    def _check_only_one_argument_for_union(self, w_ob):
+        space = self.space
+        n = space.int_w(space.len(w_ob))
+        if n > 1:
+            raise operationerrfmt(space.w_ValueError,
+                                  "initializer for '%s': %d items given, but "
+                                  "only one supported (use a dict if needed)",
+                                  self.name, n)
+
+
+class W_CField(Wrappable):
+    _immutable_ = True
+
+    BS_REGULAR     = -1
+    BS_EMPTY_ARRAY = -2
+
+    def __init__(self, ctype, offset, bitshift, bitsize):
+        self.ctype = ctype
+        self.offset = offset
+        self.bitshift = bitshift # >= 0: bitshift; or BS_REGULAR/BS_EMPTY_ARRAY
+        self.bitsize = bitsize
+
+    def is_bitfield(self):
+        return self.bitshift >= 0
+
+    def make_shifted(self, offset):
+        return W_CField(self.ctype, offset + self.offset,
+                        self.bitshift, self.bitsize)
+
+    def read(self, cdata):
+        cdata = rffi.ptradd(cdata, self.offset)
+        if self.bitshift == self.BS_REGULAR:
+            return self.ctype.convert_to_object(cdata)
+        elif self.bitshift == self.BS_EMPTY_ARRAY:
+            from pypy.module._cffi_backend import ctypearray
+            ctype = self.ctype
+            assert isinstance(ctype, ctypearray.W_CTypeArray)
+            return cdataobj.W_CData(ctype.space, cdata, ctype.ctptr)
+        else:
+            return self.convert_bitfield_to_object(cdata)
+
+    def write(self, cdata, w_ob):
+        cdata = rffi.ptradd(cdata, self.offset)
+        if self.is_bitfield():
+            self.convert_bitfield_from_object(cdata, w_ob)
+        else:
+            self.ctype.convert_from_object(cdata, w_ob)
+
+    def convert_bitfield_to_object(self, cdata):
+        ctype = self.ctype
+        space = ctype.space
+        #
+        if isinstance(ctype, ctypeprim.W_CTypePrimitiveSigned):
+            value = r_ulonglong(misc.read_raw_signed_data(cdata, ctype.size))
+            valuemask = (r_ulonglong(1) << self.bitsize) - 1
+            shiftforsign = r_ulonglong(1) << (self.bitsize - 1)
+            value = ((value >> self.bitshift) + shiftforsign) & valuemask
+            result = r_longlong(value) - r_longlong(shiftforsign)
+            if ctype.value_fits_long:
+                return space.wrap(intmask(result))
+            else:
+                return space.wrap(result)
+        #
+        if isinstance(ctype, ctypeprim.W_CTypePrimitiveUnsigned):
+            value_fits_long = ctype.value_fits_long
+        elif isinstance(ctype, ctypeprim.W_CTypePrimitiveCharOrUniChar):
+            value_fits_long = True
+        else:
+            raise NotImplementedError
+        #
+        value = misc.read_raw_unsigned_data(cdata, ctype.size)
+        valuemask = (r_ulonglong(1) << self.bitsize) - 1
+        value = (value >> self.bitshift) & valuemask
+        if value_fits_long:
+            return space.wrap(intmask(value))
+        else:
+            return space.wrap(value)
+
+    def convert_bitfield_from_object(self, cdata, w_ob):
+        ctype = self.ctype
+        space = ctype.space
+        #
+        value = misc.as_long_long(space, w_ob)
+        if isinstance(ctype, ctypeprim.W_CTypePrimitiveSigned):
+            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:
+            fmin = r_longlong(0)
+            fmax = r_longlong((r_ulonglong(1) << self.bitsize) - 1)
+        if value < fmin or value > fmax:
+            raise operationerrfmt(space.w_OverflowError,
+                                  "value %d outside the range allowed by the "
+                                  "bit field width: %d <= x <= %d",
+                                  value, fmin, fmax)
+        rawmask = ((r_ulonglong(1) << self.bitsize) - 1) << self.bitshift
+        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)
+
+
+W_CField.typedef = TypeDef(
+    'CField',
+    __module__ = '_cffi_backend',
+    type = interp_attrproperty('ctype', W_CField),
+    offset = interp_attrproperty('offset', W_CField),
+    bitshift = interp_attrproperty('bitshift', W_CField),
+    bitsize = interp_attrproperty('bitsize', W_CField),
+    )
+W_CField.typedef.acceptable_as_base_class = False
diff --git a/pypy/module/_cffi_backend/ctypevoid.py b/pypy/module/_cffi_backend/ctypevoid.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/ctypevoid.py
@@ -0,0 +1,16 @@
+"""
+Void.
+"""
+
+from pypy.module._cffi_backend.ctypeobj import W_CType
+
+
+class W_CTypeVoid(W_CType):
+    _attrs_ = []
+    cast_anything = True
+
+    def __init__(self, space):
+        W_CType.__init__(self, space, -1, "void", len("void"))
+
+    def copy_and_convert_to_object(self, cdata):
+        return self.space.w_None
diff --git a/pypy/module/_cffi_backend/func.py b/pypy/module/_cffi_backend/func.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/func.py
@@ -0,0 +1,77 @@
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.rpython.lltypesystem import lltype, rffi
+
+from pypy.module._cffi_backend import ctypeobj, cdataobj
+
+
+# ____________________________________________________________
+
+ at unwrap_spec(ctype=ctypeobj.W_CType)
+def newp(space, ctype, w_init=None):
+    return ctype.newp(w_init)
+
+# ____________________________________________________________
+
+ at unwrap_spec(ctype=ctypeobj.W_CType)
+def cast(space, ctype, w_ob):
+    return ctype.cast(w_ob)
+
+# ____________________________________________________________
+
+ at unwrap_spec(ctype=ctypeobj.W_CType)
+def callback(space, ctype, w_callable, w_error=None):
+    from pypy.module._cffi_backend.ccallback import W_CDataCallback
+    return W_CDataCallback(space, ctype, w_callable, w_error)
+
+# ____________________________________________________________
+
+ at unwrap_spec(cdata=cdataobj.W_CData)
+def typeof(space, cdata):
+    return cdata.ctype
+
+# ____________________________________________________________
+
+def sizeof(space, w_obj):
+    ob = space.interpclass_w(w_obj)
+    if isinstance(ob, cdataobj.W_CData):
+        size = ob._sizeof()
+    elif isinstance(ob, ctypeobj.W_CType):
+        size = ob.size
+        if size < 0:
+            raise operationerrfmt(space.w_ValueError,
+                                  "ctype '%s' is of unknown size",
+                                  ob.name)
+    else:
+        raise OperationError(space.w_TypeError,
+                            space.wrap("expected a 'cdata' or 'ctype' object"))
+    return space.wrap(size)
+
+ at unwrap_spec(ctype=ctypeobj.W_CType)
+def alignof(space, ctype):
+    align = ctype.alignof()
+    return space.wrap(align)
+
+ at unwrap_spec(ctype=ctypeobj.W_CType, fieldname=str)
+def offsetof(space, ctype, fieldname):
+    ofs = ctype.offsetof(fieldname)
+    return space.wrap(ofs)
+
+ at unwrap_spec(ctype=ctypeobj.W_CType)
+def _getfields(space, ctype):
+    return ctype._getfields()
+
+# ____________________________________________________________
+
+ at unwrap_spec(ctype=ctypeobj.W_CType, replace_with=str)
+def getcname(space, ctype, replace_with):
+    p = ctype.name_position
+    s = '%s%s%s' % (ctype.name[:p], replace_with, ctype.name[p:])
+    return space.wrap(s)
+
+# ____________________________________________________________
+
+ at unwrap_spec(cdata=cdataobj.W_CData, maxlen=int)
+def string(space, cdata, maxlen=-1):
+    return cdata.ctype.string(cdata, maxlen)
diff --git a/pypy/module/_cffi_backend/libraryobj.py b/pypy/module/_cffi_backend/libraryobj.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/libraryobj.py
@@ -0,0 +1,106 @@
+from __future__ import with_statement
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.typedef import TypeDef
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib.rdynload import DLLHANDLE, dlopen, dlsym, dlclose, DLOpenError
+from pypy.rlib.rdynload import RTLD_GLOBAL
+
+from pypy.module._cffi_backend.cdataobj import W_CData
+from pypy.module._cffi_backend.ctypeobj import W_CType
+
+
+class W_Library(Wrappable):
+    _immutable_ = True
+    handle = rffi.cast(DLLHANDLE, 0)
+
+    def __init__(self, space, filename, is_global):
+        self.space = space
+        if is_global and RTLD_GLOBAL is not None:
+            mode = RTLD_GLOBAL
+        else:
+            mode = -1     # default value, corresponds to RTLD_LOCAL
+        with rffi.scoped_str2charp(filename) as ll_libname:
+            if filename is None:
+                filename = "<None>"
+            try:
+                self.handle = dlopen(ll_libname, mode)
+            except DLOpenError, e:
+                raise operationerrfmt(space.w_OSError,
+                                      "cannot load '%s': %s",
+                                      filename, e.msg)
+        self.name = filename
+
+    def __del__(self):
+        h = self.handle
+        if h != rffi.cast(DLLHANDLE, 0):
+            self.handle = rffi.cast(DLLHANDLE, 0)
+            dlclose(h)
+
+    def repr(self):
+        space = self.space
+        return space.wrap("<clibrary '%s'>" % self.name)
+
+    @unwrap_spec(ctype=W_CType, name=str)
+    def load_function(self, ctype, name):
+        from pypy.module._cffi_backend import ctypefunc, ctypeptr, ctypevoid
+        space = self.space
+        #
+        ok = False
+        if isinstance(ctype, ctypefunc.W_CTypeFunc):
+            ok = True
+        if (isinstance(ctype, ctypeptr.W_CTypePointer) and
+            isinstance(ctype.ctitem, ctypevoid.W_CTypeVoid)):
+            ok = True
+        if not ok:
+            raise operationerrfmt(space.w_TypeError,
+                                  "function cdata expected, got '%s'",
+                                  ctype.name)
+        #
+        try:
+            cdata = dlsym(self.handle, name)
+        except KeyError:
+            raise operationerrfmt(space.w_KeyError,
+                                  "function '%s' not found in library '%s'",
+                                  name, self.name)
+        return W_CData(space, rffi.cast(rffi.CCHARP, cdata), ctype)
+
+    @unwrap_spec(ctype=W_CType, name=str)
+    def read_variable(self, ctype, name):
+        space = self.space
+        try:
+            cdata = dlsym(self.handle, name)
+        except KeyError:
+            raise operationerrfmt(space.w_KeyError,
+                                  "variable '%s' not found in library '%s'",
+                                  name, self.name)
+        return ctype.convert_to_object(rffi.cast(rffi.CCHARP, cdata))
+
+    @unwrap_spec(ctype=W_CType, name=str)
+    def write_variable(self, ctype, name, w_value):
+        space = self.space
+        try:
+            cdata = dlsym(self.handle, name)
+        except KeyError:
+            raise operationerrfmt(space.w_KeyError,
+                                  "variable '%s' not found in library '%s'",
+                                  name, self.name)
+        ctype.convert_from_object(rffi.cast(rffi.CCHARP, cdata), w_value)
+
+
+W_Library.typedef = TypeDef(
+    'Library',
+    __module__ = '_cffi_backend',
+    __repr__ = interp2app(W_Library.repr),
+    load_function = interp2app(W_Library.load_function),
+    read_variable = interp2app(W_Library.read_variable),
+    write_variable = interp2app(W_Library.write_variable),
+    )
+W_Library.acceptable_as_base_class = False
+
+
+ at unwrap_spec(filename="str_or_None", is_global=int)
+def load_library(space, filename, is_global=0):
+    lib = W_Library(space, filename, is_global)
+    return space.wrap(lib)
diff --git a/pypy/module/_cffi_backend/misc.py b/pypy/module/_cffi_backend/misc.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/misc.py
@@ -0,0 +1,202 @@
+from __future__ import with_statement
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rlib.rarithmetic import r_ulonglong
+from pypy.rlib.unroll import unrolling_iterable
+from pypy.rlib import jit
+
+# ____________________________________________________________
+
+_prim_signed_types = unrolling_iterable([
+    (rffi.SIGNEDCHAR, rffi.SIGNEDCHARP),
+    (rffi.SHORT, rffi.SHORTP),
+    (rffi.INT, rffi.INTP),
+    (rffi.LONG, rffi.LONGP),
+    (rffi.LONGLONG, rffi.LONGLONGP)])
+
+_prim_unsigned_types = unrolling_iterable([
+    (rffi.UCHAR, rffi.UCHARP),
+    (rffi.USHORT, rffi.USHORTP),
+    (rffi.UINT, rffi.UINTP),
+    (rffi.ULONG, rffi.ULONGP),
+    (rffi.ULONGLONG, rffi.ULONGLONGP)])
+
+_prim_float_types = unrolling_iterable([
+    (rffi.FLOAT, rffi.FLOATP),
+    (rffi.DOUBLE, rffi.DOUBLEP)])
+
+def read_raw_signed_data(target, size):
+    for TP, TPP in _prim_signed_types:
+        if size == rffi.sizeof(TP):
+            return rffi.cast(lltype.SignedLongLong, rffi.cast(TPP, target)[0])
+    raise NotImplementedError("bad integer size")
+
+def read_raw_long_data(target, size):
+    for TP, TPP in _prim_signed_types:
+        if size == rffi.sizeof(TP):
+            assert rffi.sizeof(TP) <= rffi.sizeof(lltype.Signed)
+            return rffi.cast(lltype.Signed, rffi.cast(TPP, target)[0])
+    raise NotImplementedError("bad integer size")
+
+def read_raw_unsigned_data(target, size):
+    for TP, TPP in _prim_unsigned_types:
+        if size == rffi.sizeof(TP):
+            return rffi.cast(lltype.UnsignedLongLong, rffi.cast(TPP,target)[0])
+    raise NotImplementedError("bad integer size")
+
+def read_raw_ulong_data(target, size):
+    for TP, TPP in _prim_unsigned_types:
+        if size == rffi.sizeof(TP):
+            assert rffi.sizeof(TP) < rffi.sizeof(lltype.Signed)
+            return rffi.cast(lltype.Signed, rffi.cast(TPP,target)[0])
+    raise NotImplementedError("bad integer size")
+
+def read_raw_float_data(target, size):
+    for TP, TPP in _prim_float_types:
+        if size == rffi.sizeof(TP):
+            return rffi.cast(lltype.Float, rffi.cast(TPP, target)[0])
+    raise NotImplementedError("bad float size")
+
+def read_raw_longdouble_data(target):
+    return rffi.cast(rffi.LONGDOUBLEP, target)[0]
+
+def write_raw_integer_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")
+
+def write_raw_float_data(target, source, size):
+    for TP, TPP in _prim_float_types:
+        if size == rffi.sizeof(TP):
+            rffi.cast(TPP, target)[0] = rffi.cast(TP, source)
+            return
+    raise NotImplementedError("bad float size")
+
+def write_raw_longdouble_data(target, source):
+    rffi.cast(rffi.LONGDOUBLEP, target)[0] = source
+
+# ____________________________________________________________
+
+sprintf_longdouble = rffi.llexternal(
+    "sprintf", [rffi.CCHARP, rffi.CCHARP, rffi.LONGDOUBLE], lltype.Void,
+    _nowrapper=True, sandboxsafe=True)
+
+FORMAT_LONGDOUBLE = rffi.str2charp("%LE")
+
+def longdouble2str(lvalue):
+    with lltype.scoped_alloc(rffi.CCHARP.TO, 128) as p:    # big enough
+        sprintf_longdouble(p, FORMAT_LONGDOUBLE, lvalue)
+        return rffi.charp2str(p)
+
+# ____________________________________________________________
+
+
+UNSIGNED = 0x1000
+
+TYPES = [
+    ("int8_t",        1),
+    ("uint8_t",       1 | UNSIGNED),
+    ("int16_t",       2),
+    ("uint16_t",      2 | UNSIGNED),
+    ("int32_t",       4),
+    ("uint32_t",      4 | UNSIGNED),
+    ("int64_t",       8),
+    ("uint64_t",      8 | UNSIGNED),
+
+    ("intptr_t",      rffi.sizeof(rffi.INTPTR_T)),
+    ("uintptr_t",     rffi.sizeof(rffi.UINTPTR_T) | UNSIGNED),
+    ("ptrdiff_t",     rffi.sizeof(rffi.INTPTR_T)),   # XXX can it be different?
+    ("size_t",        rffi.sizeof(rffi.SIZE_T) | UNSIGNED),
+    ("ssize_t",       rffi.sizeof(rffi.SSIZE_T)),
+]
+
+
+def nonstandard_integer_types(space):
+    w_d = space.newdict()
+    for name, size in TYPES:
+        space.setitem(w_d, space.wrap(name), space.wrap(size))
+    return w_d
+
+# ____________________________________________________________
+
+def as_long_long(space, w_ob):
+    # (possibly) convert and cast a Python object to a long long.
+    # This version accepts a Python int too, and does convertions from
+    # other types of objects.  It refuses floats.
+    if space.is_w(space.type(w_ob), space.w_int):   # shortcut
+        return space.int_w(w_ob)
+    try:
+        bigint = space.bigint_w(w_ob)
+    except OperationError, e:
+        if not e.match(space, space.w_TypeError):
+            raise
+        if space.isinstance_w(w_ob, space.w_float):
+            raise
+        bigint = space.bigint_w(space.int(w_ob))
+    try:
+        return bigint.tolonglong()
+    except OverflowError:
+        raise OperationError(space.w_OverflowError, space.wrap(ovf_msg))
+
+def as_unsigned_long_long(space, w_ob, strict):
+    # (possibly) convert and cast a Python object to an unsigned long long.
+    # This accepts a Python int too, and does convertions from other types of
+    # objects.  If 'strict', complains with OverflowError; if 'not strict',
+    # mask the result and round floats.
+    if space.is_w(space.type(w_ob), space.w_int):   # shortcut
+        value = space.int_w(w_ob)
+        if strict and value < 0:
+            raise OperationError(space.w_OverflowError, space.wrap(neg_msg))
+        return r_ulonglong(value)
+    try:
+        bigint = space.bigint_w(w_ob)
+    except OperationError, e:
+        if not e.match(space, space.w_TypeError):
+            raise
+        if strict and space.isinstance_w(w_ob, space.w_float):
+            raise
+        bigint = space.bigint_w(space.int(w_ob))
+    if strict:
+        try:
+            return bigint.toulonglong()
+        except ValueError:
+            raise OperationError(space.w_OverflowError, space.wrap(neg_msg))
+        except OverflowError:
+            raise OperationError(space.w_OverflowError, space.wrap(ovf_msg))
+    else:
+        return bigint.ulonglongmask()
+
+neg_msg = "can't convert negative number to unsigned"
+ovf_msg = "long too big to convert"
+
+# ____________________________________________________________
+
+def _raw_memcopy(source, dest, size):
+    if jit.isconstant(size):
+        # for the JIT: first handle the case where 'size' is known to be
+        # a constant equal to 1, 2, 4, 8
+        for TP, TPP in _prim_unsigned_types:
+            if size == rffi.sizeof(TP):
+                rffi.cast(TPP, dest)[0] = rffi.cast(TPP, source)[0]
+                return
+    _raw_memcopy_opaque(source, dest, size)
+
+ at jit.dont_look_inside
+def _raw_memcopy_opaque(source, dest, size):
+    # push push push at the llmemory interface (with hacks that are all
+    # removed after translation)
+    zero = llmemory.itemoffsetof(rffi.CCHARP.TO, 0)
+    llmemory.raw_memcopy(
+        llmemory.cast_ptr_to_adr(source) + zero,
+        llmemory.cast_ptr_to_adr(dest) + zero,
+        size * llmemory.sizeof(lltype.Char))
+
+def _raw_memclear(dest, size):
+    # for now, only supports the cases of size = 1, 2, 4, 8
+    for TP, TPP in _prim_unsigned_types:
+        if size == rffi.sizeof(TP):
+            rffi.cast(TPP, dest)[0] = rffi.cast(TP, 0)
+            return
+    raise NotImplementedError("bad clear size")
diff --git a/pypy/module/_cffi_backend/newtype.py b/pypy/module/_cffi_backend/newtype.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/newtype.py
@@ -0,0 +1,275 @@
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.gateway import unwrap_spec
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib.rarithmetic import ovfcheck
+from pypy.rlib.objectmodel import specialize
+
+from pypy.module._cffi_backend import ctypeobj, ctypeprim, ctypeptr, ctypearray
+from pypy.module._cffi_backend import ctypestruct, ctypevoid, ctypeenum
+
+
+ at specialize.memo()
+def alignment(TYPE):
+    S = lltype.Struct('aligncheck', ('x', lltype.Char), ('y', TYPE))
+    return rffi.offsetof(S, 'y')
+
+alignment_of_pointer = alignment(rffi.CCHARP)
+
+# ____________________________________________________________
+
+
+PRIMITIVE_TYPES = {}
+
+def eptype(name, TYPE, ctypecls):
+    PRIMITIVE_TYPES[name] = ctypecls, rffi.sizeof(TYPE), alignment(TYPE)
+
+eptype("char",        lltype.Char,     ctypeprim.W_CTypePrimitiveChar)
+eptype("wchar_t",     lltype.UniChar,  ctypeprim.W_CTypePrimitiveUniChar)
+eptype("signed char", rffi.SIGNEDCHAR, ctypeprim.W_CTypePrimitiveSigned)
+eptype("short",       rffi.SHORT,      ctypeprim.W_CTypePrimitiveSigned)
+eptype("int",         rffi.INT,        ctypeprim.W_CTypePrimitiveSigned)
+eptype("long",        rffi.LONG,       ctypeprim.W_CTypePrimitiveSigned)
+eptype("long long",   rffi.LONGLONG,   ctypeprim.W_CTypePrimitiveSigned)
+eptype("unsigned char",      rffi.UCHAR,    ctypeprim.W_CTypePrimitiveUnsigned)
+eptype("unsigned short",     rffi.SHORT,    ctypeprim.W_CTypePrimitiveUnsigned)
+eptype("unsigned int",       rffi.INT,      ctypeprim.W_CTypePrimitiveUnsigned)
+eptype("unsigned long",      rffi.LONG,     ctypeprim.W_CTypePrimitiveUnsigned)
+eptype("unsigned long long", rffi.LONGLONG, ctypeprim.W_CTypePrimitiveUnsigned)
+eptype("float",  rffi.FLOAT,  ctypeprim.W_CTypePrimitiveFloat)
+eptype("double", rffi.DOUBLE, ctypeprim.W_CTypePrimitiveFloat)
+eptype("long double", rffi.LONGDOUBLE, ctypeprim.W_CTypePrimitiveLongDouble)
+
+ at unwrap_spec(name=str)
+def new_primitive_type(space, name):
+    try:
+        ctypecls, size, align = PRIMITIVE_TYPES[name]
+    except KeyError:
+        raise OperationError(space.w_KeyError, space.wrap(name))
+    ctype = ctypecls(space, size, name, len(name), align)
+    return ctype
+
+# ____________________________________________________________
+
+ at unwrap_spec(ctype=ctypeobj.W_CType)
+def new_pointer_type(space, ctype):
+    ctypepointer = ctypeptr.W_CTypePointer(space, ctype)
+    return ctypepointer
+
+# ____________________________________________________________
+
+ at unwrap_spec(ctptr=ctypeobj.W_CType)
+def new_array_type(space, ctptr, w_length):
+    if not isinstance(ctptr, ctypeptr.W_CTypePointer):
+        raise OperationError(space.w_TypeError,
+                             space.wrap("first arg must be a pointer ctype"))
+    ctitem = ctptr.ctitem
+    if ctitem.size < 0:
+        raise operationerrfmt(space.w_ValueError,
+                              "array item of unknown size: '%s'",
+                              ctitem.name)
+    if space.is_w(w_length, space.w_None):
+        length = -1
+        arraysize = -1
+        extra = '[]'
+    else:
+        length = space.getindex_w(w_length, space.w_OverflowError)
+        if length < 0:
+            raise OperationError(space.w_ValueError,
+                                 space.wrap("negative array length"))
+        try:
+            arraysize = ovfcheck(length * ctitem.size)
+        except OverflowError:
+            raise OperationError(space.w_OverflowError,
+                space.wrap("array size would overflow a ssize_t"))
+        extra = '[%d]' % length
+    #
+    ctype = ctypearray.W_CTypeArray(space, ctptr, length, arraysize, extra)
+    return ctype
+
+# ____________________________________________________________
+
+ at unwrap_spec(name=str)
+def new_struct_type(space, name):
+    return ctypestruct.W_CTypeStruct(space, name)
+
+ at unwrap_spec(name=str)
+def new_union_type(space, name):
+    return ctypestruct.W_CTypeUnion(space, name)
+
+ at unwrap_spec(ctype=ctypeobj.W_CType, totalsize=int, totalalignment=int)
+def complete_struct_or_union(space, ctype, w_fields, w_ignored=None,
+                             totalsize=-1, totalalignment=-1):
+    if (not isinstance(ctype, ctypestruct.W_CTypeStructOrUnion)
+            or ctype.size >= 0):
+        raise OperationError(space.w_TypeError,
+                             space.wrap("first arg must be a non-initialized"
+                                        " struct or union ctype"))
+
+    is_union = isinstance(ctype, ctypestruct.W_CTypeUnion)
+    maxsize = 1
+    alignment = 1
+    offset = 0
+    fields_w = space.listview(w_fields)
+    fields_list = []
+    fields_dict = {}
+    prev_bit_position = 0
+    custom_field_pos = False
+
+    for w_field in fields_w:
+        field_w = space.fixedview(w_field)
+        if not (2 <= len(field_w) <= 4):
+            raise OperationError(space.w_TypeError,
+                                 space.wrap("bad field descr"))
+        fname = space.str_w(field_w[0])
+        ftype = space.interp_w(ctypeobj.W_CType, field_w[1])
+        fbitsize = -1
+        foffset = -1
+        if len(field_w) > 2: fbitsize = space.int_w(field_w[2])
+        if len(field_w) > 3: foffset = space.int_w(field_w[3])
+        #
+        if fname in fields_dict:
+            raise operationerrfmt(space.w_KeyError,
+                                  "duplicate field name '%s'", fname)
+        #
+        if ftype.size < 0:
+            raise operationerrfmt(space.w_TypeError,
+                    "field '%s.%s' has ctype '%s' of unknown size",
+                                  ctype.name, fname, ftype.name)
+        #
+        falign = ftype.alignof()
+        if alignment < falign:
+            alignment = falign
+        #
+        if foffset < 0:
+            # align this field to its own 'falign' by inserting padding
+            offset = (offset + falign - 1) & ~(falign-1)
+        else:
+            # a forced field position: ignore the offset just computed,
+            # except to know if we must set 'custom_field_pos'
+            custom_field_pos |= (offset != foffset)
+            offset = foffset
+        #
+        if fbitsize < 0 or (
+                fbitsize == 8 * ftype.size and not
+                isinstance(ftype, ctypeprim.W_CTypePrimitiveCharOrUniChar)):
+            fbitsize = -1
+            if isinstance(ftype, ctypearray.W_CTypeArray) and ftype.length==0:
+                bitshift = ctypestruct.W_CField.BS_EMPTY_ARRAY
+            else:
+                bitshift = ctypestruct.W_CField.BS_REGULAR
+            prev_bit_position = 0
+        else:
+            if (not (isinstance(ftype, ctypeprim.W_CTypePrimitiveSigned) or
+                     isinstance(ftype, ctypeprim.W_CTypePrimitiveUnsigned) or
+                     isinstance(ftype, ctypeprim.W_CTypePrimitiveChar)) or
+                fbitsize == 0 or
+                fbitsize > 8 * ftype.size):
+                raise operationerrfmt(space.w_TypeError,
+                                      "invalid bit field '%s'", fname)
+            if prev_bit_position > 0:
+                prev_field = fields_list[-1]
+                assert prev_field.bitshift >= 0
+                if prev_field.ctype.size != ftype.size:
+                    raise OperationError(space.w_NotImplementedError,
+                        space.wrap("consecutive bit fields should be "
+                                   "declared with a same-sized type"))
+                if prev_bit_position + fbitsize > 8 * ftype.size:
+                    prev_bit_position = 0
+                else:
+                    # we can share the same field as 'prev_field'
+                    offset = prev_field.offset
+            bitshift = prev_bit_position
+            if not is_union:
+                prev_bit_position += fbitsize
+        #
+        if (len(fname) == 0 and
+            isinstance(ftype, ctypestruct.W_CTypeStructOrUnion)):
+            # a nested anonymous struct or union
+            srcfield2names = {}
+            for name, srcfld in ftype.fields_dict.items():
+                srcfield2names[srcfld] = name
+            for srcfld in ftype.fields_list:
+                fld = srcfld.make_shifted(offset)
+                fields_list.append(fld)
+                try:
+                    fields_dict[srcfield2names[srcfld]] = fld
+                except KeyError:
+                    pass
+            # always forbid such structures from being passed by value
+            custom_field_pos = True
+        else:
+            # a regular field
+            fld = ctypestruct.W_CField(ftype, offset, bitshift, fbitsize)
+            fields_list.append(fld)
+            fields_dict[fname] = fld
+        #
+        if maxsize < ftype.size:
+            maxsize = ftype.size
+        if not is_union:
+            offset += ftype.size
+
+    if is_union:
+        assert offset == 0
+        offset = maxsize
+    else:
+        if offset == 0:
+            offset = 1
+        offset = (offset + alignment - 1) & ~(alignment-1)
+
+    if totalsize < 0:
+        totalsize = offset
+    elif totalsize < offset:
+        raise operationerrfmt(space.w_TypeError,
+                     "%s cannot be of size %d: there are fields at least "
+                     "up to %d", ctype.name, totalsize, offset)
+    if totalalignment < 0:
+        totalalignment = alignment
+
+    ctype.size = totalsize
+    ctype.alignment = totalalignment
+    ctype.fields_list = fields_list
+    ctype.fields_dict = fields_dict
+    ctype.custom_field_pos = custom_field_pos
+
+# ____________________________________________________________
+
+def new_void_type(space):
+    ctype = ctypevoid.W_CTypeVoid(space)
+    return ctype
+
+# ____________________________________________________________
+
+ at unwrap_spec(name=str)
+def new_enum_type(space, name, w_enumerators, w_enumvalues):
+    enumerators_w = space.fixedview(w_enumerators)
+    enumvalues_w  = space.fixedview(w_enumvalues)
+    if len(enumerators_w) != len(enumvalues_w):
+        raise OperationError(space.w_ValueError,
+                             space.wrap("tuple args must have the same size"))
+    enumerators = [space.str_w(w) for w in enumerators_w]
+    enumvalues  = [space.int_w(w) for w in enumvalues_w]
+    ctype = ctypeenum.W_CTypeEnum(space, name, enumerators, enumvalues)
+    return ctype
+
+# ____________________________________________________________
+
+ at unwrap_spec(fresult=ctypeobj.W_CType, ellipsis=int)
+def new_function_type(space, w_fargs, fresult, ellipsis=0):
+    from pypy.module._cffi_backend import ctypefunc
+    fargs = []
+    for w_farg in space.fixedview(w_fargs):
+        farg = space.interpclass_w(w_farg)
+        if not isinstance(farg, ctypeobj.W_CType):
+            raise OperationError(space.w_TypeError,
+                space.wrap("first arg must be a tuple of ctype objects"))
+        if isinstance(farg, ctypearray.W_CTypeArray):
+            farg = farg.ctptr
+        fargs.append(farg)
+    #
+    if ((fresult.size < 0 and not isinstance(fresult, ctypevoid.W_CTypeVoid))
+            or isinstance(fresult, ctypearray.W_CTypeArray)):
+        raise operationerrfmt(space.w_TypeError,
+                              "invalid result type: '%s'", fresult.name)
+    #
+    fct = ctypefunc.W_CTypeFunc(space, fargs, fresult, ellipsis)
+    return fct
diff --git a/pypy/module/_cffi_backend/test/__init__.py b/pypy/module/_cffi_backend/test/__init__.py
new file mode 100644
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -0,0 +1,2037 @@
+# ____________________________________________________________
+
+import sys
+if sys.version_info < (3,):
+    type_or_class = "type"
+    mandatory_b_prefix = ''
+    mandatory_u_prefix = 'u'
+    readbuf = str
+    bufchar = lambda x: x
+    bytechr = chr
+    class U(object):
+        def __add__(self, other):
+            return eval('u'+repr(other).replace(r'\\u', r'\u')
+                                       .replace(r'\\U', r'\U'))
+    u = U()
+else:
+    type_or_class = "class"
+    long = int
+    unicode = str
+    unichr = chr
+    mandatory_b_prefix = 'b'
+    mandatory_u_prefix = ''
+    readbuf = lambda buf: buf.tobytes()
+    bufchar = ord
+    bytechr = lambda n: bytes([n])
+    u = ""
+
+def size_of_int():
+    BInt = new_primitive_type("int")
+    return sizeof(BInt)
+
+def size_of_long():
+    BLong = new_primitive_type("long")
+    return sizeof(BLong)
+
+def size_of_ptr():
+    BInt = new_primitive_type("int")
+    BPtr = new_pointer_type(BInt)
+    return sizeof(BPtr)
+
+
+def find_and_load_library(name, is_global=0):
+    import ctypes.util
+    if name is None:
+        path = None
+    else:
+        path = ctypes.util.find_library(name)
+    return load_library(path, is_global)
+
+def test_load_library():
+    x = find_and_load_library('c')
+    assert repr(x).startswith("<clibrary '")
+    x = find_and_load_library('c', 1)
+    assert repr(x).startswith("<clibrary '")
+
+def test_nonstandard_integer_types():
+    d = nonstandard_integer_types()
+    assert type(d) is dict
+    assert 'char' not in d
+    assert d['size_t'] in (0x1004, 0x1008)
+    assert d['size_t'] == d['ssize_t'] + 0x1000
+
+def test_new_primitive_type():
+    py.test.raises(KeyError, new_primitive_type, "foo")
+    p = new_primitive_type("signed char")
+    assert repr(p) == "<ctype 'signed char'>"
+
+def test_cast_to_signed_char():
+    p = new_primitive_type("signed char")
+    x = cast(p, -65 + 17*256)
+    assert repr(x) == "<cdata 'signed char' -65>"
+    assert repr(type(x)) == "<%s '_cffi_backend.CData'>" % type_or_class
+    assert int(x) == -65
+    x = cast(p, -66 + (1<<199)*256)
+    assert repr(x) == "<cdata 'signed char' -66>"
+    assert int(x) == -66
+    assert (x == cast(p, -66)) is False
+    assert (x != cast(p, -66)) is True
+    q = new_primitive_type("short")
+    assert (x == cast(q, -66)) is False
+    assert (x != cast(q, -66)) is True
+
+def test_sizeof_type():
+    py.test.raises(TypeError, sizeof, 42.5)
+    p = new_primitive_type("short")
+    assert sizeof(p) == 2
+
+def test_integer_types():
+    for name in ['signed char', 'short', 'int', 'long', 'long long']:
+        p = new_primitive_type(name)
+        size = sizeof(p)
+        min = -(1 << (8*size-1))
+        max = (1 << (8*size-1)) - 1
+        assert int(cast(p, min)) == min
+        assert int(cast(p, max)) == max
+        assert int(cast(p, min - 1)) == max
+        assert int(cast(p, max + 1)) == min
+        py.test.raises(TypeError, cast, p, None)
+        assert long(cast(p, min - 1)) == max
+        assert int(cast(p, b'\x08')) == 8
+        assert int(cast(p, u+'\x08')) == 8
+    for name in ['char', 'short', 'int', 'long', 'long long']:
+        p = new_primitive_type('unsigned ' + name)
+        size = sizeof(p)
+        max = (1 << (8*size)) - 1
+        assert int(cast(p, 0)) == 0
+        assert int(cast(p, max)) == max
+        assert int(cast(p, -1)) == max
+        assert int(cast(p, max + 1)) == 0
+        assert long(cast(p, -1)) == max
+        assert int(cast(p, b'\xFE')) == 254
+        assert int(cast(p, u+'\xFE')) == 254
+
+def test_no_float_on_int_types():
+    p = new_primitive_type('long')
+    py.test.raises(TypeError, float, cast(p, 42))
+    py.test.raises(TypeError, complex, cast(p, 42))
+
+def test_float_types():
+    INF = 1E200 * 1E200
+    for name in ["float", "double"]:
+        p = new_primitive_type(name)
+        assert bool(cast(p, 0))
+        assert bool(cast(p, INF))
+        assert bool(cast(p, -INF))
+        assert int(cast(p, -150)) == -150
+        assert int(cast(p, 61.91)) == 61
+        assert long(cast(p, 61.91)) == 61
+        assert type(int(cast(p, 61.91))) is int
+        assert type(int(cast(p, 1E22))) is long
+        assert type(long(cast(p, 61.91))) is long
+        assert type(long(cast(p, 1E22))) is long
+        py.test.raises(OverflowError, int, cast(p, INF))
+        py.test.raises(OverflowError, int, cast(p, -INF))
+        assert float(cast(p, 1.25)) == 1.25
+        assert float(cast(p, INF)) == INF
+        assert float(cast(p, -INF)) == -INF
+        if name == "float":
+            assert float(cast(p, 1.1)) != 1.1     # rounding error
+            assert float(cast(p, 1E200)) == INF   # limited range
+
+        assert cast(p, -1.1) != cast(p, -1.1)
+        assert repr(float(cast(p, -0.0))) == '-0.0'
+        assert float(cast(p, b'\x09')) == 9.0
+        assert float(cast(p, u+'\x09')) == 9.0
+        assert float(cast(p, True)) == 1.0
+        py.test.raises(TypeError, cast, p, None)
+
+def test_complex_types():
+    py.test.skip("later")
+    INF = 1E200 * 1E200
+    for name in ["float", "double"]:
+        p = new_primitive_type("_Complex " + name)
+        assert bool(cast(p, 0))
+        assert bool(cast(p, INF))
+        assert bool(cast(p, -INF))
+        assert bool(cast(p, 0j))
+        assert bool(cast(p, INF*1j))
+        assert bool(cast(p, -INF*1j))
+        py.test.raises(TypeError, int, cast(p, -150))
+        py.test.raises(TypeError, long, cast(p, -150))
+        py.test.raises(TypeError, float, cast(p, -150))
+        assert complex(cast(p, 1.25)) == 1.25
+        assert complex(cast(p, 1.25j)) == 1.25j
+        assert float(cast(p, INF*1j)) == INF*1j
+        assert float(cast(p, -INF)) == -INF
+        if name == "float":
+            assert complex(cast(p, 1.1j)) != 1.1j         # rounding error
+            assert complex(cast(p, 1E200+3j)) == INF+3j   # limited range
+            assert complex(cast(p, 3+1E200j)) == 3+INF*1j # limited range
+
+        assert cast(p, -1.1j) != cast(p, -1.1j)
+        assert repr(complex(cast(p, -0.0)).real) == '-0.0'
+        assert repr(complex(cast(p, -0j))) == '-0j'
+        assert complex(cast(p, '\x09')) == 9.0
+        assert complex(cast(p, True)) == 1.0
+        py.test.raises(TypeError, cast, p, None)
+        #
+        py.test.raises(cast, new_primitive_type(name), 1+2j)
+    py.test.raises(cast, new_primitive_type("int"), 1+2j)
+
+def test_character_type():
+    p = new_primitive_type("char")
+    assert bool(cast(p, '\x00'))
+    assert cast(p, '\x00') != cast(p, -17*256)
+    assert int(cast(p, 'A')) == 65
+    assert long(cast(p, 'A')) == 65
+    assert type(int(cast(p, 'A'))) is int
+    assert type(long(cast(p, 'A'))) is long
+    assert str(cast(p, 'A')) == repr(cast(p, 'A'))
+    assert repr(cast(p, 'A')) == "<cdata 'char' %s'A'>" % mandatory_b_prefix
+    assert repr(cast(p, 255)) == r"<cdata 'char' %s'\xff'>" % mandatory_b_prefix
+    assert repr(cast(p, 0)) == r"<cdata 'char' %s'\x00'>" % mandatory_b_prefix
+
+def test_pointer_type():
+    p = new_primitive_type("int")
+    assert repr(p) == "<ctype 'int'>"
+    p = new_pointer_type(p)
+    assert repr(p) == "<ctype 'int *'>"
+    p = new_pointer_type(p)
+    assert repr(p) == "<ctype 'int * *'>"
+    p = new_pointer_type(p)
+    assert repr(p) == "<ctype 'int * * *'>"
+
+def test_pointer_to_int():
+    BInt = new_primitive_type("int")
+    py.test.raises(TypeError, newp, BInt)
+    py.test.raises(TypeError, newp, BInt, None)
+    BPtr = new_pointer_type(BInt)
+    p = newp(BPtr)
+    assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int()
+    p = newp(BPtr, None)
+    assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int()
+    p = newp(BPtr, 5000)
+    assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int()
+    q = cast(BPtr, p)
+    assert repr(q).startswith("<cdata 'int *' 0x")
+    assert p == q
+    assert hash(p) == hash(q)
+
+def test_pointer_bool():
+    BInt = new_primitive_type("int")
+    BPtr = new_pointer_type(BInt)
+    p = cast(BPtr, 0)
+    assert bool(p) is False
+    p = cast(BPtr, 42)
+    assert bool(p) is True
+
+def test_pointer_to_pointer():
+    BInt = new_primitive_type("int")
+    BPtr = new_pointer_type(BInt)
+    BPtrPtr = new_pointer_type(BPtr)
+    p = newp(BPtrPtr, None)
+    assert repr(p) == "<cdata 'int * *' owning %d bytes>" % size_of_ptr()
+
+def test_reading_pointer_to_int():
+    BInt = new_primitive_type("int")
+    BPtr = new_pointer_type(BInt)
+    p = newp(BPtr, None)
+    assert p[0] == 0
+    p = newp(BPtr, 5000)
+    assert p[0] == 5000
+    py.test.raises(IndexError, "p[1]")
+    py.test.raises(IndexError, "p[-1]")
+
+def test_reading_pointer_to_float():
+    BFloat = new_primitive_type("float")
+    py.test.raises(TypeError, newp, BFloat, None)
+    BPtr = new_pointer_type(BFloat)
+    p = newp(BPtr, None)
+    assert p[0] == 0.0 and type(p[0]) is float
+    p = newp(BPtr, 1.25)
+    assert p[0] == 1.25 and type(p[0]) is float
+    p = newp(BPtr, 1.1)
+    assert p[0] != 1.1 and abs(p[0] - 1.1) < 1E-5   # rounding errors
+
+def test_cast_float_to_int():
+    for type in ["int", "unsigned int", "long", "unsigned long",
+                 "long long", "unsigned long long"]:
+        p = new_primitive_type(type)
+        assert int(cast(p, 4.2)) == 4
+        py.test.raises(TypeError, newp, new_pointer_type(p), 4.2)
+
+def test_newp_integer_types():
+    for name in ['signed char', 'short', 'int', 'long', 'long long']:
+        p = new_primitive_type(name)
+        pp = new_pointer_type(p)
+        size = sizeof(p)
+        min = -(1 << (8*size-1))
+        max = (1 << (8*size-1)) - 1
+        assert newp(pp, min)[0] == min
+        assert newp(pp, max)[0] == max
+        py.test.raises(OverflowError, newp, pp, min - 1)
+        py.test.raises(OverflowError, newp, pp, max + 1)
+    for name in ['char', 'short', 'int', 'long', 'long long']:
+        p = new_primitive_type('unsigned ' + name)
+        pp = new_pointer_type(p)
+        size = sizeof(p)
+        max = (1 << (8*size)) - 1
+        assert newp(pp, 0)[0] == 0
+        assert newp(pp, max)[0] == max
+        py.test.raises(OverflowError, newp, pp, -1)
+        py.test.raises(OverflowError, newp, pp, max + 1)
+
+def test_reading_pointer_to_char():
+    BChar = new_primitive_type("char")
+    py.test.raises(TypeError, newp, BChar, None)
+    BPtr = new_pointer_type(BChar)
+    p = newp(BPtr, None)
+    assert p[0] == b'\x00'
+    p = newp(BPtr, b'A')
+    assert p[0] == b'A'
+    py.test.raises(TypeError, newp, BPtr, 65)
+    py.test.raises(TypeError, newp, BPtr, b"foo")
+    py.test.raises(TypeError, newp, BPtr, u+"foo")
+    c = cast(BChar, b'A')
+    assert str(c) == repr(c)
+    assert int(c) == ord(b'A')
+    py.test.raises(TypeError, cast, BChar, b'foo')
+    py.test.raises(TypeError, cast, BChar, u+'foo')
+
+def test_reading_pointer_to_pointer():
+    BVoidP = new_pointer_type(new_void_type())
+    BCharP = new_pointer_type(new_primitive_type("char"))
+    BInt = new_primitive_type("int")
+    BIntPtr = new_pointer_type(BInt)
+    BIntPtrPtr = new_pointer_type(BIntPtr)
+    q = newp(BIntPtr, 42)
+    assert q[0] == 42
+    p = newp(BIntPtrPtr, None)
+    assert p[0] is not None
+    assert p[0] == cast(BVoidP, 0)
+    assert p[0] == cast(BCharP, 0)
+    assert p[0] != None
+    assert repr(p[0]) == "<cdata 'int *' NULL>"
+    p[0] = q
+    assert p[0] != cast(BVoidP, 0)
+    assert p[0] != cast(BCharP, 0)
+    assert p[0][0] == 42
+    q[0] += 1
+    assert p[0][0] == 43
+    p = newp(BIntPtrPtr, q)
+    assert p[0][0] == 43
+
+def test_load_standard_library():
+    if sys.platform == "win32":
+        py.test.raises(OSError, find_and_load_library, None)
+        return
+    x = find_and_load_library(None)
+    BVoidP = new_pointer_type(new_void_type())
+    assert x.load_function(BVoidP, 'strcpy')
+    py.test.raises(KeyError, x.load_function,
+                   BVoidP, 'xxx_this_function_does_not_exist')
+
+def test_hash_differences():
+    BChar = new_primitive_type("char")
+    BInt = new_primitive_type("int")
+    BFloat = new_primitive_type("float")
+    for i in range(1, 20):
+        if (hash(cast(BChar, chr(i))) !=
+            hash(cast(BInt, i))):
+            break
+    else:
+        raise AssertionError("hashes are equal")
+    for i in range(1, 20):
+        if hash(cast(BFloat, i)) != hash(float(i)):
+            break
+    else:
+        raise AssertionError("hashes are equal")
+
+def test_no_len_on_nonarray():
+    p = new_primitive_type("int")
+    py.test.raises(TypeError, len, cast(p, 42))
+
+def test_cmp_none():
+    p = new_primitive_type("int")
+    x = cast(p, 42)
+    assert (x == None) is False
+    assert (x != None) is True
+    assert (x == ["hello"]) is False
+    assert (x != ["hello"]) is True
+
+def test_invalid_indexing():
+    p = new_primitive_type("int")
+    x = cast(p, 42)
+    py.test.raises(TypeError, "p[0]")
+
+def test_default_str():
+    BChar = new_primitive_type("char")
+    x = cast(BChar, 42)
+    assert str(x) == repr(x)
+    BInt = new_primitive_type("int")
+    x = cast(BInt, 42)
+    assert str(x) == repr(x)
+    BArray = new_array_type(new_pointer_type(BInt), 10)
+    x = newp(BArray, None)
+    assert str(x) == repr(x)
+
+def test_default_unicode():
+    BInt = new_primitive_type("int")
+    x = cast(BInt, 42)
+    assert unicode(x) == unicode(repr(x))
+    BArray = new_array_type(new_pointer_type(BInt), 10)
+    x = newp(BArray, None)
+    assert unicode(x) == unicode(repr(x))
+
+def test_cast_from_cdataint():
+    BInt = new_primitive_type("int")
+    x = cast(BInt, 0)
+    y = cast(new_pointer_type(BInt), x)
+    assert bool(y) is False
+    #
+    x = cast(BInt, 42)
+    y = cast(BInt, x)
+    assert int(y) == 42
+    y = cast(new_primitive_type("char"), x)
+    assert int(y) == 42
+    y = cast(new_primitive_type("float"), x)
+    assert float(y) == 42.0
+    #
+    z = cast(BInt, 42.5)
+    assert int(z) == 42
+    z = cast(BInt, y)
+    assert int(z) == 42
+
+def test_array_type():
+    p = new_primitive_type("int")
+    assert repr(p) == "<ctype 'int'>"
+    #
+    py.test.raises(TypeError, new_array_type, new_pointer_type(p), "foo")
+    py.test.raises(ValueError, new_array_type, new_pointer_type(p), -42)
+    #
+    p1 = new_array_type(new_pointer_type(p), None)
+    assert repr(p1) == "<ctype 'int[]'>"
+    py.test.raises(ValueError, new_array_type, new_pointer_type(p1), 42)
+    #
+    p1 = new_array_type(new_pointer_type(p), 42)
+    p2 = new_array_type(new_pointer_type(p1), 25)
+    assert repr(p2) == "<ctype 'int[25][42]'>"
+    p2 = new_array_type(new_pointer_type(p1), None)
+    assert repr(p2) == "<ctype 'int[][42]'>"
+    #
+    py.test.raises(OverflowError,
+                   new_array_type, new_pointer_type(p), sys.maxsize+1)
+    py.test.raises(OverflowError,
+                   new_array_type, new_pointer_type(p), sys.maxsize // 3)
+
+def test_array_instance():
+    LENGTH = 1423
+    p = new_primitive_type("int")
+    p1 = new_array_type(new_pointer_type(p), LENGTH)
+    a = newp(p1, None)
+    assert repr(a) == "<cdata 'int[%d]' owning %d bytes>" % (
+        LENGTH, LENGTH * size_of_int())
+    assert len(a) == LENGTH
+    for i in range(LENGTH):
+        assert a[i] == 0
+    py.test.raises(IndexError, "a[LENGTH]")
+    py.test.raises(IndexError, "a[-1]")
+    for i in range(LENGTH):
+        a[i] = i * i + 1
+    for i in range(LENGTH):
+        assert a[i] == i * i + 1
+    e = py.test.raises(IndexError, "a[LENGTH+100] = 500")
+    assert ('(expected %d < %d)' % (LENGTH+100, LENGTH)) in str(e.value)
+    py.test.raises(TypeError, int, a)
+
+def test_array_of_unknown_length_instance():
+    p = new_primitive_type("int")
+    p1 = new_array_type(new_pointer_type(p), None)
+    py.test.raises(TypeError, newp, p1, None)
+    py.test.raises(ValueError, newp, p1, -42)
+    a = newp(p1, 42)
+    assert len(a) == 42
+    for i in range(42):
+        a[i] -= i
+    for i in range(42):
+        assert a[i] == -i
+    py.test.raises(IndexError, "a[42]")
+    py.test.raises(IndexError, "a[-1]")
+    py.test.raises(IndexError, "a[42] = 123")
+    py.test.raises(IndexError, "a[-1] = 456")
+
+def test_array_of_unknown_length_instance_with_initializer():
+    p = new_primitive_type("int")
+    p1 = new_array_type(new_pointer_type(p), None)
+    a = newp(p1, list(range(42)))
+    assert len(a) == 42
+    a = newp(p1, tuple(range(142)))
+    assert len(a) == 142
+
+def test_array_initializer():
+    p = new_primitive_type("int")
+    p1 = new_array_type(new_pointer_type(p), None)
+    a = newp(p1, list(range(100, 142)))
+    for i in range(42):
+        assert a[i] == 100 + i
+    #
+    p2 = new_array_type(new_pointer_type(p), 43)
+    a = newp(p2, tuple(range(100, 142)))
+    for i in range(42):
+        assert a[i] == 100 + i
+    assert a[42] == 0      # extra uninitialized item
+
+def test_array_add():
+    p = new_primitive_type("int")
+    p1 = new_array_type(new_pointer_type(p), 5)    # int[5]
+    p2 = new_array_type(new_pointer_type(p1), 3)   # int[3][5]
+    a = newp(p2, [list(range(n, n+5)) for n in [100, 200, 300]])
+    assert repr(a) == "<cdata 'int[3][5]' owning %d bytes>" % (
+        3*5*size_of_int(),)
+    assert repr(a + 0).startswith("<cdata 'int(*)[5]' 0x")
+    assert repr(a[0]).startswith("<cdata 'int[5]' 0x")
+    assert repr((a + 0)[0]).startswith("<cdata 'int[5]' 0x")
+    assert repr(a[0] + 0).startswith("<cdata 'int *' 0x")
+    assert type(a[0][0]) is int
+    assert type((a[0] + 0)[0]) is int
+
+def test_array_sub():
+    BInt = new_primitive_type("int")
+    BArray = new_array_type(new_pointer_type(BInt), 5)   # int[5]
+    a = newp(BArray, None)
+    p = a + 1
+    assert p - a == 1
+    assert p - (a+0) == 1
+    assert a == (p - 1)
+    BPtr = new_pointer_type(new_primitive_type("short"))
+    q = newp(BPtr, None)
+    py.test.raises(TypeError, "p - q")
+    py.test.raises(TypeError, "q - p")
+    py.test.raises(TypeError, "a - q")
+    e = py.test.raises(TypeError, "q - a")
+    assert str(e.value) == "cannot subtract cdata 'short *' and cdata 'int *'"
+
+def test_cast_primitive_from_cdata():
+    p = new_primitive_type("int")
+    n = cast(p, cast(p, -42))
+    assert int(n) == -42
+    #
+    p = new_primitive_type("unsigned int")
+    n = cast(p, cast(p, 42))
+    assert int(n) == 42
+    #
+    p = new_primitive_type("long long")
+    n = cast(p, cast(p, -(1<<60)))
+    assert int(n) == -(1<<60)
+    #
+    p = new_primitive_type("unsigned long long")
+    n = cast(p, cast(p, 1<<63))
+    assert int(n) == 1<<63
+    #
+    p = new_primitive_type("float")
+    n = cast(p, cast(p, 42.5))
+    assert float(n) == 42.5
+    #
+    p = new_primitive_type("char")
+    n = cast(p, cast(p, "A"))
+    assert int(n) == ord("A")
+
+def test_new_primitive_from_cdata():
+    p = new_primitive_type("int")
+    p1 = new_pointer_type(p)
+    n = newp(p1, cast(p, -42))
+    assert n[0] == -42
+    #
+    p = new_primitive_type("unsigned int")
+    p1 = new_pointer_type(p)
+    n = newp(p1, cast(p, 42))
+    assert n[0] == 42
+    #
+    p = new_primitive_type("float")
+    p1 = new_pointer_type(p)
+    n = newp(p1, cast(p, 42.5))
+    assert n[0] == 42.5
+    #
+    p = new_primitive_type("char")
+    p1 = new_pointer_type(p)
+    n = newp(p1, cast(p, "A"))
+    assert n[0] == b"A"
+
+def test_cast_between_pointers():
+    BIntP = new_pointer_type(new_primitive_type("int"))
+    BIntA = new_array_type(BIntP, None)
+    a = newp(BIntA, [40, 41, 42, 43, 44])
+    BShortP = new_pointer_type(new_primitive_type("short"))
+    b = cast(BShortP, a)
+    c = cast(BIntP, b)
+    assert c[3] == 43
+    BLongLong = new_primitive_type("long long")
+    d = cast(BLongLong, c)
+    e = cast(BIntP, d)
+    assert e[3] == 43
+    f = cast(BIntP, int(d))
+    assert f[3] == 43
+    #
+    b = cast(BShortP, 0)
+    assert not b
+    c = cast(BIntP, b)
+    assert not c
+    assert int(cast(BLongLong, c)) == 0
+
+def test_alignof():
+    BInt = new_primitive_type("int")
+    assert alignof(BInt) == sizeof(BInt)
+    BPtr = new_pointer_type(BInt)
+    assert alignof(BPtr) == sizeof(BPtr)
+    BArray = new_array_type(BPtr, None)
+    assert alignof(BArray) == alignof(BInt)
+
+def test_new_struct_type():
+    BStruct = new_struct_type("foo")
+    assert repr(BStruct) == "<ctype 'struct foo'>"
+    BPtr = new_pointer_type(BStruct)
+    assert repr(BPtr) == "<ctype 'struct foo *'>"
+    py.test.raises(TypeError, alignof, BStruct)
+
+def test_new_union_type():
+    BUnion = new_union_type("foo")
+    assert repr(BUnion) == "<ctype 'union foo'>"
+    BPtr = new_pointer_type(BUnion)
+    assert repr(BPtr) == "<ctype 'union foo *'>"
+
+def test_complete_struct():
+    BLong = new_primitive_type("long")
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BStruct = new_struct_type("foo")
+    assert _getfields(BStruct) is None
+    complete_struct_or_union(BStruct, [('a1', BLong, -1),
+                                       ('a2', BChar, -1),
+                                       ('a3', BShort, -1)])
+    d = _getfields(BStruct)
+    assert len(d) == 3
+    assert d[0][0] == 'a1'
+    assert d[0][1].type is BLong
+    assert d[0][1].offset == 0
+    assert d[0][1].bitshift == -1
+    assert d[0][1].bitsize == -1
+    assert d[1][0] == 'a2'
+    assert d[1][1].type is BChar
+    assert d[1][1].offset == sizeof(BLong)
+    assert d[1][1].bitshift == -1
+    assert d[1][1].bitsize == -1
+    assert d[2][0] == 'a3'
+    assert d[2][1].type is BShort
+    assert d[2][1].offset == sizeof(BLong) + sizeof(BShort)
+    assert d[2][1].bitshift == -1
+    assert d[2][1].bitsize == -1
+    assert sizeof(BStruct) == 2 * sizeof(BLong)
+    assert alignof(BStruct) == alignof(BLong)
+
+def test_complete_union():
+    BLong = new_primitive_type("long")
+    BChar = new_primitive_type("char")
+    BUnion = new_union_type("foo")
+    assert _getfields(BUnion) is None
+    complete_struct_or_union(BUnion, [('a1', BLong, -1),
+                                      ('a2', BChar, -1)])
+    d = _getfields(BUnion)
+    assert len(d) == 2
+    assert d[0][0] == 'a1'
+    assert d[0][1].type is BLong
+    assert d[0][1].offset == 0
+    assert d[1][0] == 'a2'
+    assert d[1][1].type is BChar
+    assert d[1][1].offset == 0
+    assert sizeof(BUnion) == sizeof(BLong)
+    assert alignof(BUnion) == alignof(BLong)
+
+def test_struct_instance():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("foo")
+    BStructPtr = new_pointer_type(BStruct)
+    p = cast(BStructPtr, 0)
+    py.test.raises(AttributeError, "p.a1")    # opaque
+    complete_struct_or_union(BStruct, [('a1', BInt, -1),
+                                       ('a2', BInt, -1)])
+    p = newp(BStructPtr, None)
+    s = p[0]
+    assert s.a1 == 0
+    s.a2 = 123
+    assert s.a1 == 0
+    assert s.a2 == 123
+    py.test.raises(OverflowError, "s.a1 = sys.maxsize+1")
+    assert s.a1 == 0
+    py.test.raises(AttributeError, "p.foobar")
+    py.test.raises(AttributeError, "s.foobar")
+
+def test_union_instance():
+    BInt = new_primitive_type("int")
+    BUInt = new_primitive_type("unsigned int")
+    BUnion = new_union_type("bar")
+    complete_struct_or_union(BUnion, [('a1', BInt, -1), ('a2', BUInt, -1)])
+    p = newp(new_pointer_type(BUnion), [-42])
+    bigval = -42 + (1 << (8*size_of_int()))
+    assert p.a1 == -42
+    assert p.a2 == bigval
+    p = newp(new_pointer_type(BUnion), {'a2': bigval})
+    assert p.a1 == -42
+    assert p.a2 == bigval
+    py.test.raises(OverflowError, newp, new_pointer_type(BUnion),
+                   {'a1': bigval})
+    p = newp(new_pointer_type(BUnion), [])
+    assert p.a1 == p.a2 == 0
+
+def test_struct_pointer():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BInt, -1),
+                                       ('a2', BInt, -1)])
+    p = newp(BStructPtr, None)
+    assert p.a1 == 0      # read/write via the pointer (C equivalent: '->')
+    p.a2 = 123
+    assert p.a1 == 0
+    assert p.a2 == 123
+
+def test_struct_init_list():
+    BVoidP = new_pointer_type(new_void_type())
+    BInt = new_primitive_type("int")
+    BIntPtr = new_pointer_type(BInt)
+    BStruct = new_struct_type("foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BInt, -1),
+                                       ('a2', BInt, -1),
+                                       ('a3', BInt, -1),
+                                       ('p4', BIntPtr, -1)])
+    s = newp(BStructPtr, [123, 456])
+    assert s.a1 == 123
+    assert s.a2 == 456
+    assert s.a3 == 0
+    assert s.p4 == cast(BVoidP, 0)
+    #
+    s = newp(BStructPtr, {'a2': 41122, 'a3': -123})
+    assert s.a1 == 0
+    assert s.a2 == 41122
+    assert s.a3 == -123
+    assert s.p4 == cast(BVoidP, 0)
+    #
+    py.test.raises(KeyError, newp, BStructPtr, {'foobar': 0})
+    #
+    p = newp(BIntPtr, 14141)
+    s = newp(BStructPtr, [12, 34, 56, p])
+    assert s.p4 == p
+    #
+    s = newp(BStructPtr, [12, 34, 56, cast(BVoidP, 0)])
+    assert s.p4 == cast(BVoidP, 0)
+    #
+    py.test.raises(TypeError, newp, BStructPtr, [12, 34, 56, None])
+
+def test_array_in_struct():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("foo")
+    BArrayInt5 = new_array_type(new_pointer_type(BInt), 5)
+    complete_struct_or_union(BStruct, [('a1', BArrayInt5, -1)])
+    s = newp(new_pointer_type(BStruct), [[20, 24, 27, 29, 30]])
+    assert s.a1[2] == 27
+    assert repr(s.a1).startswith("<cdata 'int[5]' 0x")
+
+def test_offsetof():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("foo")
+    py.test.raises(TypeError, offsetof, BInt, "abc")
+    py.test.raises(TypeError, offsetof, BStruct, "abc")
+    complete_struct_or_union(BStruct, [('abc', BInt, -1), ('def', BInt, -1)])
+    assert offsetof(BStruct, 'abc') == 0
+    assert offsetof(BStruct, 'def') == size_of_int()
+    py.test.raises(KeyError, offsetof, BStruct, "ghi")
+
+def test_function_type():
+    BInt = new_primitive_type("int")
+    BFunc = new_function_type((BInt, BInt), BInt, False)
+    assert repr(BFunc) == "<ctype 'int(*)(int, int)'>"
+    BFunc2 = new_function_type((), BFunc, False)
+    assert repr(BFunc2) == "<ctype 'int(*(*)())(int, int)'>"
+
+def test_function_type_taking_struct():
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BStruct = new_struct_type("foo")
+    complete_struct_or_union(BStruct, [('a1', BChar, -1),
+                                       ('a2', BShort, -1)])
+    BFunc = new_function_type((BStruct,), BShort, False)
+    assert repr(BFunc) == "<ctype 'short(*)(struct foo)'>"
+
+def test_function_void_result():
+    BVoid = new_void_type()
+    BInt = new_primitive_type("int")
+    BFunc = new_function_type((BInt, BInt), BVoid, False)
+    assert repr(BFunc) == "<ctype 'void(*)(int, int)'>"
+
+def test_function_void_arg():
+    BVoid = new_void_type()
+    BInt = new_primitive_type("int")
+    py.test.raises(TypeError, new_function_type, (BVoid,), BInt, False)
+
+def test_call_function_0():
+    BSignedChar = new_primitive_type("signed char")
+    BFunc0 = new_function_type((BSignedChar, BSignedChar), BSignedChar, False)
+    f = cast(BFunc0, _testfunc(0))
+    assert f(40, 2) == 42
+    assert f(-100, -100) == -200 + 256
+    py.test.raises(OverflowError, f, 128, 0)
+    py.test.raises(OverflowError, f, 0, 128)
+
+def test_call_function_1():
+    BInt = new_primitive_type("int")
+    BLong = new_primitive_type("long")
+    BFunc1 = new_function_type((BInt, BLong), BLong, False)
+    f = cast(BFunc1, _testfunc(1))
+    assert f(40, 2) == 42
+    assert f(-100, -100) == -200
+    int_max = (1 << (8*size_of_int()-1)) - 1
+    long_max = (1 << (8*size_of_long()-1)) - 1
+    if int_max == long_max:
+        assert f(int_max, 1) == - int_max - 1
+    else:
+        assert f(int_max, 1) == int_max + 1
+
+def test_call_function_2():
+    BLongLong = new_primitive_type("long long")
+    BFunc2 = new_function_type((BLongLong, BLongLong), BLongLong, False)
+    f = cast(BFunc2, _testfunc(2))
+    longlong_max = (1 << (8*sizeof(BLongLong)-1)) - 1
+    assert f(longlong_max - 42, 42) == longlong_max
+    assert f(43, longlong_max - 42) == - longlong_max - 1
+
+def test_call_function_3():
+    BFloat = new_primitive_type("float")
+    BDouble = new_primitive_type("double")
+    BFunc3 = new_function_type((BFloat, BDouble), BDouble, False)
+    f = cast(BFunc3, _testfunc(3))
+    assert f(1.25, 5.1) == 1.25 + 5.1     # exact
+    res = f(1.3, 5.1)
+    assert res != 6.4 and abs(res - 6.4) < 1E-5    # inexact
+
+def test_call_function_4():
+    BFloat = new_primitive_type("float")
+    BDouble = new_primitive_type("double")
+    BFunc4 = new_function_type((BFloat, BDouble), BFloat, False)
+    f = cast(BFunc4, _testfunc(4))
+    res = f(1.25, 5.1)
+    assert res != 6.35 and abs(res - 6.35) < 1E-5    # inexact
+
+def test_call_function_5():
+    BVoid = new_void_type()
+    BFunc5 = new_function_type((), BVoid, False)
+    f = cast(BFunc5, _testfunc(5))
+    f()   # did not crash
+
+def test_call_function_6():
+    BInt = new_primitive_type("int")
+    BIntPtr = new_pointer_type(BInt)
+    BFunc6 = new_function_type((BIntPtr,), BIntPtr, False)
+    f = cast(BFunc6, _testfunc(6))
+    x = newp(BIntPtr, 42)
+    res = f(x)
+    assert typeof(res) is BIntPtr
+    assert res[0] == 42 - 1000
+    #
+    BIntArray = new_array_type(BIntPtr, None)
+    BFunc6bis = new_function_type((BIntArray,), BIntPtr, False)
+    f = cast(BFunc6bis, _testfunc(6))
+    #
+    res = f([142])
+    assert typeof(res) is BIntPtr
+    assert res[0] == 142 - 1000
+    #
+    res = f((143,))
+    assert typeof(res) is BIntPtr
+    assert res[0] == 143 - 1000
+    #
+    x = newp(BIntArray, [242])
+    res = f(x)
+    assert typeof(res) is BIntPtr
+    assert res[0] == 242 - 1000
+    #
+    py.test.raises(TypeError, f, 123456)
+    py.test.raises(TypeError, f, "foo")
+    py.test.raises(TypeError, f, u+"bar")
+
+def test_call_function_7():
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BStruct = new_struct_type("foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BChar, -1),
+                                       ('a2', BShort, -1)])
+    BFunc7 = new_function_type((BStruct,), BShort, False)
+    f = cast(BFunc7, _testfunc(7))
+    res = f({'a1': b'A', 'a2': -4042})
+    assert res == -4042 + ord(b'A')
+    #
+    x = newp(BStructPtr, {'a1': b'A', 'a2': -4042})
+    res = f(x[0])
+    assert res == -4042 + ord(b'A')
+
+def test_call_function_20():
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BStruct = new_struct_type("foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BChar, -1),
+                                       ('a2', BShort, -1)])
+    BFunc20 = new_function_type((BStructPtr,), BShort, False)
+    f = cast(BFunc20, _testfunc(20))
+    x = newp(BStructPtr, {'a1': b'A', 'a2': -4042})
+    # test the exception that allows us to pass a 'struct foo' where the
+    # function really expects a 'struct foo *'.
+    res = f(x[0])
+    assert res == -4042 + ord(b'A')
+    assert res == f(x)
+
+def test_call_function_21():
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("foo")
+    complete_struct_or_union(BStruct, [('a', BInt, -1),
+                                       ('b', BInt, -1),
+                                       ('c', BInt, -1),
+                                       ('d', BInt, -1),
+                                       ('e', BInt, -1),
+                                       ('f', BInt, -1),
+                                       ('g', BInt, -1),
+                                       ('h', BInt, -1),
+                                       ('i', BInt, -1),
+                                       ('j', BInt, -1)])
+    BFunc21 = new_function_type((BStruct,), BInt, False)


More information about the pypy-commit mailing list