aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatti Picus <matti.picus@gmail.com>2020-09-06 14:17:44 +0300
committerMatti Picus <matti.picus@gmail.com>2020-09-06 14:17:44 +0300
commit0a817299fb20f29ac1b9376560c140f6c831a3fc (patch)
treeaf021558160cbac62e023cad7d5f1491b5387f82
parentfix translation (diff)
parentrestart whatsnew (diff)
downloadpypy-0a817299fb20f29ac1b9376560c140f6c831a3fc.tar.gz
pypy-0a817299fb20f29ac1b9376560c140f6c831a3fc.tar.bz2
pypy-0a817299fb20f29ac1b9376560c140f6c831a3fc.zip
merge default into py3.6
-rw-r--r--.hgignore1
-rw-r--r--extra_tests/cffi_tests/test_c.py4
-rw-r--r--extra_tests/test_semlock.py3
-rw-r--r--lib_pypy/cffi/_embedding.h5
-rw-r--r--pypy/doc/conf.py2
-rw-r--r--pypy/doc/jit-hooks.rst15
-rw-r--r--pypy/doc/release-v7.3.2.rst34
-rw-r--r--pypy/doc/whatsnew-head.rst43
-rw-r--r--pypy/doc/whatsnew-pypy2-7.3.2.rst50
-rw-r--r--pypy/module/_cffi_backend/test/_backend_test_c.py6
-rw-r--r--pypy/module/cpyext/include/patchlevel.h4
-rw-r--r--pypy/module/pypyjit/interp_jit.py18
-rw-r--r--pypy/module/pypyjit/moduledef.py1
-rw-r--r--pypy/module/pypyjit/test_pypy_c/test_thread.py6
-rw-r--r--pypy/module/sys/version.py2
-rw-r--r--pypy/objspace/std/typeobject.py17
-rw-r--r--rpython/jit/backend/llsupport/rewrite.py77
-rw-r--r--rpython/jit/backend/llsupport/test/test_rewrite.py111
-rw-r--r--rpython/jit/metainterp/memmgr.py6
-rw-r--r--rpython/jit/metainterp/optimizeopt/intbounds.py120
-rw-r--r--rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py340
-rw-r--r--rpython/jit/metainterp/test/test_jitiface.py43
-rw-r--r--rpython/jit/metainterp/warmspot.py6
-rw-r--r--rpython/rlib/jit_hooks.py4
24 files changed, 840 insertions, 78 deletions
diff --git a/.hgignore b/.hgignore
index d9f19b23cd..f790a2286a 100644
--- a/.hgignore
+++ b/.hgignore
@@ -100,4 +100,5 @@ syntax: regexp
.hypothesis/
^release/
^rpython/_cache$
+^lib
diff --git a/extra_tests/cffi_tests/test_c.py b/extra_tests/cffi_tests/test_c.py
index e76950f4e5..14d3f662a8 100644
--- a/extra_tests/cffi_tests/test_c.py
+++ b/extra_tests/cffi_tests/test_c.py
@@ -2553,8 +2553,8 @@ def test_errno():
assert get_errno() == 95
def test_errno_callback():
- if globals().get('PY_DOT_PY') == '2.5':
- py.test.skip("cannot run this test on py.py with Python 2.5")
+ if globals().get('PY_DOT_PY'):
+ py.test.skip("cannot run this test on py.py (e.g. fails on Windows)")
set_errno(95)
def cb():
e = get_errno()
diff --git a/extra_tests/test_semlock.py b/extra_tests/test_semlock.py
index 866002a4e4..f5a8074e36 100644
--- a/extra_tests/test_semlock.py
+++ b/extra_tests/test_semlock.py
@@ -18,6 +18,8 @@ def test_notify_all():
if lock.acquire(timeout=5.):
results.append(n)
lock.release()
+ else:
+ print("lock acquire timed out!")
threads = [Thread(target=f, args=(i,)) for i in range(N_THREADS)]
n_started = N_THREADS
@@ -32,6 +34,7 @@ def test_notify_all():
else:
t.started = True
time.sleep(0.1)
+ print("started %d threads" % n_started)
for t in threads:
if t.started:
t.join()
diff --git a/lib_pypy/cffi/_embedding.h b/lib_pypy/cffi/_embedding.h
index 207d68308b..456dc52cc7 100644
--- a/lib_pypy/cffi/_embedding.h
+++ b/lib_pypy/cffi/_embedding.h
@@ -331,15 +331,20 @@ static int _cffi_carefully_make_gil(void)
/* call Py_InitializeEx() */
if (!Py_IsInitialized()) {
_cffi_py_initialize();
+#if PY_VERSION_HEX < 0x03070000
PyEval_InitThreads();
+#endif
PyEval_SaveThread(); /* release the GIL */
/* the returned tstate must be the one that has been stored into the
autoTLSkey by _PyGILState_Init() called from Py_Initialize(). */
}
else {
+#if PY_VERSION_HEX < 0x03070000
+ /* PyEval_InitThreads() is always a no-op from CPython 3.7 */
PyGILState_STATE state = PyGILState_Ensure();
PyEval_InitThreads();
PyGILState_Release(state);
+#endif
}
#ifdef WITH_THREAD
diff --git a/pypy/doc/conf.py b/pypy/doc/conf.py
index 03bb8b764b..38a7c0b887 100644
--- a/pypy/doc/conf.py
+++ b/pypy/doc/conf.py
@@ -73,7 +73,7 @@ copyright = u'2020, The PyPy Project'
# The short X.Y version.
version = '7.3'
# The full version, including alpha/beta/rc tags.
-release = '7.3.2'
+release = '7.3.3'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/pypy/doc/jit-hooks.rst b/pypy/doc/jit-hooks.rst
index 49c3f325de..c1dabe66cb 100644
--- a/pypy/doc/jit-hooks.rst
+++ b/pypy/doc/jit-hooks.rst
@@ -2,9 +2,7 @@ JIT hooks
=========
There are several hooks in the ``pypyjit`` module that may help you with
-understanding what's pypy's JIT doing while running your program. There
-are three functions related to that coming from the ``pypyjit`` module:
-
+understanding what pypy's JIT is doing while running your program:
.. function:: set_compile_hook(callable, operations=True)
@@ -80,3 +78,14 @@ are three functions related to that coming from the ``pypyjit`` module:
* ``asmlen`` - length of raw memory with assembler associated
+Resetting the JIT
+=================
+
+.. function:: releaseall()
+
+ Marks all current machine code objects as ready to release. They will be
+ released at the next GC (unless they are currently in use in the stack of
+ one of the threads). Doing ``pypyjit.releaseall(); gc.collect()`` is a
+ heavy hammer that forces the JIT roughly back to the state of a newly
+ started PyPy.
+
diff --git a/pypy/doc/release-v7.3.2.rst b/pypy/doc/release-v7.3.2.rst
index 454bb21e46..d02689b2bc 100644
--- a/pypy/doc/release-v7.3.2.rst
+++ b/pypy/doc/release-v7.3.2.rst
@@ -40,7 +40,7 @@ This move was covered more extensively in this `blog post`_. We have seen an
increase in the number of drive-by contributors who are able to use gitlab +
mercurial to create merge requests.
-The `CFFI`_ backend has been updated to version 14.1. We recommend using CFFI
+The `CFFI`_ backend has been updated to version 1.14.2. We recommend using CFFI
rather than c-extensions to interact with C, and using cppyy_ for performant
wrapping of C++ code for Python.
@@ -156,6 +156,30 @@ Changes shared across versions
like NumPy.
- Update ``libffi_msvc`` by copying the fixes done in cffi, and also fix when
returning a ``struct`` in CFFI
+- Add full precision times from ``os.stat`` on macOS
+- PyPy 2.7 only: add SOABI to distutils.sysconfig so wheel gets the correct ABI
+ tag
+- Use the full set of format strings in ``time.strftime`` on windows. After the
+ transition to a more modern MSVC compiler, windows now supports strings like
+ ``%D`` and others.
+- Raise like CPython when ``strftime()`` year outside 1 - 9999 on windows
+- Use ``wcsftime`` in ``strftime()`` on windows
+- Fix `issue 3282`_ where iterating over a ``mmap`` should return an ``int``,
+ not a ``byte``
+- Use ``_chsize_s`` for ftruncate on windows: CPython issue 23668_
+- Allow untranslated tests to run on top of PyPY2.7, not only CPython2.7
+- Add missing ``os`` functions ``os.sched_rr_get_interval``,
+ ``os.sched_getscheduler``, ``os.sched_setscheduler``, ``os.sched_getparam``
+- Set ``buffer`` to ``None`` on close of buffered ``io`` classes
+- Use the ``Suppres_IPH`` context manager wherever CPython uses
+ ``_Py_BEGIN_SUPPRESS_IPH``, ``_Py_END_SUPPRESS_IPH``
+- Fix leaked string if an exception occurs in socket.settimeout on windows
+- close open ``mmap`` and ``zipfile`` resources in stdlib tests
+- Make stack 3MB on windows which aligns expectations with Linux
+- Add ``pypyjit.releaseall()`` that marks all current machine code objects as
+ ready to release. They will be released at the next GC (unless they are
+ currently in use in the stack of one of the threads).
+
C-API (cpyext) and c-extensions
@@ -163,8 +187,9 @@ C-API (cpyext) and c-extensions
- Add ``PyCFunction_Call``
- use ``space.getitem`` in ``PySequence_ITEM``, fixes `pybind11 2146`_
- give preference to ``as_sequence.sq_item`` in ``PySequence_ITEM``
-- In Py_EnterRecursiveCall, ``char*`` -> ``const char *``, `issue 3232_`
+- In Py_EnterRecursiveCall, ``char*`` -> ``const char *``, `issue 3232`_
- Fix ``PySet_Add`` for ``frozenset`` (`issue 3251`_)
+- Support using ``sq_repeat`` and ``sq_inplace_repeat``, `issue 3281`_
Python 3.6 only
---------------
@@ -200,6 +225,8 @@ Python 3.6 only
- Inhibit compiler tail-call optimization via ``PYPY_INHIBIT_TAIL_CALL`` on windows
- When ``pypy -m pip`` fails to find ``pip``, give an error message that hints
at ``pypy -m ensurepip``
+- Fix broken ``_socket.share`` on windows
+- Add missing ``os.{gs}et_handle_inheritable`` (PEP 446) on windows
Python 3.6 C-API
~~~~~~~~~~~~~~~~
@@ -223,6 +250,8 @@ Python 3.6 C-API
.. _`issue 3255`: https://foss.heptapod.net/pypy/pypy/issues/3255
.. _`issue 3269`: https://foss.heptapod.net/pypy/pypy/issues/3269
.. _`issue 3274`: https://foss.heptapod.net/pypy/pypy/issues/3274
+.. _`issue 3282`: https://foss.heptapod.net/pypy/pypy/issues/3282
+.. _`issue 3281`: https://foss.heptapod.net/pypy/pypy/issues/3281
.. _`merge request 723`: https://foss.heptapod.net/pypy/pypy/merge_request/723
.. _`merge request 729`: https://foss.heptapod.net/pypy/pypy/merge_request/729
@@ -235,5 +264,6 @@ Python 3.6 C-API
.. _35519: https://bugs.python.org/issue35519
.. _30465: https://bugs.python.org/issue30465
.. _39413: https://bugs.python.org/issue39413
+.. _23668: https://bugs.python.org/issue39413
.. _`pybind11 2146`: https://github.com/pybind/pybind11/pull/2146
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
index fd14d8106c..2f1487d5db 100644
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -1,44 +1,7 @@
============================
-What's new in PyPy2.7 7.3.1+
+What's new in PyPy2.7 7.3.2+
============================
-.. this is a revision shortly after release-pypy-7.3.1
-.. startrev: 1cae9900d598
+.. this is a revision shortly after release-pypy-7.3.2
+.. startrev: c136fdb316e4
-.. branch: optimize-sre-unicode
-
-Speed up performance of matching Unicode strings in the ``re`` module
-significantly for characters that are part of ASCII.
-
-.. branch: rpython-recvmsg_into
-
-Refactor RSocket.xxx_into() methods and add .recvmsg_into().
-
-.. branch: bo-fix-source-links
-
-Fix documentation extlinks for heptapod directory schema
-
-.. branch: py3.6 # ignore, bad merge
-
-.. branch: ssl # ignore, small test fix
-
-.. branch: ctypes-stuff
-
-Fix implementation of PEP 3118 in ctypes.
-
-.. branch: issue3240
-
-Use make_portable on macOS
-
-.. branch: wb_before_move
-
-Add ``rgc.ll_arraymove()``, as a way to shift items inside the same
-array with proper GC support. Improves ``while lst: lst.pop(0)``.
-
-.. branch: no-str-unicode-union
-
-Remove all implicit str-unicode conversions in RPython.
-
-.. branch: initialize_lock_timeout_on_windows
-
-Fix uninitialized value in rlock.acquire on windows, fixes issue 3252
diff --git a/pypy/doc/whatsnew-pypy2-7.3.2.rst b/pypy/doc/whatsnew-pypy2-7.3.2.rst
new file mode 100644
index 0000000000..2892f94281
--- /dev/null
+++ b/pypy/doc/whatsnew-pypy2-7.3.2.rst
@@ -0,0 +1,50 @@
+===========================
+What's new in PyPy2.7 7.3.2
+===========================
+
+.. this is a revision shortly after release-pypy-7.3.1
+.. startrev: 1cae9900d598
+
+.. branch: optimize-sre-unicode
+
+Speed up performance of matching Unicode strings in the ``re`` module
+significantly for characters that are part of ASCII.
+
+.. branch: rpython-recvmsg_into
+
+Refactor RSocket.xxx_into() methods and add .recvmsg_into().
+
+.. branch: bo-fix-source-links
+
+Fix documentation extlinks for heptapod directory schema
+
+.. branch: py3.6 # ignore, bad merge
+
+.. branch: ssl # ignore, small test fix
+
+.. branch: ctypes-stuff
+
+Fix implementation of PEP 3118 in ctypes.
+
+.. branch: issue3240
+
+Use make_portable on macOS
+
+.. branch: wb_before_move
+
+Add ``rgc.ll_arraymove()``, as a way to shift items inside the same
+array with proper GC support. Improves ``while lst: lst.pop(0)``.
+
+.. branch: no-str-unicode-union
+
+Remove all implicit str-unicode conversions in RPython.
+
+.. branch: initialize_lock_timeout_on_windows
+
+Fix uninitialized value in rlock.acquire on windows, fixes issue 3252
+
+.. branch: jit-releaseall
+
+Add ``pypyjit.releaseall()`` that marks all current machine code
+objects as ready to release. They will be released at the next GC (unless they
+are currently in use in the stack of one of the threads).
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py
index 42f16ed91d..f638802a90 100644
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -52,8 +52,10 @@ def find_and_load_library(name, flags=RTLD_NOW):
path = ctypes.util.find_library(name)
if path is None and name == 'c':
assert sys.platform == 'win32'
- assert sys.version_info >= (3,)
- py.test.skip("dlopen(None) cannot work on Windows with Python 3")
+ assert (sys.version_info >= (3,) or
+ '__pypy__' in sys.builtin_module_names)
+ py.test.skip("dlopen(None) cannot work on Windows "
+ "with PyPy or Python 3")
return load_library(path, flags)
def test_load_library():
diff --git a/pypy/module/cpyext/include/patchlevel.h b/pypy/module/cpyext/include/patchlevel.h
index d5a8b9d69b..eef3e7ae3a 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.2-alpha0"
-#define PYPY_VERSION_NUM 0x07030200
+#define PYPY_VERSION "7.3.3-alpha0"
+#define PYPY_VERSION_NUM 0x07030300
/* 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/pypyjit/interp_jit.py b/pypy/module/pypyjit/interp_jit.py
index c48baa2bc8..0527e2642d 100644
--- a/pypy/module/pypyjit/interp_jit.py
+++ b/pypy/module/pypyjit/interp_jit.py
@@ -199,11 +199,13 @@ exiting (blackhole) steps, but just not from the final assembler.
Note that the return value of the callable is ignored, because
there is no reasonable way to guess what it should be in case the
-function is not called.
+function is not called. Instead, calling the callable returns
+the callable itself, for convenience (see below).
This is meant to be used notably in sys.settrace() for coverage-
like tools. For that purpose, if g = not_from_assembler(f), then
-'g(*args)' may call 'f(*args)' but it always return g itself.
+'g(*args)' may call 'f(*args)' or not. As g() always returns g
+itself, you can directly set sys.settrace(g).
""",
__new__ = interp2app(not_from_assembler_new),
__call__ = interp2app(W_NotFromAssembler.descr_call),
@@ -239,6 +241,16 @@ def trace_next_iteration_hash(space, hash):
jit_hooks.trace_next_iteration_hash('pypyjit', hash)
return space.w_None
+@dont_look_inside
+def releaseall(space):
+ """ Mark all current machine code objects as ready to release. They will
+ be released at the next GC (unless they are currently in use in the stack
+ of one of the threads). Doing pypyjit.releaseall(); gc.collect() is a
+ heavy hammer that forces the JIT roughly to the state of a newly started
+ PyPy.
+ """
+ jit_hooks.stats_memmgr_release_all(None)
+
# class Cache(object):
# in_recursion = False
@@ -253,7 +265,7 @@ def trace_next_iteration_hash(space, hash):
# def set_compile_loop(space, w_hook):
# from rpython.rlib.nonconst import NonConstant
-
+
# cache = space.fromcache(Cache)
# assert w_hook is not None
# cache.w_compile_loop = w_hook
diff --git a/pypy/module/pypyjit/moduledef.py b/pypy/module/pypyjit/moduledef.py
index 02410b7545..854d1d0790 100644
--- a/pypy/module/pypyjit/moduledef.py
+++ b/pypy/module/pypyjit/moduledef.py
@@ -12,6 +12,7 @@ class Module(MixedModule):
'dont_trace_here': 'interp_jit.dont_trace_here',
'trace_next_iteration': 'interp_jit.trace_next_iteration',
'trace_next_iteration_hash': 'interp_jit.trace_next_iteration_hash',
+ 'releaseall': 'interp_jit.releaseall',
'set_compile_hook': 'interp_resop.set_compile_hook',
'set_abort_hook': 'interp_resop.set_abort_hook',
'set_trace_too_long_hook': 'interp_resop.set_trace_too_long_hook',
diff --git a/pypy/module/pypyjit/test_pypy_c/test_thread.py b/pypy/module/pypyjit/test_pypy_c/test_thread.py
index 739a96986d..23a273e8bf 100644
--- a/pypy/module/pypyjit/test_pypy_c/test_thread.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_thread.py
@@ -70,11 +70,11 @@ class TestThread(BaseTestPyPyC):
guard_true(i56, descr=...)
p57 = force_token()
setfield_gc(p0, p57, descr=<FieldP pypy.interpreter.pyframe.PyFrame.vable_token 8>)
- i58 = call_may_force_i(ConstClass(acquire_timed), p31, -1, descr=<Calli . ri EF=7>)
+ i54 = call_release_gil_i(0, _, i37, 1, descr=<Calli 4 ii EF=7>)
guard_not_forced(descr=...)
guard_no_exception(descr=...)
- i99 = int_eq(i58, 1)
- guard_true(i99, descr=...)
+ i55 = int_ne(i54, 1) # sanity-check added in 90c5a06b0923
+ guard_false(i55, descr=...)
i58 = int_sub(i44, 1)
p98 = getfield_gc_r(p97, descr=.*inst_sys_exc_operror.*)
i59 = call_i(ConstClass(RPyThreadReleaseLock), i37, descr=<Calli . i EF=2>)
diff --git a/pypy/module/sys/version.py b/pypy/module/sys/version.py
index ceec0000bb..be64153463 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, 2, "alpha", 0)
+PYPY_VERSION = (7, 3, 3, "alpha", 0)
import pypy
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
index f65f2418da..a5081f1b10 100644
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -848,6 +848,23 @@ def _create_new_type(space, w_typetype, w_name, w_bases, w_dict, __args__):
_init_subclass(space, w_type, __args__)
return w_type
+def _calculate_metaclass(space, w_metaclass, bases_w):
+ """Determine the most derived metatype"""
+ w_winner = w_metaclass
+ for base in bases_w:
+ w_typ = space.type(base)
+ if space.is_w(w_typ, space.w_classobj):
+ continue # special-case old-style classes
+ if space.issubtype_w(w_winner, w_typ):
+ continue
+ if space.issubtype_w(w_typ, w_winner):
+ w_winner = w_typ
+ continue
+ msg = ("metaclass conflict: the metaclass of a derived class must be "
+ "a (non-strict) subclass of the metaclasses of all its bases")
+ raise oefmt(space.w_TypeError, msg)
+ return w_winner
+
def _store_type_in_classcell(space, w_type, w_classcell, dict_w):
from pypy.interpreter.nestedscope import Cell
if isinstance(w_classcell, Cell):
diff --git a/rpython/jit/backend/llsupport/rewrite.py b/rpython/jit/backend/llsupport/rewrite.py
index 2dbe377e70..9b46705c7b 100644
--- a/rpython/jit/backend/llsupport/rewrite.py
+++ b/rpython/jit/backend/llsupport/rewrite.py
@@ -60,6 +60,7 @@ class GcRewriterAssembler(object):
self._delayed_zero_setfields = {}
self.last_zero_arrays = []
self._setarrayitems_occurred = {} # {box: {set-of-indexes}}
+ self._constant_additions = {} # {old_box: (older_box, constant_add)}
def remember_known_length(self, op, val):
self._known_lengths[op] = val
@@ -137,6 +138,8 @@ class GcRewriterAssembler(object):
def emit_gc_store_or_indexed(self, op, ptr_box, index_box, value_box,
itemsize, factor, offset):
+ index_box, offset = self._try_use_older_box(index_box, factor,
+ offset)
factor, offset, index_box = \
self._emit_mul_if_factor_offset_not_supported(index_box,
factor, offset)
@@ -166,8 +169,21 @@ class GcRewriterAssembler(object):
self.emit_op(new_index_box)
return factor, offset, new_index_box
+ def _try_use_older_box(self, index_box, factor, offset):
+ # if index_box is itself an 'int_add' or 'int_sub' box created
+ # recently with an older box argument and a constant argument,
+ # then use instead the older box and fold the constant inside the
+ # offset.
+ if (not isinstance(index_box, ConstInt) and
+ index_box in self._constant_additions):
+ index_box, extra_offset = self._constant_additions[index_box]
+ offset += factor * extra_offset
+ return index_box, offset
+
def emit_gc_load_or_indexed(self, op, ptr_box, index_box, itemsize,
factor, offset, sign, type='i'):
+ index_box, offset = self._try_use_older_box(index_box, factor,
+ offset)
factor, offset, index_box = \
self._emit_mul_if_factor_offset_not_supported(index_box,
factor, offset)
@@ -352,30 +368,36 @@ class GcRewriterAssembler(object):
if self.transform_to_gc_load(op):
continue
# ---------- turn NEWxxx into CALL_MALLOC_xxx ----------
- if rop.is_malloc(op.opnum):
+ opnum = op.getopnum()
+ if rop.is_malloc(opnum):
self.handle_malloc_operation(op)
continue
- if (rop.is_guard(op.opnum) or
+ if (rop.is_guard(opnum) or
self.could_merge_with_next_guard(op, i, operations)):
self.emit_pending_zeros()
- elif rop.can_malloc(op.opnum):
+ elif rop.can_malloc(opnum):
self.emitting_an_operation_that_can_collect()
- elif op.getopnum() == rop.LABEL:
+ elif opnum == rop.LABEL:
self.emit_label()
+ # ------ record INT_ADD or INT_SUB with a constant ------
+ if opnum == rop.INT_ADD or opnum == rop.INT_ADD_OVF:
+ self.record_int_add_or_sub(op, is_subtraction=False)
+ elif opnum == rop.INT_SUB or opnum == rop.INT_SUB_OVF:
+ self.record_int_add_or_sub(op, is_subtraction=True)
# ---- change COPY{STR|UNICODE}CONTENT into a call ------
- if op.opnum == rop.COPYSTRCONTENT or op.opnum == rop.COPYUNICODECONTENT:
+ if opnum == rop.COPYSTRCONTENT or opnum == rop.COPYUNICODECONTENT:
self.rewrite_copy_str_content(op)
continue
# ---------- write barriers ----------
if self.gc_ll_descr.write_barrier_descr is not None:
- if op.getopnum() == rop.SETFIELD_GC:
+ if opnum == rop.SETFIELD_GC:
self.consider_setfield_gc(op)
self.handle_write_barrier_setfield(op)
continue
- if op.getopnum() == rop.SETINTERIORFIELD_GC:
+ if opnum == rop.SETINTERIORFIELD_GC:
self.handle_write_barrier_setinteriorfield(op)
continue
- if op.getopnum() == rop.SETARRAYITEM_GC:
+ if opnum == rop.SETARRAYITEM_GC:
self.consider_setarrayitem_gc(op)
self.handle_write_barrier_setarrayitem(op)
continue
@@ -383,15 +405,15 @@ class GcRewriterAssembler(object):
# this is dead code, but in case we have a gc that does
# not have a write barrier and does not zero memory, we would
# need to call it
- if op.getopnum() == rop.SETFIELD_GC:
+ if opnum == rop.SETFIELD_GC:
self.consider_setfield_gc(op)
- elif op.getopnum() == rop.SETARRAYITEM_GC:
+ elif opnum == rop.SETARRAYITEM_GC:
self.consider_setarrayitem_gc(op)
# ---------- call assembler -----------
- if OpHelpers.is_call_assembler(op.getopnum()):
+ if OpHelpers.is_call_assembler(opnum):
self.handle_call_assembler(op)
continue
- if op.getopnum() == rop.JUMP or op.getopnum() == rop.FINISH:
+ if opnum == rop.JUMP or opnum == rop.FINISH:
self.emit_pending_zeros()
#
self.emit_op(op)
@@ -674,6 +696,10 @@ class GcRewriterAssembler(object):
self._op_malloc_nursery = None
self._write_barrier_applied.clear()
self.emit_pending_zeros()
+ # we also clear _constant_additions here, rather than only in
+ # emit_label(), to avoid keeping alive the old boxes for a
+ # potentially very long time
+ self._constant_additions.clear()
def write_barrier_applied(self, op):
return self.get_box_replacement(op) in self._write_barrier_applied
@@ -970,6 +996,31 @@ class GcRewriterAssembler(object):
self._known_lengths.clear()
self.gcrefs_recently_loaded = None
+ def record_int_add_or_sub(self, op, is_subtraction):
+ # note: if op is a INT_ADD_OVF or INT_SUB_OVF, we ignore the OVF
+ # and proceed normally. The idea is that if we use the result later,
+ # then this means this result did not overflow.
+ v_arg1 = op.getarg(1)
+ if isinstance(v_arg1, ConstInt):
+ constant = v_arg1.getint()
+ if is_subtraction:
+ constant = -constant
+ box = op.getarg(0)
+ else:
+ if is_subtraction:
+ return
+ v_arg0 = op.getarg(0)
+ if not isinstance(v_arg0, ConstInt):
+ return
+ constant = v_arg0.getint()
+ box = v_arg1
+ # invariant: if _constant_additions[b1] = (b2, val2)
+ # then b2 itself is not a key in _constant_additions
+ if box in self._constant_additions:
+ box, extra_offset = self._constant_additions[box]
+ constant += extra_offset
+ self._constant_additions[op] = (box, constant)
+
def _gcref_index(self, gcref):
if self.gcrefs_map is None:
self.gcrefs_map = new_ref_dict()
@@ -1056,7 +1107,7 @@ class GcRewriterAssembler(object):
@always_inline
def cpu_simplify_scale(cpu, index_box, factor, offset):
- # Returns (factor, offset, index_box, [ops]) where index_box is either
+ # Returns (factor, offset, index_box, emit_flag) where index_box is either
# a non-constant BoxInt or None.
if isinstance(index_box, ConstInt):
return 1, index_box.value * factor + offset, None, False
diff --git a/rpython/jit/backend/llsupport/test/test_rewrite.py b/rpython/jit/backend/llsupport/test/test_rewrite.py
index 4b388dc0ea..2906da4506 100644
--- a/rpython/jit/backend/llsupport/test/test_rewrite.py
+++ b/rpython/jit/backend/llsupport/test/test_rewrite.py
@@ -1510,3 +1510,114 @@ class TestFramework(RewriteTests):
i4 = int_lshift(i_len, %(uni_itemscale)d)
call_n(ConstClass(memcpy_fn), i3, i2, i4, descr=memcpy_descr)
""")
+
+ def test_record_int_add_or_sub(self):
+ # ---- no rewrite ----
+ self.check_rewrite("""
+ [p0, i0]
+ i2 = getarrayitem_gc_i(p0, i0, descr=cdescr)
+ """, """
+ [p0, i0]
+ i3 = gc_load_indexed_i(p0, i0, \
+ %(cdescr.itemsize)d, \
+ %(cdescr.basesize)d, \
+ %(cdescr.itemsize)d)
+ """)
+ # ---- add 5 ----
+ self.check_rewrite("""
+ [p0, i0]
+ i1 = int_add(i0, 5)
+ i2 = getarrayitem_gc_i(p0, i1, descr=cdescr)
+ """, """
+ [p0, i0]
+ i1 = int_add(i0, 5)
+ i3 = gc_load_indexed_i(p0, i0, \
+ %(cdescr.itemsize)d, \
+ %(cdescr.basesize + 5 * cdescr.itemsize)d, \
+ %(cdescr.itemsize)d)
+ """)
+ # ---- subtract 1 ----
+ self.check_rewrite("""
+ [p0, i0]
+ i1 = int_sub(i0, 1)
+ i2 = getarrayitem_gc_i(p0, i1, descr=cdescr)
+ """, """
+ [p0, i0]
+ i1 = int_sub(i0, 1)
+ i3 = gc_load_indexed_i(p0, i0, \
+ %(cdescr.itemsize)d, \
+ %(cdescr.basesize - cdescr.itemsize)d, \
+ %(cdescr.itemsize)d)
+ """)
+ # ---- add reversed and multiple levels ----
+ self.check_rewrite("""
+ [p0, i0]
+ i1 = int_sub(i0, 1)
+ i2 = int_add(i1, 10)
+ i3 = int_add(100, i2)
+ i4 = getarrayitem_gc_i(p0, i3, descr=cdescr)
+ """, """
+ [p0, i0]
+ i1 = int_sub(i0, 1)
+ i2 = int_add(i1, 10)
+ i3 = int_add(100, i2)
+ i4 = gc_load_indexed_i(p0, i0, \
+ %(cdescr.itemsize)d, \
+ %(cdescr.basesize + 109 * cdescr.itemsize)d, \
+ %(cdescr.itemsize)d)
+ """)
+ # ---- a label stops the optimization ----
+ self.check_rewrite("""
+ [p0, i0]
+ i1 = int_sub(i0, 1)
+ label(p0, i0, i1)
+ i4 = getarrayitem_gc_i(p0, i1, descr=cdescr)
+ """, """
+ [p0, i0]
+ i1 = int_sub(i0, 1)
+ label(p0, i0, i1)
+ i4 = gc_load_indexed_i(p0, i1, \
+ %(cdescr.itemsize)d, \
+ %(cdescr.basesize)d, \
+ %(cdescr.itemsize)d)
+ """)
+ # ---- also test setarrayitem_gc ----
+ self.check_rewrite("""
+ [p0, i0, i4]
+ i1 = int_sub(i0, 1)
+ i2 = int_add(i1, 10)
+ i3 = int_add(100, i2)
+ setarrayitem_gc(p0, i3, i4, descr=cdescr)
+ """, """
+ [p0, i0, i4]
+ i1 = int_sub(i0, 1)
+ i2 = int_add(i1, 10)
+ i3 = int_add(100, i2)
+ gc_store_indexed(p0, i0, i4, \
+ %(cdescr.itemsize)d, \
+ %(cdescr.basesize + 109 * cdescr.itemsize)d, \
+ %(cdescr.itemsize)d)
+ """)
+ # ---- also check int_add_ovf, int_sub_ovf ----
+ self.check_rewrite("""
+ [p0, i0, i4]
+ i1 = int_sub_ovf(i0, 1)
+ guard_no_overflow(descr=guarddescr) []
+ i2 = int_add_ovf(i1, 10)
+ guard_no_overflow(descr=guarddescr) []
+ i3 = int_add_ovf(100, i2)
+ guard_no_overflow(descr=guarddescr) []
+ setarrayitem_gc(p0, i3, i4, descr=cdescr)
+ """, """
+ [p0, i0, i4]
+ i1 = int_sub_ovf(i0, 1)
+ guard_no_overflow(descr=guarddescr) []
+ i2 = int_add_ovf(i1, 10)
+ guard_no_overflow(descr=guarddescr) []
+ i3 = int_add_ovf(100, i2)
+ guard_no_overflow(descr=guarddescr) []
+ gc_store_indexed(p0, i0, i4, \
+ %(cdescr.itemsize)d, \
+ %(cdescr.basesize + 109 * cdescr.itemsize)d, \
+ %(cdescr.itemsize)d)
+ """)
diff --git a/rpython/jit/metainterp/memmgr.py b/rpython/jit/metainterp/memmgr.py
index dabbb1f145..b8c637059d 100644
--- a/rpython/jit/metainterp/memmgr.py
+++ b/rpython/jit/metainterp/memmgr.py
@@ -81,3 +81,9 @@ class MemoryManager(object):
# a single one is not enough for all tests :-(
rgc.collect(); rgc.collect(); rgc.collect()
debug_stop("jit-mem-collect")
+
+ def release_all_loops(self):
+ debug_start("jit-mem-releaseall")
+ debug_print("Loop tokens cleared:", len(self.alive_loops))
+ self.alive_loops.clear()
+ debug_stop("jit-mem-releaseall")
diff --git a/rpython/jit/metainterp/optimizeopt/intbounds.py b/rpython/jit/metainterp/optimizeopt/intbounds.py
index c18f7897ee..c87d545f3d 100644
--- a/rpython/jit/metainterp/optimizeopt/intbounds.py
+++ b/rpython/jit/metainterp/optimizeopt/intbounds.py
@@ -378,6 +378,61 @@ class OptIntBounds(Optimization):
else:
return self.emit(op)
+ # The optimize_UINT_xx functions are disabled. They work, but the
+ # resulting PyPy is subtly broken. This is probably because we only
+ # have INT_ADD/INT_SUB, not the unsigned version, and the logic here
+ # assumes that they are non-overflowing signed arithmetic. That's
+ # wrong, and they get too small intervals. That's usually not a
+ # problem, but it can definitely be a problem if it's followed by
+ # one of the UINT_* comparisons.
+ def DISABLED_optimize_UINT_LT(self, op):
+ arg1 = get_box_replacement(op.getarg(0))
+ arg2 = get_box_replacement(op.getarg(1))
+ b1 = self.getintbound(arg1)
+ b2 = self.getintbound(arg2)
+ if b1.known_nonnegative() and b1.known_lt(b2):
+ self.make_constant_int(op, 1)
+ elif b2.known_nonnegative() and b1.known_ge(b2):
+ self.make_constant_int(op, 0)
+ else:
+ return self.emit(op)
+
+ def DISABLED_optimize_UINT_LE(self, op):
+ arg1 = get_box_replacement(op.getarg(0))
+ arg2 = get_box_replacement(op.getarg(1))
+ b1 = self.getintbound(arg1)
+ b2 = self.getintbound(arg2)
+ if b1.known_nonnegative() and b1.known_le(b2):
+ self.make_constant_int(op, 1)
+ elif b2.known_nonnegative() and b1.known_gt(b2):
+ self.make_constant_int(op, 0)
+ else:
+ return self.emit(op)
+
+ def DISABLED_optimize_UINT_GT(self, op):
+ arg1 = get_box_replacement(op.getarg(0))
+ arg2 = get_box_replacement(op.getarg(1))
+ b1 = self.getintbound(arg1)
+ b2 = self.getintbound(arg2)
+ if b2.known_nonnegative() and b1.known_gt(b2):
+ self.make_constant_int(op, 1)
+ elif b1.known_nonnegative() and b1.known_le(b2):
+ self.make_constant_int(op, 0)
+ else:
+ return self.emit(op)
+
+ def DISABLED_optimize_UINT_GE(self, op):
+ arg1 = get_box_replacement(op.getarg(0))
+ arg2 = get_box_replacement(op.getarg(1))
+ b1 = self.getintbound(arg1)
+ b2 = self.getintbound(arg2)
+ if b2.known_nonnegative() and b1.known_ge(b2):
+ self.make_constant_int(op, 1)
+ elif b1.known_nonnegative() and b1.known_lt(b2):
+ self.make_constant_int(op, 0)
+ else:
+ return self.emit(op)
+
def optimize_INT_EQ(self, op):
arg0 = get_box_replacement(op.getarg(0))
b1 = self.getintbound(arg0)
@@ -590,6 +645,71 @@ class OptIntBounds(Optimization):
assert r.getint() == 0
self.make_int_lt(op.getarg(0), op.getarg(1))
+ def make_uint_lt(self, box1, box2):
+ b2 = self.getintbound(box2)
+ if b2.known_nonnegative:
+ b1 = self.getintbound(box1)
+ if b1.make_lt(b2) | b1.make_ge(IntBound(0, 0)):
+ self.propagate_bounds_backward(box1)
+ #if b2.make_gt(b1):
+ # ^^ probably correct but I fail to see a case where it is helpful
+ # self.propagate_bounds_backward(box2)
+ # elif box1 is known to be < 0... let's ignore that case
+
+ def make_uint_le(self, box1, box2):
+ b2 = self.getintbound(box2)
+ if b2.known_nonnegative:
+ b1 = self.getintbound(box1)
+ if b1.make_le(b2) | b1.make_ge(IntBound(0, 0)):
+ self.propagate_bounds_backward(box1)
+ #if b2.make_ge(b1):
+ # ^^ probably correct but I fail to see a case where it is helpful
+ # self.propagate_bounds_backward(box2)
+ # elif box1 is known to be < 0... let's ignore that case
+
+ def make_uint_gt(self, box1, box2):
+ self.make_uint_lt(box2, box1)
+
+ def make_uint_ge(self, box1, box2):
+ self.make_uint_le(box2, box1)
+
+ # see DISABLED_optimize_UINT_xx above.
+ def DISABLED_propagate_bounds_UINT_LT(self, op):
+ r = self.getintbound(op)
+ if r.is_constant():
+ if r.getint() == 1:
+ self.make_uint_lt(op.getarg(0), op.getarg(1))
+ else:
+ assert r.getint() == 0
+ self.make_uint_ge(op.getarg(0), op.getarg(1))
+
+ def DISABLED_propagate_bounds_UINT_GT(self, op):
+ r = self.getintbound(op)
+ if r.is_constant():
+ if r.getint() == 1:
+ self.make_uint_gt(op.getarg(0), op.getarg(1))
+ else:
+ assert r.getint() == 0
+ self.make_uint_le(op.getarg(0), op.getarg(1))
+
+ def DISABLED_propagate_bounds_UINT_LE(self, op):
+ r = self.getintbound(op)
+ if r.is_constant():
+ if r.getint() == 1:
+ self.make_uint_le(op.getarg(0), op.getarg(1))
+ else:
+ assert r.getint() == 0
+ self.make_uint_gt(op.getarg(0), op.getarg(1))
+
+ def DISABLED_propagate_bounds_UINT_GE(self, op):
+ r = self.getintbound(op)
+ if r.is_constant():
+ if r.getint() == 1:
+ self.make_uint_ge(op.getarg(0), op.getarg(1))
+ else:
+ assert r.getint() == 0
+ self.make_uint_lt(op.getarg(0), op.getarg(1))
+
def propagate_bounds_INT_EQ(self, op):
r = self.getintbound(op)
if r.is_constant():
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
index 741b2027e8..62407a3276 100644
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -3385,6 +3385,346 @@ class TestOptimizeBasic(BaseTestBasic):
"""
self.optimize_strunicode_loop(ops, expected)
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_lt(self):
+ ops = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ i4 = uint_lt(i0, 10)
+ guard_true(i4) []
+ jump()
+ """
+ expected = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+ #
+ ops = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ i4 = uint_lt(i0, 9)
+ guard_true(i4) []
+ jump()
+ """
+ self.optimize_loop(ops, ops)
+
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_le(self):
+ ops = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ i4 = uint_le(i0, 9)
+ guard_true(i4) []
+ jump()
+ """
+ expected = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+ #
+ ops = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ i4 = uint_le(i0, 8)
+ guard_true(i4) []
+ jump()
+ """
+ self.optimize_loop(ops, ops)
+
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_gt(self):
+ ops = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ i4 = uint_gt(10, i0)
+ guard_true(i4) []
+ jump()
+ """
+ expected = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+ #
+ ops = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ i4 = uint_gt(9, i0)
+ guard_true(i4) []
+ jump()
+ """
+ self.optimize_loop(ops, ops)
+
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_ge(self):
+ ops = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ i4 = uint_ge(9, i0)
+ guard_true(i4) []
+ jump()
+ """
+ expected = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+ #
+ ops = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ i4 = uint_ge(8, i0)
+ guard_true(i4) []
+ jump()
+ """
+ self.optimize_loop(ops, ops)
+
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_not_ge(self):
+ ops = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ i4 = uint_ge(i0, 10)
+ guard_false(i4) []
+ jump()
+ """
+ expected = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_not_gt(self):
+ ops = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ i4 = uint_gt(i0, 9)
+ guard_false(i4) []
+ jump()
+ """
+ expected = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_not_le(self):
+ ops = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ i4 = uint_le(10, i0)
+ guard_false(i4) []
+ jump()
+ """
+ expected = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_not_lt(self):
+ ops = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ i4 = uint_lt(9, i0)
+ guard_false(i4) []
+ jump()
+ """
+ expected = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+ #
+ ops = """
+ [i0]
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ i4 = uint_lt(8, i0)
+ guard_true(i4) []
+ jump()
+ """
+ self.optimize_loop(ops, ops)
+
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_lt_inv(self):
+ ops = """
+ [i0]
+ i1 = uint_lt(i0, 10)
+ guard_true(i1) []
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ jump()
+ """
+ expected = """
+ [i0]
+ i1 = uint_lt(i0, 10)
+ guard_true(i1) []
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_range(self):
+ ops = """
+ [i0]
+ i2 = uint_lt(i0, -10)
+ guard_true(i2) []
+ i3 = uint_gt(i0, -20)
+ guard_true(i3) []
+ jump()
+ """
+ self.optimize_loop(ops, ops)
+
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_le_inv(self):
+ ops = """
+ [i0]
+ i1 = uint_le(i0, 10)
+ guard_true(i1) []
+ i2 = int_lt(i0, 11)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ jump()
+ """
+ expected = """
+ [i0]
+ i1 = uint_le(i0, 10)
+ guard_true(i1) []
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_gt_inv(self):
+ ops = """
+ [i0]
+ i1 = uint_gt(10, i0)
+ guard_true(i1) []
+ i2 = int_lt(i0, 10)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ jump()
+ """
+ expected = """
+ [i0]
+ i1 = uint_gt(10, i0)
+ guard_true(i1) []
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_ge_inv(self):
+ ops = """
+ [i0]
+ i1 = uint_ge(10, i0)
+ guard_true(i1) []
+ i2 = int_lt(i0, 11)
+ guard_true(i2) []
+ i3 = int_ge(i0, 0)
+ guard_true(i3) []
+ jump()
+ """
+ expected = """
+ [i0]
+ i1 = uint_ge(10, i0)
+ guard_true(i1) []
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+
+ @py.test.mark.xfail() # see comment about optimize_UINT in intbounds.py
+ def test_bound_unsigned_bug1(self):
+ ops = """
+ [i0]
+ i1 = int_ge(i0, 5)
+ guard_true(i1) []
+ i2 = uint_lt(i0, -50)
+ guard_true(i2) []
+ jump()
+ """
+ self.optimize_loop(ops, ops)
+
def test_addsub_const(self):
ops = """
[i0]
diff --git a/rpython/jit/metainterp/test/test_jitiface.py b/rpython/jit/metainterp/test/test_jitiface.py
index 877ca6998c..2e1d4fd117 100644
--- a/rpython/jit/metainterp/test/test_jitiface.py
+++ b/rpython/jit/metainterp/test/test_jitiface.py
@@ -13,7 +13,7 @@ from rpython.jit.codewriter.policy import JitPolicy
class JitHookInterfaceTests(object):
# !!!note!!! - don't subclass this from the backend. Subclass the LL
# class later instead
-
+
def test_abort_quasi_immut(self):
reasons = []
@@ -94,7 +94,7 @@ class JitHookInterfaceTests(object):
def test_on_compile_bridge(self):
called = []
-
+
class MyJitIface(JitHookInterface):
def after_compile(self, di):
called.append("compile")
@@ -104,7 +104,7 @@ class JitHookInterfaceTests(object):
def before_compile_bridge(self, di):
called.append("before_compile_bridge")
-
+
driver = JitDriver(greens = ['n', 'm'], reds = ['i'])
def loop(n, m):
@@ -231,7 +231,7 @@ class JitHookInterfaceTests(object):
driver = JitDriver(greens = ['s'], reds = ['i'], name="name")
class Hashes(object):
check = False
-
+
def __init__(self):
self.l = []
self.t = []
@@ -316,10 +316,43 @@ class JitHookInterfaceTests(object):
assert res == 721
assert reasons == []
+ def test_memmgr_release_all(self):
+ driver = JitDriver(greens = [], reds = ['i'])
+ def loop(i):
+ while i > 0:
+ driver.jit_merge_point(i=i)
+ i -= 1
+ def num_loops():
+ return jit_hooks.stats_get_counter_value(None,
+ Counters.TOTAL_COMPILED_LOOPS)
+ def main():
+ loop(30)
+ if num_loops() != 1:
+ return 1000 + num_loops()
+ loop(30)
+ if num_loops() != 1:
+ return 1500 + num_loops()
+ #
+ jit_hooks.stats_memmgr_release_all(None)
+ from rpython.rlib import rgc
+ rgc.collect(); rgc.collect(); rgc.collect()
+ #
+ loop(30)
+ if num_loops() != 2:
+ return 2000 + num_loops()
+ loop(30)
+ if num_loops() != 2:
+ return 2500 + num_loops()
+ return 42
+
+ res = self.meta_interp(main, [], ProfilerClass=Profiler,
+ no_stats_history=True)
+ assert res == 42
+
class LLJitHookInterfaceTests(JitHookInterfaceTests):
# use this for any backend, instead of the super class
-
+
def test_ll_get_stats(self):
driver = JitDriver(greens = [], reds = ['i', 's'])
diff --git a/rpython/jit/metainterp/warmspot.py b/rpython/jit/metainterp/warmspot.py
index 6c9c2022ff..ea89fdfbfe 100644
--- a/rpython/jit/metainterp/warmspot.py
+++ b/rpython/jit/metainterp/warmspot.py
@@ -473,7 +473,7 @@ class WarmRunnerDesc(object):
auto_inline_graphs(self.translator, graphs, 0.01)
def build_cpu(self, CPUClass, translate_support_code=False,
- no_stats=False, supports_floats=True,
+ no_stats=False, no_stats_history=False, supports_floats=True,
supports_longlong=True, supports_singlefloats=True,
**kwds):
assert CPUClass is not None
@@ -482,6 +482,10 @@ class WarmRunnerDesc(object):
stats = history.NoStats()
else:
stats = history.Stats(None)
+ if no_stats_history:
+ stats.set_history = lambda history: None
+ # ^^^ for test_jitiface.test_memmgr_release_all. otherwise,
+ # stats.history attribute keeps the most recent loop alive
self.stats = stats
if translate_support_code:
self.annhelper = MixLevelHelperAnnotator(self.translator.rtyper)
diff --git a/rpython/rlib/jit_hooks.py b/rpython/rlib/jit_hooks.py
index 2ef9c32e33..688c3c1dcd 100644
--- a/rpython/rlib/jit_hooks.py
+++ b/rpython/rlib/jit_hooks.py
@@ -128,6 +128,10 @@ def stats_asmmemmgr_allocated(warmrunnerdesc):
def stats_asmmemmgr_used(warmrunnerdesc):
return warmrunnerdesc.metainterp_sd.cpu.asmmemmgr.get_stats()[1]
+@register_helper(None)
+def stats_memmgr_release_all(warmrunnerdesc):
+ warmrunnerdesc.memory_manager.release_all_loops()
+
# ---------------------- jitcell interface ----------------------
def _new_hook(name, resulttype):