diff options
author | Matti Picus <matti.picus@gmail.com> | 2023-09-28 19:19:50 +0300 |
---|---|---|
committer | Matti Picus <matti.picus@gmail.com> | 2023-09-28 19:19:50 +0300 |
commit | a4fde3b30a193840c660e33a2ae690e8b59fefd2 (patch) | |
tree | da99ee881bd8f60391f6372575e75c7c07840731 | |
parent | update version for release (diff) | |
parent | more release note tweaks (diff) | |
download | pypy-a4fde3b30a193840c660e33a2ae690e8b59fefd2.tar.gz pypy-a4fde3b30a193840c660e33a2ae690e8b59fefd2.tar.bz2 pypy-a4fde3b30a193840c660e33a2ae690e8b59fefd2.zip |
merge default, update versionsrelease-pypy2.7-v7.3.13
67 files changed, 1520 insertions, 590 deletions
@@ -166,3 +166,6 @@ feeb267ead3e6771d3f2f49b83e1894839f64fb7 release-pypy3.9-v7.3.11 4ac174a992a398e4f42bc48852e2fb218a403baa release-pypy2.7-v7.3.12rc2 a6c2a04c0d03054c5a9fbc1eca282046a8c5fd70 release-pypy3.9-v7.3.12rc2 07561e2940ea7838339a8d615f9f314a0e83bbc0 release-pypy3.10-v7.3.12rc2 +8d509266596ab6a70defcf87c8b29f57b8e32426 release-pypy2.7-v7.3.12 +3f3f2298ddc56db44bbdb4551ce992d8e9401646 release-pypy3.9-v7.3.12 +af44d0b8114cb82c40a07bb9ee9c1ca8a1b3688c release-pypy3.10-v7.3.12 diff --git a/lib_pypy/pypy_tools/build_cffi_imports.py b/lib_pypy/pypy_tools/build_cffi_imports.py index 4bee2ec645..9c28608457 100644 --- a/lib_pypy/pypy_tools/build_cffi_imports.py +++ b/lib_pypy/pypy_tools/build_cffi_imports.py @@ -53,15 +53,15 @@ configure_args = ['./configure', # without an _ssl module, but the OpenSSL download site redirect HTTP # to HTTPS cffi_dependencies = { - '_ssl1': ('http://artfiles.org/openssl.org/source/openssl-1.1.1u.tar.gz', - 'e2f8d84b523eecd06c7be7626830370300fbcc15386bf5142d72758f6963ebc6', + '_ssl1': ('http://artfiles.org/openssl.org/source/openssl-1.1.1w.tar.gz', + 'cf3098950cb4d853ad95c0841f1f9c6d3dc102dccfcacd521d93925208b76ac8', [ ['./config', '--prefix=/usr', 'no-shared'], ['make', '-s', '-j', str(multiprocessing.cpu_count())], ['make', 'install', 'DESTDIR={}/'.format(deps_destdir)], ]), - '_ssl3': ('http://artfiles.org/openssl.org/source/openssl-3.0.9.tar.gz', - 'eb1ab04781474360f77c318ab89d8c5a03abc38e63d65a603cabbf1b00a1dc90', + '_ssl3': ('http://artfiles.org/openssl.org/source/openssl-3.0.10.tar.gz', + '1761d4f5b13a1028b9b6f3d4b8e17feb0cedc9370f6afe61d7193d2cdce83323', [ ['./config', '--prefix=/usr', 'no-shared', 'enable-fips'], ['make', '-s', '-j', str(multiprocessing.cpu_count())], diff --git a/pypy/doc/architecture.rst b/pypy/doc/architecture.rst index c820db2413..f22e497191 100644 --- a/pypy/doc/architecture.rst +++ b/pypy/doc/architecture.rst @@ -135,5 +135,5 @@ GC written as more RPython code. The best one we have so far is in ``rpython/memory/gc/incminimark.py``. .. _`Getting Started with RPython`: https://rpython.readthedocs.io/en/latest/getting-started.html -.. _RPython By Example: https://mesapy.org/rpython-by-example/ +.. _RPython By Example: https://mssun.github.io/rpython-by-example/index.html diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst index 805814258a..aad4dfc532 100644 --- a/pypy/doc/cpython_differences.rst +++ b/pypy/doc/cpython_differences.rst @@ -454,7 +454,8 @@ Miscellaneous support (see ``multiline_input()``). On the other hand, ``parse_and_bind()`` calls are ignored (issue `#2072`_). -* ``sys.getsizeof()`` always raises ``TypeError``. This is because a +* ``sys.getsizeof()`` always raises ``TypeError`` (and objects do not have a + ``__sizeof__`` method). This is because a memory profiler using this function is most likely to give results inconsistent with reality on PyPy. It would be possible to have ``sys.getsizeof()`` return a number (with enough work), but that may @@ -473,7 +474,7 @@ Miscellaneous items' size, that operation will by itself create one million integer objects that never existed in the first place. Note that some of these concerns also exist on CPython, just less so. For this reason - we explicitly don't implement ``sys.getsizeof()``. + we explicitly don't implement ``sys.getsizeof()`` (nor ``__sizeof__``). * The ``timeit`` module behaves differently under PyPy: it prints the average time and the standard deviation, instead of the minimum, since the minimum is diff --git a/pypy/doc/faq.rst b/pypy/doc/faq.rst index b12304b484..a0059b6505 100644 --- a/pypy/doc/faq.rst +++ b/pypy/doc/faq.rst @@ -42,8 +42,8 @@ Module xyz does not work with PyPy: ImportError ----------------------------------------------- A module installed for CPython is not automatically available for PyPy ---- just like a module installed for CPython 2.6 is not automatically -available for CPython 2.7 if you installed both. In other words, you +--- just like a module installed for CPython 3.6 is not automatically +available for CPython 3.7 if you installed both. In other words, you need to install the module xyz specifically for PyPy. On Linux, this means that you cannot use ``apt-get`` or some similar @@ -141,19 +141,17 @@ works on Mac and Windows: it is tested there, but most of us are running Linux so fixes may depend on 3rd-party contributions. To bootstrap from sources, PyPy can use either CPython 2.7 or -another (e.g. older) PyPy. Cross-translation is not really supported: +PyPy 2.7. Cross-translation is not really supported: e.g. to build a 32-bit PyPy, you need to have a 32-bit environment. -Which Python version (2.x?) does PyPy implement? ------------------------------------------------- - -PyPy comes in two versions: - -* one is fully compatible with Python 2.7; +Which Python versions does PyPy implement? +------------------------------------------ -* the other is fully compatible with one 3.x version. At the time of - this writing, this is 3.7. +PyPy will always support 2.7 since RPython is written for it. In addition, PyPy +supports various Python3 versions, see the `release notes`_ for the latest +releases. Typically, we will support one or two versions of Python3. +.. _ `release notes`: index-of-release-notes.html .. _threading: @@ -228,7 +226,6 @@ progressing fast, we have discontinued support for ``numpypy``. .. _`started to reimplement`: https://www.pypy.org/posts/2011/05/numpy-in-pypy-status-and-roadmap-8332894230779779992.html .. _fork: https://github.com/pypy/numpypy -.. _`PyPy binary wheels`: https://github.com/antocuni/pypy-wheels .. _HPy: https://hpyproject.org/ Is PyPy more clever than CPython about Tail Calls? diff --git a/pypy/doc/index-of-release-notes.rst b/pypy/doc/index-of-release-notes.rst index 25d9ce1b92..bdd45798fd 100644 --- a/pypy/doc/index-of-release-notes.rst +++ b/pypy/doc/index-of-release-notes.rst @@ -6,6 +6,7 @@ Combined releases .. toctree:: + release-v7.3.13.rst release-v7.3.12.rst release-v7.3.11.rst release-v7.3.10.rst diff --git a/pypy/doc/release-v7.3.12.rst b/pypy/doc/release-v7.3.12.rst index b97f4cc711..bdf6d1b17f 100644 --- a/pypy/doc/release-v7.3.12.rst +++ b/pypy/doc/release-v7.3.12.rst @@ -5,12 +5,6 @@ PyPy v7.3.12: release of python 2.7, 3.9, and 3.10 .. Changelog up to commit ba2e9a6c433e -.. note:: - This is a pre-release announcement. When the release actually happens, it - will be announced on the `PyPy blog`_ - -.. _`PyPy blog`: https://pypy.org/blog - The PyPy team is proud to release version 7.3.12 of PyPy. This release includes a new string-to-int algorithm (also appearing in CPython 3.12) that is faster than the older one; support for symlinks in Windows; and diff --git a/pypy/doc/release-v7.3.13.rst b/pypy/doc/release-v7.3.13.rst new file mode 100644 index 0000000000..56216df8ea --- /dev/null +++ b/pypy/doc/release-v7.3.13.rst @@ -0,0 +1,187 @@ +================================================== +PyPy v7.3.13: release of python 2.7, 3.9, and 3.10 +================================================== + +.. note:: + This is a pre-release announcement. When the release actually happens, it + will be announced on the PyPy blog_ + + +The PyPy team is proud to release version 7.3.13 of PyPy. +This is primarily a security/bug-fix release. CPython released security +patches, and this release also improves the ability to use type +specfifications via ``PyType_FromSpec`` and friends. There are also some +small speed-ups. + +The release includes three different interpreters: + + - PyPy2.7, which is an interpreter supporting the syntax and the features of + Python 2.7 including the stdlib for CPython 2.7.18+ (the ``+`` is for + backported security updates) + + - PyPy3.9, which is an interpreter supporting the syntax and the features of + Python 3.9, including the stdlib for CPython 3.9.18. + + - PyPy3.10, which is an interpreter supporting the syntax and the features of + Python 3.10, including the stdlib for CPython 3.10.13. Note it requires at + least cython 0.29.35 or cython 3.0.0b3. + +The interpreters are based on much the same codebase, thus the multiple +release. This is a micro release, all APIs are compatible with the other 7.3 +releases. It follows after 7.3.12 release on June 16, 2023. + +We recommend updating. You can find links to download the v7.3.13 releases here: + + https://pypy.org/download.html + +We would like to thank our donors for the continued support of the PyPy +project. If PyPy is not quite good enough for your needs, we are available for +`direct consulting`_ work. If PyPy is helping you out, we would love to hear about +it and encourage submissions to our blog_ via a pull request +to https://github.com/pypy/pypy.org + +We would also like to thank our contributors and encourage new people to join +the project. PyPy has many layers and we need help with all of them: bug fixes, +`PyPy`_ and `RPython`_ documentation improvements, or general `help`_ with making +RPython's JIT even better. + +If you are a python library maintainer and use C-extensions, please consider +making a HPy_ / CFFI_ / cppyy_ version of your library that would be performant +on PyPy. In any case, both `cibuildwheel`_ and the `multibuild system`_ support +building wheels for PyPy. + +.. _`PyPy`: index.html +.. _`RPython`: https://rpython.readthedocs.org +.. _`help`: project-ideas.html +.. _CFFI: https://cffi.readthedocs.io +.. _cppyy: https://cppyy.readthedocs.io +.. _`multibuild system`: https://github.com/matthew-brett/multibuild +.. _`cibuildwheel`: https://github.com/joerick/cibuildwheel +.. _blog: https://pypy.org/blog +.. _HPy: https://hpyproject.org/ +.. _was sponsored: https://www.pypy.org/posts/2022/07/m1-support-for-pypy.html +.. _direct consulting: https://www.pypy.org/pypy-sponsors.html +.. _has built: https://www.pypy.org/posts/2022/11/pypy-and-conda-forge.html + +What is PyPy? +============= + +PyPy is a Python interpreter, a drop-in replacement for CPython +It's fast (`PyPy and CPython 3.7.4`_ 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. + +We provide binary builds for: + + * **x86** machines on most common operating systems + (Linux 32/64 bits, Mac OS 64 bits, Windows 64 bits) + + * 64-bit **ARM** machines running Linux (``aarch64``). + + * Apple **M1 arm64** machines (``macos_arm64``). + + * **s390x** running Linux + +PyPy support Windows 32-bit, Linux PPC64 big- and little-endian, and Linux ARM +32 bit, but does not release binaries. Please reach out to us if you wish to +sponsor binary releases for those platforms. Downstream packagers provide +binary builds for debian, Fedora, conda, OpenBSD, FreeBSD, Gentoo, and more. + +.. _`PyPy and CPython 3.7.4`: https://speed.pypy.org +.. _`dynamic languages`: https://rpython.readthedocs.io/en/latest/examples.html + +Changelog +========= + +For all versions +---------------- +- Update to ssl 1.1.1w, or 3.0.10 when embedding libraries for a portable build + +Bugfixes +~~~~~~~~ +- Report exceptions that are created when forcing an oefmt as unraisable (issue + _3978) + +Speedups and enhancements +~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Shrink the size of the PyPy binaries by about ~700KiB by auto-generating + fewer versions of the RPython dict and list types. +- Speed up tracing and code generation in the JIT slightly by reducing the size + in memory of one of its central data structures, the ``MIFrame``. This is + done by not allocating three lists of length 256 for the registers in the + MIFrames and the blackhole frames. Almost all jitcodes have much smaller + frame sizes. +- Improve ``str.strip()`` to make it better optimizable by the JIT. +- Make access to ``sys.flags`` faster by making sure the JIT can constant-fold + the access most of the time. (especially important on py3.x, where every + ``bytes.decode`` call checks ``sys.flags.utf8_mode``) +- Speed up the flowspace a lot for huge functions. This makes building the PyPy + binary a little bit faster. +- Make ``UnicodeIO`` store its data in a list of ``r_int32``, as opposed to + using the rpython (Python2.7) unicode type. we want to get rid of the unicode + type and also it requires an extra copy all the time. +- Make every Python integer that doesn't fit into a machine word use one word + less memory by storing the sign differently. + + +Python 3.9+ +----------- + +- Create c-extension modules used in tests as part of the build (in + package.py), not as part of testing. + +Bugfixes +~~~~~~~~ +- More selectively clear ``StopIteration`` exceptions on ``tp_iternext`` (issue + 3956_) +- Copy less when creating a venv by using a ``PYPY_PORTABLE_DEPS.txt`` file to + state which dlls to copy in a portable build (issue 3611_) +- On macos ``sendfile`` can return an error while sending part of the file + (issue 3964_) +- Fixes on both app-level and C level for ``Py_TPFLAGS_BASETYPE=0`` (issue + 2742_). Also set ``PyType_Type.tp_itemsize`` to ``sizeof(PyMemberDef)`` like + on CPython +- Fix ``PyType_FromSpecWithBases`` to correctly use ``Py_tp_doc``, + ``Py_tp_members`` in spec, fix ``__module__`` assignment, better handle + ``__name__`` and ``tp_name`` +- Hide ``functools`` wrappers from the stack when reporting warnings (issue + 3988_) +- Fix edge case of datetime isoformat parsing (issue 3989_) +- Accept NULL ``tp_doc`` (bpo-41832_) +- Align ``nb_int`` with ``PyNumber_Long`` (to get consistent error messages) +- Handle ``pathlib.Path`` objects in ``_ssl`` (issue 4002_) +- Implement ``_PyLong_AsInt`` which is not part of the stable API but used in + testing + +Speedups and enhancements +~~~~~~~~~~~~~~~~~~~~~~~~~ +- Avoid compiling a new regex where not needed (in ``email``, ``csv``, and + elsewhere) (issue 3961_) + +Python 3.10 +----------- + +Bugfixes +~~~~~~~~ +- Fix, test locking in HMAC update (issue 3962_) +- When re-assigning to ``type.__bases__``, rebuild the cpyext type struct + (issue 3976_) +- Add missing slot macro ``Py_am_send`` (issue 3990_) + +.. _bpo-41832: https://bugs.python.org/issue41832 +.. _2742: https://foss.heptapod.net/pypy/pypy/-/issues/2742 +.. _3611: https://foss.heptapod.net/pypy/pypy/-/issues/3611 +.. _3956: https://foss.heptapod.net/pypy/pypy/-/issues/3956 +.. _3961: https://foss.heptapod.net/pypy/pypy/-/issues/3961 +.. _3962: https://foss.heptapod.net/pypy/pypy/-/issues/3962 +.. _3964: https://foss.heptapod.net/pypy/pypy/-/issues/3964 +.. _3976: https://foss.heptapod.net/pypy/pypy/-/issues/3976 +.. _3978: https://foss.heptapod.net/pypy/pypy/-/issues/3978 +.. _3988: https://foss.heptapod.net/pypy/pypy/-/issues/3988 +.. _3989: https://foss.heptapod.net/pypy/pypy/-/issues/3989 +.. _3990: https://foss.heptapod.net/pypy/pypy/-/issues/3990 +.. _4002: https://foss.heptapod.net/pypy/pypy/-/issues/4002 + diff --git a/pypy/interpreter/app_main.py b/pypy/interpreter/app_main.py index 824131e9ed..da045412b1 100755 --- a/pypy/interpreter/app_main.py +++ b/pypy/interpreter/app_main.py @@ -572,7 +572,11 @@ def parse_command_line(argv): if WE_ARE_TRANSLATED: flags = [options[flag] for flag in sys_flags] - sys.flags = type(sys.flags)(flags) + oldflags = sys.flags + # hack: delete the flags first to make sure they don't turn into an + # cell in the celldict + del sys.flags + sys.flags = type(oldflags)(flags) sys.py3kwarning = bool(sys.flags.py3k_warning) sys.dont_write_bytecode = bool(sys.flags.dont_write_bytecode) diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py index 84ada70d6f..2c0df2e981 100644 --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -283,6 +283,7 @@ class OperationError(Exception): w_value = self._w_value if w_value is None: value = self._compute_value(space) + self._w_value = w_value = space.newtext(value) return w_value @@ -379,7 +380,11 @@ def get_operrcls2(valuefmt): lst[i + i] = self.xstrings[i] value = getattr(self, attr) if fmt == 'R': - result = space.text_w(space.repr(value)) + try: + result = space.text_w(space.repr(value)) + except OperationError as e: + e.write_unraisable(space, "exception formatting: ", value) + result = '<repr raised>' elif fmt == 'T': result = space.type(value).name elif fmt == 'N': diff --git a/pypy/interpreter/test/apptest_raise.py b/pypy/interpreter/test/apptest_raise.py index 30e02eb78f..de48ee4209 100644 --- a/pypy/interpreter/test/apptest_raise.py +++ b/pypy/interpreter/test/apptest_raise.py @@ -283,3 +283,12 @@ def test_with_exit_True(): return 42 assert False, "unreachable" assert g() == 42 + +def test_unraisable_error_in_repr(): + class ReprError(): + def __repr__(self): + raise RuntimeError("Oh no!") + try: + [].index(ReprError()) + except ValueError as e: + assert str(e) == '<repr raised> is not in list' diff --git a/pypy/module/__builtin__/functional.py b/pypy/module/__builtin__/functional.py index c480af3cad..db64752307 100644 --- a/pypy/module/__builtin__/functional.py +++ b/pypy/module/__builtin__/functional.py @@ -111,7 +111,7 @@ def range_with_longs(space, w_start, w_stop, w_step): if not step.tobool(): raise oefmt(space.w_ValueError, "step argument must not be zero") - elif step.sign < 0: + elif step.get_sign() < 0: lo, hi, st = hi, lo, st.neg() if lo.lt(hi): diff --git a/pypy/module/_cffi_backend/cdlopen.py b/pypy/module/_cffi_backend/cdlopen.py index e204c0fc21..61d69fe9ff 100644 --- a/pypy/module/_cffi_backend/cdlopen.py +++ b/pypy/module/_cffi_backend/cdlopen.py @@ -157,7 +157,7 @@ def ffiobj_init(ffi, module_name, version, types, w_globals, ll_set_cdl_realize_global_int(nglobs[i]) bigint = space.bigint_w(w_integer) ullvalue = bigint.ulonglongmask() - rffi.setintfield(nintconsts[i], 'neg', int(bigint.sign <= 0)) + rffi.setintfield(nintconsts[i], 'neg', int(bigint.get_sign() <= 0)) rffi.setintfield(nintconsts[i], 'value', ullvalue) ffi.ctxobj.ctx.c_globals = nglobs rffi.setintfield(ffi.ctxobj.ctx, 'c_num_globals', n) diff --git a/pypy/module/_collections/interp_deque.py b/pypy/module/_collections/interp_deque.py index f2580e7d66..6b4c738106 100644 --- a/pypy/module/_collections/interp_deque.py +++ b/pypy/module/_collections/interp_deque.py @@ -53,9 +53,34 @@ class Lock(object): pass -def get_printable_location(tp): - return "deque._find [%s]" % (tp.getname(tp.space), ) -find_jmp = jit.JitDriver(greens=['tp'], reds='auto', name='deque._find', get_printable_location=get_printable_location) +def get_printable_location_find_or_count(is_find, tp): + if is_find: + name = "deque._find" + else: + name = "deque.count" + return "%s [%s]" % (name, tp.getname(tp.space), ) + +find_jmp = jit.JitDriver( + greens=['is_find', 'tp'], + reds='auto', + name='deque._find_or_count', + get_printable_location=get_printable_location_find_or_count) + + +def get_printable_location_extend(is_extend_right, greenkey): + if is_extend_right: + name = "" + else: + name = "left" + return "deque.extend%s [%s]" % (name, greenkey.iterator_greenkey_printable()) + +extend_jmp = jit.JitDriver( + name="deque.extend", + reds="auto", + greens=["is_extend_right", "greenkey"], + get_printable_location=get_printable_location_extend +) + # ------------------------------------------------------------ @@ -148,39 +173,35 @@ class W_Deque(W_Root): def count(self, w_x): "Return number of occurrences of value." - space = self.space - result = 0 - block = self.leftblock - index = self.leftindex - lock = self.getlock() - for i in range(self.len): - w_item = block.data[index] - if space.eq_w(w_item, w_x): - result += 1 - self.checklock(lock) - # Advance the block/index pair - index += 1 - if index >= BLOCKLEN: - block = block.rightlink - index = 0 - return space.newint(result) + result = self._find_or_count(w_x, is_find=False) + return self.space.newint(result) def extend(self, w_iterable): "Extend the right side of the deque with elements from the iterable" + self._extend(w_iterable, is_extend_right=True) + + def _extend(self, w_iterable, is_extend_right): # Handle case where id(deque) == id(iterable) space = self.space if space.is_w(self, w_iterable): w_iterable = space.call_function(space.w_list, w_iterable) # w_iter = space.iter(w_iterable) + greenkey = space.iterator_greenkey(w_iter) while True: + extend_jmp.jit_merge_point( + is_extend_right=is_extend_right, + greenkey=greenkey) try: w_obj = space.next(w_iter) except OperationError as e: if e.match(space, space.w_StopIteration): break raise - self.append(w_obj) + if is_extend_right: + self.append(w_obj) + else: + self.appendleft(w_obj) def iadd(self, w_iterable): self.extend(w_iterable) @@ -188,21 +209,7 @@ class W_Deque(W_Root): def extendleft(self, w_iterable): "Extend the left side of the deque with elements from the iterable" - # Handle case where id(deque) == id(iterable) - space = self.space - if space.is_w(self, w_iterable): - w_iterable = space.call_function(space.w_list, w_iterable) - # - space = self.space - w_iter = space.iter(w_iterable) - while True: - try: - w_obj = space.next(w_iter) - except OperationError as e: - if e.match(space, space.w_StopIteration): - break - raise - self.appendleft(w_obj) + return self._extend(w_iterable, is_extend_right=False) def pop(self): "Remove and return the rightmost element." @@ -250,29 +257,35 @@ class W_Deque(W_Root): self.modified() return w_obj - def _find(self, w_x): + def _find_or_count(self, w_x, is_find=True): space = self.space block = self.leftblock index = self.leftindex lock = self.getlock() tp = space.type(w_x) + result = 0 for i in range(self.len): - find_jmp.jit_merge_point(tp=tp) + find_jmp.jit_merge_point(tp=tp, is_find=is_find) w_item = block.data[index] equal = space.eq_w(w_item, w_x) self.checklock(lock) - if equal: - return i + if is_find: + if equal: + return i + else: + result += equal # Advance the block/index pair index += 1 if index >= BLOCKLEN: block = block.rightlink index = 0 - return -1 + if is_find: + return -1 + return result def remove(self, w_x): "Remove first occurrence of value." - i = self._find(w_x) + i = self._find_or_count(w_x) if i < 0: raise oefmt(self.space.w_ValueError, "deque.remove(x): x not in deque") diff --git a/pypy/module/_io/interp_stringio.py b/pypy/module/_io/interp_stringio.py index 6da86d7896..4a65c90fab 100644 --- a/pypy/module/_io/interp_stringio.py +++ b/pypy/module/_io/interp_stringio.py @@ -12,6 +12,7 @@ from pypy.module._io.interp_textio import ( W_TextIOBase, W_IncrementalNewlineDecoder) from pypy.module._io.interp_iobase import convert_size from pypy.objspace.std.unicodeobject import W_UnicodeObject +from rpython.rlib import rarithmetic def _find_end(start, size, total): @@ -24,21 +25,33 @@ def _find_end(start, size, total): return end class UnicodeIO(object): - def __init__(self, data=None): + def __init__(self, data=None, length=0): if data is None: data = '' - self.data = [] - self.write(data, 0) + self.data = [] # list of r_int32 + self.write(data, length, 0) + + def getdata(self, i): + return rarithmetic.widen(self.data[i]) + + def setdata(self, i, val): + self.data[i] = rarithmetic.r_int32(val) + + def getdata_slice(self, start, end): + builder = Utf8StringBuilder(end - start) + for index in range(start, end): + builder.append_code(self.getdata(index)) + return builder def resize(self, newlength): if len(self.data) > newlength: self.data = self.data[:newlength] if len(self.data) < newlength: - self.data.extend([u'\0'] * (newlength - len(self.data))) + self.data.extend([0] * (newlength - len(self.data))) def read(self, start, size): end = _find_end(start, size, len(self.data)) - return u''.join(self.data[start:end]) + return self.getdata_slice(start, end) def _convert_limit(self, limit, start): if limit < 0 or limit > len(self.data) - start: @@ -52,32 +65,33 @@ class UnicodeIO(object): limit = self._convert_limit(limit, start) end = start + limit pos = start + builder = Utf8StringBuilder() while pos < end: - ch = self.data[pos] + ch = self.getdata(pos) + builder.append_code(ch) pos += 1 - if ch == u'\n': + if ch == ord('\n'): break - if ch == u'\r': + if ch == ord('\r'): if pos >= end: break - if self.data[pos] == u'\n': + if self.getdata(pos) == ord('\n'): + builder.append_code(ch) pos += 1 break else: break - result = u''.join(self.data[start:pos]) - return result + return builder def readline(self, marker, start, limit): limit = self._convert_limit(limit, start) end = start + limit found = False - marker = marker.decode('utf-8') for pos in range(start, end - len(marker) + 1): - ch = self.data[pos] - if ch == marker[0]: + ch = self.getdata(pos) + if ch == ord(marker[0]): for j in range(1, len(marker)): - if self.data[pos + j] != marker[j]: + if self.getdata(pos + j) != ord(marker[j]): break # from inner loop else: pos += len(marker) @@ -85,24 +99,23 @@ class UnicodeIO(object): break if not found: pos = end - result = u''.join(self.data[start:pos]) - return result + return self.getdata_slice(start, pos) - def write(self, string, start): - ustr = string.decode('utf-8') - newlen = start + len(ustr) + def write(self, string, length, start): + newlen = start + length if newlen > len(self.data): self.resize(newlen) - for i in range(len(ustr)): - self.data[start + i] = ustr[i] - return len(ustr) + for ch in Utf8StringIterator(string): + self.setdata(start, ch) + start += 1 + return length def truncate(self, size): if size < len(self.data): self.resize(size) def getvalue(self): - return u''.join(self.data).encode('utf-8') + return self.getdata_slice(0, len(self.data)) READING, ACCUMULATING, RWBUFFER, CLOSED = range(4) @@ -247,7 +260,7 @@ class W_StringIO(W_TextIOBase): def write_w(self, space, w_obj): w_decoded = self._decode_string(space, w_obj) - string = space.utf8_w(w_decoded) + string, length = space.utf8_len_w(w_decoded) orig_size = space.len_w(w_obj) if not string: return space.newint(orig_size) @@ -260,21 +273,22 @@ class W_StringIO(W_TextIOBase): self.state = ACCUMULATING else: # switch to RWBUFFER - self.buf = UnicodeIO(space.utf8_w(self.w_value)) + s, l = space.utf8_len_w(self.w_value) + self.buf = UnicodeIO(s, l) self.w_value = None self.state = RWBUFFER elif self.state == ACCUMULATING and self.pos != self.get_length(): s = self.builder.build() - self.buf = UnicodeIO(s) + self.buf = UnicodeIO(s, self.builder.getlength()) self.builder = None self.state = RWBUFFER if self.state == ACCUMULATING: - written = w_decoded._len() - self.builder.append_utf8(string, written) + written = length + self.builder.append_utf8(string, length) else: assert self.state == RWBUFFER - written = self.buf.write(string, self.pos) + written = self.buf.write(string, length, self.pos) self.pos += written return space.newint(orig_size) @@ -298,13 +312,16 @@ class W_StringIO(W_TextIOBase): end = _find_end(self.pos, size, length) if self.pos > end: return space.newutf8('', 0) + if self.pos == 0 and end == length: + self.pos = end + return self.w_value w_res = self.w_value._unicode_sliced(space, self.pos, end) self.pos = end return w_res assert self.state == RWBUFFER - result_u = self.buf.read(self.pos, size) - self.pos += len(result_u) - return space.newutf8(result_u.encode('utf-8'), len(result_u)) + result_builder = self.buf.read(self.pos, size) + self.pos += result_builder.getlength() + return W_UnicodeObject.from_utf8builder(result_builder) def readline_w(self, space, w_limit=None): self._check_closed(space) @@ -324,14 +341,14 @@ class W_StringIO(W_TextIOBase): for ch in it: if self.pos >= end: break - if ch == ord(u'\n'): + if ch == ord('\n'): self.pos += 1 break - elif ch == ord(u'\r'): + elif ch == ord('\r'): self.pos += 1 if self.pos >= end: break - if it.next() == ord(u'\n'): + if it.next() == ord('\n'): self.pos += 1 break else: @@ -369,16 +386,16 @@ class W_StringIO(W_TextIOBase): return w_res if self.readuniversal: - result_u = self.buf.readline_universal(self.pos, limit) + result_builder = self.buf.readline_universal(self.pos, limit) else: if self.readtranslate: # Newlines are already translated, only search for \n newline = '\n' else: newline = self.readnl - result_u = self.buf.readline(newline, self.pos, limit) - self.pos += len(result_u) - return space.newutf8(result_u.encode('utf-8'), len(result_u)) + result_builder = self.buf.readline(newline, self.pos, limit) + self.pos += result_builder.getlength() + return W_UnicodeObject.from_utf8builder(result_builder) @unwrap_spec(pos=int, mode=int) def seek_w(self, space, pos, mode=0): @@ -424,9 +441,8 @@ class W_StringIO(W_TextIOBase): self._realize(space) if self.state == READING: return self.w_value - v = self.buf.getvalue() - lgt = codepoints_in_utf8(v) - return space.newutf8(v, lgt) + builder = self.buf.getvalue() + return W_UnicodeObject.from_utf8builder(builder) def readable_w(self, space): self._check_closed(space) diff --git a/pypy/module/_io/test/apptest_stringio.py b/pypy/module/_io/test/apptest_stringio.py index 5aceb28b4d..c44a51d6da 100644 --- a/pypy/module/_io/test/apptest_stringio.py +++ b/pypy/module/_io/test/apptest_stringio.py @@ -317,3 +317,36 @@ def test_roundtrip_state(): assert sio2.foo == 42 assert sio2.tell() == 2 +def rwbuffer(u, **kwargs): + sio = StringIO(u, **kwargs) + sio.seek(0) + sio.write(u) + sio.seek(0) + return sio + +def test_rwbuffer_read(): + s = u'12345678' + sio = StringIO(s) + sio.seek(0) + sio.write(u'aaa') + sio.seek(0) + assert sio.read() == u'aaa45678' + +def test_rwbuffer_readline(): + sio = rwbuffer(u'123\n456') + assert sio.readline(0) == '' + assert sio.readline(2) == '12' + assert sio.readline(None) == '3\n' + assert sio.readline() == '456' + +def test_rwbuffer_readline(): + sio = rwbuffer(u'123\n456') + assert sio.readline(0) == '' + assert sio.readline(2) == '12' + assert sio.readline(None) == '3\n' + assert sio.readline() == '456' + +def test_rwbuffer_newline_none(): + sio = rwbuffer(u"a\nb\r\nc\rd", newline=None) + res = list(sio) + assert res == [u"a\n", u"b\n", u"c\n", u"d"] diff --git a/pypy/module/cpyext/include/patchlevel.h b/pypy/module/cpyext/include/patchlevel.h index e9ab86d202..50c66765b3 100644 --- a/pypy/module/cpyext/include/patchlevel.h +++ b/pypy/module/cpyext/include/patchlevel.h @@ -32,8 +32,8 @@ * module/sys/version.py * doc/conf.py */ -#define PYPY_VERSION "7.3.12" -#define PYPY_VERSION_NUM 0x07030c00 +#define PYPY_VERSION "7.3.13" +#define PYPY_VERSION_NUM 0x07030d00 /* Defined to mean a PyPy where cpyext holds more regular references to PyObjects, e.g. staying alive as long as the internal PyPy object stays alive. */ diff --git a/pypy/module/cpyext/longobject.py b/pypy/module/cpyext/longobject.py index e9c60761af..6e14fc0dce 100644 --- a/pypy/module/cpyext/longobject.py +++ b/pypy/module/cpyext/longobject.py @@ -1,3 +1,4 @@ + from rpython.rtyper.lltypesystem import lltype, rffi from rpython.rlib.rbigint import rbigint, InvalidSignednessError from pypy.module.cpyext.api import ( @@ -230,7 +231,7 @@ def _PyLong_NumBits(space, w_long): @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL) def _PyLong_Sign(space, w_long): assert isinstance(w_long, W_LongObject) - return w_long.num.sign + return w_long.num.get_sign() CONST_UCHARP = lltype.Ptr(lltype.Array(rffi.UCHAR, hints={'nolength': True, 'render_as_const': True})) diff --git a/pypy/module/pypyjit/test_pypy_c/test_misc.py b/pypy/module/pypyjit/test_pypy_c/test_misc.py index 41fc823074..c795f58805 100644 --- a/pypy/module/pypyjit/test_pypy_c/test_misc.py +++ b/pypy/module/pypyjit/test_pypy_c/test_misc.py @@ -449,8 +449,7 @@ class TestMisc(BaseTestPyPyC): log = self.run(main, [3000]) loop, = log.loops_by_filename(self.filepath) assert loop.match(""" - i79 = int_lt(i76, 0) - guard_false(i79, descr=...) + ... i80 = int_ge(i76, i33) guard_false(i80, descr=...) i82 = int_add(i76, 1) @@ -482,3 +481,17 @@ class TestMisc(BaseTestPyPyC): loop, = log.loops_by_filename(self.filepath) opnames = log.opnames(loop.allops()) assert "new" not in opnames + + def test_sys_flags_access(self): + def main(n): + import sys + x = 0 + for i in range(n): + import sys + flags = sys.flags # ID: flags + x += flags.debug + log = self.run(main, [3000]) + loop, = log.loops_by_id("flags", is_entry_bridge=True) + ops = loop.ops_by_id("flags") + assert ops == [] # used to be a getfield_gc_r on an ObjectMutableCell + diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py index 3041fe59ee..42cc7f5c6e 100644 --- a/pypy/module/pypyjit/test_pypy_c/test_string.py +++ b/pypy/module/pypyjit/test_pypy_c/test_string.py @@ -33,7 +33,7 @@ class TestString(BaseTestPyPyC): i24 = int_ge(i83, i12) guard_false(i24, descr=...) i93 = int_add(i83, 1) - i94 = int_gt(i93, i56) + i94 = int_gt(i93, i12) guard_false(i94, descr=...) p96 = newstr(1) strsetitem(p96, 0, i89) @@ -83,10 +83,7 @@ class TestString(BaseTestPyPyC): strsetitem(p25, 0, i23) p93 = call_r(ConstClass(fromstr), p25, 16, 0, descr=<Callr . rii EF=4>) guard_no_exception(descr=...) - i95 = getfield_gc_i(p93, descr=<FieldS rpython.rlib.rbigint.rbigint.inst_size .*>) - i96 = int_gt(i95, #) - guard_false(i96, descr=...) - i94 = call_i(ConstClass(rbigint._toint_helper), p93, descr=<Calli . r EF=4>) + i94 = call_i(ConstClass(rbigint.toint), p93, descr=<Calli . r EF=4>) guard_no_exception(descr=...) i95 = int_add_ovf(i6, i94) guard_no_overflow(descr=...) @@ -362,3 +359,68 @@ class TestString(BaseTestPyPyC): i2 = strgetitem(p1, i1) ''') + def test_strip_doesnt_escape_bytes(self): + log = self.run(""" + def main(n): + l = [unicode(x).decode("ascii") for x in range(10000)] + res = 0 + for data in l: + res += len(data.strip(b'1')) # ID: striparg + for data in l: + res += len(data.strip()) # ID: stripnone + return res + """, [10000]) + _, loop1, loop2 = log.loops_by_filename(self.filepath) + opnames = log.opnames(loop1.ops_by_id('striparg')) + assert "new_with_vtable" not in opnames + assert "call_may_force_r" not in opnames + assert opnames.count("call_i") == 2 # _strip_bytes_unboxed_left/right + opnames = log.opnames(loop2.ops_by_id('stripnone')) + assert "new_with_vtable" not in opnames + assert "call_may_force_r" not in opnames + assert opnames.count("call_i") == 2 # _strip_bytes_unboxed_left/right + + def test_unicode_strip_doesnt_escape_uniobject(self): + log = self.run(""" + def main(n): + uni = b'\xc3\xa4'.decode("utf-8") + l = [unicode(x) + uni + unicode(x) for x in range(10000)] + res = 0 + for data in l: + res += len(data.strip(u'1')) # ID: stripnone + for data in l: + res += len(data.strip()) # ID: striparg + return res + """, [10000]) + _, loop1, loop2 = log.loops_by_filename(self.filepath) + opnames = log.opnames(loop1.ops_by_id('stripnone')) + assert "new_with_vtable" not in opnames + assert "call_may_force_r" not in opnames + assert "call_r" in opnames # _strip_unboxed + opnames = log.opnames(loop2.ops_by_id('striparg')) + assert "new_with_vtable" not in opnames + assert "call_may_force_r" not in opnames + assert "call_r" in opnames # _strip_none_unboxed + + def test_unicode_strip_doesnt_escape_uniobject_ascii(self): + log = self.run(""" + def main(n): + l = [unicode(x) for x in range(10000)] + res = 0 + for data in l: + res += len(data.strip(u'1')) # ID: stripnone + for data in l: + res += len(data.strip()) # ID: striparg + return res + """, [10000]) + _, loop1, loop2 = log.loops_by_filename(self.filepath) + opnames = log.opnames(loop1.ops_by_id('stripnone')) + assert "new_with_vtable" not in opnames + assert "call_may_force_r" not in opnames + assert "call_r" not in opnames + assert opnames.count("call_i") == 2 # _strip_ascii_unboxed_left/right + opnames = log.opnames(loop2.ops_by_id('striparg')) + assert "new_with_vtable" not in opnames + assert "call_may_force_r" not in opnames + assert "call_r" not in opnames + assert opnames.count("call_i") == 2 # _strip_none_ascii_unboxed_left/right diff --git a/pypy/module/sys/version.py b/pypy/module/sys/version.py index b3e32c308c..f2df181590 100644 --- a/pypy/module/sys/version.py +++ b/pypy/module/sys/version.py @@ -13,7 +13,7 @@ CPYTHON_API_VERSION = 1013 #XXX # sync with include/modsupport.h # make sure to keep PYPY_VERSION in sync with: # module/cpyext/include/patchlevel.h # doc/conf.py -PYPY_VERSION = (7, 3, 12, "final", 0) +PYPY_VERSION = (7, 3, 13, "final", 0) import pypy diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py index 672878f688..a518780c42 100644 --- a/pypy/objspace/std/longobject.py +++ b/pypy/objspace/std/longobject.py @@ -322,7 +322,7 @@ class W_LongObject(W_AbstractLongObject): return space.w_NotImplemented else: exp_bigint = w_exponent.asbigint() - sign = exp_bigint.sign + sign = exp_bigint.get_sign() if space.is_none(w_modulus): if sign < 0: @@ -460,7 +460,7 @@ class W_LongObject(W_AbstractLongObject): return descr_binop, descr_rbinop def _lshift(self, space, w_other): - if w_other.asbigint().sign < 0: + if w_other.asbigint().get_sign() < 0: raise oefmt(space.w_ValueError, "negative shift count") try: shift = w_other.asbigint().toint() @@ -476,7 +476,7 @@ class W_LongObject(W_AbstractLongObject): descr_lshift, descr_rlshift = _make_descr_binop(_lshift, _int_lshift) def _rshift(self, space, w_other): - if w_other.asbigint().sign < 0: + if w_other.asbigint().get_sign() < 0: raise oefmt(space.w_ValueError, "negative shift count") try: shift = w_other.asbigint().toint() diff --git a/pypy/objspace/std/marshal_impl.py b/pypy/objspace/std/marshal_impl.py index 038cbf3e94..2528ba9712 100644 --- a/pypy/objspace/std/marshal_impl.py +++ b/pypy/objspace/std/marshal_impl.py @@ -161,7 +161,7 @@ def marshal_long(space, w_long, m): SHIFT = 15 MASK = (1 << SHIFT) - 1 num = w_long.asbigint() - sign = num.sign + sign = num.get_sign() num = num.abs() total_length = (num.bit_length() + (SHIFT - 1)) / SHIFT m.put_int(total_length * sign) diff --git a/pypy/objspace/std/stringmethods.py b/pypy/objspace/std/stringmethods.py index 422c276699..2569e8ecf5 100644 --- a/pypy/objspace/std/stringmethods.py +++ b/pypy/objspace/std/stringmethods.py @@ -665,16 +665,33 @@ class StringMethods(object): rpos = len(value) if left: - while lpos < rpos and value[lpos] in chars: - lpos += 1 + lpos = StringMethods._strip_bytes_unboxed_left(value, chars) if right: - while rpos > lpos and value[rpos - 1] in chars: - rpos -= 1 + rpos = StringMethods._strip_bytes_unboxed_right(value, chars, lpos) assert rpos >= lpos # annotator hint, don't remove return self._sliced(space, value, lpos, rpos, self) + @staticmethod + @jit.elidable + @specialize.argtype(0) + def _strip_bytes_unboxed_left(value, chars): + lpos = 0 + rpos = len(value) + while lpos < rpos and value[lpos] in chars: + lpos += 1 + return lpos + + @staticmethod + @jit.elidable + @specialize.argtype(0) + def _strip_bytes_unboxed_right(value, chars, lpos): + rpos = len(value) + while rpos > lpos and value[rpos - 1] in chars: + rpos -= 1 + return rpos + def _strip_none(self, space, left, right): "internal function called by str_xstrip methods" value = self._val(space) @@ -683,16 +700,33 @@ class StringMethods(object): rpos = len(value) if left: - while lpos < rpos and self._isspace(value[lpos]): - lpos += 1 + lpos = StringMethods._strip_none_bytes_unboxed_left(value) if right: - while rpos > lpos and self._isspace(value[rpos - 1]): - rpos -= 1 + rpos = StringMethods._strip_none_bytes_unboxed_right(value, lpos) assert rpos >= lpos # annotator hint, don't remove return self._sliced(space, value, lpos, rpos, self) + @staticmethod + @jit.elidable + @specialize.argtype(0) + def _strip_none_bytes_unboxed_left(value): + lpos = 0 + rpos = len(value) + while lpos < rpos and value[lpos].isspace(): + lpos += 1 + return lpos + + @staticmethod + @jit.elidable + @specialize.argtype(0) + def _strip_none_bytes_unboxed_right(value, lpos): + rpos = len(value) + while rpos > lpos and value[rpos - 1].isspace(): + rpos -= 1 + return rpos + def descr_strip(self, space, w_chars=None): if space.is_none(w_chars): return self._strip_none(space, left=1, right=1) diff --git a/pypy/objspace/std/test/test_bytesobject.py b/pypy/objspace/std/test/test_bytesobject.py index b8eaebdcd1..acf97d7acc 100644 --- a/pypy/objspace/std/test/test_bytesobject.py +++ b/pypy/objspace/std/test/test_bytesobject.py @@ -884,9 +884,9 @@ class AppTestBytesObject: assert x.rindex(y) == 0 assert x.split(y) == ['', ''] assert x.rsplit(y) == ['', ''] - assert x.strip(y) == '' - assert x.rstrip(y) == '' - assert x.lstrip(y) == '' + assert x.strip(y) == u'' + assert x.rstrip(y) == u'' + assert x.lstrip(y) == u'' def test_replace_overflow(self): import sys diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py index 4b63252b6b..48794934b6 100644 --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -579,7 +579,7 @@ class AppTestUnicodeString: assert s.rstrip() == u" a b" assert s.lstrip() == u"a b " assert u'xyzzyhelloxyzzy'.strip(u'xyz') == u'hello' - assert u'xyzzyhelloxyzzy'.lstrip('xyz') == u'helloxyzzy' + assert u'xyzzyhelloxyzzy'.lstrip(u'xyz') == u'helloxyzzy' assert u'xyzzyhelloxyzzy'.rstrip(u'xyz') == u'xyzzyhello' exc = raises(TypeError, s.strip, buffer(' ')) assert str(exc.value) == 'strip arg must be None, unicode or str' @@ -588,6 +588,15 @@ class AppTestUnicodeString: exc = raises(TypeError, s.lstrip, buffer(' ')) assert str(exc.value) == 'lstrip arg must be None, unicode or str' + def test_strip_nonascii(self): + s = u" ä b " + assert s.strip() == u"ä b" + assert s.rstrip() == u" ä b" + assert s.lstrip() == u"ä b " + assert u'xyzzyh³lloxyzzy'.strip(u'xyzü') == u'h³llo' + assert u'xyzzyh³lloxyzzy'.lstrip(u'xyzü') == u'h³lloxyzzy' + assert u'xyzzyh³lloxyzzy'.rstrip(u'xyzü') == u'xyzzyh³llo' + def test_strip_str_unicode(self): x = "--abc--".strip(u"-") assert (x, type(x)) == (u"abc", unicode) @@ -599,6 +608,10 @@ class AppTestUnicodeString: raises(UnicodeDecodeError, "\x80".lstrip, u"") raises(UnicodeDecodeError, "\x80".rstrip, u"") + def test_rstrip_bug(self): + assert u"aaaaaaaaaaaaaaaaaaa".rstrip(u"a") == u"" + assert u"äääääääääääääääääääääääää".rstrip(u"ä") == u"" + def test_long_from_unicode(self): assert long(u'12345678901234567890') == 12345678901234567890 assert int(u'12345678901234567890') == 12345678901234567890 diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py index b4776e541b..e0dce9fdba 100644 --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -48,7 +48,7 @@ def codepoint_at_pos_dont_look_inside(utf8, p): class W_UnicodeObject(W_Root): import_from_mixin(StringMethods) - _immutable_fields_ = ['_utf8'] + _immutable_fields_ = ['_utf8', '_length'] @enforceargs(utf8str=str) def __init__(self, utf8str, length): @@ -1107,10 +1107,24 @@ class W_UnicodeObject(W_Root): def _strip_none(self, space, left, right): "internal function called by str_xstrip methods" value = self._utf8 + lgt = self._len() + if self.is_ascii(): + # in the ascii case we can do even better and do the allocation in + # the trace + lpos = 0 + rpos = len(value) + if left: + lpos = StringMethods._strip_none_bytes_unboxed_left(value) + if right: + rpos = StringMethods._strip_none_bytes_unboxed_right(value, lpos) + return self._utf8_sliced(lpos, rpos, rpos - lpos) + return self._strip_none_unboxed(value, lgt, left, right) + @staticmethod + @jit.elidable + def _strip_none_unboxed(value, lgt, left, right): lpos = 0 rpos = len(value) - lgt = self._len() if left: while lpos < rpos and rutf8.isspace(value, lpos): @@ -1126,17 +1140,34 @@ class W_UnicodeObject(W_Root): lgt -= 1 assert rpos >= lpos # annotator hint, don't remove - return self._utf8_sliced(lpos, rpos, lgt) + assert lpos >= 0 + assert rpos >= 0 + return W_UnicodeObject(value[lpos:rpos], lgt) def _strip(self, space, w_chars, left, right, name='strip'): "internal function called by str_xstrip methods" value = self._utf8 chars = self.convert_arg_to_w_unicode(space, w_chars, strict=name)._utf8 - lpos = 0 - rpos = len(value) lgt = self._len() + if self.is_ascii(): + # in the ascii case we can do even better and do the allocation in + # the trace + lpos = 0 + rpos = len(value) + if left: + lpos = StringMethods._strip_bytes_unboxed_left(value, chars) + if right: + rpos = StringMethods._strip_bytes_unboxed_right(value, chars, lpos) + return self._utf8_sliced(lpos, rpos, rpos - lpos) + return self._strip_unboxed(value, lgt, chars, left, right) + + @staticmethod + @jit.elidable + def _strip_unboxed(value, lgt, chars, left, right): + lpos = 0 + rpos = len(value) if left: while lpos < rpos and rutf8.utf8_in_chars(value, lpos, chars): lpos = rutf8.next_codepoint_pos(value, lpos) @@ -1151,7 +1182,9 @@ class W_UnicodeObject(W_Root): lgt -= 1 assert rpos >= lpos # annotator hint, don't remove - return self._utf8_sliced(lpos, rpos, lgt) + assert lpos >= 0 + assert rpos >= 0 + return W_UnicodeObject(value[lpos:rpos], lgt) def descr_getnewargs(self, space): return space.newtuple([W_UnicodeObject(self._utf8, self._length)]) diff --git a/pypy/tool/release/check_versions.py b/pypy/tool/release/check_versions.py index 233c75bc63..8c025d7273 100644 --- a/pypy/tool/release/check_versions.py +++ b/pypy/tool/release/check_versions.py @@ -34,6 +34,9 @@ def assert_in(a, b): pypy_versions = { + '7.3.12': {'python_version': ['3.10.12', '3.9.17', '2.7.18'], + 'date': '2023-06-16', + }, '7.3.12rc2': {'python_version': ['3.10.11', '3.9.16', '2.7.18'], 'date': '2023-05-28', }, @@ -159,6 +162,7 @@ def check_versions(data, url, verbose=0, check_times=True, nightly_only=False): assert_equal(latest_pypys[d['python_version']], d['pypy_version']) else: try: + # Make sure there is only one latest version assert_different(latest_pypys[d['python_version']], d['pypy_version']) except KeyError: assert 'rc' in d['pypy_version'] diff --git a/pypy/tool/release/repackage.sh b/pypy/tool/release/repackage.sh index 1fc0161e6f..b8579ea35d 100644..100755 --- a/pypy/tool/release/repackage.sh +++ b/pypy/tool/release/repackage.sh @@ -2,11 +2,11 @@ # Edit these appropriately before running this script pmaj=3 # python main version: 2 or 3 -pmin=9 # python minor version +pmin=10 # python minor version maj=7 min=3 rev=12 -rc=rc2 # comment this line for actual release +#rc=rc2 # comment this line for actual release function maybe_exit { if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then diff --git a/pypy/tool/release/versions.json b/pypy/tool/release/versions.json index d05edd083a..25cf3d4a58 100644 --- a/pypy/tool/release/versions.json +++ b/pypy/tool/release/versions.json @@ -1,154 +1,154 @@ [ { - "pypy_version": "7.3.12rc2", - "python_version": "3.10.11", - "stable": false, - "latest_pypy": false, - "date": "2023-05-28", + "pypy_version": "7.3.12", + "python_version": "3.10.12", + "stable": true, + "latest_pypy": true, + "date": "2023-06-16", "files": [ { - "filename": "pypy3.10-v7.3.12rc2-aarch64.tar.bz2", + "filename": "pypy3.10-v7.3.12-aarch64.tar.bz2", "arch": "aarch64", "platform": "linux", - "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12rc2-aarch64.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12-aarch64.tar.bz2" }, { - "filename": "pypy3.10-v7.3.12rc2-linux32.tar.bz2", + "filename": "pypy3.10-v7.3.12-linux32.tar.bz2", "arch": "i686", "platform": "linux", - "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12rc2-linux32.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12-linux32.tar.bz2" }, { - "filename": "pypy3.10-v7.3.12rc2-linux64.tar.bz2", + "filename": "pypy3.10-v7.3.12-linux64.tar.bz2", "arch": "x64", "platform": "linux", - "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12rc2-linux64.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12-linux64.tar.bz2" }, { - "filename": "pypy3.10-v7.3.12rc2-macos_x86_64.tar.bz2", + "filename": "pypy3.10-v7.3.12-macos_x86_64.tar.bz2", "arch": "x64", "platform": "darwin", - "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12rc2-macos_x86_64.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12-macos_x86_64.tar.bz2" }, { - "filename": "pypy3.10-v7.3.12rc2-macos_arm64.tar.bz2", + "filename": "pypy3.10-v7.3.12-macos_arm64.tar.bz2", "arch": "arm64", "platform": "darwin", - "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12rc2-macos_arm64.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12-macos_arm64.tar.bz2" }, { - "filename": "pypy3.10-v7.3.12rc2-win64.zip", + "filename": "pypy3.10-v7.3.12-win64.zip", "arch": "x64", "platform": "win64", - "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12rc2-win64.zip" + "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12-win64.zip" }, { - "filename": "pypy3.10-v7.3.12rc2-s390x.tar.bz2", + "filename": "pypy3.10-v7.3.12-s390x.tar.bz2", "arch": "s390x", "platform": "linux", - "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12rc2-s390x.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy3.10-v7.3.12-s390x.tar.bz2" } ] }, { - "pypy_version": "7.3.12rc2", - "python_version": "3.9.16", - "stable": false, - "latest_pypy": false, - "date": "2023-05-28", + "pypy_version": "7.3.12", + "python_version": "3.9.17", + "stable": true, + "latest_pypy": true, + "date": "2023-06-16", "files": [ { - "filename": "pypy3.9-v7.3.12rc2-aarch64.tar.bz2", + "filename": "pypy3.9-v7.3.12-aarch64.tar.bz2", "arch": "aarch64", "platform": "linux", - "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12rc2-aarch64.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12-aarch64.tar.bz2" }, { - "filename": "pypy3.9-v7.3.12rc2-linux32.tar.bz2", + "filename": "pypy3.9-v7.3.12-linux32.tar.bz2", "arch": "i686", "platform": "linux", - "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12rc2-linux32.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12-linux32.tar.bz2" }, { - "filename": "pypy3.9-v7.3.12rc2-linux64.tar.bz2", + "filename": "pypy3.9-v7.3.12-linux64.tar.bz2", "arch": "x64", "platform": "linux", - "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12rc2-linux64.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12-linux64.tar.bz2" }, { - "filename": "pypy3.9-v7.3.12rc2-macos_x86_64.tar.bz2", + "filename": "pypy3.9-v7.3.12-macos_x86_64.tar.bz2", "arch": "x64", "platform": "darwin", - "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12rc2-macos_x86_64.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12-macos_x86_64.tar.bz2" }, { - "filename": "pypy3.9-v7.3.12rc2-macos_arm64.tar.bz2", + "filename": "pypy3.9-v7.3.12-macos_arm64.tar.bz2", "arch": "arm64", "platform": "darwin", - "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12rc2-macos_arm64.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12-macos_arm64.tar.bz2" }, { - "filename": "pypy3.9-v7.3.12rc2-win64.zip", + "filename": "pypy3.9-v7.3.12-win64.zip", "arch": "x64", "platform": "win64", - "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12rc2-win64.zip" + "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12-win64.zip" }, { - "filename": "pypy3.9-v7.3.12rc2-s390x.tar.bz2", + "filename": "pypy3.9-v7.3.12-s390x.tar.bz2", "arch": "s390x", "platform": "linux", - "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12rc2-s390x.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy3.9-v7.3.12-s390x.tar.bz2" } ] }, { - "pypy_version": "7.3.12rc2", + "pypy_version": "7.3.12", "python_version": "2.7.18", - "stable": false, - "latest_pypy": false, - "date": "2023-05-28", + "stable": true, + "latest_pypy": true, + "date": "2023-06-16", "files": [ { - "filename": "pypy2.7-v7.3.12rc2-aarch64.tar.bz2", + "filename": "pypy2.7-v7.3.12-aarch64.tar.bz2", "arch": "aarch64", "platform": "linux", - "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12rc2-aarch64.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12-aarch64.tar.bz2" }, { - "filename": "pypy2.7-v7.3.12rc2-linux32.tar.bz2", + "filename": "pypy2.7-v7.3.12-linux32.tar.bz2", "arch": "i686", "platform": "linux", - "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12rc2-linux32.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12-linux32.tar.bz2" }, { - "filename": "pypy2.7-v7.3.12rc2-linux64.tar.bz2", + "filename": "pypy2.7-v7.3.12-linux64.tar.bz2", "arch": "x64", "platform": "linux", - "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12rc2-linux64.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12-linux64.tar.bz2" }, { - "filename": "pypy2.7-v7.3.12rc2-macos_x86_64.tar.bz2", + "filename": "pypy2.7-v7.3.12-macos_x86_64.tar.bz2", "arch": "x64", "platform": "darwin", - "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12rc2-macos_x86_64.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12-macos_x86_64.tar.bz2" }, { - "filename": "pypy2.7-v7.3.12rc2-macos_arm64.tar.bz2", + "filename": "pypy2.7-v7.3.12-macos_arm64.tar.bz2", "arch": "arm64", "platform": "darwin", - "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12rc2-macos_arm64.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12-macos_arm64.tar.bz2" }, { - "filename": "pypy2.7-v7.3.12rc2-win64.zip", + "filename": "pypy2.7-v7.3.12-win64.zip", "arch": "x64", "platform": "win64", - "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12rc2-win64.zip" + "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12-win64.zip" }, { - "filename": "pypy2.7-v7.3.12rc2-s390x.tar.bz2", + "filename": "pypy2.7-v7.3.12-s390x.tar.bz2", "arch": "s390x", "platform": "linux", - "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12rc2-s390x.tar.bz2" + "download_url": "https://downloads.python.org/pypy/pypy2.7-v7.3.12-s390x.tar.bz2" } ] }, @@ -411,7 +411,7 @@ "pypy_version": "7.3.11", "python_version": "2.7.18", "stable": true, - "latest_pypy": true, + "latest_pypy": false, "date": "2022-12-29", "files": [ { diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py index 0f73fd139c..88156072e4 100644 --- a/rpython/annotator/test/test_annrpython.py +++ b/rpython/annotator/test/test_annrpython.py @@ -4750,6 +4750,25 @@ class TestAnnotateTestCase: s = ann.build_types(g, [int]) assert s.const == 43 + def test_isinstance_const(self): + class A(object): + pass + + class B(A): + pass + B.singleton = B() + class C(A): + pass + C.singleton = C() + def f(): + x = B.singleton + if not isinstance(x, B): + if not isinstance(x, C): + raise TypeError + return 12 + ann = self.RPythonAnnotator() + s = ann.build_types(f, []) + def g(n): return [0, 1, 2, n] diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py index 4718ea4da8..085ca8a481 100644 --- a/rpython/annotator/unaryop.py +++ b/rpython/annotator/unaryop.py @@ -62,7 +62,7 @@ def s_isinstance(annotator, s_obj, s_type, variables): # is quite border case for RPython r.const = False for v in variables: - assert v.annotation == s_obj + assert annotator.binding(v) == s_obj knowntypedata = defaultdict(dict) if not hasattr(typ, '_freeze_') and isinstance(s_type, SomePBC): add_knowntypedata(knowntypedata, True, variables, bk.valueoftype(typ)) diff --git a/rpython/flowspace/bytecode.py b/rpython/flowspace/bytecode.py index 38423bf785..982ce600f0 100644 --- a/rpython/flowspace/bytecode.py +++ b/rpython/flowspace/bytecode.py @@ -6,6 +6,11 @@ from opcode import EXTENDED_ARG, HAVE_ARGUMENT import opcode from rpython.flowspace.argument import Signature +try: + from __pypy__ import _promote +except ImportError: + _promote = lambda x: x + CO_GENERATOR = 0x0020 CO_VARARGS = 0x0004 CO_VARKEYWORDS = 0x0008 @@ -30,12 +35,13 @@ def cpython_code_signature(code): class BytecodeCorruption(Exception): pass +HASJREL = b"".join([chr(_opnum in opcode.hasjrel) for _opnum in range(256)]) class HostCode(object): """ A wrapper around a native code object of the host interpreter """ - opnames = host_bytecode_spec.method_names + opnames = tuple(host_bytecode_spec.method_names) def __init__(self, argcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, @@ -108,9 +114,9 @@ class HostCode(object): next_offset += 3 oparg = (oparg * 65536) | (hi * 256) | lo - if opnum in opcode.hasjrel: + if ord(HASJREL[opnum]): oparg += next_offset - opname = self.opnames[opnum] + opname = self.opnames[_promote(opnum)] return next_offset, opname, oparg @property diff --git a/rpython/flowspace/flowcontext.py b/rpython/flowspace/flowcontext.py index d960089a05..3c9338bd0c 100644 --- a/rpython/flowspace/flowcontext.py +++ b/rpython/flowspace/flowcontext.py @@ -306,6 +306,11 @@ class FlowContext(object): The locals are ordered according to self.pycode.signature. """ self.nlocals = code.co_nlocals + # locals_w is immutable in the sense that every write should make a new + # list first. this means FlowContext.getstate does not have to make a + # copy of locals_w. This is a good trade-off, because changes to + # locals_w (in STORE_FAST and DELETE_FAST) are much less common that + # calls to getstate, which happens after every bytecode self.locals_w = [None] * code.co_nlocals self.stack = [] @@ -339,7 +344,7 @@ class FlowContext(object): del self.stack[finaldepth:] def getstate(self, next_offset): - return FrameState(self.locals_w[:], self.stack[:], + return FrameState(self.locals_w, self.stack[:], self.last_exception, self.blockstack[:], next_offset) def setstate(self, state): @@ -873,6 +878,7 @@ class FlowContext(object): def STORE_FAST(self, varindex): w_newvalue = self.popvalue() assert w_newvalue is not None + self.locals_w = self.locals_w[:] self.locals_w[varindex] = w_newvalue if isinstance(w_newvalue, Variable): w_newvalue.rename(self.getlocalvarname(varindex)) @@ -1090,6 +1096,7 @@ class FlowContext(object): varname = self.getlocalvarname(varindex) message = "local variable '%s' referenced before assignment" raise UnboundLocalError(message, varname) + self.locals_w = self.locals_w[:] self.locals_w[varindex] = None def STORE_MAP(self, oparg): diff --git a/rpython/flowspace/framestate.py b/rpython/flowspace/framestate.py index cd5666db4d..1efe367986 100644 --- a/rpython/flowspace/framestate.py +++ b/rpython/flowspace/framestate.py @@ -26,16 +26,17 @@ class FrameState(object): @property def mergeable(self): + from rpython.flowspace.flowcontext import FlowSignal if self._mergeable is not None: return self._mergeable - self._mergeable = data = self.locals_w + self.stack + + self._mergeable = data = self.locals_w + recursively_flatten(self.stack) if self.last_exception is None: data.append(Constant(None)) data.append(Constant(None)) else: data.append(self.last_exception.w_type) data.append(self.last_exception.w_value) - recursively_flatten(data) return data def copy(self): @@ -91,12 +92,12 @@ class FrameState(object): def getoutputargs(self, targetstate): "Return the output arguments needed to link self to targetstate." result = [] - for w_output, w_target in zip(self.mergeable, targetstate.mergeable): + mergeable = self.mergeable + for i, w_target in enumerate(targetstate.mergeable): if isinstance(w_target, Variable): - result.append(w_output) + result.append(mergeable[i]) return result - class UnionError(Exception): "The two states should be merged." @@ -129,10 +130,19 @@ def union(w1, w2): def recursively_flatten(lst): from rpython.flowspace.flowcontext import FlowSignal - i = 0 + if not lst: + return lst + i = -1 + for i in range(len(lst)): + if isinstance(lst[i], FlowSignal): + break + else: + return lst + lst = lst[:] while i < len(lst): unroller = lst[i] if not isinstance(unroller, FlowSignal): i += 1 else: lst[i:i + 1] = unroller.args + return lst diff --git a/rpython/flowspace/operation.py b/rpython/flowspace/operation.py index af482bfae1..4d3d6acb6d 100644 --- a/rpython/flowspace/operation.py +++ b/rpython/flowspace/operation.py @@ -129,7 +129,11 @@ class PureOperation(HLOperation): msg = "%s%r always raises %s: %s" % ( self.opname, tuple(args), type(e), e) raise FlowingError(msg) - else: + return self._pure_result(result) + + def _pure_result(self, result): + # indented for history preservation + # don't try to constant-fold operations giving a 'long' # result. The result is probably meant to be sent to # an intmask(), but the 'long' constant confuses the @@ -148,6 +152,43 @@ class PureOperation(HLOperation): # store operation with variable result instead pass +class PureOperation1(PureOperation): + pure = True + + def constfold(self): + w_arg0, = self.args + if not w_arg0.foldable(): + return + arg0 = w_arg0.value + # argument is constant: call the operator now + try: + result = self.pyfunc(arg0) + except Exception as e: + from rpython.flowspace.flowcontext import FlowingError + msg = "%s%r always raises %s: %s" % ( + self.opname, (arg0, ), type(e), e) + raise FlowingError(msg) + return self._pure_result(result) + +class PureOperation2(PureOperation): + pure = True + + def constfold(self): + w_arg0, w_arg1 = self.args + if not w_arg0.foldable() or not w_arg1.foldable(): + return + arg0 = w_arg0.value + arg1 = w_arg1.value + # argument is constant: call the operator now + try: + result = self.pyfunc(arg0, arg1) + except Exception as e: + from rpython.flowspace.flowcontext import FlowingError + msg = "%s%r always raises %s: %s" % ( + self.opname, (arg0, arg1), type(e), e) + raise FlowingError(msg) + return self._pure_result(result) + class OverflowingOperation(PureOperation): can_overflow = True @@ -269,7 +310,12 @@ def add_operator(name, arity, dispatch=None, pyfunc=None, pure=False, ovf=False) assert pure base_cls = OverflowingOperation elif pure: - base_cls = PureOperation + if arity == 1: + base_cls = PureOperation1 + elif arity == 2: + base_cls = PureOperation2 + else: + base_cls = PureOperation else: base_cls = HLOperation bases.append(base_cls) diff --git a/rpython/flowspace/test/test_framestate.py b/rpython/flowspace/test/test_framestate.py index 33f05790c1..be787e4c39 100644 --- a/rpython/flowspace/test/test_framestate.py +++ b/rpython/flowspace/test/test_framestate.py @@ -15,6 +15,7 @@ class TestFrameState: ctx = FlowContext(graph, code) # hack the frame ctx.setstate(graph.startblock.framestate) + ctx.locals_w = ctx.locals_w[:] ctx.locals_w[-1] = Constant(None) return ctx @@ -31,6 +32,7 @@ class TestFrameState: def test_neq_hacked_framestate(self): ctx = self.get_context(self.func_simple) fs1 = ctx.getstate(0) + ctx.locals_w = ctx.locals_w[:] ctx.locals_w[-1] = Variable() fs2 = ctx.getstate(0) assert not fs1.matches(fs2) @@ -44,6 +46,7 @@ class TestFrameState: def test_union_on_hacked_framestates(self): ctx = self.get_context(self.func_simple) fs1 = ctx.getstate(0) + ctx.locals_w = ctx.locals_w[:] ctx.locals_w[-1] = Variable() fs2 = ctx.getstate(0) assert fs1.union(fs2).matches(fs2) # fs2 is more general @@ -52,6 +55,7 @@ class TestFrameState: def test_restore_frame(self): ctx = self.get_context(self.func_simple) fs1 = ctx.getstate(0) + ctx.locals_w = ctx.locals_w[:] ctx.locals_w[-1] = Variable() ctx.setstate(fs1) assert fs1.matches(ctx.getstate(0)) @@ -71,6 +75,7 @@ class TestFrameState: def test_getoutputargs(self): ctx = self.get_context(self.func_simple) fs1 = ctx.getstate(0) + ctx.locals_w = ctx.locals_w[:] ctx.locals_w[-1] = Variable() fs2 = ctx.getstate(0) outputargs = fs1.getoutputargs(fs2) @@ -81,6 +86,7 @@ class TestFrameState: def test_union_different_constants(self): ctx = self.get_context(self.func_simple) fs1 = ctx.getstate(0) + ctx.locals_w = ctx.locals_w[:] ctx.locals_w[-1] = Constant(42) fs2 = ctx.getstate(0) fs3 = fs1.union(fs2) @@ -90,6 +96,7 @@ class TestFrameState: def test_union_spectag(self): ctx = self.get_context(self.func_simple) fs1 = ctx.getstate(0) + ctx.locals_w = ctx.locals_w[:] ctx.locals_w[-1] = Constant(SpecTag()) fs2 = ctx.getstate(0) assert fs1.union(fs2) is None # UnionError diff --git a/rpython/flowspace/test/test_objspace.py b/rpython/flowspace/test/test_objspace.py index c7a9ab128e..6c5af2f662 100644 --- a/rpython/flowspace/test/test_objspace.py +++ b/rpython/flowspace/test/test_objspace.py @@ -8,6 +8,7 @@ from rpython.flowspace.model import ( from rpython.translator.simplify import simplify_graph from rpython.flowspace.objspace import build_flow from rpython.flowspace.flowcontext import FlowingError, FlowContext +from rpython.flowspace.bytecode import HostCode from rpython.conftest import option from rpython.tool.stdlib_opcode import host_bytecode_spec @@ -22,9 +23,12 @@ def patching_opcodes(**opcodes): for name, num in opcodes.items(): old_name[num] = meth_names[num] meth_names[num] = name + old_tuple = HostCode.opnames + HostCode.opnames = tuple(host_bytecode_spec.method_names) yield for name in opcodes: meth_names[num] = old_name[num] + HostCode.opname = old_tuple class Base: diff --git a/rpython/jit/codewriter/assembler.py b/rpython/jit/codewriter/assembler.py index 0b7822cab1..9acc17cb1e 100644 --- a/rpython/jit/codewriter/assembler.py +++ b/rpython/jit/codewriter/assembler.py @@ -31,11 +31,12 @@ class Assembler(object): self.all_liveness_positions = {} self.num_liveness_ops = 0 - def assemble(self, ssarepr, jitcode=None): + def assemble(self, ssarepr, jitcode=None, num_regs=None): """Take the 'ssarepr' representation of the code and assemble it inside the 'jitcode'. If jitcode is None, make a new one. """ self.setup(ssarepr.name) + self.count_regs.update(num_regs) ssarepr._insns_pos = [] for insn in ssarepr.insns: ssarepr._insns_pos.append(len(self.code)) @@ -68,9 +69,12 @@ class Assembler(object): self.ssareprname = name def emit_reg(self, reg): + assert reg.index < self.count_regs[reg.kind] + self.code.append(chr(reg.index)) + + def count_reg(self, reg): if reg.index >= self.count_regs[reg.kind]: self.count_regs[reg.kind] = reg.index + 1 - self.code.append(chr(reg.index)) def emit_const(self, const, kind, allow_short=False): value = const.value @@ -124,8 +128,8 @@ class Assembler(object): val = self.constants_dict[key] except KeyError: constants.append(value) - val = 256 - len(constants) - assert val >= 0, "too many constants" + val = self.count_regs[kind] + len(constants) - 1 + assert 0 <= val < 256, "too many constants" self.constants_dict[key] = val # emit the constant normally, as one byte that is an index in the # list of constants diff --git a/rpython/jit/codewriter/codewriter.py b/rpython/jit/codewriter/codewriter.py index 2948044a15..e74f8ef036 100644 --- a/rpython/jit/codewriter/codewriter.py +++ b/rpython/jit/codewriter/codewriter.py @@ -57,7 +57,12 @@ class CodeWriter(object): # of bytes and lists of constants. It's during this step that # constants are cast to their normalized type (Signed, GCREF or # Float). - self.assembler.assemble(ssarepr, jitcode) + num_regs = {kind: + (max(regallocs[kind]._coloring.values()) + 1 + if regallocs[kind]._coloring + else 0) + for kind in KINDS} + self.assembler.assemble(ssarepr, jitcode, num_regs) jitcode.index = index # # print the resulting assembler diff --git a/rpython/jit/codewriter/jitcode.py b/rpython/jit/codewriter/jitcode.py index 931ab12ad5..3b3d3d87e9 100644 --- a/rpython/jit/codewriter/jitcode.py +++ b/rpython/jit/codewriter/jitcode.py @@ -51,6 +51,16 @@ class JitCode(AbstractDescr): def num_regs_f(self): return ord(self.c_num_regs_f) + def num_regs_and_consts_i(self): + return ord(self.c_num_regs_i) + len(self.constants_i) + + def num_regs_and_consts_r(self): + return ord(self.c_num_regs_r) + len(self.constants_r) + + def num_regs_and_consts_f(self): + return ord(self.c_num_regs_f) + len(self.constants_f) + + def _live_vars(self, pc, all_liveness, op_live): from rpython.jit.codewriter.liveness import LivenessIterator # for testing only diff --git a/rpython/jit/codewriter/support.py b/rpython/jit/codewriter/support.py index 14a1f6d656..10c104aaaa 100644 --- a/rpython/jit/codewriter/support.py +++ b/rpython/jit/codewriter/support.py @@ -477,10 +477,8 @@ class LLtypeHelpers: _ll_1_odict_keys = rordereddict.ll_dict_keys _ll_1_odict_values = rordereddict.ll_dict_values - _ll_1_odict_items = rordereddict.ll_dict_items _ll_1_odict_keys .need_result_type = True _ll_1_odict_values.need_result_type = True - _ll_1_odict_items .need_result_type = True _ll_1_odictiter_next = rordereddict._ll_dictnext _ll_1_odict_resize = rordereddict.ll_dict_resize diff --git a/rpython/jit/codewriter/test/test_assembler.py b/rpython/jit/codewriter/test/test_assembler.py index f389408bf9..c4fdf544a0 100644 --- a/rpython/jit/codewriter/test/test_assembler.py +++ b/rpython/jit/codewriter/test/test_assembler.py @@ -20,7 +20,7 @@ def test_assemble_simple(): ('int_return', i2), ] assembler = Assembler() - jitcode = assembler.assemble(ssarepr) + jitcode = assembler.assemble(ssarepr, num_regs={'int': 3}) assert jitcode.code == ("\x00\x00\x01\x02" "\x01\x02") assert assembler.insns == {'int_add/ii>i': 0, @@ -39,12 +39,12 @@ def test_assemble_consts(): ('int_return', Constant(-129, lltype.Signed)), ] assembler = Assembler() - jitcode = assembler.assemble(ssarepr) + jitcode = assembler.assemble(ssarepr, num_regs={'int': 14}) assert jitcode.code == ("\x00\x0D" "\x01\x12" # use int_return/c for one-byte consts "\x01\xFC" - "\x00\xFF" # use int_return/i for larger consts - "\x00\xFE") + "\x00\x0E" # use int_return/i for larger consts + "\x00\x0F") assert assembler.insns == {'int_return/i': 0, 'int_return/c': 1} assert jitcode.constants_i == [128, -129] @@ -58,11 +58,11 @@ def test_assemble_float_consts(): ('float_return', Constant(128.1, lltype.Float)), ] assembler = Assembler() - jitcode = assembler.assemble(ssarepr) + jitcode = assembler.assemble(ssarepr, num_regs={'float': 14}) assert jitcode.code == ("\x00\x0D" - "\x00\xFF" - "\x00\xFE" - "\x00\xFD") + "\x00\x0e" + "\x00\x0f" + "\x00\x10") assert assembler.insns == {'float_return/f': 0} assert jitcode.constants_f == [longlong.getfloatstorage(18.0), longlong.getfloatstorage(-4.0), @@ -104,14 +104,14 @@ def test_assemble_cast_consts(): ('ref_return', Constant(np, lltype.Ptr(S))), ] assembler = Assembler() - jitcode = assembler.assemble(ssarepr) + jitcode = assembler.assemble(ssarepr, num_regs={}) assert jitcode.code == ("\x00\x58" - "\x01\xFF" - "\x01\xFE" - "\x02\xFF" - "\x02\xFE" - "\x02\xFF" - "\x02\xFE") + "\x01\x00" + "\x01\x01" + "\x02\x00" + "\x02\x01" + "\x02\x00" + "\x02\x01") assert assembler.insns == {'int_return/c': 0, 'int_return/i': 1, 'ref_return/r': 2} @@ -134,7 +134,7 @@ def test_assemble_loop(): ('int_return', i1), ] assembler = Assembler() - jitcode = assembler.assemble(ssarepr) + jitcode = assembler.assemble(ssarepr, num_regs={'int': 0x18}) assert jitcode.code == ("\x00\x16\x04\x10\x00" "\x01\x17\x16\x17" "\x02\x16\x01\x16" @@ -154,8 +154,8 @@ def test_assemble_list(): ListOfKind('ref', [])), ] assembler = Assembler() - jitcode = assembler.assemble(ssarepr) - assert jitcode.code == "\x00\x03\x16\x17\xFF\x00" + jitcode = assembler.assemble(ssarepr, num_regs=dict(int=0x18)) + assert jitcode.code == "\x00\x03\x16\x17\x18\x00" assert assembler.insns == {'foobar/IR': 0} assert jitcode.constants_i == [42] @@ -171,11 +171,11 @@ def test_assemble_list_semibug(): ('bok', Constant(41, lltype.Signed)), ] assembler = Assembler() - jitcode = assembler.assemble(ssarepr) - assert jitcode.code == ("\x00\x01\xFF" - "\x00\x01\xFF" + jitcode = assembler.assemble(ssarepr, num_regs={}) + assert jitcode.code == ("\x00\x01\x00" + "\x00\x01\x00" "\x01\x2A" - "\x02\xFE") + "\x02\x01") assert assembler.insns == {'foobar/I': 0, 'baz/c': 1, # in USE_C_FORM 'bok/i': 2} # not in USE_C_FORM @@ -188,7 +188,7 @@ def test_assemble_descr(): ssarepr = SSARepr("test") ssarepr.insns = [('foobar', d) for d in descrs[::-1]] assembler = Assembler() - jitcode = assembler.assemble(ssarepr) + jitcode = assembler.assemble(ssarepr, num_regs={}) assert jitcode.code == ''.join(["\x00" + struct.pack("<H", i) for i in range(300)]) assert assembler.insns == {'foobar/d': 0} @@ -201,14 +201,14 @@ def test_assemble_indirect_call(): ssarepr.insns = [('foobar', IndirectCallTargets(lst1)), ('foobar', IndirectCallTargets(lst2))] assembler = Assembler() - assembler.assemble(ssarepr) + assembler.assemble(ssarepr, num_regs={}) assert assembler.indirectcalltargets == set(lst1).union(lst2) def test_num_regs(): assembler = Assembler() ssarepr = SSARepr("test") ssarepr.insns = [] - jitcode = assembler.assemble(ssarepr) + jitcode = assembler.assemble(ssarepr, num_regs={}) assert jitcode.num_regs_i() == 0 assert jitcode.num_regs_r() == 0 assert jitcode.num_regs_f() == 0 @@ -216,7 +216,7 @@ def test_num_regs(): ssarepr.insns = [('foobar', Register('int', 51), Register('ref', 27), Register('int', 12))] - jitcode = assembler.assemble(ssarepr) + jitcode = assembler.assemble(ssarepr, num_regs={'int': 52, 'ref': 28}) assert jitcode.num_regs_i() == 52 assert jitcode.num_regs_r() == 28 assert jitcode.num_regs_f() == 0 @@ -231,7 +231,7 @@ def test_liveness(): ('-live-', i2), ] assembler = Assembler() - jitcode = assembler.assemble(ssarepr) + jitcode = assembler.assemble(ssarepr, num_regs={'int': 3}) assert jitcode.code == ("\x00\x00\x0A\x01" # ends at 4 "\x01\x00\x00" "\x00\x00\x03\x02" # ends at 13 @@ -251,7 +251,7 @@ def test_assemble_error_string_constant(): ('duh', c), ] assembler = Assembler() - py.test.raises(AssemblerError, assembler.assemble, ssarepr) + py.test.raises(AssemblerError, assembler.assemble, ssarepr, num_regs={}) def test_assemble_r_int(): # r_int is a strange type, which the jit should replace with int. @@ -263,6 +263,6 @@ def test_assemble_r_int(): ('int_add', i0, Constant(r_int(42424243), lltype.Signed), '->', i2), ] assembler = Assembler() - jitcode = assembler.assemble(ssarepr) + jitcode = assembler.assemble(ssarepr, num_regs={'int': 3}) assert jitcode.constants_i == [42424242, 42424243] assert map(type, jitcode.constants_i) == [int, int] diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py index b02e020f49..a9f1fc252d 100644 --- a/rpython/jit/metainterp/blackhole.py +++ b/rpython/jit/metainterp/blackhole.py @@ -16,6 +16,7 @@ from rpython.rlib.unroll import unrolling_iterable from rpython.rtyper.lltypesystem import lltype, llmemory, rffi from rpython.rtyper import rclass from rpython.rtyper.lltypesystem.lloperation import llop +from rpython.rlib.debug import debug_start, debug_stop, debug_print from rpython.rlib.jit_libffi import CIF_DESCRIPTION_P SIZE_LIVE_OP = OFFSET_SIZE + 1 @@ -54,7 +55,6 @@ class BlackholeInterpBuilder(object): self.setup_insns(asm.insns) self.setup_descrs(asm.descrs) self.metainterp_sd = metainterp_sd - self.num_interpreters = 0 self.blackholeinterps = None def _cleanup_(self): @@ -245,8 +245,7 @@ class BlackholeInterpBuilder(object): self.blackholeinterps = res.back return res else: - self.num_interpreters += 1 - return BlackholeInterpreter(self, self.num_interpreters) + return BlackholeInterpreter(self) def release_interp(self, interp): interp.cleanup_registers() @@ -264,7 +263,8 @@ def check_list_of_plain_integers(s_arg, bookkeeper): from rpython.annotator import model as annmodel assert isinstance(s_arg, annmodel.SomeList) s_arg.listdef.never_resize() - assert s_arg.listdef.listitem.s_value.knowntype is int + s_value = s_arg.listdef.listitem.s_value + assert s_value.knowntype is int or isinstance(s_value, annmodel.SomeImpossibleValue) def _check_int(s_arg, bookkeeper): assert s_arg.knowntype is int @@ -274,17 +274,17 @@ def plain_int(x): check_annotation(x, _check_int) return x +EMPTY_LIST_I = [] # shared class BlackholeInterpreter(object): - def __init__(self, builder, count_interpreter): + def __init__(self, builder): self.builder = builder self.cpu = builder.cpu self.dispatch_loop = builder.dispatch_loop self.descrs = builder.descrs self.op_catch_exception = builder.op_catch_exception self.op_rvmprof_code = builder.op_rvmprof_code - self.count_interpreter = count_interpreter # if we_are_translated(): default_i = 0 @@ -294,28 +294,42 @@ class BlackholeInterpreter(object): default_i = MissingValue() default_r = MissingValue() default_f = MissingValue() - self.registers_i = [default_i] * 256 - self.registers_r = [default_r] * 256 - self.registers_f = [default_f] * 256 + self.registers_i = EMPTY_LIST_I + self.registers_r = None + self.registers_f = None self.tmpreg_i = default_i self.tmpreg_r = default_r self.tmpreg_f = default_f self.jitcode = None - self.back = None # chain unused interpreters together via this check_annotation(self.registers_i, check_list_of_plain_integers) def __repr__(self): - return '<BHInterp #%d>' % self.count_interpreter + return '<BHInterp %s>' % self.jitcode def setposition(self, jitcode, position): - if jitcode is not self.jitcode: - # the real performance impact of the following code is unclear, - # but it should be minimized by the fact that a given - # BlackholeInterpreter instance is likely to be reused with - # exactly the same jitcode, so we don't do the copy again. - self.copy_constants(self.registers_i, jitcode.constants_i) - self.copy_constants(self.registers_r, jitcode.constants_r) - self.copy_constants(self.registers_f, jitcode.constants_f) + num_regs_and_consts_i = jitcode.num_regs_and_consts_i() + num_regs_and_consts_r = jitcode.num_regs_and_consts_r() + num_regs_and_consts_f = jitcode.num_regs_and_consts_f() + if we_are_translated(): + default_i = 0 + default_r = NULL + default_f = longlong.ZEROF + else: + default_i = MissingValue() + default_r = MissingValue() + default_f = MissingValue() + if num_regs_and_consts_i: + if self.registers_i is None or len(self.registers_i) < num_regs_and_consts_i: + self.registers_i = [default_i] * num_regs_and_consts_i + self.copy_constants(self.registers_i, jitcode.constants_i, jitcode.num_regs_i()) + if num_regs_and_consts_r: + if self.registers_r is None or len(self.registers_r) < num_regs_and_consts_r: + self.registers_r = [default_r] * num_regs_and_consts_r + self.copy_constants(self.registers_r, jitcode.constants_r, jitcode.num_regs_r()) + if num_regs_and_consts_f: + if self.registers_f is None or len(self.registers_f) < num_regs_and_consts_f: + self.registers_f = [default_f] * num_regs_and_consts_f + self.copy_constants(self.registers_f, jitcode.constants_f, jitcode.num_regs_f()) self.jitcode = jitcode self.position = position @@ -421,18 +435,15 @@ class BlackholeInterpreter(object): from rpython.rlib.rvmprof import cintf cintf.jit_rvmprof_code(0, arg2) - def copy_constants(self, registers, constants): + def copy_constants(self, registers, constants, targetindex): """Copy jitcode.constants[0] to registers[255], jitcode.constants[1] to registers[254], jitcode.constants[2] to registers[253], etc.""" make_sure_not_resized(registers) make_sure_not_resized(constants) - i = len(constants) - 1 - while i >= 0: - j = 255 - i - assert j >= 0 - registers[j] = constants[i] - i -= 1 + for i in range(len(constants)): + registers[targetindex] = constants[i] + targetindex += 1 copy_constants._annspecialcase_ = 'specialize:arglistitemtype(1)' # ---------- diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py index 56faf0670a..914270b110 100644 --- a/rpython/jit/metainterp/pyjitpl.py +++ b/rpython/jit/metainterp/pyjitpl.py @@ -65,44 +65,56 @@ class MIFrame(object): def __init__(self, metainterp): self.metainterp = metainterp - self.registers_i = [None] * 256 - self.registers_r = [None] * 256 - self.registers_f = [None] * 256 + self.registers_i = None + self.registers_r = None + self.registers_f = None def setup(self, jitcode, greenkey=None): # if not translated, fill the registers with MissingValue() - if not we_are_translated(): - self.registers_i = [MissingValue()] * 256 - self.registers_r = [MissingValue()] * 256 - self.registers_f = [MissingValue()] * 256 assert isinstance(jitcode, JitCode) self.jitcode = jitcode self.bytecode = jitcode.code # this is not None for frames that are recursive portal calls self.greenkey = greenkey - # copy the constants in place - self.copy_constants(self.registers_i, jitcode.constants_i, ConstInt) - self.copy_constants(self.registers_r, jitcode.constants_r, ConstPtrJitCode) - self.copy_constants(self.registers_f, jitcode.constants_f, ConstFloat) + # create registers_* lists and copy the constants in place + num_regs_and_consts_i = jitcode.num_regs_and_consts_i() + num_regs_and_consts_r = jitcode.num_regs_and_consts_r() + num_regs_and_consts_f = jitcode.num_regs_and_consts_f() + if num_regs_and_consts_i: + self.registers_i = self.copy_constants(self.registers_i, jitcode.constants_i, jitcode.num_regs_i(), ConstInt) + if num_regs_and_consts_r: + self.registers_r = self.copy_constants(self.registers_r, jitcode.constants_r, jitcode.num_regs_r(), ConstPtrJitCode) + if num_regs_and_consts_f: + self.registers_f = self.copy_constants(self.registers_f, jitcode.constants_f, jitcode.num_regs_f(), ConstFloat) self._result_argcode = 'v' # for resume.py operation self.parent_snapshot = None # counter for unrolling inlined loops self.unroll_iterations = 1 - @specialize.arg(3) - def copy_constants(self, registers, constants, ConstClass): - """Copy jitcode.constants[0] to registers[255], - jitcode.constants[1] to registers[254], - jitcode.constants[2] to registers[253], etc.""" + @specialize.arg(4) + def copy_constants(self, registers, constants, targetindex, ConstClass): + """Copy jitcode.constants[0] to registers[self.jitcode.num_regs_x() + 0], + jitcode.constants[1] to registers[self.jitcode.num_regs_x() + 1], + jitcode.constants[2] to registers[self.jitcode.num_regs_x() + 2], + etc.""" + if not we_are_translated(): + missing = MissingValue() + else: + missing = None + num_regs_and_consts = targetindex + len(constants) + # increase size if its too small + if registers is None or len(registers) < num_regs_and_consts: + registers = [missing] * num_regs_and_consts + elif not we_are_translated(): + for i in range(len(registers)): + registers[i] = missing if nonconst.NonConstant(0): # force the right type constants[0] = ConstClass.value # (useful for small tests) - i = len(constants) - 1 - while i >= 0: - j = 255 - i - assert j >= 0 - registers[j] = ConstClass(constants[i]) - i -= 1 + for i in range(len(constants)): + registers[targetindex] = ConstClass(constants[i]) + targetindex += 1 + return registers def cleanup_registers(self): # To avoid keeping references alive, this cleans up the registers_r. @@ -232,6 +244,8 @@ class MIFrame(object): registers = self.registers_f else: assert 0, oldbox + if not count: + return for i in range(count): if registers[i] is oldbox: registers[i] = newbox @@ -397,8 +411,7 @@ class MIFrame(object): @arguments("box", "box", "boxes2", "descr", "orgpc") def opimpl_record_known_result_i_ir_v(self, resbox, funcbox, argboxes, calldescr, pc): - allboxes = self._build_allboxes(funcbox, argboxes, calldescr) - allboxes = [resbox] + allboxes + allboxes = self._build_allboxes(funcbox, argboxes, calldescr, prepend_box=resbox) # this is a weird op! we don't want to execute anything, so just record # an operation self.metainterp._record_helper_varargs(rop.RECORD_KNOWN_RESULT, None, calldescr, allboxes) @@ -1889,11 +1902,15 @@ class MIFrame(object): self.metainterp.assert_no_exception() return op - def _build_allboxes(self, funcbox, argboxes, descr): - allboxes = [None] * (len(argboxes)+1) - allboxes[0] = funcbox + def _build_allboxes(self, funcbox, argboxes, descr, prepend_box=None): + allboxes = [None] * (len(argboxes)+1 + int(prepend_box is not None)) + i = 0 + if prepend_box is not None: + allboxes[0] = prepend_box + i = 1 + allboxes[i] = funcbox + i += 1 src_i = src_r = src_f = 0 - i = 1 for kind in descr.get_arg_types(): if kind == history.INT or kind == 'S': # single float while True: @@ -2055,11 +2072,10 @@ class MIFrame(object): def do_conditional_call(self, condbox, funcbox, argboxes, descr, pc, is_value=False): - allboxes = self._build_allboxes(funcbox, argboxes, descr) + allboxes = self._build_allboxes(funcbox, argboxes, descr, prepend_box=condbox) effectinfo = descr.get_extra_info() assert not effectinfo.check_forces_virtual_or_virtualizable() exc = effectinfo.check_can_raise() - allboxes = [condbox] + allboxes # COND_CALL cannot be pure (=elidable): it has no result. # On the other hand, COND_CALL_VALUE is always calling a pure # function. @@ -2396,7 +2412,7 @@ class MetaInterp(object): (jitcode.jitdriver_sd, None, self.history.get_trace_position())) # we save the freed MIFrames to avoid needing to re-create new # MIFrame objects all the time; they are a bit big, with their - # 3*256 register entries. + # up to 3*256 register entries. frame.cleanup_registers() self.free_frames_list.append(frame) diff --git a/rpython/jit/metainterp/resume.py b/rpython/jit/metainterp/resume.py index a38ac7cfcc..32e966952d 100644 --- a/rpython/jit/metainterp/resume.py +++ b/rpython/jit/metainterp/resume.py @@ -84,17 +84,17 @@ class AccumInfo(VectorInfo): self.location) def _ensure_parent_resumedata(framestack, n, t, snapshot): - if n == 0: - return - target = framestack[n] - back = framestack[n - 1] - if target.parent_snapshot: - snapshot.prev = target.parent_snapshot - return - s = t.create_snapshot(back.jitcode, back.pc, back, True) - snapshot.prev = s - _ensure_parent_resumedata(framestack, n - 1, t, s) - target.parent_snapshot = s + while n > 0: + target = framestack[n] + back = framestack[n - 1] + if target.parent_snapshot: + snapshot.prev = target.parent_snapshot + return + s = t.create_snapshot(back.jitcode, back.pc, back, True) + snapshot.prev = s + target.parent_snapshot = s + n -= 1 + snapshot = s def capture_resumedata(framestack, virtualizable_boxes, virtualref_boxes, t, after_residual_call=False): n = len(framestack) - 1 diff --git a/rpython/jit/metainterp/test/support.py b/rpython/jit/metainterp/test/support.py index 85a4eed42e..5ded76e8f7 100644 --- a/rpython/jit/metainterp/test/support.py +++ b/rpython/jit/metainterp/test/support.py @@ -112,6 +112,8 @@ def _run_with_blackhole(testself, args): cw = testself.cw blackholeinterpbuilder = BlackholeInterpBuilder(cw) blackholeinterp = blackholeinterpbuilder.acquire_interp() + [jitdriver_sd] = cw.callcontrol.jitdrivers_sd + blackholeinterp.setposition(jitdriver_sd.mainjitcode, 0) count_i = count_r = count_f = 0 for value in args: T = lltype.typeOf(value) @@ -127,8 +129,6 @@ def _run_with_blackhole(testself, args): count_f += 1 else: raise TypeError(T) - [jitdriver_sd] = cw.callcontrol.jitdrivers_sd - blackholeinterp.setposition(jitdriver_sd.mainjitcode, 0) blackholeinterp.run() return blackholeinterp._final_result_anytype() diff --git a/rpython/jit/metainterp/test/test_blackhole.py b/rpython/jit/metainterp/test/test_blackhole.py index 5b2e0cd7e9..7ca0b059a0 100644 --- a/rpython/jit/metainterp/test/test_blackhole.py +++ b/rpython/jit/metainterp/test/test_blackhole.py @@ -1,4 +1,4 @@ -import py +import pytest from rpython.rlib.jit import JitDriver from rpython.jit.metainterp.test.support import LLJitMixin from rpython.jit.metainterp.blackhole import BlackholeInterpBuilder @@ -58,9 +58,10 @@ def test_simple_const(): def test_simple_bigconst(): jitcode = JitCode("test") - jitcode.setup("\x00\xFD\x01\x02" + jitcode.setup("\x00\x05\x01\x02" "\x01\x02", - [666, 666, 10042, 666]) + [666, 666, 10042, 666], + num_regs_i=3) blackholeinterp = getblackholeinterp({'int_sub/ii>i': 0, 'int_return/i': 1}) blackholeinterp.setposition(jitcode, 0) @@ -137,13 +138,14 @@ def test_convert_and_run_from_pyjitpl(): MyMetaInterp.staticdata.blackholeinterpbuilder.metainterp_sd = \ MyMetaInterp.staticdata # - d = py.test.raises(jitexc.DoneWithThisFrameInt, + d = pytest.raises(jitexc.DoneWithThisFrameInt, convert_and_run_from_pyjitpl, MyMetaInterp()) assert d.value.result == 42 class TestBlackhole(LLJitMixin): + @pytest.mark.xfail def test_blackholeinterp_cache_basic(self): class FakeJitcode: def num_regs_r(self): @@ -156,6 +158,7 @@ class TestBlackhole(LLJitMixin): interp3 = builder.acquire_interp() assert builder.num_interpreters == 2 + @pytest.mark.xfail def test_blackholeinterp_cache_normal(self): myjitdriver = JitDriver(greens = [], reds = ['x', 'y']) def choices(x): @@ -189,6 +192,7 @@ class TestBlackhole(LLJitMixin): assert builder.num_interpreters == 2 assert len(seen) == 2 * 3 + @pytest.mark.xfail def test_blackholeinterp_cache_exc(self): myjitdriver = JitDriver(greens = [], reds = ['x', 'y']) class FooError(Exception): @@ -212,18 +216,18 @@ class TestBlackhole(LLJitMixin): x -= 1 return y res = self.meta_interp(f, [7], repeat=7) - assert res == sum([py.test.raises(FooError, choices, x).value.num + assert res == sum([pytest.raises(FooError, choices, x).value.num for x in range(1, 8)]) builder = pyjitpl._warmrunnerdesc.metainterp_sd.blackholeinterpbuilder assert builder.num_interpreters == 2 def test_bad_shift(): - py.test.raises(ValueError, BlackholeInterpreter.bhimpl_int_lshift.im_func, 7, 100) - py.test.raises(ValueError, BlackholeInterpreter.bhimpl_int_rshift.im_func, 7, 100) - py.test.raises(ValueError, BlackholeInterpreter.bhimpl_uint_rshift.im_func, 7, 100) - py.test.raises(ValueError, BlackholeInterpreter.bhimpl_int_lshift.im_func, 7, -1) - py.test.raises(ValueError, BlackholeInterpreter.bhimpl_int_rshift.im_func, 7, -1) - py.test.raises(ValueError, BlackholeInterpreter.bhimpl_uint_rshift.im_func, 7, -1) + pytest.raises(ValueError, BlackholeInterpreter.bhimpl_int_lshift.im_func, 7, 100) + pytest.raises(ValueError, BlackholeInterpreter.bhimpl_int_rshift.im_func, 7, 100) + pytest.raises(ValueError, BlackholeInterpreter.bhimpl_uint_rshift.im_func, 7, 100) + pytest.raises(ValueError, BlackholeInterpreter.bhimpl_int_lshift.im_func, 7, -1) + pytest.raises(ValueError, BlackholeInterpreter.bhimpl_int_rshift.im_func, 7, -1) + pytest.raises(ValueError, BlackholeInterpreter.bhimpl_uint_rshift.im_func, 7, -1) assert BlackholeInterpreter.bhimpl_int_lshift.im_func(100, 3) == 100<<3 assert BlackholeInterpreter.bhimpl_int_rshift.im_func(100, 3) == 100>>3 @@ -235,7 +239,7 @@ def test_debug_fatalerror(): msg = rstr.mallocstr(1) msg.chars[0] = "!" msg = lltype.cast_opaque_ptr(llmemory.GCREF, msg) - e = py.test.raises(LLFatalError, + e = pytest.raises(LLFatalError, BlackholeInterpreter.bhimpl_debug_fatalerror.im_func, msg) assert str(e.value) == '!' diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py index ce19b7e377..6a976d7f76 100644 --- a/rpython/rlib/rbigint.py +++ b/rpython/rlib/rbigint.py @@ -154,7 +154,7 @@ def intsign(i): class rbigint(object): """This is a reimplementation of longs using a list of digits.""" _immutable_ = True - _immutable_fields_ = ["_digits[*]", "size", "sign"] + _immutable_fields_ = ["_digits[*]", "_size"] def __init__(self, digits=NULLDIGITS, sign=0, size=0): if not we_are_translated(): @@ -163,9 +163,13 @@ class rbigint(object): self._digits = digits assert size >= 0 - self.size = size or len(digits) + self._size = (size or len(digits)) * sign - self.sign = sign + def get_sign(self): + return intsign(self._size) + + def _set_sign(self, sign): + self._size = abs(self._size) * sign # __eq__ and __ne__ method exist for testing only, they are not RPython! @not_rpython @@ -209,7 +213,9 @@ class rbigint(object): setdigit._always_inline_ = True def numdigits(self): - w = self.size + w = abs(self._size) + if not w: + w = 1 assert w > 0 return w numdigits._always_inline_ = True @@ -386,7 +392,7 @@ class rbigint(object): def tobytes(self, nbytes, byteorder, signed): if byteorder not in ('big', 'little'): raise InvalidEndiannessError() - if not signed and self.sign == -1: + if not signed and self.get_sign() == -1: raise InvalidSignednessError() bswap = byteorder == 'big' @@ -400,7 +406,7 @@ class rbigint(object): for i in range(0, imax): d = self.widedigit(i) - if self.sign == -1: + if self.get_sign() == -1: d = (d ^ MASK) + carry carry = d >> SHIFT d &= MASK @@ -408,7 +414,7 @@ class rbigint(object): accum |= d << accumbits if i == imax - 1: # Avoid bogus 0's - s = d ^ MASK if self.sign == -1 else d + s = d ^ MASK if self.get_sign() == -1 else d while s: s >>= 1 accumbits += 1 @@ -429,14 +435,14 @@ class rbigint(object): raise OverflowError() j += 1 - if self.sign == -1: + if self.get_sign() == -1: # Add a sign bit accum |= (~_widen_digit(0)) << accumbits result.append(chr(accum & 0xFF)) if j < nbytes: - signbyte = 0xFF if self.sign == -1 else 0 + signbyte = 0xFF if self.get_sign() == -1 else 0 result.append_multiple_char(chr(signbyte), nbytes - j) digits = result.build() @@ -444,7 +450,7 @@ class rbigint(object): if j == nbytes and nbytes > 0 and signed: # If not already set, we cannot contain the sign bit msb = digits[-1] - if (self.sign == -1) != (ord(msb) >= 0x80): + if (self.get_sign() == -1) != (ord(msb) >= 0x80): raise OverflowError() if bswap: @@ -455,6 +461,7 @@ class rbigint(object): digits = ''.join([digits[i] for i in range(length-1, -1, -1)]) return digits + @jit.elidable def toint(self): """ Get an integer from a bigint object. @@ -468,7 +475,7 @@ class rbigint(object): def _toint_helper(self): x = self._touint_helper() # Haven't lost any bits so far - if self.sign >= 0: + if self.get_sign() >= 0: res = intmask(x) if res < 0: raise OverflowError @@ -490,7 +497,7 @@ class rbigint(object): x = self._touint_helper() except OverflowError: return False - if self.sign >= 0: + if self.get_sign() >= 0: res = intmask(x) return res >= 0 else: @@ -502,11 +509,11 @@ class rbigint(object): return _AsLongLong(self) def tobool(self): - return self.sign != 0 + return self.get_sign() != 0 @jit.elidable def touint(self): - if self.sign == -1: + if self.get_sign() == -1: raise ValueError("cannot convert negative integer to unsigned int") return self._touint_helper() @@ -524,7 +531,7 @@ class rbigint(object): @jit.elidable def toulonglong(self): - if self.sign == -1: + if self.get_sign() == -1: raise ValueError("cannot convert negative integer to unsigned int") return _AsULonglong_ignore_sign(self) @@ -565,7 +572,7 @@ class rbigint(object): @jit.elidable def eq(self, other): - if (self.sign != other.sign or + if (self.get_sign() != other.get_sign() or self.numdigits() != other.numdigits()): return False @@ -587,7 +594,7 @@ class rbigint(object): if self.numdigits() > 1: return False - return (self.sign * self.digit(0)) == iother + return (self.get_sign() * self.digit(0)) == iother def ne(self, other): return not self.eq(other) @@ -597,19 +604,21 @@ class rbigint(object): @jit.elidable def lt(self, other): - if self.sign > other.sign: + selfsign = self.get_sign() + othersign = other.get_sign() + if selfsign > othersign: return False - if self.sign < other.sign: + if selfsign < othersign: return True ld1 = self.numdigits() ld2 = other.numdigits() if ld1 > ld2: - if other.sign > 0: + if othersign > 0: return False else: return True elif ld1 < ld2: - if other.sign > 0: + if othersign > 0: return True else: return False @@ -618,12 +627,12 @@ class rbigint(object): d1 = self.digit(i) d2 = other.digit(i) if d1 < d2: - if other.sign > 0: + if othersign > 0: return True else: return False elif d1 > d2: - if other.sign > 0: + if othersign > 0: return False else: return True @@ -670,86 +679,94 @@ class rbigint(object): @jit.elidable def add(self, other): - if self.sign == 0: + selfsign = self.get_sign() + othersign = other.get_sign() + if selfsign == 0: return other - if other.sign == 0: + if othersign == 0: return self - if self.sign == other.sign: + if selfsign == othersign: result = _x_add(self, other) else: result = _x_sub(other, self) - result.sign *= other.sign + result._set_sign(result.get_sign() * othersign) return result @jit.elidable def int_add(self, iother): + selfsign = self.get_sign() if not int_in_valid_range(iother): # Fallback to long. return self.add(rbigint.fromint(iother)) - elif self.sign == 0: + elif selfsign == 0: return rbigint.fromint(iother) elif iother == 0: return self - sign = intsign(iother) - if self.sign == sign: + othersign = intsign(iother) + if selfsign == othersign: result = _x_int_add(self, iother) else: result = _x_int_sub(self, iother) - result.sign *= -1 - result.sign *= sign + result._set_sign(-result.get_sign()) + result._set_sign(result.get_sign() * othersign) return result @jit.elidable def sub(self, other): - if other.sign == 0: + selfsign = self.get_sign() + othersign = other.get_sign() + if othersign == 0: return self - elif self.sign == 0: - return rbigint(other._digits[:other.numdigits()], -other.sign, other.numdigits()) - elif self.sign == other.sign: + elif selfsign == 0: + return rbigint(other._digits[:other.numdigits()], -othersign, other.numdigits()) + elif selfsign == othersign: result = _x_sub(self, other) else: result = _x_add(self, other) - result.sign *= self.sign + result._set_sign(result.get_sign() * selfsign) return result @jit.elidable def int_sub(self, iother): + selfsign = self.get_sign() if not int_in_valid_range(iother): # Fallback to long. return self.sub(rbigint.fromint(iother)) elif iother == 0: return self - elif self.sign == 0: + elif selfsign == 0: return rbigint.fromint(-iother) - elif self.sign == intsign(iother): + elif selfsign == intsign(iother): result = _x_int_sub(self, iother) else: result = _x_int_add(self, iother) - result.sign *= self.sign + result._set_sign(result.get_sign() * selfsign) return result @jit.elidable def mul(self, other): selfsize = self.numdigits() othersize = other.numdigits() + selfsign = self.get_sign() + othersign = other.get_sign() if selfsize > othersize: self, other, selfsize, othersize = other, self, othersize, selfsize - if self.sign == 0 or other.sign == 0: + if selfsign == 0 or othersign == 0: return NULLRBIGINT if selfsize == 1: if self._digits[0] == ONEDIGIT: - return rbigint(other._digits[:othersize], self.sign * other.sign, othersize) + return rbigint(other._digits[:othersize], selfsign * othersign, othersize) elif othersize == 1: res = other.uwidedigit(0) * self.udigit(0) carry = res >> SHIFT if carry: - return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * other.sign, 2) + return rbigint([_store_digit(res & MASK), _store_digit(carry)], selfsign * othersign, 2) else: - return rbigint([_store_digit(res & MASK)], self.sign * other.sign, 1) + return rbigint([_store_digit(res & MASK)], selfsign * othersign, 1) result = _x_mul(self, other, self.digit(0)) elif USE_KARATSUBA: @@ -765,7 +782,7 @@ class rbigint(object): else: result = _x_mul(self, other) - result.sign = self.sign * other.sign + result._set_sign(selfsign * othersign) return result @jit.elidable @@ -774,7 +791,8 @@ class rbigint(object): # Fallback to long. return self.mul(rbigint.fromint(iother)) - if self.sign == 0 or iother == 0: + selfsign = self.get_sign() + if selfsign == 0 or iother == 0: return NULLRBIGINT asize = self.numdigits() @@ -785,21 +803,21 @@ class rbigint(object): if digit == 1: if othersign == 1: return self - return rbigint(self._digits[:asize], self.sign * othersign, asize) + return rbigint(self._digits[:asize], selfsign * othersign, asize) elif asize == 1: udigit = r_uint(digit) res = self.uwidedigit(0) * udigit carry = res >> SHIFT if carry: - return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * othersign, 2) + return rbigint([_store_digit(res & MASK), _store_digit(carry)], selfsign * othersign, 2) else: - return rbigint([_store_digit(res & MASK)], self.sign * othersign, 1) + return rbigint([_store_digit(res & MASK)], selfsign * othersign, 1) elif digit & (digit - 1) == 0: result = self.lqshift(ptwotable[digit]) else: result = _muladd1(self, digit) - result.sign = self.sign * othersign + result._set_sign(selfsign * othersign) return result @jit.elidable @@ -826,7 +844,8 @@ class rbigint(object): digit = abs(iother) assert digit > 0 - if self.sign == 1 and iother > 0: + selfsign = self.get_sign() + if selfsign == 1 and iother > 0: if digit == 1: return self elif digit & (digit - 1) == 0: @@ -834,11 +853,12 @@ class rbigint(object): div, mod = _divrem1(self, digit) - if mod != 0 and self.sign * intsign(iother) == -1: - if div.sign == 0: + othersign = intsign(iother) + if mod != 0 and selfsign * othersign == -1: + if div.get_sign() == 0: return ONENEGATIVERBIGINT div = div.int_add(1) - div.sign = self.sign * intsign(iother) + div._set_sign(selfsign * othersign) div._normalize() return div @@ -853,7 +873,8 @@ class rbigint(object): def int_mod(self, iother): if iother == 0: raise ZeroDivisionError("long division or modulo by zero") - if self.sign == 0: + selfsign = self.get_sign() + if selfsign == 0: return NULLRBIGINT elif not int_in_valid_range(iother): @@ -875,17 +896,18 @@ class rbigint(object): rem = _int_rem_core(self, digit) if rem == 0: return NULLRBIGINT - mod = rbigint([rem], -1 if self.sign < 0 else 1, 1) + mod = rbigint([rem], -1 if selfsign < 0 else 1, 1) - if mod.sign * intsign(iother) == -1: + if mod.get_sign() * intsign(iother) == -1: mod = mod.int_add(iother) return mod @jit.elidable def int_mod_int_result(self, iother): + selfsign = self.get_sign() if iother == 0: raise ZeroDivisionError("long division or modulo by zero") - if self.sign == 0: + if selfsign == 0: return 0 elif not int_in_valid_range(iother): @@ -904,7 +926,7 @@ class rbigint(object): elif digit & (digit - 1) == 0: mod = self.int_and_(digit - 1).toint() # XXX improve else: - mod = _int_rem_core(self, digit) * self.sign + mod = _int_rem_core(self, digit) * selfsign if intsign(mod) * intsign(iother) == -1: mod = mod + iother return mod @@ -927,12 +949,14 @@ class rbigint(object): have different signs. We then subtract one from the 'div' part of the outcome to keep the invariant intact. """ - if other.sign == 0: + selfsign = self.get_sign() + othersign = other.get_sign() + if othersign == 0: raise ZeroDivisionError("long division or modulo by zero") - if self.sign == 0: + if selfsign == 0: return TWO_NULLRBIGINTS - if other.numdigits() == 1 and not (-1 == other.sign != self.sign): - otherint = other.digit(0) * other.sign + if other.numdigits() == 1 and not (-1 == othersign != selfsign): + otherint = other.digit(0) * othersign assert int_in_valid_range(otherint) return self.int_divmod(otherint) @@ -948,9 +972,9 @@ class rbigint(object): def _divmod_small(self, other): div, mod = _divrem(self, other) - if mod.sign * other.sign == -1: + if mod.get_sign() * other.get_sign() == -1: mod = mod.add(other) - if div.sign == 0: + if div.get_sign() == 0: return ONENEGATIVERBIGINT, mod div = div.int_sub(1) return div, mod @@ -962,8 +986,9 @@ class rbigint(object): if iother == 0: raise ZeroDivisionError("long division or modulo by zero") - wsign = intsign(iother) - if not int_in_valid_range(iother) or (wsign == -1 and self.sign != wsign): + selfsign = self.get_sign() + othersign = intsign(iother) + if not int_in_valid_range(iother) or (othersign == -1 and selfsign != othersign): # Just fallback. return self.divmod(rbigint.fromint(iother)) @@ -972,15 +997,15 @@ class rbigint(object): div, mod = _divrem1(self, digit) # _divrem1 doesn't fix the sign - if div.size == 1 and div._digits[0] == NULLDIGIT: - div.sign = 0 + if div._size == 0: + assert div.get_sign() == 0 else: - div.sign = self.sign * wsign - if self.sign < 0: + div._set_sign(selfsign * othersign) + if selfsign < 0: mod = -mod - if mod and self.sign * wsign == -1: + if mod and selfsign * othersign == -1: mod += iother - if div.sign == 0: + if div.get_sign() == 0: div = ONENEGATIVERBIGINT else: div = div.int_sub(1) @@ -989,13 +1014,15 @@ class rbigint(object): @jit.elidable def pow(self, other, modulus=None): + selfsign = self.get_sign() + othersign = other.get_sign() negativeOutput = False # if x<0 return negative output # 5-ary values. If the exponent is large enough, table is # precomputed so that table[i] == self**i % modulus for i in range(32). # python translation: the table is computed when needed. - if other.sign < 0: # if exponent is negative + if othersign < 0: # if exponent is negative if modulus is not None: raise TypeError( "pow() 2nd argument " @@ -1005,10 +1032,11 @@ class rbigint(object): size_b = UDIGIT_TYPE(other.numdigits()) if modulus is not None: - if modulus.sign == 0: + modulussign = modulus.get_sign() + if modulussign == 0: raise ValueError("pow() 3rd argument cannot be 0") - if modulus.sign < 0: + if modulussign < 0: negativeOutput = True modulus = modulus.neg() @@ -1026,11 +1054,11 @@ class rbigint(object): # base % modulus instead. # We could _always_ do this reduction, but mod() isn't cheap, # so we only do it when it buys something. - if self.sign < 0 or self.numdigits() > modulus.numdigits(): + if selfsign < 0 or self.numdigits() > modulus.numdigits(): self = self.mod(modulus) - elif other.sign == 0: + elif othersign == 0: return ONERBIGINT - elif self.sign == 0: + elif selfsign == 0: return NULLRBIGINT elif size_b == 1: if other._digits[0] == ONEDIGIT: @@ -1039,13 +1067,13 @@ class rbigint(object): adigit = self.digit(0) digit = other.digit(0) if adigit == 1: - if self.sign == -1 and digit % 2: + if selfsign == -1 and digit % 2: return ONENEGATIVERBIGINT return ONERBIGINT elif adigit & (adigit - 1) == 0: ret = self.lshift(((digit-1)*(ptwotable[adigit]-1)) + digit-1) - if self.sign == -1 and not digit % 2: - ret.sign = 1 + if selfsign == -1 and not digit % 2: + ret._set_sign(1) return ret # At this point self, other, and modulus are guaranteed non-negative UNLESS @@ -1117,7 +1145,7 @@ class rbigint(object): # assert j == -5 - if negativeOutput and z.sign != 0: + if negativeOutput and z.get_sign() != 0: z = z.sub(modulus) return z @@ -1136,12 +1164,14 @@ class rbigint(object): "cannot be negative when 3rd argument specified") raise ValueError("bigint pow() too negative") + selfsign = self.get_sign() assert iother >= 0 if modulus is not None: - if modulus.sign == 0: + modulussign = modulus.get_sign() + if modulussign == 0: raise ValueError("pow() 3rd argument cannot be 0") - if modulus.sign < 0: + if modulussign < 0: negativeOutput = True modulus = modulus.neg() @@ -1159,24 +1189,24 @@ class rbigint(object): # base % modulus instead. # We could _always_ do this reduction, but mod() isn't cheap, # so we only do it when it buys something. - if self.sign < 0 or self.numdigits() > modulus.numdigits(): + if selfsign < 0 or self.numdigits() > modulus.numdigits(): self = self.mod(modulus) elif iother == 0: return ONERBIGINT - elif self.sign == 0: + elif selfsign == 0: return NULLRBIGINT elif iother == 1: return self elif self.numdigits() == 1: adigit = self.digit(0) if adigit == 1: - if self.sign == -1 and iother % 2: + if selfsign == -1 and iother % 2: return ONENEGATIVERBIGINT return ONERBIGINT elif adigit & (adigit - 1) == 0: ret = self.lshift(((iother-1)*(ptwotable[adigit]-1)) + iother-1) - if self.sign == -1 and not iother % 2: - ret.sign = 1 + if selfsign == -1 and not iother % 2: + ret._set_sign(1) return ret # At this point self, iother, and modulus are guaranteed non-negative UNLESS @@ -1194,34 +1224,36 @@ class rbigint(object): z = _help_mult(z, self, modulus) j >>= 1 - if negativeOutput and z.sign != 0: + if negativeOutput and z.get_sign() != 0: z = z.sub(modulus) return z @jit.elidable def neg(self): - return rbigint(self._digits, -self.sign, self.numdigits()) + return rbigint(self._digits, -self.get_sign(), self.numdigits()) @jit.elidable def abs(self): - if self.sign != -1: + selfsign = self.get_sign() + if selfsign != -1: return self - return rbigint(self._digits, 1, self.numdigits()) + return rbigint(self._digits, abs(selfsign), self.numdigits()) @jit.elidable def invert(self): #Implement ~x as -(x + 1) - if self.sign == 0: + if self.get_sign() == 0: return ONENEGATIVERBIGINT ret = self.int_add(1) - ret.sign = -ret.sign + ret._set_sign(-ret.get_sign()) return ret @jit.elidable def lshift(self, int_other): + selfsign = self.get_sign() if int_other < 0: raise ValueError("negative shift count") - elif int_other == 0 or self.sign == 0: + elif int_other == 0 or selfsign == 0: return self # wordshift, remshift = divmod(int_other, SHIFT) @@ -1230,11 +1262,11 @@ class rbigint(object): if not remshift: # So we can avoid problems with eq, AND avoid the need for normalize. - return rbigint([NULLDIGIT] * wordshift + self._digits, self.sign, self.numdigits() + wordshift) + return rbigint([NULLDIGIT] * wordshift + self._digits, selfsign, self.numdigits() + wordshift) oldsize = self.numdigits() newsize = oldsize + wordshift + 1 - z = rbigint([NULLDIGIT] * newsize, self.sign, newsize) + z = rbigint([NULLDIGIT] * newsize, selfsign, newsize) accum = _unsigned_widen_digit(0) j = 0 while j < oldsize: @@ -1258,8 +1290,9 @@ class rbigint(object): assert int_other > 0 oldsize = self.numdigits() + selfsign = self.get_sign() - z = rbigint([NULLDIGIT] * (oldsize + 1), self.sign, (oldsize + 1)) + z = rbigint([NULLDIGIT] * (oldsize + 1), selfsign, (oldsize + 1)) accum = _unsigned_widen_digit(0) i = 0 while i < oldsize: @@ -1278,7 +1311,8 @@ class rbigint(object): raise ValueError("negative shift count") elif int_other == 0: return self - if self.sign == -1 and not dont_invert: + selfsign = self.get_sign() + if selfsign == -1 and not dont_invert: a = self.invert().rshift(int_other) return a.invert() @@ -1289,7 +1323,7 @@ class rbigint(object): loshift = int_other % SHIFT hishift = SHIFT - loshift - z = rbigint([NULLDIGIT] * newsize, self.sign, newsize) + z = rbigint([NULLDIGIT] * newsize, selfsign, newsize) i = 0 while i < newsize: newdigit = (self.digit(wordshift) >> loshift) @@ -1312,7 +1346,8 @@ class rbigint(object): return NULLRBIGINT hishift = SHIFT - loshift - z = rbigint([NULLDIGIT] * newsize, self.sign, newsize) + selfsign = self.get_sign() + z = rbigint([NULLDIGIT] * newsize, selfsign, newsize) i = 0 while i < newsize: @@ -1407,7 +1442,7 @@ class rbigint(object): @jit.elidable def oct(self): - if self.sign == 0: + if self.get_sign() == 0: return '0L' else: return _format(self, BASE8, '0', 'L') @@ -1437,9 +1472,9 @@ class rbigint(object): for d in digits: l = l << SHIFT l += intmask(d) - result = l * self.sign + result = l * self.get_sign() if result == 0: - assert self.sign == 0 + assert self.get_sign() == 0 return result def _normalize(self): @@ -1449,9 +1484,9 @@ class rbigint(object): i -= 1 assert i > 0 - self.size = i + self._size = i * self.get_sign() if i == 1 and self._digits[0] == NULLDIGIT: - self.sign = 0 + self._size = 0 self._digits = NULLDIGITS _normalize._always_inline_ = True @@ -1480,10 +1515,25 @@ class rbigint(object): other """ return gcd_lehmer(self.abs(), other.abs()) + def isqrt(self): + """ Compute the integer square root of self """ + if self.int_lt(0): + raise ValueError("isqrt() argument must be nonnegative") + if self.int_eq(0): + return NULLRBIGINT + c = (self.bit_length() - 1) // 2 + a = ONERBIGINT + d = 0 + for s in range(bits_in_digit(_store_digit(c)) - 1, -1, -1): + # Loop invariant: (a-1)**2 < (self >> 2*(c - d)) < (a+1)**2 + e = d + d = c >> s + a = a.lshift(d - e - 1).add(self.rshift(2*c - e - d + 1).floordiv(a)) + return a.int_sub(a.mul(a).gt(self)) def __repr__(self): return "<rbigint digits=%s, sign=%s, size=%d, len=%d, %s>" % (self._digits, - self.sign, self.numdigits(), len(self._digits), + self.get_sign(), self.numdigits(), len(self._digits), self.tolong()) ONERBIGINT = rbigint([ONEDIGIT], 1, 1) @@ -1896,14 +1946,14 @@ def _k_mul(a, b): # 2. t1 <- ah*bh, and copy into high digits of result. t1 = ah.mul(bh) - assert t1.sign >= 0 + assert t1.get_sign() >= 0 assert 2*shift + t1.numdigits() <= ret.numdigits() for i in range(t1.numdigits()): ret._digits[2*shift + i] = t1._digits[i] # 3. t2 <- al*bl, and copy into the low digits. t2 = al.mul(bl) - assert t2.sign >= 0 + assert t2.get_sign() >= 0 assert t2.numdigits() <= 2*shift # no overlap with high digits for i in range(t2.numdigits()): ret._digits[i] = t2._digits[i] @@ -1923,7 +1973,7 @@ def _k_mul(a, b): t2 = _x_add(bh, bl) t3 = t1.mul(t2) - assert t3.sign >= 0 + assert t3.get_sign() >= 0 # Add t3. It's not obvious why we can't run out of room here. # See the (*) comment after this function. @@ -2234,7 +2284,7 @@ def _divrem(a, b): size_a = a.numdigits() size_b = b.numdigits() - if b.sign == 0: + if b.get_sign() == 0: raise ZeroDivisionError("long division or modulo by zero") if (size_a < size_b or @@ -2251,10 +2301,10 @@ def _divrem(a, b): # The quotient z has the sign of a*b; # the remainder r has the sign of a, # so a = b*z + r. - if a.sign != b.sign: - z.sign = - z.sign - if a.sign < 0 and rem.sign != 0: - rem.sign = - rem.sign + if a.get_sign() != b.get_sign(): + z._set_sign(-z.get_sign()) + if a.get_sign() < 0 and rem.get_sign() != 0: + rem._set_sign(-rem.get_sign()) return z, rem @@ -2298,7 +2348,7 @@ def div2n1n(a_container, a_startindex, b, n_S): """ if n_S <= HOLDER.DIV_LIMIT: a = _extract_digits(a_container, a_startindex, 2 * n_S) - if a.sign == 0: + if a.get_sign() == 0: return NULLRBIGINT, NULLRBIGINT res = _divrem(a, b) return res @@ -2334,7 +2384,7 @@ def div3n2n(a12_container, a12_startindex, a3_container, a3_startindex, b, b1, b """Helper function for div2n1n; not intended to be called directly.""" q, r = div2n1n(a12_container, a12_startindex, b1, n_S) # equivalent to r = _full_digits_lshift_then_or(r, n_S, _extract_digits(a_container, a3_startindex, n_S)) - if r.sign == 0: + if r.get_sign() == 0: r = _extract_digits(a3_container, a3_startindex, n_S) else: digits = [NULLDIGIT] * (n_S + r.numdigits()) @@ -2348,12 +2398,12 @@ def div3n2n(a12_container, a12_startindex, a3_container, a3_startindex, b, b1, b index += 1 r = rbigint(digits, 1) r._normalize() - if q.sign == 0: + if q.get_sign() == 0: return q, r r = r.sub(q.mul(b2)) # loop runs at most twice - while r.sign < 0: + while r.get_sign() < 0: q = q.int_sub(1) r = r.add(b) return q, r @@ -2362,7 +2412,7 @@ def _full_digits_lshift_then_or(a, n, b): """ equivalent to a.lshift(n * SHIFT).or_(b) the size of b must be smaller than n """ - if a.sign == 0: + if a.get_sign() == 0: return b bdigits = b.numdigits() assert bdigits <= n @@ -2440,13 +2490,13 @@ def divmod_big(a, b): # follows cr.yp.to/bib/1998/burnikel.ps if b.eq(NULLRBIGINT): raise ZeroDivisionError - elif b.sign < 0: + elif b.get_sign() < 0: q, r = divmod_big(a.neg(), b.neg()) return q, r.neg() - elif a.sign < 0: + elif a.get_sign() < 0: q, r = divmod_big(a.invert(), b) return q.invert(), b.add(r.invert()) - elif a.sign == 0: + elif a.get_sign() == 0: return TWO_NULLRBIGINTS else: return _divmod_fast_pos(a, b) @@ -2459,9 +2509,9 @@ def _x_int_lt(a, b, eq=False): elif b < 0: osign = -1 - if a.sign > osign: + if a.get_sign() > osign: return False - elif a.sign < osign: + elif a.get_sign() < osign: return True digits = a.numdigits() @@ -2472,7 +2522,7 @@ def _x_int_lt(a, b, eq=False): else: return True - d1 = a.sign * a.digit(0) + d1 = a.get_sign() * a.digit(0) if eq: if d1 <= b: return True @@ -2496,10 +2546,10 @@ def _AsScaledDouble(v): least one round bit to stand in for the ignored least-significant bits. """ NBITS_WANTED = 57 - if v.sign == 0: + if v.get_sign() == 0: return 0.0, 0 i = v.numdigits() - 1 - sign = v.sign + sign = v.get_sign() x = float(v.digit(i)) nbitsneeded = NBITS_WANTED - 1 # Invariant: i Python digits remain unaccounted for. @@ -2540,7 +2590,7 @@ def _AsDouble(n): assert DBL_MANT_DIG < r_ulonglong.BITS # Reduce to case n positive. - sign = n.sign + sign = n.get_sign() if sign == 0: return 0.0 elif sign < 0: @@ -2654,7 +2704,7 @@ def _bigint_true_divide(a, b): MANT_DIG_BITS = DBL_MANT_DIG % SHIFT # Reduce to case where a and b are both positive. - negate = (a.sign < 0) ^ (b.sign < 0) + negate = (a.get_sign() < 0) ^ (b.get_sign() < 0) if not b.tobool(): raise ZeroDivisionError("long division or modulo by zero") if not a.tobool(): @@ -2810,7 +2860,7 @@ def _format_base2_notzero(a, digits, prefix='', suffix='', max_str_digits=0): j -= 1 result[next_char_index] = prefix[j] - if a.sign < 0: + if a.get_sign() < 0: next_char_index -= 1 result[next_char_index] = '-' @@ -2871,7 +2921,7 @@ def _format_recursive(x, i, output, pts, digits, size_prefix, mindigits, _format if i < 0: # this checks whether any digit has been appended yet if curlen == size_prefix: - if x.sign != 0: + if x.get_sign() != 0: s = _format_int(x.toint(), digits) output.append(s) else: @@ -2887,14 +2937,14 @@ def _format_recursive(x, i, output, pts, digits, size_prefix, mindigits, _format _format_recursive(bot, i-1, output, pts, digits, size_prefix, mindigits, _format_int, max_str_digits) def _format(x, digits, prefix='', suffix='', max_str_digits=0): - if x.sign == 0: + if x.get_sign() == 0: return prefix + "0" + suffix base = len(digits) assert base >= 2 and base <= 36 if (base & (base - 1)) == 0: # base is 2, 4, 8, 16, ... return _format_base2_notzero(x, digits, prefix, suffix, max_str_digits) - negative = x.sign < 0 + negative = x.get_sign() < 0 if negative: x = x.neg() rbase = rbigint.fromint(base) @@ -2940,12 +2990,12 @@ def _format(x, digits, prefix='', suffix='', max_str_digits=0): def _bitwise(a, op, b): # '&', '|', '^' """ Bitwise and/or/xor operations """ - if a.sign < 0: + if a.get_sign() < 0: a = a.invert() maska = MASK else: maska = 0 - if b.sign < 0: + if b.get_sign() < 0: b = b.invert() maskb = MASK else: @@ -3027,7 +3077,7 @@ def _int_bitwise(a, op, b): # '&', '|', '^' # Fallback to long. return _bitwise(a, op, rbigint.fromint(b)) - if a.sign < 0: + if a.get_sign() < 0: a = a.invert() maska = MASK else: @@ -3114,13 +3164,13 @@ def _AsLongLong(v): x = _AsULonglong_ignore_sign(v) # grr grr grr if x >= ULONGLONG_BOUND: - if x == ULONGLONG_BOUND and v.sign < 0: + if x == ULONGLONG_BOUND and v.get_sign() < 0: x = LONGLONG_MIN else: raise OverflowError else: x = r_longlong(x) - if v.sign < 0: + if v.get_sign() < 0: x = -x return x @@ -3143,7 +3193,7 @@ def make_unsigned_mask_conversion(T): while i >= 0: x = (x << SHIFT) + T(v.digit(i)) i -= 1 - if v.sign < 0: + if v.get_sign() < 0: x = -x return x return _As_unsigned_mask @@ -3159,7 +3209,7 @@ def _hash(v): # hash(x) == hash(x % ULONG_MAX). In particular, this # implies that hash(x) == hash(x % (2**64-1)). i = v.numdigits() - 1 - sign = v.sign + sign = v.get_sign() x = r_uint(0) LONG_BIT_SHIFT = LONG_BIT - SHIFT while i >= 0: @@ -3219,8 +3269,8 @@ def _decimalstr_to_bigint(s, start=0, lim=-1): a = rbigint([_store_digit(dig)], int(dig != 0)) tens = 1 dig = 0 - if sign and a.sign == 1: - a.sign = -1 + if sign and a.get_sign() == 1: + a._set_sign(-1) return a def parse_digit_string(parser): @@ -3232,7 +3282,7 @@ def parse_digit_string(parser): # check for errors and potentially remove underscores s, start, end = parser._all_digits10() a = _str_to_int_big_base10(s, start, end, HOLDER.STR2INT_LIMIT) - a.sign *= parser.sign + a._set_sign(a.get_sign() * parser.sign) return a a = NULLRBIGINT digitmax = BASE_MAX[base] @@ -3252,7 +3302,7 @@ def parse_digit_string(parser): else: dig = dig * base + digit baseexp *= base - a.sign *= parser.sign + a._set_sign(a.get_sign() * parser.sign) return a @@ -3407,8 +3457,8 @@ def gcd_lehmer(a, b): if a.lt(b): a, b = b, a - while b.size > 1: - a_ms = a.digit(abs(a.size-1)) + while b.numdigits() > 1: + a_ms = a.digit(abs(a.numdigits()-1)) x = 0 while a_ms & (0xFF << SHIFT-8) == 0: @@ -3419,12 +3469,12 @@ def gcd_lehmer(a, b): a_ms <<= 1 x += 1 - a_ms |= a.digit(abs(a.size-2)) >> SHIFT-x + a_ms |= a.digit(abs(a.numdigits()-2)) >> SHIFT-x - if a.size == b.size: - b_ms = (b.digit(abs(b.size-1)) << x) | (b.digit(abs(b.size-2)) >> SHIFT-x) - elif a.size == b.size+1: - b_ms = b.digit(abs(b.size-1)) >> SHIFT-x + if a.numdigits() == b.numdigits(): + b_ms = (b.digit(abs(b.numdigits()-1)) << x) | (b.digit(abs(b.numdigits()-2)) >> SHIFT-x) + elif a.numdigits() == b.numdigits()+1: + b_ms = b.digit(abs(b.numdigits()-1)) >> SHIFT-x else: b_ms = 0 diff --git a/rpython/rlib/test/test_rbigint.py b/rpython/rlib/test/test_rbigint.py index 828cbefc18..35bce60793 100644 --- a/rpython/rlib/test/test_rbigint.py +++ b/rpython/rlib/test/test_rbigint.py @@ -835,8 +835,11 @@ class Test_rbigint(object): def test_normalize(self): f1 = bigint([1, 0], 1) f1._normalize() - assert f1.size == 1 + assert f1.numdigits() == 1 f0 = bigint([0], 0) + f0._normalize() + assert f0.numdigits() == 1 + assert f0._size == 0 assert f1.sub(f1).eq(f0) def test_invert(self): @@ -1401,6 +1404,15 @@ class TestTranslatable(object): res = interpret(fn, []) assert res == -42.0 + def test_isqrt(self): + def fn(x): + num = rbigint.fromint(3).int_pow(x) + return num.mul(num).isqrt().eq(num) + + + res = interpret(fn, [100]) + assert res == True + class TestTranslated(StandaloneTests): @@ -1807,6 +1819,21 @@ class TestHypothesis(object): li = rbigint.fromrarith_int(i) assert li.tolong() == int(i) + @given(biglongs) + @example(3**100) + def test_isqrt(self, a): + a = abs(a) + la = rbigint.fromlong(a) + lsq = la.isqrt() + sq = lsq.tolong() + assert sq * sq <= a + assert (sq + 1) ** 2 > a + + x = a * a + lx = rbigint.fromlong(x) + assert lx.isqrt().tolong() == a + + @pytest.mark.parametrize(['methname'], [(methodname, ) for methodname in dir(TestHypothesis) if methodname.startswith("test_")]) def test_hypothesis_small_shift(methname): diff --git a/rpython/rlib/test/test_rsocket.py b/rpython/rlib/test/test_rsocket.py index d547b6219d..2b1ad80b4e 100644 --- a/rpython/rlib/test/test_rsocket.py +++ b/rpython/rlib/test/test_rsocket.py @@ -423,7 +423,8 @@ def getaddrinfo_pydotorg(i, result): for family, socktype, protocol, canonname, addr in lst: if addr.get_host() in ('138.197.63.241', '104.130.43.121', '23.253.135.79', '45.55.99.72', - '151.101.129.168', '151.101.193.168'): + '151.101.129.168', '151.101.193.168', + '151.101.64.223'): found = True elif family == AF_INET: print 'pydotorg changed to', addr.get_host() diff --git a/rpython/rtyper/lltypesystem/lltype.py b/rpython/rtyper/lltypesystem/lltype.py index c75fd90e7a..5871d3a321 100644 --- a/rpython/rtyper/lltypesystem/lltype.py +++ b/rpython/rtyper/lltypesystem/lltype.py @@ -924,14 +924,6 @@ def _cast_whatever(TGT, value): raise TypeError("don't know how to cast from %r to %r" % (ORIG, TGT)) -def erasedType(T): - while isinstance(T, Ptr) and isinstance(T.TO, Struct): - first, FIRSTTYPE = T.TO._first_struct() - if first is None: - break - T = Ptr(FIRSTTYPE) - return T - class InvalidCast(TypeError): pass @@ -1003,6 +995,8 @@ def cast_opaque_ptr(PTRTYPE, ptr): except AttributeError: raise InvalidCast("%r does not come from a container" % (ptr,)) solid = getattr(ptr._obj, 'solid', False) + if isinstance(container, int): # tagged int + return _ptr(PTRTYPE, container, solid=True) p = _ptr(Ptr(typeOf(container)), container, solid) return cast_pointer(PTRTYPE, p) elif (not isinstance(CURTYPE.TO, OpaqueType) diff --git a/rpython/rtyper/lltypesystem/rdict.py b/rpython/rtyper/lltypesystem/rdict.py index dc45981d56..52b63092d9 100644 --- a/rpython/rtyper/lltypesystem/rdict.py +++ b/rpython/rtyper/lltypesystem/rdict.py @@ -1,7 +1,7 @@ from rpython.tool.pairtype import pairtype from rpython.flowspace.model import Constant from rpython.rtyper.rdict import AbstractDictRepr, AbstractDictIteratorRepr -from rpython.rtyper.lltypesystem import lltype +from rpython.rtyper.lltypesystem import lltype, llmemory from rpython.rtyper.lltypesystem.lloperation import llop from rpython.rlib import objectmodel, jit from rpython.rtyper.debug import ll_assert @@ -212,7 +212,6 @@ class DictRepr(AbstractDictRepr): def convert_const(self, dictobj): - from rpython.rtyper.lltypesystem import llmemory # get object from bound dict methods #dictobj = getattr(dictobj, '__self__', dictobj) if dictobj is None: @@ -823,29 +822,32 @@ def ll_prepare_dict_update(d, num_extra): # and very efficient functions are created. def recast(P, v): + # XXX this function is a terrible hack + if P is llmemory.GCREF: + return lltype.cast_opaque_ptr(llmemory.GCREF, v) if isinstance(P, lltype.Ptr): return lltype.cast_pointer(P, v) else: return v def _make_ll_keys_values_items(kind): - def ll_kvi(LIST, dic): + def ll_kvi(LIST, dic, EXTERNAL_ELEM=None): res = LIST.ll_newlist(dic.num_items) entries = dic.entries dlen = len(entries) items = res.ll_items() + ELEM = lltype.typeOf(items).TO.OF i = 0 p = 0 while i < dlen: if entries.valid(i): - ELEM = lltype.typeOf(items).TO.OF if ELEM is not lltype.Void: entry = entries[i] if kind == 'items': - r = lltype.malloc(ELEM.TO) - r.item0 = recast(ELEM.TO.item0, entry.key) - r.item1 = recast(ELEM.TO.item1, entry.value) - items[p] = r + r = lltype.malloc(EXTERNAL_ELEM.TO) + r.item0 = recast(EXTERNAL_ELEM.TO.item0, entry.key) + r.item1 = recast(EXTERNAL_ELEM.TO.item1, entry.value) + items[p] = recast(ELEM, r) elif kind == 'keys': items[p] = recast(ELEM, entry.key) elif kind == 'values': diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py index 84160071af..d3851201a6 100644 --- a/rpython/rtyper/lltypesystem/rffi.py +++ b/rpython/rtyper/lltypesystem/rffi.py @@ -1073,7 +1073,7 @@ def str2constcharp(s, track_allocation=True): """ cp = str2charp(s, track_allocation) return cast(CONST_CCHARP, cp) -str2constcharp._annenforceargs_ = [str] +str2constcharp._annenforceargs_ = [str, bool] @not_rpython def _deprecated_get_nonmovingbuffer(*args): diff --git a/rpython/rtyper/lltypesystem/rgcref.py b/rpython/rtyper/lltypesystem/rgcref.py new file mode 100644 index 0000000000..f1d698af8c --- /dev/null +++ b/rpython/rtyper/lltypesystem/rgcref.py @@ -0,0 +1,106 @@ +from rpython.rtyper.rmodel import Repr +from rpython.rtyper.lltypesystem import lltype, llmemory + +from rpython.tool.pairtype import pairtype, extendabletype, pair + +UNKNOWN = object() + +class GCRefRepr(Repr): + lowleveltype = llmemory.GCREF + + @staticmethod + def make(r_base, cache): + try: + return cache[r_base] + except KeyError: + res = cache[r_base] = GCRefRepr(r_base) + return res + + + def __init__(self, r_base): + self.r_base = r_base + self._ll_eq_func = UNKNOWN + self._ll_hash_func = UNKNOWN + if hasattr(r_base, 'll_str'): + ll_base_str = r_base.ll_str + def ll_str(ptr): + return ll_base_str(lltype.cast_opaque_ptr(r_base.lowleveltype, ptr)) + self.ll_str = ll_str + + def convert_const(self, x): + return lltype.cast_opaque_ptr(llmemory.GCREF, self.r_base.convert_const(x)) + + def get_ll_eq_function(self): + if self._ll_eq_func is UNKNOWN: + ll_base_eq_function = self.r_base.get_ll_eq_function() + if ll_base_eq_function is None: + ll_eq_func = None + else: + def ll_eq_func(ptr1, ptr2): + ptr1 = lltype.cast_opaque_ptr(self.r_base.lowleveltype, ptr1) + ptr2 = lltype.cast_opaque_ptr(self.r_base.lowleveltype, ptr2) + return ll_base_eq_function(ptr1, ptr2) + self._ll_eq_func = ll_eq_func + return self._ll_eq_func + + def get_ll_hash_function(self): + if self._ll_hash_func is UNKNOWN: + ll_base_hash_function = self.r_base.get_ll_hash_function() + if ll_base_hash_function is None: + ll_hash_func = None + else: + def ll_hash_func(ptr): + ptr = lltype.cast_opaque_ptr(self.r_base.lowleveltype, ptr) + return ll_base_hash_function(ptr) + self._ll_hash_func = ll_hash_func + return self._ll_hash_func + + def get_ll_dummyval_obj(self, rtyper, s_value): + return DummyValueBuilderGCRef(rtyper) + +class __extend__(pairtype(GCRefRepr, Repr)): + def convert_from_to((r_from, r_to), v, llops): + if isinstance(r_to.lowleveltype, lltype.Ptr) and r_to.lowleveltype.TO._gckind == 'gc': + return llops.genop('cast_opaque_ptr', [v], r_to.lowleveltype) + return NotImplemented + +class __extend__(pairtype(Repr, GCRefRepr)): + def convert_from_to((r_from, r_to), v, llops): + if r_from != r_to.r_base: + v = pair(r_from, r_to.r_base).convert_from_to(v, llops) + return llops.genop('cast_opaque_ptr', [v], r_to.lowleveltype) + + +class DummyValueBuilderGCRef(object): + + def __init__(self, rtyper): + self.rtyper = rtyper + + def _freeze_(self): + return True + + def __hash__(self): + return hash(llmemory.GCREF) + + def __eq__(self, other): + return (isinstance(other, DummyValueBuilderGCRef) and + self.rtyper is other.rtyper) + + def __ne__(self, other): + return not (self == other) + + @property + def ll_dummy_value(self): + try: + return self.rtyper.cache_dummy_values[llmemory.GCREF] + except KeyError: + from rpython.rtyper import rclass + from rpython.rtyper.rmodel import DummyValueBuilder + rinstbase = rclass.getinstancerepr(self.rtyper, None) + TYPE = rinstbase.lowleveltype + val = DummyValueBuilder(self.rtyper, TYPE.TO).ll_dummy_value + p = lltype.cast_opaque_ptr(llmemory.GCREF, val) + self.rtyper.cache_dummy_values[llmemory.GCREF] = p + return p + + diff --git a/rpython/rtyper/lltypesystem/rlist.py b/rpython/rtyper/lltypesystem/rlist.py index 00f316c9c7..676a2c275a 100644 --- a/rpython/rtyper/lltypesystem/rlist.py +++ b/rpython/rtyper/lltypesystem/rlist.py @@ -60,7 +60,7 @@ class BaseListRepr(AbstractBaseListRepr): assert callable(item_repr) self._item_repr_computer = item_repr else: - self.external_item_repr, self.item_repr = externalvsinternal(rtyper, item_repr) + self.external_item_repr, self.item_repr = externalvsinternal(rtyper, item_repr, gcref=True) self.listitem = listitem self.list_cache = {} # setup() needs to be called to finish this initialization @@ -108,7 +108,7 @@ class ListRepr(AbstractListRepr, BaseListRepr): def _setup_repr(self): if 'item_repr' not in self.__dict__: - self.external_item_repr, self.item_repr = externalvsinternal(self.rtyper, self._item_repr_computer()) + self.external_item_repr, self.item_repr = externalvsinternal(self.rtyper, self._item_repr_computer(), gcref=True) if isinstance(self.LIST, GcForwardReference): ITEM = self.item_repr.lowleveltype ITEMARRAY = self.get_itemarray_lowleveltype() @@ -174,7 +174,7 @@ class FixedSizeListRepr(AbstractFixedSizeListRepr, BaseListRepr): def _setup_repr(self): if 'item_repr' not in self.__dict__: - self.external_item_repr, self.item_repr = externalvsinternal(self.rtyper, self._item_repr_computer()) + self.external_item_repr, self.item_repr = externalvsinternal(self.rtyper, self._item_repr_computer(), gcref=True) if isinstance(self.LIST, GcForwardReference): ITEMARRAY = self.get_itemarray_lowleveltype() self.LIST.become(ITEMARRAY) diff --git a/rpython/rtyper/lltypesystem/rordereddict.py b/rpython/rtyper/lltypesystem/rordereddict.py index 977c60fd48..045760670d 100644 --- a/rpython/rtyper/lltypesystem/rordereddict.py +++ b/rpython/rtyper/lltypesystem/rordereddict.py @@ -202,7 +202,7 @@ class OrderedDictRepr(AbstractDictRepr): key_repr = self._key_repr_computer() self.external_key_repr, self.key_repr = self.pickkeyrepr(key_repr) if 'value_repr' not in self.__dict__: - self.external_value_repr, self.value_repr = self.pickrepr(self._value_repr_computer()) + self.external_value_repr, self.value_repr = rmodel.externalvsinternal(self.rtyper, self._value_repr_computer(), gcref=True) if isinstance(self.DICT, lltype.GcForwardReference): DICTKEY = self.key_repr.lowleveltype DICTVALUE = self.value_repr.lowleveltype @@ -315,12 +315,16 @@ class OrderedDictRepr(AbstractDictRepr): hop.exception_cannot_occur() hop.gendirectcall(ll_prepare_dict_update, v_dict, v_num) - def _rtype_method_kvi(self, hop, ll_func): + def _rtype_method_kvi(self, hop, ll_func, extraarg=None): v_dic, = hop.inputargs(self) r_list = hop.r_result cLIST = hop.inputconst(lltype.Void, r_list.lowleveltype.TO) hop.exception_cannot_occur() - return hop.gendirectcall(ll_func, cLIST, v_dic) + if extraarg: + c_extraarg = hop.inputconst(lltype.Void, extraarg) + else: + c_extraarg = hop.inputconst(lltype.Void, None) + return hop.gendirectcall(ll_func, cLIST, v_dic, c_extraarg) def rtype_method_keys(self, hop): return self._rtype_method_kvi(hop, ll_dict_keys) @@ -329,7 +333,8 @@ class OrderedDictRepr(AbstractDictRepr): return self._rtype_method_kvi(hop, ll_dict_values) def rtype_method_items(self, hop): - return self._rtype_method_kvi(hop, ll_dict_items) + EXTERNAL_ELEM = hop.r_result.external_item_repr.lowleveltype + return self._rtype_method_kvi(hop, ll_dict_items, EXTERNAL_ELEM) def rtype_bltn_list(self, hop): return self._rtype_method_kvi(hop, ll_dict_keys) @@ -1397,29 +1402,34 @@ def ll_prepare_dict_update(d, num_extra): # and very efficient functions are created. def recast(P, v): + # XXX this function is a terrible hack + if P is llmemory.GCREF: + return lltype.cast_opaque_ptr(llmemory.GCREF, v) + if lltype.typeOf(v) is llmemory.GCREF: + return lltype.cast_opaque_ptr(P, v) if isinstance(P, lltype.Ptr): return lltype.cast_pointer(P, v) else: return v def _make_ll_keys_values_items(kind): - def ll_kvi(LIST, dic): + def ll_kvi(LIST, dic, EXTERNAL_ELEM=None): res = LIST.ll_newlist(dic.num_live_items) entries = dic.entries dlen = dic.num_ever_used_items items = res.ll_items() + ELEM = lltype.typeOf(items).TO.OF i = 0 p = 0 while i < dlen: if entries.valid(i): - ELEM = lltype.typeOf(items).TO.OF if ELEM is not lltype.Void: entry = entries[i] if kind == 'items': - r = lltype.malloc(ELEM.TO) - r.item0 = recast(ELEM.TO.item0, entry.key) - r.item1 = recast(ELEM.TO.item1, entry.value) - items[p] = r + r = lltype.malloc(EXTERNAL_ELEM.TO) + r.item0 = recast(EXTERNAL_ELEM.TO.item0, entry.key) + r.item1 = recast(EXTERNAL_ELEM.TO.item1, entry.value) + items[p] = recast(ELEM, r) elif kind == 'keys': items[p] = recast(ELEM, entry.key) elif kind == 'values': @@ -1428,7 +1438,8 @@ def _make_ll_keys_values_items(kind): i += 1 assert p == res.ll_length() return res - ll_kvi.oopspec = 'odict.%s(dic)' % kind + if kind != "items": + ll_kvi.oopspec = 'odict.%s(dic)' % kind return ll_kvi ll_dict_keys = _make_ll_keys_values_items('keys') diff --git a/rpython/rtyper/lltypesystem/rstr.py b/rpython/rtyper/lltypesystem/rstr.py index 40bfc6c32b..aa6633b23c 100644 --- a/rpython/rtyper/lltypesystem/rstr.py +++ b/rpython/rtyper/lltypesystem/rstr.py @@ -539,6 +539,7 @@ class LLHelpers(AbstractLLHelpers): @staticmethod def ll_join(s, length, items): s_chars = s.chars + TYP = typeOf(s) s_len = len(s_chars) num_items = length if num_items == 0: @@ -547,7 +548,7 @@ class LLHelpers(AbstractLLHelpers): i = 0 while i < num_items: try: - itemslen = ovfcheck(itemslen + len(items[i].chars)) + itemslen = ovfcheck(itemslen + len(llmemory.cast_any_ptr(TYP, items[i]).chars)) except OverflowError: raise MemoryError i += 1 @@ -558,14 +559,16 @@ class LLHelpers(AbstractLLHelpers): # a single '+' at the end is allowed to overflow: it gets # a negative result, and the gc will complain result = s.malloc(itemslen + seplen) - res_index = len(items[0].chars) - s.copy_contents(items[0], result, 0, 0, res_index) + items0 = llmemory.cast_any_ptr(TYP, items[0]) + res_index = len(items0.chars) + s.copy_contents(items0, result, 0, 0, res_index) i = 1 while i < num_items: s.copy_contents(s, result, 0, res_index, s_len) res_index += s_len - lgt = len(items[i].chars) - s.copy_contents(items[i], result, 0, res_index, lgt) + item = llmemory.cast_any_ptr(TYP, items[i]) + lgt = len(item.chars) + s.copy_contents(item, result, 0, res_index, lgt) res_index += lgt i += 1 return result @@ -778,24 +781,25 @@ class LLHelpers(AbstractLLHelpers): return rstring._search(hlunicode(s1), hlunicode(s2), start, end, mode) @staticmethod - @signature(types.int(), types.any(), returns=types.any()) - @jit.look_inside_iff(lambda length, items: jit.loop_unrolling_heuristic( + @signature(types.int(), types.any(), types.any(), returns=types.any()) + @jit.look_inside_iff(lambda length, items, TYP: jit.loop_unrolling_heuristic( items, length)) - def ll_join_strs(length, items): + def ll_join_strs(length, items, TYP): # Special case for length 1 items, helps both the JIT and other code if length == 1: - return items[0] + return llmemory.cast_any_ptr(TYP, items[0]) num_items = length itemslen = 0 i = 0 while i < num_items: try: - itemslen = ovfcheck(itemslen + len(items[i].chars)) + item = llmemory.cast_any_ptr(TYP, items[i]) + itemslen = ovfcheck(itemslen + len(item.chars)) except OverflowError: raise MemoryError i += 1 - if typeOf(items).TO.OF.TO == STR: + if TYP.TO == STR: malloc = mallocstr copy_contents = copy_string_contents else: @@ -805,9 +809,10 @@ class LLHelpers(AbstractLLHelpers): res_index = 0 i = 0 while i < num_items: - item_chars = items[i].chars + item = llmemory.cast_any_ptr(TYP, items[i]) + item_chars = item.chars item_len = len(item_chars) - copy_contents(items[i], result, 0, res_index, item_len) + copy_contents(item, result, 0, res_index, item_len) res_index += item_len i += 1 return result @@ -886,6 +891,10 @@ class LLHelpers(AbstractLLHelpers): i += 1 res = LIST.ll_newlist(count) items = res.ll_items() + ITEM = typeOf(items).TO.OF + if count == 1: + items[0] = llmemory.cast_any_ptr(ITEM, s) + return res i = 0 j = 0 resindex = 0 @@ -893,15 +902,17 @@ class LLHelpers(AbstractLLHelpers): j = strlen while j < strlen: if chars[j] == c: - item = items[resindex] = s.malloc(j - i) + item = s.malloc(j - i) item.copy_contents(s, item, i, 0, j - i) + items[resindex] = llmemory.cast_any_ptr(ITEM, item) resindex += 1 i = j + 1 if max >= 0 and resindex >= max: j = strlen break j += 1 - item = items[resindex] = s.malloc(j - i) + item = s.malloc(j - i) + items[resindex] = llmemory.cast_any_ptr(ITEM, item) item.copy_contents(s, item, i, 0, j - i) return res @@ -919,22 +930,25 @@ class LLHelpers(AbstractLLHelpers): count += 1 res = LIST.ll_newlist(count) items = res.ll_items() + ITEM = typeOf(items).TO.OF + if count == 1: + items[0] = llmemory.cast_any_ptr(ITEM, s) + return res pos = 0 count = 0 pos = s.find(c, 0, last) prev_pos = 0 - if pos < 0: - items[0] = s - return res while pos >= 0 and count < max: - item = items[count] = s.malloc(pos - prev_pos) + item = s.malloc(pos - prev_pos) item.copy_contents(s, item, prev_pos, 0, pos - prev_pos) + items[count] = llmemory.cast_any_ptr(ITEM, item) count += 1 prev_pos = pos + markerlen pos = s.find(c, pos + markerlen, last) - item = items[count] = s.malloc(last - prev_pos) + item = s.malloc(last - prev_pos) item.copy_contents(s, item, prev_pos, 0, last - prev_pos) + items[count] = llmemory.cast_any_ptr(ITEM, item) return res @staticmethod @@ -953,6 +967,10 @@ class LLHelpers(AbstractLLHelpers): i += 1 res = LIST.ll_newlist(count) items = res.ll_items() + ITEM = typeOf(items).TO.OF + if count == 1: + items[0] = llmemory.cast_any_ptr(ITEM, s) + return res i = strlen j = strlen resindex = count - 1 @@ -962,15 +980,17 @@ class LLHelpers(AbstractLLHelpers): while j > 0: j -= 1 if chars[j] == c: - item = items[resindex] = s.malloc(i - j - 1) + item = s.malloc(i - j - 1) item.copy_contents(s, item, j + 1, 0, i - j - 1) + items[resindex] = llmemory.cast_any_ptr(ITEM, item) resindex -= 1 i = j if resindex == 0: j = 0 break - item = items[resindex] = s.malloc(i - j) + item = s.malloc(i - j) item.copy_contents(s, item, j, 0, i - j) + items[resindex] = llmemory.cast_any_ptr(ITEM, item) return res @staticmethod @@ -986,23 +1006,25 @@ class LLHelpers(AbstractLLHelpers): count += 1 res = LIST.ll_newlist(count) items = res.ll_items() - pos = 0 + ITEM = typeOf(items).TO.OF + if count == 1: + items[0] = llmemory.cast_any_ptr(ITEM, s) + return res pos = len(s.chars) prev_pos = pos pos = s.rfind(c, 0, pos) - if pos < 0: - items[0] = s - return res count -= 1 while pos >= 0 and count > 0: - item = items[count] = s.malloc(prev_pos - pos - markerlen) + item = s.malloc(prev_pos - pos - markerlen) item.copy_contents(s, item, pos + markerlen, 0, prev_pos - pos - markerlen) + items[count] = llmemory.cast_any_ptr(ITEM, item) count -= 1 prev_pos = pos pos = s.rfind(c, 0, pos) - item = items[count] = s.malloc(prev_pos) + item = s.malloc(prev_pos) item.copy_contents(s, item, 0, 0, prev_pos) + items[count] = llmemory.cast_any_ptr(ITEM, item) return res @staticmethod @@ -1163,7 +1185,8 @@ class LLHelpers(AbstractLLHelpers): hop.genop('setarrayitem', [vtemp, i, vchunk]) hop.exception_cannot_occur() # to ignore the ZeroDivisionError of '%' - return hop.gendirectcall(cls.ll_join_strs, size, vtemp) + c_tp = inputconst(Void, TEMPBUF.OF) + return hop.gendirectcall(cls.ll_join_strs, size, vtemp, c_tp) @staticmethod @jit.dont_look_inside diff --git a/rpython/rtyper/rmodel.py b/rpython/rtyper/rmodel.py index 79bb308e18..422435c330 100644 --- a/rpython/rtyper/rmodel.py +++ b/rpython/rtyper/rmodel.py @@ -5,7 +5,6 @@ from rpython.rtyper.lltypesystem import lltype from rpython.rtyper.lltypesystem.lltype import Void, Bool, LowLevelType, Ptr from rpython.tool.pairtype import pairtype, extendabletype, pair - # initialization states for Repr instances class setupstate(object): @@ -415,8 +414,14 @@ def getgcflavor(classdef): alloc_flavor = classdesc.get_param('_alloc_flavor_', default='gc') return alloc_flavor -def externalvsinternal(rtyper, item_repr): # -> external_item_repr, (internal_)item_repr +def externalvsinternal(rtyper, item_repr, gcref=False): # -> external_item_repr, (internal_)item_repr from rpython.rtyper import rclass + from rpython.rtyper.lltypesystem import rgcref + if rtyper is None or rtyper.annotator.translator.config.translation.gc == "ref": + gcref = False # refcounting GC cannot deal with gcrefs + if (gcref and isinstance(item_repr.lowleveltype, Ptr) and + item_repr.lowleveltype.TO._gckind == 'gc'): + return item_repr, rgcref.GCRefRepr.make(item_repr, rtyper.gcrefreprcache) if (isinstance(item_repr, rclass.InstanceRepr) and getattr(item_repr, 'gcflavor', 'gc') == 'gc'): return item_repr, rclass.getinstancerepr(rtyper, None) diff --git a/rpython/rtyper/rpbc.py b/rpython/rtyper/rpbc.py index cbb860a4aa..faef963bd2 100644 --- a/rpython/rtyper/rpbc.py +++ b/rpython/rtyper/rpbc.py @@ -475,8 +475,18 @@ class SmallFunctionSetPBCRepr(FunctionReprBase): links.append(Link(inputargs[1:], b, chr(i))) links[-1].llexitcase = chr(i) startblock.closeblock(*links) + graph.name = self._invent_dispatcher_name(row_of_graphs) return graph + def _invent_dispatcher_name(self, row): + import os + names = [value.name.rsplit(".", 1)[-1] for value in row.itervalues()] + commonprefix = os.path.commonprefix(names) # bit silly, but works well + + if not commonprefix: + commonprefix = sorted(names)[0] + "_etc" # just pick one + return "dispatcher_" + commonprefix + def call(self, hop): bk = self.rtyper.annotator.bookkeeper args = hop.spaceop.build_args(hop.args_s[1:]) diff --git a/rpython/rtyper/rstr.py b/rpython/rtyper/rstr.py index 3045b33341..1de86f9aec 100644 --- a/rpython/rtyper/rstr.py +++ b/rpython/rtyper/rstr.py @@ -3,7 +3,7 @@ from rpython.rlib import jit from rpython.rtyper import rint from rpython.rtyper.error import TyperError from rpython.rtyper.lltypesystem.lltype import Signed, Bool, Void, UniChar -from rpython.rtyper.lltypesystem import lltype +from rpython.rtyper.lltypesystem import lltype, llmemory from rpython.rtyper.rmodel import IteratorRepr, inputconst, Repr from rpython.rtyper.rint import IntegerRepr from rpython.rtyper.rfloat import FloatRepr @@ -287,18 +287,18 @@ class AbstractStringRepr(Repr): v_length, v_items = self._list_length_items(hop, v_lst, r_lst.lowleveltype) if hop.args_s[0].is_constant() and hop.args_s[0].const == '': - if r_lst.item_repr == rstr.repr: + v_tp = hop.inputconst(Void, self.lowleveltype) + if r_lst.external_item_repr == rstr.repr: llfn = self.ll.ll_join_strs + return hop.gendirectcall(llfn, v_length, v_items, v_tp) elif (r_lst.item_repr == char_repr or r_lst.item_repr == unichar_repr): - v_tp = hop.inputconst(Void, self.lowleveltype) return hop.gendirectcall(self.ll.ll_join_chars, v_length, v_items, v_tp) else: raise TyperError("''.join() of non-string list: %r" % r_lst) - return hop.gendirectcall(llfn, v_length, v_items) else: - if r_lst.item_repr == rstr.repr: + if r_lst.external_item_repr == rstr.repr: llfn = self.ll.ll_join else: raise TyperError("sep.join() of non-string list: %r" % r_lst) @@ -1026,6 +1026,7 @@ class AbstractLLHelpers(object): i = 0 j = 0 # The annotator makes sure this list is resizable. + ITEM = LIST.items.TO.OF res = LIST.ll_newlist(0) while j < strlen: while i < strlen and s[i] != '\n' and s[i] != '\r': @@ -1041,11 +1042,11 @@ class AbstractLLHelpers(object): list_length = res.ll_length() res._ll_resize_ge(list_length + 1) item = cls.ll_stringslice_startstop(ll_str, j, eol) - res.ll_setitem_fast(list_length, item) + res.ll_setitem_fast(list_length, llmemory.cast_any_ptr(ITEM, item)) j = i if j < strlen: list_length = res.ll_length() res._ll_resize_ge(list_length + 1) item = cls.ll_stringslice_startstop(ll_str, j, strlen) - res.ll_setitem_fast(list_length, item) + res.ll_setitem_fast(list_length, llmemory.cast_any_ptr(ITEM, item)) return res diff --git a/rpython/rtyper/rtyper.py b/rpython/rtyper/rtyper.py index 383f17ac62..cd2a2fe857 100644 --- a/rpython/rtyper/rtyper.py +++ b/rpython/rtyper/rtyper.py @@ -54,7 +54,6 @@ class RPythonTyper(object): self.reprs = {} self._reprs_must_call_setup = [] self._seen_reprs_must_call_setup = {} - self._dict_traits = {} self.rootclass_repr = RootClassRepr(self) self.rootclass_repr.setup() self.instance_reprs = {} @@ -71,6 +70,7 @@ class RPythonTyper(object): self.isinstance_helpers = {} self.exceptiondata = ExceptionData(self) self.custom_trace_funcs = [] + self.gcrefreprcache = {} try: self.seed = int(os.getenv('RTYPERSEED')) diff --git a/rpython/rtyper/test/test_rdict.py b/rpython/rtyper/test/test_rdict.py index 81d3a6f67f..1812548a2e 100644 --- a/rpython/rtyper/test/test_rdict.py +++ b/rpython/rtyper/test/test_rdict.py @@ -1069,6 +1069,32 @@ class TestRDict(BaseTestRDict): assert r_AB_dic.lowleveltype == r_BA_dic.lowleveltype + def test_type_erase_gcref(self): + class A(object): + pass + class B(object): + pass + + def f(): + d = {} + d[A()] = B() + d2 = {} + d2[B()] = "abc" + return d, d2 + + t = TranslationContext() + t.config.translation.gc = 'incminimark' # not ref, otherwise this won't work + s = t.buildannotator().build_types(f, []) + rtyper = t.buildrtyper() + rtyper.specialize() + + s_AB_dic = s.items[0] + s_BA_dic = s.items[1] + + r_AB_dic = rtyper.getrepr(s_AB_dic) + r_BA_dic = rtyper.getrepr(s_BA_dic) + + assert r_AB_dic.lowleveltype == r_BA_dic.lowleveltype def test_opt_dummykeymarker(self): def f(): @@ -1175,6 +1201,11 @@ class Action(object): class PseudoRTyper: cache_dummy_values = {} + class annotator: + class translator: + class config: + class translation: + gc = 'ref' # XXX: None keys crash the test, but translation sort-of allows it keytypes_s = [ diff --git a/rpython/rtyper/test/test_rlist.py b/rpython/rtyper/test/test_rlist.py index 3dbe6c9f87..dda764b400 100644 --- a/rpython/rtyper/test/test_rlist.py +++ b/rpython/rtyper/test/test_rlist.py @@ -311,22 +311,36 @@ class TestRlist(BaseRtypingTest): assert self.ll_to_list(res) == [5, 6, 7, 8, 9] def test_slice(self): - def dummyfn(): + def dummyfn(i): l = [5, 6, 7, 8, 9] - return l[:2], l[1:4], l[3:] - res = self.interpret(dummyfn, []) - assert self.ll_to_list(res.item0) == [5, 6] - assert self.ll_to_list(res.item1) == [6, 7, 8] - assert self.ll_to_list(res.item2) == [8, 9] + if i == 0: + return l[:2] + elif i == 1: + return l[1:4] + else: + return l[3:] + res = self.interpret(dummyfn, [0]) + assert self.ll_to_list(res) == [5, 6] + res = self.interpret(dummyfn, [1]) + assert self.ll_to_list(res) == [6, 7, 8] + res = self.interpret(dummyfn, [2]) + assert self.ll_to_list(res) == [8, 9] - def dummyfn(): + def dummyfn(i): l = [5, 6, 7, 8] l.append(9) - return l[:2], l[1:4], l[3:] - res = self.interpret(dummyfn, []) - assert self.ll_to_list(res.item0) == [5, 6] - assert self.ll_to_list(res.item1) == [6, 7, 8] - assert self.ll_to_list(res.item2) == [8, 9] + if i == 0: + return l[:2] + elif i == 1: + return l[1:4] + else: + return l[3:] + res = self.interpret(dummyfn, [0]) + assert self.ll_to_list(res) == [5, 6] + res = self.interpret(dummyfn, [1]) + assert self.ll_to_list(res) == [6, 7, 8] + res = self.interpret(dummyfn, [2]) + assert self.ll_to_list(res) == [8, 9] def test_getslice_not_constant_folded(self): l = list('abcdef') @@ -1538,6 +1552,29 @@ class TestRlist(BaseRtypingTest): assert r_A_list.lowleveltype == r_B_list.lowleveltype + def test_type_erase_gcref(self): + class A(object): + pass + + def f(): + return [A()], [str(A())] + + t = TranslationContext() + s = t.buildannotator().build_types(f, []) + t.config.translation.gc = 'incminimark' # not ref, otherwise this won't work + rtyper = t.buildrtyper() + rtyper.specialize() + + s_A_list = s.items[0] + s_B_list = s.items[1] + + r_A_list = rtyper.getrepr(s_A_list) + assert isinstance(r_A_list, self.rlist.FixedSizeListRepr) + r_B_list = rtyper.getrepr(s_B_list) + assert isinstance(r_B_list, self.rlist.FixedSizeListRepr) + + assert r_A_list.lowleveltype == r_B_list.lowleveltype + def test_type_erase_var_size(self): class A(object): pass @@ -1566,6 +1603,33 @@ class TestRlist(BaseRtypingTest): assert r_A_list.lowleveltype == r_B_list.lowleveltype + def test_type_erase_var_size_gcref(self): + class A(object): + pass + + def f(): + la = [A()] + lb = ["abc"] + la.append(None) + lb.append(None) + return la, lb + + t = TranslationContext() + s = t.buildannotator().build_types(f, []) + t.config.translation.gc = 'incminimark' # not ref, otherwise this won't work + rtyper = t.buildrtyper() + rtyper.specialize() + + s_A_list = s.items[0] + s_B_list = s.items[1] + + r_A_list = rtyper.getrepr(s_A_list) + assert isinstance(r_A_list, self.rlist.ListRepr) + r_B_list = rtyper.getrepr(s_B_list) + assert isinstance(r_B_list, self.rlist.ListRepr) + + assert r_A_list.lowleveltype == r_B_list.lowleveltype + def test_no_unneeded_refs(self): def fndel(p, q): lis = ["5", "3", "99"] diff --git a/rpython/rtyper/test/test_rpbc.py b/rpython/rtyper/test/test_rpbc.py index fcc3a01781..184e92ae8e 100644 --- a/rpython/rtyper/test/test_rpbc.py +++ b/rpython/rtyper/test/test_rpbc.py @@ -2175,8 +2175,43 @@ def test_smallfuncsets_basic(): rtyper = t.buildrtyper() rtyper.specialize() graph = graphof(t, f) + calldispatcherop = graph.startblock.exits[0].target.operations[0] + assert calldispatcherop.args[0].value._obj0.graph.name == "dispatcher_g_etc" interp = LLInterpreter(rtyper) res = interp.eval_graph(graph, [0, 0]) assert res == -1 res = interp.eval_graph(graph, [0, 1]) assert res == 1 + +def test_smallfuncsets_method(): + from rpython.translator.translator import TranslationContext, graphof + from rpython.config.translationoption import get_combined_translation_config + from rpython.rtyper.llinterp import LLInterpreter + config = get_combined_translation_config(translating=True) + config.translation.withsmallfuncsets = 10 + + class A(object): + def meth(self, x): + return x - 1 + class B(A): + def meth(self, x): + return x + 1 + def f(x, y): + if y > 0: + a = A() + else: + a = B() + return a.meth(x) + t = TranslationContext(config=config) + a = t.buildannotator() + a.build_types(f, [int, int]) + rtyper = t.buildrtyper() + rtyper.specialize() + graph = graphof(t, f) + calldispatcherop = [op for (_, op) in graph.iterblockops() if op.opname == "direct_call"][0] + assert calldispatcherop.args[0].value._obj0.graph.name == "dispatcher_meth" + interp = LLInterpreter(rtyper) + res = interp.eval_graph(graph, [0, 0]) + assert res == f(0, 0) + res = interp.eval_graph(graph, [0, 1]) + assert res == f(0, 1) diff --git a/rpython/translator/c/test/test_backendoptimized.py b/rpython/translator/c/test/test_backendoptimized.py index cf450a3b4b..afcd4036e5 100644 --- a/rpython/translator/c/test/test_backendoptimized.py +++ b/rpython/translator/c/test/test_backendoptimized.py @@ -1,3 +1,4 @@ +import gc from rpython.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong from rpython.translator.c.test.test_typed import TestTypedTestCase as _TestTypedTestCase from rpython.translator.c.test.test_genc import compile @@ -36,6 +37,8 @@ class TestTypedOptimizedTestCase(_TestTypedTestCase): for i in range(x): a = A() gc.collect() + gc.collect() + gc.collect() return b.num_deleted fn = self.getcompiled(f, [int], gcpolicy='ref') diff --git a/rpython/translator/c/test/test_newgc.py b/rpython/translator/c/test/test_newgc.py index 950b1ec77d..ce8b3c5937 100644 --- a/rpython/translator/c/test/test_newgc.py +++ b/rpython/translator/c/test/test_newgc.py @@ -1380,33 +1380,30 @@ class TestSemiSpaceGC(UsingFrameworkTest, snippet.SemiSpaceGCTestDefines): l3 = [] def f(): + typeid_S = -1 for i in range(10): s = lltype.malloc(S) + gcref = lltype.cast_opaque_ptr(llmemory.GCREF, s) + typeid_S = rgc.get_rpy_type_index(gcref) l1.append(s) l2.append(s) l3.append(s) + assert typeid_S > 0 + gc.collect() tb = rgc._heap_stats() - a = 0 - nr = 0 - b = 0 - c = 0 - for i in range(len(tb)): - if tb[i].count == 10: # the type of S - a += 1 - nr = i + assert tb[typeid_S].count == 10 + # find the lists + lcount = -1 for i in range(len(tb)): - if tb[i].count == 3: # the type GcArray(Ptr(S)) - b += 1 - c += tb[i].links[nr] - # b can be 1 or 2 here since _heap_stats() is free to return or - # ignore the three GcStructs that point to the GcArray(Ptr(S)). - # important one is c, a is for check - return c * 100 + b * 10 + a + if tb[i].count >= 3 and tb[i].links[typeid_S] == 30: + assert lcount == -1 + lcount = tb[i].count + return lcount return f def test_gc_heap_stats(self): res = self.run("gc_heap_stats") - assert res == 3011 or res == 3021 + assert res >= 3 def definestr_string_builder(cls): def fn(_): |