Author: Ronan Lamy <ronan.l...@gmail.com>
Branch: test-cpyext
Changeset: r87011:df6683b8ed30
Date: 2016-09-11 21:03 +0100
http://bitbucket.org/pypy/pypy/changeset/df6683b8ed30/

Log:    hg merge default

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
@@ -12,4 +12,7 @@
 Implement PyObject_GetBuffer, PyMemoryView_GET_BUFFER, and handles memoryviews
 in numpypy
 
-
+.. branch: force-virtual-state
+Improve merging of virtual states in the JIT in order to avoid jumping to the
+preamble. Accomplished by allocating virtual objects where non-virtuals are
+expected.
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -119,7 +119,7 @@
 
 constant_names = """
 Py_TPFLAGS_READY Py_TPFLAGS_READYING Py_TPFLAGS_HAVE_GETCHARBUFFER
-METH_COEXIST METH_STATIC METH_CLASS Py_TPFLAGS_BASETYPE
+METH_COEXIST METH_STATIC METH_CLASS Py_TPFLAGS_BASETYPE Py_MAX_FMT
 METH_NOARGS METH_VARARGS METH_KEYWORDS METH_O Py_TPFLAGS_HAVE_INPLACEOPS
 Py_TPFLAGS_HEAPTYPE Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_HAVE_NEWBUFFER
 Py_LT Py_LE Py_EQ Py_NE Py_GT Py_GE Py_TPFLAGS_CHECKTYPES Py_MAX_NDIMS
@@ -645,7 +645,7 @@
         ('format', rffi.CCHARP),
         ('shape', Py_ssize_tP),
         ('strides', Py_ssize_tP),
-        ('_format', rffi.UCHAR),
+        ('_format', rffi.CFixedArray(rffi.UCHAR, Py_MAX_FMT)),
         ('_shape', rffi.CFixedArray(Py_ssize_t, Py_MAX_NDIMS)),
         ('_strides', rffi.CFixedArray(Py_ssize_t, Py_MAX_NDIMS)),
         ('suboffsets', Py_ssize_tP),
diff --git a/pypy/module/cpyext/buffer.py b/pypy/module/cpyext/buffer.py
--- a/pypy/module/cpyext/buffer.py
+++ b/pypy/module/cpyext/buffer.py
@@ -1,9 +1,7 @@
-from pypy.interpreter.error import oefmt
-from rpython.rtyper.lltypesystem import rffi, lltype
-from rpython.rlib.rarithmetic import widen
+from rpython.rtyper.lltypesystem import rffi
 from pypy.module.cpyext.api import (
-    cpython_api, CANNOT_FAIL, Py_buffer, Py_TPFLAGS_HAVE_NEWBUFFER, 
Py_ssize_tP)
-from pypy.module.cpyext.pyobject import PyObject, make_ref, incref
+    cpython_api, CANNOT_FAIL, Py_TPFLAGS_HAVE_NEWBUFFER)
+from pypy.module.cpyext.pyobject import PyObject
 
 @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
 def PyObject_CheckBuffer(space, pyobj):
@@ -14,102 +12,4 @@
         return 1
     return 0  
 
-@cpython_api([PyObject, lltype.Ptr(Py_buffer), rffi.INT_real],
-             rffi.INT_real, error=-1)
-def PyObject_GetBuffer(space, w_obj, view, flags):
-    """Export obj into a Py_buffer, view.  These arguments must
-    never be NULL.  The flags argument is a bit field indicating what
-    kind of buffer the caller is prepared to deal with and therefore what
-    kind of buffer the exporter is allowed to return.  The buffer interface
-    allows for complicated memory sharing possibilities, but some caller may
-    not be able to handle all the complexity but may want to see if the
-    exporter will let them take a simpler view to its memory.
-
-    Some exporters may not be able to share memory in every possible way and
-    may need to raise errors to signal to some consumers that something is
-    just not possible. These errors should be a BufferError unless
-    there is another error that is actually causing the problem. The
-    exporter can use flags information to simplify how much of the
-    Py_buffer structure is filled in with non-default values and/or
-    raise an error if the object can't support a simpler view of its memory.
-
-    0 is returned on success and -1 on error."""
-    flags = widen(flags)
-    buf = space.buffer_w(w_obj, flags)
-    try:
-        view.c_buf = rffi.cast(rffi.VOIDP, buf.get_raw_address())
-    except ValueError:
-        raise BufferError("could not create buffer from object")
-    view.c_len = buf.getlength()
-    view.c_obj = make_ref(space, w_obj)
-    ndim = buf.getndim()
-    view.c_itemsize = buf.getitemsize()
-    rffi.setintfield(view, 'c_readonly', int(buf.readonly))
-    rffi.setintfield(view, 'c_ndim', ndim)
-    view.c_format = rffi.str2charp(buf.getformat())
-    view.c_shape = lltype.malloc(Py_ssize_tP.TO, ndim, flavor='raw')
-    view.c_strides = lltype.malloc(Py_ssize_tP.TO, ndim, flavor='raw')
-    shape = buf.getshape()
-    strides = buf.getstrides()
-    for i in range(ndim):
-        view.c_shape[i] = shape[i]
-        view.c_strides[i] = strides[i]
-    view.c_suboffsets = lltype.nullptr(Py_ssize_tP.TO)
-    view.c_internal = lltype.nullptr(rffi.VOIDP.TO)
-    return 0
-
-def _IsFortranContiguous(view):
-    ndim = widen(view.c_ndim)
-    if ndim == 0:
-        return 1
-    if not view.c_strides:
-        return ndim == 1
-    sd = view.c_itemsize
-    if ndim == 1:
-        return view.c_shape[0] == 1 or sd == view.c_strides[0]
-    for i in range(view.c_ndim):
-        dim = view.c_shape[i]
-        if dim == 0:
-            return 1
-        if view.c_strides[i] != sd:
-            return 0
-        sd *= dim
-    return 1
-
-def _IsCContiguous(view):
-    ndim = widen(view.c_ndim)
-    if ndim == 0:
-        return 1
-    if not view.c_strides:
-        return ndim == 1
-    sd = view.c_itemsize
-    if ndim == 1:
-        return view.c_shape[0] == 1 or sd == view.c_strides[0]
-    for i in range(ndim - 1, -1, -1):
-        dim = view.c_shape[i]
-        if dim == 0:
-            return 1
-        if view.c_strides[i] != sd:
-            return 0
-        sd *= dim
-    return 1
-        
-
-@cpython_api([lltype.Ptr(Py_buffer), lltype.Char], rffi.INT_real, 
error=CANNOT_FAIL)
-def PyBuffer_IsContiguous(space, view, fort):
-    """Return 1 if the memory defined by the view is C-style (fortran is
-    'C') or Fortran-style (fortran is 'F') contiguous or either one
-    (fortran is 'A').  Return 0 otherwise."""
-    # traverse the strides, checking for consistent stride increases from
-    # right-to-left (c) or left-to-right (fortran). Copied from cpython
-    if not view.c_suboffsets:
-        return 0
-    if (fort == 'C'):
-        return _IsCContiguous(view)
-    elif (fort == 'F'):
-        return _IsFortranContiguous(view)
-    elif (fort == 'A'):
-        return (_IsCContiguous(view) or _IsFortranContiguous(view))
-    return 0
-
     
diff --git a/pypy/module/cpyext/include/object.h 
b/pypy/module/cpyext/include/object.h
--- a/pypy/module/cpyext/include/object.h
+++ b/pypy/module/cpyext/include/object.h
@@ -144,6 +144,7 @@
 
 /* Py3k buffer interface, adapted for PyPy */
 #define Py_MAX_NDIMS 32
+#define Py_MAX_FMT 5
 typedef struct bufferinfo {
     void *buf;
     PyObject *obj;        /* owned reference */
@@ -158,7 +159,7 @@
     Py_ssize_t *shape;
     Py_ssize_t *strides;
     Py_ssize_t *suboffsets; /* alway NULL for app-level objects*/
-    unsigned char _format;
+    unsigned char _format[Py_MAX_FMT];
     Py_ssize_t _strides[Py_MAX_NDIMS];
     Py_ssize_t _shape[Py_MAX_NDIMS];
     /* static store for shape and strides of
diff --git a/pypy/module/cpyext/memoryobject.py 
b/pypy/module/cpyext/memoryobject.py
--- a/pypy/module/cpyext/memoryobject.py
+++ b/pypy/module/cpyext/memoryobject.py
@@ -1,11 +1,125 @@
 from pypy.module.cpyext.api import (cpython_api, Py_buffer, CANNOT_FAIL,
-                               Py_MAX_NDIMS, build_type_checkers, Py_ssize_tP)
+                         Py_MAX_FMT, Py_MAX_NDIMS, build_type_checkers, 
Py_ssize_tP)
 from pypy.module.cpyext.pyobject import PyObject, make_ref, incref
 from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rlib.rarithmetic import widen
 from pypy.objspace.std.memoryobject import W_MemoryView
 
 PyMemoryView_Check, PyMemoryView_CheckExact = 
build_type_checkers("MemoryView", "w_memoryview")
 
+@cpython_api([PyObject, lltype.Ptr(Py_buffer), rffi.INT_real],
+             rffi.INT_real, error=-1)
+def PyObject_GetBuffer(space, w_obj, view, flags):
+    """Export obj into a Py_buffer, view.  These arguments must
+    never be NULL.  The flags argument is a bit field indicating what
+    kind of buffer the caller is prepared to deal with and therefore what
+    kind of buffer the exporter is allowed to return.  The buffer interface
+    allows for complicated memory sharing possibilities, but some caller may
+    not be able to handle all the complexity but may want to see if the
+    exporter will let them take a simpler view to its memory.
+
+    Some exporters may not be able to share memory in every possible way and
+    may need to raise errors to signal to some consumers that something is
+    just not possible. These errors should be a BufferError unless
+    there is another error that is actually causing the problem. The
+    exporter can use flags information to simplify how much of the
+    Py_buffer structure is filled in with non-default values and/or
+    raise an error if the object can't support a simpler view of its memory.
+
+    0 is returned on success and -1 on error."""
+    flags = widen(flags)
+    buf = space.buffer_w(w_obj, flags)
+    try:
+        view.c_buf = rffi.cast(rffi.VOIDP, buf.get_raw_address())
+    except ValueError:
+        raise BufferError("could not create buffer from object")
+    view.c_obj = make_ref(space, w_obj)
+    return fill_Py_buffer(space, buf, view)
+
+def fill_Py_buffer(space, buf, view):    
+    # c_buf, c_obj have been filled in
+    ndim = buf.getndim()
+    view.c_len = buf.getlength()
+    view.c_itemsize = buf.getitemsize()
+    rffi.setintfield(view, 'c_ndim', ndim)
+    view.c_format = rffi.cast(rffi.CCHARP, view.c__format)
+    view.c_shape = rffi.cast(Py_ssize_tP, view.c__shape)
+    view.c_strides = rffi.cast(Py_ssize_tP, view.c__strides)
+    fmt = buf.getformat()
+    n = Py_MAX_FMT - 1 # NULL terminated buffer
+    if len(fmt) > n:
+        ### WARN?
+        pass
+    else:
+        n = len(fmt)
+    for i in range(n):
+        if ord(fmt[i]) > 255:
+            view.c_format[i] = '*'
+        else:
+            view.c_format[i] = fmt[i]
+    view.c_format[n] = '\x00'        
+    shape = buf.getshape()
+    strides = buf.getstrides()
+    for i in range(ndim):
+        view.c_shape[i] = shape[i]
+        view.c_strides[i] = strides[i]
+    view.c_suboffsets = lltype.nullptr(Py_ssize_tP.TO)
+    view.c_internal = lltype.nullptr(rffi.VOIDP.TO)
+    return 0
+
+def _IsFortranContiguous(view):
+    ndim = widen(view.c_ndim)
+    if ndim == 0:
+        return 1
+    if not view.c_strides:
+        return ndim == 1
+    sd = view.c_itemsize
+    if ndim == 1:
+        return view.c_shape[0] == 1 or sd == view.c_strides[0]
+    for i in range(view.c_ndim):
+        dim = view.c_shape[i]
+        if dim == 0:
+            return 1
+        if view.c_strides[i] != sd:
+            return 0
+        sd *= dim
+    return 1
+
+def _IsCContiguous(view):
+    ndim = widen(view.c_ndim)
+    if ndim == 0:
+        return 1
+    if not view.c_strides:
+        return ndim == 1
+    sd = view.c_itemsize
+    if ndim == 1:
+        return view.c_shape[0] == 1 or sd == view.c_strides[0]
+    for i in range(ndim - 1, -1, -1):
+        dim = view.c_shape[i]
+        if dim == 0:
+            return 1
+        if view.c_strides[i] != sd:
+            return 0
+        sd *= dim
+    return 1
+
+@cpython_api([lltype.Ptr(Py_buffer), lltype.Char], rffi.INT_real, 
error=CANNOT_FAIL)
+def PyBuffer_IsContiguous(space, view, fort):
+    """Return 1 if the memory defined by the view is C-style (fortran is
+    'C') or Fortran-style (fortran is 'F') contiguous or either one
+    (fortran is 'A').  Return 0 otherwise."""
+    # traverse the strides, checking for consistent stride increases from
+    # right-to-left (c) or left-to-right (fortran). Copied from cpython
+    if not view.c_suboffsets:
+        return 0
+    if (fort == 'C'):
+        return _IsCContiguous(view)
+    elif (fort == 'F'):
+        return _IsFortranContiguous(view)
+    elif (fort == 'A'):
+        return (_IsCContiguous(view) or _IsFortranContiguous(view))
+    return 0
+
 @cpython_api([PyObject], PyObject)
 def PyMemoryView_FromObject(space, w_obj):
     return space.call_method(space.builtin, "memoryview", w_obj)
@@ -38,19 +152,6 @@
         view.c_obj = make_ref(space, w_s)
         rffi.setintfield(view, 'c_readonly', 1)
         isstr = True
-    view.c_len = w_obj.getlength()
-    view.c_itemsize = w_obj.buf.getitemsize()
-    rffi.setintfield(view, 'c_ndim', ndim)
-    view.c__format = rffi.cast(rffi.UCHAR, w_obj.buf.getformat())
-    view.c_format = rffi.cast(rffi.CCHARP, view.c__format)
-    view.c_shape = rffi.cast(Py_ssize_tP, view.c__shape)
-    view.c_strides = rffi.cast(Py_ssize_tP, view.c__strides)
-    shape = w_obj.buf.getshape()
-    strides = w_obj.buf.getstrides()
-    for i in range(ndim):
-        view.c_shape[i] = shape[i]
-        view.c_strides[i] = strides[i]
-    view.c_suboffsets = lltype.nullptr(Py_ssize_tP.TO)
-    view.c_internal = lltype.nullptr(rffi.VOIDP.TO)
+    fill_Py_buffer(space, w_obj.buf, view)     
     return view
 
diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py
--- a/pypy/module/cpyext/sequence.py
+++ b/pypy/module/cpyext/sequence.py
@@ -43,16 +43,20 @@
 def PySequence_Fast(space, w_obj, m):
     """Returns the sequence o as a tuple, unless it is already a tuple or 
list, in
     which case o is returned.  Use PySequence_Fast_GET_ITEM() to access the
-    members of the result.  Returns NULL on failure.  If the object is not a
-    sequence, raises TypeError with m as the message text."""
+    members of the result.  Returns NULL on failure.  If the object cannot be
+    converted to a sequence, and raises a TypeError, raise a new TypeError with
+    m as the message text. If the conversion otherwise, fails, reraise the
+    original exception"""
     if isinstance(w_obj, W_ListObject):
         # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM 
   
         w_obj.convert_to_cpy_strategy(space)
         return w_obj
     try:
         return W_ListObject.newlist_cpyext(space, space.listview(w_obj))
-    except OperationError:
-        raise OperationError(space.w_TypeError, space.wrap(rffi.charp2str(m)))
+    except OperationError as e:
+        if e.match(space, space.w_TypeError):
+            raise OperationError(space.w_TypeError, 
space.wrap(rffi.charp2str(m)))
+        raise e
 
 @cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_borrowed=True)
 def PySequence_Fast_GET_ITEM(space, w_obj, index):
diff --git a/pypy/module/cpyext/test/test_api.py 
b/pypy/module/cpyext/test/test_api.py
--- a/pypy/module/cpyext/test/test_api.py
+++ b/pypy/module/cpyext/test/test_api.py
@@ -1,5 +1,5 @@
 import py, pytest
-from rpython.rtyper.lltypesystem import rffi, lltype
+from rpython.rtyper.lltypesystem import lltype
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.module.cpyext.state import State
 from pypy.module.cpyext import api
diff --git a/pypy/module/cpyext/test/test_bufferobject.py 
b/pypy/module/cpyext/test/test_bufferobject.py
--- a/pypy/module/cpyext/test/test_bufferobject.py
+++ b/pypy/module/cpyext/test/test_bufferobject.py
@@ -1,4 +1,4 @@
-from rpython.rtyper.lltypesystem import rffi, lltype
+from rpython.rtyper.lltypesystem import lltype
 from pypy.module.cpyext.test.test_api import BaseApiTest
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 from pypy.module.cpyext.api import PyObject
diff --git a/pypy/module/cpyext/test/test_cpyext.py 
b/pypy/module/cpyext/test/test_cpyext.py
--- a/pypy/module/cpyext/test/test_cpyext.py
+++ b/pypy/module/cpyext/test/test_cpyext.py
@@ -18,6 +18,8 @@
 
 from .support import c_compile
 
+only_pypy ="config.option.runappdirect and '__pypy__' not in 
sys.builtin_module_names" 
+
 @api.cpython_api([], api.PyObject)
 def PyPy_Crash1(space):
     1/0
@@ -288,11 +290,11 @@
             "the test actually passed in the first place; if it failed "
             "it is likely to reach this place.")
 
-    @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, 
reason='pypy only test')
+    @pytest.mark.skipif(only_pypy, reason='pypy only test')
     def test_only_import(self):
         import cpyext
 
-    @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, 
reason='pypy only test')
+    @pytest.mark.skipif(only_pypy, reason='pypy only test')
     def test_load_error(self):
         import cpyext
         raises(ImportError, cpyext.load_module, "missing.file", "foo")
@@ -873,7 +875,7 @@
             ])
         raises(SystemError, mod.newexc, "name", Exception, {})
 
-    @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, 
reason='pypy specific test')
+    @pytest.mark.skipif(only_pypy, reason='pypy specific test')
     def test_hash_pointer(self):
         mod = self.import_extension('foo', [
             ('get_hash', 'METH_NOARGS',
@@ -924,7 +926,7 @@
         print p
         assert 'py' in p
 
-    @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, 
reason='pypy only test')
+    @pytest.mark.skipif(only_pypy, reason='pypy only test')
     def test_get_version(self):
         mod = self.import_extension('foo', [
             ('get_version', 'METH_NOARGS',
diff --git a/pypy/module/cpyext/test/test_memoryobject.py 
b/pypy/module/cpyext/test/test_memoryobject.py
--- a/pypy/module/cpyext/test/test_memoryobject.py
+++ b/pypy/module/cpyext/test/test_memoryobject.py
@@ -1,3 +1,4 @@
+from rpython.rtyper.lltypesystem import rffi
 from pypy.module.cpyext.test.test_api import BaseApiTest
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 from rpython.rlib.buffer import StringBuffer
@@ -16,8 +17,12 @@
         w_buf = space.newbuffer(StringBuffer("hello"))
         w_memoryview = api.PyMemoryView_FromObject(w_buf)
         w_view = api.PyMemoryView_GET_BUFFER(w_memoryview)
-        ndim = w_view.c_ndim
-        assert ndim == 1
+        assert w_view.c_ndim == 1
+        f = rffi.charp2str(w_view.c_format)
+        assert f == 'B'
+        assert w_view.c_shape[0] == 5
+        assert w_view.c_strides[0] == 1
+        assert w_view.c_len == 5
 
 class AppTestBufferProtocol(AppTestCpythonExtensionBase):
     def test_buffer_protocol(self):
@@ -38,14 +43,10 @@
         from _numpypy import multiarray as np
         module = self.import_module(name='buffer_test')
         get_buffer_info = module.get_buffer_info
-        # test_export_flags from numpy test_multiarray
         raises(ValueError, get_buffer_info, np.arange(5)[::2], ('SIMPLE',))
-        # test_relaxed_strides from numpy test_multiarray
-        arr = np.zeros((1, 10))
-        if arr.flags.f_contiguous:
-            shape, strides = get_buffer_info(arr, ['F_CONTIGUOUS'])
-            assert strides[0] == 8
-            arr = np.ones((10, 1), order='F')
-            shape, strides = get_buffer_info(arr, ['C_CONTIGUOUS'])
-            assert strides[-1] == 8
-
+        arr = np.zeros((1, 10), order='F')
+        shape, strides = get_buffer_info(arr, ['F_CONTIGUOUS'])
+        assert strides[0] == 8
+        arr = np.zeros((10, 1), order='C')
+        shape, strides = get_buffer_info(arr, ['C_CONTIGUOUS'])
+        assert strides[-1] == 8
diff --git a/pypy/module/cpyext/test/test_sequence.py 
b/pypy/module/cpyext/test/test_sequence.py
--- a/pypy/module/cpyext/test/test_sequence.py
+++ b/pypy/module/cpyext/test/test_sequence.py
@@ -267,3 +267,31 @@
         assert module.test_fast_sequence(s[0:-1])
         assert module.test_fast_sequence(s[::-1])
 
+    def test_fast_keyerror(self):
+        module = self.import_extension('foo', [
+            ("test_fast_sequence", "METH_VARARGS",
+             """
+                PyObject *foo;
+                PyObject * seq = PyTuple_GetItem(args, 0);
+                if (seq == NULL)
+                    Py_RETURN_NONE;
+                foo = PySequence_Fast(seq, "Could not convert object to 
sequence");
+                if (foo != NULL)
+                {
+                    return foo;
+                }
+                if (PyErr_ExceptionMatches(PyExc_KeyError)) {
+                    PyErr_Clear();
+                    return PyBool_FromLong(1);
+                }
+                return NULL;
+             """)])
+        class Map(object):
+            def __len__(self):
+                return 1
+
+            def __getitem__(self, index):
+                raise KeyError()
+
+        assert module.test_fast_sequence(Map()) is True
+
diff --git a/pypy/module/cpyext/test/test_thread.py 
b/pypy/module/cpyext/test/test_thread.py
--- a/pypy/module/cpyext/test/test_thread.py
+++ b/pypy/module/cpyext/test/test_thread.py
@@ -1,12 +1,13 @@
 import sys
 
-import py, pytest
+import pytest
 
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 
+only_pypy ="config.option.runappdirect and '__pypy__' not in 
sys.builtin_module_names" 
 
 class AppTestThread(AppTestCpythonExtensionBase):
-    @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, 
reason='pypy only test')
+    @pytest.mark.skipif(only_pypy, reason='pypy only test')
     def test_get_thread_ident(self):
         module = self.import_extension('foo', [
             ("get_thread_ident", "METH_NOARGS",
@@ -33,7 +34,7 @@
 
         assert results[0][0] != results[1][0]
 
-    @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, 
reason='pypy only test')
+    @pytest.mark.skipif(only_pypy, reason='pypy only test')
     def test_acquire_lock(self):
         module = self.import_extension('foo', [
             ("test_acquire_lock", "METH_NOARGS",
@@ -57,7 +58,7 @@
             ])
         module.test_acquire_lock()
 
-    @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, 
reason='pypy only test')
+    @pytest.mark.skipif(only_pypy, reason='pypy only test')
     def test_release_lock(self):
         module = self.import_extension('foo', [
             ("test_release_lock", "METH_NOARGS",
@@ -79,7 +80,7 @@
             ])
         module.test_release_lock()
 
-    @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, 
reason='pypy only test')
+    @pytest.mark.skipif(only_pypy, reason='pypy only test')
     def test_tls(self):
         module = self.import_extension('foo', [
             ("create_key", "METH_NOARGS",
diff --git a/pypy/module/cpyext/test/test_version.py 
b/pypy/module/cpyext/test/test_version.py
--- a/pypy/module/cpyext/test/test_version.py
+++ b/pypy/module/cpyext/test/test_version.py
@@ -3,6 +3,7 @@
 import py, pytest
 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 
+only_pypy ="config.option.runappdirect and '__pypy__' not in 
sys.builtin_module_names" 
 
 def test_pragma_version():
     from pypy.module.sys.version import CPYTHON_VERSION
@@ -32,11 +33,9 @@
         assert module.py_minor_version == sys.version_info.minor
         assert module.py_micro_version == sys.version_info.micro
 
-    #@pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, 
reason='pypy only test')
+    @pytest.mark.skipif(only_pypy, reason='pypy only test')
     def test_pypy_versions(self):
         import sys
-        if '__pypy__' not in sys.builtin_module_names:
-            py.test.skip("pypy only test")
         init = """
         if (Py_IsInitialized()) {
             PyObject *m = Py_InitModule("foo", NULL);
diff --git a/rpython/jit/metainterp/optimizeopt/info.py 
b/rpython/jit/metainterp/optimizeopt/info.py
--- a/rpython/jit/metainterp/optimizeopt/info.py
+++ b/rpython/jit/metainterp/optimizeopt/info.py
@@ -365,6 +365,13 @@
     def visitor_dispatch_virtual_type(self, visitor):
         raise NotImplementedError("abstract")
 
+    def make_guards(self, op, short, optimizer):
+        from rpython.jit.metainterp.optimizeopt.optimizer import CONST_0
+        op = ResOperation(rop.INT_EQ, [op, CONST_0])
+        short.append(op)
+        op = ResOperation(rop.GUARD_FALSE, [op])
+        short.append(op)
+
 class RawBufferPtrInfo(AbstractRawPtrInfo):
     buffer = None
 
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py 
b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -7592,7 +7592,7 @@
         ops = """
         [i0]
         p1 = new_with_vtable(descr=nodesize)
-        cond_call(1, 123, p1, descr=clear_vable)
+        cond_call(i0, 123, p1, descr=clear_vable)
         jump(i0)
         """
         expected = """
diff --git a/rpython/jit/metainterp/optimizeopt/unroll.py 
b/rpython/jit/metainterp/optimizeopt/unroll.py
--- a/rpython/jit/metainterp/optimizeopt/unroll.py
+++ b/rpython/jit/metainterp/optimizeopt/unroll.py
@@ -16,7 +16,7 @@
 from rpython.rlib.debug import debug_print, debug_start, debug_stop,\
      have_debug_prints
 
-class UnrollableOptimizer(Optimizer):    
+class UnrollableOptimizer(Optimizer):
     def force_op_from_preamble(self, preamble_op):
         if isinstance(preamble_op, PreambleOp):
             if self.optunroll.short_preamble_producer is None:
@@ -120,7 +120,8 @@
                 assert op.get_forwarded() is None
         if check_newops:
             assert not self.optimizer._newoperations
-    
+
+
     def optimize_preamble(self, trace, runtime_boxes, call_pure_results, memo):
         info, newops = self.optimizer.propagate_all_forward(
             trace.get_iter(), call_pure_results, flush=False)
@@ -156,7 +157,7 @@
         current_vs = self.get_virtual_state(end_jump.getarglist())
         # pick the vs we want to jump to
         assert isinstance(celltoken, JitCellToken)
-        
+
         target_virtual_state = self.pick_virtual_state(current_vs,
                                                        state.virtual_state,
                                                 celltoken.target_tokens)
@@ -180,17 +181,27 @@
             self.jump_to_preamble(celltoken, end_jump, info)
             return (UnrollInfo(target_token, label_op, extra_same_as,
                                self.optimizer.quasi_immutable_deps),
-                    self.optimizer._newoperations)            
+                    self.optimizer._newoperations)
 
         try:
-            new_virtual_state = self.jump_to_existing_trace(end_jump, label_op,
-                                                            
state.runtime_boxes)
+            new_virtual_state = self.jump_to_existing_trace(
+                    end_jump, label_op, state.runtime_boxes, force_boxes=False)
         except InvalidLoop:
             # inlining short preamble failed, jump to preamble
             self.jump_to_preamble(celltoken, end_jump, info)
             return (UnrollInfo(target_token, label_op, extra_same_as,
                                self.optimizer.quasi_immutable_deps),
                     self.optimizer._newoperations)
+
+        if new_virtual_state is not None:
+            # Attempt to force virtual boxes in order to avoid jumping
+            # to the preamble.
+            try:
+                new_virtual_state = self.jump_to_existing_trace(
+                        end_jump, label_op, state.runtime_boxes, 
force_boxes=True)
+            except InvalidLoop:
+                pass
+
         if new_virtual_state is not None:
             self.jump_to_preamble(celltoken, end_jump, info)
             return (UnrollInfo(target_token, label_op, extra_same_as,
@@ -199,7 +210,7 @@
 
         self.disable_retracing_if_max_retrace_guards(
             self.optimizer._newoperations, target_token)
-        
+
         return (UnrollInfo(target_token, label_op, extra_same_as,
                            self.optimizer.quasi_immutable_deps),
                 self.optimizer._newoperations)
@@ -241,7 +252,8 @@
         for a in jump_op.getarglist():
             self.optimizer.force_box_for_end_of_preamble(a)
         try:
-            vs = self.jump_to_existing_trace(jump_op, None, runtime_boxes)
+            vs = self.jump_to_existing_trace(jump_op, None, runtime_boxes,
+                                             force_boxes=False)
         except InvalidLoop:
             return self.jump_to_preamble(cell_token, jump_op, info)
         if vs is None:
@@ -252,6 +264,14 @@
             cell_token.retraced_count += 1
             debug_print('Retracing (%d/%d)' % (cell_token.retraced_count, 
limit))
         else:
+            # Try forcing boxes to avoid jumping to the preamble
+            try:
+                vs = self.jump_to_existing_trace(jump_op, None, runtime_boxes,
+                                                 force_boxes=True)
+            except InvalidLoop:
+                pass
+            if vs is None:
+                return info, self.optimizer._newoperations[:]
             debug_print("Retrace count reached, jumping to preamble")
             return self.jump_to_preamble(cell_token, jump_op, info)
         exported_state = self.export_state(info.jump_op.getarglist(),
@@ -288,7 +308,7 @@
         return info, self.optimizer._newoperations[:]
 
 
-    def jump_to_existing_trace(self, jump_op, label_op, runtime_boxes):
+    def jump_to_existing_trace(self, jump_op, label_op, runtime_boxes, 
force_boxes=False):
         jitcelltoken = jump_op.getdescr()
         assert isinstance(jitcelltoken, JitCellToken)
         virtual_state = self.get_virtual_state(jump_op.getarglist())
@@ -299,7 +319,8 @@
                 continue
             try:
                 extra_guards = target_virtual_state.generate_guards(
-                    virtual_state, args, runtime_boxes, self.optimizer)
+                    virtual_state, args, runtime_boxes, self.optimizer,
+                    force_boxes=force_boxes)
                 patchguardop = self.optimizer.patchguardop
                 for guard in extra_guards.extra_guards:
                     if isinstance(guard, GuardResOp):
@@ -308,8 +329,18 @@
                     self.send_extra_operation(guard)
             except VirtualStatesCantMatch:
                 continue
-            args, virtuals = target_virtual_state.make_inputargs_and_virtuals(
-                args, self.optimizer)
+
+            # When force_boxes == True, creating the virtual args can fail when
+            # components of the virtual state alias. If this occurs, we must
+            # recompute the virtual state as boxes will have been forced.
+            try:
+                args, virtuals = 
target_virtual_state.make_inputargs_and_virtuals(
+                    args, self.optimizer, force_boxes=force_boxes)
+            except VirtualStatesCantMatch:
+                assert force_boxes
+                virtual_state = self.get_virtual_state(args)
+                continue
+
             short_preamble = target_token.short_preamble
             try:
                 extra = self.inline_short_preamble(args + virtuals, args,
@@ -452,7 +483,7 @@
         # by short preamble
         label_args = exported_state.virtual_state.make_inputargs(
             targetargs, self.optimizer)
-        
+
         self.short_preamble_producer = ShortPreambleBuilder(
             label_args, exported_state.short_boxes,
             exported_state.short_inputargs, exported_state.exported_infos,
@@ -497,7 +528,7 @@
     * runtime_boxes - runtime values for boxes, necessary when generating
                       guards to jump to
     """
-    
+
     def __init__(self, end_args, next_iteration_args, virtual_state,
                  exported_infos, short_boxes, renamed_inputargs,
                  short_inputargs, runtime_boxes, memo):
diff --git a/rpython/jit/metainterp/optimizeopt/virtualstate.py 
b/rpython/jit/metainterp/optimizeopt/virtualstate.py
--- a/rpython/jit/metainterp/optimizeopt/virtualstate.py
+++ b/rpython/jit/metainterp/optimizeopt/virtualstate.py
@@ -4,7 +4,7 @@
      ArrayStructInfo, AbstractStructPtrInfo
 from rpython.jit.metainterp.optimizeopt.intutils import \
      MININT, MAXINT, IntBound, IntLowerBound
-from rpython.jit.metainterp.resoperation import rop, ResOperation,\
+from rpython.jit.metainterp.resoperation import rop, ResOperation, \
      InputArgInt, InputArgRef, InputArgFloat
 from rpython.rlib.debug import debug_print
 
@@ -20,7 +20,7 @@
 
 
 class GenerateGuardState(object):
-    def __init__(self, optimizer=None, guards=None, renum=None, bad=None):
+    def __init__(self, optimizer=None, guards=None, renum=None, bad=None, 
force_boxes=False):
         self.optimizer = optimizer
         self.cpu = optimizer.cpu
         if guards is None:
@@ -32,6 +32,7 @@
         if bad is None:
             bad = {}
         self.bad = bad
+        self.force_boxes = force_boxes
 
     def get_runtime_item(self, box, descr, i):
         array = box.getref_base()
@@ -303,7 +304,7 @@
             opinfo = state.optimizer.getptrinfo(box)
             assert isinstance(opinfo, ArrayPtrInfo)
         else:
-            opinfo = None            
+            opinfo = None
         for i in range(self.length):
             for descr in self.fielddescrs:
                 index = i * len(self.fielddescrs) + descr.get_index()
@@ -514,6 +515,8 @@
         NotVirtualStateInfo.__init__(self, cpu, type, info)
 
     def _generate_guards(self, other, box, runtime_box, state):
+        if state.force_boxes and isinstance(other, VirtualStateInfo):
+            return self._generate_virtual_guards(other, box, runtime_box, 
state)
         if not isinstance(other, NotVirtualStateInfoPtr):
             raise VirtualStatesCantMatch(
                     'The VirtualStates does not match as a ' +
@@ -545,6 +548,23 @@
     # to an existing compiled loop or retracing the loop. Both alternatives
     # will always generate correct behaviour, but performance will differ.
 
+    def _generate_virtual_guards(self, other, box, runtime_box, state):
+        """
+        Generate the guards and add state information for unifying a virtual
+        object with a non-virtual. This involves forcing the object in the
+        event that unification can succeed. Since virtual objects cannot be 
null,
+        this method need only check that the virtual object has the expected 
type.
+        """
+        assert state.force_boxes and isinstance(other, VirtualStateInfo)
+
+        if self.level == LEVEL_CONSTANT:
+            raise VirtualStatesCantMatch(
+                    "cannot unify a constant value with a virtual object")
+
+        if self.level == LEVEL_KNOWNCLASS:
+            if not self.known_class.same_constant(other.known_class):
+                raise VirtualStatesCantMatch("classes don't match")
+
     def _generate_guards_nonnull(self, other, box, runtime_box, extra_guards,
                                  state):
         if not isinstance(other, NotVirtualStateInfoPtr):
@@ -617,10 +637,10 @@
             return False
         return True
 
-    def generate_guards(self, other, boxes, runtime_boxes, optimizer):
+    def generate_guards(self, other, boxes, runtime_boxes, optimizer, 
force_boxes=False):
         assert (len(self.state) == len(other.state) == len(boxes) ==
                 len(runtime_boxes))
-        state = GenerateGuardState(optimizer)
+        state = GenerateGuardState(optimizer, force_boxes=force_boxes)
         for i in range(len(self.state)):
             self.state[i].generate_guards(other.state[i], boxes[i],
                                           runtime_boxes[i], state)
@@ -644,8 +664,8 @@
 
         return boxes
 
-    def make_inputargs_and_virtuals(self, inputargs, optimizer):
-        inpargs = self.make_inputargs(inputargs, optimizer)
+    def make_inputargs_and_virtuals(self, inputargs, optimizer, 
force_boxes=False):
+        inpargs = self.make_inputargs(inputargs, optimizer, force_boxes)
         # we append the virtuals here in case some stuff is proven
         # to be not a virtual and there are getfields in the short preamble
         # that will read items out of there
@@ -653,7 +673,7 @@
         for i in range(len(inputargs)):
             if not isinstance(self.state[i], NotVirtualStateInfo):
                 virtuals.append(inputargs[i])
-            
+
         return inpargs, virtuals
 
     def debug_print(self, hdr='', bad=None, metainterp_sd=None):
diff --git a/rpython/jit/metainterp/test/test_ajit.py 
b/rpython/jit/metainterp/test/test_ajit.py
--- a/rpython/jit/metainterp/test/test_ajit.py
+++ b/rpython/jit/metainterp/test/test_ajit.py
@@ -4507,3 +4507,54 @@
                 i += 1
             return i
         self.meta_interp(f, [])
+
+    def test_round_trip_raw_pointer(self):
+        # The goal of this test to to get a raw pointer op into the short 
preamble
+        # so we can check that the proper guards are generated
+        # In this case, the resulting short preamble contains
+        #
+        # i1 = getfield_gc_i(p0, descr=inst__ptr)
+        # i2 = int_eq(i1, 0)
+        # guard_false(i2)
+        #
+        # as opposed to what the JIT used to produce
+        #
+        # i1 = getfield_gc_i(p0, descr=inst__ptr)
+        # guard_nonnull(i1)
+        #
+        # Which will probably generate correct assembly, but the optimization
+        # pipline expects guard_nonnull arguments to be pointer ops and may 
crash
+        # and may crash on other input types.
+        driver = JitDriver(greens=[], reds=['i', 'val'])
+
+        class Box(object):
+            _ptr = lltype.nullptr(rffi.CCHARP.TO)
+
+        def new_int_buffer(value):
+            data = lltype.malloc(rffi.CCHARP.TO, rffi.sizeof(rffi.INT), 
flavor='raw')
+            rffi.cast(rffi.INTP, data)[0] = rffi.cast(rffi.INT, value)
+            return data
+
+        def read_int_buffer(buf):
+            return rffi.cast(rffi.INTP, buf)[0]
+
+        def f():
+            i = 0
+            val = Box()
+            val._ptr = new_int_buffer(1)
+
+            set_param(None, 'retrace_limit', -1)
+            while i < 100:
+                driver.jit_merge_point(i=i, val=val)
+                driver.can_enter_jit(i=i, val=val)
+                # Just to produce a side exit
+                if i & 0b100:
+                    i += 1
+                i += int(read_int_buffer(val._ptr))
+                lltype.free(val._ptr, flavor='raw')
+                val._ptr = new_int_buffer(1)
+            lltype.free(val._ptr, flavor='raw')
+
+        self.meta_interp(f, [])
+        self.check_resops(guard_nonnull=0)
+
diff --git a/rpython/jit/metainterp/test/test_virtual.py 
b/rpython/jit/metainterp/test/test_virtual.py
--- a/rpython/jit/metainterp/test/test_virtual.py
+++ b/rpython/jit/metainterp/test/test_virtual.py
@@ -1,8 +1,8 @@
 import py
-from rpython.rlib.jit import JitDriver, promote, dont_look_inside
+from rpython.rlib.jit import JitDriver, promote, dont_look_inside, set_param
 from rpython.rlib.objectmodel import compute_unique_id
 from rpython.jit.codewriter.policy import StopAtXPolicy
-from rpython.jit.metainterp.test.support import LLJitMixin
+from rpython.jit.metainterp.test.support import LLJitMixin, get_stats
 from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.rtyper import rclass
 from rpython.rtyper.lltypesystem.lloperation import llop
@@ -965,6 +965,82 @@
         self.check_aborted_count(0)
         self.check_target_token_count(4)
 
+    def test_avoid_preamble(self):
+        driver = JitDriver(greens=[], reds=['i', 'val'])
+        class X(object):
+            def __init__(self, v):
+                self.v = v
+
+        class Box(object):
+            def __init__(self, v):
+                self.unbox = v
+
+        mask = -2
+        const = Box(X(5))
+        def f():
+            # Prevent all retracing of side exits. Ensures that the unroll
+            # optimizer will attempt to jump to either the preamble or loop.
+            set_param(driver, 'retrace_limit', -1)
+            set_param(driver, 'threshold', 1)
+            val   = X(0)
+            i     = 0
+            const.unbox = X(5)
+            while i < 17:
+                driver.can_enter_jit(i=i, val=val)
+                driver.jit_merge_point(i=i, val=val)
+                # Logical & rather than comparison to confuse range analysis.
+                # Test only succeeds on the first 2 iterations
+                if i & -2 == 0:
+                    val = const.unbox
+                else:
+                    val = X(i)
+                i += 1
+            return 0
+
+        self.meta_interp(f, [])
+
+        # With retracing disable, there will be one optimized loop expecting a
+        # non-virtual X object. The side exit creates a virtual object which 
must
+        # be allocated to jump to the optimized trace.
+        self.check_resops(jump=3, label=2, new_with_vtable=2)
+        self.check_target_token_count(2)
+        self.check_trace_count(3)
+
+    def test_conflated_virtual_states(self):
+        # All cases are covered when forcing one component of the virtual state
+        # also forces an as yet unseen component.
+        # i.e. expect [NotVirtual, Virtual] and given a pair of aliasing 
virtual
+        # objects
+        driver = JitDriver(greens=[], reds=['i', 'v1', 'v2'])
+        class Box(object):
+            def __init__(self, v):
+                self.v = v
+
+        class X(object):
+            def __init__(self, v):
+                self.v = v
+
+        const = Box(X(0))
+        def f():
+            set_param(None, 'retrace_limit', -1)
+            set_param(None, 'threshold', 1)
+            i = 0
+            v1 = X(0)
+            v2 = X(0)
+            const.v = X(0)
+            while i < 17:
+                driver.jit_merge_point(i=i, v1=v1, v2=v2)
+                driver.can_enter_jit(i=i, v1=v1, v2=v2)
+                if i & 1 == 0:
+                    v1 = const.v
+                    v2 = X(i)
+                else:
+                    v1 = v2 = X(i)
+                i += 1
+            return None
+        self.meta_interp(f, [])
+        # assert did not crash
+
 
 class VirtualMiscTests:
     def test_multiple_equal_virtuals(self):
diff --git a/rpython/rlib/ropenssl.py b/rpython/rlib/ropenssl.py
--- a/rpython/rlib/ropenssl.py
+++ b/rpython/rlib/ropenssl.py
@@ -97,7 +97,8 @@
 OPENSSL_VERSION_NUMBER = cconfig["OPENSSL_VERSION_NUMBER"]
 HAVE_TLSv1_2 = OPENSSL_VERSION_NUMBER >= 0x10001000
 
-if OPENSSL_VERSION_NUMBER >= 0x10100000:
+if (OPENSSL_VERSION_NUMBER >= 0x10100000 and
+     OPENSSL_VERSION_NUMBER < 0x20000000):    # <= libressl :-(
     eci.pre_include_bits = ()
     eci.post_include_bits = ()
     raise Exception("""OpenSSL version >= 1.1 not supported yet.
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to