Author: Ronan Lamy <ronan.l...@gmail.com> Branch: py3k Changeset: r84885:1de2e9ff0c99 Date: 2016-06-02 17:04 +0100 http://bitbucket.org/pypy/pypy/changeset/1de2e9ff0c99/
Log: hg merge default diff too long, truncating to 2000 out of 5903 lines diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -23,3 +23,5 @@ 3260adbeba4a8b6659d1cc0d0b41f266769b74da release-5.1 b0a649e90b6642251fb4a765fe5b27a97b1319a9 release-5.1.1 80ef432a32d9baa4b3c5a54c215e8ebe499f6374 release-5.1.2 +40497617ae91caa1a394d8be6f9cd2de31cb0628 release-pypy3.3-v5.2 +40497617ae91caa1a394d8be6f9cd2de31cb0628 release-pypy3.3-v5.2 diff --git a/lib-python/2.7/subprocess.py b/lib-python/2.7/subprocess.py --- a/lib-python/2.7/subprocess.py +++ b/lib-python/2.7/subprocess.py @@ -834,54 +834,63 @@ c2pread, c2pwrite = None, None errread, errwrite = None, None + ispread = False if stdin is None: p2cread = _subprocess.GetStdHandle(_subprocess.STD_INPUT_HANDLE) if p2cread is None: p2cread, _ = _subprocess.CreatePipe(None, 0) + ispread = True elif stdin == PIPE: p2cread, p2cwrite = _subprocess.CreatePipe(None, 0) + ispread = True elif isinstance(stdin, int): p2cread = msvcrt.get_osfhandle(stdin) else: # Assuming file-like object p2cread = msvcrt.get_osfhandle(stdin.fileno()) - p2cread = self._make_inheritable(p2cread) + p2cread = self._make_inheritable(p2cread, ispread) # We just duplicated the handle, it has to be closed at the end to_close.add(p2cread) if stdin == PIPE: to_close.add(p2cwrite) + ispwrite = False if stdout is None: c2pwrite = _subprocess.GetStdHandle(_subprocess.STD_OUTPUT_HANDLE) if c2pwrite is None: _, c2pwrite = _subprocess.CreatePipe(None, 0) + ispwrite = True elif stdout == PIPE: c2pread, c2pwrite = _subprocess.CreatePipe(None, 0) + ispwrite = True elif isinstance(stdout, int): c2pwrite = msvcrt.get_osfhandle(stdout) else: # Assuming file-like object c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) - c2pwrite = self._make_inheritable(c2pwrite) + c2pwrite = self._make_inheritable(c2pwrite, ispwrite) # We just duplicated the handle, it has to be closed at the end to_close.add(c2pwrite) if stdout == PIPE: to_close.add(c2pread) + ispwrite = False if stderr is None: errwrite = _subprocess.GetStdHandle(_subprocess.STD_ERROR_HANDLE) if errwrite is None: _, errwrite = _subprocess.CreatePipe(None, 0) + ispwrite = True elif stderr == PIPE: errread, errwrite = _subprocess.CreatePipe(None, 0) + ispwrite = True elif stderr == STDOUT: - errwrite = c2pwrite.handle # pass id to not close it + errwrite = c2pwrite elif isinstance(stderr, int): errwrite = msvcrt.get_osfhandle(stderr) else: # Assuming file-like object errwrite = msvcrt.get_osfhandle(stderr.fileno()) - errwrite = self._make_inheritable(errwrite) + errwrite = self._make_inheritable(errwrite, ispwrite) # We just duplicated the handle, it has to be closed at the end to_close.add(errwrite) if stderr == PIPE: @@ -892,13 +901,14 @@ errread, errwrite), to_close - def _make_inheritable(self, handle): + def _make_inheritable(self, handle, close=False): """Return a duplicate of handle, which is inheritable""" dupl = _subprocess.DuplicateHandle(_subprocess.GetCurrentProcess(), handle, _subprocess.GetCurrentProcess(), 0, 1, _subprocess.DUPLICATE_SAME_ACCESS) - # If the initial handle was obtained with CreatePipe, close it. - if not isinstance(handle, int): + # PyPy: If the initial handle was obtained with CreatePipe, + # close it. + if close: handle.Close() return dupl diff --git a/lib_pypy/_pypy_irc_topic.py b/lib_pypy/_pypy_irc_topic.py --- a/lib_pypy/_pypy_irc_topic.py +++ b/lib_pypy/_pypy_irc_topic.py @@ -224,23 +224,9 @@ va ClCl orvat bayl zbqrengryl zntvp vf n tbbq guvat <psobym> """ -from string import ascii_uppercase, ascii_lowercase - def rot13(data): - """ A simple rot-13 encoder since `str.encode('rot13')` was removed from - Python as of version 3.0. It rotates both uppercase and lowercase letters individually. - """ - total = [] - for char in data: - if char in ascii_uppercase: - index = (ascii_uppercase.find(char) + 13) % 26 - total.append(ascii_uppercase[index]) - elif char in ascii_lowercase: - index = (ascii_lowercase.find(char) + 13) % 26 - total.append(ascii_lowercase[index]) - else: - total.append(char) - return "".join(total) + return ''.join(chr(ord(c)+(13 if 'A'<=c.upper()<='M' else + -13 if 'N'<=c.upper()<='Z' else 0)) for c in data) def some_topic(): import time diff --git a/lib_pypy/pyrepl/simple_interact.py b/lib_pypy/pyrepl/simple_interact.py --- a/lib_pypy/pyrepl/simple_interact.py +++ b/lib_pypy/pyrepl/simple_interact.py @@ -43,11 +43,13 @@ return short return text -def run_multiline_interactive_console(mainmodule=None): +def run_multiline_interactive_console(mainmodule=None, future_flags=0): import code import __main__ mainmodule = mainmodule or __main__ console = code.InteractiveConsole(mainmodule.__dict__, filename='<stdin>') + if future_flags: + console.compile.compiler.flags |= future_flags def more_lines(unicodetext): # ooh, look at the hack: diff --git a/pypy/doc/build.rst b/pypy/doc/build.rst --- a/pypy/doc/build.rst +++ b/pypy/doc/build.rst @@ -70,9 +70,6 @@ bz2 libbz2 -lzma (PyPy3 only) - liblzma - pyexpat libexpat1 @@ -98,11 +95,16 @@ tk tk-dev +lzma (PyPy3 only) + liblzma + +To run untranslated tests, you need the Boehm garbage collector libgc. + On Debian, this is the command to install all build-time dependencies:: apt-get install gcc make libffi-dev pkg-config libz-dev libbz2-dev \ libsqlite3-dev libncurses-dev libexpat1-dev libssl-dev libgdbm-dev \ - tk-dev libgc-dev + tk-dev libgc-dev liblzma-dev For the optional lzma module on PyPy3 you will also need ``liblzma-dev``. diff --git a/pypy/doc/index-of-release-notes.rst b/pypy/doc/index-of-release-notes.rst --- a/pypy/doc/index-of-release-notes.rst +++ b/pypy/doc/index-of-release-notes.rst @@ -49,6 +49,13 @@ release-0.6 +CPython 3.3 compatible versions +------------------------------- + +.. toctree:: + + release-pypy3.3-v5.2-alpha1.rst + CPython 3.2 compatible versions ------------------------------- diff --git a/pypy/doc/release-pypy3.3-v5.2-alpha1.rst b/pypy/doc/release-pypy3.3-v5.2-alpha1.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-pypy3.3-v5.2-alpha1.rst @@ -0,0 +1,69 @@ +=================== +PyPy3 v5.2 alpha 1 +=================== + +We're pleased to announce the first alpha release of PyPy3.3 v5.2. This is the +first release of PyPy which targets Python 3.3 (3.3.5) compatibility. + +We would like to thank all of the people who donated_ to the `py3k proposal`_ +for supporting the work that went into this and future releases. + +You can download the PyPy3.3 v5.2 alpha 1 release here: + + http://pypy.org/download.html#python-3-3-5-compatible-pypy3-3-v5-2 + +Highlights +========== + +* Python 3.3.5 support! + + - Being an early alpha release, there are some `missing features`_ such as a + `PEP 393-like space efficient string representation`_ and `known issues`_ + including performance regressions (e.g. issue `#2305`_). The focus for this + release has been updating to 3.3 compatibility. Windows is also not yet + supported. + +* `ensurepip`_ is also included (it's only included in CPython 3 >= 3.4). + +What is PyPy? +============== + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7.10 and one day 3.3.5. It's fast due to its integrated tracing JIT +compiler. + +We also welcome developers of other `dynamic languages`_ to see what RPython +can do for them. + +This release supports: + + * **x86** machines on most common operating systems except Windows + (Linux 32/64, Mac OS X 64, OpenBSD, FreeBSD), + + * newer **ARM** hardware (ARMv6 or ARMv7, with VFPv3) running Linux, + + * big- and little-endian variants of **PPC64** running Linux, + + * **s390x** running Linux + +Please try it out and let us know what you think. We welcome feedback, we know +you are using PyPy, please tell us about it! + +We'd especially like to thank these people for their contributions to this +release: + +Manuel Jacob, Ronan Lamy, Mark Young, Amaury Forgeot d'Arc, Philip Jenvey, +Martin Matusiak, Vasily Kuznetsov, Matti Picus, Armin Rigo and many others. + +Cheers + +The PyPy Team + +.. _donated: http://morepypy.blogspot.com/2012/01/py3k-and-numpy-first-stage-thanks-to.html +.. _`py3k proposal`: http://pypy.org/py3donate.html +.. _`PEP 393-like space efficient string representation`: https://bitbucket.org/pypy/pypy/issues/2309/optimized-unicode-representation +.. _`missing features`: https://bitbucket.org/pypy/pypy/issues?status=new&status=open&component=PyPy3+%28running+Python+3.x%29&kind=enhancement +.. _`known issues`: https://bitbucket.org/pypy/pypy/issues?status=new&status=open&component=PyPy3%20%28running%20Python%203.x%29 +.. _`#2305`: https://bitbucket.org/pypy/pypy/issues/2305 +.. _`ensurepip`: https://docs.python.org/3/library/ensurepip.html#module-ensurepip +.. _`dynamic languages`: http://pypyjs.org diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -105,3 +105,29 @@ Fix some warnings when compiling CPython C extension modules .. branch: syntax_fix + +.. branch: remove-raisingops + +Remove most of the _ovf, _zer and _val operations from RPython. Kills +quite some code internally, and allows the JIT to do better +optimizations: for example, app-level code like ``x / 2`` or ``x % 2`` +can now be turned into ``x >> 1`` or ``x & 1``, even if x is possibly +negative. + +.. branch: cpyext-old-buffers + +Generalize cpyext old-style buffers to more than just str/buffer, add support for mmap + +.. branch: numpy-includes + +Move _numpypy headers into a directory so they are not picked up by upstream numpy, scipy +This allows building upstream numpy and scipy in pypy via cpyext + +.. branch: traceviewer-common-merge-point-formats + +Teach RPython JIT's off-line traceviewer the most common ``debug_merge_point`` formats. + +.. branch: cpyext-pickle + +Enable pickling of W_PyCFunctionObject by monkeypatching pickle.Pickler.dispatch +at cpyext import time diff --git a/pypy/doc/whatsnew-pypy3-5.1.1-alpha1.rst b/pypy/doc/whatsnew-pypy3-5.1.1-alpha1.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/whatsnew-pypy3-5.1.1-alpha1.rst @@ -0,0 +1,10 @@ +================================= +What's new in PyPy3 5.1.1 alpha 1 +================================= + +.. A recent revision, ignoring all other branches for this release +.. startrev: 29d14733e007 + +.. branch: py3.3 + +Python 3.3 compatibility diff --git a/pypy/doc/windows.rst b/pypy/doc/windows.rst --- a/pypy/doc/windows.rst +++ b/pypy/doc/windows.rst @@ -238,6 +238,15 @@ for use. The release packaging script will pick up the tcltk runtime in the lib directory and put it in the archive. +The lzma compression library +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Python 3.3 ship with CFFI wrappers for the lzma library, which can be +downloaded from this site http://tukaani.org/xz. Python 3.3-3.5 use version +5.0.5, a prebuilt version can be downloaded from +http://tukaani.org/xz/xz-5.0.5-windows.zip, check the signature +http://tukaani.org/xz/xz-5.0.5-windows.zip.sig + Using the mingw compiler ------------------------ diff --git a/pypy/interpreter/test/test_app_main.py b/pypy/interpreter/test/test_app_main.py --- a/pypy/interpreter/test/test_app_main.py +++ b/pypy/interpreter/test/test_app_main.py @@ -72,6 +72,11 @@ print('Goodbye2') # should not be reached """) +script_with_future = getscript(""" + from __future__ import division + from __future__ import print_function + """) + @contextmanager def setpythonpath(): @@ -441,6 +446,31 @@ finally: os.environ['PYTHONSTARTUP'] = old + def test_future_in_executed_script(self): + child = self.spawn(['-i', script_with_future]) + child.expect('>>> ') + child.sendline('x=1; print(x/2, 3/4)') + child.expect('0.5 0.75') + + def test_future_in_python_startup(self, monkeypatch): + monkeypatch.setenv('PYTHONSTARTUP', script_with_future) + child = self.spawn([]) + child.expect('>>> ') + child.sendline('x=1; print(x/2, 3/4)') + child.expect('0.5 0.75') + + def test_future_in_cmd(self): + child = self.spawn(['-i', '-c', 'from __future__ import division']) + child.expect('>>> ') + child.sendline('x=1; x/2; 3/4') + child.expect('0.5') + child.expect('0.75') + + def test_cmd_co_name(self): + child = self.spawn(['-c', + 'import sys; print sys._getframe(0).f_code.co_name']) + child.expect('<module>') + def test_ignore_python_inspect(self): os.environ['PYTHONINSPECT_'] = '1' try: diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py --- a/pypy/interpreter/typedef.py +++ b/pypy/interpreter/typedef.py @@ -12,7 +12,8 @@ class TypeDef(object): - def __init__(self, __name, __base=None, __total_ordering__=None, **rawdict): + def __init__(self, __name, __base=None, __total_ordering__=None, + __buffer=None, **rawdict): "NOT_RPYTHON: initialization-time only" self.name = __name if __base is None: @@ -22,6 +23,8 @@ else: bases = [__base] self.bases = bases + assert __buffer in {None, 'read-write', 'read'}, "Unknown value for __buffer" + self.buffer = __buffer self.heaptype = False self.hasdict = '__dict__' in rawdict # no __del__: use an RPython _finalize_() method and register_finalizer diff --git a/pypy/module/__pypy__/interp_intop.py b/pypy/module/__pypy__/interp_intop.py --- a/pypy/module/__pypy__/interp_intop.py +++ b/pypy/module/__pypy__/interp_intop.py @@ -2,6 +2,19 @@ from rpython.rtyper.lltypesystem import lltype from rpython.rtyper.lltypesystem.lloperation import llop from rpython.rlib.rarithmetic import r_uint, intmask +from rpython.rlib import jit + + +# XXX maybe temporary: hide llop.int_{floordiv,mod} from the JIT, +# because now it expects only Python-style divisions, not the +# C-style divisions of these two ll operations +@jit.dont_look_inside +def _int_floordiv(n, m): + return llop.int_floordiv(lltype.Signed, n, m) + +@jit.dont_look_inside +def _int_mod(n, m): + return llop.int_mod(lltype.Signed, n, m) @unwrap_spec(n=int, m=int) @@ -18,11 +31,11 @@ @unwrap_spec(n=int, m=int) def int_floordiv(space, n, m): - return space.wrap(llop.int_floordiv(lltype.Signed, n, m)) + return space.wrap(_int_floordiv(n, m)) @unwrap_spec(n=int, m=int) def int_mod(space, n, m): - return space.wrap(llop.int_mod(lltype.Signed, n, m)) + return space.wrap(_int_mod(n, m)) @unwrap_spec(n=int, m=int) def int_lshift(space, n, m): diff --git a/pypy/module/_cffi_backend/test/test_recompiler.py b/pypy/module/_cffi_backend/test/test_recompiler.py --- a/pypy/module/_cffi_backend/test/test_recompiler.py +++ b/pypy/module/_cffi_backend/test/test_recompiler.py @@ -1784,3 +1784,9 @@ assert ffi.list_types() == (['CFFIb', 'CFFIbb', 'CFFIbbb'], ['CFFIa', 'CFFIcc', 'CFFIccc'], ['CFFIaa', 'CFFIaaa', 'CFFIg']) + + def test_FFIFunctionWrapper(self): + ffi, lib = self.prepare("void f(void);", "test_FFIFunctionWrapper", + "void f(void) { }") + assert lib.f.__get__(42) is lib.f + assert lib.f.__get__(42, int) is lib.f diff --git a/pypy/module/_cffi_backend/wrapper.py b/pypy/module/_cffi_backend/wrapper.py --- a/pypy/module/_cffi_backend/wrapper.py +++ b/pypy/module/_cffi_backend/wrapper.py @@ -100,6 +100,11 @@ doc = '%s;\n\nCFFI C function from %s.lib' % (doc, self.modulename) return space.wrap(doc) + def descr_get(self, space, w_obj, w_type=None): + # never bind anything, but a __get__ is still present so that + # pydoc displays useful information (namely, the __repr__) + return self + @jit.unroll_safe def prepare_args(space, rawfunctype, args_w, start_index): @@ -136,5 +141,6 @@ __name__ = interp_attrproperty('fnname', cls=W_FunctionWrapper), __module__ = interp_attrproperty('modulename', cls=W_FunctionWrapper), __doc__ = GetSetProperty(W_FunctionWrapper.descr_get_doc), + __get__ = interp2app(W_FunctionWrapper.descr_get), ) W_FunctionWrapper.typedef.acceptable_as_base_class = False diff --git a/pypy/module/cpyext/__init__.py b/pypy/module/cpyext/__init__.py --- a/pypy/module/cpyext/__init__.py +++ b/pypy/module/cpyext/__init__.py @@ -1,4 +1,5 @@ from pypy.interpreter.mixedmodule import MixedModule +from pypy.interpreter import gateway from pypy.module.cpyext.state import State from pypy.module.cpyext import api @@ -14,6 +15,12 @@ def startup(self, space): space.fromcache(State).startup(space) + method = pypy.module.cpyext.typeobject.get_new_method_def(space) + w_obj = pypy.module.cpyext.methodobject.W_PyCFunctionObject(space, method, space.wrap('')) + space.appexec([space.type(w_obj)], """(methodtype): + from pickle import Pickler + Pickler.dispatch[methodtype] = Pickler.save_global + """) def register_atexit(self, function): if len(self.atexit_funcs) >= 32: @@ -64,6 +71,7 @@ import pypy.module.cpyext.pyfile import pypy.module.cpyext.pystrtod import pypy.module.cpyext.pytraceback +import pypy.module.cpyext.methodobject # now that all rffi_platform.Struct types are registered, configure them api.configure_types() diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -161,12 +161,13 @@ if copy_numpy_headers: try: - dstdir.mkdir('numpy') + dstdir.mkdir('_numpypy') + dstdir.mkdir('_numpypy/numpy') except py.error.EEXIST: pass - numpy_dstdir = dstdir / 'numpy' + numpy_dstdir = dstdir / '_numpypy' / 'numpy' - numpy_include_dir = include_dir / 'numpy' + numpy_include_dir = include_dir / '_numpypy' / 'numpy' numpy_headers = numpy_include_dir.listdir('*.h') + numpy_include_dir.listdir('*.inl') _copy_header_files(numpy_headers, numpy_dstdir) diff --git a/pypy/module/cpyext/include/numpy/README b/pypy/module/cpyext/include/_numpypy/numpy/README rename from pypy/module/cpyext/include/numpy/README rename to pypy/module/cpyext/include/_numpypy/numpy/README diff --git a/pypy/module/cpyext/include/numpy/__multiarray_api.h b/pypy/module/cpyext/include/_numpypy/numpy/__multiarray_api.h rename from pypy/module/cpyext/include/numpy/__multiarray_api.h rename to pypy/module/cpyext/include/_numpypy/numpy/__multiarray_api.h diff --git a/pypy/module/cpyext/include/numpy/arrayobject.h b/pypy/module/cpyext/include/_numpypy/numpy/arrayobject.h rename from pypy/module/cpyext/include/numpy/arrayobject.h rename to pypy/module/cpyext/include/_numpypy/numpy/arrayobject.h diff --git a/pypy/module/cpyext/include/numpy/ndarraytypes.h b/pypy/module/cpyext/include/_numpypy/numpy/ndarraytypes.h rename from pypy/module/cpyext/include/numpy/ndarraytypes.h rename to pypy/module/cpyext/include/_numpypy/numpy/ndarraytypes.h diff --git a/pypy/module/cpyext/include/numpy/npy_3kcompat.h b/pypy/module/cpyext/include/_numpypy/numpy/npy_3kcompat.h rename from pypy/module/cpyext/include/numpy/npy_3kcompat.h rename to pypy/module/cpyext/include/_numpypy/numpy/npy_3kcompat.h diff --git a/pypy/module/cpyext/include/numpy/npy_common.h b/pypy/module/cpyext/include/_numpypy/numpy/npy_common.h rename from pypy/module/cpyext/include/numpy/npy_common.h rename to pypy/module/cpyext/include/_numpypy/numpy/npy_common.h diff --git a/pypy/module/cpyext/include/numpy/old_defines.h b/pypy/module/cpyext/include/_numpypy/numpy/old_defines.h rename from pypy/module/cpyext/include/numpy/old_defines.h rename to pypy/module/cpyext/include/_numpypy/numpy/old_defines.h diff --git a/pypy/module/cpyext/include/cStringIO.h b/pypy/module/cpyext/include/cStringIO.h new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/include/cStringIO.h @@ -0,0 +1,73 @@ +#ifndef Py_CSTRINGIO_H +#define Py_CSTRINGIO_H +#ifdef __cplusplus +extern "C" { +#endif +/* + + This header provides access to cStringIO objects from C. + Functions are provided for calling cStringIO objects and + macros are provided for testing whether you have cStringIO + objects. + + Before calling any of the functions or macros, you must initialize + the routines with: + + PycString_IMPORT + + This would typically be done in your init function. + +*/ + +#define PycStringIO_CAPSULE_NAME "cStringIO.cStringIO_CAPI" + +#define PycString_IMPORT \ + PycStringIO = ((struct PycStringIO_CAPI*)PyCapsule_Import(\ + PycStringIO_CAPSULE_NAME, 0)) + +/* Basic functions to manipulate cStringIO objects from C */ + +static struct PycStringIO_CAPI { + + /* Read a string from an input object. If the last argument + is -1, the remainder will be read. + */ + int(*cread)(PyObject *, char **, Py_ssize_t); + + /* Read a line from an input object. Returns the length of the read + line as an int and a pointer inside the object buffer as char** (so + the caller doesn't have to provide its own buffer as destination). + */ + int(*creadline)(PyObject *, char **); + + /* Write a string to an output object*/ + int(*cwrite)(PyObject *, const char *, Py_ssize_t); + + /* Get the output object as a Python string (returns new reference). */ + PyObject *(*cgetvalue)(PyObject *); + + /* Create a new output object */ + PyObject *(*NewOutput)(int); + + /* Create an input object from a Python string + (copies the Python string reference). + */ + PyObject *(*NewInput)(PyObject *); + + /* The Python types for cStringIO input and output objects. + Note that you can do input on an output object. + */ + PyTypeObject *InputType, *OutputType; + +} *PycStringIO; + +/* These can be used to test if you have one */ +#define PycStringIO_InputCheck(O) \ + (0) /* Py_TYPE(O)==PycStringIO->InputType) */ +#define PycStringIO_OutputCheck(O) \ + (0) /* Py_TYPE(O)==PycStringIO->OutputType) */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CSTRINGIO_H */ diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py --- a/pypy/module/cpyext/methodobject.py +++ b/pypy/module/cpyext/methodobject.py @@ -44,8 +44,8 @@ dealloc=cfunction_dealloc) def cfunction_attach(space, py_obj, w_obj): + assert isinstance(w_obj, W_PyCFunctionObject) py_func = rffi.cast(PyCFunctionObject, py_obj) - assert isinstance(w_obj, W_PyCFunctionObject) py_func.c_m_ml = w_obj.ml py_func.c_m_self = make_ref(space, w_obj.w_self) py_func.c_m_module = make_ref(space, w_obj.w_module) diff --git a/pypy/module/cpyext/src/ndarrayobject.c b/pypy/module/cpyext/src/ndarrayobject.c --- a/pypy/module/cpyext/src/ndarrayobject.c +++ b/pypy/module/cpyext/src/ndarrayobject.c @@ -1,7 +1,7 @@ #include "Python.h" #include "pypy_numpy.h" -#include "numpy/arrayobject.h" +#include "_numpypy/numpy/arrayobject.h" #include <string.h> /* memset, memcpy */ void diff --git a/pypy/module/cpyext/test/test_bytesobject.py b/pypy/module/cpyext/test/test_bytesobject.py --- a/pypy/module/cpyext/test/test_bytesobject.py +++ b/pypy/module/cpyext/test/test_bytesobject.py @@ -252,12 +252,16 @@ lenp = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw') w_text = space.wrapbytes("text") - assert api.PyObject_AsCharBuffer(w_text, bufp, lenp) == 0 + ref = make_ref(space, w_text) + prev_refcnt = ref.c_ob_refcnt + assert api.PyObject_AsCharBuffer(ref, bufp, lenp) == 0 + assert ref.c_ob_refcnt == prev_refcnt assert lenp[0] == 4 assert rffi.charp2str(bufp[0]) == 'text' lltype.free(bufp, flavor='raw') lltype.free(lenp, flavor='raw') + api.Py_DecRef(ref) def test_eq(self, space, api): assert 1 == api._PyBytes_Eq(space.wrapbytes("hello"), space.wrapbytes("hello")) diff --git a/pypy/module/cpyext/test/test_ndarrayobject.py b/pypy/module/cpyext/test/test_ndarrayobject.py --- a/pypy/module/cpyext/test/test_ndarrayobject.py +++ b/pypy/module/cpyext/test/test_ndarrayobject.py @@ -1,4 +1,5 @@ import py +import os from pypy.module.cpyext.test.test_api import BaseApiTest from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase from rpython.rtyper.lltypesystem import rffi, lltype @@ -238,8 +239,10 @@ except: skip('numpy not importable') else: - cls.w_numpy_include = cls.space.wrap([]) - + numpy_incl = os.path.abspath(os.path.dirname(__file__) + + '/../include/_numpypy') + assert os.path.exists(numpy_incl) + cls.w_numpy_include = cls.space.wrap([numpy_incl]) def test_ndarray_object_c(self): mod = self.import_extension('foo', [ diff --git a/pypy/module/micronumpy/compile.py b/pypy/module/micronumpy/compile.py --- a/pypy/module/micronumpy/compile.py +++ b/pypy/module/micronumpy/compile.py @@ -125,7 +125,8 @@ return None def issubtype_w(self, w_sub, w_type): - return w_sub is w_type + is_root(w_type) + return NonConstant(True) def isinstance_w(self, w_obj, w_tp): try: @@ -414,6 +415,10 @@ def warn(self, w_msg, w_warn_type): pass +def is_root(w_obj): + assert isinstance(w_obj, W_Root) +is_root.expecting = W_Root + class FloatObject(W_Root): tp = FakeSpace.w_float def __init__(self, floatval): diff --git a/pypy/module/pypyjit/test_pypy_c/test_00_model.py b/pypy/module/pypyjit/test_pypy_c/test_00_model.py --- a/pypy/module/pypyjit/test_pypy_c/test_00_model.py +++ b/pypy/module/pypyjit/test_pypy_c/test_00_model.py @@ -262,7 +262,7 @@ [i0] i1 = int_add(i0, 1) i2 = int_sub(i1, 10) - i3 = int_floordiv(i2, 100) + i3 = int_xor(i2, 100) i4 = int_mul(i1, 1000) jump(i4) """ @@ -298,7 +298,7 @@ [i0] i1 = int_add(i0, 1) i2 = int_sub(i1, 10) - i3 = int_floordiv(i2, 100) + i3 = int_xor(i2, 100) i4 = int_mul(i1, 1000) jump(i4) """ diff --git a/pypy/module/pypyjit/test_pypy_c/test_shift.py b/pypy/module/pypyjit/test_pypy_c/test_shift.py --- a/pypy/module/pypyjit/test_pypy_c/test_shift.py +++ b/pypy/module/pypyjit/test_pypy_c/test_shift.py @@ -47,26 +47,74 @@ res = 0 a = 0 while a < 300: - assert a >= 0 - assert 0 <= b <= 10 - res = a/b # ID: div + res1 = a/b # ID: div + res2 = a/2 # ID: shift + res3 = a/11 # ID: mul + res += res1 + res2 + res3 a += 1 return res # log = self.run(main, [3]) - assert log.result == 99 + assert log.result == main(3) loop, = log.loops_by_filename(self.filepath) - if sys.maxint == 2147483647: - SHIFT = 31 + assert loop.match_by_id('div', """ + i56 = int_eq(i48, %d) + i57 = int_and(i56, i37) + guard_false(i57, descr=...) + i1 = call_i(_, i48, i3, descr=...) + """ % (-sys.maxint-1,)) + assert loop.match_by_id('shift', """ + i1 = int_rshift(i2, 1) + """) + if sys.maxint > 2**32: + args = (63, -5030930201920786804, 3) else: - SHIFT = 63 - assert loop.match_by_id('div', """ - i10 = int_floordiv(i6, i7) - i11 = int_mul(i10, i7) - i12 = int_sub(i6, i11) - i14 = int_rshift(i12, %d) - i15 = int_add(i10, i14) - """ % SHIFT) + args = (31, -1171354717, 3) + assert loop.match_by_id('mul', """ + i2 = int_rshift(i1, %d) + i3 = int_xor(i1, i2) + i4 = uint_mul_high(i3, %d) + i5 = uint_rshift(i4, %d) + i6 = int_xor(i5, i2) + """ % args) + + def test_modulo_optimization(self): + def main(b): + res = 0 + a = 0 + while a < 300: + res1 = a%b # ID: mod + res2 = a%2 # ID: and + res3 = a%11 # ID: mul + res += res1 + res2 + res3 + a += 1 + return res + # + log = self.run(main, [3]) + assert log.result == main(3) + loop, = log.loops_by_filename(self.filepath) + assert loop.match_by_id('mod', """ + i56 = int_eq(i48, %d) + i57 = int_and(i56, i37) + guard_false(i57, descr=...) + i1 = call_i(_, i48, i3, descr=...) + """ % (-sys.maxint-1,)) + assert loop.match_by_id('and', """ + i1 = int_and(i2, 1) + """) + if sys.maxint > 2**32: + args = (63, -5030930201920786804, 3) + else: + args = (31, -1171354717, 3) + assert loop.match_by_id('mul', """ + i2 = int_rshift(i1, %d) + i3 = int_xor(i1, i2) + i4 = uint_mul_high(i3, %d) + i5 = uint_rshift(i4, %d) + i6 = int_xor(i5, i2) + i7 = int_mul(i6, 11) + i8 = int_sub(i1, i7) + """ % args) def test_division_to_rshift_allcases(self): """ diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py --- a/pypy/module/pypyjit/test_pypy_c/test_string.py +++ b/pypy/module/pypyjit/test_pypy_c/test_string.py @@ -1,11 +1,6 @@ import sys from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC -if sys.maxint == 2147483647: - SHIFT = 31 -else: - SHIFT = 63 - # XXX review the <Call> descrs to replace some EF=5 with EF=4 (elidable) @@ -28,10 +23,7 @@ guard_true(i14, descr=...) guard_not_invalidated(descr=...) i16 = int_eq(i6, %d) - i15 = int_mod(i6, i10) - i17 = int_rshift(i15, %d) - i18 = int_and(i10, i17) - i19 = int_add(i15, i18) + i19 = call_i(ConstClass(ll_int_mod__Signed_Signed), i6, i10, descr=<Calli . ii EF=0 OS=14>) i21 = int_lt(i19, 0) guard_false(i21, descr=...) i22 = int_ge(i19, i10) @@ -49,7 +41,7 @@ i34 = int_add(i6, 1) --TICK-- jump(..., descr=...) - """ % (-sys.maxint-1, SHIFT)) + """ % (-sys.maxint-1,)) def test_long(self): def main(n): @@ -62,19 +54,25 @@ log = self.run(main, [1100], import_site=True) assert log.result == main(1100) loop, = log.loops_by_filename(self.filepath) + if sys.maxint > 2**32: + args = (63, -3689348814741910323, 3) + else: + args = (31, -858993459, 3) assert loop.match(""" i11 = int_lt(i6, i7) guard_true(i11, descr=...) guard_not_invalidated(descr=...) i13 = int_eq(i6, %d) # value provided below - i15 = int_mod(i6, 10) - i17 = int_rshift(i15, %d) # value provided below - i18 = int_and(10, i17) - i19 = int_add(i15, i18) - i21 = int_lt(i19, 0) - guard_false(i21, descr=...) - i22 = int_ge(i19, 10) - guard_false(i22, descr=...) + + # "mod 10" block: + i79 = int_rshift(i6, %d) + i80 = int_xor(i6, i79) + i82 = uint_mul_high(i80, %d) + i84 = uint_rshift(i82, %d) + i85 = int_xor(i84, i79) + i87 = int_mul(i85, 10) + i19 = int_sub(i6, i87) + i23 = strgetitem(p10, i19) p25 = newstr(1) strsetitem(p25, 0, i23) @@ -89,7 +87,7 @@ guard_no_overflow(descr=...) --TICK-- jump(..., descr=...) - """ % (-sys.maxint-1, SHIFT)) + """ % ((-sys.maxint-1,)+args)) def test_str_mod(self): def main(n): diff --git a/pypy/objspace/std/newformat.py b/pypy/objspace/std/newformat.py --- a/pypy/objspace/std/newformat.py +++ b/pypy/objspace/std/newformat.py @@ -7,6 +7,7 @@ from rpython.rlib import rstring, runicode, rlocale, rfloat, jit from rpython.rlib.objectmodel import specialize from rpython.rlib.rfloat import copysign, formatd +from rpython.rlib.rarithmetic import r_uint, intmask @specialize.argtype(1) @@ -836,33 +837,37 @@ return s # This part is slow. negative = value < 0 - value = abs(value) + base = r_uint(base) + value = r_uint(value) + if negative: # change the sign on the unsigned number: otherwise, + value = -value # we'd risk overflow if value==-sys.maxint-1 + # buf = ["\0"] * (8 * 8 + 6) # Too much on 32 bit, but who cares? i = len(buf) - 1 while True: - div = value // base - mod = value - div * base - digit = abs(mod) + div = value // base # unsigned + mod = value - div * base # unsigned, always in range(0,base) + digit = intmask(mod) digit += ord("0") if digit < 10 else ord("a") - 10 buf[i] = chr(digit) - value = div + value = div # unsigned i -= 1 if not value: break - if base == 2: + if base == r_uint(2): buf[i] = "b" buf[i - 1] = "0" - elif base == 8: + elif base == r_uint(8): buf[i] = "o" buf[i - 1] = "0" - elif base == 16: + elif base == r_uint(16): buf[i] = "x" buf[i - 1] = "0" else: buf[i] = "#" - buf[i - 1] = chr(ord("0") + base % 10) - if base > 10: - buf[i - 2] = chr(ord("0") + base // 10) + buf[i - 1] = chr(ord("0") + intmask(base % r_uint(10))) + if base > r_uint(10): + buf[i - 2] = chr(ord("0") + intmask(base // r_uint(10))) i -= 1 i -= 1 if negative: diff --git a/pypy/tool/release/force-builds.py b/pypy/tool/release/force-builds.py --- a/pypy/tool/release/force-builds.py +++ b/pypy/tool/release/force-builds.py @@ -19,16 +19,16 @@ BUILDERS = [ 'own-linux-x86-32', 'own-linux-x86-64', - 'own-linux-armhf', +# 'own-linux-armhf', 'own-win-x86-32', - 'own-linux-s390x-2', + 'own-linux-s390x', # 'own-macosx-x86-32', 'pypy-c-jit-linux-x86-32', 'pypy-c-jit-linux-x86-64', # 'pypy-c-jit-freebsd-9-x86-64', 'pypy-c-jit-macosx-x86-64', 'pypy-c-jit-win-x86-32', - 'pypy-c-jit-linux-s390x-2', + 'pypy-c-jit-linux-s390x', 'build-pypy-c-jit-linux-armhf-raring', 'build-pypy-c-jit-linux-armhf-raspbian', 'build-pypy-c-jit-linux-armel', diff --git a/rpython/config/translationoption.py b/rpython/config/translationoption.py --- a/rpython/config/translationoption.py +++ b/rpython/config/translationoption.py @@ -213,11 +213,6 @@ default=False), BoolOption("merge_if_blocks", "Merge if ... elif chains", cmdline="--if-block-merge", default=True), - BoolOption("raisingop2direct_call", - "Transform operations that can implicitly raise an " - "exception into calls to functions that explicitly " - "raise exceptions", - default=False, cmdline="--raisingop2direct_call"), BoolOption("mallocs", "Remove mallocs", default=True), BoolOption("constfold", "Constant propagation", default=True), diff --git a/rpython/jit/backend/arm/codebuilder.py b/rpython/jit/backend/arm/codebuilder.py --- a/rpython/jit/backend/arm/codebuilder.py +++ b/rpython/jit/backend/arm/codebuilder.py @@ -1,6 +1,5 @@ from rpython.jit.backend.arm import conditions as cond from rpython.jit.backend.arm import registers as reg -from rpython.jit.backend.arm import support from rpython.jit.backend.arm.arch import WORD, PC_OFFSET from rpython.jit.backend.arm.instruction_builder import define_instructions from rpython.jit.backend.llsupport.asmmemmgr import BlockBuilderMixin @@ -17,17 +16,6 @@ sandboxsafe=True) -def binary_helper_call(name): - function = getattr(support, 'arm_%s' % name) - - def f(self, c=cond.AL): - """Generates a call to a helper function, takes its - arguments in r0 and r1, result is placed in r0""" - addr = rffi.cast(lltype.Signed, function) - self.BL(addr, c) - return f - - class AbstractARMBuilder(object): def __init__(self, arch_version=7): self.arch_version = arch_version @@ -348,10 +336,6 @@ self.write32(c << 28 | 0x157ff05f) - DIV = binary_helper_call('int_div') - MOD = binary_helper_call('int_mod') - UDIV = binary_helper_call('uint_div') - FMDRR = VMOV_cr # uh, there are synonyms? FMRRD = VMOV_rc diff --git a/rpython/jit/backend/arm/helper/assembler.py b/rpython/jit/backend/arm/helper/assembler.py --- a/rpython/jit/backend/arm/helper/assembler.py +++ b/rpython/jit/backend/arm/helper/assembler.py @@ -46,20 +46,6 @@ f.__name__ = 'emit_op_%s' % name return f -def gen_emit_op_by_helper_call(name, opname): - helper = getattr(InstrBuilder, opname) - def f(self, op, arglocs, regalloc, fcond): - assert fcond is not None - if op.type != 'v': - regs = r.caller_resp[1:] + [r.ip] - else: - regs = r.caller_resp - with saved_registers(self.mc, regs, r.caller_vfp_resp): - helper(self.mc, fcond) - return fcond - f.__name__ = 'emit_op_%s' % name - return f - def gen_emit_cmp_op(name, true_cond): def f(self, op, arglocs, regalloc, fcond): l0, l1, res = arglocs diff --git a/rpython/jit/backend/arm/helper/regalloc.py b/rpython/jit/backend/arm/helper/regalloc.py --- a/rpython/jit/backend/arm/helper/regalloc.py +++ b/rpython/jit/backend/arm/helper/regalloc.py @@ -72,25 +72,6 @@ res = self.force_allocate_reg_or_cc(op) return [loc1, loc2, res] -def prepare_op_by_helper_call(name): - def f(self, op, fcond): - assert fcond is not None - a0 = op.getarg(0) - a1 = op.getarg(1) - arg1 = self.rm.make_sure_var_in_reg(a0, selected_reg=r.r0) - arg2 = self.rm.make_sure_var_in_reg(a1, selected_reg=r.r1) - assert arg1 == r.r0 - assert arg2 == r.r1 - if not isinstance(a0, Const) and self.stays_alive(a0): - self.force_spill_var(a0) - self.possibly_free_vars_for_op(op) - self.free_temp_vars() - self.after_call(op) - self.possibly_free_var(op) - return [] - f.__name__ = name - return f - def prepare_int_cmp(self, op, fcond): assert fcond is not None boxes = list(op.getarglist()) diff --git a/rpython/jit/backend/arm/opassembler.py b/rpython/jit/backend/arm/opassembler.py --- a/rpython/jit/backend/arm/opassembler.py +++ b/rpython/jit/backend/arm/opassembler.py @@ -3,7 +3,7 @@ from rpython.jit.backend.arm import registers as r from rpython.jit.backend.arm import shift from rpython.jit.backend.arm.arch import WORD, DOUBLE_WORD, JITFRAME_FIXED_SIZE -from rpython.jit.backend.arm.helper.assembler import (gen_emit_op_by_helper_call, +from rpython.jit.backend.arm.helper.assembler import ( gen_emit_op_unary_cmp, gen_emit_op_ri, gen_emit_cmp_op, @@ -92,6 +92,11 @@ self.mc.MUL(res.value, reg1.value, reg2.value) return fcond + def emit_op_uint_mul_high(self, op, arglocs, regalloc, fcond): + reg1, reg2, res = arglocs + self.mc.UMULL(r.ip.value, res.value, reg1.value, reg2.value) + return fcond + def emit_op_int_force_ge_zero(self, op, arglocs, regalloc, fcond): arg, res = arglocs self.mc.CMP_ri(arg.value, 0) @@ -132,10 +137,6 @@ self.guard_success_cc = c.VC return fcond - emit_op_int_floordiv = gen_emit_op_by_helper_call('int_floordiv', 'DIV') - emit_op_int_mod = gen_emit_op_by_helper_call('int_mod', 'MOD') - emit_op_uint_floordiv = gen_emit_op_by_helper_call('uint_floordiv', 'UDIV') - emit_op_int_and = gen_emit_op_ri('int_and', 'AND') emit_op_int_or = gen_emit_op_ri('int_or', 'ORR') emit_op_int_xor = gen_emit_op_ri('int_xor', 'EOR') @@ -466,7 +467,11 @@ assert saveerrloc.is_imm() cb.emit_call_release_gil(saveerrloc.value) else: - cb.emit() + effectinfo = descr.get_extra_info() + if effectinfo is None or effectinfo.check_can_collect(): + cb.emit() + else: + cb.emit_no_collect() return fcond def _genop_same_as(self, op, arglocs, regalloc, fcond): diff --git a/rpython/jit/backend/arm/regalloc.py b/rpython/jit/backend/arm/regalloc.py --- a/rpython/jit/backend/arm/regalloc.py +++ b/rpython/jit/backend/arm/regalloc.py @@ -7,7 +7,7 @@ from rpython.jit.backend.arm import conditions as c from rpython.jit.backend.arm import locations from rpython.jit.backend.arm.locations import imm, get_fp_offset -from rpython.jit.backend.arm.helper.regalloc import (prepare_op_by_helper_call, +from rpython.jit.backend.arm.helper.regalloc import ( prepare_unary_cmp, prepare_op_ri, prepare_int_cmp, @@ -397,9 +397,9 @@ else: self.rm.force_spill_var(var) - def before_call(self, force_store=[], save_all_regs=False): - self.rm.before_call(force_store, save_all_regs) - self.vfprm.before_call(force_store, save_all_regs) + def before_call(self, save_all_regs=False): + self.rm.before_call(save_all_regs) + self.vfprm.before_call(save_all_regs) def _sync_var(self, v): if v.type == FLOAT: @@ -467,6 +467,8 @@ self.possibly_free_var(op) return [reg1, reg2, res] + prepare_op_uint_mul_high = prepare_op_int_mul + def prepare_op_int_force_ge_zero(self, op, fcond): argloc = self.make_sure_var_in_reg(op.getarg(0)) resloc = self.force_allocate_reg(op, [op.getarg(0)]) @@ -478,10 +480,6 @@ resloc = self.force_allocate_reg(op) return [argloc, imm(numbytes), resloc] - prepare_op_int_floordiv = prepare_op_by_helper_call('int_floordiv') - prepare_op_int_mod = prepare_op_by_helper_call('int_mod') - prepare_op_uint_floordiv = prepare_op_by_helper_call('unit_floordiv') - prepare_op_int_and = prepare_op_ri('int_and') prepare_op_int_or = prepare_op_ri('int_or') prepare_op_int_xor = prepare_op_ri('int_xor') @@ -554,8 +552,7 @@ prepare_op_call_f = _prepare_op_call prepare_op_call_n = _prepare_op_call - def _prepare_call(self, op, force_store=[], save_all_regs=False, - first_arg_index=1): + def _prepare_call(self, op, save_all_regs=False, first_arg_index=1): args = [None] * (op.numargs() + 3) calldescr = op.getdescr() assert isinstance(calldescr, CallDescr) @@ -573,17 +570,27 @@ args[1] = imm(size) args[2] = sign_loc - args[0] = self._call(op, args, force_store, save_all_regs) + effectinfo = calldescr.get_extra_info() + if save_all_regs: + gc_level = 2 + elif effectinfo is None or effectinfo.check_can_collect(): + gc_level = 1 + else: + gc_level = 0 + + args[0] = self._call(op, args, gc_level) return args - def _call(self, op, arglocs, force_store=[], save_all_regs=False): - # spill variables that need to be saved around calls - self.vfprm.before_call(force_store, save_all_regs=save_all_regs) - if not save_all_regs: - gcrootmap = self.cpu.gc_ll_descr.gcrootmap - if gcrootmap and gcrootmap.is_shadow_stack: - save_all_regs = 2 - self.rm.before_call(force_store, save_all_regs=save_all_regs) + def _call(self, op, arglocs, gc_level): + # spill variables that need to be saved around calls: + # gc_level == 0: callee cannot invoke the GC + # gc_level == 1: can invoke GC, save all regs that contain pointers + # gc_level == 2: can force, save all regs + save_all_regs = gc_level == 2 + self.vfprm.before_call(save_all_regs=save_all_regs) + if gc_level == 1 and self.cpu.gc_ll_descr.gcrootmap: + save_all_regs = 2 + self.rm.before_call(save_all_regs=save_all_regs) resloc = self.after_call(op) return resloc @@ -1070,7 +1077,7 @@ def _prepare_op_call_assembler(self, op, fcond): locs = self.locs_for_call_assembler(op) tmploc = self.get_scratch_reg(INT, selected_reg=r.r0) - resloc = self._call(op, locs + [tmploc], save_all_regs=True) + resloc = self._call(op, locs + [tmploc], gc_level=2) return locs + [resloc, tmploc] prepare_op_call_assembler_i = _prepare_op_call_assembler diff --git a/rpython/jit/backend/arm/support.py b/rpython/jit/backend/arm/support.py deleted file mode 100644 --- a/rpython/jit/backend/arm/support.py +++ /dev/null @@ -1,54 +0,0 @@ -from rpython.rtyper.lltypesystem import lltype, rffi, llmemory -from rpython.rlib.rarithmetic import r_uint -from rpython.translator.tool.cbuild import ExternalCompilationInfo - -eci = ExternalCompilationInfo(post_include_bits=[""" -static int pypy__arm_int_div(int a, int b) { - return a/b; -} -static unsigned int pypy__arm_uint_div(unsigned int a, unsigned int b) { - return a/b; -} -static int pypy__arm_int_mod(int a, int b) { - return a % b; -} -"""]) - - -def arm_int_div_emulator(a, b): - return int(a / float(b)) -arm_int_div_sign = lltype.Ptr( - lltype.FuncType([lltype.Signed, lltype.Signed], lltype.Signed)) -arm_int_div = rffi.llexternal( - "pypy__arm_int_div", [lltype.Signed, lltype.Signed], lltype.Signed, - _callable=arm_int_div_emulator, - compilation_info=eci, - _nowrapper=True, elidable_function=True) - - -def arm_uint_div_emulator(a, b): - return r_uint(a) / r_uint(b) -arm_uint_div_sign = lltype.Ptr( - lltype.FuncType([lltype.Unsigned, lltype.Unsigned], lltype.Unsigned)) -arm_uint_div = rffi.llexternal( - "pypy__arm_uint_div", [lltype.Unsigned, lltype.Unsigned], lltype.Unsigned, - _callable=arm_uint_div_emulator, - compilation_info=eci, - _nowrapper=True, elidable_function=True) - - -def arm_int_mod_emulator(a, b): - sign = 1 - if a < 0: - a = -1 * a - sign = -1 - if b < 0: - b = -1 * b - res = a % b - return sign * res -arm_int_mod_sign = arm_int_div_sign -arm_int_mod = rffi.llexternal( - "pypy__arm_int_mod", [lltype.Signed, lltype.Signed], lltype.Signed, - _callable=arm_int_mod_emulator, - compilation_info=eci, - _nowrapper=True, elidable_function=True) diff --git a/rpython/jit/backend/arm/test/test_arch.py b/rpython/jit/backend/arm/test/test_arch.py deleted file mode 100644 --- a/rpython/jit/backend/arm/test/test_arch.py +++ /dev/null @@ -1,23 +0,0 @@ -from rpython.jit.backend.arm import support - -def test_mod(): - assert support.arm_int_mod(10, 2) == 0 - assert support.arm_int_mod(11, 2) == 1 - assert support.arm_int_mod(11, 3) == 2 - -def test_mod2(): - assert support.arm_int_mod(-10, 2) == 0 - assert support.arm_int_mod(-11, 2) == -1 - assert support.arm_int_mod(-11, 3) == -2 - -def test_mod3(): - assert support.arm_int_mod(10, -2) == 0 - assert support.arm_int_mod(11, -2) == 1 - assert support.arm_int_mod(11, -3) == 2 - - -def test_div(): - assert support.arm_int_div(-7, 2) == -3 - assert support.arm_int_div(9, 2) == 4 - assert support.arm_int_div(10, 5) == 2 - diff --git a/rpython/jit/backend/arm/test/test_assembler.py b/rpython/jit/backend/arm/test/test_assembler.py --- a/rpython/jit/backend/arm/test/test_assembler.py +++ b/rpython/jit/backend/arm/test/test_assembler.py @@ -193,32 +193,6 @@ self.a.gen_func_epilog() assert run_asm(self.a) == 61 - def test_DIV(self): - self.a.gen_func_prolog() - self.a.mc.MOV_ri(r.r0.value, 123) - self.a.mc.MOV_ri(r.r1.value, 2) - self.a.mc.DIV() - self.a.gen_func_epilog() - assert run_asm(self.a) == 61 - - def test_DIV2(self): - self.a.gen_func_prolog() - self.a.mc.gen_load_int(r.r0.value, -110) - self.a.mc.gen_load_int(r.r1.value, 3) - self.a.mc.DIV() - self.a.gen_func_epilog() - assert run_asm(self.a) == -36 - - def test_DIV3(self): - self.a.gen_func_prolog() - self.a.mc.gen_load_int(r.r8.value, 110) - self.a.mc.gen_load_int(r.r9.value, -3) - self.a.mc.MOV_rr(r.r0.value, r.r8.value) - self.a.mc.MOV_rr(r.r1.value, r.r9.value) - self.a.mc.DIV() - self.a.gen_func_epilog() - assert run_asm(self.a) == -36 - def test_bl_with_conditional_exec(self): functype = lltype.Ptr(lltype.FuncType([lltype.Signed], lltype.Signed)) call_addr = rffi.cast(lltype.Signed, llhelper(functype, callme)) diff --git a/rpython/jit/backend/llsupport/regalloc.py b/rpython/jit/backend/llsupport/regalloc.py --- a/rpython/jit/backend/llsupport/regalloc.py +++ b/rpython/jit/backend/llsupport/regalloc.py @@ -574,27 +574,113 @@ self.assembler.regalloc_mov(reg, to) # otherwise it's clean + def _bc_spill(self, v, new_free_regs): + self._sync_var(v) + new_free_regs.append(self.reg_bindings.pop(v)) + def before_call(self, force_store=[], save_all_regs=0): - """ Spill registers before a call, as described by - 'self.save_around_call_regs'. Registers are not spilled if - they don't survive past the current operation, unless they - are listed in 'force_store'. 'save_all_regs' can be 0 (default), - 1 (save all), or 2 (save default+PTRs). + """Spill or move some registers before a call. By default, + this means: for every register in 'self.save_around_call_regs', + if there is a variable there and it survives longer than + the current operation, then it is spilled/moved somewhere else. + + 'save_all_regs' can be 0 (default set of registers), 1 (do that + for all registers), or 2 (default + gc ptrs). + + Overview of what we do (the implementation does it differently, + for the same result): + + * we first check the set of registers that are free: call it F. + + * possibly_free_vars() is implied for all variables (except + the ones listed in force_store): if they don't survive past + the current operation, they are forgotten now. (Their + register remain not in F, because they are typically + arguments to the call, so they should not be overwritten by + the next step.) + + * then for every variable that needs to be spilled/moved: if + there is an entry in F that is acceptable, pick it and emit a + move. Otherwise, emit a spill. Start doing this with the + variables that survive the shortest time, to give them a + better change to remain in a register---similar algo as + _pick_variable_to_spill(). + + Note: when a register is moved, it often (but not always) means + we could have been more clever and picked a better register in + the first place, when we did so earlier. It is done this way + anyway, as a local hack in this function, because on x86 CPUs + such register-register moves are almost free. """ + new_free_regs = [] + move_or_spill = [] + for v, reg in self.reg_bindings.items(): - if v not in force_store and self.longevity[v][1] <= self.position: + max_age = self.longevity[v][1] + if v not in force_store and max_age <= self.position: # variable dies del self.reg_bindings[v] - self.free_regs.append(reg) + new_free_regs.append(reg) continue - if save_all_regs != 1 and reg not in self.save_around_call_regs: - if save_all_regs == 0: - continue # we don't have to - if v.type != REF: - continue # only save GC pointers - self._sync_var(v) - del self.reg_bindings[v] - self.free_regs.append(reg) + + if save_all_regs == 1: + # we need to spill all registers in this mode + self._bc_spill(v, new_free_regs) + # + elif save_all_regs == 2 and v.type == REF: + # we need to spill all GC ptrs in this mode + self._bc_spill(v, new_free_regs) + # + elif reg not in self.save_around_call_regs: + continue # in a register like ebx/rbx: it is fine where it is + # + else: + # this is a register like eax/rax, which needs either + # spilling or moving. + move_or_spill.append((v, max_age)) + + if len(move_or_spill) > 0: + while len(self.free_regs) > 0: + new_reg = self.free_regs.pop() + if new_reg in self.save_around_call_regs: + new_free_regs.append(new_reg) # not this register... + continue + # This 'new_reg' is suitable for moving a candidate to. + # Pick the one with the smallest max_age. (This + # is one step of a naive sorting algo, slow in theory, + # but the list should always be very small so it + # doesn't matter.) + best_i = 0 + smallest_max_age = move_or_spill[0][1] + for i in range(1, len(move_or_spill)): + max_age = move_or_spill[i][1] + if max_age < smallest_max_age: + best_i = i + smallest_max_age = max_age + v, max_age = move_or_spill.pop(best_i) + # move from 'reg' to 'new_reg' + reg = self.reg_bindings[v] + if not we_are_translated(): + if move_or_spill: + assert max_age <= min([_a for _, _a in move_or_spill]) + assert reg in self.save_around_call_regs + assert new_reg not in self.save_around_call_regs + self.assembler.regalloc_mov(reg, new_reg) + self.reg_bindings[v] = new_reg # change the binding + new_free_regs.append(reg) + # + if len(move_or_spill) == 0: + break + else: + # no more free registers to move to, spill the rest + for v, max_age in move_or_spill: + self._bc_spill(v, new_free_regs) + + # re-add registers in 'new_free_regs', but in reverse order, + # so that the last ones (added just above, from + # save_around_call_regs) are picked last by future '.pop()' + while len(new_free_regs) > 0: + self.free_regs.append(new_free_regs.pop()) def after_call(self, v): """ Adjust registers according to the result of the call, diff --git a/rpython/jit/backend/llsupport/test/test_regalloc_integration.py b/rpython/jit/backend/llsupport/test/test_regalloc_integration.py --- a/rpython/jit/backend/llsupport/test/test_regalloc_integration.py +++ b/rpython/jit/backend/llsupport/test/test_regalloc_integration.py @@ -496,22 +496,6 @@ self.interpret(ops, [s, ord('a')]) assert s[1] == 'a' - def test_division_optimized(self): - ops = ''' - [i7, i6] - label(i7, i6, descr=targettoken) - i18 = int_floordiv(i7, i6) - i19 = int_xor(i7, i6) - i21 = int_lt(i19, 0) - i22 = int_mod(i7, i6) - i23 = int_is_true(i22) - i24 = int_eq(i6, 4) - guard_false(i24) [i18] - jump(i18, i6, descr=targettoken) - ''' - self.interpret(ops, [10, 4]) - assert self.getint(0) == 2 - # FIXME: Verify that i19 - i23 are removed class TestRegallocFloats(BaseTestRegalloc): def setup_class(cls): diff --git a/rpython/jit/backend/ppc/opassembler.py b/rpython/jit/backend/ppc/opassembler.py --- a/rpython/jit/backend/ppc/opassembler.py +++ b/rpython/jit/backend/ppc/opassembler.py @@ -62,6 +62,12 @@ else: self.mc.mulld(res.value, l0.value, l1.value) + def emit_uint_mul_high(self, op, arglocs, regalloc): + l0, l1, res = arglocs + assert not l0.is_imm() + assert not l1.is_imm() + self.mc.mulhdu(res.value, l0.value, l1.value) + def do_emit_int_binary_ovf(self, op, arglocs): l0, l1, res = arglocs[0], arglocs[1], arglocs[2] self.mc.load_imm(r.SCRATCH, 0) @@ -80,24 +86,6 @@ else: self.mc.mulldox(*self.do_emit_int_binary_ovf(op, arglocs)) - def emit_int_floordiv(self, op, arglocs, regalloc): - l0, l1, res = arglocs - if IS_PPC_32: - self.mc.divw(res.value, l0.value, l1.value) - else: - self.mc.divd(res.value, l0.value, l1.value) - - def emit_int_mod(self, op, arglocs, regalloc): - l0, l1, res = arglocs - if IS_PPC_32: - self.mc.divw(r.r0.value, l0.value, l1.value) - self.mc.mullw(r.r0.value, r.r0.value, l1.value) - else: - self.mc.divd(r.r0.value, l0.value, l1.value) - self.mc.mulld(r.r0.value, r.r0.value, l1.value) - self.mc.subf(r.r0.value, r.r0.value, l0.value) - self.mc.mr(res.value, r.r0.value) - def emit_int_and(self, op, arglocs, regalloc): l0, l1, res = arglocs self.mc.and_(res.value, l0.value, l1.value) @@ -130,13 +118,6 @@ self.mc.srw(res.value, l0.value, l1.value) else: self.mc.srd(res.value, l0.value, l1.value) - - def emit_uint_floordiv(self, op, arglocs, regalloc): - l0, l1, res = arglocs - if IS_PPC_32: - self.mc.divwu(res.value, l0.value, l1.value) - else: - self.mc.divdu(res.value, l0.value, l1.value) emit_int_le = gen_emit_cmp_op(c.LE) emit_int_lt = gen_emit_cmp_op(c.LT) @@ -622,7 +603,11 @@ assert saveerrloc.is_imm() cb.emit_call_release_gil(saveerrloc.value) else: - cb.emit() + effectinfo = descr.get_extra_info() + if effectinfo is None or effectinfo.check_can_collect(): + cb.emit() + else: + cb.emit_no_collect() def _genop_call(self, op, arglocs, regalloc): oopspecindex = regalloc.get_oopspecindex(op) diff --git a/rpython/jit/backend/ppc/regalloc.py b/rpython/jit/backend/ppc/regalloc.py --- a/rpython/jit/backend/ppc/regalloc.py +++ b/rpython/jit/backend/ppc/regalloc.py @@ -1,6 +1,7 @@ from rpython.jit.backend.llsupport.regalloc import (RegisterManager, FrameManager, TempVar, compute_vars_longevity, BaseRegalloc) +from rpython.jit.backend.llsupport.descr import CallDescr from rpython.jit.backend.ppc.arch import (WORD, MY_COPY_OF_REGS, IS_PPC_32) from rpython.jit.codewriter import longlong from rpython.jit.backend.ppc.jump import (remap_frame_layout, @@ -369,9 +370,9 @@ # This operation is used only for testing self.force_spill_var(op.getarg(0)) - def before_call(self, force_store=[], save_all_regs=False): - self.rm.before_call(force_store, save_all_regs) - self.fprm.before_call(force_store, save_all_regs) + def before_call(self, save_all_regs=False): + self.rm.before_call(save_all_regs) + self.fprm.before_call(save_all_regs) def after_call(self, v): if v.type == FLOAT: @@ -432,15 +433,13 @@ prepare_int_mul = helper.prepare_int_add_or_mul prepare_nursery_ptr_increment = prepare_int_add - prepare_int_floordiv = helper.prepare_binary_op - prepare_int_mod = helper.prepare_binary_op prepare_int_and = helper.prepare_binary_op prepare_int_or = helper.prepare_binary_op prepare_int_xor = helper.prepare_binary_op prepare_int_lshift = helper.prepare_binary_op prepare_int_rshift = helper.prepare_binary_op prepare_uint_rshift = helper.prepare_binary_op - prepare_uint_floordiv = helper.prepare_binary_op + prepare_uint_mul_high = helper.prepare_binary_op prepare_int_add_ovf = helper.prepare_binary_op prepare_int_sub_ovf = helper.prepare_binary_op @@ -758,7 +757,7 @@ src_ofs_loc = self.ensure_reg_or_any_imm(op.getarg(2)) dst_ofs_loc = self.ensure_reg_or_any_imm(op.getarg(3)) length_loc = self.ensure_reg_or_any_imm(op.getarg(4)) - self._spill_before_call(save_all_regs=False) + self._spill_before_call(gc_level=0) return [src_ptr_loc, dst_ptr_loc, src_ofs_loc, dst_ofs_loc, length_loc] @@ -791,13 +790,15 @@ prepare_call_f = _prepare_call prepare_call_n = _prepare_call - def _spill_before_call(self, save_all_regs=False): - # spill variables that need to be saved around calls + def _spill_before_call(self, gc_level): + # spill variables that need to be saved around calls: + # gc_level == 0: callee cannot invoke the GC + # gc_level == 1: can invoke GC, save all regs that contain pointers + # gc_level == 2: can force, save all regs + save_all_regs = gc_level == 2 self.fprm.before_call(save_all_regs=save_all_regs) - if not save_all_regs: - gcrootmap = self.assembler.cpu.gc_ll_descr.gcrootmap - if gcrootmap and gcrootmap.is_shadow_stack: - save_all_regs = 2 + if gc_level == 1 and self.cpu.gc_ll_descr.gcrootmap: + save_all_regs = 2 self.rm.before_call(save_all_regs=save_all_regs) def _prepare_call(self, op, save_all_regs=False): @@ -805,7 +806,18 @@ args.append(None) for i in range(op.numargs()): args.append(self.loc(op.getarg(i))) - self._spill_before_call(save_all_regs) + + calldescr = op.getdescr() + assert isinstance(calldescr, CallDescr) + effectinfo = calldescr.get_extra_info() + if save_all_regs: + gc_level = 2 + elif effectinfo is None or effectinfo.check_can_collect(): + gc_level = 1 + else: + gc_level = 0 + self._spill_before_call(gc_level=gc_level) + if op.type != VOID: resloc = self.after_call(op) args[0] = resloc @@ -934,7 +946,7 @@ def _prepare_call_assembler(self, op): locs = self.locs_for_call_assembler(op) - self._spill_before_call(save_all_regs=True) + self._spill_before_call(gc_level=2) if op.type != VOID: resloc = self.after_call(op) else: diff --git a/rpython/jit/backend/test/test_random.py b/rpython/jit/backend/test/test_random.py --- a/rpython/jit/backend/test/test_random.py +++ b/rpython/jit/backend/test/test_random.py @@ -532,6 +532,7 @@ rop.INT_AND, rop.INT_OR, rop.INT_XOR, + rop.UINT_MUL_HIGH, ]: OPERATIONS.append(BinaryOperation(_op)) @@ -548,8 +549,8 @@ ]: OPERATIONS.append(BinaryOperation(_op, boolres=True)) -OPERATIONS.append(BinaryOperation(rop.INT_FLOORDIV, ~3, 2)) -OPERATIONS.append(BinaryOperation(rop.INT_MOD, ~3, 2)) +#OPERATIONS.append(BinaryOperation(rop.INT_FLOORDIV, ~3, 2)) +#OPERATIONS.append(BinaryOperation(rop.INT_MOD, ~3, 2)) OPERATIONS.append(BinaryOperation(rop.INT_RSHIFT, LONG_BIT-1)) OPERATIONS.append(BinaryOperation(rop.INT_LSHIFT, LONG_BIT-1)) OPERATIONS.append(BinaryOperation(rop.UINT_RSHIFT, LONG_BIT-1)) diff --git a/rpython/jit/backend/x86/assembler.py b/rpython/jit/backend/x86/assembler.py --- a/rpython/jit/backend/x86/assembler.py +++ b/rpython/jit/backend/x86/assembler.py @@ -1289,6 +1289,9 @@ genop_float_mul = _binaryop('MULSD') genop_float_truediv = _binaryop('DIVSD') + def genop_uint_mul_high(self, op, arglocs, result_loc): + self.mc.MUL(arglocs[0]) + def genop_int_and(self, op, arglocs, result_loc): arg1 = arglocs[1] if IS_X86_64 and (isinstance(arg1, ImmedLoc) and @@ -1444,20 +1447,6 @@ self.mov(imm0, resloc) self.mc.CMOVNS(resloc, arglocs[0]) - def genop_int_mod(self, op, arglocs, resloc): - if IS_X86_32: - self.mc.CDQ() - elif IS_X86_64: - self.mc.CQO() - - self.mc.IDIV_r(ecx.value) - - genop_int_floordiv = genop_int_mod - - def genop_uint_floordiv(self, op, arglocs, resloc): - self.mc.XOR_rr(edx.value, edx.value) - self.mc.DIV_r(ecx.value) - genop_llong_add = _binaryop("PADDQ") genop_llong_sub = _binaryop("PSUBQ") genop_llong_and = _binaryop("PAND") @@ -2123,7 +2112,11 @@ assert isinstance(saveerrloc, ImmedLoc) cb.emit_call_release_gil(saveerrloc.value) else: - cb.emit() + effectinfo = descr.get_extra_info() + if effectinfo is None or effectinfo.check_can_collect(): + cb.emit() + else: + cb.emit_no_collect() def _store_force_index(self, guard_op): assert (guard_op.getopnum() == rop.GUARD_NOT_FORCED or diff --git a/rpython/jit/backend/x86/regalloc.py b/rpython/jit/backend/x86/regalloc.py --- a/rpython/jit/backend/x86/regalloc.py +++ b/rpython/jit/backend/x86/regalloc.py @@ -561,6 +561,27 @@ consider_int_sub_ovf = _consider_binop consider_int_add_ovf = _consider_binop_symm + def consider_uint_mul_high(self, op): + arg1, arg2 = op.getarglist() + # should support all cases, but is optimized for (box, const) + if isinstance(arg1, Const): + arg1, arg2 = arg2, arg1 + self.rm.make_sure_var_in_reg(arg2, selected_reg=eax) + l1 = self.loc(arg1) + # l1 is a register != eax, or stack_bp; or, just possibly, it + # can be == eax if arg1 is arg2 + assert not isinstance(l1, ImmedLoc) + assert l1 is not eax or arg1 is arg2 + # + # eax will be trash after the operation + self.rm.possibly_free_var(arg2) + tmpvar = TempVar() + self.rm.force_allocate_reg(tmpvar, selected_reg=eax) + self.rm.possibly_free_var(tmpvar) + # + self.rm.force_allocate_reg(op, selected_reg=edx) + self.perform(op, [l1], edx) + def consider_int_neg(self, op): res = self.rm.force_result_in_reg(op, op.getarg(0)) self.perform(op, [res], res) @@ -585,29 +606,6 @@ consider_int_rshift = consider_int_lshift consider_uint_rshift = consider_int_lshift - def _consider_int_div_or_mod(self, op, resultreg, trashreg): - l0 = self.rm.make_sure_var_in_reg(op.getarg(0), selected_reg=eax) - l1 = self.rm.make_sure_var_in_reg(op.getarg(1), selected_reg=ecx) - l2 = self.rm.force_allocate_reg(op, selected_reg=resultreg) - # the register (eax or edx) not holding what we are looking for - # will be just trash after that operation - tmpvar = TempVar() - self.rm.force_allocate_reg(tmpvar, selected_reg=trashreg) - assert l0 is eax - assert l1 is ecx - assert l2 is resultreg - self.rm.possibly_free_var(tmpvar) - - def consider_int_mod(self, op): - self._consider_int_div_or_mod(op, edx, eax) - self.perform(op, [eax, ecx], edx) - - def consider_int_floordiv(self, op): - self._consider_int_div_or_mod(op, eax, edx) - self.perform(op, [eax, ecx], eax) - - consider_uint_floordiv = consider_int_floordiv - def _consider_compop(self, op): vx = op.getarg(0) vy = op.getarg(1) @@ -797,22 +795,22 @@ else: self._consider_call(op) - def _call(self, op, arglocs, force_store=[], guard_not_forced=False): + def _call(self, op, arglocs, gc_level): # we need to save registers on the stack: # # - at least the non-callee-saved registers # - # - we assume that any call can collect, and we - # save also the callee-saved registers that contain GC pointers + # - if gc_level > 0, we save also the callee-saved registers that + # contain GC pointers # - # - for CALL_MAY_FORCE or CALL_ASSEMBLER, we have to save all regs - # anyway, in case we need to do cpu.force(). The issue is that - # grab_frame_values() would not be able to locate values in - # callee-saved registers. + # - gc_level == 2 for CALL_MAY_FORCE or CALL_ASSEMBLER. We + # have to save all regs anyway, in case we need to do + # cpu.force(). The issue is that grab_frame_values() would + # not be able to locate values in callee-saved registers. # - save_all_regs = guard_not_forced - self.xrm.before_call(force_store, save_all_regs=save_all_regs) - if not save_all_regs: + save_all_regs = gc_level == 2 + self.xrm.before_call(save_all_regs=save_all_regs) + if gc_level == 1: gcrootmap = self.assembler.cpu.gc_ll_descr.gcrootmap # we save all the registers for shadowstack and asmgcc for now # --- for asmgcc too: we can't say "register x is a gc ref" @@ -820,7 +818,7 @@ # more for now. if gcrootmap: # and gcrootmap.is_shadow_stack: save_all_regs = 2 - self.rm.before_call(force_store, save_all_regs=save_all_regs) + self.rm.before_call(save_all_regs=save_all_regs) if op.type != 'v': if op.type == FLOAT: resloc = self.xrm.after_call(op) @@ -840,9 +838,18 @@ sign_loc = imm1 else: sign_loc = imm0 + # + effectinfo = calldescr.get_extra_info() + if guard_not_forced: + gc_level = 2 + elif effectinfo is None or effectinfo.check_can_collect(): + gc_level = 1 + else: + gc_level = 0 + # self._call(op, [imm(size), sign_loc] + [self.loc(op.getarg(i)) for i in range(op.numargs())], - guard_not_forced=guard_not_forced) + gc_level=gc_level) def _consider_real_call(self, op): effectinfo = op.getdescr().get_extra_info() @@ -901,7 +908,7 @@ def _consider_call_assembler(self, op): locs = self.locs_for_call_assembler(op) - self._call(op, locs, guard_not_forced=True) + self._call(op, locs, gc_level=2) consider_call_assembler_i = _consider_call_assembler consider_call_assembler_r = _consider_call_assembler consider_call_assembler_f = _consider_call_assembler diff --git a/rpython/jit/backend/x86/regloc.py b/rpython/jit/backend/x86/regloc.py --- a/rpython/jit/backend/x86/regloc.py +++ b/rpython/jit/backend/x86/regloc.py @@ -641,6 +641,7 @@ SUB = _binaryop('SUB') IMUL = _binaryop('IMUL') NEG = _unaryop('NEG') + MUL = _unaryop('MUL') CMP = _binaryop('CMP') CMP16 = _binaryop('CMP16') diff --git a/rpython/jit/backend/x86/rx86.py b/rpython/jit/backend/x86/rx86.py --- a/rpython/jit/backend/x86/rx86.py +++ b/rpython/jit/backend/x86/rx86.py @@ -558,6 +558,9 @@ DIV_r = insn(rex_w, '\xF7', register(1), '\xF0') IDIV_r = insn(rex_w, '\xF7', register(1), '\xF8') + MUL_r = insn(rex_w, '\xF7', orbyte(4<<3), register(1), '\xC0') + MUL_b = insn(rex_w, '\xF7', orbyte(4<<3), stack_bp(1)) + IMUL_rr = insn(rex_w, '\x0F\xAF', register(1, 8), register(2), '\xC0') IMUL_rb = insn(rex_w, '\x0F\xAF', register(1, 8), stack_bp(2)) diff --git a/rpython/jit/codewriter/call.py b/rpython/jit/codewriter/call.py --- a/rpython/jit/codewriter/call.py +++ b/rpython/jit/codewriter/call.py @@ -14,6 +14,7 @@ from rpython.translator.backendopt.canraise import RaiseAnalyzer from rpython.translator.backendopt.writeanalyze import ReadWriteAnalyzer from rpython.translator.backendopt.graphanalyze import DependencyTracker +from rpython.translator.backendopt.collectanalyze import CollectAnalyzer class CallControl(object): @@ -37,9 +38,9 @@ self.virtualizable_analyzer = VirtualizableAnalyzer(translator) self.quasiimmut_analyzer = QuasiImmutAnalyzer(translator) self.randomeffects_analyzer = RandomEffectsAnalyzer(translator) - self.seen = DependencyTracker(self.readwrite_analyzer) - else: - self.seen = None + self.collect_analyzer = CollectAnalyzer(translator) + self.seen_rw = DependencyTracker(self.readwrite_analyzer) + self.seen_gc = DependencyTracker(self.collect_analyzer) # for index, jd in enumerate(jitdrivers_sd): jd.index = index @@ -294,9 +295,9 @@ "but the function has no result" % (op, )) # effectinfo = effectinfo_from_writeanalyze( - self.readwrite_analyzer.analyze(op, self.seen), self.cpu, + self.readwrite_analyzer.analyze(op, self.seen_rw), self.cpu, extraeffect, oopspecindex, can_invalidate, call_release_gil_target, - extradescr, + extradescr, self.collect_analyzer.analyze(op, self.seen_gc), ) # assert effectinfo is not None diff --git a/rpython/jit/codewriter/effectinfo.py b/rpython/jit/codewriter/effectinfo.py --- a/rpython/jit/codewriter/effectinfo.py +++ b/rpython/jit/codewriter/effectinfo.py @@ -28,6 +28,11 @@ OS_THREADLOCALREF_GET = 5 # llop.threadlocalref_get OS_NOT_IN_TRACE = 8 # for calls not recorded in the jit trace # + OS_INT_PY_DIV = 12 # python signed division (neg. corrected) + OS_INT_UDIV = 13 # regular unsigned division + OS_INT_PY_MOD = 14 # python signed modulo (neg. corrected) + OS_INT_UMOD = 15 # regular unsigned modulo + # OS_STR_CONCAT = 22 # "stroruni.concat" OS_STR_SLICE = 23 # "stroruni.slice" OS_STR_EQUAL = 24 # "stroruni.equal" @@ -111,7 +116,8 @@ oopspecindex=OS_NONE, can_invalidate=False, call_release_gil_target=_NO_CALL_RELEASE_GIL_TARGET, - extradescrs=None): + extradescrs=None, + can_collect=True): readonly_descrs_fields = frozenset_or_none(readonly_descrs_fields) readonly_descrs_arrays = frozenset_or_none(readonly_descrs_arrays) readonly_descrs_interiorfields = frozenset_or_none( @@ -128,7 +134,8 @@ write_descrs_interiorfields, extraeffect, oopspecindex, - can_invalidate) + can_invalidate, + can_collect) tgt_func, tgt_saveerr = call_release_gil_target if tgt_func: key += (object(),) # don't care about caching in this case @@ -179,6 +186,7 @@ # result.extraeffect = extraeffect result.can_invalidate = can_invalidate + result.can_collect = can_collect result.oopspecindex = oopspecindex result.extradescrs = extradescrs _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit