Author: Ronan Lamy <ronan.l...@gmail.com> Branch: py3k Changeset: r85315:fb39f32b33f1 Date: 2016-06-21 17:54 +0100 http://bitbucket.org/pypy/pypy/changeset/fb39f32b33f1/
Log: hg merge default diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -26,3 +26,4 @@ 40497617ae91caa1a394d8be6f9cd2de31cb0628 release-pypy3.3-v5.2 40497617ae91caa1a394d8be6f9cd2de31cb0628 release-pypy3.3-v5.2 c09c19272c990a0611b17569a0085ad1ab00c8ff release-pypy2.7-v5.3 +7e8df3df96417c16c2d55b41352ec82c9c69c978 release-pypy2.7-v5.3.1 diff --git a/lib_pypy/greenlet.egg-info b/lib_pypy/greenlet.egg-info --- a/lib_pypy/greenlet.egg-info +++ b/lib_pypy/greenlet.egg-info @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: greenlet -Version: 0.4.9 +Version: 0.4.10 Summary: Lightweight in-process concurrent programming Home-page: https://github.com/python-greenlet/greenlet Author: Ralf Schmitt (for CPython), PyPy team diff --git a/lib_pypy/greenlet.py b/lib_pypy/greenlet.py --- a/lib_pypy/greenlet.py +++ b/lib_pypy/greenlet.py @@ -2,7 +2,7 @@ import __pypy__ import _continuation -__version__ = "0.4.9" +__version__ = "0.4.10" # ____________________________________________________________ # Exceptions diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst --- a/pypy/doc/cpython_differences.rst +++ b/pypy/doc/cpython_differences.rst @@ -315,13 +315,28 @@ - ``complex`` + - ``str`` (empty or single-character strings only) + + - ``unicode`` (empty or single-character strings only) + + - ``tuple`` (empty tuples only) + + - ``frozenset`` (empty frozenset only) + This change requires some changes to ``id`` as well. ``id`` fulfills the following condition: ``x is y <=> id(x) == id(y)``. Therefore ``id`` of the above types will return a value that is computed from the argument, and can thus be larger than ``sys.maxint`` (i.e. it can be an arbitrary long). -Notably missing from the list above are ``str`` and ``unicode``. If your -code relies on comparing strings with ``is``, then it might break in PyPy. +Note that strings of length 2 or greater can be equal without being +identical. Similarly, ``x is (2,)`` is not necessarily true even if +``x`` contains a tuple and ``x == (2,)``. The uniqueness rules apply +only to the particular cases described above. The ``str``, ``unicode``, +``tuple`` and ``frozenset`` rules were added in PyPy 5.4; before that, a +test like ``if x is "?"`` or ``if x is ()`` could fail even if ``x`` was +equal to ``"?"`` or ``()``. The new behavior added in PyPy 5.4 is +closer to CPython's, which caches precisely the empty tuple/frozenset, +and (generally but not always) the strings and unicodes of length <= 1. Note that for floats there "``is``" only one object per "bit pattern" of the float. So ``float('nan') is float('nan')`` is true on PyPy, diff --git a/pypy/doc/index-of-release-notes.rst b/pypy/doc/index-of-release-notes.rst --- a/pypy/doc/index-of-release-notes.rst +++ b/pypy/doc/index-of-release-notes.rst @@ -6,6 +6,7 @@ .. toctree:: + release-pypy2.7-v5.3.1.rst release-pypy2.7-v5.3.0.rst release-5.1.1.rst release-5.1.0.rst diff --git a/pypy/doc/index-of-whatsnew.rst b/pypy/doc/index-of-whatsnew.rst --- a/pypy/doc/index-of-whatsnew.rst +++ b/pypy/doc/index-of-whatsnew.rst @@ -7,6 +7,7 @@ .. toctree:: whatsnew-head.rst + whatsnew-pypy2-5.3.1.rst whatsnew-pypy2-5.3.0.rst whatsnew-5.1.0.rst whatsnew-5.0.0.rst diff --git a/pypy/doc/release-pypy2.7-v5.3.1.rst b/pypy/doc/release-pypy2.7-v5.3.1.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-pypy2.7-v5.3.1.rst @@ -0,0 +1,41 @@ +========== +PyPy 5.3.1 +========== + +We have released a bugfix for PyPy2.7-v5.3.0, released last week, +due to issues_ reported by users. + +Thanks to those who reported the issues. + +.. _issues: http://doc.pypy.org/en/latest/whatsnew-pypy2-5.3.1.html + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7. It's fast (`PyPy and CPython 2.7.x`_ performance comparison) +due to its integrated tracing JIT compiler. + +We also welcome developers of other +`dynamic languages`_ to see what RPython can do for them. + +This release supports: + + * **x86** machines on most common operating systems + (Linux 32/64, Mac OS X 64, Windows 32, OpenBSD, FreeBSD), + + * newer **ARM** hardware (ARMv6 or ARMv7, with VFPv3) running Linux, + + * big- and little-endian variants of **PPC64** running Linux, + + * **s390x** running Linux + +.. _`PyPy and CPython 2.7.x`: http://speed.pypy.org +.. _`dynamic languages`: http://pypyjs.org + +Please update, and continue to help us make PyPy better. + +Cheers + +The PyPy Team + 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 @@ -5,6 +5,10 @@ .. this is a revision shortly after release-pypy2.7-v5.3 .. startrev: 873218a739f1 +.. 418b05f95db5 +Improve CPython compatibility for ``is``. Now code like ``if x is ():`` +works the same way as it does on CPython. See http://pypy.readthedocs.io/en/latest/cpython_differences.html#object-identity-of-primitive-values-is-and-id . + .. pull request #455 Add sys.{get,set}dlopenflags, for cpyext extensions. @@ -31,3 +35,10 @@ Simplify handling of interp-level tests and make it more forward- compatible. +.. branch: pyfile-tell +Sync w_file with the c-level FILE* before returning FILE* in PyFile_AsFile + +.. branch: rw-PyString_AS_STRING +Allow rw access to the char* returned from PyString_AS_STRING, also refactor +PyStringObject to look like cpython's and allow subclassing PyString_Type and +PyUnicode_Type diff --git a/pypy/doc/whatsnew-pypy2-5.3.1.rst b/pypy/doc/whatsnew-pypy2-5.3.1.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/whatsnew-pypy2-5.3.1.rst @@ -0,0 +1,15 @@ +=========================== +What's new in PyPy2.7 5.3.1 +=========================== + +.. this is a revision shortly after release-pypy2.7-v5.3.0 +.. startrev: f4d726d1a010 + + +A bug-fix release, merging these changes: + + * Add include guards to pymem.h, fixes issue #2321 + + * Make vmprof build on OpenBSD, from pull request #456 + + * Fix ``bytearray('').replace('a', 'ab')``, issue #2324 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 @@ -1212,8 +1212,6 @@ cpyext_type_init = self.cpyext_type_init self.cpyext_type_init = None for pto, w_type in cpyext_type_init: - if space.is_w(w_type, space.w_str): - pto.c_tp_itemsize = 1 finish_type_1(space, pto) finish_type_2(space, pto, w_type) diff --git a/pypy/module/cpyext/bytesobject.py b/pypy/module/cpyext/bytesobject.py --- a/pypy/module/cpyext/bytesobject.py +++ b/pypy/module/cpyext/bytesobject.py @@ -16,7 +16,7 @@ ## ----------- ## ## PyBytes_AsString() must return a (non-movable) pointer to the underlying -## buffer, whereas pypy strings are movable. C code may temporarily store +## ob_sval, whereas pypy strings are movable. C code may temporarily store ## this address and use it, as long as it owns a reference to the PyObject. ## There is no "release" function to specify that the pointer is not needed ## any more. @@ -28,7 +28,7 @@ ## -------- ## ## PyBytesObject contains two additional members: the ob_size and a pointer to a -## char buffer; it may be NULL. +## char ob_sval; it may be NULL. ## ## - A string allocated by pypy will be converted into a PyBytesObject with a ## NULL buffer. The first time PyBytes_AsString() is called, memory is @@ -41,6 +41,9 @@ ## the pypy string is created, and added to the global map of tracked ## objects. The buffer is then supposed to be immutable. ## +##- A buffer obtained from PyBytes_AS_STRING() could be mutable iff +## there is no corresponding pypy object for the string +## ## - _PyBytes_Resize() works only on not-yet-pypy'd strings, and returns a ## similar object. ## @@ -53,7 +56,7 @@ PyBytesObjectStruct = lltype.ForwardReference() PyBytesObject = lltype.Ptr(PyBytesObjectStruct) PyBytesObjectFields = PyVarObjectFields + \ - (("ob_shash", rffi.LONG), ("ob_sstate", rffi.INT), ("buffer", rffi.CCHARP)) + (("ob_shash", rffi.LONG), ("ob_sstate", rffi.INT), ("ob_sval", rffi.CArray(lltype.Char))) cpython_struct("PyBytesObject", PyBytesObjectFields, PyBytesObjectStruct) @bootstrap_function @@ -69,44 +72,43 @@ def new_empty_str(space, length): """ - Allocate a PyBytesObject and its buffer, but without a corresponding - interpreter object. The buffer may be mutated, until bytes_realize() is + Allocate a PyBytesObject and its ob_sval, but without a corresponding + interpreter object. The ob_sval may be mutated, until bytes_realize() is called. Refcount of the result is 1. """ typedescr = get_typedescr(space.w_bytes.layout.typedef) py_obj = typedescr.allocate(space, space.w_bytes) py_str = rffi.cast(PyBytesObject, py_obj) - - buflen = length + 1 - py_str.c_ob_size = length - py_str.c_buffer = lltype.malloc(rffi.CCHARP.TO, buflen, - flavor='raw', zero=True, - add_memory_pressure=True) + py_str.c_ob_shash = -1 py_str.c_ob_sstate = rffi.cast(rffi.INT, 0) # SSTATE_NOT_INTERNED return py_str def bytes_attach(space, py_obj, w_obj): """ - Fills a newly allocated PyBytesObject with the given string object. The - buffer must not be modified. + Copy RPython string object contents to a PyBytesObject. The + c_ob_sval must not be modified. """ py_str = rffi.cast(PyBytesObject, py_obj) - py_str.c_ob_size = len(space.str_w(w_obj)) - py_str.c_buffer = lltype.nullptr(rffi.CCHARP.TO) + s = space.str_w(w_obj) + if py_str.c_ob_size < len(s): + raise oefmt(space.w_ValueError, + "bytes_attach called on object with ob_size %d but trying to store %d", + py_str.c_ob_size, len(s)) + rffi.c_memcpy(py_str.c_ob_sval, rffi.str2charp(s), len(s)) + py_str.c_ob_sval[len(s)] = '\0' py_str.c_ob_shash = space.hash_w(w_obj) py_str.c_ob_sstate = rffi.cast(rffi.INT, 1) # SSTATE_INTERNED_MORTAL def bytes_realize(space, py_obj): """ - Creates the string in the interpreter. The PyBytesObject buffer must not + Creates the string in the interpreter. The PyBytesObject ob_sval must not be modified after this call. """ py_str = rffi.cast(PyBytesObject, py_obj) - if not py_str.c_buffer: - py_str.c_buffer = lltype.malloc(rffi.CCHARP.TO, py_str.c_ob_size + 1, - flavor='raw', zero=True) - s = rffi.charpsize2str(py_str.c_buffer, py_str.c_ob_size) - w_obj = space.wrapbytes(s) + s = rffi.charpsize2str(py_str.c_ob_sval, py_str.c_ob_size) + w_type = from_ref(space, rffi.cast(PyObject, py_obj.c_ob_type)) + w_obj = space.allocate_instance(W_BytesObject, w_type) + w_obj.__init__(s) py_str.c_ob_shash = space.hash_w(w_obj) py_str.c_ob_sstate = rffi.cast(rffi.INT, 1) # SSTATE_INTERNED_MORTAL track_reference(space, py_obj, w_obj) @@ -116,9 +118,6 @@ def bytes_dealloc(space, py_obj): """Frees allocated PyBytesObject resources. """ - py_str = rffi.cast(PyBytesObject, py_obj) - if py_str.c_buffer: - lltype.free(py_str.c_buffer, flavor="raw") from pypy.module.cpyext.object import PyObject_dealloc PyObject_dealloc(space, py_obj) @@ -139,36 +138,45 @@ @cpython_api([PyObject], rffi.CCHARP, error=0) def PyBytes_AsString(space, ref): + return _PyBytes_AsString(space, ref) + +def _PyBytes_AsString(space, ref): if from_ref(space, rffi.cast(PyObject, ref.c_ob_type)) is space.w_str: pass # typecheck returned "ok" without forcing 'ref' at all elif not PyBytes_Check(space, ref): # otherwise, use the alternate way raise oefmt(space.w_TypeError, "expected bytes, %T found", from_ref(space, ref)) ref_str = rffi.cast(PyBytesObject, ref) - if not ref_str.c_buffer: - # copy string buffer - w_str = from_ref(space, ref) - s = space.bytes_w(w_str) - ref_str.c_buffer = rffi.str2charp(s) - return ref_str.c_buffer + if not pyobj_has_w_obj(ref): + # XXX Force the ref? + bytes_realize(space, ref) + return ref_str.c_ob_sval + +@cpython_api([rffi.VOIDP], rffi.CCHARP, error=0) +def PyBytes_AS_STRING(space, void_ref): + ref = rffi.cast(PyObject, void_ref) + # if no w_str is associated with this ref, + # return the c-level ptr as RW + if not pyobj_has_w_obj(ref): + py_str = rffi.cast(PyBytesObject, ref) + return py_str.c_ob_sval + return _PyBytes_AsString(space, ref) @cpython_api([PyObject, rffi.CCHARPP, rffi.CArrayPtr(Py_ssize_t)], rffi.INT_real, error=-1) -def PyBytes_AsStringAndSize(space, ref, buffer, length): +def PyBytes_AsStringAndSize(space, ref, data, length): if not PyBytes_Check(space, ref): raise oefmt(space.w_TypeError, "expected bytes, %T found", from_ref(space, ref)) + if not pyobj_has_w_obj(ref): + # force the ref + bytes_realize(space, ref) ref_str = rffi.cast(PyBytesObject, ref) - if not ref_str.c_buffer: - # copy string buffer - w_str = from_ref(space, ref) - s = space.bytes_w(w_str) - ref_str.c_buffer = rffi.str2charp(s) - buffer[0] = ref_str.c_buffer + data[0] = ref_str.c_ob_sval if length: length[0] = ref_str.c_ob_size else: i = 0 - while ref_str.c_buffer[i] != '\0': + while ref_str.c_ob_sval[i] != '\0': i += 1 if i != ref_str.c_ob_size: raise oefmt(space.w_TypeError, @@ -197,10 +205,10 @@ set to NULL, a memory exception is set, and -1 is returned. """ # XXX always create a new string so far - py_str = rffi.cast(PyBytesObject, ref[0]) - if not py_str.c_buffer: + if pyobj_has_w_obj(ref[0]): raise oefmt(space.w_SystemError, "_PyBytes_Resize called on already created string") + py_str = rffi.cast(PyBytesObject, ref[0]) try: py_newstr = new_empty_str(space, newsize) except MemoryError: @@ -212,7 +220,7 @@ if oldsize < newsize: to_cp = oldsize for i in range(to_cp): - py_newstr.c_buffer[i] = py_str.c_buffer[i] + py_newstr.c_ob_sval[i] = py_str.c_ob_sval[i] Py_DecRef(space, ref[0]) ref[0] = rffi.cast(PyObject, py_newstr) return 0 diff --git a/pypy/module/cpyext/include/patchlevel.h b/pypy/module/cpyext/include/patchlevel.h --- a/pypy/module/cpyext/include/patchlevel.h +++ b/pypy/module/cpyext/include/patchlevel.h @@ -29,8 +29,8 @@ #define PY_VERSION "3.3.5" /* PyPy version as a string */ -#define PYPY_VERSION "5.3.1-alpha0" -#define PYPY_VERSION_NUM 0x05030100 +#define PYPY_VERSION "5.3.2-alpha0" +#define PYPY_VERSION_NUM 0x05030200 /* Defined to mean a PyPy where cpyext holds more regular references to PyObjects, e.g. staying alive as long as the internal PyPy object diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -7,7 +7,7 @@ from pypy.module.cpyext.api import ( cpython_api, bootstrap_function, PyObject, PyObjectP, ADDR, CANNOT_FAIL, Py_TPFLAGS_HEAPTYPE, PyTypeObjectPtr, is_PyObject, - INTERPLEVEL_API) + INTERPLEVEL_API, PyVarObject) from pypy.module.cpyext.state import State from pypy.objspace.std.typeobject import W_TypeObject from pypy.objspace.std.objectobject import W_ObjectObject @@ -47,13 +47,16 @@ size = pytype.c_tp_basicsize else: size = rffi.sizeof(self.basestruct) - if itemcount and w_type is not space.w_str: + if pytype.c_tp_itemsize: size += itemcount * pytype.c_tp_itemsize assert size >= rffi.sizeof(PyObject.TO) buf = lltype.malloc(rffi.VOIDP.TO, size, flavor='raw', zero=True, add_memory_pressure=True) pyobj = rffi.cast(PyObject, buf) + if pytype.c_tp_itemsize: + pyvarobj = rffi.cast(PyVarObject, pyobj) + pyvarobj.c_ob_size = itemcount pyobj.c_ob_refcnt = 1 #pyobj.c_ob_pypy_link should get assigned very quickly pyobj.c_ob_type = pytype @@ -152,13 +155,18 @@ class InvalidPointerException(Exception): pass -def create_ref(space, w_obj, itemcount=0): +def create_ref(space, w_obj): """ Allocates a PyObject, and fills its fields with info from the given interpreter object. """ w_type = space.type(w_obj) + pytype = rffi.cast(PyTypeObjectPtr, as_pyobj(space, w_type)) typedescr = get_typedescr(w_obj.typedef) + if pytype.c_tp_itemsize != 0: + itemcount = space.len_w(w_obj) # PyStringObject and subclasses + else: + itemcount = 0 py_obj = typedescr.allocate(space, w_type, itemcount=itemcount) track_reference(space, py_obj, w_obj) # diff --git a/pypy/module/cpyext/test/test_bytearrayobject.py b/pypy/module/cpyext/test/test_bytearrayobject.py --- a/pypy/module/cpyext/test/test_bytearrayobject.py +++ b/pypy/module/cpyext/test/test_bytearrayobject.py @@ -1,5 +1,6 @@ from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase + class AppTestStringObject(AppTestCpythonExtensionBase): def test_basic(self): module = self.import_extension('foo', [ diff --git a/pypy/module/cpyext/test/test_bytesobject.py b/pypy/module/cpyext/test/test_bytesobject.py --- a/pypy/module/cpyext/test/test_bytesobject.py +++ b/pypy/module/cpyext/test/test_bytesobject.py @@ -113,7 +113,15 @@ Py_INCREF(obj); return obj; """), + ('alloc_rw', "METH_NOARGS", + ''' + PyObject *obj = _PyObject_NewVar(&PyBytes_Type, 10); + memcpy(PyBytes_AS_STRING(obj), "works", 6); + return (PyObject*)obj; + '''), ]) + s = module.alloc_rw() + assert s == b'works' + b'\x00' * 5 s = module.tpalloc() assert s == b'\x00' * 10 @@ -194,22 +202,22 @@ def test_bytes_resize(self, space, api): py_str = new_empty_str(space, 10) ar = lltype.malloc(PyObjectP.TO, 1, flavor='raw') - py_str.c_buffer[0] = 'a' - py_str.c_buffer[1] = 'b' - py_str.c_buffer[2] = 'c' + py_str.c_ob_sval[0] = 'a' + py_str.c_ob_sval[1] = 'b' + py_str.c_ob_sval[2] = 'c' ar[0] = rffi.cast(PyObject, py_str) api._PyBytes_Resize(ar, 3) py_str = rffi.cast(PyBytesObject, ar[0]) assert py_str.c_ob_size == 3 - assert py_str.c_buffer[1] == 'b' - assert py_str.c_buffer[3] == '\x00' + assert py_str.c_ob_sval[1] == 'b' + assert py_str.c_ob_sval[3] == '\x00' # the same for growing ar[0] = rffi.cast(PyObject, py_str) api._PyBytes_Resize(ar, 10) py_str = rffi.cast(PyBytesObject, ar[0]) assert py_str.c_ob_size == 10 - assert py_str.c_buffer[1] == 'b' - assert py_str.c_buffer[10] == '\x00' + assert py_str.c_ob_sval[1] == 'b' + assert py_str.c_ob_sval[10] == '\x00' Py_DecRef(space, ar[0]) lltype.free(ar, flavor='raw') diff --git a/pypy/module/cpyext/test/test_datetime.py b/pypy/module/cpyext/test/test_datetime.py --- a/pypy/module/cpyext/test/test_datetime.py +++ b/pypy/module/cpyext/test/test_datetime.py @@ -142,7 +142,7 @@ 2000, 6, 6, 6, 6, 6, 6, Py_None, PyDateTimeAPI->DateTimeType); """), - ]) + ], prologue='#include "datetime.h"\n') import datetime assert module.new_date() == datetime.date(2000, 6, 6) assert module.new_time() == datetime.time(6, 6, 6, 6) @@ -241,6 +241,9 @@ PyObject* obj = PyDelta_FromDSU(6, 6, 6); PyDateTime_Delta* delta = (PyDateTime_Delta*)obj; +#if defined(PYPY_VERSION) || PY_VERSION_HEX >= 0x03030000 + // These macros are only defined in CPython 3.x and PyPy. + // See: http://bugs.python.org/issue13727 PyDateTime_DELTA_GET_DAYS(obj); PyDateTime_DELTA_GET_DAYS(delta); @@ -249,10 +252,10 @@ PyDateTime_DELTA_GET_MICROSECONDS(obj); PyDateTime_DELTA_GET_MICROSECONDS(delta); - +#endif return obj; """), - ]) + ], prologue='#include "datetime.h"\n') import datetime assert module.test_date_macros() == datetime.date(2000, 6, 6) assert module.test_datetime_macros() == datetime.datetime( diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py --- a/pypy/module/cpyext/test/test_typeobject.py +++ b/pypy/module/cpyext/test/test_typeobject.py @@ -875,7 +875,7 @@ module.footype("X", (object,), {}) def test_app_subclass_of_c_type(self): - # on cpython, the size changes (6 bytes added) + import sys module = self.import_module(name='foo') size = module.size_of_instances(module.fooType) class f1(object): @@ -885,7 +885,11 @@ class bar(f1, f2): pass assert bar.__base__ is f2 - assert module.size_of_instances(bar) == size + # On cpython, the size changes. + if '__pypy__' in sys.builtin_module_names: + assert module.size_of_instances(bar) == size + else: + assert module.size_of_instances(bar) >= size def test_app_cant_subclass_two_types(self): module = self.import_module(name='foo') diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py --- a/pypy/module/cpyext/tupleobject.py +++ b/pypy/module/cpyext/tupleobject.py @@ -142,6 +142,7 @@ ref = rffi.cast(PyTupleObject, ref) size = ref.c_ob_size if index < 0 or index >= size: + decref(space, py_obj) raise oefmt(space.w_IndexError, "tuple assignment index out of range") old_ref = ref.c_ob_item[index] ref.c_ob_item[index] = py_obj # consumes a reference diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py --- a/pypy/module/cpyext/typeobject.py +++ b/pypy/module/cpyext/typeobject.py @@ -563,6 +563,8 @@ pto.c_tp_dealloc = llhelper( subtype_dealloc.api_func.functype, subtype_dealloc.api_func.get_wrapper(space)) + if space.is_w(w_type, space.w_str): + pto.c_tp_itemsize = 1 # buffer protocol if space.is_w(w_type, space.w_str): setup_bytes_buffer_procs(space, pto) @@ -602,6 +604,8 @@ if pto.c_tp_base: if pto.c_tp_base.c_tp_basicsize > pto.c_tp_basicsize: pto.c_tp_basicsize = pto.c_tp_base.c_tp_basicsize + if pto.c_tp_itemsize < pto.c_tp_base.c_tp_itemsize: + pto.c_tp_itemsize = pto.c_tp_base.c_tp_itemsize # will be filled later on with the correct value # may not be 0 diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py --- a/pypy/module/cpyext/unicodeobject.py +++ b/pypy/module/cpyext/unicodeobject.py @@ -72,7 +72,10 @@ """ py_uni = rffi.cast(PyUnicodeObject, py_obj) s = rffi.wcharpsize2unicode(py_uni.c_buffer, py_uni.c_length) - w_obj = space.wrap(s) + w_type = from_ref(space, rffi.cast(PyObject, py_obj.c_ob_type)) + w_obj = space.allocate_instance(unicodeobject.W_UnicodeObject, w_type) + w_obj.__init__(s) + py_uni.c_hash = space.hash_w(w_obj) track_reference(space, py_obj, w_obj) return w_obj diff --git a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py --- a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py +++ b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py @@ -163,14 +163,10 @@ guard_not_invalidated(descr=...) i32 = float_ne(f31, 0.000000) guard_true(i32, descr=...) - i34 = getarrayitem_raw_i(#, #, descr=<ArrayU 1>) # XXX what are these? - guard_value(i34, #, descr=...) # XXX don't appear in - i35 = getarrayitem_raw_i(#, #, descr=<ArrayU 1>) # XXX equiv test_zjit i36 = int_add(i24, 1) i37 = int_add(i29, 8) i38 = int_ge(i36, i30) guard_false(i38, descr=...) - guard_value(i35, #, descr=...) # XXX jump(..., descr=...) """) diff --git a/pypy/module/sys/version.py b/pypy/module/sys/version.py --- a/pypy/module/sys/version.py +++ b/pypy/module/sys/version.py @@ -10,7 +10,7 @@ #XXX # sync CPYTHON_VERSION with patchlevel.h, package.py CPYTHON_API_VERSION = 1013 #XXX # sync with include/modsupport.h -PYPY_VERSION = (5, 3, 1, "alpha", 0) #XXX # sync patchlevel.h +PYPY_VERSION = (5, 3, 2, "alpha", 0) #XXX # sync patchlevel.h import pypy diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py --- a/pypy/objspace/std/bytesobject.py +++ b/pypy/objspace/std/bytesobject.py @@ -13,6 +13,7 @@ WrappedDefault, interp2app, interpindirect2app, unwrap_spec) from pypy.interpreter.typedef import TypeDef from pypy.objspace.std.stringmethods import StringMethods +from pypy.objspace.std.util import IDTAG_SPECIAL, IDTAG_SHIFT class W_AbstractBytesObject(W_Root): @@ -25,12 +26,26 @@ return True if self.user_overridden_class or w_other.user_overridden_class: return False - return space.bytes_w(self) is space.bytes_w(w_other) + s1 = space.str_w(self) + s2 = space.str_w(w_other) + if len(s2) > 1: + return s1 is s2 + else: # strings of len <= 1 are unique-ified + return s1 == s2 def immutable_unique_id(self, space): if self.user_overridden_class: return None - return space.wrap(compute_unique_id(space.bytes_w(self))) + s = space.str_w(self) + if len(s) > 1: + uid = compute_unique_id(s) + else: # strings of len <= 1 are unique-ified + if len(s) == 1: + base = ord(s[0]) # base values 0-255 + else: + base = 256 # empty string: base value 256 + uid = (base << IDTAG_SHIFT) | IDTAG_SPECIAL + return space.wrap(uid) def descr_add(self, space, w_other): """x.__add__(y) <==> x+y""" diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py --- a/pypy/objspace/std/setobject.py +++ b/pypy/objspace/std/setobject.py @@ -6,6 +6,7 @@ from pypy.objspace.std.bytesobject import W_BytesObject from pypy.objspace.std.intobject import W_IntObject from pypy.objspace.std.unicodeobject import W_UnicodeObject +from pypy.objspace.std.util import IDTAG_SPECIAL, IDTAG_SHIFT from rpython.rlib.objectmodel import r_dict from rpython.rlib.objectmodel import iterkeys_with_hash, contains_with_hash @@ -562,6 +563,23 @@ class W_FrozensetObject(W_BaseSetObject): hash = 0 + def is_w(self, space, w_other): + if not isinstance(w_other, W_FrozensetObject): + return False + if self is w_other: + return True + if self.user_overridden_class or w_other.user_overridden_class: + return False + # empty frozensets are unique-ified + return 0 == w_other.length() == self.length() + + def immutable_unique_id(self, space): + if self.user_overridden_class or self.length() > 0: + return None + # empty frozenset: base value 259 + uid = (259 << IDTAG_SHIFT) | IDTAG_SPECIAL + return space.wrap(uid) + def _newobj(self, space, w_iterable): """Make a new frozenset by taking ownership of 'w_iterable'.""" return W_FrozensetObject(space, w_iterable) diff --git a/pypy/objspace/std/stringmethods.py b/pypy/objspace/std/stringmethods.py --- a/pypy/objspace/std/stringmethods.py +++ b/pypy/objspace/std/stringmethods.py @@ -182,7 +182,7 @@ else: res = count(value, sub, start, end) assert res >= 0 - return space.wrap(max(res, 0)) + return space.wrap(res) def descr_decode(self, space, w_encoding=None, w_errors=None): from pypy.objspace.std.unicodeobject import ( diff --git a/pypy/objspace/std/test/test_obj.py b/pypy/objspace/std/test/test_obj.py --- a/pypy/objspace/std/test/test_obj.py +++ b/pypy/objspace/std/test/test_obj.py @@ -183,37 +183,54 @@ def test_id_on_strs(self): if self.appdirect: skip("cannot run this test as apptest") - u = "a" - assert id(self.unwrap_wrap_unicode(u)) == id(u) - s = b"a" - assert id(self.unwrap_wrap_bytes(s)) == id(s) + for u in [u"", u"a", u"aa"]: + assert id(self.unwrap_wrap_unicode(u)) == id(u) + s = str(u) + assert id(self.unwrap_wrap_str(s)) == id(s) + # + assert id('') == (256 << 4) | 11 # always + assert id(u'') == (257 << 4) | 11 + assert id('a') == (ord('a') << 4) | 11 + assert id(u'\u1234') == ((~0x1234) << 4) | 11 + + def test_id_of_tuples(self): + l = [] + x = (l,) + assert id(x) != id((l,)) # no caching at all + if self.appdirect: + skip("cannot run this test as apptest") + assert id(()) == (258 << 4) | 11 # always + + def test_id_of_frozensets(self): + x = frozenset([4]) + assert id(x) != id(frozenset([4])) # no caching at all + if self.appdirect: + skip("cannot run this test as apptest") + assert id(frozenset()) == (259 << 4) | 11 # always + assert id(frozenset([])) == (259 << 4) | 11 # always def test_identity_vs_id_primitives(self): - if self.cpython_apptest: - skip("cpython behaves differently") import sys - l = list(range(-10, 10)) - for i in range(10): + l = range(-10, 10, 2) + for i in [0, 1, 3]: l.append(float(i)) l.append(i + 0.1) + l.append(long(i)) l.append(i + sys.maxsize) l.append(i - sys.maxsize) l.append(i + 1j) l.append(i - 1j) l.append(1 + i * 1j) l.append(1 - i * 1j) - s = str(i) - l.append(s) - u = bytes(s, 'ascii') - l.append(u) + l.append((i,)) + l.append(frozenset([i])) l.append(-0.0) l.append(None) l.append(True) l.append(False) - s = "s" - l.append(s) - s = b"s" - l.append(s) + l.append(()) + l.append(tuple([])) + l.append(frozenset()) for i, a in enumerate(l): for b in l[i:]: @@ -224,21 +241,18 @@ def test_identity_vs_id_str(self): if self.appdirect: skip("cannot run this test as apptest") - import sys - l = list(range(-10, 10)) - for i in range(10): - s = bytes(i) + l = [] + def add(s, u): l.append(s) - l.append(self.unwrap_wrap_bytes(s)) - u = str(s) + l.append(self.unwrap_wrap_str(s)) + l.append(s[:1] + s[1:]) l.append(u) l.append(self.unwrap_wrap_unicode(u)) - s = b"s" - l.append(s) - l.append(self.unwrap_wrap_bytes(s)) - s = "s" - l.append(s) - l.append(self.unwrap_wrap_unicode(s)) + l.append(u[:1] + u[1:]) + for i in range(3, 18): + add(str(i), unicode(i)) + add(b"s", u"s") + add(b"", u"") for i, a in enumerate(l): for b in l[i:]: diff --git a/pypy/objspace/std/tupleobject.py b/pypy/objspace/std/tupleobject.py --- a/pypy/objspace/std/tupleobject.py +++ b/pypy/objspace/std/tupleobject.py @@ -9,7 +9,7 @@ from pypy.interpreter.typedef import TypeDef from pypy.objspace.std.sliceobject import (W_SliceObject, unwrap_start_stop, normalize_simple_slice) -from pypy.objspace.std.util import negate +from pypy.objspace.std.util import negate, IDTAG_SPECIAL, IDTAG_SHIFT from rpython.rlib import jit from rpython.rlib.debug import make_sure_not_resized from rpython.rlib.rarithmetic import intmask @@ -38,6 +38,23 @@ class W_AbstractTupleObject(W_Root): __slots__ = () + def is_w(self, space, w_other): + if not isinstance(w_other, W_AbstractTupleObject): + return False + if self is w_other: + return True + if self.user_overridden_class or w_other.user_overridden_class: + return False + # empty tuples are unique-ified + return 0 == w_other.length() == self.length() + + def immutable_unique_id(self, space): + if self.user_overridden_class or self.length() > 0: + return None + # empty tuple: base value 258 + uid = (258 << IDTAG_SHIFT) | IDTAG_SPECIAL + return space.wrap(uid) + def __repr__(self): """representation for debugging purposes""" reprlist = [repr(w_item) for w_item in self.tolist()] 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 @@ -17,6 +17,7 @@ from pypy.objspace.std import newformat from pypy.objspace.std.formatting import mod_format from pypy.objspace.std.stringmethods import StringMethods +from pypy.objspace.std.util import IDTAG_SPECIAL, IDTAG_SHIFT __all__ = ['W_UnicodeObject', 'wrapunicode', 'encode_object', 'decode_object', 'unicode_from_object', 'unicode_to_decimal_w'] @@ -51,12 +52,26 @@ return True if self.user_overridden_class or w_other.user_overridden_class: return False - return space.unicode_w(self) is space.unicode_w(w_other) + s1 = space.unicode_w(self) + s2 = space.unicode_w(w_other) + if len(s2) > 1: + return s1 is s2 + else: # strings of len <= 1 are unique-ified + return s1 == s2 def immutable_unique_id(self, space): if self.user_overridden_class: return None - return space.wrap(compute_unique_id(space.unicode_w(self))) + s = space.unicode_w(self) + if len(s) > 1: + uid = compute_unique_id(s) + else: # strings of len <= 1 are unique-ified + if len(s) == 1: + base = ~ord(s[0]) # negative base values + else: + base = 257 # empty unicode string: base value 257 + uid = (base << IDTAG_SHIFT) | IDTAG_SPECIAL + return space.wrap(uid) def unicode_w(self, space): return self._value diff --git a/pypy/objspace/std/util.py b/pypy/objspace/std/util.py --- a/pypy/objspace/std/util.py +++ b/pypy/objspace/std/util.py @@ -10,6 +10,12 @@ IDTAG_FLOAT = 5 IDTAG_COMPLEX = 7 IDTAG_METHOD = 9 +IDTAG_SPECIAL = 11 # -1 - (-maxunicode-1): unichar + # 0 - 255: char + # 256: empty string + # 257: empty unicode + # 258: empty tuple + # 259: empty frozenset CMP_OPS = dict(lt='<', le='<=', eq='==', ne='!=', gt='>', ge='>=') BINARY_BITWISE_OPS = {'and': '&', 'lshift': '<<', 'or': '|', 'rshift': '>>', diff --git a/pypy/tool/release/repackage.sh b/pypy/tool/release/repackage.sh --- a/pypy/tool/release/repackage.sh +++ b/pypy/tool/release/repackage.sh @@ -1,9 +1,9 @@ # Edit these appropriately before running this script maj=5 min=3 -rev=0 +rev=1 branchname=release-$maj.x # ==OR== release-$maj.$min.x -tagname=release-pypy2.7-v$maj.$min # ==OR== release-$maj.$min +tagname=release-pypy2.7-v$maj.$min.$rev # ==OR== release-$maj.$min echo checking hg log -r $branchname hg log -r $branchname || exit 1 diff --git a/rpython/jit/metainterp/optimizeopt/test/test_virtualstate.py b/rpython/jit/metainterp/optimizeopt/test/test_virtualstate.py --- a/rpython/jit/metainterp/optimizeopt/test/test_virtualstate.py +++ b/rpython/jit/metainterp/optimizeopt/test/test_virtualstate.py @@ -2,7 +2,7 @@ import py from rpython.jit.metainterp.optimizeopt.virtualstate import VirtualStateInfo,\ VStructStateInfo, LEVEL_CONSTANT,\ - VArrayStateInfo, NotVirtualStateInfo, VirtualState,\ + VArrayStateInfo, not_virtual, VirtualState,\ GenerateGuardState, VirtualStatesCantMatch, VArrayStructStateInfo from rpython.jit.metainterp.history import ConstInt, ConstPtr, TargetToken from rpython.jit.metainterp.resoperation import InputArgInt, InputArgRef,\ @@ -31,10 +31,10 @@ def setup_class(self): classbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) value = info.InstancePtrInfo(None, classbox) - self.knownclass_info = NotVirtualStateInfo(self.cpu, 'r', value) + self.knownclass_info = not_virtual(self.cpu, 'r', value) classbox = self.cpu.ts.cls_of_box(InputArgRef(self.node2addr)) value = info.InstancePtrInfo(None, classbox) - self.knownclass_info2 = NotVirtualStateInfo(self.cpu, 'r', value) + self.knownclass_info2 = not_virtual(self.cpu, 'r', value) def guards(self, info1, info2, box, runtime_box, expected, inputargs=None): if inputargs is None: @@ -80,7 +80,7 @@ def test_make_inputargs(self): optimizer = FakeOptimizer(self.cpu) args = [InputArgInt()] - info0 = NotVirtualStateInfo(self.cpu, args[0].type, None) + info0 = not_virtual(self.cpu, args[0].type, None) vs = VirtualState([info0]) assert vs.make_inputargs(args, optimizer) == args info0.level = LEVEL_CONSTANT @@ -108,8 +108,8 @@ assert info1 in state.bad and info2 in state.bad for BoxType in (InputArgInt, InputArgFloat, InputArgRef): - info1 = NotVirtualStateInfo(self.cpu, BoxType.type, None) - info2 = NotVirtualStateInfo(self.cpu, BoxType.type, None) + info1 = not_virtual(self.cpu, BoxType.type, None) + info2 = not_virtual(self.cpu, BoxType.type, None) postest(info1, info2) info1, info2 = VArrayStateInfo(42), VArrayStateInfo(42) @@ -126,9 +126,9 @@ def test_NotVirtualStateInfo_generalization(self): def isgeneral(tp1, info1, tp2, info2): - info1 = NotVirtualStateInfo(self.cpu, tp1, info1) + info1 = not_virtual(self.cpu, tp1, info1) info1.position = 0 - info2 = NotVirtualStateInfo(self.cpu, tp2, info2) + info2 = not_virtual(self.cpu, tp2, info2) info2.position = 0 return VirtualState([info1]).generalization_of(VirtualState([info2]), FakeOptimizer(self.cpu)) @@ -166,8 +166,8 @@ assert not isgeneral('r', value1, 'r', value2) def test_field_matching_generalization(self): - const1 = NotVirtualStateInfo(self.cpu, 'i', ConstIntBound(1)) - const2 = NotVirtualStateInfo(self.cpu, 'i', ConstIntBound(2)) + const1 = not_virtual(self.cpu, 'i', ConstIntBound(1)) + const2 = not_virtual(self.cpu, 'i', ConstIntBound(2)) const1.position = const2.position = 1 self.check_invalid(const1, const2) self.check_invalid(const2, const1) @@ -192,16 +192,16 @@ def test_known_class_generalization(self): knownclass1 = info.InstancePtrInfo(None, ConstPtr(self.myptr)) - info1 = NotVirtualStateInfo(self.cpu, 'r', knownclass1) + info1 = not_virtual(self.cpu, 'r', knownclass1) info1.position = 0 knownclass2 = info.InstancePtrInfo(None, ConstPtr(self.myptr)) - info2 = NotVirtualStateInfo(self.cpu, 'r', knownclass2) + info2 = not_virtual(self.cpu, 'r', knownclass2) info2.position = 0 self.check_no_guards(info1, info2) self.check_no_guards(info2, info1) knownclass3 = info.InstancePtrInfo(None, ConstPtr(self.myptr2)) - info3 = NotVirtualStateInfo(self.cpu, 'r', knownclass3) + info3 = not_virtual(self.cpu, 'r', knownclass3) info3.position = 0 self.check_invalid(info1, info3) self.check_invalid(info2, info3) @@ -222,26 +222,26 @@ #unknown_val = PtrOptValue(self.nodebox) #unknownnull_val = PtrOptValue(BoxPtr(self.nullptr)) opt = FakeOptimizer(self.cpu) - unknown_info = NotVirtualStateInfo(self.cpu, 'r', None) + unknown_info = not_virtual(self.cpu, 'r', None) - nonnull_info = NotVirtualStateInfo(self.cpu, 'r', info.NonNullPtrInfo()) + nonnull_info = not_virtual(self.cpu, 'r', info.NonNullPtrInfo()) classbox1 = self.cpu.ts.cls_of_box(ConstPtr(self.nodeaddr)) - knownclass_info = NotVirtualStateInfo(self.cpu, 'r', - info.InstancePtrInfo(None, classbox1)) + knownclass_info = not_virtual(self.cpu, 'r', + info.InstancePtrInfo(None, classbox1)) classbox2 = self.cpu.ts.cls_of_box(ConstPtr(self.node2addr)) - knownclass2_info = NotVirtualStateInfo(self.cpu, 'r', - info.InstancePtrInfo(None, classbox2)) + knownclass2_info = not_virtual(self.cpu, 'r', + info.InstancePtrInfo(None, classbox2)) - constant_info = NotVirtualStateInfo(self.cpu, 'i', - ConstIntBound(1)) - constant_ptr_info = NotVirtualStateInfo(self.cpu, 'r', + constant_info = not_virtual(self.cpu, 'i', + ConstIntBound(1)) + constant_ptr_info = not_virtual(self.cpu, 'r', info.ConstPtrInfo(ConstPtr(self.nodeaddr))) constclass_val = info.ConstPtrInfo(ConstPtr(self.nodeaddr)) - constclass_info = NotVirtualStateInfo(self.cpu, 'r', constclass_val) - constclass2_info = NotVirtualStateInfo(self.cpu, 'r', + constclass_info = not_virtual(self.cpu, 'r', constclass_val) + constclass2_info = not_virtual(self.cpu, 'r', info.ConstPtrInfo(ConstPtr(self.node2addr))) - constantnull_info = NotVirtualStateInfo(self.cpu, 'r', + constantnull_info = not_virtual(self.cpu, 'r', info.ConstPtrInfo(ConstPtr(self.nullptr))) # unknown unknown @@ -260,9 +260,10 @@ self.check_no_guards(unknown_info, knownclass_info) # unknown constant - self.check_no_guards(unknown_info, constant_info, + unknown_info_int = not_virtual(self.cpu, 'i', None) + self.check_no_guards(unknown_info_int, constant_info, ConstInt(1), ConstIntBound(1)) - self.check_no_guards(unknown_info, constant_info) + self.check_no_guards(unknown_info_int, constant_info) # nonnull unknown @@ -293,11 +294,11 @@ const_nonnull = ConstPtr(self.nodeaddr) const_nonnull2 = ConstPtr(self.node2addr) const_null = ConstPtr(lltype.nullptr(llmemory.GCREF.TO)) - self.check_no_guards(nonnull_info, constant_info, const_nonnull, + self.check_no_guards(nonnull_info, constant_ptr_info, const_nonnull, info.ConstPtrInfo(const_nonnull)) self.check_invalid(nonnull_info, constantnull_info, const_null, info.ConstPtrInfo(const_null)) - self.check_no_guards(nonnull_info, constant_info) + self.check_no_guards(nonnull_info, constant_ptr_info) self.check_invalid(nonnull_info, constantnull_info) @@ -392,8 +393,8 @@ value1 = IntUnbounded() value1.make_ge(IntBound(0, 10)) value1.make_le(IntBound(20, 30)) - info1 = NotVirtualStateInfo(self.cpu, 'i', value1) - info2 = NotVirtualStateInfo(self.cpu, 'i', IntUnbounded()) + info1 = not_virtual(self.cpu, 'i', value1) + info2 = not_virtual(self.cpu, 'i', IntUnbounded()) expected = """ [i0] i1 = int_ge(i0, 0) @@ -408,18 +409,18 @@ value1 = IntUnbounded() value1.make_ge(IntBound(0, 10)) value1.make_le(IntBound(20, 30)) - info1 = NotVirtualStateInfo(self.cpu, 'i', value1) - info2 = NotVirtualStateInfo(self.cpu, 'i', ConstIntBound(10000)) + info1 = not_virtual(self.cpu, 'i', value1) + info2 = not_virtual(self.cpu, 'i', ConstIntBound(10000)) self.check_invalid(info1, info2) - info1 = NotVirtualStateInfo(self.cpu, 'i', value1) - info2 = NotVirtualStateInfo(self.cpu, 'i', ConstIntBound(11)) + info1 = not_virtual(self.cpu, 'i', value1) + info2 = not_virtual(self.cpu, 'i', ConstIntBound(11)) self.check_no_guards(info1, info2) def test_known_class(self): classbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) value1 = info.InstancePtrInfo(None, classbox) - info1 = NotVirtualStateInfo(self.cpu, 'r', value1) - info2 = NotVirtualStateInfo(self.cpu, 'r', None) + info1 = not_virtual(self.cpu, 'r', value1) + info2 = not_virtual(self.cpu, 'r', None) expected = """ [p0] guard_nonnull_class(p0, ConstClass(node_vtable)) [] @@ -456,18 +457,18 @@ def test_equal_inputargs(self): classbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) value = info.InstancePtrInfo(None, classbox) - knownclass_info = NotVirtualStateInfo(self.cpu, 'r', value) + knownclass_info = not_virtual(self.cpu, 'r', value) vstate1 = VirtualState([knownclass_info, knownclass_info]) assert vstate1.generalization_of(vstate1, FakeOptimizer(self.cpu)) - unknown_info1 = NotVirtualStateInfo(self.cpu, 'r', None) + unknown_info1 = not_virtual(self.cpu, 'r', None) vstate2 = VirtualState([unknown_info1, unknown_info1]) assert vstate2.generalization_of(vstate2, FakeOptimizer(self.cpu)) assert not vstate1.generalization_of(vstate2, FakeOptimizer(self.cpu)) assert vstate2.generalization_of(vstate1, FakeOptimizer(self.cpu)) - unknown_info1 = NotVirtualStateInfo(self.cpu, 'r', None) - unknown_info2 = NotVirtualStateInfo(self.cpu, 'r', None) + unknown_info1 = not_virtual(self.cpu, 'r', None) + unknown_info2 = not_virtual(self.cpu, 'r', None) vstate3 = VirtualState([unknown_info1, unknown_info2]) assert vstate3.generalization_of(vstate2, FakeOptimizer(self.cpu)) assert vstate3.generalization_of(vstate1, FakeOptimizer(self.cpu)) @@ -494,9 +495,9 @@ def test_generate_guards_on_virtual_fields_matches_array(self): classbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) innervalue1 = info.InstancePtrInfo(None, classbox) - innerinfo1 = NotVirtualStateInfo(self.cpu, 'r', innervalue1) + innerinfo1 = not_virtual(self.cpu, 'r', innervalue1) innerinfo1.position = 1 - innerinfo2 = NotVirtualStateInfo(self.cpu, 'r', None) + innerinfo2 = not_virtual(self.cpu, 'r', None) innerinfo2.position = 1 descr = ArrayDescr(lltype.GcArray(llmemory.GCREF), self.cpu) @@ -524,9 +525,9 @@ def test_generate_guards_on_virtual_fields_matches_instance(self): classbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) innervalue1 = info.InstancePtrInfo(None, classbox) - innerinfo1 = NotVirtualStateInfo(self.cpu, 'r', innervalue1) + innerinfo1 = not_virtual(self.cpu, 'r', innervalue1) innerinfo1.position = 1 - innerinfo2 = NotVirtualStateInfo(self.cpu, 'r', None) + innerinfo2 = not_virtual(self.cpu, 'r', None) innerinfo2.position = 1 info1 = VirtualStateInfo(ConstInt(42), [self.nextdescr]) @@ -552,9 +553,9 @@ def test_generate_guards_on_virtual_fields_matches_struct(self): constclassbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) innervalue1 = info.InstancePtrInfo(None, constclassbox) - innerinfo1 = NotVirtualStateInfo(self.cpu, 'r', innervalue1) + innerinfo1 = not_virtual(self.cpu, 'r', innervalue1) innerinfo1.position = 1 - innerinfo2 = NotVirtualStateInfo(self.cpu, 'r', None) + innerinfo2 = not_virtual(self.cpu, 'r', None) innerinfo2.position = 1 structdescr = self.nodesize @@ -583,9 +584,9 @@ def test_generate_guards_on_virtual_fields_matches_arraystruct(self): constclassbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) innervalue1 = info.InstancePtrInfo(None, constclassbox) - innerinfo1 = NotVirtualStateInfo(self.cpu, 'r', innervalue1) + innerinfo1 = not_virtual(self.cpu, 'r', innervalue1) innerinfo1.position = 1 - innerinfo2 = NotVirtualStateInfo(self.cpu, 'r', None) + innerinfo2 = not_virtual(self.cpu, 'r', None) innerinfo2.position = 1 NODE = lltype.Struct('NODE', ('x', llmemory.GCREF)) @@ -627,7 +628,7 @@ assert vstate1.generalization_of(vstate1, FakeOptimizer(self.cpu)) info2 = VirtualStateInfo(ConstInt(42), [1, 2]) - unknown_info1 = NotVirtualStateInfo(self.cpu, 'r', + unknown_info1 = not_virtual(self.cpu, 'r', info.InstancePtrInfo()) info2.fieldstate = [unknown_info1, unknown_info1] vstate2 = VirtualState([info2]) @@ -636,9 +637,9 @@ assert vstate2.generalization_of(vstate1, FakeOptimizer(self.cpu)) info3 = VirtualStateInfo(ConstInt(42), [1, 2]) - unknown_info1 = NotVirtualStateInfo(self.cpu, 'r', + unknown_info1 = not_virtual(self.cpu, 'r', info.InstancePtrInfo()) - unknown_info2 = NotVirtualStateInfo(self.cpu, 'r', + unknown_info2 = not_virtual(self.cpu, 'r', info.InstancePtrInfo()) info3.fieldstate = [unknown_info1, unknown_info2] vstate3 = VirtualState([info3]) @@ -651,7 +652,7 @@ info1 = VirtualStateInfo(ConstInt(42), [1, 2]) classbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) value = info.InstancePtrInfo(None, classbox) - knownclass_info = NotVirtualStateInfo(self.cpu, 'r', value) + knownclass_info = not_virtual(self.cpu, 'r', value) info1.fieldstate = [knownclass_info, knownclass_info] vstate1 = VirtualState([info1]) assert vstate1.generalization_of(vstate1, FakeOptimizer(self.cpu)) @@ -659,7 +660,7 @@ info2 = VirtualStateInfo(ConstInt(42), [1, 2]) classbox = self.cpu.ts.cls_of_box(InputArgRef(self.node2addr)) value = info.InstancePtrInfo(None, classbox) - knownclass_info = NotVirtualStateInfo(self.cpu, 'r', value) + knownclass_info = not_virtual(self.cpu, 'r', value) info2.fieldstate = [knownclass_info, knownclass_info] vstate2 = VirtualState([info2]) assert vstate2.generalization_of(vstate2, FakeOptimizer(self.cpu)) @@ -671,7 +672,7 @@ info1 = VirtualStateInfo(ConstInt(42), [10, 20]) classbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) value = info.InstancePtrInfo(None, classbox) - knownclass_info = NotVirtualStateInfo(self.cpu, 'r', value) + knownclass_info = not_virtual(self.cpu, 'r', value) info1.fieldstate = [knownclass_info, knownclass_info] vstate1 = VirtualState([info1]) assert vstate1.generalization_of(vstate1, FakeOptimizer(self.cpu)) @@ -679,7 +680,7 @@ info2 = VirtualStateInfo(ConstInt(42), [1, 2]) classbox = self.cpu.ts.cls_of_box(InputArgRef(self.node2addr)) value = info.InstancePtrInfo(None, classbox) - knownclass_info = NotVirtualStateInfo(self.cpu, 'r', value) + knownclass_info = not_virtual(self.cpu, 'r', value) info2.fieldstate = [knownclass_info, knownclass_info] vstate2 = VirtualState([info2]) assert vstate2.generalization_of(vstate2, FakeOptimizer(self.cpu)) @@ -691,7 +692,7 @@ info1 = VirtualStateInfo(ConstInt(42), [1, 2]) classbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) value = info.InstancePtrInfo(None, classbox) - knownclass_info = NotVirtualStateInfo(self.cpu, 'r', value) + knownclass_info = not_virtual(self.cpu, 'r', value) info1.fieldstate = [knownclass_info, knownclass_info] vstate1 = VirtualState([info1]) assert vstate1.generalization_of(vstate1, FakeOptimizer(self.cpu)) @@ -699,24 +700,24 @@ info2 = VirtualStateInfo(ConstInt(7), [1, 2]) classbox = self.cpu.ts.cls_of_box(InputArgRef(self.node2addr)) value = info.InstancePtrInfo(None, classbox) - knownclass_info = NotVirtualStateInfo(self.cpu, 'r', value) + knownclass_info = not_virtual(self.cpu, 'r', value) info2.fieldstate = [knownclass_info, knownclass_info] vstate2 = VirtualState([info2]) assert vstate2.generalization_of(vstate2, FakeOptimizer(self.cpu)) assert not vstate2.generalization_of(vstate1, FakeOptimizer(self.cpu)) assert not vstate1.generalization_of(vstate2, FakeOptimizer(self.cpu)) - + def test_nonvirtual_is_not_virtual(self): info1 = VirtualStateInfo(ConstInt(42), [1, 2]) classbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) value = info.InstancePtrInfo(None, classbox) - knownclass_info = NotVirtualStateInfo(self.cpu, 'r', value) + knownclass_info = not_virtual(self.cpu, 'r', value) info1.fieldstate = [knownclass_info, knownclass_info] vstate1 = VirtualState([info1]) assert vstate1.generalization_of(vstate1, FakeOptimizer(self.cpu)) - info2 = NotVirtualStateInfo(self.cpu, 'r', value) + info2 = not_virtual(self.cpu, 'r', value) vstate2 = VirtualState([info2]) assert vstate2.generalization_of(vstate2, FakeOptimizer(self.cpu)) @@ -727,7 +728,7 @@ info1 = VArrayStateInfo(42) classbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) value = info.InstancePtrInfo(None, classbox) - knownclass_info = NotVirtualStateInfo(self.cpu, 'r', value) + knownclass_info = not_virtual(self.cpu, 'r', value) info1.fieldstate = [knownclass_info, knownclass_info] vstate1 = VirtualState([info1]) assert vstate1.generalization_of(vstate1, FakeOptimizer(self.cpu)) @@ -735,7 +736,7 @@ info2 = VArrayStateInfo(42) classbox = self.cpu.ts.cls_of_box(InputArgRef(self.node2addr)) value = info.InstancePtrInfo(None, classbox) - knownclass_info = NotVirtualStateInfo(self.cpu, 'r', value) + knownclass_info = not_virtual(self.cpu, 'r', value) info2.fieldstate = [knownclass_info, knownclass_info] vstate2 = VirtualState([info2]) assert vstate2.generalization_of(vstate2, FakeOptimizer(self.cpu)) @@ -747,7 +748,7 @@ info1 = VArrayStateInfo(42) classbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) value = info.InstancePtrInfo(None, classbox) - knownclass_info = NotVirtualStateInfo(self.cpu, 'r', value) + knownclass_info = not_virtual(self.cpu, 'r', value) info1.fieldstate = [knownclass_info, knownclass_info] vstate1 = VirtualState([info1]) assert vstate1.generalization_of(vstate1, FakeOptimizer(self.cpu)) @@ -755,7 +756,7 @@ info2 = VArrayStateInfo(42) classbox = self.cpu.ts.cls_of_box(InputArgRef(self.node2addr)) value = info.InstancePtrInfo(None, classbox) - knownclass_info = NotVirtualStateInfo(self.cpu, 'r', value) + knownclass_info = not_virtual(self.cpu, 'r', value) info2.fieldstate = [knownclass_info] vstate2 = VirtualState([info2]) assert vstate2.generalization_of(vstate2, FakeOptimizer(self.cpu)) @@ -793,7 +794,7 @@ def test_crash_varay_clear(self): classbox = self.cpu.ts.cls_of_box(InputArgRef(self.nodeaddr)) innervalue1 = info.InstancePtrInfo(None, classbox) - innerinfo1 = NotVirtualStateInfo(self.cpu, 'r', innervalue1) + innerinfo1 = not_virtual(self.cpu, 'r', innervalue1) innerinfo1.position = 1 innerinfo1.position_in_notvirtuals = 0 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 @@ -350,40 +350,23 @@ def debug_header(self, indent): debug_print(indent + 'VArrayStructStateInfo(%d):' % self.position) + +def not_virtual(cpu, type, info): + if type == 'i': + return NotVirtualStateInfoInt(cpu, type, info) + if type == 'r': + return NotVirtualStateInfoPtr(cpu, type, info) + return NotVirtualStateInfo(cpu, type, info) + + class NotVirtualStateInfo(AbstractVirtualStateInfo): - lenbound = None - intbound = None level = LEVEL_UNKNOWN constbox = None - known_class = None - + def __init__(self, cpu, type, info): if info and info.is_constant(): self.level = LEVEL_CONSTANT self.constbox = info.getconst() - if type == 'r': - self.known_class = info.get_known_class(cpu) - elif type == 'i': - self.intbound = info - elif type == 'r': - if info: - self.known_class = info.get_known_class(cpu) - if self.known_class: - self.level = LEVEL_KNOWNCLASS - elif info.is_nonnull(): - self.level = LEVEL_NONNULL - self.lenbound = info.getlenbound(None) - elif type == 'i': - if isinstance(info, IntBound): - if info.lower < MININT / 2: - info.lower = MININT - if info.upper > MAXINT / 2: - info.upper = MAXINT - self.intbound = info - elif type == 'f': - if info and info.is_constant(): - self.level = LEVEL_CONSTANT - self.constbox = info.getconst() def is_const(self): return self.constbox is not None @@ -394,84 +377,15 @@ def _generate_guards(self, other, box, runtime_box, state): # XXX This will always retrace instead of forcing anything which # might be what we want sometimes? - if not isinstance(other, NotVirtualStateInfo): - raise VirtualStatesCantMatch( - 'The VirtualStates does not match as a ' + - 'virtual appears where a pointer is needed ' + - 'and it is too late to force it.') - - extra_guards = state.extra_guards - cpu = state.cpu - if self.lenbound: - if other.lenbound is None: - other_bound = IntLowerBound(0) - else: - other_bound = other.lenbound - if not self.lenbound.contains_bound(other_bound): - raise VirtualStatesCantMatch("length bound does not match") - if self.level == LEVEL_UNKNOWN: - # confusingly enough, this is done also for pointers - # which have the full range as the "bound", so it always works - return self._generate_guards_intbounds(other, box, runtime_box, - extra_guards, - state.optimizer) - - # the following conditions often peek into the runtime value that the - # box had when tracing. This value is only used as an educated guess. - # It is used here to choose between either emitting a guard and jumping - # to an existing compiled loop or retracing the loop. Both alternatives - # will always generate correct behaviour, but performance will differ. - elif self.level == LEVEL_NONNULL: - if other.level == LEVEL_UNKNOWN: - if runtime_box is not None and runtime_box.nonnull(): - op = ResOperation(rop.GUARD_NONNULL, [box]) - extra_guards.append(op) - return - else: - raise VirtualStatesCantMatch("other not known to be nonnull") - elif other.level == LEVEL_NONNULL: - return - elif other.level == LEVEL_KNOWNCLASS: - return # implies nonnull - else: - assert other.level == LEVEL_CONSTANT - assert other.constbox - if not other.constbox.nonnull(): - raise VirtualStatesCantMatch("constant is null") - return - - elif self.level == LEVEL_KNOWNCLASS: - if other.level == LEVEL_UNKNOWN: - if (runtime_box and runtime_box.nonnull() and - self.known_class.same_constant(cpu.ts.cls_of_box(runtime_box))): - op = ResOperation(rop.GUARD_NONNULL_CLASS, [box, self.known_class]) - extra_guards.append(op) - return - else: - raise VirtualStatesCantMatch("other's class is unknown") - elif other.level == LEVEL_NONNULL: - if (runtime_box and self.known_class.same_constant( - cpu.ts.cls_of_box(runtime_box))): - op = ResOperation(rop.GUARD_CLASS, [box, self.known_class]) - extra_guards.append(op) - return - else: - raise VirtualStatesCantMatch("other's class is unknown") - elif other.level == LEVEL_KNOWNCLASS: - if self.known_class.same_constant(other.known_class): - return - raise VirtualStatesCantMatch("classes don't match") - else: - assert other.level == LEVEL_CONSTANT - if (other.constbox.nonnull() and - self.known_class.same_constant(cpu.ts.cls_of_box(other.constbox))): - return - else: - raise VirtualStatesCantMatch("classes don't match") - + return self._generate_guards_unkown(other, box, runtime_box, + extra_guards, + state) else: + if not isinstance(other, NotVirtualStateInfo): + raise VirtualStatesCantMatch( + 'comparing a constant against something that is a virtual') assert self.level == LEVEL_CONSTANT if other.level == LEVEL_CONSTANT: if self.constbox.same_constant(other.constbox): @@ -485,19 +399,9 @@ raise VirtualStatesCantMatch("other not constant") assert 0, "unreachable" - def _generate_guards_intbounds(self, other, box, runtime_box, extra_guards, - optimizer): - if self.intbound is None: - return - if self.intbound.contains_bound(other.intbound): - return - if (runtime_box is not None and - self.intbound.contains(runtime_box.getint())): - # this may generate a few more guards than needed, but they are - # optimized away when emitting them - self.intbound.make_guards(box, extra_guards, optimizer) - return - raise VirtualStatesCantMatch("intbounds don't match") + def _generate_guards_unkown(self, other, box, runtime_box, extra_guards, + state): + return def enum_forced_boxes(self, boxes, box, optimizer, force_boxes=False): if self.level == LEVEL_CONSTANT: @@ -553,8 +457,145 @@ if self.lenbound: lb = ', ' + self.lenbound.bound.__repr__() - debug_print(indent + mark + 'NotVirtualInfo(%d' % self.position + - ', ' + l + ', ' + self.intbound.__repr__() + lb + ')') + result = indent + mark + 'NotVirtualStateInfo(%d' % self.position + ', ' + l + extra = self._extra_repr() + if extra: + result += ', ' + extra + result += lb + ')' + debug_print(result) + +class NotVirtualStateInfoInt(NotVirtualStateInfo): + intbound = None + + def __init__(self, cpu, type, info): + NotVirtualStateInfo.__init__(self, cpu, type, info) + assert type == 'i' + if isinstance(info, IntBound): + if info.lower < MININT / 2: + info.lower = MININT + if info.upper > MAXINT / 2: + info.upper = MAXINT + self.intbound = info + + def _generate_guards_unkown(self, other, box, runtime_box, extra_guards, + state): + other_intbound = None + if isinstance(other, NotVirtualStateInfoInt): + other_intbound = other.intbound + if self.intbound is None: + return + if self.intbound.contains_bound(other_intbound): + return + if (runtime_box is not None and + self.intbound.contains(runtime_box.getint())): + # this may generate a few more guards than needed, but they are + # optimized away when emitting them + self.intbound.make_guards(box, extra_guards, state.optimizer) + return + raise VirtualStatesCantMatch("intbounds don't match") + + def _extra_repr(self): + return self.intbound.__repr__() + + +class NotVirtualStateInfoPtr(NotVirtualStateInfo): + lenbound = None + known_class = None + + def __init__(self, cpu, type, info): + if info: + self.known_class = info.get_known_class(cpu) + if self.known_class: + self.level = LEVEL_KNOWNCLASS + elif info.is_nonnull(): + self.level = LEVEL_NONNULL + self.lenbound = info.getlenbound(None) + # might set it to LEVEL_CONSTANT + NotVirtualStateInfo.__init__(self, cpu, type, info) + + def _generate_guards(self, other, box, runtime_box, state): + if not isinstance(other, NotVirtualStateInfoPtr): + raise VirtualStatesCantMatch( + 'The VirtualStates does not match as a ' + + 'virtual appears where a pointer is needed ' + + 'and it is too late to force it.') + extra_guards = state.extra_guards + if self.lenbound: + if other.lenbound is None: + other_bound = IntLowerBound(0) + else: + other_bound = other.lenbound + if not self.lenbound.contains_bound(other_bound): + raise VirtualStatesCantMatch("length bound does not match") + if self.level == LEVEL_NONNULL: + return self._generate_guards_nonnull(other, box, runtime_box, + extra_guards, + state) + elif self.level == LEVEL_KNOWNCLASS: + return self._generate_guards_knownclass(other, box, runtime_box, + extra_guards, + state) + return NotVirtualStateInfo._generate_guards(self, other, box, + runtime_box, state) + + + # the following methods often peek into the runtime value that the + # box had when tracing. This value is only used as an educated guess. + # It is used here to choose between either emitting a guard and jumping + # to an existing compiled loop or retracing the loop. Both alternatives + # will always generate correct behaviour, but performance will differ. + + def _generate_guards_nonnull(self, other, box, runtime_box, extra_guards, + state): + if not isinstance(other, NotVirtualStateInfoPtr): + raise VirtualStatesCantMatch('trying to match ptr with non-ptr??!') + if other.level == LEVEL_UNKNOWN: + if runtime_box is not None and runtime_box.nonnull(): + op = ResOperation(rop.GUARD_NONNULL, [box]) + extra_guards.append(op) + return + else: + raise VirtualStatesCantMatch("other not known to be nonnull") + elif other.level == LEVEL_NONNULL: + pass + elif other.level == LEVEL_KNOWNCLASS: + pass # implies nonnull + else: + assert other.level == LEVEL_CONSTANT + assert other.constbox + if not other.constbox.nonnull(): + raise VirtualStatesCantMatch("constant is null") + + def _generate_guards_knownclass(self, other, box, runtime_box, extra_guards, + state): + cpu = state.cpu + if not isinstance(other, NotVirtualStateInfoPtr): + raise VirtualStatesCantMatch('trying to match ptr with non-ptr??!') + if other.level == LEVEL_UNKNOWN: + if (runtime_box and runtime_box.nonnull() and + self.known_class.same_constant(cpu.ts.cls_of_box(runtime_box))): + op = ResOperation(rop.GUARD_NONNULL_CLASS, [box, self.known_class]) + extra_guards.append(op) + else: + raise VirtualStatesCantMatch("other's class is unknown") + elif other.level == LEVEL_NONNULL: + if (runtime_box and self.known_class.same_constant( + cpu.ts.cls_of_box(runtime_box))): + op = ResOperation(rop.GUARD_CLASS, [box, self.known_class]) + extra_guards.append(op) + else: + raise VirtualStatesCantMatch("other's class is unknown") + elif other.level == LEVEL_KNOWNCLASS: + if self.known_class.same_constant(other.known_class): + return + raise VirtualStatesCantMatch("classes don't match") + else: + assert other.level == LEVEL_CONSTANT + if (other.constbox.nonnull() and + self.known_class.same_constant(cpu.ts.cls_of_box(other.constbox))): + pass + else: + raise VirtualStatesCantMatch("classes don't match") class VirtualState(object): @@ -678,8 +719,8 @@ return VirtualState(state) def visit_not_virtual(self, box): - return NotVirtualStateInfo(self.optimizer.cpu, box.type, - self.optimizer.getinfo(box)) + return not_virtual(self.optimizer.cpu, box.type, + self.optimizer.getinfo(box)) def visit_virtual(self, descr, fielddescrs): known_class = ConstInt(descr.get_vtable()) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit