Author: Matti Picus <matti.pi...@gmail.com>
Branch: stdlib-2.7.16
Changeset: r97099:5e792e528dc7
Date: 2019-06-26 17:06 +0300
http://bitbucket.org/pypy/pypy/changeset/5e792e528dc7/

Log:    merge default into branch

diff too long, truncating to 2000 out of 9174 lines

diff --git a/extra_tests/cffi_tests/cffi0/test_parsing.py 
b/extra_tests/cffi_tests/cffi0/test_parsing.py
--- a/extra_tests/cffi_tests/cffi0/test_parsing.py
+++ b/extra_tests/cffi_tests/cffi0/test_parsing.py
@@ -410,7 +410,17 @@
 def test_enum():
     ffi = FFI()
     ffi.cdef("""
-        enum Enum { POS = +1, TWO = 2, NIL = 0, NEG = -1, OP = (POS+TWO)-1};
+        enum Enum {
+            POS = +1,
+            TWO = 2,
+            NIL = 0,
+            NEG = -1,
+            ADDSUB = (POS+TWO)-1,
+            DIVMULINT = (3 * 3) / 2,
+            SHIFT = (1 << 3) >> 1,
+            BINOPS = (0x7 & 0x1) | 0x8,
+            XOR = 0xf ^ 0xa
+        };
         """)
     needs_dlopen_none()
     C = ffi.dlopen(None)
@@ -418,7 +428,11 @@
     assert C.TWO == 2
     assert C.NIL == 0
     assert C.NEG == -1
-    assert C.OP == 2
+    assert C.ADDSUB == 2
+    assert C.DIVMULINT == 4
+    assert C.SHIFT == 4
+    assert C.BINOPS == 0b1001
+    assert C.XOR == 0b0101
 
 def test_stdcall():
     ffi = FFI()
diff --git a/extra_tests/cffi_tests/cffi0/test_verify.py 
b/extra_tests/cffi_tests/cffi0/test_verify.py
--- a/extra_tests/cffi_tests/cffi0/test_verify.py
+++ b/extra_tests/cffi_tests/cffi0/test_verify.py
@@ -2535,3 +2535,29 @@
         x.p = p
         x.cyclic = x
         del p, x
+
+def test_arithmetic_in_cdef():
+    for a in [0, 11, 15]:
+        ffi = FFI()
+        ffi.cdef("""
+            enum FOO {
+                DIVNN = ((-?) / (-3)),
+                DIVNP = ((-?) / (+3)),
+                DIVPN = ((+?) / (-3)),
+                MODNN = ((-?) % (-3)),
+                MODNP = ((-?) % (+3)),
+                MODPN = ((+?) % (-3)),
+                };
+        """.replace('?', str(a)))
+        lib = ffi.verify("""
+            enum FOO {
+                DIVNN = ((-?) / (-3)),
+                DIVNP = ((-?) / (+3)),
+                DIVPN = ((+?) / (-3)),
+                MODNN = ((-?) % (-3)),
+                MODNP = ((-?) % (+3)),
+                MODPN = ((+?) % (-3)),
+                };
+        """.replace('?', str(a)))
+        # the verify() crashes if the values in the enum are different from
+        # the values we computed ourselves from the cdef()
diff --git a/extra_tests/cffi_tests/cffi0/test_zintegration.py 
b/extra_tests/cffi_tests/cffi0/test_zintegration.py
--- a/extra_tests/cffi_tests/cffi0/test_zintegration.py
+++ b/extra_tests/cffi_tests/cffi0/test_zintegration.py
@@ -2,11 +2,13 @@
 import py, os, sys, shutil
 import subprocess
 from extra_tests.cffi_tests.udir import udir
+import pytest
 
 if sys.platform == 'win32':
-    py.test.skip('snippets do not run on win32')
+    pytestmark = pytest.mark.skip('snippets do not run on win32')
 if sys.version_info < (2, 7):
-    py.test.skip('fails e.g. on a Debian/Ubuntu which patches virtualenv'
+    pytestmark = pytest.mark.skip(
+                 'fails e.g. on a Debian/Ubuntu which patches virtualenv'
                  ' in a non-2.6-friendly way')
 
 def create_venv(name):
diff --git a/extra_tests/cffi_tests/cffi1/test_recompiler.py 
b/extra_tests/cffi_tests/cffi1/test_recompiler.py
--- a/extra_tests/cffi_tests/cffi1/test_recompiler.py
+++ b/extra_tests/cffi_tests/cffi1/test_recompiler.py
@@ -2339,3 +2339,77 @@
         typedef int foo_t; struct foo_s { void (*x)(foo_t); };
     """)
     py.test.raises(TypeError, ffi.new, "struct foo_s *")
+
+def test_from_buffer_struct():
+    ffi = FFI()
+    ffi.cdef("""struct foo_s { int a, b; };""")
+    lib = verify(ffi, "test_from_buffer_struct_p", """
+        struct foo_s { int a, b; };
+    """)
+    p = ffi.new("struct foo_s *", [-219239, 58974983])
+    q = ffi.from_buffer("struct foo_s[]", ffi.buffer(p))
+    assert ffi.typeof(q) == ffi.typeof("struct foo_s[]")
+    assert len(q) == 1
+    assert q[0].a == p.a
+    assert q[0].b == p.b
+    assert q == p
+    q = ffi.from_buffer("struct foo_s *", ffi.buffer(p))
+    assert ffi.typeof(q) == ffi.typeof("struct foo_s *")
+    assert q.a == p.a
+    assert q.b == p.b
+    assert q[0].a == p.a
+    assert q[0].b == p.b
+    assert q == p
+
+def test_unnamed_bitfield_1():
+    ffi = FFI()
+    ffi.cdef("""struct A { char : 1; };""")
+    lib = verify(ffi, "test_unnamed_bitfield_1", """
+        struct A { char : 1; };
+    """)
+    p = ffi.new("struct A *")
+    assert ffi.sizeof(p[0]) == 1
+    # Note: on gcc, the type name is ignored for anonymous bitfields
+    # and that's why the result is 1.  On MSVC, the result is
+    # sizeof("char") which is also 1.
+
+def test_unnamed_bitfield_2():
+    ffi = FFI()
+    ffi.cdef("""struct A {
+        short c : 1; short : 1; short d : 1; short : 1; };""")
+    lib = verify(ffi, "test_unnamed_bitfield_2", """
+        struct A {
+            short c : 1; short : 1; short d : 1; short : 1;
+        };
+    """)
+    p = ffi.new("struct A *")
+    assert ffi.sizeof(p[0]) == ffi.sizeof("short")
+
+def test_unnamed_bitfield_3():
+    ffi = FFI()
+    ffi.cdef("""struct A { struct { char : 1; char : 1; } b; };""")
+    lib = verify(ffi, "test_unnamed_bitfield_3", """
+        struct A { struct { char : 1; char : 1; } b; };
+    """)
+    p = ffi.new("struct A *")
+    assert ffi.sizeof(p[0]) == 1
+    # Note: on gcc, the type name is ignored for anonymous bitfields
+    # and that's why the result is 1.  On MSVC, the result is
+    # sizeof("char") which is also 1.
+
+def test_unnamed_bitfield_4():
+    ffi = FFI()
+    ffi.cdef("""struct A { struct {
+        unsigned c : 1; unsigned : 1; unsigned d : 1; unsigned : 1; } a;
+        };
+        struct B { struct A a; };""")
+    lib = verify(ffi, "test_unnamed_bitfield_4", """
+        struct A { struct {
+            unsigned c : 1; unsigned : 1; unsigned d : 1; unsigned : 1; } a;
+        };
+        struct B { struct A a; };
+    """)
+    b = ffi.new("struct B *")
+    a = ffi.new("struct A *")
+    assert ffi.sizeof(a[0]) == ffi.sizeof("unsigned")
+    assert ffi.sizeof(b[0]) == ffi.sizeof(a[0])
diff --git a/extra_tests/cffi_tests/embedding/test_basic.py 
b/extra_tests/cffi_tests/embedding/test_basic.py
--- a/extra_tests/cffi_tests/embedding/test_basic.py
+++ b/extra_tests/cffi_tests/embedding/test_basic.py
@@ -64,8 +64,8 @@
         output = popen.stdout.read()
         err = popen.wait()
         if err:
-            raise OSError("popen failed with exit code %r: %r" % (
-                err, args))
+            raise OSError(("popen failed with exit code %r: %r\n\n%s" % (
+                err, args, output)).rstrip())
         print(output.rstrip())
         return output
 
diff --git a/extra_tests/cffi_tests/embedding/test_performance.py 
b/extra_tests/cffi_tests/embedding/test_performance.py
--- a/extra_tests/cffi_tests/embedding/test_performance.py
+++ b/extra_tests/cffi_tests/embedding/test_performance.py
@@ -3,8 +3,8 @@
 from extra_tests.cffi_tests.embedding.test_basic import EmbeddingTests
 
 if sys.platform == 'win32':
-    import py
-    py.test.skip("written with POSIX functions")
+    import pytest
+    pytestmark = pytest.mark.skip("written with POSIX functions")
 
 
 class TestPerformance(EmbeddingTests):
diff --git a/lib-python/2.7/ctypes/test/test_byteswap.py 
b/lib-python/2.7/ctypes/test/test_byteswap.py
--- a/lib-python/2.7/ctypes/test/test_byteswap.py
+++ b/lib-python/2.7/ctypes/test/test_byteswap.py
@@ -2,7 +2,6 @@
 from binascii import hexlify
 
 from ctypes import *
-from ctypes.test import xfail
 
 def bin(s):
     return hexlify(memoryview(s)).upper()
diff --git a/lib-python/2.7/ctypes/test/test_loading.py 
b/lib-python/2.7/ctypes/test/test_loading.py
--- a/lib-python/2.7/ctypes/test/test_loading.py
+++ b/lib-python/2.7/ctypes/test/test_loading.py
@@ -2,7 +2,7 @@
 import sys, unittest
 import os
 from ctypes.util import find_library
-from ctypes.test import is_resource_enabled, xfail
+from ctypes.test import is_resource_enabled
 import test.test_support as support
 
 libc_name = None
@@ -87,7 +87,6 @@
 
         self.assertRaises(AttributeError, dll.__getitem__, 1234)
 
-    @xfail
     @unittest.skipUnless(os.name == "nt", 'Windows-specific test')
     def test_1703286_A(self):
         from _ctypes import LoadLibrary, FreeLibrary
@@ -99,7 +98,6 @@
         handle = LoadLibrary("advapi32")
         FreeLibrary(handle)
 
-    @xfail
     @unittest.skipUnless(os.name == "nt", 'Windows-specific test')
     def test_1703286_B(self):
         # Since on winXP 64-bit advapi32 loads like described
diff --git a/lib-python/2.7/distutils/sysconfig_pypy.py 
b/lib-python/2.7/distutils/sysconfig_pypy.py
--- a/lib-python/2.7/distutils/sysconfig_pypy.py
+++ b/lib-python/2.7/distutils/sysconfig_pypy.py
@@ -86,6 +86,7 @@
             arch = platform.machine()
         g['LDSHARED'] += ' -undefined dynamic_lookup'
         g['CC'] += ' -arch %s' % (arch,)
+        g['MACOSX_DEPLOYMENT_TARGET'] = '10.14'
 
     global _config_vars
     _config_vars = g
diff --git a/lib-python/2.7/test/test_ssl.py b/lib-python/2.7/test/test_ssl.py
--- a/lib-python/2.7/test/test_ssl.py
+++ b/lib-python/2.7/test/test_ssl.py
@@ -801,7 +801,11 @@
             ctx.set_ciphers("^$:,;?*'dorothyx")
 
     @skip_if_broken_ubuntu_ssl
-    def test_options(self):
+    def _test_options(self):
+        '''
+        Disable this test, it is too flaky. Different platforms define
+        different defaults
+        '''
         ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
         # OP_ALL | OP_NO_SSLv2 | OP_NO_SSLv3 is the default value
         default = (ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3)
diff --git a/lib-python/2.7/test/test_timeit.py 
b/lib-python/2.7/test/test_timeit.py
--- a/lib-python/2.7/test/test_timeit.py
+++ b/lib-python/2.7/test/test_timeit.py
@@ -317,9 +317,9 @@
     def test_main_recommends_perf(self):
         s = self.run_main(seconds_per_increment=2.0, switches=['-n35', '-s', 
'print("CustomSetup")'])
         self.assertIn(dedent("""\
-            WARNING: timeit is a very unreliable tool. use perf or something 
else for real measurements
+            WARNING: timeit is a very unreliable tool. use pyperf or something 
else for real measurements
         """), s)
-        self.assertIn("-m pip install perf", s)
+        self.assertIn("-m pip install pyperf", s)
 
 
 
diff --git a/lib-python/2.7/timeit.py b/lib-python/2.7/timeit.py
--- a/lib-python/2.7/timeit.py
+++ b/lib-python/2.7/timeit.py
@@ -308,10 +308,10 @@
             return 0
     setup = "\n".join(setup) or "pass"
 
-    print "WARNING: timeit is a very unreliable tool. use perf or something 
else for real measurements"
+    print "WARNING: timeit is a very unreliable tool. use pyperf or something 
else for real measurements"
     executable = os.path.basename(sys.executable)
-    print "%s -m pip install perf" % executable
-    print "%s -m perf timeit %s" % (
+    print "%s -m pip install pyperf" % executable
+    print "%s -m pyperf timeit %s" % (
         executable,
         " ".join([(arg if arg.startswith("-") else repr(arg))
                         for arg in origargs]), )
diff --git a/lib-python/conftest.py b/lib-python/conftest.py
--- a/lib-python/conftest.py
+++ b/lib-python/conftest.py
@@ -177,7 +177,7 @@
     RegrTest('test_copy_reg.py', core=True),
     RegrTest('test_cpickle.py', core=True),
     RegrTest('test_cprofile.py'),
-    RegrTest('test_crypt.py', usemodules='crypt'),
+    RegrTest('test_crypt.py'),
     RegrTest('test_csv.py', usemodules='_csv'),
     RegrTest('test_ctypes.py', usemodules="_rawffi thread cpyext"),
     RegrTest('test_curses.py'),
diff --git a/lib_pypy/_cffi_ssl/_cffi_src/openssl/ssl.py 
b/lib_pypy/_cffi_ssl/_cffi_src/openssl/ssl.py
--- a/lib_pypy/_cffi_ssl/_cffi_src/openssl/ssl.py
+++ b/lib_pypy/_cffi_ssl/_cffi_src/openssl/ssl.py
@@ -40,6 +40,11 @@
  * supported
  */
 static const long Cryptography_HAS_OP_NO_COMPRESSION;
+/* Internally invented symbol to tell us if SSL_OP_ENABLE_MIDDLEBOX_COMPAT is
+ * supported
+ */
+static const long Cryptography_HAS_OP_ENABLE_MIDDLEBOX_COMPAT;
+
 static const long Cryptography_HAS_SSL_OP_MSIE_SSLV2_RSA_PADDING;
 static const long Cryptography_HAS_SSL_SET_SSL_CTX;
 static const long Cryptography_HAS_SSL_OP_NO_TICKET;
@@ -73,6 +78,7 @@
 static const long SSL_OP_NETSCAPE_CHALLENGE_BUG;
 static const long SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG;
 static const long SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG;
+static const long SSL_OP_ENABLE_MIDDLEBOX_COMPAT;
 static const long SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER;
 static const long SSL_OP_MSIE_SSLV2_RSA_PADDING;
 static const long SSL_OP_SSLEAY_080_CLIENT_DH_BUG;
@@ -562,6 +568,13 @@
 const long SSL_OP_NO_COMPRESSION = 0;
 #endif
 
+#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
+static const long Cryptography_HAS_OP_ENABLE_MIDDLEBOX_COMPAT = 1;
+#else
+static const long Cryptography_HAS_OP_ENABLE_MIDDLEBOX_COMPAT = 0;
+const long SSL_OP_ENABLE_MIDDLEBOX_COMPAT = 0;
+#endif
+
 #ifdef SSL_OP_NO_TLSv1_1
 static const long Cryptography_HAS_TLSv1_1 = 1;
 #else
diff --git a/lib_pypy/_cffi_ssl/_stdssl/__init__.py 
b/lib_pypy/_cffi_ssl/_stdssl/__init__.py
--- a/lib_pypy/_cffi_ssl/_stdssl/__init__.py
+++ b/lib_pypy/_cffi_ssl/_stdssl/__init__.py
@@ -87,12 +87,12 @@
     PROTOCOL_TLSv1 = 3
     PROTOCOL_TLSv1_1 = 4
     PROTOCOL_TLSv1_2 = 5
+# PROTOCOL_TLS_CLIENT = 0x10
+# PROTOCOL_TLS_SERVER = 0x11
 if lib.Cryptography_HAS_TLSv1_3:
     HAS_TLSv1_3 = True
 else:
     HAS_TLSv1_3 = False
-PROTOCOL_TLS_CLIENT = 0x10
-PROTOCOL_TLS_SERVER = 0x11
 
 _PROTOCOL_NAMES = (name for name in dir(lib) if name.startswith('PROTOCOL_'))
 
@@ -219,6 +219,7 @@
     def _new__ssl_socket(sslctx, sock, socket_type, server_hostname, ssl_sock):
         self = _SSLSocket(sslctx)
         ctx = sslctx.ctx
+        self.owner = ssl_sock  # weakref
 
         if server_hostname:
             if isinstance(server_hostname, unicode):
@@ -289,7 +290,8 @@
     def owner(self, value):
         if value is None:
             self._owner = None
-        self._owner = weakref.ref(value)
+        else:
+            self._owner = weakref.ref(value)
 
     @property
     def context(self):
@@ -337,7 +339,7 @@
                 sockstate = SOCKET_OPERATION_OK
 
             if sockstate == SOCKET_HAS_TIMED_OUT:
-                raise socket.timeout("The handshake operation timed out")
+                raise SSLError("The handshake operation timed out")
             elif sockstate == SOCKET_HAS_BEEN_CLOSED:
                 raise SSLError("Underlying socket has been closed.")
             elif sockstate == SOCKET_TOO_LARGE_FOR_SELECT:
@@ -781,10 +783,10 @@
             method = lib.SSLv2_method()
         elif protocol == PROTOCOL_SSLv23:
             method = lib.SSLv23_method()
-        elif protocol == PROTOCOL_TLS_CLIENT:
-            method = lib.SSLv23_client_method()
-        elif protocol == PROTOCOL_TLS_SERVER:
-            method = lib.SSLv23_server_method()
+        # elif protocol == PROTOCOL_TLS_CLIENT:
+        #     method = lib.SSLv23_client_method()
+        # elif protocol == PROTOCOL_TLS_SERVER:
+        #     method = lib.SSLv23_server_method()
         else:
             raise ValueError("invalid protocol version")
 
@@ -795,7 +797,7 @@
 
         # Don't check host name by default
         self._check_hostname = False
-        if protocol == PROTOCOL_TLS_CLIENT:
+        if 0 and protocol == PROTOCOL_TLS_CLIENT:
             self._check_hostname = True
             self.verify_mode = CERT_REQUIRED
         else:
@@ -811,7 +813,7 @@
         # Minimal security flags for server and client side context.
         # Client sockets ignore server-side parameters.
         options |= lib.SSL_OP_NO_COMPRESSION
-        options |= lib.SSL_OP_CIPHER_SERVER_PREFERENCE
+        # options |= lib.SSL_OP_CIPHER_SERVER_PREFERENCE
         options |= lib.SSL_OP_SINGLE_DH_USE
         options |= lib.SSL_OP_SINGLE_ECDH_USE
         lib.SSL_CTX_set_options(self.ctx, options)
diff --git a/lib_pypy/_cffi_ssl/_stdssl/win32_extra.py 
b/lib_pypy/_cffi_ssl/_stdssl/win32_extra.py
--- a/lib_pypy/_cffi_ssl/_stdssl/win32_extra.py
+++ b/lib_pypy/_cffi_ssl/_stdssl/win32_extra.py
@@ -1,101 +1,101 @@
 from _pypy_openssl import lib, ffi
-
-
-def enum_certificates(store_name):
-    """Retrieve certificates from Windows' cert store.
-
-store_name may be one of 'CA', 'ROOT' or 'MY'.  The system may provide
-more cert storages, too.  The function returns a list of (bytes,
-encoding_type, trust) tuples.  The encoding_type flag can be interpreted
-with X509_ASN_ENCODING or PKCS_7_ASN_ENCODING. The trust setting is either
-a set of OIDs or the boolean True.
-    """
-    hStore = lib.CertOpenStore(lib.CERT_STORE_PROV_SYSTEM_A, 0, ffi.NULL,
-                               lib.CERT_STORE_READONLY_FLAG | 
lib.CERT_SYSTEM_STORE_LOCAL_MACHINE,
-                               bytes(store_name, "ascii"))
-    if hStore == ffi.NULL:
-        raise WindowsError(*ffi.getwinerror())
-    
-    result = []
-    pCertCtx = ffi.NULL
-    try:
-        while True:
-            pCertCtx = lib.CertEnumCertificatesInStore(hStore, pCertCtx)
-            if pCertCtx == ffi.NULL:
-                break
-            cert = ffi.buffer(pCertCtx.pbCertEncoded, 
pCertCtx.cbCertEncoded)[:]
-            enc = certEncodingType(pCertCtx.dwCertEncodingType)
-            keyusage = parseKeyUsage(pCertCtx, 
lib.CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG)
-            if keyusage is True:
-                keyusage = parseKeyUsage(pCertCtx, 
lib.CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG)
-            result.append((cert, enc, keyusage))
-    finally:
-        if pCertCtx != ffi.NULL:
-            lib.CertFreeCertificateContext(pCertCtx)
-        if not lib.CertCloseStore(hStore, 0):
-            # This error case might shadow another exception.
-            raise WindowsError(*ffi.getwinerror())
-    return result
-
-
-def enum_crls(store_name):
-    """Retrieve CRLs from Windows' cert store.
-
-store_name may be one of 'CA', 'ROOT' or 'MY'.  The system may provide
-more cert storages, too.  The function returns a list of (bytes,
-encoding_type) tuples.  The encoding_type flag can be interpreted with
-X509_ASN_ENCODING or PKCS_7_ASN_ENCODING."""
-    hStore = lib.CertOpenStore(lib.CERT_STORE_PROV_SYSTEM_A, 0, ffi.NULL,
-                               lib.CERT_STORE_READONLY_FLAG | 
lib.CERT_SYSTEM_STORE_LOCAL_MACHINE,
-                               bytes(store_name, "ascii"))
-    if hStore == ffi.NULL:
-        raise WindowsError(*ffi.getwinerror())
-
-    result = []
-    pCrlCtx = ffi.NULL
-    try:
-        while True:
-            pCrlCtx = lib.CertEnumCRLsInStore(hStore, pCrlCtx)
-            if pCrlCtx == ffi.NULL:
-                break
-            crl = ffi.buffer(pCrlCtx.pbCrlEncoded, pCrlCtx.cbCrlEncoded)[:]
-            enc = certEncodingType(pCrlCtx.dwCertEncodingType)
-            result.append((crl, enc))
-    finally:
-        if pCrlCtx != ffi.NULL:
-            lib.CertFreeCRLContext(pCrlCtx)
-        if not lib.CertCloseStore(hStore, 0):
-            # This error case might shadow another exception.
-            raise WindowsError(*ffi.getwinerror())
-    return result
-
-
-def certEncodingType(encodingType):
-    if encodingType == lib.X509_ASN_ENCODING:
-        return "x509_asn"
-    if encodingType == lib.PKCS_7_ASN_ENCODING:
-        return "pkcs_7_asn"
-    return encodingType
-
-def parseKeyUsage(pCertCtx, flags):
-    pSize = ffi.new("DWORD *")
-    if not lib.CertGetEnhancedKeyUsage(pCertCtx, flags, ffi.NULL, pSize):
-        error_with_message = ffi.getwinerror()
-        if error_with_message[0] == lib.CRYPT_E_NOT_FOUND:
-            return True
-        raise WindowsError(*error_with_message)
-
-    pUsageMem = ffi.new("char[]", pSize[0])
-    pUsage = ffi.cast("PCERT_ENHKEY_USAGE", pUsageMem)
-    if not lib.CertGetEnhancedKeyUsage(pCertCtx, flags, pUsage, pSize):
-        error_with_message = ffi.getwinerror()
-        if error_with_message[0] == lib.CRYPT_E_NOT_FOUND:
-            return True
-        raise WindowsError(*error_with_message)
-
-    retval = set()
-    for i in range(pUsage.cUsageIdentifier):
-        if pUsage.rgpszUsageIdentifier[i]:
-            oid = ffi.string(pUsage.rgpszUsageIdentifier[i]).decode('ascii')
-            retval.add(oid)
-    return retval
+
+
+def enum_certificates(store_name):
+    """Retrieve certificates from Windows' cert store.
+
+store_name may be one of 'CA', 'ROOT' or 'MY'.  The system may provide
+more cert storages, too.  The function returns a list of (bytes,
+encoding_type, trust) tuples.  The encoding_type flag can be interpreted
+with X509_ASN_ENCODING or PKCS_7_ASN_ENCODING. The trust setting is either
+a set of OIDs or the boolean True.
+    """
+    hStore = lib.CertOpenStore(lib.CERT_STORE_PROV_SYSTEM_A, 0, ffi.NULL,
+                               lib.CERT_STORE_READONLY_FLAG | 
lib.CERT_SYSTEM_STORE_LOCAL_MACHINE,
+                               bytes(store_name))
+    if hStore == ffi.NULL:
+        raise WindowsError(*ffi.getwinerror())
+
+    result = []
+    pCertCtx = ffi.NULL
+    try:
+        while True:
+            pCertCtx = lib.CertEnumCertificatesInStore(hStore, pCertCtx)
+            if pCertCtx == ffi.NULL:
+                break
+            cert = ffi.buffer(pCertCtx.pbCertEncoded, 
pCertCtx.cbCertEncoded)[:]
+            enc = certEncodingType(pCertCtx.dwCertEncodingType)
+            keyusage = parseKeyUsage(pCertCtx, 
lib.CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG)
+            if keyusage is True:
+                keyusage = parseKeyUsage(pCertCtx, 
lib.CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG)
+            result.append((cert, enc, keyusage))
+    finally:
+        if pCertCtx != ffi.NULL:
+            lib.CertFreeCertificateContext(pCertCtx)
+        if not lib.CertCloseStore(hStore, 0):
+            # This error case might shadow another exception.
+            raise WindowsError(*ffi.getwinerror())
+    return result
+
+
+def enum_crls(store_name):
+    """Retrieve CRLs from Windows' cert store.
+
+store_name may be one of 'CA', 'ROOT' or 'MY'.  The system may provide
+more cert storages, too.  The function returns a list of (bytes,
+encoding_type) tuples.  The encoding_type flag can be interpreted with
+X509_ASN_ENCODING or PKCS_7_ASN_ENCODING."""
+    hStore = lib.CertOpenStore(lib.CERT_STORE_PROV_SYSTEM_A, 0, ffi.NULL,
+                               lib.CERT_STORE_READONLY_FLAG | 
lib.CERT_SYSTEM_STORE_LOCAL_MACHINE,
+                               bytes(store_name))
+    if hStore == ffi.NULL:
+        raise WindowsError(*ffi.getwinerror())
+
+    result = []
+    pCrlCtx = ffi.NULL
+    try:
+        while True:
+            pCrlCtx = lib.CertEnumCRLsInStore(hStore, pCrlCtx)
+            if pCrlCtx == ffi.NULL:
+                break
+            crl = ffi.buffer(pCrlCtx.pbCrlEncoded, pCrlCtx.cbCrlEncoded)[:]
+            enc = certEncodingType(pCrlCtx.dwCertEncodingType)
+            result.append((crl, enc))
+    finally:
+        if pCrlCtx != ffi.NULL:
+            lib.CertFreeCRLContext(pCrlCtx)
+        if not lib.CertCloseStore(hStore, 0):
+            # This error case might shadow another exception.
+            raise WindowsError(*ffi.getwinerror())
+    return result
+
+
+def certEncodingType(encodingType):
+    if encodingType == lib.X509_ASN_ENCODING:
+        return "x509_asn"
+    if encodingType == lib.PKCS_7_ASN_ENCODING:
+        return "pkcs_7_asn"
+    return encodingType
+
+def parseKeyUsage(pCertCtx, flags):
+    pSize = ffi.new("DWORD *")
+    if not lib.CertGetEnhancedKeyUsage(pCertCtx, flags, ffi.NULL, pSize):
+        error_with_message = ffi.getwinerror()
+        if error_with_message[0] == lib.CRYPT_E_NOT_FOUND:
+            return True
+        raise WindowsError(*error_with_message)
+
+    pUsageMem = ffi.new("char[]", pSize[0])
+    pUsage = ffi.cast("PCERT_ENHKEY_USAGE", pUsageMem)
+    if not lib.CertGetEnhancedKeyUsage(pCertCtx, flags, pUsage, pSize):
+        error_with_message = ffi.getwinerror()
+        if error_with_message[0] == lib.CRYPT_E_NOT_FOUND:
+            return True
+        raise WindowsError(*error_with_message)
+
+    retval = set()
+    for i in range(pUsage.cUsageIdentifier):
+        if pUsage.rgpszUsageIdentifier[i]:
+            oid = ffi.string(pUsage.rgpszUsageIdentifier[i]).decode('ascii')
+            retval.add(oid)
+    return retval
diff --git a/lib_pypy/cffi.egg-info/PKG-INFO b/lib_pypy/cffi.egg-info/PKG-INFO
--- a/lib_pypy/cffi.egg-info/PKG-INFO
+++ b/lib_pypy/cffi.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: cffi
-Version: 1.12.3
+Version: 1.13.0
 Summary: Foreign Function Interface for Python calling C code.
 Home-page: http://cffi.readthedocs.org
 Author: Armin Rigo, Maciej Fijalkowski
diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py
--- a/lib_pypy/cffi/__init__.py
+++ b/lib_pypy/cffi/__init__.py
@@ -5,8 +5,8 @@
 from .error import CDefError, FFIError, VerificationError, VerificationMissing
 from .error import PkgConfigError
 
-__version__ = "1.12.3"
-__version_info__ = (1, 12, 3)
+__version__ = "1.13.0"
+__version_info__ = (1, 13, 0)
 
 # The verifier module file names are based on the CRC32 of a string that
 # contains the following version number.  It may be older than __version__
diff --git a/lib_pypy/cffi/_embedding.h b/lib_pypy/cffi/_embedding.h
--- a/lib_pypy/cffi/_embedding.h
+++ b/lib_pypy/cffi/_embedding.h
@@ -145,6 +145,7 @@
     int result;
     PyGILState_STATE state;
     PyObject *pycode=NULL, *global_dict=NULL, *x;
+    PyObject *builtins;
 
     state = PyGILState_Ensure();
 
@@ -169,7 +170,7 @@
     global_dict = PyDict_New();
     if (global_dict == NULL)
         goto error;
-    PyObject *builtins = PyEval_GetBuiltins();
+    builtins = PyEval_GetBuiltins();
     if (builtins == NULL)
         goto error;
     if (PyDict_SetItemString(global_dict, "__builtins__", builtins) < 0)
@@ -223,7 +224,7 @@
 
         if (f != NULL && f != Py_None) {
             PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME
-                               "\ncompiled with cffi version: 1.12.3"
+                               "\ncompiled with cffi version: 1.13.0"
                                "\n_cffi_backend module: ", f);
             modules = PyImport_GetModuleDict();
             mod = PyDict_GetItemString(modules, "_cffi_backend");
diff --git a/lib_pypy/cffi/cparser.py b/lib_pypy/cffi/cparser.py
--- a/lib_pypy/cffi/cparser.py
+++ b/lib_pypy/cffi/cparser.py
@@ -858,19 +858,39 @@
                            "the actual array length in this context"
                            % exprnode.coord.line)
         #
-        if (isinstance(exprnode, pycparser.c_ast.BinaryOp) and
-                exprnode.op == '+'):
-            return (self._parse_constant(exprnode.left) +
-                    self._parse_constant(exprnode.right))
-        #
-        if (isinstance(exprnode, pycparser.c_ast.BinaryOp) and
-                exprnode.op == '-'):
-            return (self._parse_constant(exprnode.left) -
-                    self._parse_constant(exprnode.right))
+        if isinstance(exprnode, pycparser.c_ast.BinaryOp):
+            left = self._parse_constant(exprnode.left)
+            right = self._parse_constant(exprnode.right)
+            if exprnode.op == '+':
+                return left + right
+            elif exprnode.op == '-':
+                return left - right
+            elif exprnode.op == '*':
+                return left * right
+            elif exprnode.op == '/':
+                return self._c_div(left, right)
+            elif exprnode.op == '%':
+                return left - self._c_div(left, right) * right
+            elif exprnode.op == '<<':
+                return left << right
+            elif exprnode.op == '>>':
+                return left >> right
+            elif exprnode.op == '&':
+                return left & right
+            elif exprnode.op == '|':
+                return left | right
+            elif exprnode.op == '^':
+                return left ^ right
         #
         raise FFIError(":%d: unsupported expression: expected a "
                        "simple numeric constant" % exprnode.coord.line)
 
+    def _c_div(self, a, b):
+        result = a // b
+        if ((a < 0) ^ (b < 0)) and (a % b) != 0:
+            result += 1
+        return result
+
     def _build_enum_type(self, explicit_name, decls):
         if decls is not None:
             partial = False
diff --git a/lib_pypy/cffi/recompiler.py b/lib_pypy/cffi/recompiler.py
--- a/lib_pypy/cffi/recompiler.py
+++ b/lib_pypy/cffi/recompiler.py
@@ -855,8 +855,9 @@
             try:
                 if ftype.is_integer_type() or fbitsize >= 0:
                     # accept all integers, but complain on float or double
-                    prnt("  (void)((p->%s) | 0);  /* check that '%s.%s' is "
-                         "an integer */" % (fname, cname, fname))
+                    if fname != '':
+                        prnt("  (void)((p->%s) | 0);  /* check that '%s.%s' is 
"
+                             "an integer */" % (fname, cname, fname))
                     continue
                 # only accept exactly the type declared, except that '[]'
                 # is interpreted as a '*' and so will match any array length.
diff --git a/lib_pypy/crypt/__init__.py b/lib_pypy/crypt/__init__.py
new file mode 100644
--- /dev/null
+++ b/lib_pypy/crypt/__init__.py
@@ -0,0 +1,21 @@
+"""
+CFFI based implementation of the crypt module
+"""
+
+import sys
+import cffi
+
+ffi = cffi.FFI()
+ffi.cdef('char *crypt(char *word, char *salt);')
+
+try:
+    lib = ffi.dlopen('crypt')
+except OSError:
+    raise ImportError('crypt not available')
+
+
+def crypt(word, salt):
+    res = lib.crypt(word, salt)
+    if not res:
+        return None
+    return ffi.string(res)
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -31,13 +31,13 @@
 working_modules = default_modules.copy()
 working_modules.update([
     "_socket", "unicodedata", "mmap", "fcntl", "_locale", "pwd",
-    "select", "zipimport", "_lsprof", "crypt", "signal", "_rawffi", "termios",
+    "select", "zipimport", "_lsprof", "signal", "_rawffi", "termios",
     "zlib", "bz2", "struct", "_md5", "_sha", "_minimal_curses",
     "cStringIO", "thread", "itertools", "pyexpat", "cpyext", "array",
     "binascii", "_multiprocessing", '_warnings', "_collections",
     "_multibytecodec", "micronumpy", "_continuation", "_cffi_backend",
     "_csv", "_cppyy", "_pypyjson", "_jitlog",
-    #" _ssl", "_hashlib"
+    #" _ssl", "_hashlib", "crypt"
 ])
 
 import rpython.rlib.rvmprof.cintf
@@ -65,7 +65,8 @@
     working_modules.add("_winreg")
     # unix only modules
     for name in ["crypt", "fcntl", "pwd", "termios", "_minimal_curses"]:
-        working_modules.remove(name)
+        if name in working_modules:
+            working_modules.remove(name)
         if name in translation_modules:
             translation_modules.remove(name)
 
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
@@ -34,3 +34,14 @@
 .. branch: fix-vmprof-memory-tracking
 
 Fix a bug that prevent memory-tracking in vmprof working on PyPy.
+
+.. branch: optimizeopt-cleanup
+
+Cleanup optimizeopt
+
+.. branch: copystrcontents-in-rewrite
+
+Remove ``copystrcontent`` and ``copyunicodecontent`` in the backends.
+Instead, replace it in ``rewrite.py`` with a direct call to ``memcpy()`` and
+new basic operation, ``load_effective_address``, which the backend can
+even decide not to implement.
diff --git a/pypy/module/_cffi_backend/__init__.py 
b/pypy/module/_cffi_backend/__init__.py
--- a/pypy/module/_cffi_backend/__init__.py
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -3,7 +3,7 @@
 from rpython.rlib import rdynload, clibffi
 from rpython.rtyper.lltypesystem import rffi
 
-VERSION = "1.12.3"
+VERSION = "1.13.0"
 
 FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI
 try:
diff --git a/pypy/module/_cffi_backend/cdataobj.py 
b/pypy/module/_cffi_backend/cdataobj.py
--- a/pypy/module/_cffi_backend/cdataobj.py
+++ b/pypy/module/_cffi_backend/cdataobj.py
@@ -677,11 +677,14 @@
         return self.length
 
     def _repr_extra(self):
-        if self.w_keepalive is not None:
-            name = self.space.type(self.w_keepalive).name
+        from pypy.module._cffi_backend import ctypearray
+        if self.w_keepalive is None:
+            return "buffer RELEASED"
+        obj_tp_name = self.space.type(self.w_keepalive).name
+        if isinstance(self.ctype, ctypearray.W_CTypeArray):
+            return "buffer len %d from '%s' object" % (self.length, 
obj_tp_name)
         else:
-            name = "(released)"
-        return "buffer len %d from '%s' object" % (self.length, name)
+            return "buffer from '%s' object" % (obj_tp_name,)
 
     def enter_exit(self, exit_now):
         # for now, limited effect on PyPy
diff --git a/pypy/module/_cffi_backend/func.py 
b/pypy/module/_cffi_backend/func.py
--- a/pypy/module/_cffi_backend/func.py
+++ b/pypy/module/_cffi_backend/func.py
@@ -112,9 +112,10 @@
 
 @unwrap_spec(w_ctype=ctypeobj.W_CType, require_writable=int)
 def from_buffer(space, w_ctype, w_x, require_writable=0):
-    from pypy.module._cffi_backend import ctypearray
-    if not isinstance(w_ctype, ctypearray.W_CTypeArray):
-        raise oefmt(space.w_TypeError, "expected an array ctype, got '%s'",
+    from pypy.module._cffi_backend import ctypeptr, ctypearray
+    if not isinstance(w_ctype, ctypeptr.W_CTypePtrOrArray):
+        raise oefmt(space.w_TypeError,
+                    "expected a poiunter or array ctype, got '%s'",
                     w_ctype.name)
     if space.isinstance_w(w_x, space.w_unicode):
         raise oefmt(space.w_TypeError,
@@ -135,33 +136,36 @@
                         "raw address on PyPy", w_x)
     #
     buffersize = buf.getlength()
-    arraylength = w_ctype.length
-    if arraylength >= 0:
-        # it's an array with a fixed length; make sure that the
-        # buffer contains enough bytes.
-        if buffersize < w_ctype.size:
-            raise oefmt(space.w_ValueError,
-                "buffer is too small (%d bytes) for '%s' (%d bytes)",
-                buffersize, w_ctype.name, w_ctype.size)
+    if not isinstance(w_ctype, ctypearray.W_CTypeArray):
+        arraylength = buffersize   # number of bytes, not used so far
     else:
-        # it's an open 'array[]'
-        itemsize = w_ctype.ctitem.size
-        if itemsize == 1:
-            # fast path, performance only
-            arraylength = buffersize
-        elif itemsize > 0:
-            # give it as many items as fit the buffer.  Ignore a
-            # partial last element.
-            arraylength = buffersize / itemsize
+        arraylength = w_ctype.length
+        if arraylength >= 0:
+            # it's an array with a fixed length; make sure that the
+            # buffer contains enough bytes.
+            if buffersize < w_ctype.size:
+                raise oefmt(space.w_ValueError,
+                    "buffer is too small (%d bytes) for '%s' (%d bytes)",
+                    buffersize, w_ctype.name, w_ctype.size)
         else:
-            # it's an array 'empty[]'.  Unsupported obscure case:
-            # the problem is that setting the length of the result
-            # to anything large (like SSIZE_T_MAX) is dangerous,
-            # because if someone tries to loop over it, it will
-            # turn effectively into an infinite loop.
-            raise oefmt(space.w_ZeroDivisionError,
-                "from_buffer('%s', ..): the actual length of the array "
-                "cannot be computed", w_ctype.name)
+            # it's an open 'array[]'
+            itemsize = w_ctype.ctitem.size
+            if itemsize == 1:
+                # fast path, performance only
+                arraylength = buffersize
+            elif itemsize > 0:
+                # give it as many items as fit the buffer.  Ignore a
+                # partial last element.
+                arraylength = buffersize / itemsize
+            else:
+                # it's an array 'empty[]'.  Unsupported obscure case:
+                # the problem is that setting the length of the result
+                # to anything large (like SSIZE_T_MAX) is dangerous,
+                # because if someone tries to loop over it, it will
+                # turn effectively into an infinite loop.
+                raise oefmt(space.w_ZeroDivisionError,
+                    "from_buffer('%s', ..): the actual length of the array "
+                    "cannot be computed", w_ctype.name)
     #
     return cdataobj.W_CDataFromBuffer(space, _cdata, arraylength,
                                       w_ctype, buf, w_x)
diff --git a/pypy/module/_cffi_backend/newtype.py 
b/pypy/module/_cffi_backend/newtype.py
--- a/pypy/module/_cffi_backend/newtype.py
+++ b/pypy/module/_cffi_backend/newtype.py
@@ -549,10 +549,11 @@
                 if sflags & SF_GCC_BIG_ENDIAN:
                     bitshift = 8 * ftype.size - fbitsize- bitshift
 
-                fld = ctypestruct.W_CField(ftype, field_offset_bytes,
-                                           bitshift, fbitsize, fflags)
-                fields_list.append(fld)
-                fields_dict[fname] = fld
+                if fname != '':
+                    fld = ctypestruct.W_CField(ftype, field_offset_bytes,
+                                               bitshift, fbitsize, fflags)
+                    fields_list.append(fld)
+                    fields_dict[fname] = fld
 
         if boffset > boffsetmax:
             boffsetmax = boffset
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py 
b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -1,7 +1,7 @@
 # ____________________________________________________________
 
 import sys
-assert __version__ == "1.12.3", ("This test_c.py file is for testing a version"
+assert __version__ == "1.13.0", ("This test_c.py file is for testing a version"
                                  " of cffi that differs from the one that we"
                                  " get from 'import _cffi_backend'")
 if sys.version_info < (3,):
@@ -3830,7 +3830,9 @@
     BIntP = new_pointer_type(BInt)
     BIntA = new_array_type(BIntP, None)
     lst = [-12345678, 87654321, 489148]
-    bytestring = buffer(newp(BIntA, lst))[:] + b'XYZ'
+    bytestring = bytearray(buffer(newp(BIntA, lst))[:] + b'XYZ')
+    lst2 = lst + [42, -999999999]
+    bytestring2 = bytearray(buffer(newp(BIntA, lst2))[:] + b'XYZ')
     #
     p1 = from_buffer(BIntA, bytestring)      # int[]
     assert typeof(p1) is BIntA
@@ -3844,7 +3846,19 @@
         p1[-1]
     #
     py.test.raises(TypeError, from_buffer, BInt, bytestring)
-    py.test.raises(TypeError, from_buffer, BIntP, bytestring)
+    #
+    p2 = from_buffer(BIntP, bytestring)      # int *
+    assert p2 == p1 or 'PY_DOT_PY' in globals()
+    # note: on py.py ^^^, bytearray buffers are not emulated well enough
+    assert typeof(p2) is BIntP
+    assert p2[0] == lst[0]
+    assert p2[1] == lst[1]
+    assert p2[2] == lst[2]
+    # hopefully does not crash, but doesn't raise an exception:
+    p2[3]
+    p2[-1]
+    # not enough data even for one, but this is not enforced:
+    from_buffer(BIntP, b"")
     #
     BIntA2 = new_array_type(BIntP, 2)
     p2 = from_buffer(BIntA2, bytestring)     # int[2]
@@ -3856,7 +3870,7 @@
         p2[2]
     with pytest.raises(IndexError):
         p2[-1]
-    assert p2 == p1
+    assert p2 == p1 or 'PY_DOT_PY' in globals()
     #
     BIntA4 = new_array_type(BIntP, 4)        # int[4]: too big
     py.test.raises(ValueError, from_buffer, BIntA4, bytestring)
@@ -3866,13 +3880,37 @@
                                        ('a2', BInt, -1)])
     BStructP = new_pointer_type(BStruct)
     BStructA = new_array_type(BStructP, None)
-    p1 = from_buffer(BStructA, bytestring)   # struct[]
-    assert len(p1) == 1
+    p1 = from_buffer(BStructA, bytestring2)   # struct[]
+    assert len(p1) == 2
     assert typeof(p1) is BStructA
-    assert p1[0].a1 == lst[0]
-    assert p1[0].a2 == lst[1]
+    assert p1[0].a1 == lst2[0]
+    assert p1[0].a2 == lst2[1]
+    assert p1[1].a1 == lst2[2]
+    assert p1[1].a2 == lst2[3]
     with pytest.raises(IndexError):
-        p1[1]
+        p1[2]
+    with pytest.raises(IndexError):
+        p1[-1]
+    assert repr(p1) == "<cdata 'foo[]' buffer len 2 from 'bytearray' object>"
+    #
+    p2 = from_buffer(BStructP, bytestring2)    # 'struct *'
+    assert p2 == p1 or 'PY_DOT_PY' in globals()
+    assert typeof(p2) is BStructP
+    assert p2.a1 == lst2[0]
+    assert p2.a2 == lst2[1]
+    assert p2[0].a1 == lst2[0]
+    assert p2[0].a2 == lst2[1]
+    assert p2[1].a1 == lst2[2]
+    assert p2[1].a2 == lst2[3]
+    # does not crash:
+    p2[2]
+    p2[-1]
+    # not enough data even for one, but this is not enforced:
+    from_buffer(BStructP, b"")
+    from_buffer(BStructP, b"1234567")
+    #
+    release(p1)
+    assert repr(p1) == "<cdata 'foo[]' buffer RELEASED>"
     #
     BEmptyStruct = new_struct_type("empty")
     complete_struct_or_union(BEmptyStruct, [], Ellipsis, 0)
@@ -3886,7 +3924,37 @@
     p1 = from_buffer(BEmptyStructA5, bytestring)   # struct empty[5]
     assert typeof(p1) is BEmptyStructA5
     assert len(p1) == 5
-    assert cast(BIntP, p1) == from_buffer(BIntA, bytestring)
+    assert (cast(BIntP, p1) == from_buffer(BIntA, bytestring)
+            or 'PY_DOT_PY' in globals())
+    #
+    BVarStruct = new_struct_type("varfoo")
+    BVarStructP = new_pointer_type(BVarStruct)
+    complete_struct_or_union(BVarStruct, [('a1', BInt, -1),
+                                          ('va', BIntA, -1)])
+    with pytest.raises(TypeError):
+        from_buffer(BVarStruct, bytestring)
+    pv = from_buffer(BVarStructP, bytestring)    # varfoo *
+    assert pv.a1 == lst[0]
+    assert pv.va[0] == lst[1]
+    assert pv.va[1] == lst[2]
+    assert sizeof(pv[0]) == 1 * size_of_int()
+    with pytest.raises(TypeError):
+        len(pv.va)
+    # hopefully does not crash, but doesn't raise an exception:
+    pv.va[2]
+    pv.va[-1]
+    # not enough data even for one, but this is not enforced:
+    from_buffer(BVarStructP, b"")
+    assert repr(pv) == "<cdata 'varfoo *' buffer from 'bytearray' object>"
+    assert repr(pv[0]).startswith("<cdata 'varfoo &' ")
+    #
+    release(pv)
+    assert repr(pv) == "<cdata 'varfoo *' buffer RELEASED>"
+    assert repr(pv[0]).startswith("<cdata 'varfoo &' ")
+    #
+    pv = from_buffer(BVarStructP, bytestring)    # make a fresh one
+    with pytest.raises(ValueError):
+        release(pv[0])
 
 def test_memmove():
     Short = new_primitive_type("short")
@@ -4312,8 +4380,10 @@
     BCharA = new_array_type(BCharP, None)
     p = from_buffer(BCharA, a)
     assert p[2] == b"z"
+    assert repr(p) == "<cdata 'char[]' buffer len 3 from 'bytearray' object>"
     release(p)
     assert p[2] == b"z"  # true so far, but might change to raise RuntimeError
+    assert repr(p) == "<cdata 'char[]' buffer RELEASED>"
     release(p)   # no effect
 
 def test_explicit_release_from_buffer_contextmgr():
@@ -4325,6 +4395,7 @@
     with p:
         assert p[2] == b"z"
     assert p[2] == b"z"  # true so far, but might change to raise RuntimeError
+    assert repr(p) == "<cdata 'char[]' buffer RELEASED>"
     release(p)   # no effect
 
 def test_explicit_release_bytearray_on_cpython():
diff --git a/pypy/module/cpyext/bufferobject.py 
b/pypy/module/cpyext/bufferobject.py
--- a/pypy/module/cpyext/bufferobject.py
+++ b/pypy/module/cpyext/bufferobject.py
@@ -5,6 +5,7 @@
     cpython_api, Py_ssize_t, cpython_struct, bootstrap_function, slot_function,
     PyObjectFields, PyObject)
 from pypy.module.cpyext.pyobject import make_typedescr, decref, make_ref
+from pypy.module.cpyext.buffer import CBuffer
 from pypy.module.array.interp_array import ArrayBuffer
 from pypy.objspace.std.bufferobject import W_Buffer
 
@@ -33,7 +34,7 @@
 
 def buffer_attach(space, py_obj, w_obj, w_userdata=None):
     """
-    Fills a newly allocated PyBufferObject with the given (str) buffer object.
+    Fills a newly allocated PyBufferObject with the given buffer object.
     """
     py_buf = rffi.cast(PyBufferObject, py_obj)
     py_buf.c_b_offset = 0
@@ -60,7 +61,17 @@
         py_buf.c_b_base = make_ref(space, w_base)
         py_buf.c_b_ptr = rffi.cast(rffi.VOIDP, buf.w_array._charbuf_start())
         py_buf.c_b_size = buf.getlength()
+    elif isinstance(buf, CBuffer):
+        py_buf.c_b_base = make_ref(space, buf.view.w_obj)
+        py_buf.c_b_ptr = rffi.cast(rffi.VOIDP, buf.view.ptr)
+        py_buf.c_b_size = buf.getlength()
     else:
+        # Raising in attach will segfault.
+        # It would be nice if we could handle the error more gracefully
+        # with something like this
+        # py_buf.c_b_base = lltype.nullptr(PyObject.TO)
+        # py_buf.c_b_ptr = rffi.cast(rffi.VOIDP, 0)
+        # py_buf.c_b_size = buf.getlength()
         raise oefmt(space.w_NotImplementedError, "buffer flavor not supported")
 
 
diff --git a/pypy/module/cpyext/test/array.c b/pypy/module/cpyext/test/array.c
--- a/pypy/module/cpyext/test/array.c
+++ b/pypy/module/cpyext/test/array.c
@@ -2573,6 +2573,11 @@
     Py_RETURN_NONE;
 }
 
+static PyObject *
+passthrough(PyObject *self, PyObject* args) {
+    Py_INCREF(args);
+    return args;
+} 
 /*********************** Install Module **************************/
 
 static PyMethodDef a_methods[] = {
@@ -2584,6 +2589,7 @@
     {"same_dealloc",   (PyCFunction)same_dealloc, METH_VARARGS, NULL},
     {"getitem", (PyCFunction)getitem, METH_VARARGS, NULL},
     {"subclass_with_attribute", (PyCFunction)subclass_with_attribute, 
METH_VARARGS, NULL},
+    {"passthrough", (PyCFunction)passthrough, METH_O, NULL},
     {NULL, NULL, 0, NULL}        /* Sentinel */
 };
 
diff --git a/pypy/module/cpyext/test/test_arraymodule.py 
b/pypy/module/cpyext/test/test_arraymodule.py
--- a/pypy/module/cpyext/test/test_arraymodule.py
+++ b/pypy/module/cpyext/test/test_arraymodule.py
@@ -79,6 +79,9 @@
         assert str(buffer('a') + arr) == "a" + expected
         # python2 special cases empty-buffer + obj
         assert str(buffer('') + arr) == "array('i', [1, 2, 3, 4])"
+        # make sure buffer_attach is called
+        buf2 = module.passthrough(buf)
+        assert str(buf2) == str(buf)
 
     def test_releasebuffer(self):
         module = self.import_module(name='array')
diff --git a/pypy/module/pypyjit/test_pypy_c/test_ffi.py 
b/pypy/module/pypyjit/test_pypy_c/test_ffi.py
--- a/pypy/module/pypyjit/test_pypy_c/test_ffi.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_ffi.py
@@ -425,9 +425,11 @@
         setarrayitem_raw(i153, 0, i106, descr=...)
         p156 = getfield_gc_r(p48, descr=...)
         i158 = getfield_raw_i(..., descr=...)
+        i160 = int_sub(i158, 16)
+        setfield_raw(#, i160, descr=...)
         setfield_gc(p48, p49, descr=...)
         setfield_gc(p134, ConstPtr(null), descr=...)
-        i160 = int_lt(i158, 0)
+        i160 = int_lt(i160, 0)
         guard_false(i160, descr=...)
         jump(..., descr=...)
         """)
diff --git a/pypy/module/thread/test/test_local.py 
b/pypy/module/thread/test/test_local.py
--- a/pypy/module/thread/test/test_local.py
+++ b/pypy/module/thread/test/test_local.py
@@ -5,6 +5,7 @@
 
     def test_local_1(self):
         import thread
+        import gc
         from thread import _local as tlsobject
         freed = []
         class X:
@@ -34,8 +35,9 @@
             thread.start_new_thread(f, (i,))
         self.waitfor(lambda: len(ok) == 20, delay=3)
         assert ok == 20*[True] # see stdout/stderr for failures in the threads
+        gc.collect(); gc.collect(); gc.collect()
 
-        self.waitfor(lambda: len(freed) >= 40)
+        self.waitfor(lambda: len(freed) >= 40, delay=20)
         assert len(freed) == 40
         #  in theory, all X objects should have been freed by now.  Note that
         #  Python's own thread._local objects suffer from the very same "bug" 
that
diff --git a/pypy/objspace/std/dictmultiobject.py 
b/pypy/objspace/std/dictmultiobject.py
--- a/pypy/objspace/std/dictmultiobject.py
+++ b/pypy/objspace/std/dictmultiobject.py
@@ -78,8 +78,9 @@
             W_ModuleDictObject.__init__(w_obj, space, strategy, storage)
             return w_obj
         elif instance:
-            from pypy.objspace.std.mapdict import MapDictStrategy
-            strategy = space.fromcache(MapDictStrategy)
+            from pypy.objspace.std.mapdict import make_instance_dict
+            assert w_type is None
+            return make_instance_dict(space)
         elif strdict or module:
             assert w_type is None
             strategy = space.fromcache(BytesDictStrategy)
diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -753,6 +753,7 @@
         self.space = space
 
     def get_empty_storage(self):
+        # mainly used for tests
         w_result = Object()
         terminator = self.space.fromcache(get_terminator_for_dicts)
         w_result._mapdict_init_empty(terminator)
@@ -865,6 +866,11 @@
     def iteritems(self, w_dict):
         return MapDictIteratorItems(self.space, self, w_dict)
 
+def make_instance_dict(space):
+    w_fake_object = Object()
+    terminator = space.fromcache(get_terminator_for_dicts)
+    w_fake_object._mapdict_init_empty(terminator)
+    return w_fake_object.getdict(space)
 
 def materialize_r_dict(space, obj, dict_w):
     map = obj._get_mapdict_map()
diff --git a/pypy/objspace/std/test/test_mapdict.py 
b/pypy/objspace/std/test/test_mapdict.py
--- a/pypy/objspace/std/test/test_mapdict.py
+++ b/pypy/objspace/std/test/test_mapdict.py
@@ -897,6 +897,17 @@
         d = x.__dict__
         assert list(__pypy__.reversed_dict(d)) == d.keys()[::-1]
 
+    def test_bug_materialize_huge_dict(self):
+        import __pypy__
+        d = __pypy__.newdict("instance")
+        for i in range(100):
+            d[str(i)] = i
+        assert len(d) == 100
+
+        for key in d:
+            assert d[key] == int(key)
+
+
 
 class AppTestWithMapDictAndCounters(object):
     spaceconfig = {"objspace.std.withmethodcachecounter": True}
diff --git a/pypy/objspace/std/unicodeobject.py 
b/pypy/objspace/std/unicodeobject.py
--- a/pypy/objspace/std/unicodeobject.py
+++ b/pypy/objspace/std/unicodeobject.py
@@ -1,5 +1,7 @@
 """The builtin unicode implementation"""
 
+import sys
+
 from rpython.rlib.objectmodel import (
     compute_hash, compute_unique_id, import_from_mixin, always_inline,
     enforceargs, newlist_hint, specialize, we_are_translated)
@@ -41,13 +43,12 @@
         self._utf8 = utf8str
         self._length = length
         self._index_storage = rutf8.null_storage()
-        if not we_are_translated():
+        if not we_are_translated() and not sys.platform == 'win32':
             # utf8str must always be a valid utf8 string, except maybe with
             # explicit surrogate characters---which .decode('utf-8') doesn't
             # special-case in Python 2, which is exactly what we want here
             assert length == len(utf8str.decode('utf-8'))
 
-
     @staticmethod
     def from_utf8builder(builder):
         return W_UnicodeObject(
@@ -1095,11 +1096,11 @@
             if rutf8.has_surrogates(utf8):
                 utf8 = rutf8.reencode_utf8_with_surrogates(utf8)
             return space.newbytes(utf8)
-    return encode(space, w_obj, encoding, errors) 
+    return encode(space, w_obj, encoding, errors)
 
 
 def decode_object(space, w_obj, encoding, errors):
-    from pypy.module._codecs.interp_codecs import lookup_codec, decode 
+    from pypy.module._codecs.interp_codecs import lookup_codec, decode
     if errors is None or errors == 'strict':
         # fast paths
         if encoding is None:
@@ -1109,7 +1110,7 @@
             unicodehelper.check_ascii_or_raise(space, s)
             return space.newutf8(s, len(s))
         if encoding == 'utf-8' or encoding == 'utf8':
-            if (space.isinstance_w(w_obj, space.w_unicode) or 
+            if (space.isinstance_w(w_obj, space.w_unicode) or
                 space.isinstance_w(w_obj, space.w_bytes)):
                 s = space.utf8_w(w_obj)
             else:
@@ -1718,34 +1719,28 @@
 def unicode_to_decimal_w(space, w_unistr):
     if not isinstance(w_unistr, W_UnicodeObject):
         raise oefmt(space.w_TypeError, "expected unicode, got '%T'", w_unistr)
-    unistr = w_unistr._utf8
-    result = ['\0'] * w_unistr._length
-    digits = ['0', '1', '2', '3', '4',
-              '5', '6', '7', '8', '9']
-    res_pos = 0
-    iter = rutf8.Utf8StringIterator(unistr)
-    for uchr in iter:
+    utf8 = w_unistr._utf8
+    result = StringBuilder(w_unistr._len())
+    it = rutf8.Utf8StringIterator(utf8)
+    for uchr in it:
         if W_UnicodeObject._isspace(uchr):
-            result[res_pos] = ' '
-            res_pos += 1
+            result.append(' ')
             continue
-        try:
-            result[res_pos] = digits[unicodedb.decimal(uchr)]
-        except KeyError:
-            if 0 < uchr < 256:
-                result[res_pos] = chr(uchr)
-            else:
+        if not (0 < uchr < 256):
+            try:
+                uchr = ord('0') + unicodedb.decimal(uchr)
+            except KeyError:
                 w_encoding = space.newtext('decimal')
-                pos = iter.get_pos()
+                pos = it.get_pos()
                 w_start = space.newint(pos)
-                w_end = space.newint(pos+1)
+                w_end = space.newint(pos + 1)
                 w_reason = space.newtext('invalid decimal Unicode string')
                 raise OperationError(space.w_UnicodeEncodeError,
-                                     space.newtuple([w_encoding, w_unistr,
-                                                     w_start, w_end,
-                                                     w_reason]))
-        res_pos += 1
-    return ''.join(result)
+                                        space.newtuple([w_encoding, w_unistr,
+                                                        w_start, w_end,
+                                                        w_reason]))
+        result.append(chr(uchr))
+    return result.build()
 
 
 _repr_function = rutf8.make_utf8_escape_function(
diff --git a/requirements.txt b/requirements.txt
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,5 @@
+--only-binary vmprof
+
 cffi>=1.4.0
 
 # parse log files in rvmprof tests
diff --git a/rpython/doc/jit/optimizer.rst b/rpython/doc/jit/optimizer.rst
--- a/rpython/doc/jit/optimizer.rst
+++ b/rpython/doc/jit/optimizer.rst
@@ -13,7 +13,7 @@
 
 Before some optimizations are explained in more detail, it is essential to
 understand how traces look like.
-The optimizer comes with a test suit. It contains many trace
+The optimizer comes with a test suite. It contains many trace
 examples and you might want to take a look at it
 (in `rpython/jit/metainterp/optimizeopt/test/*.py`).
 The allowed operations can be found in 
`rpython/jit/metainterp/resoperation.py`.
@@ -21,7 +21,7 @@
 
     [p0,i0,i1]
     label(p0, i0, i1)
-    i2 = getarray_item_raw(p0, i0, descr=<Array Signed>)
+    i2 = getarrayitem_raw(p0, i0, descr=<Array Signed>)
     i3 = int_add(i1,i2)
     i4 = int_add(i0,1)
     i5 = int_le(i4, 100) # lower-or-equal
@@ -32,7 +32,7 @@
 to compare the Python code that constructed the trace::
 
     from array import array
-    a = array('i',range(101))
+    a = array('i', range(101))
     sum = 0; i = 0
     while i <= 100: # can be seen as label
         sum += a[i]
@@ -131,20 +131,16 @@
 
 Whenever such an operation is encountered (e.g. ``y = x & 0``), no operation is
 emitted. Instead the variable y is made equal to 0
-(= ``make_equal_to(op.result, 0)``). The variables found in a trace are
-instances of Box classes that can be found in
-`rpython/jit/metainterp/history.py`. `OptValue` wraps those variables again
-and maps the boxes to the optimization values in the optimizer. When a
-value is made equal, the two variable's boxes are made to point to the same
-`OptValue` instance.
+(= ``make_constant_int(op, 0)``). The variables found in a trace are instances
+of classes that can be found in `rpython/jit/metainterp/history.py`. When a
+value is made equal to another, its box is made to point to the other one.
 
-**NOTE: this OptValue organization is currently being refactored in a branch.**
 
 Pure optimization
 -----------------
 
-Is interwoven into the basic optimizer. It saves operations, results,
-arguments to be known to have pure semantics.
+The 'pure' optimizations interwoven into the basic optimizer. It saves
+operations, results, arguments to be known to have pure semantics.
 
 "Pure" here means the same as the ``jit.elidable`` decorator:
 free of "observable" side effects and referentially transparent
diff --git a/rpython/jit/backend/arm/opassembler.py 
b/rpython/jit/backend/arm/opassembler.py
--- a/rpython/jit/backend/arm/opassembler.py
+++ b/rpython/jit/backend/arm/opassembler.py
@@ -834,73 +834,12 @@
         else:
             assert 0
 
-    #from ../x86/regalloc.py:928 ff.
-    def emit_op_copystrcontent(self, op, arglocs, regalloc, fcond):
-        assert len(arglocs) == 0
-        self._emit_copystrcontent(op, regalloc, fcond, is_unicode=False)
+    def emit_op_load_effective_address(self, op, arglocs, regalloc, fcond):
+        static_ofs = op.getarg(2).getint()
+        scale = op.getarg(3).getint()
+        self._gen_address(arglocs[2], arglocs[0], arglocs[1], scale, 
static_ofs)
         return fcond
 
-    def emit_op_copyunicodecontent(self, op, arglocs, regalloc, fcond):
-        assert len(arglocs) == 0
-        self._emit_copystrcontent(op, regalloc, fcond, is_unicode=True)
-        return fcond
-
-    def _emit_copystrcontent(self, op, regalloc, fcond, is_unicode):
-        # compute the source address
-        args = op.getarglist()
-        base_loc = regalloc.rm.make_sure_var_in_reg(args[0], args)
-        ofs_loc = regalloc.rm.make_sure_var_in_reg(args[2], args)
-        assert args[0] is not args[1]    # forbidden case of aliasing
-        srcaddr_box = TempVar()
-        forbidden_vars = [args[1], args[3], args[4], srcaddr_box]
-        srcaddr_loc = regalloc.rm.force_allocate_reg(srcaddr_box, 
forbidden_vars)
-        self._gen_address_inside_string(base_loc, ofs_loc, srcaddr_loc,
-                                        is_unicode=is_unicode)
-        # compute the destination address
-        base_loc = regalloc.rm.make_sure_var_in_reg(args[1], forbidden_vars)
-        ofs_loc = regalloc.rm.make_sure_var_in_reg(args[3], forbidden_vars)
-        forbidden_vars = [args[4], srcaddr_box]
-        dstaddr_box = TempVar()
-        dstaddr_loc = regalloc.rm.force_allocate_reg(dstaddr_box, 
forbidden_vars)
-        self._gen_address_inside_string(base_loc, ofs_loc, dstaddr_loc,
-                                        is_unicode=is_unicode)
-        # compute the length in bytes
-        length_box = args[4]
-        length_loc = regalloc.loc(length_box)
-        if is_unicode:
-            forbidden_vars = [srcaddr_box, dstaddr_box]
-            bytes_box = TempVar()
-            bytes_loc = regalloc.rm.force_allocate_reg(bytes_box, 
forbidden_vars)
-            scale = self._get_unicode_item_scale()
-            if not length_loc.is_core_reg():
-                self.regalloc_mov(length_loc, bytes_loc)
-                length_loc = bytes_loc
-            assert length_loc.is_core_reg()
-            self.mc.MOV_ri(r.ip.value, 1 << scale)
-            self.mc.MUL(bytes_loc.value, r.ip.value, length_loc.value)
-            length_box = bytes_box
-            length_loc = bytes_loc
-        # call memcpy()
-        regalloc.before_call()
-        self.simple_call_no_collect(imm(self.memcpy_addr),
-                                  [dstaddr_loc, srcaddr_loc, length_loc])
-        regalloc.rm.possibly_free_var(length_box)
-        regalloc.rm.possibly_free_var(dstaddr_box)
-        regalloc.rm.possibly_free_var(srcaddr_box)
-
-    def _gen_address_inside_string(self, baseloc, ofsloc, resloc, is_unicode):
-        if is_unicode:
-            ofs_items, _, _ = symbolic.get_array_token(rstr.UNICODE,
-                                              self.cpu.translate_support_code)
-            scale = self._get_unicode_item_scale()
-        else:
-            ofs_items, itemsize, _ = symbolic.get_array_token(rstr.STR,
-                                              self.cpu.translate_support_code)
-            assert itemsize == 1
-            ofs_items -= 1     # for the extra null character
-            scale = 0
-        self._gen_address(resloc, baseloc, ofsloc, scale, ofs_items)
-
    # result = base_loc  + (scaled_loc << scale) + static_offset
     def _gen_address(self, result, base_loc, scaled_loc, scale=0, 
static_offset=0):
         assert scaled_loc.is_core_reg()
@@ -915,16 +854,6 @@
         self.mc.ADD_rr(result.value, base_loc.value, scaled_loc.value)
         self.mc.ADD_ri(result.value, result.value, static_offset)
 
-    def _get_unicode_item_scale(self):
-        _, itemsize, _ = symbolic.get_array_token(rstr.UNICODE,
-                                              self.cpu.translate_support_code)
-        if itemsize == 4:
-            return 2
-        elif itemsize == 2:
-            return 1
-        else:
-            raise AssertionError("bad unicode item size")
-
     def store_force_descr(self, op, fail_locs, frame_depth):
         pos = self.mc.currpos()
         guard_token = self.build_guard_token(op, frame_depth, fail_locs, pos, 
c.AL)
diff --git a/rpython/jit/backend/arm/regalloc.py 
b/rpython/jit/backend/arm/regalloc.py
--- a/rpython/jit/backend/arm/regalloc.py
+++ b/rpython/jit/backend/arm/regalloc.py
@@ -873,8 +873,6 @@
     prepare_op_gc_load_indexed_r = _prepare_op_gc_load_indexed
     prepare_op_gc_load_indexed_f = _prepare_op_gc_load_indexed
 
-    prepare_op_copystrcontent = void
-    prepare_op_copyunicodecontent = void
     prepare_op_zero_array = void
 
     def _prepare_op_same_as(self, op, fcond):
@@ -899,6 +897,13 @@
         resloc = self.force_allocate_reg(op)
         return [resloc]
 
+    def prepare_op_load_effective_address(self, op, fcond):
+        args = op.getarglist()
+        arg0 = self.make_sure_var_in_reg(args[0], args)
+        arg1 = self.make_sure_var_in_reg(args[1], args)
+        res = self.force_allocate_reg(op)
+        return [arg0, arg1, res]
+
     def prepare_op_call_malloc_nursery(self, op, fcond):
         size_box = op.getarg(0)
         assert isinstance(size_box, ConstInt)
diff --git a/rpython/jit/backend/arm/runner.py 
b/rpython/jit/backend/arm/runner.py
--- a/rpython/jit/backend/arm/runner.py
+++ b/rpython/jit/backend/arm/runner.py
@@ -23,6 +23,7 @@
     supports_floats = True
     supports_longlong = True
     supports_singlefloats = True
+    supports_load_effective_address = True
 
     from rpython.jit.backend.arm.arch import JITFRAME_FIXED_SIZE
     all_reg_indexes = range(len(all_regs))
diff --git a/rpython/jit/backend/llsupport/gc.py 
b/rpython/jit/backend/llsupport/gc.py
--- a/rpython/jit/backend/llsupport/gc.py
+++ b/rpython/jit/backend/llsupport/gc.py
@@ -14,6 +14,7 @@
 from rpython.jit.metainterp.support import ptr2int
 from rpython.jit.backend.llsupport import symbolic, jitframe
 from rpython.jit.backend.llsupport.symbolic import WORD
+from rpython.jit.backend.llsupport.memcpy import memcpy_fn
 from rpython.jit.backend.llsupport.descr import SizeDescr, ArrayDescr, 
FieldDescr
 from rpython.jit.backend.llsupport.descr import GcCache, get_field_descr
 from rpython.jit.backend.llsupport.descr import get_array_descr
@@ -36,6 +37,11 @@
             self.fielddescr_vtable = get_field_descr(self, rclass.OBJECT,
                                                      'typeptr')
         self._generated_functions = []
+        self.memcpy_fn = memcpy_fn
+        self.memcpy_descr = get_call_descr(self,
+            [lltype.Signed, lltype.Signed, lltype.Signed], lltype.Void,
+            EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+                can_collect=False))
 
     def _setup_str(self):
         self.str_descr     = get_array_descr(self, rstr.STR)
diff --git a/rpython/jit/backend/llsupport/rewrite.py 
b/rpython/jit/backend/llsupport/rewrite.py
--- a/rpython/jit/backend/llsupport/rewrite.py
+++ b/rpython/jit/backend/llsupport/rewrite.py
@@ -34,6 +34,10 @@
      - Add COND_CALLs to the write barrier before SETFIELD_GC and
        SETARRAYITEM_GC operations.
 
+     - Rewrites copystrcontent to a call to memcopy
+
+     - XXX does more than that, please write it down
+
     '_write_barrier_applied' contains a dictionary of variable -> None.
     If a variable is in the dictionary, next setfields can be called without
     a write barrier.  The idea is that an object that was freshly allocated
@@ -335,6 +339,10 @@
                 self.emitting_an_operation_that_can_collect()
             elif op.getopnum() == rop.LABEL:
                 self.emit_label()
+            # ---- change COPY{STR|UNICODE}CONTENT into a call ------
+            if op.opnum == rop.COPYSTRCONTENT or op.opnum == 
rop.COPYUNICODECONTENT:
+                self.rewrite_copy_str_content(op)
+                continue
             # ---------- write barriers ----------
             if self.gc_ll_descr.write_barrier_descr is not None:
                 if op.getopnum() == rop.SETFIELD_GC:
@@ -953,6 +961,61 @@
         self.gcrefs_output_list.append(gcref)
         return index
 
+    def rewrite_copy_str_content(self, op):
+        funcaddr = llmemory.cast_ptr_to_adr(self.gc_ll_descr.memcpy_fn)
+        memcpy_fn = self.cpu.cast_adr_to_int(funcaddr)
+        memcpy_descr = self.gc_ll_descr.memcpy_descr
+        if op.getopnum() == rop.COPYSTRCONTENT:
+            basesize = self.gc_ll_descr.str_descr.basesize
+            # because we have one extra item after alloc, the actual address
+            # of string start is 1 lower, from extra_item_after_malloc
+            basesize -= 1
+            assert self.gc_ll_descr.str_descr.itemsize == 1
+            itemscale = 0
+        else:
+            basesize = self.gc_ll_descr.unicode_descr.basesize
+            itemsize = self.gc_ll_descr.unicode_descr.itemsize
+            if itemsize == 2:
+                itemscale = 1
+            elif itemsize == 4:
+                itemscale = 2
+            else:
+                assert False, "unknown size of unicode"
+        i1 = self.emit_load_effective_address(op.getarg(0), op.getarg(2),
+                                              basesize, itemscale)
+        i2 = self.emit_load_effective_address(op.getarg(1), op.getarg(3),
+                                              basesize, itemscale)
+        if op.getopnum() == rop.COPYSTRCONTENT:
+            arg = op.getarg(4)
+        else:
+            # do some basic constant folding
+            if isinstance(op.getarg(4), ConstInt):
+                arg = ConstInt(op.getarg(4).getint() << itemscale)
+            else:
+                arg = ResOperation(rop.INT_LSHIFT,
+                                   [op.getarg(4), ConstInt(itemscale)])
+                self.emit_op(arg)
+        self.emit_op(ResOperation(rop.CALL_N,
+            [ConstInt(memcpy_fn), i2, i1, arg], descr=memcpy_descr))
+
+    def emit_load_effective_address(self, v_gcptr, v_index, base, itemscale):
+        if self.cpu.supports_load_effective_address:
+            i1 = ResOperation(rop.LOAD_EFFECTIVE_ADDRESS,
+                              [v_gcptr, v_index, ConstInt(base),
+                               ConstInt(itemscale)])
+            self.emit_op(i1)
+            return i1
+        else:
+            if itemscale > 0:
+                v_index = ResOperation(rop.INT_LSHIFT,
+                                       [v_index, ConstInt(itemscale)])
+                self.emit_op(v_index)
+            i1b = ResOperation(rop.INT_ADD, [v_gcptr, v_index])
+            self.emit_op(i1b)
+            i1 = ResOperation(rop.INT_ADD, [i1b, ConstInt(base)])
+            self.emit_op(i1)
+            return i1
+
     def remove_constptr(self, c):
         """Remove all ConstPtrs, and replace them with load_from_gc_table.
         """
diff --git a/rpython/jit/backend/llsupport/test/test_rewrite.py 
b/rpython/jit/backend/llsupport/test/test_rewrite.py
--- a/rpython/jit/backend/llsupport/test/test_rewrite.py
+++ b/rpython/jit/backend/llsupport/test/test_rewrite.py
@@ -142,11 +142,16 @@
         raw_sfdescr = get_array_descr(self.gc_ll_descr, RAW_SF)
         #
         strdescr     = self.gc_ll_descr.str_descr
+        str_basesize = self.gc_ll_descr.str_descr.basesize - 1
         unicodedescr = self.gc_ll_descr.unicode_descr
         strlendescr     = strdescr.lendescr
         unicodelendescr = unicodedescr.lendescr
         strhashdescr     = self.gc_ll_descr.str_hash_descr
         unicodehashdescr = self.gc_ll_descr.unicode_hash_descr
+        uni_basesize  = unicodedescr.basesize
+        uni_itemscale = {2: 1, 4: 2}[unicodedescr.itemsize]
+        memcpy_fn = self.gc_ll_descr.memcpy_fn
+        memcpy_descr = self.gc_ll_descr.memcpy_descr
 
         casmdescr = JitCellToken()
         clt = FakeLoopToken()
@@ -169,6 +174,7 @@
         signedframedescr = self.cpu.signedframedescr
         floatframedescr = self.cpu.floatframedescr
         casmdescr.compiled_loop_token = clt
+
         #
         guarddescr = AbstractFailDescr()
         #
@@ -200,6 +206,7 @@
 
     load_constant_offset = True
     load_supported_factors = (1,2,4,8)
+    supports_load_effective_address = True
 
     translate_support_code = None
 
@@ -237,6 +244,9 @@
             self._cache[key] = r
             return r
 
+    def cast_adr_to_int(self, adr):
+        return llmemory.AddressAsInt(adr)
+
 class TestBoehm(RewriteTests):
     def setup_method(self, meth):
         class FakeCPU(BaseFakeCPU):
@@ -1436,3 +1446,57 @@
             jump()
         """)
         assert len(self.gcrefs) == 2
+
+    def test_rewrite_copystrcontents(self):
+        self.check_rewrite("""
+        [p0, p1, i0, i1, i_len]
+        copystrcontent(p0, p1, i0, i1, i_len)
+        """, """
+        [p0, p1, i0, i1, i_len]
+        i2 = load_effective_address(p0, i0, %(str_basesize)s, 0)
+        i3 = load_effective_address(p1, i1, %(str_basesize)s, 0)
+        call_n(ConstClass(memcpy_fn), i3, i2, i_len, descr=memcpy_descr)
+        """)
+
+    def test_rewrite_copystrcontents_without_load_effective_address(self):
+        self.cpu.supports_load_effective_address = False
+        self.check_rewrite("""
+        [p0, p1, i0, i1, i_len]
+        copystrcontent(p0, p1, i0, i1, i_len)
+        """, """
+        [p0, p1, i0, i1, i_len]
+        i2b = int_add(p0, i0)
+        i2 = int_add(i2b, %(str_basesize)s)
+        i3b = int_add(p1, i1)
+        i3 = int_add(i3b, %(str_basesize)s)
+        call_n(ConstClass(memcpy_fn), i3, i2, i_len, descr=memcpy_descr)
+        """)
+
+    def test_rewrite_copyunicodecontents(self):
+        self.check_rewrite("""
+        [p0, p1, i0, i1, i_len]
+        copyunicodecontent(p0, p1, i0, i1, i_len)
+        """, """
+        [p0, p1, i0, i1, i_len]
+        i2 = load_effective_address(p0, i0, %(uni_basesize)s, 
%(uni_itemscale)d)
+        i3 = load_effective_address(p1, i1, %(uni_basesize)s, 
%(uni_itemscale)d)
+        i4 = int_lshift(i_len, %(uni_itemscale)d)
+        call_n(ConstClass(memcpy_fn), i3, i2, i4, descr=memcpy_descr)
+        """)
+
+    def test_rewrite_copyunicodecontents_without_load_effective_address(self):
+        self.cpu.supports_load_effective_address = False
+        self.check_rewrite("""
+        [p0, p1, i0, i1, i_len]
+        copyunicodecontent(p0, p1, i0, i1, i_len)
+        """, """
+        [p0, p1, i0, i1, i_len]
+        i0s = int_lshift(i0, %(uni_itemscale)d)
+        i2b = int_add(p0, i0s)
+        i2 = int_add(i2b, %(uni_basesize)s)
+        i1s = int_lshift(i1, %(uni_itemscale)d)
+        i3b = int_add(p1, i1s)
+        i3 = int_add(i3b, %(uni_basesize)s)
+        i4 = int_lshift(i_len, %(uni_itemscale)d)
+        call_n(ConstClass(memcpy_fn), i3, i2, i4, descr=memcpy_descr)
+        """)
diff --git a/rpython/jit/backend/model.py b/rpython/jit/backend/model.py
--- a/rpython/jit/backend/model.py
+++ b/rpython/jit/backend/model.py
@@ -19,6 +19,7 @@
     # Boxes and Consts are BoxFloats and ConstFloats.
     supports_singlefloats = False
     supports_guard_gc_type = False
+    supports_load_effective_address = False
 
     propagate_exception_descr = None
 
diff --git a/rpython/jit/backend/ppc/opassembler.py 
b/rpython/jit/backend/ppc/opassembler.py
--- a/rpython/jit/backend/ppc/opassembler.py
+++ b/rpython/jit/backend/ppc/opassembler.py
@@ -966,72 +966,6 @@
         pmc.overwrite()
 
 
-class StrOpAssembler(object):
-
-    _mixin_ = True
-
-    def emit_copystrcontent(self, op, arglocs, regalloc):
-        self._emit_copycontent(arglocs, is_unicode=False)
-
-    def emit_copyunicodecontent(self, op, arglocs, regalloc):
-        self._emit_copycontent(arglocs, is_unicode=True)
-
-    def _emit_load_for_copycontent(self, dst, src_ptr, src_ofs, scale):
-        if src_ofs.is_imm():
-            value = src_ofs.value << scale
-            if value < 32768:
-                self.mc.addi(dst.value, src_ptr.value, value)
-            else:
-                self.mc.load_imm(dst, value)
-                self.mc.add(dst.value, src_ptr.value, dst.value)
-        elif scale == 0:
-            self.mc.add(dst.value, src_ptr.value, src_ofs.value)
-        else:
-            self.mc.sldi(dst.value, src_ofs.value, scale)
-            self.mc.add(dst.value, src_ptr.value, dst.value)
-
-    def _emit_copycontent(self, arglocs, is_unicode):
-        [src_ptr_loc, dst_ptr_loc,
-         src_ofs_loc, dst_ofs_loc, length_loc] = arglocs
-
-        if is_unicode:
-            basesize, itemsize, _ = symbolic.get_array_token(rstr.UNICODE,
-                                        self.cpu.translate_support_code)
-            if   itemsize == 2: scale = 1
-            elif itemsize == 4: scale = 2
-            else: raise AssertionError
-        else:
-            basesize, itemsize, _ = symbolic.get_array_token(rstr.STR,
-                                        self.cpu.translate_support_code)
-            assert itemsize == 1
-            basesize -= 1     # for the extra null character
-            scale = 0
-
-        self._emit_load_for_copycontent(r.r0, src_ptr_loc, src_ofs_loc, scale)
-        self._emit_load_for_copycontent(r.r2, dst_ptr_loc, dst_ofs_loc, scale)
-
-        if length_loc.is_imm():
-            length = length_loc.getint()
-            self.mc.load_imm(r.r5, length << scale)
-        else:
-            if scale > 0:
-                self.mc.sldi(r.r5.value, length_loc.value, scale)
-            elif length_loc is not r.r5:
-                self.mc.mr(r.r5.value, length_loc.value)
-
-        self.mc.mr(r.r4.value, r.r0.value)
-        self.mc.addi(r.r4.value, r.r4.value, basesize)
-        self.mc.addi(r.r3.value, r.r2.value, basesize)
-
-        self.mc.load_imm(self.mc.RAW_CALL_REG, self.memcpy_addr)
-        self.mc.raw_call()
-
-
-class UnicodeOpAssembler(object):
-    _mixin_ = True
-    # empty!
-
-
 class AllocOpAssembler(object):
 
     _mixin_ = True
@@ -1336,8 +1270,7 @@
 
 class OpAssembler(IntOpAssembler, GuardOpAssembler,
                   MiscOpAssembler, FieldOpAssembler,
-                  StrOpAssembler, CallOpAssembler,
-                  UnicodeOpAssembler, ForceOpAssembler,
+                  CallOpAssembler, ForceOpAssembler,
                   AllocOpAssembler, FloatOpAssembler,
                   VectorAssembler):
     _mixin_ = True
diff --git a/rpython/jit/backend/ppc/regalloc.py 
b/rpython/jit/backend/ppc/regalloc.py
--- a/rpython/jit/backend/ppc/regalloc.py
+++ b/rpython/jit/backend/ppc/regalloc.py
@@ -802,18 +802,6 @@
         temp_loc = r.SCRATCH2
         return [base_loc, temp_loc]
 
-    def prepare_copystrcontent(self, op):
-        src_ptr_loc = self.ensure_reg(op.getarg(0))
-        dst_ptr_loc = self.ensure_reg(op.getarg(1))
-        src_ofs_loc = self.ensure_reg_or_any_imm(op.getarg(2))
-        dst_ofs_loc = self.ensure_reg_or_any_imm(op.getarg(3))
-        length_loc  = self.ensure_reg_or_any_imm(op.getarg(4))
-        self._spill_before_call(gc_level=0)
-        return [src_ptr_loc, dst_ptr_loc,
-                src_ofs_loc, dst_ofs_loc, length_loc]
-
-    prepare_copyunicodecontent = prepare_copystrcontent
-
     prepare_same_as_i = helper.prepare_unary_op
     prepare_same_as_r = helper.prepare_unary_op
     prepare_same_as_f = helper.prepare_unary_op
diff --git a/rpython/jit/backend/x86/regalloc.py 
b/rpython/jit/backend/x86/regalloc.py
--- a/rpython/jit/backend/x86/regalloc.py
+++ b/rpython/jit/backend/x86/regalloc.py
@@ -1222,78 +1222,16 @@
         resloc = self.force_allocate_reg(op, [op.getarg(0)])
         self.perform(op, [argloc], resloc)
 
-    def consider_copystrcontent(self, op):
-        self._consider_copystrcontent(op, is_unicode=False)
-
-    def consider_copyunicodecontent(self, op):
-        self._consider_copystrcontent(op, is_unicode=True)
-
-    def _consider_copystrcontent(self, op, is_unicode):
-        # compute the source address
-        args = op.getarglist()
-        base_loc = self.rm.make_sure_var_in_reg(args[0], args)
-        ofs_loc = self.rm.make_sure_var_in_reg(args[2], args)
-        assert args[0] is not args[1]    # forbidden case of aliasing
-        srcaddr_box = TempVar()
-        forbidden_vars = [args[1], args[3], args[4], srcaddr_box]
-        srcaddr_loc = self.rm.force_allocate_reg(srcaddr_box, forbidden_vars)
-        self._gen_address_inside_string(base_loc, ofs_loc, srcaddr_loc,
-                                        is_unicode=is_unicode)
-        # compute the destination address
-        base_loc = self.rm.make_sure_var_in_reg(args[1], forbidden_vars)
-        ofs_loc = self.rm.make_sure_var_in_reg(args[3], forbidden_vars)
-        forbidden_vars = [args[4], srcaddr_box]
-        dstaddr_box = TempVar()
-        dstaddr_loc = self.rm.force_allocate_reg(dstaddr_box, forbidden_vars)
-        self._gen_address_inside_string(base_loc, ofs_loc, dstaddr_loc,
-                                        is_unicode=is_unicode)
-        # compute the length in bytes
-        length_box = args[4]
-        length_loc = self.loc(length_box)
-        if is_unicode:
-            forbidden_vars = [srcaddr_box, dstaddr_box]
-            bytes_box = TempVar()
-            bytes_loc = self.rm.force_allocate_reg(bytes_box, forbidden_vars)
-            scale = self._get_unicode_item_scale()
-            if not (isinstance(length_loc, ImmedLoc) or
-                    isinstance(length_loc, RegLoc)):
-                self.assembler.mov(length_loc, bytes_loc)
-                length_loc = bytes_loc
-            self.assembler.load_effective_addr(length_loc, 0, scale, bytes_loc)
-            length_box = bytes_box
-            length_loc = bytes_loc
-        # call memcpy()
-        self.rm.before_call()
-        self.xrm.before_call()
-        self.assembler.simple_call_no_collect(imm(self.assembler.memcpy_addr),
-                                        [dstaddr_loc, srcaddr_loc, length_loc])
-        self.rm.possibly_free_var(length_box)
-        self.rm.possibly_free_var(dstaddr_box)
-        self.rm.possibly_free_var(srcaddr_box)
-
-    def _gen_address_inside_string(self, baseloc, ofsloc, resloc, is_unicode):
-        if is_unicode:
-            ofs_items, _, _ = symbolic.get_array_token(rstr.UNICODE,
-                                                  self.translate_support_code)
-            scale = self._get_unicode_item_scale()
-        else:
-            ofs_items, itemsize, _ = symbolic.get_array_token(rstr.STR,
-                                                  self.translate_support_code)
-            assert itemsize == 1
-            ofs_items -= 1     # for the extra null character
-            scale = 0
-        self.assembler.load_effective_addr(ofsloc, ofs_items, scale,
-                                           resloc, baseloc)
-
-    def _get_unicode_item_scale(self):
-        _, itemsize, _ = symbolic.get_array_token(rstr.UNICODE,
-                                                  self.translate_support_code)
-        if itemsize == 4:
-            return 2
-        elif itemsize == 2:
-            return 1
-        else:
-            raise AssertionError("bad unicode item size")
+    def consider_load_effective_address(self, op):
+        p0 = op.getarg(0)
+        i0 = op.getarg(1)
+        ploc = self.make_sure_var_in_reg(p0, [i0])
+        iloc = self.make_sure_var_in_reg(i0, [p0])
+        res = self.rm.force_allocate_reg(op, [p0, i0])
+        assert isinstance(op.getarg(2), ConstInt)
+        assert isinstance(op.getarg(3), ConstInt)
+        self.assembler.load_effective_addr(iloc, op.getarg(2).getint(),
+            op.getarg(3).getint(), res, ploc)
 
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to