Author: Manuel Jacob <m...@manueljacob.de> Branch: py3k Changeset: r76125:1ea84a73908b Date: 2015-02-25 12:16 +0100 http://bitbucket.org/pypy/pypy/changeset/1ea84a73908b/
Log: hg merge default diff too long, truncating to 2000 out of 3584 lines diff --git a/lib_pypy/_gdbm.py b/lib_pypy/_gdbm.py --- a/lib_pypy/_gdbm.py +++ b/lib_pypy/_gdbm.py @@ -20,9 +20,11 @@ } datum; datum gdbm_fetch(void*, datum); +datum pygdbm_fetch(void*, char*, int); int gdbm_delete(void*, datum); int gdbm_store(void*, datum, datum, int); int gdbm_exists(void*, datum); +int pygdbm_exists(void*, char*, int); int gdbm_reorganize(void*); @@ -37,19 +39,29 @@ ''') try: + verify_code = ''' + #include "gdbm.h" + + static datum pygdbm_fetch(GDBM_FILE gdbm_file, char *dptr, int dsize) { + datum key = {dptr, dsize}; + return gdbm_fetch(gdbm_file, key); + } + + static int pygdbm_exists(GDBM_FILE gdbm_file, char *dptr, int dsize) { + datum key = {dptr, dsize}; + return gdbm_exists(gdbm_file, key); + } + + ''' if sys.platform.startswith('freebsd'): import os.path _localbase = os.environ.get('LOCALBASE', '/usr/local') - lib = ffi.verify(''' - #include "gdbm.h" - ''', libraries=['gdbm'], + lib = ffi.verify(verify_code, libraries=['gdbm'], include_dirs=[os.path.join(_localbase, 'include')], library_dirs=[os.path.join(_localbase, 'lib')] ) else: - lib = ffi.verify(''' - #include "gdbm.h" - ''', libraries=['gdbm']) + lib = ffi.verify(verify_code, libraries=['gdbm']) except cffi.VerificationError as e: # distutils does not preserve the actual message, # but the verification is simple enough that the @@ -59,6 +71,13 @@ class error(IOError): pass +def _checkstr(key): + if isinstance(key, unicode): + key = key.encode("ascii") + if not isinstance(key, str): + raise TypeError("gdbm mappings have string indices only") + return key + def _fromstr(key): if isinstance(key, str): key = key.encode(sys.getdefaultencoding()) @@ -108,12 +127,14 @@ def __contains__(self, key): self._check_closed() - return lib.gdbm_exists(self.ll_dbm, _fromstr(key)) + key = _checkstr(key) + return lib.pygdbm_exists(self.ll_dbm, key, len(key)) has_key = __contains__ def get(self, key, default=None): self._check_closed() - drec = lib.gdbm_fetch(self.ll_dbm, _fromstr(key)) + key = _checkstr(key) + drec = lib.pygdbm_fetch(self.ll_dbm, key, len(key)) if not drec.dptr: return default res = bytes(ffi.buffer(drec.dptr, drec.dsize)) diff --git a/pypy/doc/embedding.rst b/pypy/doc/embedding.rst --- a/pypy/doc/embedding.rst +++ b/pypy/doc/embedding.rst @@ -36,7 +36,8 @@ "PyPy home directory". The arguments are: * ``home``: NULL terminated path to an executable inside the pypy directory - (can be a .so name, can be made up) + (can be a .so name, can be made up). Used to look up the standard + library, and is also set as ``sys.executable``. * ``verbose``: if non-zero, it will print error messages to stderr 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 @@ -6,8 +6,8 @@ .. startrev: 397b96217b85 -Fix non-blocking file reads sometimes raising EAGAIN even though they -have buffered data waiting (b1c4fcb04a42) +Non-blocking file reads sometimes raised EAGAIN even though they +had buffered data waiting, fixed in b1c4fcb04a42 .. branch: vmprof @@ -18,3 +18,19 @@ .. branch: stdlib-2.7.9 Update stdlib to version 2.7.9 + +.. branch: fix-kqueue-error2 +Fix exception being raised by kqueue.control (CPython compatibility) + +.. branch: gitignore + +.. branch: framestate2 +Refactor rpython.flowspace.framestate.FrameState. + +.. branch: alt_errno +Add an alternative location to save LastError, errno around ctypes, +cffi external calls so things like pdb will not overwrite it + +.. branch: nonquadratic-heapcache +Speed up the warmup times of the JIT by removing a quadratic algorithm in the +heapcache. diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -113,6 +113,9 @@ space.call_function(w_pathsetter, w_path) # import site try: + space.setattr(space.getbuiltinmodule('sys'), + space.wrap('executable'), + space.wrap(home)) import_ = space.getattr(space.getbuiltinmodule('builtins'), space.wrap('__import__')) space.call_function(import_, space.wrap('site')) diff --git a/pypy/interpreter/app_main.py b/pypy/interpreter/app_main.py --- a/pypy/interpreter/app_main.py +++ b/pypy/interpreter/app_main.py @@ -1,5 +1,5 @@ #! /usr/bin/env python -# App-level version of py.py. +# This is pure Python code that handles the main entry point into "pypy". # See test/test_app_main. # Missing vs CPython: -b, -d, -v, -x, -3 @@ -158,11 +158,14 @@ current = group raise SystemExit +def get_sys_executable(): + return getattr(sys, 'executable', 'pypy') + def print_help(*args): import os initstdio() print('usage: %s [option] ... [-c cmd | -m mod | file | -] [arg] ...' % ( - sys.executable,)) + get_sys_executable(),)) print(USAGE1, end='') if 'pypyjit' in sys.builtin_module_names: print("--jit options: advanced JIT options: try 'off' or 'help'") @@ -174,7 +177,7 @@ try: import pypyjit except ImportError: - print("No jit support in %s" % (sys.executable,), file=sys.stderr) + print("No jit support in %s" % (get_sys_executable(),), file=sys.stderr) return items = sorted(pypyjit.defaults.items()) print('Advanced JIT options: a comma-separated list of OPTION=VALUE:') @@ -213,7 +216,7 @@ raise SystemExit if 'pypyjit' not in sys.builtin_module_names: initstdio() - print("Warning: No jit support in %s" % (sys.executable,), + print("Warning: No jit support in %s" % (get_sys_executable(),), file=sys.stderr) else: import pypyjit @@ -224,8 +227,8 @@ def print_error(msg): print(msg, file=sys.stderr) - print('usage: %s [options]' % (sys.executable,), file=sys.stderr) - print('Try `%s -h` for more information.' % (sys.executable,), file=sys.stderr) + print('usage: %s [options]' % (get_sys_executable(),), file=sys.stderr) + print('Try `%s -h` for more information.' % (get_sys_executable(),), file=sys.stderr) def fdopen(fd, mode, bufsize=-1): try: diff --git a/pypy/interpreter/test/test_objspace.py b/pypy/interpreter/test/test_objspace.py --- a/pypy/interpreter/test/test_objspace.py +++ b/pypy/interpreter/test/test_objspace.py @@ -373,7 +373,7 @@ config = make_config(None) space = make_objspace(config) w_executable = space.wrap('executable') - assert space.str_w(space.getattr(space.sys, w_executable)) == 'py.py' + assert space.findattr(space.sys, w_executable) is None space.setattr(space.sys, w_executable, space.wrap('foobar')) assert space.str_w(space.getattr(space.sys, w_executable)) == 'foobar' space.startup() diff --git a/pypy/interpreter/test/test_targetpypy.py b/pypy/interpreter/test/test_targetpypy.py --- a/pypy/interpreter/test/test_targetpypy.py +++ b/pypy/interpreter/test/test_targetpypy.py @@ -8,7 +8,7 @@ entry_point = get_entry_point(config)[0] entry_point(['pypy-c' , '-S', '-c', 'print 3']) -def test_exeucte_source(space): +def test_execute_source(space): _, d = create_entry_point(space, None) execute_source = d['pypy_execute_source'] lls = rffi.str2charp("import sys; sys.modules['xyz'] = 3") diff --git a/pypy/module/_cffi_backend/ccallback.py b/pypy/module/_cffi_backend/ccallback.py --- a/pypy/module/_cffi_backend/ccallback.py +++ b/pypy/module/_cffi_backend/ccallback.py @@ -210,6 +210,6 @@ space.threadlocals.leave_thread(space) def invoke_callback(ffi_cif, ll_res, ll_args, ll_userdata): - cerrno._errno_after(rffi.RFFI_ERR_ALL) + cerrno._errno_after(rffi.RFFI_ERR_ALL | rffi.RFFI_ALT_ERRNO) _invoke_callback(ffi_cif, ll_res, ll_args, ll_userdata) - cerrno._errno_before(rffi.RFFI_ERR_ALL) + cerrno._errno_before(rffi.RFFI_ERR_ALL | rffi.RFFI_ALT_ERRNO) diff --git a/pypy/module/_cffi_backend/cerrno.py b/pypy/module/_cffi_backend/cerrno.py --- a/pypy/module/_cffi_backend/cerrno.py +++ b/pypy/module/_cffi_backend/cerrno.py @@ -13,18 +13,18 @@ _errno_after = rposix._errno_after def get_errno(space): - return space.wrap(rposix.get_saved_errno()) + return space.wrap(rposix.get_saved_alterrno()) @unwrap_spec(errno=int) def set_errno(space, errno): - rposix.set_saved_errno(errno) + rposix.set_saved_alterrno(errno) # ____________________________________________________________ @unwrap_spec(code=int) def getwinerror(space, code=-1): - from rpython.rlib.rwin32 import GetLastError_saved, FormatError + from rpython.rlib.rwin32 import GetLastError_alt_saved, FormatError if code == -1: - code = GetLastError_saved() + code = GetLastError_alt_saved() message = FormatError(code) return space.newtuple([space.wrap(code), space.wrap(message)]) diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -2716,6 +2716,14 @@ assert data == b"Xhello\n" posix.close(fdr) +def test_errno_saved(): + set_errno(42) + # a random function that will reset errno to 0 (at least on non-windows) + import os; os.stat('.') + # + res = get_errno() + assert res == 42 + def test_GetLastError(): if sys.platform != "win32": py.test.skip("GetLastError(): only for Windows") diff --git a/pypy/module/_rawffi/array.py b/pypy/module/_rawffi/array.py --- a/pypy/module/_rawffi/array.py +++ b/pypy/module/_rawffi/array.py @@ -15,7 +15,7 @@ from pypy.module._rawffi.interp_rawffi import unpack_shape_with_length from pypy.module._rawffi.interp_rawffi import read_ptr, write_ptr from rpython.rlib.rarithmetic import r_uint -from rpython.rlib import rgc +from rpython.rlib import rgc, clibffi class W_Array(W_DataShape): @@ -84,14 +84,11 @@ class W_ArrayInstance(W_DataInstance): def __init__(self, space, shape, length, address=r_uint(0)): - # Workaround for a strange behavior of libffi: make sure that - # we always have at least 8 bytes. For W_ArrayInstances that are - # used as the result value of a function call, ffi_call() writes - # 8 bytes into it even if the function's result type asks for less. - # This strange behavior is documented. memsize = shape.size * length - if memsize < 8: - memsize = 8 + # For W_ArrayInstances that are used as the result value of a + # function call, ffi_call() writes 8 bytes into it even if the + # function's result type asks for less. + memsize = clibffi.adjust_return_size(memsize) W_DataInstance.__init__(self, space, memsize, address) self.length = length self.shape = shape diff --git a/pypy/module/_rawffi/interp_rawffi.py b/pypy/module/_rawffi/interp_rawffi.py --- a/pypy/module/_rawffi/interp_rawffi.py +++ b/pypy/module/_rawffi/interp_rawffi.py @@ -498,6 +498,7 @@ try: if self.resshape is not None: result = self.resshape.allocate(space, 1, autofree=True) + # adjust_return_size() was used here on result.ll_buffer self.ptr.call(args_ll, result.ll_buffer) return space.wrap(result) else: @@ -611,19 +612,19 @@ return space.wrap(W_CDLL(space, name, cdll)) def get_errno(space): - return space.wrap(rposix.get_saved_errno()) + return space.wrap(rposix.get_saved_alterrno()) def set_errno(space, w_errno): - rposix.set_saved_errno(space.int_w(w_errno)) + rposix.set_saved_alterrno(space.int_w(w_errno)) if sys.platform == 'win32': # see also # https://bitbucket.org/pypy/pypy/issue/1944/ctypes-on-windows-getlasterror def get_last_error(space): - return space.wrap(rwin32.GetLastError_saved()) + return space.wrap(rwin32.GetLastError_alt_saved()) @unwrap_spec(error=int) def set_last_error(space, error): - rwin32.SetLastError_saved(error) + rwin32.SetLastError_alt_saved(error) else: # always have at least a dummy version of these functions # (https://bugs.pypy.org/issue1242) diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py --- a/pypy/module/_ssl/interp_ssl.py +++ b/pypy/module/_ssl/interp_ssl.py @@ -4,6 +4,8 @@ from rpython.rlib.rarithmetic import intmask, widen, r_uint from rpython.rlib import rpoll, rsocket, rthread, rweakref from rpython.rlib.ropenssl import * +from pypy.module._socket import interp_socket +from rpython.rlib._rsocket_rffi import MAX_FD_SIZE from rpython.rlib.rposix import get_saved_errno from rpython.rlib.rweakref import RWeakValueDictionary from rpython.rlib.objectmodel import specialize, compute_unique_id @@ -16,7 +18,6 @@ from pypy.interpreter.unicodehelper import fsdecode from pypy.module._ssl.ssl_data import ( LIBRARY_CODES_TO_NAMES, ERROR_CODES_TO_NAMES) -from pypy.module._socket import interp_socket # user defined constants diff --git a/pypy/module/_ssl/test/test_ssl.py b/pypy/module/_ssl/test/test_ssl.py --- a/pypy/module/_ssl/test/test_ssl.py +++ b/pypy/module/_ssl/test/test_ssl.py @@ -236,6 +236,9 @@ def test_npn_protocol(self): import socket, _ssl, gc + if not _ssl.HAS_NPN: + skip("NPN requires OpenSSL 1.0.1 or greater") + ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) ctx._set_npn_protocols(b'\x08http/1.1\x06spdy/2') ss = ctx._wrap_socket(self.s, True, @@ -304,12 +307,13 @@ os.path.dirname(__file__), 'dh512.pem')) def test_load_cert_chain(self): - import _ssl + import _ssl, errno ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) ctx.load_cert_chain(self.keycert) ctx.load_cert_chain(self.cert, self.key) - raises(IOError, ctx.load_cert_chain, "inexistent.pem") - raises(_ssl.SSLError, ctx.load_cert_chain, self.badcert) + exc = raises(IOError, ctx.load_cert_chain, "inexistent.pem") + assert exc.value.errno == errno.ENOENT + exc = raises(_ssl.SSLError, ctx.load_cert_chain, self.badcert) raises(_ssl.SSLError, ctx.load_cert_chain, self.emptycert) # Password protected key and cert raises(_ssl.SSLError, ctx.load_cert_chain, self.cert_protected, @@ -367,12 +371,14 @@ assert ctx.cert_store_stats() == {'x509_ca': 0, 'crl': 0, 'x509': 1} def test_load_dh_params(self): - import _ssl + import _ssl, errno ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1) ctx.load_dh_params(self.dh512) raises(TypeError, ctx.load_dh_params) raises(TypeError, ctx.load_dh_params, None) raises(_ssl.SSLError, ctx.load_dh_params, self.keycert) + exc = raises(IOError, ctx.load_dh_params, "inexistent.pem") + assert exc.value.errno == errno.ENOENT def test_set_ecdh_curve(self): import _ssl diff --git a/pypy/module/cpyext/include/patchlevel.h b/pypy/module/cpyext/include/patchlevel.h --- a/pypy/module/cpyext/include/patchlevel.h +++ b/pypy/module/cpyext/include/patchlevel.h @@ -29,7 +29,7 @@ #define PY_VERSION "3.2.5" /* PyPy version as a string */ -#define PYPY_VERSION "2.6.0" +#define PYPY_VERSION "2.6.0-alpha0" /* Subversion Revision number of this file (not of the repository). * Empty since Mercurial migration. */ diff --git a/pypy/module/pypyjit/test_pypy_c/test_ffi.py b/pypy/module/pypyjit/test_pypy_c/test_ffi.py --- a/pypy/module/pypyjit/test_pypy_c/test_ffi.py +++ b/pypy/module/pypyjit/test_pypy_c/test_ffi.py @@ -202,7 +202,7 @@ assert loop.match_by_id('cfficall', """ p96 = force_token() setfield_gc(p0, p96, descr=<FieldP pypy.interpreter.pyframe.PyFrame.vable_token .>) - f97 = call_release_gil(27, i59, 1.0, 3, descr=<Callf 8 fi EF=6 OS=62>) + f97 = call_release_gil(91, i59, 1.0, 3, descr=<Callf 8 fi EF=6 OS=62>) guard_not_forced(descr=...) guard_no_exception(descr=...) """, ignore_ops=['guard_not_invalidated']) diff --git a/pypy/module/pypyjit/test_pypy_c/test_thread.py b/pypy/module/pypyjit/test_pypy_c/test_thread.py --- a/pypy/module/pypyjit/test_pypy_c/test_thread.py +++ b/pypy/module/pypyjit/test_pypy_c/test_thread.py @@ -67,21 +67,10 @@ i58 = call_release_gil(0, _, i37, 1, descr=<Calli 4 ii EF=6>) guard_not_forced(descr=...) guard_no_exception(descr=...) - i59 = int_is_true(i58) - guard_true(i59, descr=...) - i60 = int_sub(i44, 1) - p62 = force_token() - setfield_gc(p0, p62, descr=<FieldP pypy.interpreter.pyframe.PyFrame.vable_token 8>) - i63 = call_release_gil(0, _, i37, 0, descr=<Calli 4 ii EF=6>) - guard_not_forced(descr=...) - guard_no_exception(descr=...) - i64 = int_is_true(i63) - guard_false(i64, descr=...) - p65 = force_token() - setfield_gc(p0, p65, descr=<FieldP pypy.interpreter.pyframe.PyFrame.vable_token 8>) - call_release_gil(0, _, i37, descr=<Callv 0 i EF=6>) - guard_not_forced(descr=...) - guard_no_exception(descr=...) + i58 = int_sub(i44, 1) + i59 = call(ConstClass(RPyThreadReleaseLock), i37, descr=<Calli . i EF=2>) + i60 = int_is_true(i59) + guard_false(i60, descr=...) guard_not_invalidated(descr=...) --TICK-- jump(..., descr=...) diff --git a/pypy/module/sys/__init__.py b/pypy/module/sys/__init__.py --- a/pypy/module/sys/__init__.py +++ b/pypy/module/sys/__init__.py @@ -57,7 +57,6 @@ 'getsizeof' : 'vm.getsizeof', 'intern' : 'vm.intern', - 'executable' : 'space.wrap("py.py")', 'api_version' : 'version.get_api_version(space)', 'version_info' : 'version.get_version_info(space)', 'version' : 'version.get_version(space)', diff --git a/pypy/module/sys/test/test_sysmodule.py b/pypy/module/sys/test/test_sysmodule.py --- a/pypy/module/sys/test/test_sysmodule.py +++ b/pypy/module/sys/test/test_sysmodule.py @@ -497,7 +497,7 @@ assert isinstance(sys.builtin_module_names, tuple) assert isinstance(sys.copyright, str) #assert isinstance(sys.exec_prefix, str) -- not present! - assert isinstance(sys.executable, str) + #assert isinstance(sys.executable, str) assert isinstance(sys.hexversion, int) assert isinstance(sys.maxsize, int) assert isinstance(sys.maxunicode, int) @@ -515,6 +515,12 @@ assert vi[3] in ("alpha", "beta", "candidate", "final") assert isinstance(vi[4], int) + def test_reload_doesnt_override_sys_executable(self): + import sys + sys.executable = 'from_test_sysmodule' + reload(sys) + assert sys.executable == 'from_test_sysmodule' + def test_settrace(self): import sys counts = [] diff --git a/pypy/module/test_lib_pypy/ctypes_tests/_ctypes_test.c b/pypy/module/test_lib_pypy/ctypes_tests/_ctypes_test.c --- a/pypy/module/test_lib_pypy/ctypes_tests/_ctypes_test.c +++ b/pypy/module/test_lib_pypy/ctypes_tests/_ctypes_test.c @@ -272,7 +272,11 @@ { double x, sum=0.0, dx=(b-a)/(double)nstep; for(x=a+0.5*dx; (b-x)*(x-a)>0.0; x+=dx) + { + double y = f(x); + printf("f(x)=%.1f\n", y); sum += f(x); + } return sum/(double)nstep; } diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_callbacks.py b/pypy/module/test_lib_pypy/ctypes_tests/test_callbacks.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_callbacks.py +++ b/pypy/module/test_lib_pypy/ctypes_tests/test_callbacks.py @@ -138,6 +138,7 @@ integrate.restype = c_double def func(x): + print 'calculating x**2 of',x return x**2 result = integrate(0.0, 1.0, CALLBACK(func), 10) diff --git a/pypy/module/time/interp_time.py b/pypy/module/time/interp_time.py --- a/pypy/module/time/interp_time.py +++ b/pypy/module/time/interp_time.py @@ -168,7 +168,6 @@ if cConfig.has_gettimeofday: c_gettimeofday = external('gettimeofday', [rffi.VOIDP, rffi.VOIDP], rffi.INT) TM_P = lltype.Ptr(tm) -c_clock = external('clock', [rffi.TIME_TP], clock_t) c_time = external('time', [rffi.TIME_TP], rffi.TIME_T) c_gmtime = external('gmtime', [rffi.TIME_TP], TM_P, save_err=rffi.RFFI_SAVE_ERRNO) diff --git a/pypy/module/zipimport/interp_zipimport.py b/pypy/module/zipimport/interp_zipimport.py --- a/pypy/module/zipimport/interp_zipimport.py +++ b/pypy/module/zipimport/interp_zipimport.py @@ -199,8 +199,7 @@ magic = importing._get_long(buf[:4]) timestamp = importing._get_long(buf[4:8]) if not self.can_use_pyc(space, filename, magic, timestamp): - return self.import_py_file(space, modname, filename[:-1], buf, - pkgpath) + return None buf = buf[8:] # XXX ugly copy, should use sequential read instead w_mod = w(Module(space, w(modname))) real_name = self.filename + os.path.sep + self.corr_zname(filename) @@ -249,7 +248,6 @@ def load_module(self, space, fullname): w = space.wrap filename = self.make_filename(fullname) - last_exc = None for compiled, is_package, ext in ENUMERATE_EXTS: fname = filename + ext try: @@ -268,19 +266,18 @@ pkgpath = None try: if compiled: - return self.import_pyc_file(space, fullname, fname, - buf, pkgpath) + w_result = self.import_pyc_file(space, fullname, fname, + buf, pkgpath) + if w_result is not None: + return w_result else: return self.import_py_file(space, fullname, fname, buf, pkgpath) - except OperationError, e: - last_exc = e + except: w_mods = space.sys.get('modules') - space.call_method(w_mods, 'pop', w(fullname), space.w_None) - if last_exc: - raise OperationError(get_error(space), last_exc.get_w_value(space)) - # should never happen I think - return space.w_None + space.call_method(w_mods, 'pop', w(fullname), space.w_None) + raise + raise oefmt(get_error(space), "can't find module '%s'", fullname) @unwrap_spec(filename='str0') def get_data(self, space, filename): diff --git a/pypy/module/zipimport/test/test_zipimport.py b/pypy/module/zipimport/test/test_zipimport.py --- a/pypy/module/zipimport/test/test_zipimport.py +++ b/pypy/module/zipimport/test/test_zipimport.py @@ -195,7 +195,8 @@ m0 ^= 0x04 test_pyc = bytes([m0]) + self.get_pyc()[1:] self.writefile("uu.pyc", test_pyc) - raises(ImportError, "__import__('uu', globals(), locals(), [])") + raises(zipimport.ZipImportError, + "__import__('uu', globals(), locals(), [])") assert 'uu' not in sys.modules def test_force_py(self): @@ -381,6 +382,11 @@ finally: os.remove(filename) + def test_import_exception(self): + self.writefile('x1test.py', '1/0') + self.writefile('x1test/__init__.py', 'raise ValueError') + raises(ValueError, __import__, 'x1test', None, None, []) + if os.sep != '/': class AppTestNativePathSep(AppTestZipimport): diff --git a/pypy/objspace/std/intobject.py b/pypy/objspace/std/intobject.py --- a/pypy/objspace/std/intobject.py +++ b/pypy/objspace/std/intobject.py @@ -567,9 +567,11 @@ def descr_bit_length(self, space): val = self.intval + bits = 0 if val < 0: - val = -val - bits = 0 + # warning, "-val" overflows here + val = -((val + 1) >> 1) + bits = 1 while val: bits += 1 val >>= 1 diff --git a/pypy/objspace/std/test/test_intobject.py b/pypy/objspace/std/test/test_intobject.py --- a/pypy/objspace/std/test/test_intobject.py +++ b/pypy/objspace/std/test/test_intobject.py @@ -476,11 +476,20 @@ (10, 4), (150, 8), (-1, 1), + (-2, 2), + (-3, 2), + (-4, 3), (-10, 4), (-150, 8), ]: assert val.bit_length() == bits + def test_bit_length_max(self): + import sys + val = -sys.maxint-1 + bits = 32 if val == -2147483648 else 64 + assert val.bit_length() == bits + def test_int_real(self): class A(int): pass b = A(5).real diff --git a/rpython/flowspace/flowcontext.py b/rpython/flowspace/flowcontext.py --- a/rpython/flowspace/flowcontext.py +++ b/rpython/flowspace/flowcontext.py @@ -12,8 +12,7 @@ from rpython.flowspace.argument import CallSpec from rpython.flowspace.model import (Constant, Variable, Block, Link, c_last_exception, const, FSException) -from rpython.flowspace.framestate import (FrameState, recursively_unflatten, - recursively_flatten) +from rpython.flowspace.framestate import FrameState from rpython.flowspace.specialcase import (rpython_print_item, rpython_print_newline) from rpython.flowspace.operation import op @@ -278,6 +277,7 @@ "cmp_exc_match", ] + class FlowContext(object): def __init__(self, graph, code): self.graph = graph @@ -307,112 +307,91 @@ The locals are ordered according to self.pycode.signature. """ - self.valuestackdepth = code.co_nlocals - self.locals_stack_w = [None] * (code.co_stacksize + code.co_nlocals) + self.nlocals = code.co_nlocals + self.locals_w = [None] * code.co_nlocals + self.stack = [] + + @property + def stackdepth(self): + return len(self.stack) def pushvalue(self, w_object): - depth = self.valuestackdepth - self.locals_stack_w[depth] = w_object - self.valuestackdepth = depth + 1 + self.stack.append(w_object) def popvalue(self): - depth = self.valuestackdepth - 1 - assert depth >= self.pycode.co_nlocals, "pop from empty value stack" - w_object = self.locals_stack_w[depth] - self.locals_stack_w[depth] = None - self.valuestackdepth = depth - return w_object + return self.stack.pop() def peekvalue(self, index_from_top=0): # NOTE: top of the stack is peekvalue(0). - index = self.valuestackdepth + ~index_from_top - assert index >= self.pycode.co_nlocals, ( - "peek past the bottom of the stack") - return self.locals_stack_w[index] + index = ~index_from_top + return self.stack[index] def settopvalue(self, w_object, index_from_top=0): - index = self.valuestackdepth + ~index_from_top - assert index >= self.pycode.co_nlocals, ( - "settop past the bottom of the stack") - self.locals_stack_w[index] = w_object + index = ~index_from_top + self.stack[index] = w_object def popvalues(self, n): - values_w = [self.popvalue() for i in range(n)] - values_w.reverse() + if n == 0: + return [] + values_w = self.stack[-n:] + del self.stack[-n:] return values_w - def dropvalues(self, n): - finaldepth = self.valuestackdepth - n - for n in range(finaldepth, self.valuestackdepth): - self.locals_stack_w[n] = None - self.valuestackdepth = finaldepth - def dropvaluesuntil(self, finaldepth): - for n in range(finaldepth, self.valuestackdepth): - self.locals_stack_w[n] = None - self.valuestackdepth = finaldepth - - def save_locals_stack(self): - return self.locals_stack_w[:self.valuestackdepth] - - def restore_locals_stack(self, items_w): - self.locals_stack_w[:len(items_w)] = items_w - self.dropvaluesuntil(len(items_w)) + del self.stack[finaldepth:] def getstate(self, next_offset): - # getfastscope() can return real None, for undefined locals - data = self.save_locals_stack() - if self.last_exception is None: - data.append(Constant(None)) - data.append(Constant(None)) - else: - data.append(self.last_exception.w_type) - data.append(self.last_exception.w_value) - recursively_flatten(data) - return FrameState(data, self.blockstack[:], next_offset) + return FrameState(self.locals_w[:], self.stack[:], + self.last_exception, self.blockstack[:], next_offset) def setstate(self, state): """ Reset the context to the given frame state. """ - data = state.mergeable[:] - recursively_unflatten(data) - self.restore_locals_stack(data[:-2]) # Nones == undefined locals - if data[-2] == Constant(None): - assert data[-1] == Constant(None) - self.last_exception = None - else: - self.last_exception = FSException(data[-2], data[-1]) + self.locals_w = state.locals_w[:] + self.stack = state.stack[:] + self.last_exception = state.last_exception self.blockstack = state.blocklist[:] + self._normalize_raise_signals() + + def _normalize_raise_signals(self): + st = self.stack + for i in range(len(st)): + if isinstance(st[i], RaiseImplicit): + st[i] = Raise(st[i].w_exc) def guessbool(self, w_condition): if isinstance(w_condition, Constant): return w_condition.value return self.recorder.guessbool(self, w_condition) - def record(self, spaceop): + def maybe_merge(self): recorder = self.recorder if getattr(recorder, 'final_state', None) is not None: self.mergeblock(recorder.crnt_block, recorder.final_state) raise StopFlowing + + def record(self, spaceop): spaceop.offset = self.last_offset - recorder.append(spaceop) + self.recorder.append(spaceop) def do_op(self, op): + self.maybe_merge() self.record(op) self.guessexception(op.canraise) return op.result - def guessexception(self, exceptions, force=False): + def guessexception(self, exceptions): """ Catch possible exceptions implicitly. """ if not exceptions: return - if not force and not any(isinstance(block, (ExceptBlock, FinallyBlock)) - for block in self.blockstack): - # The implicit exception wouldn't be caught and would later get - # removed, so don't bother creating it. - return - self.recorder.guessexception(self, *exceptions) + # Implicit exceptions are ignored unless they are caught explicitly + if self.has_exc_handler(): + self.recorder.guessexception(self, *exceptions) + + def has_exc_handler(self): + return any(isinstance(block, (ExceptBlock, FinallyBlock)) + for block in self.blockstack) def build_flow(self): graph = self.graph @@ -430,35 +409,8 @@ while True: next_offset = self.handle_bytecode(next_offset) self.recorder.final_state = self.getstate(next_offset) - - except RaiseImplicit as e: - w_exc = e.w_exc - if isinstance(w_exc.w_type, Constant): - exc_cls = w_exc.w_type.value - else: - exc_cls = Exception - msg = "implicit %s shouldn't occur" % exc_cls.__name__ - w_type = Constant(AssertionError) - w_value = Constant(AssertionError(msg)) - link = Link([w_type, w_value], self.graph.exceptblock) - self.recorder.crnt_block.closeblock(link) - - except Raise as e: - w_exc = e.w_exc - if w_exc.w_type == const(ImportError): - msg = 'import statement always raises %s' % e - raise ImportError(msg) - link = Link([w_exc.w_type, w_exc.w_value], self.graph.exceptblock) - self.recorder.crnt_block.closeblock(link) - except StopFlowing: pass - - except Return as exc: - w_result = exc.w_value - link = Link([w_result], self.graph.returnblock) - self.recorder.crnt_block.closeblock(link) - except FlowingError as exc: if exc.ctx is None: exc.ctx = self @@ -476,14 +428,8 @@ if newstate is not None: break else: - newstate = currentstate.copy() - newblock = SpamBlock(newstate) - # unconditionally link the current block to the newblock - outputargs = currentstate.getoutputargs(newstate) - link = Link(outputargs, newblock) - currentblock.closeblock(link) + newblock = self.make_next_block(currentblock, currentstate) candidates.insert(0, newblock) - self.pendingblocks.append(newblock) return if newstate.matches(block.framestate): @@ -493,7 +439,7 @@ newblock = SpamBlock(newstate) varnames = self.pycode.co_varnames - for name, w_value in zip(varnames, newstate.mergeable): + for name, w_value in zip(varnames, newstate.locals_w): if isinstance(w_value, Variable): w_value.rename(name) # unconditionally link the current block to the newblock @@ -513,11 +459,21 @@ candidates.insert(0, newblock) self.pendingblocks.append(newblock) + def make_next_block(self, block, state): + newstate = state.copy() + newblock = SpamBlock(newstate) + # unconditionally link the current block to the newblock + outputargs = state.getoutputargs(newstate) + link = Link(outputargs, newblock) + block.closeblock(link) + self.pendingblocks.append(newblock) + return newblock + # hack for unrolling iterables, don't use this def replace_in_stack(self, oldvalue, newvalue): w_new = Constant(newvalue) - stack_items_w = self.locals_stack_w - for i in range(self.valuestackdepth - 1, self.pycode.co_nlocals - 1, -1): + stack_items_w = self.stack + for i in range(self.stackdepth - 1, - 1, -1): w_v = stack_items_w[i] if isinstance(w_v, Constant): if w_v.value is oldvalue: @@ -541,7 +497,7 @@ if isinstance(signal, block.handles): return block.handle(self, signal) block.cleanupstack(self) - return signal.nomoreblocks() + return signal.nomoreblocks(self) def getlocalvarname(self, index): return self.pycode.co_varnames[index] @@ -870,7 +826,7 @@ op.simple_call(w_exitfunc, w_None, w_None, w_None).eval(self) def LOAD_FAST(self, varindex): - w_value = self.locals_stack_w[varindex] + w_value = self.locals_w[varindex] if w_value is None: raise FlowingError("Local variable referenced before assignment") self.pushvalue(w_value) @@ -915,7 +871,7 @@ def STORE_FAST(self, varindex): w_newvalue = self.popvalue() assert w_newvalue is not None - self.locals_stack_w[varindex] = w_newvalue + self.locals_w[varindex] = w_newvalue if isinstance(w_newvalue, Variable): w_newvalue.rename(self.getlocalvarname(varindex)) @@ -1128,11 +1084,11 @@ op.simple_call(w_append_meth, w_value).eval(self) def DELETE_FAST(self, varindex): - if self.locals_stack_w[varindex] is None: + if self.locals_w[varindex] is None: varname = self.getlocalvarname(varindex) message = "local variable '%s' referenced before assignment" raise UnboundLocalError(message, varname) - self.locals_stack_w[varindex] = None + self.locals_w[varindex] = None def STORE_MAP(self, oparg): w_key = self.popvalue() @@ -1220,25 +1176,32 @@ WHY_CONTINUE, Continue WHY_YIELD not needed """ - def nomoreblocks(self): + def nomoreblocks(self, ctx): raise BytecodeCorruption("misplaced bytecode - should not return") + def __eq__(self, other): + return type(other) is type(self) and other.args == self.args + class Return(FlowSignal): """Signals a 'return' statement. - Argument is the wrapped object to return.""" - + Argument is the wrapped object to return. + """ def __init__(self, w_value): self.w_value = w_value - def nomoreblocks(self): - raise Return(self.w_value) + def nomoreblocks(self, ctx): + w_result = self.w_value + link = Link([w_result], ctx.graph.returnblock) + ctx.recorder.crnt_block.closeblock(link) + raise StopFlowing - def state_unpack_variables(self): + @property + def args(self): return [self.w_value] @staticmethod - def state_pack_variables(w_value): + def rebuild(w_value): return Return(w_value) class Raise(FlowSignal): @@ -1248,28 +1211,48 @@ def __init__(self, w_exc): self.w_exc = w_exc - def nomoreblocks(self): - raise self + def nomoreblocks(self, ctx): + w_exc = self.w_exc + if w_exc.w_type == const(ImportError): + msg = 'import statement always raises %s' % self + raise ImportError(msg) + link = Link([w_exc.w_type, w_exc.w_value], ctx.graph.exceptblock) + ctx.recorder.crnt_block.closeblock(link) + raise StopFlowing - def state_unpack_variables(self): + @property + def args(self): return [self.w_exc.w_type, self.w_exc.w_value] - @staticmethod - def state_pack_variables(w_type, w_value): - return Raise(FSException(w_type, w_value)) + @classmethod + def rebuild(cls, w_type, w_value): + return cls(FSException(w_type, w_value)) class RaiseImplicit(Raise): """Signals an exception raised implicitly""" + def nomoreblocks(self, ctx): + w_exc = self.w_exc + if isinstance(w_exc.w_type, Constant): + exc_cls = w_exc.w_type.value + else: + exc_cls = Exception + msg = "implicit %s shouldn't occur" % exc_cls.__name__ + w_type = Constant(AssertionError) + w_value = Constant(AssertionError(msg)) + link = Link([w_type, w_value], ctx.graph.exceptblock) + ctx.recorder.crnt_block.closeblock(link) + raise StopFlowing class Break(FlowSignal): """Signals a 'break' statement.""" - def state_unpack_variables(self): + @property + def args(self): return [] @staticmethod - def state_pack_variables(): + def rebuild(): return Break.singleton Break.singleton = Break() @@ -1281,11 +1264,12 @@ def __init__(self, jump_to): self.jump_to = jump_to - def state_unpack_variables(self): + @property + def args(self): return [const(self.jump_to)] @staticmethod - def state_pack_variables(w_jump_to): + def rebuild(w_jump_to): return Continue(w_jump_to.value) @@ -1295,21 +1279,21 @@ def __init__(self, ctx, handlerposition): self.handlerposition = handlerposition - self.valuestackdepth = ctx.valuestackdepth + self.stackdepth = ctx.stackdepth def __eq__(self, other): return (self.__class__ is other.__class__ and self.handlerposition == other.handlerposition and - self.valuestackdepth == other.valuestackdepth) + self.stackdepth == other.stackdepth) def __ne__(self, other): return not (self == other) def __hash__(self): - return hash((self.handlerposition, self.valuestackdepth)) + return hash((self.handlerposition, self.stackdepth)) def cleanupstack(self, ctx): - ctx.dropvaluesuntil(self.valuestackdepth) + ctx.dropvaluesuntil(self.stackdepth) def handle(self, ctx, unroller): raise NotImplementedError diff --git a/rpython/flowspace/framestate.py b/rpython/flowspace/framestate.py --- a/rpython/flowspace/framestate.py +++ b/rpython/flowspace/framestate.py @@ -1,21 +1,50 @@ -from rpython.flowspace.model import Variable, Constant +from rpython.flowspace.model import Variable, Constant, FSException from rpython.rlib.unroll import SpecTag +def _copy(v): + from rpython.flowspace.flowcontext import FlowSignal + if isinstance(v, Variable): + return Variable(v) + elif isinstance(v, FlowSignal): + vars = [_copy(var) for var in v.args] + return v.rebuild(*vars) + else: + return v + +def _union(seq1, seq2): + return [union(v1, v2) for v1, v2 in zip(seq1, seq2)] + class FrameState(object): - def __init__(self, mergeable, blocklist, next_offset): - self.mergeable = mergeable + def __init__(self, locals_w, stack, last_exception, blocklist, next_offset): + self.locals_w = locals_w + self.stack = stack + self.last_exception = last_exception self.blocklist = blocklist self.next_offset = next_offset + self._mergeable = None + + @property + def mergeable(self): + if self._mergeable is not None: + return self._mergeable + self._mergeable = data = self.locals_w + self.stack + if self.last_exception is None: + data.append(Constant(None)) + data.append(Constant(None)) + else: + data.append(self.last_exception.w_type) + data.append(self.last_exception.w_value) + recursively_flatten(data) + return data def copy(self): "Make a copy of this state in which all Variables are fresh." - newstate = [] - for w in self.mergeable: - if isinstance(w, Variable): - w = Variable(w) - newstate.append(w) - return FrameState(newstate, self.blocklist, self.next_offset) + exc = self.last_exception + if exc is not None: + exc = FSException(_copy(exc.w_type), _copy(exc.w_value)) + return FrameState(map(_copy, self.locals_w), map(_copy, self.stack), + exc, self.blocklist, self.next_offset) def getvariables(self): return [w for w in self.mergeable if isinstance(w, Variable)] @@ -33,18 +62,31 @@ return False return True + def _exc_args(self): + if self.last_exception is None: + return [Constant(None), Constant(None)] + else: + return [self.last_exception.w_type, + self.last_exception.w_value] + def union(self, other): """Compute a state that is at least as general as both self and other. A state 'a' is more general than a state 'b' if all Variables in 'b' are also Variables in 'a', but 'a' may have more Variables. """ - newstate = [] try: - for w1, w2 in zip(self.mergeable, other.mergeable): - newstate.append(union(w1, w2)) + locals = _union(self.locals_w, other.locals_w) + stack = _union(self.stack, other.stack) + if self.last_exception is None and other.last_exception is None: + exc = None + else: + args1 = self._exc_args() + args2 = other._exc_args() + exc = FSException(union(args1[0], args2[0]), + union(args1[1], args2[1])) except UnionError: return None - return FrameState(newstate, self.blocklist, self.next_offset) + return FrameState(locals, stack, exc, self.blocklist, self.next_offset) def getoutputargs(self, targetstate): "Return the output arguments needed to link self to targetstate." @@ -61,6 +103,7 @@ def union(w1, w2): "Union of two variables or constants." + from rpython.flowspace.flowcontext import FlowSignal if w1 == w2: return w1 if w1 is None or w2 is None: @@ -69,38 +112,21 @@ if isinstance(w1, Variable) or isinstance(w2, Variable): return Variable() # new fresh Variable if isinstance(w1, Constant) and isinstance(w2, Constant): - # FlowSignal represent stack unrollers in the stack. - # They should not be merged because they will be unwrapped. - # This is needed for try:except: and try:finally:, though - # it makes the control flow a bit larger by duplicating the - # handlers. - dont_merge_w1 = w1 in UNPICKLE_TAGS or isinstance(w1.value, SpecTag) - dont_merge_w2 = w2 in UNPICKLE_TAGS or isinstance(w2.value, SpecTag) - if dont_merge_w1 or dont_merge_w2: + if isinstance(w1.value, SpecTag) or isinstance(w2.value, SpecTag): raise UnionError else: return Variable() # generalize different constants + if isinstance(w1, FlowSignal) and isinstance(w2, FlowSignal): + if type(w1) is not type(w2): + raise UnionError + vars = [union(v1, v2) for v1, v2 in zip(w1.args, w2.args)] + return w1.rebuild(*vars) + if isinstance(w1, FlowSignal) or isinstance(w2, FlowSignal): + raise UnionError raise TypeError('union of %r and %r' % (w1.__class__.__name__, w2.__class__.__name__)) -# ____________________________________________________________ -# -# We have to flatten out the state of the frame into a list of -# Variables and Constants. This is done above by collecting the -# locals and the items on the value stack, but the latter may contain -# FlowSignal. We have to handle these specially, because -# some of them hide references to more Variables and Constants. -# The trick is to flatten ("pickle") them into the list so that the -# extra Variables show up directly in the list too. - -class PickleTag: - pass - -PICKLE_TAGS = {} -UNPICKLE_TAGS = {} - - def recursively_flatten(lst): from rpython.flowspace.flowcontext import FlowSignal i = 0 @@ -109,22 +135,4 @@ if not isinstance(unroller, FlowSignal): i += 1 else: - vars = unroller.state_unpack_variables() - key = unroller.__class__, len(vars) - try: - tag = PICKLE_TAGS[key] - except KeyError: - tag = PICKLE_TAGS[key] = Constant(PickleTag()) - UNPICKLE_TAGS[tag] = key - lst[i:i + 1] = [tag] + vars - - -def recursively_unflatten(lst): - for i in xrange(len(lst) - 1, -1, -1): - item = lst[i] - if item in UNPICKLE_TAGS: - unrollerclass, argcount = UNPICKLE_TAGS[item] - arguments = lst[i + 1:i + 1 + argcount] - del lst[i + 1:i + 1 + argcount] - unroller = unrollerclass.state_pack_variables(*arguments) - lst[i] = unroller + lst[i:i + 1] = unroller.args diff --git a/rpython/flowspace/operation.py b/rpython/flowspace/operation.py --- a/rpython/flowspace/operation.py +++ b/rpython/flowspace/operation.py @@ -517,7 +517,7 @@ ctx.replace_in_stack(it, next_unroller) return const(v) w_item = ctx.do_op(self) - ctx.guessexception([StopIteration, RuntimeError], force=True) + ctx.recorder.guessexception(ctx, StopIteration, RuntimeError) return w_item class GetAttr(SingleDispatchMixin, HLOperation): diff --git a/rpython/flowspace/pygraph.py b/rpython/flowspace/pygraph.py --- a/rpython/flowspace/pygraph.py +++ b/rpython/flowspace/pygraph.py @@ -11,10 +11,10 @@ def __init__(self, func, code): from rpython.flowspace.flowcontext import SpamBlock - data = [None] * code.co_nlocals + locals = [None] * code.co_nlocals for i in range(code.formalargcount): - data[i] = Variable(code.co_varnames[i]) - state = FrameState(data + [Constant(None), Constant(None)], [], 0) + locals[i] = Variable(code.co_varnames[i]) + state = FrameState(locals, [], None, [], 0) initialblock = SpamBlock(state) super(PyGraph, self).__init__(self._sanitize_funcname(func), initialblock) self.func = func diff --git a/rpython/flowspace/test/test_flowcontext.py b/rpython/flowspace/test/test_flowcontext.py new file mode 100644 --- /dev/null +++ b/rpython/flowspace/test/test_flowcontext.py @@ -0,0 +1,15 @@ +""" Unit tests for flowcontext.py """ +import pytest +from rpython.flowspace.model import Variable, FSException +from rpython.flowspace.flowcontext import ( + Return, Raise, RaiseImplicit, Continue, Break) + +@pytest.mark.parametrize('signal', [ + Return(Variable()), + Raise(FSException(Variable(), Variable())), + RaiseImplicit(FSException(Variable(), Variable())), + Break(), + Continue(42), +]) +def test_signals(signal): + assert signal.rebuild(*signal.args) == signal diff --git a/rpython/flowspace/test/test_framestate.py b/rpython/flowspace/test/test_framestate.py --- a/rpython/flowspace/test/test_framestate.py +++ b/rpython/flowspace/test/test_framestate.py @@ -15,7 +15,7 @@ ctx = FlowContext(graph, code) # hack the frame ctx.setstate(graph.startblock.framestate) - ctx.locals_stack_w[ctx.pycode.co_nlocals-1] = Constant(None) + ctx.locals_w[-1] = Constant(None) return ctx def func_simple(x): @@ -31,7 +31,7 @@ def test_neq_hacked_framestate(self): ctx = self.get_context(self.func_simple) fs1 = ctx.getstate(0) - ctx.locals_stack_w[ctx.pycode.co_nlocals-1] = Variable() + ctx.locals_w[-1] = Variable() fs2 = ctx.getstate(0) assert not fs1.matches(fs2) @@ -44,7 +44,7 @@ def test_union_on_hacked_framestates(self): ctx = self.get_context(self.func_simple) fs1 = ctx.getstate(0) - ctx.locals_stack_w[ctx.pycode.co_nlocals-1] = Variable() + ctx.locals_w[-1] = Variable() fs2 = ctx.getstate(0) assert fs1.union(fs2).matches(fs2) # fs2 is more general assert fs2.union(fs1).matches(fs2) # fs2 is more general @@ -52,7 +52,7 @@ def test_restore_frame(self): ctx = self.get_context(self.func_simple) fs1 = ctx.getstate(0) - ctx.locals_stack_w[ctx.pycode.co_nlocals-1] = Variable() + ctx.locals_w[-1] = Variable() ctx.setstate(fs1) assert fs1.matches(ctx.getstate(0)) @@ -71,26 +71,25 @@ def test_getoutputargs(self): ctx = self.get_context(self.func_simple) fs1 = ctx.getstate(0) - ctx.locals_stack_w[ctx.pycode.co_nlocals-1] = Variable() + ctx.locals_w[-1] = Variable() fs2 = ctx.getstate(0) outputargs = fs1.getoutputargs(fs2) # 'x' -> 'x' is a Variable # locals_w[n-1] -> locals_w[n-1] is Constant(None) - assert outputargs == [ctx.locals_stack_w[0], Constant(None)] + assert outputargs == [ctx.locals_w[0], Constant(None)] def test_union_different_constants(self): ctx = self.get_context(self.func_simple) fs1 = ctx.getstate(0) - ctx.locals_stack_w[ctx.pycode.co_nlocals-1] = Constant(42) + ctx.locals_w[-1] = Constant(42) fs2 = ctx.getstate(0) fs3 = fs1.union(fs2) ctx.setstate(fs3) - assert isinstance(ctx.locals_stack_w[ctx.pycode.co_nlocals-1], - Variable) # generalized + assert isinstance(ctx.locals_w[-1], Variable) # generalized def test_union_spectag(self): ctx = self.get_context(self.func_simple) fs1 = ctx.getstate(0) - ctx.locals_stack_w[ctx.pycode.co_nlocals-1] = Constant(SpecTag()) + ctx.locals_w[-1] = Constant(SpecTag()) fs2 = ctx.getstate(0) assert fs1.union(fs2) is None # UnionError diff --git a/rpython/jit/backend/arm/arch.py b/rpython/jit/backend/arm/arch.py --- a/rpython/jit/backend/arm/arch.py +++ b/rpython/jit/backend/arm/arch.py @@ -1,4 +1,3 @@ -FUNC_ALIGN = 8 WORD = 4 DOUBLE_WORD = 8 diff --git a/rpython/jit/backend/arm/assembler.py b/rpython/jit/backend/arm/assembler.py --- a/rpython/jit/backend/arm/assembler.py +++ b/rpython/jit/backend/arm/assembler.py @@ -4,7 +4,7 @@ from rpython.jit.backend.arm import conditions as c, registers as r from rpython.jit.backend.arm import shift -from rpython.jit.backend.arm.arch import (WORD, DOUBLE_WORD, FUNC_ALIGN, +from rpython.jit.backend.arm.arch import (WORD, DOUBLE_WORD, JITFRAME_FIXED_SIZE) from rpython.jit.backend.arm.codebuilder import InstrBuilder, OverwritingBuilder from rpython.jit.backend.arm.locations import imm, StackLocation, get_fp_offset @@ -484,10 +484,6 @@ self.mc.BL(target) return startpos - def align(self): - while(self.mc.currpos() % FUNC_ALIGN != 0): - self.mc.writechar(chr(0)) - def gen_func_epilog(self, mc=None, cond=c.AL): gcrootmap = self.cpu.gc_ll_descr.gcrootmap if mc is None: @@ -557,7 +553,7 @@ debug_stop('jit-backend-ops') def _call_header(self): - self.align() + assert self.currpos() == 0 self.gen_func_prolog() def _call_header_with_stack_check(self): diff --git a/rpython/jit/backend/arm/callbuilder.py b/rpython/jit/backend/arm/callbuilder.py --- a/rpython/jit/backend/arm/callbuilder.py +++ b/rpython/jit/backend/arm/callbuilder.py @@ -176,11 +176,14 @@ def write_real_errno(self, save_err): if save_err & rffi.RFFI_READSAVED_ERRNO: - # Just before a call, read 'rpy_errno' and write it into the + # Just before a call, read '*_errno' and write it into the # real 'errno'. The r0-r3 registers contain arguments to the # future call; the r5-r7 registers contain various stuff. # We still have r8-r12. - rpy_errno = llerrno.get_rpy_errno_offset(self.asm.cpu) + if save_err & rffi.RFFI_ALT_ERRNO: + rpy_errno = llerrno.get_alt_errno_offset(self.asm.cpu) + else: + rpy_errno = llerrno.get_rpy_errno_offset(self.asm.cpu) p_errno = llerrno.get_p_errno_offset(self.asm.cpu) self.mc.LDR_ri(r.r9.value, r.sp.value, self.asm.saved_threadlocal_addr + self.current_sp) @@ -199,10 +202,13 @@ def read_real_errno(self, save_err): if save_err & rffi.RFFI_SAVE_ERRNO: # Just after a call, read the real 'errno' and save a copy of - # it inside our thread-local 'rpy_errno'. Registers r8-r12 + # it inside our thread-local '*_errno'. Registers r8-r12 # are unused here, and registers r2-r3 never contain anything # after the call. - rpy_errno = llerrno.get_rpy_errno_offset(self.asm.cpu) + if save_err & rffi.RFFI_ALT_ERRNO: + rpy_errno = llerrno.get_alt_errno_offset(self.asm.cpu) + else: + rpy_errno = llerrno.get_rpy_errno_offset(self.asm.cpu) p_errno = llerrno.get_p_errno_offset(self.asm.cpu) self.mc.LDR_ri(r.r3.value, r.sp.value, self.asm.saved_threadlocal_addr) 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,7 +1,7 @@ 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, FUNC_ALIGN, PC_OFFSET) +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 from rpython.rlib.objectmodel import we_are_translated @@ -29,14 +29,9 @@ class AbstractARMBuilder(object): - def __init__(self, arch_version=7): self.arch_version = arch_version - def align(self): - while(self.currpos() % FUNC_ALIGN != 0): - self.writechar(chr(0)) - def NOP(self): self.MOV_rr(0, 0) @@ -467,21 +462,6 @@ f.write(data[i]) f.close() - # XXX remove and setup aligning in llsupport - def materialize(self, asmmemmgr, allblocks, gcrootmap=None): - size = self.get_relative_pos() + WORD - malloced = asmmemmgr.malloc(size, size + 7) - allblocks.append(malloced) - rawstart = malloced[0] - while(rawstart % FUNC_ALIGN != 0): - rawstart += 1 - self.copy_to_raw_memory(rawstart) - if self.gcroot_markers is not None: - assert gcrootmap is not None - for pos, mark in self.gcroot_markers: - gcrootmap.put(rawstart + pos, mark) - return rawstart - def clear_cache(self, addr): if we_are_translated(): startaddr = rffi.cast(llmemory.Address, addr) diff --git a/rpython/jit/backend/llsupport/asmmemmgr.py b/rpython/jit/backend/llsupport/asmmemmgr.py --- a/rpython/jit/backend/llsupport/asmmemmgr.py +++ b/rpython/jit/backend/llsupport/asmmemmgr.py @@ -208,6 +208,8 @@ ('data', lltype.FixedSizeArray(lltype.Char, SUBBLOCK_SIZE))) SUBBLOCK_PTR.TO.become(SUBBLOCK) + ALIGN_MATERIALIZE = 16 + gcroot_markers = None def __init__(self, translated=None): @@ -303,9 +305,12 @@ def materialize(self, asmmemmgr, allblocks, gcrootmap=None): size = self.get_relative_pos() + align = self.ALIGN_MATERIALIZE + size += align - 1 malloced = asmmemmgr.malloc(size, size) allblocks.append(malloced) rawstart = malloced[0] + rawstart = (rawstart + align - 1) & (-align) self.copy_to_raw_memory(rawstart) if self.gcroot_markers is not None: assert gcrootmap is not None diff --git a/rpython/jit/backend/llsupport/llerrno.py b/rpython/jit/backend/llsupport/llerrno.py --- a/rpython/jit/backend/llsupport/llerrno.py +++ b/rpython/jit/backend/llsupport/llerrno.py @@ -18,19 +18,41 @@ return 3 * WORD +def get_debug_saved_alterrno(cpu): + return cpu._debug_errno_container[4] + +def set_debug_saved_alterrno(cpu, nerrno): + assert nerrno >= 0 + cpu._debug_errno_container[4] = nerrno + +def get_alt_errno_offset(cpu): + if cpu.translate_support_code: + from rpython.rlib import rthread + return rthread.tlfield_alt_errno.getoffset() + else: + return 4 * WORD + + def get_debug_saved_lasterror(cpu): - return cpu._debug_errno_container[4] + return cpu._debug_errno_container[5] def set_debug_saved_lasterror(cpu, nerrno): assert nerrno >= 0 - cpu._debug_errno_container[4] = nerrno + cpu._debug_errno_container[5] = nerrno def get_rpy_lasterror_offset(cpu): if cpu.translate_support_code: from rpython.rlib import rthread return rthread.tlfield_rpy_lasterror.getoffset() else: - return 4 * WORD + return 5 * WORD + +def get_alt_lasterror_offset(cpu): + if cpu.translate_support_code: + from rpython.rlib import rthread + return rthread.tlfield_alt_lasterror.getoffset() + else: + return 6 * WORD def _fetch_addr_errno(): diff --git a/rpython/jit/backend/llsupport/llmodel.py b/rpython/jit/backend/llsupport/llmodel.py --- a/rpython/jit/backend/llsupport/llmodel.py +++ b/rpython/jit/backend/llsupport/llmodel.py @@ -63,7 +63,7 @@ ad.lendescr, FLAG_FLOAT) self.setup() self._debug_errno_container = lltype.malloc( - rffi.CArray(lltype.Signed), 5, flavor='raw', zero=True, + rffi.CArray(lltype.Signed), 7, flavor='raw', zero=True, track_allocation=False) def getarraydescr_for_frame(self, type): diff --git a/rpython/jit/backend/llsupport/test/zrpy_releasegil_test.py b/rpython/jit/backend/llsupport/test/zrpy_releasegil_test.py --- a/rpython/jit/backend/llsupport/test/zrpy_releasegil_test.py +++ b/rpython/jit/backend/llsupport/test/zrpy_releasegil_test.py @@ -98,6 +98,7 @@ self.run('close_stack') assert 'call_release_gil' in udir.join('TestCompileFramework.log').read() + # XXX this should also test get/set_alterrno ? def define_get_set_errno(self): eci = ExternalCompilationInfo( post_include_bits=[r''' diff --git a/rpython/jit/backend/test/runner_test.py b/rpython/jit/backend/test/runner_test.py --- a/rpython/jit/backend/test/runner_test.py +++ b/rpython/jit/backend/test/runner_test.py @@ -47,6 +47,7 @@ add_loop_instructions = ['overload for a specific cpu'] bridge_loop_instructions = ['overload for a specific cpu'] + bridge_loop_instructions_alternative = None # or another possible answer def execute_operation(self, opname, valueboxes, result_type, descr=None): inputargs, operations = self._get_single_operation_list(opname, @@ -2948,7 +2949,11 @@ calldescr = self.cpu._calldescr_dynamic_for_tests([types.slong]*7, types.slong) # - for saveerr in [rffi.RFFI_ERR_NONE, rffi.RFFI_SAVE_ERRNO]: + for saveerr in [rffi.RFFI_ERR_NONE, + rffi.RFFI_SAVE_ERRNO, + rffi.RFFI_ERR_NONE | rffi.RFFI_ALT_ERRNO, + rffi.RFFI_SAVE_ERRNO | rffi.RFFI_ALT_ERRNO, + ]: faildescr = BasicFailDescr(1) inputargs = [BoxInt() for i in range(7)] i1 = BoxInt() @@ -2965,15 +2970,23 @@ self.cpu.compile_loop(inputargs, ops, looptoken) # llerrno.set_debug_saved_errno(self.cpu, 24) + llerrno.set_debug_saved_alterrno(self.cpu, 25) deadframe = self.cpu.execute_token(looptoken, 9, 8, 7, 6, 5, 4, 3) original_result = self.cpu.get_int_value(deadframe, 0) result = llerrno.get_debug_saved_errno(self.cpu) - print 'saveerr =', saveerr, ': got result =', result + altresult = llerrno.get_debug_saved_alterrno(self.cpu) + print 'saveerr =', saveerr, ': got result =', result, \ + 'altresult =', altresult # - if saveerr == rffi.RFFI_SAVE_ERRNO: - assert result == 42 # from the C code - else: - assert result == 24 # not touched + expected = { + rffi.RFFI_ERR_NONE: (24, 25), + rffi.RFFI_SAVE_ERRNO: (42, 25), + rffi.RFFI_ERR_NONE | rffi.RFFI_ALT_ERRNO: (24, 25), + rffi.RFFI_SAVE_ERRNO | rffi.RFFI_ALT_ERRNO: (24, 42), + } + # expected (24, 25) as originally set, with possibly one + # of the two changed to 42 by the assembler code + assert (result, altresult) == expected[saveerr] assert original_result == 3456789 def test_call_release_gil_readsaved_errno(self): @@ -3007,7 +3020,11 @@ calldescr = self.cpu._calldescr_dynamic_for_tests([types.slong]*7, types.slong) # - for saveerr in [rffi.RFFI_READSAVED_ERRNO, rffi.RFFI_ZERO_ERRNO_BEFORE]: + for saveerr in [rffi.RFFI_READSAVED_ERRNO, + rffi.RFFI_ZERO_ERRNO_BEFORE, + rffi.RFFI_READSAVED_ERRNO | rffi.RFFI_ALT_ERRNO, + rffi.RFFI_ZERO_ERRNO_BEFORE | rffi.RFFI_ALT_ERRNO, + ]: faildescr = BasicFailDescr(1) inputargs = [BoxInt() for i in range(7)] i1 = BoxInt() @@ -3024,12 +3041,17 @@ self.cpu.compile_loop(inputargs, ops, looptoken) # llerrno.set_debug_saved_errno(self.cpu, 24) + llerrno.set_debug_saved_alterrno(self.cpu, 25) deadframe = self.cpu.execute_token(looptoken, 9, 8, 7, 6, 5, 4, 3) result = self.cpu.get_int_value(deadframe, 0) assert llerrno.get_debug_saved_errno(self.cpu) == 24 + assert llerrno.get_debug_saved_alterrno(self.cpu) == 25 # - if saveerr == rffi.RFFI_READSAVED_ERRNO: - assert result == 24 + 345678900 + if saveerr & rffi.RFFI_READSAVED_ERRNO: + if saveerr & rffi.RFFI_ALT_ERRNO: + assert result == 25 + 345678900 + else: + assert result == 24 + 345678900 else: assert result == 0 + 345678900 @@ -3064,7 +3086,10 @@ types.slong) # for saveerr in [rffi.RFFI_SAVE_ERRNO, # but not _LASTERROR - rffi.RFFI_SAVE_LASTERROR]: + rffi.RFFI_SAVE_ERRNO | rffi.RFFI_ALT_ERRNO, + rffi.RFFI_SAVE_LASTERROR, + rffi.RFFI_SAVE_LASTERROR | rffi.RFFI_ALT_ERRNO, + ]: faildescr = BasicFailDescr(1) inputargs = [BoxInt() for i in range(7)] i1 = BoxInt() @@ -3125,7 +3150,9 @@ calldescr = self.cpu._calldescr_dynamic_for_tests([types.slong]*7, types.slong) # - for saveerr in [rffi.RFFI_READSAVED_LASTERROR]: + for saveerr in [rffi.RFFI_READSAVED_LASTERROR, + rffi.RFFI_READSAVED_LASTERROR | rffi.RFFI_ALT_ERRNO, + ]: faildescr = BasicFailDescr(1) inputargs = [BoxInt() for i in range(7)] i1 = BoxInt() @@ -3198,7 +3225,10 @@ calldescr = self.cpu._calldescr_dynamic_for_tests([types.slong]*7, types.slong) # - for saveerr in [rffi.RFFI_ERR_ALL]: + for saveerr in [rffi.RFFI_ERR_ALL, + rffi.RFFI_ERR_ALL | rffi.RFFI_ALT_ERRNO, + ]: + use_alt_errno = saveerr & rffi.RFFI_ALT_ERRNO faildescr = BasicFailDescr(1) inputargs = [BoxInt() for i in range(7)] i1 = BoxInt() @@ -3214,7 +3244,10 @@ looptoken = JitCellToken() self.cpu.compile_loop(inputargs, ops, looptoken) # - llerrno.set_debug_saved_errno(self.cpu, 8) + if use_alt_errno: + llerrno.set_debug_saved_alterrno(self.cpu, 8) + else: + llerrno.set_debug_saved_errno(self.cpu, 8) llerrno.set_debug_saved_lasterror(self.cpu, 9) deadframe = self.cpu.execute_token(looptoken, 1, 2, 3, 4, 5, 6, 7) result = self.cpu.get_int_value(deadframe, 0) @@ -4252,7 +4285,9 @@ # XXX we have to check the precise assembler, otherwise # we don't quite know if borders are correct - def checkops(mc, ops): + def checkops(mc, ops, alt_ops=None): + if len(mc) != len(ops) and alt_ops is not None: + ops = alt_ops assert len(mc) == len(ops) for i in range(len(mc)): if ops[i] == '*': @@ -4267,7 +4302,8 @@ data = ctypes.string_at(bridge_info.asmaddr, bridge_info.asmlen) mc = list(machine_code_dump(data, bridge_info.asmaddr, cpuname)) lines = [line for line in mc if line.count('\t') >= 2] - checkops(lines, self.bridge_loop_instructions) + checkops(lines, self.bridge_loop_instructions, + self.bridge_loop_instructions_alternative) except ObjdumpNotFound: py.test.skip("requires (g)objdump") diff --git a/rpython/jit/backend/x86/callbuilder.py b/rpython/jit/backend/x86/callbuilder.py --- a/rpython/jit/backend/x86/callbuilder.py +++ b/rpython/jit/backend/x86/callbuilder.py @@ -196,21 +196,27 @@ SetLastError_addr = self.asm.cpu.cast_adr_to_int(adr) assert isinstance(self, CallBuilder32) # Windows 32-bit only # - rpy_lasterror = llerrno.get_rpy_lasterror_offset(self.asm.cpu) + if save_err & rffi.RFFI_ALT_ERRNO: + lasterror = llerrno.get_alt_lasterror_offset(self.asm.cpu) + else: + lasterror = llerrno.get_rpy_lasterror_offset(self.asm.cpu) tlofsreg = self.get_tlofs_reg() # => esi, callee-saved self.save_stack_position() # => edi, callee-saved - mc.PUSH_m((tlofsreg.value, rpy_lasterror)) + mc.PUSH_m((tlofsreg.value, lasterror)) mc.CALL(imm(SetLastError_addr)) # restore the stack position without assuming a particular # calling convention of _SetLastError() self.mc.MOV(esp, self.saved_stack_position_reg) if save_err & rffi.RFFI_READSAVED_ERRNO: - # Just before a call, read 'rpy_errno' and write it into the + # Just before a call, read '*_errno' and write it into the # real 'errno'. Most registers are free here, including the # callee-saved ones, except 'ebx' and except the ones used to # pass the arguments on x86-64. - rpy_errno = llerrno.get_rpy_errno_offset(self.asm.cpu) + if save_err & rffi.RFFI_ALT_ERRNO: + rpy_errno = llerrno.get_alt_errno_offset(self.asm.cpu) + else: + rpy_errno = llerrno.get_rpy_errno_offset(self.asm.cpu) p_errno = llerrno.get_p_errno_offset(self.asm.cpu) tlofsreg = self.get_tlofs_reg() # => esi or r12, callee-saved if IS_X86_32: @@ -234,11 +240,14 @@ if save_err & rffi.RFFI_SAVE_ERRNO: # Just after a call, read the real 'errno' and save a copy of - # it inside our thread-local 'rpy_errno'. Most registers are + # it inside our thread-local '*_errno'. Most registers are # free here, including the callee-saved ones, except 'ebx'. # The tlofs register might have been loaded earlier and is # callee-saved, so it does not need to be reloaded. - rpy_errno = llerrno.get_rpy_errno_offset(self.asm.cpu) + if save_err & rffi.RFFI_ALT_ERRNO: + rpy_errno = llerrno.get_alt_errno_offset(self.asm.cpu) + else: + rpy_errno = llerrno.get_rpy_errno_offset(self.asm.cpu) p_errno = llerrno.get_p_errno_offset(self.asm.cpu) tlofsreg = self.get_tlofs_reg() # => esi or r12 (possibly reused) mc.MOV_rm(edi.value, (tlofsreg.value, p_errno)) @@ -256,13 +265,16 @@ GetLastError_addr = self.asm.cpu.cast_adr_to_int(adr) assert isinstance(self, CallBuilder32) # Windows 32-bit only # - rpy_lasterror = llerrno.get_rpy_lasterror_offset(self.asm.cpu) + if save_err & rffi.RFFI_ALT_ERRNO: + lasterror = llerrno.get_alt_lasterror_offset(self.asm.cpu) + else: + lasterror = llerrno.get_rpy_lasterror_offset(self.asm.cpu) self.save_result_value(save_edx=True) # save eax/edx/xmm0 self.result_value_saved_early = True mc.CALL(imm(GetLastError_addr)) # tlofsreg = self.get_tlofs_reg() # => esi (possibly reused) - mc.MOV32_mr((tlofsreg.value, rpy_lasterror), eax.value) + mc.MOV32_mr((tlofsreg.value, lasterror), eax.value) def move_real_result_and_call_reacqgil_addr(self, fastgil): from rpython.jit.backend.x86 import rx86 @@ -314,8 +326,8 @@ # in 'ebx'), and if not, we fall back to 'reacqgil_addr'. mc.J_il8(rx86.Conditions['NE'], 0) jne_location = mc.get_relative_pos() - # here, ecx is zero (so rpy_fastgil was in 'released' state - # before the XCHG, but the XCHG acquired it by writing 1) + # here, ecx (=old_value) is zero (so rpy_fastgil was in 'released' + # state before the XCHG, but the XCHG acquired it by writing 1) rst = gcrootmap.get_root_stack_top_addr() mc = self.mc mc.CMP(ebx, heap(rst)) 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 @@ -339,14 +339,21 @@ self.possibly_free_var(arg) def flush_loop(self): + # Force the code to be aligned to a multiple of 16. Also, # rare case: if the loop is too short, or if we are just after - # a GUARD_NOT_INVALIDATED, pad with NOPs. Important! This must - # be called to ensure that there are enough bytes produced, - # because GUARD_NOT_INVALIDATED or redirect_call_assembler() - # will maybe overwrite them. + # a GUARD_NOT_INVALIDATED, we need to make sure we insert enough + # NOPs. This is important to ensure that there are enough bytes + # produced, because GUARD_NOT_INVALIDATED or + # redirect_call_assembler() will maybe overwrite them. (In that + # rare case we don't worry too much about alignment.) mc = self.assembler.mc - while mc.get_relative_pos() < self.min_bytes_before_label: - mc.NOP() + current_pos = mc.get_relative_pos() + target_pos = (current_pos + 15) & ~15 + target_pos = max(target_pos, self.min_bytes_before_label) + insert_nops = target_pos - current_pos + assert 0 <= insert_nops <= 15 + for c in mc.MULTIBYTE_NOPs[insert_nops]: + mc.writechar(c) def loc(self, v): if v is None: # xxx kludgy 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 @@ -671,11 +671,39 @@ def invert_condition(cond_num): return cond_num ^ 1 + class X86_32_CodeBuilder(AbstractX86CodeBuilder): WORD = 4 PMOVMSKB_rx = xmminsn('\x66', rex_nw, '\x0F\xD7', register(1, 8), register(2), '\xC0') + # multibyte nops, from 0 to 15 bytes + MULTIBYTE_NOPs = [ + '', + '\x90', # nop + '\x66\x90', # xchg ax, ax + '\x8d\x76\x00', # lea 0x0(%esi),%esi + '\x8d\x74\x26\x00', # lea 0x0(%esi,%eiz,1),%esi + '\x90\x8d\x74\x26\x00', # nop; lea 0x0(%esi,%eiz,1),%esi + '\x8d\xb6\x00\x00\x00\x00', # lea 0x0(%esi),%esi + '\x8d\xb4\x26\x00\x00\x00\x00', # lea 0x0(%esi,%eiz,1),%esi + ('\x90' # nop + '\x8d\xb4\x26\x00\x00\x00\x00'),# lea 0x0(%esi,%eiz,1),%esi + ('\x89\xf6' # mov %esi,%esi + '\x8d\xbc\x27\x00\x00\x00\x00'),# lea 0x0(%edi,%eiz,1),%edi + ('\x8d\x76\x00' # lea 0x0(%esi),%esi + '\x8d\xbc\x27\x00\x00\x00\x00'),# lea 0x0(%edi,%eiz,1),%edi + ('\x8d\x74\x26\x00' # lea 0x0(%esi,%eiz,1),%esi + '\x8d\xbc\x27\x00\x00\x00\x00'),# lea 0x0(%edi,%eiz,1),%edi + ('\x8d\xb6\x00\x00\x00\x00' # lea 0x0(%esi),%esi + '\x8d\xbf\x00\x00\x00\x00'), # lea 0x0(%edi),%edi + ('\x8d\xb6\x00\x00\x00\x00' # lea 0x0(%esi),%esi + '\x8d\xbc\x27\x00\x00\x00\x00'),# lea 0x0(%edi,%eiz,1),%edi + ('\x8d\xb4\x26\x00\x00\x00\x00' # lea 0x0(%esi,%eiz,1),%esi + '\x8d\xbc\x27\x00\x00\x00\x00'),# lea 0x0(%edi,%eiz,1),%edi + ('\xeb\x0d' + '\x90' * 13)] # jmp +x0d; a bunch of nops + + class X86_64_CodeBuilder(AbstractX86CodeBuilder): WORD = 8 @@ -706,6 +734,24 @@ else: self.MOV_ri64(reg, immed) + # multibyte nops, from 0 to 15 bytes + MULTIBYTE_NOPs = ([ + '', + '\x90', # nop + '\x66\x90', # xchg ax, ax + '\x0f\x1f\x00', # nopl (%rax) + '\x0f\x1f\x40\x00', # nopl 0x0(%rax) + '\x0f\x1f\x44\x00\x00', # nopl 0x0(%rax,%rax,1) + '\x66\x0f\x1f\x44\x00\x00', # nopw 0x0(%rax,%rax,1) + '\x0f\x1f\x80\x00\x00\x00\x00', # nopl 0x0(%rax) + ('\x0f\x1f\x84\x00\x00\x00\x00' # nopl 0x0(%rax,%rax,1) + '\x00'), + ('\x66\x0f\x1f\x84\x00\x00\x00' # nopw 0x0(%rax,%rax,1) + '\x00\x00')] + + ['\x66' * _i + '\x2e\x0f\x1f' # nopw %cs:0x0(%rax,%rax,1) + '\x84\x00\x00\x00\x00\x00' for _i in range(1, 7)]) + + def define_modrm_modes(insnname_template, before_modrm, after_modrm=[], regtype='GPR'): def add_insn(code, *modrm): args = before_modrm + list(modrm) diff --git a/rpython/jit/backend/x86/test/test_runner.py b/rpython/jit/backend/x86/test/test_runner.py --- a/rpython/jit/backend/x86/test/test_runner.py +++ b/rpython/jit/backend/x86/test/test_runner.py @@ -30,12 +30,24 @@ # for the individual tests see # ====> ../../test/runner_test.py - add_loop_instructions = ['mov', 'add', 'test', 'je', 'jmp'] if WORD == 4: - bridge_loop_instructions = ['cmp', 'jge', 'mov', 'mov', 'call', 'jmp'] + add_loop_instructions = ['mov', + 'lea', # a nop, for the label + 'add', 'test', 'je', 'jmp', + 'nop'] # padding + bridge_loop_instructions = ['cmp', 'jge', 'mov', 'mov', 'call', 'jmp', + 'lea', 'lea'] # padding else: - bridge_loop_instructions = ['cmp', 'jge', 'mov', 'mov', 'mov', 'mov', - 'call', 'mov', 'jmp'] + add_loop_instructions = ['mov', + 'nop', # for the label + 'add', 'test', 'je', 'jmp', + 'data32'] # padding + bridge_loop_instructions = [ + 'cmp', 'jge', 'mov', 'mov', 'mov', 'mov', 'call', 'mov', 'jmp', + 'nop'] # padding + bridge_loop_instructions_alternative = [ + 'cmp', 'jge', 'mov', 'mov', 'mov', 'call', 'mov', 'jmp', + 'nop'] # padding def get_cpu(self): cpu = CPU(rtyper=None, stats=FakeStats()) diff --git a/rpython/jit/backend/x86/test/test_rx86.py b/rpython/jit/backend/x86/test/test_rx86.py --- a/rpython/jit/backend/x86/test/test_rx86.py +++ b/rpython/jit/backend/x86/test/test_rx86.py @@ -229,3 +229,9 @@ s = CodeBuilder64() s.MOVSD_xj(xmm2, 0x01234567) assert s.getvalue() == '\xF2\x0F\x10\x14\x25\x67\x45\x23\x01' + +def test_multibyte_nops(): + for cls in [X86_64_CodeBuilder, X86_32_CodeBuilder]: + assert len(cls.MULTIBYTE_NOPs) == 16 + for i in range(16): + assert len(cls.MULTIBYTE_NOPs[i]) == i diff --git a/rpython/jit/backend/x86/test/test_rx86_32_auto_encoding.py b/rpython/jit/backend/x86/test/test_rx86_32_auto_encoding.py --- a/rpython/jit/backend/x86/test/test_rx86_32_auto_encoding.py +++ b/rpython/jit/backend/x86/test/test_rx86_32_auto_encoding.py @@ -317,7 +317,9 @@ # CALL_j is actually relative, so tricky to test (instrname == 'CALL' and argmodes == 'j') or # SET_ir must be tested manually - (instrname == 'SET' and argmodes == 'ir') + (instrname == 'SET' and argmodes == 'ir') or + # MULTIBYTE_NOPs can't easily be tested the same way _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit