Author: Richard Plangger <[email protected]>
Branch: py3.5-ssl
Changeset: r88695:2294debaacb0
Date: 2016-11-28 11:22 +0100
http://bitbucket.org/pypy/pypy/changeset/2294debaacb0/
Log: catch up with py3.5
diff too long, truncating to 2000 out of 3598 lines
diff --git a/lib-python/2.7/distutils/sysconfig_pypy.py
b/lib-python/2.7/distutils/sysconfig_pypy.py
--- a/lib-python/2.7/distutils/sysconfig_pypy.py
+++ b/lib-python/2.7/distutils/sysconfig_pypy.py
@@ -12,7 +12,6 @@
import sys
import os
-import shlex
import imp
from distutils.errors import DistutilsPlatformError
@@ -62,11 +61,18 @@
def _init_posix():
"""Initialize the module as appropriate for POSIX systems."""
g = {}
+ g['CC'] = "gcc -pthread"
+ g['CXX'] = "g++ -pthread"
+ g['OPT'] = "-DNDEBUG -O2"
+ g['CFLAGS'] = "-DNDEBUG -O2"
+ g['CCSHARED'] = "-fPIC"
+ g['LDSHARED'] = "gcc -pthread -shared"
+ g['SO'] = [s[0] for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION][0]
+ g['AR'] = "ar"
+ g['ARFLAGS'] = "rc"
g['EXE'] = ""
- g['SO'] = [s[0] for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION][0]
g['LIBDIR'] = os.path.join(sys.prefix, 'lib')
- g['CC'] = "gcc -pthread" # -pthread might not be valid on OS/X, check
- g['OPT'] = ""
+ g['VERSION'] = get_python_version()
global _config_vars
_config_vars = g
@@ -118,30 +124,80 @@
"""
return get_config_vars().get(name)
+
def customize_compiler(compiler):
- """Dummy method to let some easy_install packages that have
- optional C speedup components.
+ """Do any platform-specific customization of a CCompiler instance.
+
+ Mainly needed on Unix, so we can plug in the information that
+ varies across Unices and is stored in Python's Makefile (CPython)
+ or hard-coded in _init_posix() (PyPy).
"""
- def customize(executable, flags):
- command = compiler.executables[executable] + flags
- setattr(compiler, executable, command)
+ if compiler.compiler_type == "unix":
+ if sys.platform == "darwin":
+ # Perform first-time customization of compiler-related
+ # config vars on OS X now that we know we need a compiler.
+ # This is primarily to support Pythons from binary
+ # installers. The kind and paths to build tools on
+ # the user system may vary significantly from the system
+ # that Python itself was built on. Also the user OS
+ # version and build tools may not support the same set
+ # of CPU architectures for universal builds.
+ global _config_vars
+ # Use get_config_var() to ensure _config_vars is initialized.
+ if not get_config_var('CUSTOMIZED_OSX_COMPILER'):
+ import _osx_support
+ _osx_support.customize_compiler(_config_vars)
+ _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
- if compiler.compiler_type == "unix":
- # compiler_so can be c++ which has no -Wimplicit
- #compiler.compiler_so.extend(['-O2', '-fPIC', '-Wimplicit'])
- compiler.compiler_so.extend(['-O2', '-fPIC'])
- compiler.shared_lib_extension = get_config_var('SO')
- if "CPPFLAGS" in os.environ:
- cppflags = shlex.split(os.environ["CPPFLAGS"])
- for executable in ('compiler', 'compiler_so', 'linker_so'):
- customize(executable, cppflags)
- if "CFLAGS" in os.environ:
- cflags = shlex.split(os.environ["CFLAGS"])
- for executable in ('compiler', 'compiler_so', 'linker_so'):
- customize(executable, cflags)
- if "LDFLAGS" in os.environ:
- ldflags = shlex.split(os.environ["LDFLAGS"])
- customize('linker_so', ldflags)
+ (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \
+ get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
+ 'CCSHARED', 'LDSHARED', 'SO', 'AR',
+ 'ARFLAGS')
+
+ if 'CC' in os.environ:
+ newcc = os.environ['CC']
+ if (sys.platform == 'darwin'
+ and 'LDSHARED' not in os.environ
+ and ldshared.startswith(cc)):
+ # On OS X, if CC is overridden, use that as the default
+ # command for LDSHARED as well
+ ldshared = newcc + ldshared[len(cc):]
+ cc = newcc
+ if 'CXX' in os.environ:
+ cxx = os.environ['CXX']
+ if 'LDSHARED' in os.environ:
+ ldshared = os.environ['LDSHARED']
+ if 'CPP' in os.environ:
+ cpp = os.environ['CPP']
+ else:
+ cpp = cc + " -E" # not always
+ if 'LDFLAGS' in os.environ:
+ ldshared = ldshared + ' ' + os.environ['LDFLAGS']
+ if 'CFLAGS' in os.environ:
+ cflags = opt + ' ' + os.environ['CFLAGS']
+ ldshared = ldshared + ' ' + os.environ['CFLAGS']
+ if 'CPPFLAGS' in os.environ:
+ cpp = cpp + ' ' + os.environ['CPPFLAGS']
+ cflags = cflags + ' ' + os.environ['CPPFLAGS']
+ ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
+ if 'AR' in os.environ:
+ ar = os.environ['AR']
+ if 'ARFLAGS' in os.environ:
+ archiver = ar + ' ' + os.environ['ARFLAGS']
+ else:
+ archiver = ar + ' ' + ar_flags
+
+ cc_cmd = cc + ' ' + cflags
+ compiler.set_executables(
+ preprocessor=cpp,
+ compiler=cc_cmd,
+ compiler_so=cc_cmd + ' ' + ccshared,
+ compiler_cxx=cxx,
+ linker_so=ldshared,
+ linker_exe=cc,
+ archiver=archiver)
+
+ compiler.shared_lib_extension = so_ext
from sysconfig_cpython import (
diff --git a/lib-python/3/ctypes/__init__.py b/lib-python/3/ctypes/__init__.py
--- a/lib-python/3/ctypes/__init__.py
+++ b/lib-python/3/ctypes/__init__.py
@@ -370,12 +370,13 @@
func.__name__ = name_or_ordinal
return func
-class PyDLL(CDLL):
- """This class represents the Python library itself. It allows to
- access Python API functions. The GIL is not released, and
- Python exceptions are handled correctly.
- """
- _func_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
+# Not in PyPy
+#class PyDLL(CDLL):
+# """This class represents the Python library itself. It allows to
+# access Python API functions. The GIL is not released, and
+# Python exceptions are handled correctly.
+# """
+# _func_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
if _os.name in ("nt", "ce"):
@@ -428,15 +429,8 @@
return self._dlltype(name)
cdll = LibraryLoader(CDLL)
-pydll = LibraryLoader(PyDLL)
-
-if _os.name in ("nt", "ce"):
- pythonapi = PyDLL("python dll", None, _sys.dllhandle)
-elif _sys.platform == "cygwin":
- pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2])
-else:
- pythonapi = PyDLL(None)
-
+# not on PyPy
+#pydll = LibraryLoader(PyDLL)
if _os.name in ("nt", "ce"):
windll = LibraryLoader(WinDLL)
diff --git a/lib-python/3/distutils/sysconfig_pypy.py
b/lib-python/3/distutils/sysconfig_pypy.py
--- a/lib-python/3/distutils/sysconfig_pypy.py
+++ b/lib-python/3/distutils/sysconfig_pypy.py
@@ -12,7 +12,7 @@
import sys
import os
-import shlex
+import imp
from distutils.errors import DistutilsPlatformError
@@ -61,11 +61,19 @@
def _init_posix():
"""Initialize the module as appropriate for POSIX systems."""
g = {}
+ g['CC'] = "gcc -pthread"
+ g['CXX'] = "g++ -pthread"
+ g['OPT'] = "-DNDEBUG -O2"
+ g['CFLAGS'] = "-DNDEBUG -O2"
+ g['CCSHARED'] = "-fPIC"
+ g['LDSHARED'] = "gcc -pthread -shared"
+ g['SO'] = [s[0] for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION][0]
+ g['SHLIB_SUFFIX'] = g['SO']
+ g['AR'] = "ar"
+ g['ARFLAGS'] = "rc"
g['EXE'] = ""
- g['SO'] = ".so"
- g['SOABI'] = g['SO'].rsplit('.')[0]
g['LIBDIR'] = os.path.join(sys.prefix, 'lib')
- g['CC'] = "gcc -pthread" # -pthread might not be valid on OS/X, check
+ g['VERSION'] = get_python_version()
global _config_vars
_config_vars = g
@@ -76,7 +84,7 @@
g = {}
g['EXE'] = ".exe"
g['SO'] = ".pyd"
- g['SOABI'] = g['SO'].rsplit('.')[0]
+ g['SOABI'] = g['SO'].rsplit('.')[0] # xxx?
global _config_vars
_config_vars = g
@@ -118,26 +126,78 @@
"""
return get_config_vars().get(name)
+
def customize_compiler(compiler):
- """Dummy method to let some easy_install packages that have
- optional C speedup components.
+ """Do any platform-specific customization of a CCompiler instance.
+
+ Mainly needed on Unix, so we can plug in the information that
+ varies across Unices and is stored in Python's Makefile.
"""
if compiler.compiler_type == "unix":
- compiler.compiler_so.extend(['-O2', '-fPIC', '-Wimplicit'])
- compiler.shared_lib_extension = get_config_var('SO')
- if "CPPFLAGS" in os.environ:
- cppflags = shlex.split(os.environ["CPPFLAGS"])
- compiler.compiler.extend(cppflags)
- compiler.compiler_so.extend(cppflags)
- compiler.linker_so.extend(cppflags)
- if "CFLAGS" in os.environ:
- cflags = shlex.split(os.environ["CFLAGS"])
- compiler.compiler.extend(cflags)
- compiler.compiler_so.extend(cflags)
- compiler.linker_so.extend(cflags)
- if "LDFLAGS" in os.environ:
- ldflags = shlex.split(os.environ["LDFLAGS"])
- compiler.linker_so.extend(ldflags)
+ if sys.platform == "darwin":
+ # Perform first-time customization of compiler-related
+ # config vars on OS X now that we know we need a compiler.
+ # This is primarily to support Pythons from binary
+ # installers. The kind and paths to build tools on
+ # the user system may vary significantly from the system
+ # that Python itself was built on. Also the user OS
+ # version and build tools may not support the same set
+ # of CPU architectures for universal builds.
+ global _config_vars
+ # Use get_config_var() to ensure _config_vars is initialized.
+ if not get_config_var('CUSTOMIZED_OSX_COMPILER'):
+ import _osx_support
+ _osx_support.customize_compiler(_config_vars)
+ _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
+
+ (cc, cxx, opt, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags)
= \
+ get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
+ 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR',
'ARFLAGS')
+
+ if 'CC' in os.environ:
+ newcc = os.environ['CC']
+ if (sys.platform == 'darwin'
+ and 'LDSHARED' not in os.environ
+ and ldshared.startswith(cc)):
+ # On OS X, if CC is overridden, use that as the default
+ # command for LDSHARED as well
+ ldshared = newcc + ldshared[len(cc):]
+ cc = newcc
+ if 'CXX' in os.environ:
+ cxx = os.environ['CXX']
+ if 'LDSHARED' in os.environ:
+ ldshared = os.environ['LDSHARED']
+ if 'CPP' in os.environ:
+ cpp = os.environ['CPP']
+ else:
+ cpp = cc + " -E" # not always
+ if 'LDFLAGS' in os.environ:
+ ldshared = ldshared + ' ' + os.environ['LDFLAGS']
+ if 'CFLAGS' in os.environ:
+ cflags = opt + ' ' + os.environ['CFLAGS']
+ ldshared = ldshared + ' ' + os.environ['CFLAGS']
+ if 'CPPFLAGS' in os.environ:
+ cpp = cpp + ' ' + os.environ['CPPFLAGS']
+ cflags = cflags + ' ' + os.environ['CPPFLAGS']
+ ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
+ if 'AR' in os.environ:
+ ar = os.environ['AR']
+ if 'ARFLAGS' in os.environ:
+ archiver = ar + ' ' + os.environ['ARFLAGS']
+ else:
+ archiver = ar + ' ' + ar_flags
+
+ cc_cmd = cc + ' ' + cflags
+ compiler.set_executables(
+ preprocessor=cpp,
+ compiler=cc_cmd,
+ compiler_so=cc_cmd + ' ' + ccshared,
+ compiler_cxx=cxx,
+ linker_so=ldshared,
+ linker_exe=cc,
+ archiver=archiver)
+
+ compiler.shared_lib_extension = shlib_suffix
from .sysconfig_cpython import (
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
@@ -29,3 +29,8 @@
.. branch: clean-exported-state
Clean-ups in the jit optimizeopt
+
+.. branch: conditional_call_value_4
+
+Add jit.conditional_call_elidable(), a way to tell the JIT "conditonally
+call this function" returning a result.
diff --git a/pypy/goal/targetpypystandalone.py
b/pypy/goal/targetpypystandalone.py
--- a/pypy/goal/targetpypystandalone.py
+++ b/pypy/goal/targetpypystandalone.py
@@ -119,7 +119,7 @@
try:
# initialize sys.{path,executable,stdin,stdout,stderr}
# (in unbuffered mode, to avoid troubles) and import site
- space.appexec([w_path, space.wrap(home), w_initstdio],
+ space.appexec([w_path, space.wrap_fsdecoded(home), w_initstdio],
r"""(path, home, initstdio):
import sys
sys.path[:] = path
diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py
--- a/pypy/interpreter/argument.py
+++ b/pypy/interpreter/argument.py
@@ -113,7 +113,9 @@
self.keywords = self.keywords + keywords
self.keywords_w = self.keywords_w + values_w
return
+ is_dict = False
if space.isinstance_w(w_starstararg, space.w_dict):
+ is_dict = True
keys_w = space.unpackiterable(w_starstararg)
else:
try:
@@ -127,7 +129,9 @@
keys_w = space.unpackiterable(w_keys)
keywords_w = [None] * len(keys_w)
keywords = [None] * len(keys_w)
- _do_combine_starstarargs_wrapped(space, keys_w, w_starstararg,
keywords, keywords_w, self.keywords)
+ _do_combine_starstarargs_wrapped(
+ space, keys_w, w_starstararg, keywords, keywords_w, self.keywords,
+ is_dict)
self.keyword_names_w = keys_w
if self.keywords is None:
self.keywords = keywords
@@ -396,7 +400,7 @@
key)
def _do_combine_starstarargs_wrapped(space, keys_w, w_starstararg, keywords,
- keywords_w, existingkeywords):
+ keywords_w, existingkeywords, is_dict):
i = 0
for w_key in keys_w:
try:
@@ -417,7 +421,16 @@
"got multiple values for keyword argument '%s'",
key)
keywords[i] = key
- keywords_w[i] = space.getitem(w_starstararg, w_key)
+ if is_dict:
+ # issue 2435: bug-to-bug compatibility with cpython. for a
subclass of
+ # dict, just ignore the __getitem__ and access the underlying dict
+ # directly
+ from pypy.objspace.descroperation import dict_getitem
+ w_descr = dict_getitem(space)
+ w_value = space.get_and_call_function(w_descr, w_starstararg,
w_key)
+ else:
+ w_value = space.getitem(w_starstararg, w_key)
+ keywords_w[i] = w_value
i += 1
@jit.look_inside_iff(
diff --git a/pypy/interpreter/astcompiler/misc.py
b/pypy/interpreter/astcompiler/misc.py
--- a/pypy/interpreter/astcompiler/misc.py
+++ b/pypy/interpreter/astcompiler/misc.py
@@ -20,7 +20,7 @@
If the user has set this warning to raise an error, a SyntaxError will be
raised."""
w_msg = space.wrap(msg)
- w_filename = space.wrap(fn)
+ w_filename = space.wrap_fsdecoded(fn)
w_lineno = space.wrap(lineno)
w_offset = space.wrap(offset)
_emit_syntax_warning(space, w_msg, w_filename, w_lineno, w_offset)
diff --git a/pypy/interpreter/pyparser/error.py
b/pypy/interpreter/pyparser/error.py
--- a/pypy/interpreter/pyparser/error.py
+++ b/pypy/interpreter/pyparser/error.py
@@ -30,7 +30,7 @@
'replace')
w_text = space.wrap(text)
if self.filename is not None:
- w_filename = space.fsdecode(space.newbytes(self.filename))
+ w_filename = space.wrap_fsdecoded(self.filename)
return space.newtuple([space.wrap(self.msg),
space.newtuple([w_filename,
space.wrap(self.lineno),
diff --git a/pypy/interpreter/test/test_argument.py
b/pypy/interpreter/test/test_argument.py
--- a/pypy/interpreter/test/test_argument.py
+++ b/pypy/interpreter/test/test_argument.py
@@ -127,6 +127,12 @@
raise OperationError(AttributeError, name)
return method(*args)
+ def lookup_in_type(self, cls, name):
+ return getattr(cls, name)
+
+ def get_and_call_function(self, w_descr, w_obj, *args):
+ return w_descr.__get__(w_obj)(*args)
+
def type(self, obj):
class Type:
def getname(self, space):
@@ -886,3 +892,19 @@
assert "keywords must be strings" in str(e.value)
e = raises(TypeError, "f(y=2, **{'x': 5}, x=6)")
assert "got multiple values for keyword argument 'x'" in str(e.value)
+
+ def test_dict_subclass_with_weird_getitem(self):
+ # issue 2435: bug-to-bug compatibility with cpython. for a subclass of
+ # dict, just ignore the __getitem__ and behave like ext_do_call in
ceval.c
+ # which just uses the underlying dict
+ class d(dict):
+ def __getitem__(self, key):
+ return key
+
+ for key in ["foo", u"foo"]:
+ q = d()
+ q[key] = "bar"
+
+ def test(**kwargs):
+ return kwargs
+ assert test(**q) == {"foo": "bar"}
diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py
--- a/pypy/module/cpyext/dictobject.py
+++ b/pypy/module/cpyext/dictobject.py
@@ -137,8 +137,7 @@
"""This is the same as PyDict_Merge(a, b, 1) in C, or a.update(b) in
Python. Return 0 on success or -1 if an exception was raised.
"""
- space.call_method(space.w_dict, "update", w_obj, w_other)
- return 0
+ return PyDict_Merge(space, w_obj, w_other, 1)
@cpython_api([PyObject], PyObject)
def PyDict_Keys(space, w_obj):
diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py
--- a/pypy/module/cpyext/listobject.py
+++ b/pypy/module/cpyext/listobject.py
@@ -62,12 +62,14 @@
position must be positive, indexing from the end of the list is not
supported. If pos is out of bounds, return NULL and set an
IndexError exception."""
+ from pypy.module.cpyext.sequence import CPyListStrategy
if not isinstance(w_list, W_ListObject):
PyErr_BadInternalCall(space)
if index < 0 or index >= w_list.length():
raise oefmt(space.w_IndexError, "list index out of range")
- w_list.ensure_object_strategy() # make sure we can return a borrowed obj
- # XXX ^^^ how does this interact with CPyListStrategy?
+ cpy_strategy = space.fromcache(CPyListStrategy)
+ if w_list.strategy is not cpy_strategy:
+ w_list.ensure_object_strategy() # make sure we can return a borrowed
obj
w_res = w_list.getitem(index)
return w_res # borrowed ref
diff --git a/pypy/module/cpyext/memoryobject.py
b/pypy/module/cpyext/memoryobject.py
--- a/pypy/module/cpyext/memoryobject.py
+++ b/pypy/module/cpyext/memoryobject.py
@@ -112,12 +112,10 @@
The memoryview object then owns the buffer represented by view, which
means you shouldn't try to call PyBuffer_Release() yourself: it
will be done on deallocation of the memoryview object."""
- if not view.c_buf:
- raise oefmt(space.w_ValueError,
- "cannot make memory view from a buffer with a NULL data "
- "pointer")
- buf = CBuffer(space, view.c_buf, view.c_len, view.c_obj)
- return space.wrap(W_MemoryView(buf))
+ w_obj = from_ref(space, view.c_obj)
+ if isinstance(w_obj, W_MemoryView):
+ return w_obj
+ return space.call_method(space.builtin, "memoryview", w_obj)
@cpython_api([PyObject], PyObject)
def PyMemoryView_GET_BASE(space, w_obj):
diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py
--- a/pypy/module/cpyext/modsupport.py
+++ b/pypy/module/cpyext/modsupport.py
@@ -1,7 +1,7 @@
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.module.cpyext.api import cpython_api, cpython_struct, \
METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, CONST_STRING
-from pypy.module.cpyext.pyobject import PyObject
+from pypy.module.cpyext.pyobject import PyObject, as_pyobj
from pypy.interpreter.module import Module
from pypy.module.cpyext.methodobject import (
W_PyCFunctionObject, PyCFunction_NewEx, PyDescr_NewMethod,
@@ -111,11 +111,17 @@
else:
PyErr_BadInternalCall(space)
-@cpython_api([PyObject], rffi.CCHARP, error=0)
-def PyModule_GetName(space, module):
+@cpython_api([PyObject], rffi.CCHARP)
+def PyModule_GetName(space, w_mod):
"""
Return module's __name__ value. If the module does not provide one,
- or if it is not a string, SystemError is raised and NULL is returned."""
- raise NotImplementedError
-
-
+ or if it is not a string, SystemError is raised and NULL is returned.
+ """
+ # NOTE: this version of the code works only because w_mod.w_name is
+ # a wrapped string object attached to w_mod; so it makes a
+ # PyStringObject that will live as long as the module itself,
+ # and returns a "char *" inside this PyStringObject.
+ if not isinstance(w_mod, Module):
+ raise oefmt(space.w_SystemError, "PyModule_GetName(): not a module")
+ from pypy.module.cpyext.unicodeobject import _PyUnicode_AsString
+ return _PyUnicode_AsString(space, as_pyobj(space, w_mod.w_name))
diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py
--- a/pypy/module/cpyext/object.py
+++ b/pypy/module/cpyext/object.py
@@ -260,7 +260,10 @@
def PyObject_Format(space, w_obj, w_format_spec):
if w_format_spec is None:
w_format_spec = space.wrap('')
- return space.call_method(w_obj, '__format__', w_format_spec)
+ w_ret = space.call_method(w_obj, '__format__', w_format_spec)
+ if space.isinstance_w(w_format_spec, space.w_unicode):
+ return space.unicode_from_object(w_ret)
+ return w_ret
@cpython_api([PyObject], PyObject)
def PyObject_ASCII(space, w_obj):
diff --git a/pypy/module/cpyext/test/test_arraymodule.py
b/pypy/module/cpyext/test/test_arraymodule.py
--- a/pypy/module/cpyext/test/test_arraymodule.py
+++ b/pypy/module/cpyext/test/test_arraymodule.py
@@ -4,15 +4,6 @@
class AppTestArrayModule(AppTestCpythonExtensionBase):
enable_leak_checking = True
- def setup_class(cls):
- from rpython.tool.udir import udir
- AppTestCpythonExtensionBase.setup_class.im_func(cls)
- if option.runappdirect:
- cls.w_udir = str(udir)
- else:
- cls.w_udir = cls.space.wrap(str(udir))
-
-
def test_basic(self):
module = self.import_module(name='array')
arr = module.array('i', [1,2,3])
@@ -105,30 +96,28 @@
# Not really part of array, refactor
import struct
module = self.import_module(name='array')
- val = module.readbuffer_as_string('abcd')
- assert val == 'abcd'
- val = module.readbuffer_as_string(u'\u03a3')
- assert val is not None
+ val = module.readbuffer_as_string(b'abcd')
+ assert val == b'abcd'
def test_readinto(self):
module = self.import_module(name='array')
- a = module.array('c')
- a.fromstring('0123456789')
+ a = module.array('B')
+ a.fromstring(b'0123456789')
filename = self.udir + "/_test_file"
f = open(filename, 'w+b')
- f.write('foobar')
+ f.write(b'foobar')
f.seek(0)
n = f.readinto(a)
f.close()
assert n == 6
assert len(a) == 10
- assert a.tostring() == 'foobar6789'
+ assert a.tostring() == b'foobar6789'
def test_iowrite(self):
module = self.import_module(name='array')
from io import BytesIO
- a = module.array('c')
- a.fromstring('0123456789')
+ a = module.array('B')
+ a.fromstring(b'0123456789')
fd = BytesIO()
# only test that it works
fd.write(a)
diff --git a/pypy/module/cpyext/test/test_dictobject.py
b/pypy/module/cpyext/test/test_dictobject.py
--- a/pypy/module/cpyext/test/test_dictobject.py
+++ b/pypy/module/cpyext/test/test_dictobject.py
@@ -103,6 +103,17 @@
api.PyDict_Update(w_d, w_d2)
assert space.unwrap(w_d) == dict(a='b', c='d', e='f')
+ def test_update_doesnt_accept_list_of_tuples(self, space, api):
+ w_d = space.newdict()
+ space.setitem(w_d, space.wrap("a"), space.wrap("b"))
+
+ w_d2 = space.wrap([("c", "d"), ("e", "f")])
+
+ api.PyDict_Update(w_d, w_d2)
+ assert api.PyErr_Occurred() is space.w_AttributeError
+ api.PyErr_Clear()
+ assert space.unwrap(w_d) == dict(a='b') # unchanged
+
def test_iter(self, space, api):
w_dict = space.sys.getdict(space)
py_dict = make_ref(space, w_dict)
@@ -203,3 +214,18 @@
"""),
])
assert module.dict_proxy({'a': 1, 'b': 2}) == 2
+
+ def test_update(self):
+ module = self.import_extension('foo', [
+ ("update", "METH_VARARGS",
+ '''
+ if (PyDict_Update(PyTuple_GetItem(args, 0), PyTuple_GetItem(args,
1)))
+ return NULL;
+ Py_RETURN_NONE;
+ ''')])
+ d = {"a": 1}
+ module.update(d, {"c": 2})
+ assert d == dict(a=1, c=2)
+ d = {"a": 1}
+ raises(AttributeError, module.update, d, [("c", 2)])
+
diff --git a/pypy/module/cpyext/test/test_module.py
b/pypy/module/cpyext/test/test_module.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/test_module.py
@@ -0,0 +1,12 @@
+from pypy.module.cpyext.test.test_api import BaseApiTest
+from rpython.rtyper.lltypesystem import rffi
+
+
+class TestModuleObject(BaseApiTest):
+ def test_module_getname(self, space, api):
+ w_sys = space.wrap(space.sys)
+ p = api.PyModule_GetName(w_sys)
+ assert rffi.charp2str(p) == 'sys'
+ p2 = api.PyModule_GetName(w_sys)
+ assert p2 == p
+ self.raises(space, api, SystemError, api.PyModule_GetName,
space.w_True)
diff --git a/pypy/module/cpyext/test/test_object.py
b/pypy/module/cpyext/test/test_object.py
--- a/pypy/module/cpyext/test/test_object.py
+++ b/pypy/module/cpyext/test/test_object.py
@@ -285,6 +285,16 @@
assert isinstance(dict(), collections.Mapping)
assert module.ismapping(dict())
+ def test_format_returns_unicode(self):
+ module = self.import_extension('foo', [
+ ("empty_format", "METH_O",
+ """
+ PyObject* empty_unicode = PyUnicode_FromStringAndSize("", 0);
+ PyObject* obj = PyObject_Format(args, empty_unicode);
+ return obj;
+ """)])
+ a = module.empty_format('hello')
+ assert isinstance(a, unicode)
class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase):
"""
diff --git a/pypy/module/cpyext/test/test_pyerrors.py
b/pypy/module/cpyext/test/test_pyerrors.py
--- a/pypy/module/cpyext/test/test_pyerrors.py
+++ b/pypy/module/cpyext/test/test_pyerrors.py
@@ -106,7 +106,7 @@
class AppTestFetch(AppTestCpythonExtensionBase):
def setup_class(cls):
- from pypy.module.imp.test.support import get_special_char
+ from pypy.interpreter.test.test_fsencode import get_special_char
space = cls.space
cls.special_char = get_special_char()
cls.w_special_char = space.wrap(cls.special_char)
@@ -380,7 +380,7 @@
"XXX seems to pass, but doesn't: 'py.test -s' shows errors in
PyObject_Free")
def test_GetSetExcInfo(self):
import sys
- if self.runappdirect and (sys.version_info.major < 3 or
+ if self.runappdirect and (sys.version_info.major < 3 or
sys.version_info.minor < 3):
skip('PyErr_{GS}etExcInfo introduced in python 3.3')
module = self.import_extension('foo', [
diff --git a/pypy/module/cpyext/test/test_typeobject.py
b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -306,12 +306,34 @@
PyHeapTypeObject *heaptype = (PyHeapTypeObject *)args;
Py_INCREF(heaptype->ht_name);
return heaptype->ht_name;
+ '''),
+ ("setattr", "METH_O",
'''
- )
+ int ret;
+ PyObject* name = PyString_FromString("mymodule");
+ PyObject *obj = PyType_Type.tp_alloc(&PyType_Type, 0);
+ PyHeapTypeObject *type = (PyHeapTypeObject*)obj;
+ if ((type->ht_type.tp_flags & Py_TPFLAGS_HEAPTYPE) == 0)
+ {
+ PyErr_SetString(PyExc_ValueError,
+ "Py_TPFLAGS_HEAPTYPE not set");
+ return NULL;
+ }
+ type->ht_type.tp_name = ((PyTypeObject*)args)->tp_name;
+ PyType_Ready(&type->ht_type);
+ ret = PyObject_SetAttrString((PyObject*)&type->ht_type,
+ "__module__", name);
+ Py_DECREF(name);
+ if (ret < 0)
+ return NULL;
+ return PyLong_FromLong(ret);
+ '''),
])
class C(object):
pass
assert module.name_by_heaptype(C) == "C"
+ assert module.setattr(C) == 0
+
def test_type_dict(self):
foo = self.import_module("foo")
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -467,7 +467,7 @@
W_TypeObject.__init__(self, space, name,
bases_w or [space.w_object], dict_w, force_new_layout=new_layout)
self.flag_cpytype = True
- self.flag_heaptype = False
+ self.flag_heaptype = pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE
# if a sequence or a mapping, then set the flag to force it
if pto.c_tp_as_sequence and pto.c_tp_as_sequence.c_sq_item:
self.flag_map_or_seq = 'S'
@@ -725,14 +725,14 @@
w_obj = space.allocate_instance(W_PyCTypeObject, w_metatype)
track_reference(space, py_obj, w_obj)
# __init__ wraps all slotdefs functions from py_type via add_operators
- w_obj.__init__(space, py_type)
+ w_obj.__init__(space, py_type)
w_obj.ready()
finish_type_2(space, py_type, w_obj)
base = py_type.c_tp_base
if base:
# XXX refactor - parts of this are done in finish_type_2 ->
inherit_slots
- if not py_type.c_tp_as_number:
+ if not py_type.c_tp_as_number:
py_type.c_tp_as_number = base.c_tp_as_number
py_type.c_tp_flags |= base.c_tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS
if not py_type.c_tp_as_sequence:
diff --git a/pypy/module/faulthandler/test/test_ztranslation.py
b/pypy/module/faulthandler/test/test_ztranslation.py
--- a/pypy/module/faulthandler/test/test_ztranslation.py
+++ b/pypy/module/faulthandler/test/test_ztranslation.py
@@ -1,4 +1,5 @@
from pypy.objspace.fake.checkmodule import checkmodule
def test_faulthandler_translates():
+ import pypy.module._vmprof.interp_vmprof # register_code_object_class()
checkmodule('faulthandler')
diff --git a/pypy/module/imp/test/test_import.py
b/pypy/module/imp/test/test_import.py
--- a/pypy/module/imp/test/test_import.py
+++ b/pypy/module/imp/test/test_import.py
@@ -75,6 +75,9 @@
abs_sys = "import sys",
struct = "inpackage = 1",
errno = "",
+ # Python 3 note: this __future__ has no effect any more,
+ # kept around for testing and to avoid increasing the diff
+ # with PyPy2
absolute = "from __future__ import absolute_import\nimport
struct",
relative_b = "from __future__ import absolute_import\nfrom .
import struct",
relative_c = "from __future__ import absolute_import\nfrom
.struct import inpackage",
@@ -597,7 +600,7 @@
imp.reload(sys)
assert sys.path is oldpath
- assert 'settrace' in dir(sys)
+ assert 'settrace' not in dir(sys) # at least on CPython 3.5.2
def test_reload_builtin_doesnt_clear(self):
import imp
@@ -657,16 +660,15 @@
assert False, 'should not work'
def test_cache_from_source(self):
- import imp
+ import imp, sys
+ tag = sys.implementation.cache_tag
pycfile = imp.cache_from_source('a/b/c.py')
- assert pycfile.startswith('a/b/__pycache__/c.pypy3-')
- assert pycfile.endswith('.pyc')
- assert imp.source_from_cache('a/b/__pycache__/c.pypy3-17.pyc'
+ assert pycfile == 'a/b/__pycache__/c.%s.pyc' % tag
+ assert imp.source_from_cache('a/b/__pycache__/c.%s.pyc' % tag
) == 'a/b/c.py'
raises(ValueError, imp.source_from_cache, 'a/b/c.py')
def test_invalid_pathname(self):
- skip("This test fails on CPython 3.3, but passes on CPython 3.4+")
import imp
import pkg
import os
@@ -906,6 +908,10 @@
stream.close()
def test_pyc_magic_changes(self):
+ # skipped: for now, PyPy generates only one kind of .pyc file
+ # per version. Different versions should differ in
+ # sys.implementation.cache_tag, which means that they'll look up
+ # different .pyc files anyway. See test_get_tag() in test_app.py.
py.test.skip("For now, PyPy generates only one kind of .pyc files")
# test that the pyc files produced by a space are not reimportable
# from another, if they differ in what opcodes they support
@@ -1009,7 +1015,9 @@
sys.meta_path.insert(0, Importer())
try:
import math
- assert len(tried_imports) == 1
+ # the above line may trigger extra imports, like _operator
+ # from app_math.py. The first one should be 'math'.
+ assert len(tried_imports) >= 1
package_name = '.'.join(__name__.split('.')[:-1])
if package_name:
assert tried_imports[0][0] == package_name + ".math"
diff --git a/pypy/module/pypyjit/test_pypy_c/test_containers.py
b/pypy/module/pypyjit/test_pypy_c/test_containers.py
--- a/pypy/module/pypyjit/test_pypy_c/test_containers.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_containers.py
@@ -67,7 +67,8 @@
p10 = call_r(ConstClass(ll_str__IntegerR_SignedConst_Signed), i5,
descr=<Callr . i EF=3>)
guard_no_exception(descr=...)
guard_nonnull(p10, descr=...)
- i12 = call_i(ConstClass(_ll_strhash__rpy_stringPtr), p10,
descr=<Calli . r EF=0>)
+ i99 = strhash(p10)
+ i12 = cond_call_value_i(i99,
ConstClass(_ll_strhash__rpy_stringPtr), p10, descr=<Calli . r EF=2>)
p13 = new(descr=...)
p15 = new_array_clear(16, descr=<ArrayU 1>)
{{{
@@ -86,6 +87,7 @@
call_n(ConstClass(_ll_dict_setitem_lookup_done_trampoline), p13,
p10, p20, i12, i17, descr=<Callv 0 rrrii EF=5>)
setfield_gc(p20, i5, descr=<FieldS .*W_IntObject.inst_intval .*
pure>)
guard_no_exception(descr=...)
+ i98 = strhash(p10)
i23 = call_i(ConstClass(ll_call_lookup_function), p13, p10, i12,
0, descr=<Calli . rrii EF=5 OS=4>)
guard_no_exception(descr=...)
i27 = int_lt(i23, 0)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py
b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py
--- a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py
@@ -16,6 +16,7 @@
return not detect_simd_z()
return True
[email protected](True, reason='no _numpypy on pypy3')
class TestMicroNumPy(BaseTestPyPyC):
arith_comb = [('+','float','float', 4*3427, 3427, 1.0,3.0),
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,9 +47,9 @@
res = 0
a = 0
while a < 300:
- res1 = a/b # ID: div
- res2 = a/2 # ID: shift
- res3 = a/11 # ID: mul
+ res1 = a // b # ID: div
+ res2 = a // 2 # ID: shift
+ res3 = a // 11 # ID: mul
res += res1 + res2 + res3
a += 1
return res
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -11,58 +11,48 @@
@specialize.memo()
def object_getattribute(space):
"Utility that returns the app-level descriptor object.__getattribute__."
- w_src, w_getattribute = space.lookup_in_type_where(space.w_object,
- '__getattribute__')
- return w_getattribute
+ return space.lookup_in_type(space.w_object, '__getattribute__')
@specialize.memo()
def object_setattr(space):
"Utility that returns the app-level descriptor object.__setattr__."
- w_src, w_setattr = space.lookup_in_type_where(space.w_object,
- '__setattr__')
- return w_setattr
+ return space.lookup_in_type(space.w_object, '__setattr__')
@specialize.memo()
def object_delattr(space):
"Utility that returns the app-level descriptor object.__delattr__."
- w_src, w_delattr = space.lookup_in_type_where(space.w_object,
- '__delattr__')
- return w_delattr
+ return space.lookup_in_type(space.w_object, '__delattr__')
@specialize.memo()
def object_hash(space):
"Utility that returns the app-level descriptor object.__hash__."
- w_src, w_hash = space.lookup_in_type_where(space.w_object,
- '__hash__')
- return w_hash
+ return space.lookup_in_type(space.w_object, '__hash__')
@specialize.memo()
def type_eq(space):
"Utility that returns the app-level descriptor type.__eq__."
- w_src, w_eq = space.lookup_in_type_where(space.w_type,
- '__eq__')
- return w_eq
+ return space.lookup_in_type(space.w_type, '__eq__')
@specialize.memo()
def list_iter(space):
"Utility that returns the app-level descriptor list.__iter__."
- w_src, w_iter = space.lookup_in_type_where(space.w_list,
- '__iter__')
- return w_iter
+ return space.lookup_in_type(space.w_list, '__iter__')
@specialize.memo()
def tuple_iter(space):
"Utility that returns the app-level descriptor tuple.__iter__."
- w_src, w_iter = space.lookup_in_type_where(space.w_tuple,
- '__iter__')
- return w_iter
+ return space.lookup_in_type(space.w_tuple, '__iter__')
@specialize.memo()
def unicode_iter(space):
"Utility that returns the app-level descriptor str.__iter__."
- w_src, w_iter = space.lookup_in_type_where(space.w_unicode,
- '__iter__')
- return w_iter
+ return space.lookup_in_type(space.w_unicode, '__iter__')
+
[email protected]()
+def dict_getitem(space):
+ "Utility that returns the app-level descriptor dict.__getitem__."
+ return space.lookup_in_type(space.w_dict, '__getitem__')
+
def raiseattrerror(space, w_obj, w_name, w_descr=None):
# space.repr always returns an encodable string.
diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py
--- a/pypy/objspace/fake/objspace.py
+++ b/pypy/objspace/fake/objspace.py
@@ -339,12 +339,18 @@
def unicode_from_object(self, w_obj):
return w_some_obj()
+ def encode_unicode_object(self, w_unicode, encoding, errors):
+ return w_some_obj()
+
def _try_fetch_pycode(self, w_func):
return None
def is_generator(self, w_obj):
return NonConstant(False)
+ def lookup_in_type(self, w_type, name):
+ return w_some_obj()
+
# ----------
def translates(self, func=None, argtypes=None, seeobj_w=[], **kwds):
diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py
--- a/pypy/objspace/std/objspace.py
+++ b/pypy/objspace/std/objspace.py
@@ -374,6 +374,10 @@
return w_type.lookup(name)
lookup._annspecialcase_ = 'specialize:lookup'
+ def lookup_in_type(self, w_type, name):
+ w_src, w_descr = self.lookup_in_type_where(w_type, name)
+ return w_descr
+
def lookup_in_type_where(self, w_type, name):
return w_type.lookup_where(name)
lookup_in_type_where._annspecialcase_ = 'specialize:lookup_in_type_where'
@@ -671,6 +675,10 @@
from pypy.objspace.std.unicodeobject import unicode_from_object
return unicode_from_object(self, w_obj)
+ def encode_unicode_object(self, w_unicode, encoding, errors):
+ from pypy.objspace.std.unicodeobject import encode_object
+ return encode_object(self, w_unicode, encoding, errors)
+
def call_method(self, w_obj, methname, *arg_w):
return callmethod.call_method_opt(self, w_obj, methname, *arg_w)
@@ -700,4 +708,4 @@
@specialize.arg(2, 3)
def is_overloaded(self, w_obj, tp, method):
return (self.lookup(w_obj, method) is not
- self.lookup_in_type_where(tp, method)[1])
+ self.lookup_in_type(tp, method))
diff --git a/pypy/objspace/std/unicodeobject.py
b/pypy/objspace/std/unicodeobject.py
--- a/pypy/objspace/std/unicodeobject.py
+++ b/pypy/objspace/std/unicodeobject.py
@@ -79,9 +79,10 @@
def identifier_w(self, space):
try:
- # call the elidable function, with a jit.call_shortcut in case
- # self._utf8 is already computed
- identifier = g_identifier_w(self, space)
+ identifier = jit.conditional_call_elidable(
+ self._utf8, g_encode_utf8, self._value)
+ if not jit.isconstant(self):
+ self._utf8 = identifier
except UnicodeEncodeError:
# bah, this is just to get an official app-level
# UnicodeEncodeError
@@ -1273,15 +1274,9 @@
allow_surrogates=allow_surrogates)
@jit.elidable
[email protected]_shortcut
-def g_identifier_w(self, space):
- """This is a global function because of @jit.call_shortcut"""
- identifier = self._utf8
- if identifier is not None:
- return identifier
- identifier = self._value.encode('utf-8')
- self._utf8 = identifier
- return identifier
+def g_encode_utf8(value):
+ """This is a global function because of jit.conditional_call_value"""
+ return value.encode('utf-8')
_repr_function, _ = make_unicode_escape_function(
pass_printable=True, unicode_output=True, quotes=True, prefix='')
diff --git a/rpython/annotator/annrpython.py b/rpython/annotator/annrpython.py
--- a/rpython/annotator/annrpython.py
+++ b/rpython/annotator/annrpython.py
@@ -22,7 +22,8 @@
"""Block annotator for RPython.
See description in doc/translation.txt."""
- def __init__(self, translator=None, policy=None, bookkeeper=None):
+ def __init__(self, translator=None, policy=None, bookkeeper=None,
+ keepgoing=False):
import rpython.rtyper.extfuncregistry # has side effects
if translator is None:
@@ -50,6 +51,9 @@
if bookkeeper is None:
bookkeeper = Bookkeeper(self)
self.bookkeeper = bookkeeper
+ self.keepgoing = keepgoing
+ self.failed_blocks = set()
+ self.errors = []
def __getstate__(self):
attrs = """translator pendingblocks annotated links_followed
@@ -202,6 +206,12 @@
else:
newgraphs = self.translator.graphs #all of them
got_blocked_blocks = False in self.annotated.values()
+ if self.failed_blocks:
+ text = ('Annotation failed, %s errors were recorded:' %
+ len(self.errors))
+ text += '\n-----'.join(str(e) for e in self.errors)
+ raise annmodel.AnnotatorError(text)
+
if got_blocked_blocks:
for graph in self.blocked_graphs.values():
self.blocked_graphs[graph] = True
@@ -348,6 +358,8 @@
#print '* processblock', block, cells
self.annotated[block] = graph
+ if block in self.failed_blocks:
+ return
if block in self.blocked_blocks:
del self.blocked_blocks[block]
try:
@@ -392,6 +404,10 @@
except annmodel.UnionError as e:
# Add source code to the UnionError
e.source = '\n'.join(source_lines(graph, block, None, long=True))
+ if self.keepgoing:
+ self.errors.append(e)
+ self.failed_blocks.add(block)
+ return
raise
# if the merged cells changed, we must redo the analysis
if unions != oldcells:
@@ -482,6 +498,10 @@
except annmodel.AnnotatorError as e: # note that UnionError is a
subclass
e.source = gather_error(self, graph, block, i)
+ if self.keepgoing:
+ self.errors.append(e)
+ self.failed_blocks.add(block)
+ return
raise
else:
diff --git a/rpython/annotator/test/test_model.py
b/rpython/annotator/test/test_model.py
--- a/rpython/annotator/test/test_model.py
+++ b/rpython/annotator/test/test_model.py
@@ -192,6 +192,20 @@
assert union(union(s1, s2), s3) == union(s1, union(s2, s3))
[email protected]
+@given(st_annotation, st_annotation)
+def test_generalize_isinstance(annotator, s1, s2):
+ try:
+ s_12 = union(s1, s2)
+ except UnionError:
+ assume(False)
+ assume(s1 != s_ImpossibleValue)
+ from rpython.annotator.unaryop import s_isinstance
+ s_int = annotator.bookkeeper.immutablevalue(int)
+ s_res_12 = s_isinstance(annotator, s_12, s_int, [])
+ s_res_1 = s_isinstance(annotator, s1, s_int, [])
+ assert s_res_12.contains(s_res_1)
+
def compile_function(function, annotation=[]):
t = TranslationContext()
t.buildannotator().build_types(function, annotation)
diff --git a/rpython/config/translationoption.py
b/rpython/config/translationoption.py
--- a/rpython/config/translationoption.py
+++ b/rpython/config/translationoption.py
@@ -188,6 +188,10 @@
"When true, enable the use of tagged pointers. "
"If false, use normal boxing",
default=False),
+ BoolOption("keepgoing",
+ "Continue annotating when errors are encountered, and report "
+ "them all at the end of the annotation phase",
+ default=False, cmdline="--keepgoing"),
BoolOption("lldebug",
"If true, makes an lldebug build", default=False,
cmdline="--lldebug"),
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
@@ -268,12 +268,15 @@
"""
mc = InstrBuilder(self.cpu.cpuinfo.arch_version)
#
- self._push_all_regs_to_jitframe(mc, [], self.cpu.supports_floats,
callee_only)
+ # We don't save/restore r4; instead the return value (if any)
+ # will be stored there.
+ self._push_all_regs_to_jitframe(mc, [r.r4], self.cpu.supports_floats,
callee_only)
## args are in their respective positions
mc.PUSH([r.ip.value, r.lr.value])
mc.BLX(r.r4.value)
+ mc.MOV_rr(r.r4.value, r.r0.value)
self._reload_frame_if_necessary(mc)
- self._pop_all_regs_from_jitframe(mc, [], supports_floats,
+ self._pop_all_regs_from_jitframe(mc, [r.r4], supports_floats,
callee_only)
# return
mc.POP([r.ip.value, r.pc.value])
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
@@ -357,7 +357,13 @@
return fcond
def emit_op_cond_call(self, op, arglocs, regalloc, fcond):
- [call_loc] = arglocs
+ call_loc = arglocs[0]
+ if len(arglocs) == 2:
+ res_loc = arglocs[1] # cond_call_value
+ else:
+ res_loc = None # cond_call
+ # useless to list res_loc in the gcmap, because if the call is
+ # done it means res_loc was initially NULL
gcmap = regalloc.get_gcmap([call_loc])
assert call_loc is r.r4
@@ -378,8 +384,13 @@
floats = True
cond_call_adr = self.cond_call_slowpath[floats * 2 + callee_only]
self.mc.BL(cond_call_adr)
+ # if this is a COND_CALL_VALUE, we need to move the result in place
+ # from its current location (which is, unusually, in r4: see
+ # cond_call_slowpath)
+ if res_loc is not None and res_loc is not r.r4:
+ self.mc.MOV_rr(res_loc.value, r.r4.value)
+ #
self.pop_gcmap(self.mc)
- # never any result value
cond = c.get_opposite_of(self.guard_success_cc)
self.guard_success_cc = c.cond_none
pmc = OverwritingBuilder(self.mc, jmp_adr, WORD)
@@ -389,6 +400,9 @@
self.previous_cond_call_jcond = jmp_adr, cond
return fcond
+ emit_op_cond_call_value_i = emit_op_cond_call
+ emit_op_cond_call_value_r = emit_op_cond_call
+
def emit_op_jump(self, op, arglocs, regalloc, fcond):
target_token = op.getdescr()
assert isinstance(target_token, TargetToken)
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
@@ -1004,7 +1004,6 @@
def prepare_op_cond_call(self, op, fcond):
# XXX don't force the arguments to be loaded in specific
# locations before knowing if we can take the fast path
- # XXX add cond_call_value support
assert 2 <= op.numargs() <= 4 + 2
tmpreg = self.get_scratch_reg(INT, selected_reg=r.r4)
v = op.getarg(1)
@@ -1017,8 +1016,33 @@
arg = op.getarg(i)
self.make_sure_var_in_reg(arg, args_so_far, selected_reg=reg)
args_so_far.append(arg)
- self.load_condition_into_cc(op.getarg(0))
- return [tmpreg]
+
+ if op.type == 'v':
+ # a plain COND_CALL. Calls the function when args[0] is
+ # true. Often used just after a comparison operation.
+ self.load_condition_into_cc(op.getarg(0))
+ return [tmpreg]
+ else:
+ # COND_CALL_VALUE_I/R. Calls the function when args[0]
+ # is equal to 0 or NULL. Returns the result from the
+ # function call if done, or args[0] if it was not 0/NULL.
+ # Implemented by forcing the result to live in the same
+ # register as args[0], and overwriting it if we really do
+ # the call.
+
+ # Load the register for the result. Possibly reuse 'args[0]'.
+ # But the old value of args[0], if it survives, is first
+ # spilled away. We can't overwrite any of op.args[2:] here.
+ args = op.getarglist()
+ resloc = self.rm.force_result_in_reg(op, args[0],
+ forbidden_vars=args[2:])
+ # Test the register for the result.
+ self.assembler.mc.CMP_ri(resloc.value, 0)
+ self.assembler.guard_success_cc = c.EQ
+ return [tmpreg, resloc]
+
+ prepare_op_cond_call_value_i = prepare_op_cond_call
+ prepare_op_cond_call_value_r = prepare_op_cond_call
def prepare_op_force_token(self, op, fcond):
# XXX for now we return a regular reg
diff --git a/rpython/jit/backend/llgraph/runner.py
b/rpython/jit/backend/llgraph/runner.py
--- a/rpython/jit/backend/llgraph/runner.py
+++ b/rpython/jit/backend/llgraph/runner.py
@@ -15,11 +15,12 @@
from rpython.rtyper.llinterp import LLInterpreter, LLException
from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr
from rpython.rtyper.lltypesystem.lloperation import llop
+from rpython.rtyper.annlowlevel import hlstr, hlunicode
from rpython.rtyper import rclass
from rpython.rlib.clibffi import FFI_DEFAULT_ABI
from rpython.rlib.rarithmetic import ovfcheck, r_uint, r_ulonglong, intmask
-from rpython.rlib.objectmodel import Symbolic
+from rpython.rlib.objectmodel import Symbolic, compute_hash
class LLAsmInfo(object):
def __init__(self, lltrace):
@@ -326,7 +327,6 @@
supports_longlong = r_uint is not r_ulonglong
supports_singlefloats = True
supports_guard_gc_type = True
- supports_cond_call_value = True
translate_support_code = False
is_llgraph = True
vector_ext = VectorExt()
@@ -789,6 +789,10 @@
assert 0 <= dststart <= dststart + length <= len(dst.chars)
rstr.copy_string_contents(src, dst, srcstart, dststart, length)
+ def bh_strhash(self, s):
+ lls = s._obj.container
+ return compute_hash(hlstr(lls._as_ptr()))
+
def bh_newunicode(self, length):
return lltype.cast_opaque_ptr(llmemory.GCREF,
lltype.malloc(rstr.UNICODE, length,
@@ -811,6 +815,10 @@
assert 0 <= dststart <= dststart + length <= len(dst.chars)
rstr.copy_unicode_contents(src, dst, srcstart, dststart, length)
+ def bh_unicodehash(self, s):
+ lls = s._obj.container
+ return compute_hash(hlunicode(lls._as_ptr()))
+
def bh_new(self, sizedescr):
return lltype.cast_opaque_ptr(llmemory.GCREF,
lltype.malloc(sizedescr.S, zero=True))
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
@@ -3,8 +3,9 @@
from rpython.rtyper.lltypesystem.lloperation import llop
from rpython.rtyper.llinterp import LLInterpreter
from rpython.rtyper.annlowlevel import llhelper, MixLevelHelperAnnotator
+from rpython.rtyper.annlowlevel import hlstr, hlunicode
from rpython.rtyper.llannotation import lltype_to_annotation
-from rpython.rlib.objectmodel import we_are_translated, specialize
+from rpython.rlib.objectmodel import we_are_translated, specialize,
compute_hash
from rpython.jit.metainterp import history, compile
from rpython.jit.metainterp.optimize import SpeculativeError
from rpython.jit.codewriter import heaptracker, longlong
@@ -663,6 +664,14 @@
u = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), string)
return len(u.chars)
+ def bh_strhash(self, string):
+ s = lltype.cast_opaque_ptr(lltype.Ptr(rstr.STR), string)
+ return compute_hash(hlstr(s))
+
+ def bh_unicodehash(self, string):
+ u = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), string)
+ return compute_hash(hlunicode(u))
+
def bh_strgetitem(self, string, index):
s = lltype.cast_opaque_ptr(lltype.Ptr(rstr.STR), string)
return ord(s.chars[index])
diff --git a/rpython/jit/backend/llsupport/rewrite.py
b/rpython/jit/backend/llsupport/rewrite.py
--- a/rpython/jit/backend/llsupport/rewrite.py
+++ b/rpython/jit/backend/llsupport/rewrite.py
@@ -9,9 +9,9 @@
from rpython.jit.metainterp.typesystem import rd_eq, rd_hash
from rpython.jit.codewriter import heaptracker
from rpython.jit.backend.llsupport.symbolic import (WORD,
- get_array_token)
+ get_field_token, get_array_token)
from rpython.jit.backend.llsupport.descr import SizeDescr, ArrayDescr,\
- FLAG_POINTER, CallDescr
+ FLAG_POINTER
from rpython.jit.metainterp.history import JitCellToken
from rpython.jit.backend.llsupport.descr import (unpack_arraydescr,
unpack_fielddescr, unpack_interiorfielddescr)
@@ -262,6 +262,18 @@
self.cpu.translate_support_code)
self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0),
WORD, 1, ofs_length, NOT_SIGNED)
+ elif opnum == rop.STRHASH:
+ offset, size = get_field_token(rstr.STR,
+ 'hash',
self.cpu.translate_support_code)
+ assert size == WORD
+ self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0),
+ WORD, 1, offset, sign=True)
+ elif opnum == rop.UNICODEHASH:
+ offset, size = get_field_token(rstr.UNICODE,
+ 'hash',
self.cpu.translate_support_code)
+ assert size == WORD
+ self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0),
+ WORD, 1, offset, sign=True)
elif opnum == rop.STRGETITEM:
basesize, itemsize, ofs_length = get_array_token(rstr.STR,
self.cpu.translate_support_code)
@@ -347,9 +359,7 @@
self.consider_setfield_gc(op)
elif op.getopnum() == rop.SETARRAYITEM_GC:
self.consider_setarrayitem_gc(op)
- # ---------- calls -----------
- if OpHelpers.is_plain_call(op.getopnum()):
- self.expand_call_shortcut(op)
+ # ---------- call assembler -----------
if OpHelpers.is_call_assembler(op.getopnum()):
self.handle_call_assembler(op)
continue
@@ -595,33 +605,6 @@
self.emit_gc_store_or_indexed(None, ptr, ConstInt(0), value,
size, 1, ofs)
- def expand_call_shortcut(self, op):
- if not self.cpu.supports_cond_call_value:
- return
- descr = op.getdescr()
- if descr is None:
- return
- assert isinstance(descr, CallDescr)
- effectinfo = descr.get_extra_info()
- if effectinfo is None or effectinfo.call_shortcut is None:
- return
- if op.type == 'r':
- cond_call_opnum = rop.COND_CALL_VALUE_R
- elif op.type == 'i':
- cond_call_opnum = rop.COND_CALL_VALUE_I
- else:
- return
- cs = effectinfo.call_shortcut
- ptr_box = op.getarg(1 + cs.argnum)
- if cs.fielddescr is not None:
- value_box = self.emit_getfield(ptr_box, descr=cs.fielddescr,
- raw=(ptr_box.type == 'i'))
- else:
- value_box = ptr_box
- self.replace_op_with(op, ResOperation(cond_call_opnum,
- [value_box] + op.getarglist(),
- descr=descr))
-
def handle_call_assembler(self, op):
descrs = self.gc_ll_descr.getframedescrs(self.cpu)
loop_token = op.getdescr()
diff --git a/rpython/jit/backend/llsupport/test/test_rewrite.py
b/rpython/jit/backend/llsupport/test/test_rewrite.py
--- a/rpython/jit/backend/llsupport/test/test_rewrite.py
+++ b/rpython/jit/backend/llsupport/test/test_rewrite.py
@@ -1,8 +1,7 @@
import py
from rpython.jit.backend.llsupport.descr import get_size_descr,\
get_field_descr, get_array_descr, ArrayDescr, FieldDescr,\
- SizeDescr, get_interiorfield_descr, get_call_descr
-from rpython.jit.codewriter.effectinfo import EffectInfo, CallShortcut
+ SizeDescr, get_interiorfield_descr
from rpython.jit.backend.llsupport.gc import GcLLDescr_boehm,\
GcLLDescr_framework
from rpython.jit.backend.llsupport import jitframe
@@ -81,21 +80,6 @@
lltype.malloc(T, zero=True))
self.myT = myT
#
- call_shortcut = CallShortcut(0, tzdescr)
- effectinfo = EffectInfo(None, None, None, None, None, None,
- EffectInfo.EF_RANDOM_EFFECTS,
- call_shortcut=call_shortcut)
- call_shortcut_descr = get_call_descr(self.gc_ll_descr,
- [lltype.Ptr(T)], lltype.Signed,
- effectinfo)
- call_shortcut_2 = CallShortcut(0, None)
- effectinfo_2 = EffectInfo(None, None, None, None, None, None,
- EffectInfo.EF_RANDOM_EFFECTS,
- call_shortcut=call_shortcut_2)
- call_shortcut_descr_2 = get_call_descr(self.gc_ll_descr,
- [lltype.Signed], lltype.Signed,
- effectinfo_2)
- #
A = lltype.GcArray(lltype.Signed)
adescr = get_array_descr(self.gc_ll_descr, A)
adescr.tid = 4321
@@ -216,7 +200,6 @@
load_constant_offset = True
load_supported_factors = (1,2,4,8)
- supports_cond_call_value = True
translate_support_code = None
@@ -1239,6 +1222,10 @@
'i3 = gc_load_i(p0,'
'%(strlendescr.offset)s,'
'%(strlendescr.field_size)s)'],
+ [True, (1,), 'i3 = strhash(p0)' '->'
+ 'i3 = gc_load_i(p0,'
+ '%(strhashdescr.offset)s,'
+ '-%(strhashdescr.field_size)s)'],
#[False, (1,), 'i3 = unicodelen(p0)' '->'
# 'i3 = gc_load_i(p0,'
# '%(unicodelendescr.offset)s,'
@@ -1247,7 +1234,10 @@
'i3 = gc_load_i(p0,'
'%(unicodelendescr.offset)s,'
'%(unicodelendescr.field_size)s)'],
-
+ [True, (1,), 'i3 = unicodehash(p0)' '->'
+ 'i3 = gc_load_i(p0,'
+ '%(unicodehashdescr.offset)s,'
+ '-%(unicodehashdescr.field_size)s)'],
## getitem str/unicode
[True, (2,4), 'i3 = unicodegetitem(p0,i1)' '->'
'i3 = gc_load_indexed_i(p0,i1,'
@@ -1446,26 +1436,3 @@
jump()
""")
assert len(self.gcrefs) == 2
-
- def test_handle_call_shortcut(self):
- self.check_rewrite("""
- [p0]
- i1 = call_i(123, p0, descr=call_shortcut_descr)
- jump(i1)
- """, """
- [p0]
- i2 = gc_load_i(p0, %(tzdescr.offset)s, %(tzdescr.field_size)s)
- i1 = cond_call_value_i(i2, 123, p0, descr=call_shortcut_descr)
- jump(i1)
- """)
-
- def test_handle_call_shortcut_2(self):
- self.check_rewrite("""
- [i0]
- i1 = call_i(123, i0, descr=call_shortcut_descr_2)
- jump(i1)
- """, """
- [i0]
- i1 = cond_call_value_i(i0, 123, i0, descr=call_shortcut_descr_2)
- jump(i1)
- """)
diff --git a/rpython/jit/backend/model.py b/rpython/jit/backend/model.py
--- a/rpython/jit/backend/model.py
+++ b/rpython/jit/backend/model.py
@@ -16,7 +16,6 @@
# Boxes and Consts are BoxFloats and ConstFloats.
supports_singlefloats = False
supports_guard_gc_type = False
- supports_cond_call_value = False
propagate_exception_descr = None
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
@@ -656,10 +656,12 @@
_COND_CALL_SAVE_REGS = [r.r3, r.r4, r.r5, r.r6, r.r12]
def emit_cond_call(self, op, arglocs, regalloc):
+ resloc = arglocs[0]
+ arglocs = arglocs[1:]
+
fcond = self.guard_success_cc
self.guard_success_cc = c.cond_none
assert fcond != c.cond_none
- fcond = c.negate(fcond)
jmp_adr = self.mc.get_relative_pos()
self.mc.trap() # patched later to a 'bc'
@@ -691,7 +693,10 @@
cond_call_adr = self.cond_call_slowpath[floats * 2 + callee_only]
self.mc.bl_abs(cond_call_adr)
# restoring the registers saved above, and doing pop_gcmap(), is left
- # to the cond_call_slowpath helper. We never have any result value.
+ # to the cond_call_slowpath helper. If we have a result, move
+ # it from r2 to its expected location.
+ if resloc is not None:
+ self.mc.mr(resloc.value, r.SCRATCH2.value)
relative_target = self.mc.currpos() - jmp_adr
pmc = OverwritingBuilder(self.mc, jmp_adr, 1)
BI, BO = c.encoding[fcond]
@@ -701,6 +706,9 @@
# guard_no_exception too
self.previous_cond_call_jcond = jmp_adr, BI, BO
+ emit_cond_call_value_i = emit_cond_call
+ emit_cond_call_value_r = emit_cond_call
+
class FieldOpAssembler(object):
diff --git a/rpython/jit/backend/ppc/ppc_assembler.py
b/rpython/jit/backend/ppc/ppc_assembler.py
--- a/rpython/jit/backend/ppc/ppc_assembler.py
+++ b/rpython/jit/backend/ppc/ppc_assembler.py
@@ -315,6 +315,7 @@
# * r2 is the gcmap
# * the old value of these regs must already be stored in the
jitframe
# * on exit, all registers are restored from the jitframe
+ # * the result of the call, if any, is moved to r2
mc = PPCBuilder()
self.mc = mc
@@ -347,7 +348,11 @@
# Finish
self._reload_frame_if_necessary(mc)
+ # Move the result, if any, to r2
+ mc.mr(r.SCRATCH2.value, r.r3.value)
+
mc.mtlr(r.RCS1.value) # restore LR
+
self._pop_core_regs_from_jitframe(mc, saved_regs)
if supports_floats:
self._pop_fp_regs_from_jitframe(mc)
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
@@ -1038,14 +1038,34 @@
def prepare_cond_call(self, op):
self.load_condition_into_cc(op.getarg(0))
- locs = []
+ self.assembler.guard_success_cc = c.negate(
+ self.assembler.guard_success_cc)
+ # ^^^ if arg0==0, we jump over the next block of code (the call)
+ locs = [None]
# support between 0 and 4 integer arguments
assert 2 <= op.numargs() <= 2 + 4
for i in range(1, op.numargs()):
loc = self.loc(op.getarg(i))
assert loc.type != FLOAT
locs.append(loc)
- return locs
+ return locs # [None, function, args...]
+
+ def prepare_cond_call_value_i(self, op):
+ x = self.ensure_reg(op.getarg(0))
+ self.load_condition_into_cc(op.getarg(0))
+ self.rm.force_allocate_reg(op, selected_reg=x) # spilled if survives
+ # ^^^ if arg0!=0, we jump over the next block of code (the call)
+ locs = [x]
+ # support between 0 and 4 integer arguments
+ assert 2 <= op.numargs() <= 2 + 4
+ for i in range(1, op.numargs()):
+ loc = self.loc(op.getarg(i))
+ assert loc.type != FLOAT
+ locs.append(loc)
+ return locs # [res, function, args...]
+
+ prepare_cond_call_value_r = prepare_cond_call_value_i
+
def notimplemented(self, op):
msg = '[PPC/regalloc] %s not implemented\n' % op.getopname()
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
@@ -2448,9 +2448,6 @@
assert called == [(67, 89)]
def test_cond_call_value(self):
- if not self.cpu.supports_cond_call_value:
- py.test.skip("missing supports_cond_call_value")
-
def func_int(*args):
called.append(args)
return len(args) * 100 + 1000
diff --git a/rpython/jit/backend/test/test_ll_random.py
b/rpython/jit/backend/test/test_ll_random.py
--- a/rpython/jit/backend/test/test_ll_random.py
+++ b/rpython/jit/backend/test/test_ll_random.py
@@ -711,11 +711,6 @@
# 6. a conditional call (for now always with no exception raised)
class CondCallOperation(BaseCallOperation):
- def filter(self, builder):
- if not builder.cpu.supports_cond_call_value and \
- self.opnum == rop.COND_CALL_VALUE_I:
- raise test_random.CannotProduceOperation
-
def produce_into(self, builder, r):
fail_subset = builder.subset_of_intvars(r)
if self.opnum == rop.COND_CALL:
diff --git a/rpython/jit/backend/test/zll_stress.py
b/rpython/jit/backend/test/zll_stress.py
--- a/rpython/jit/backend/test/zll_stress.py
+++ b/rpython/jit/backend/test/zll_stress.py
@@ -20,11 +20,6 @@
r = Random()
r.jumpahead(piece*99999999)
OPERATIONS = LLtypeOperationBuilder.OPERATIONS[:]
- if not cpu.supports_cond_call_value:
- # remove COND_CALL_VALUE_I if the cpu does not support it
- ops = LLtypeOperationBuilder.OPERATIONS
- LLtypeOperationBuilder.OPERATIONS = [op for op in ops \
- if op.opnum != rop.COND_CALL_VALUE_I]
for i in range(piece*per_piece, (piece+1)*per_piece):
print " i = %d; r.setstate(%s)" % (i, r.getstate())
check_random_function(cpu, LLtypeOperationBuilder, r, i,
total_iterations)
diff --git a/rpython/jit/backend/x86/runner.py
b/rpython/jit/backend/x86/runner.py
--- a/rpython/jit/backend/x86/runner.py
+++ b/rpython/jit/backend/x86/runner.py
@@ -16,7 +16,6 @@
debug = True
supports_floats = True
supports_singlefloats = True
- supports_cond_call_value = True
dont_keepalive_stuff = False # for tests
with_threads = False
diff --git a/rpython/jit/backend/x86/test/test_call.py
b/rpython/jit/backend/x86/test/test_call.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/backend/x86/test/test_call.py
@@ -0,0 +1,7 @@
+from rpython.jit.backend.x86.test.test_basic import Jit386Mixin
+from rpython.jit.metainterp.test import test_call
+
+class TestCall(Jit386Mixin, test_call.CallTest):
+ # for the individual tests see
+ # ====> ../../../metainterp/test/test_call.py
+ pass
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
@@ -7,10 +7,9 @@
from rpython.jit.codewriter.jitcode import JitCode
from rpython.jit.codewriter.effectinfo import (VirtualizableAnalyzer,
QuasiImmutAnalyzer, RandomEffectsAnalyzer, effectinfo_from_writeanalyze,
- EffectInfo, CallInfoCollection, CallShortcut)
+ EffectInfo, CallInfoCollection)
from rpython.rtyper.lltypesystem import lltype, llmemory
from rpython.rtyper.lltypesystem.lltype import getfunctionptr
-from rpython.flowspace.model import Constant, Variable
from rpython.rlib import rposix
from rpython.translator.backendopt.canraise import RaiseAnalyzer
from rpython.translator.backendopt.writeanalyze import ReadWriteAnalyzer
@@ -215,7 +214,6 @@
elidable = False
loopinvariant = False
call_release_gil_target = EffectInfo._NO_CALL_RELEASE_GIL_TARGET
- call_shortcut = None
if op.opname == "direct_call":
funcobj = op.args[0].value._obj
assert getattr(funcobj, 'calling_conv', 'c') == 'c', (
@@ -230,12 +228,6 @@
tgt_func, tgt_saveerr = func._call_aroundstate_target_
tgt_func = llmemory.cast_ptr_to_adr(tgt_func)
call_release_gil_target = (tgt_func, tgt_saveerr)
- if hasattr(funcobj, 'graph'):
- call_shortcut = self.find_call_shortcut(funcobj.graph)
- if getattr(func, "_call_shortcut_", False):
- assert call_shortcut is not None, (
- "%r: marked as @jit.call_shortcut but shortcut not found"
- % (func,))
elif op.opname == 'indirect_call':
# check that we're not trying to call indirectly some
# function with the special flags
@@ -250,8 +242,6 @@
error = '@jit.loop_invariant'
if hasattr(graph.func, '_call_aroundstate_target_'):
error = '_call_aroundstate_target_'
- if hasattr(graph.func, '_call_shortcut_'):
- error = '@jit.call_shortcut'
if not error:
continue
raise Exception(
@@ -308,7 +298,6 @@
self.readwrite_analyzer.analyze(op, self.seen_rw), self.cpu,
extraeffect, oopspecindex, can_invalidate, call_release_gil_target,
extradescr, self.collect_analyzer.analyze(op, self.seen_gc),
- call_shortcut,
)
#
assert effectinfo is not None
@@ -379,76 +368,3 @@
if GTYPE_fieldname in jd.greenfield_info.green_fields:
return True
return False
-
- def find_call_shortcut(self, graph):
- """Identifies graphs that start like this:
-
- def graph(x, y, z): def graph(x, y, z):
- if y.field: r = y.field
- return y.field if r: return r
- """
- block = graph.startblock
- operations = block.operations
- c_fieldname = None
- if not operations:
- v_inst = v_result = block.exitswitch
- else:
- op = operations[0]
- if len(op.args) == 0:
- return
- if op.opname != 'getfield': # check for this form:
- v_inst = op.args[0] # if y is not None;
- v_result = v_inst # return y
- else:
- operations = operations[1:]
- [v_inst, c_fieldname] = op.args
- v_result = op.result
- if not isinstance(v_inst, Variable):
- return
- if v_result.concretetype != graph.getreturnvar().concretetype:
- return
- if v_result.concretetype == lltype.Void:
- return
- argnum = i = 0
- while block.inputargs[i] is not v_inst:
- if block.inputargs[i].concretetype != lltype.Void:
- argnum += 1
- i += 1
- PSTRUCT = v_inst.concretetype
- v_check = v_result
- fastcase = True
- for op in operations:
- if (op.opname in ('int_is_true', 'ptr_nonzero', 'same_as')
- and v_check is op.args[0]):
- v_check = op.result
- elif op.opname == 'ptr_iszero' and v_check is op.args[0]:
- v_check = op.result
- fastcase = not fastcase
- elif (op.opname in ('int_eq', 'int_ne')
- and v_check is op.args[0]
- and isinstance(op.args[1], Constant)
- and op.args[1].value == 0):
- v_check = op.result
- if op.opname == 'int_eq':
- fastcase = not fastcase
- else:
- return
- if v_check.concretetype is not lltype.Bool:
- return
- if block.exitswitch is not v_check:
- return
-
- links = [link for link in block.exits if link.exitcase == fastcase]
- if len(links) != 1:
- return
- [link] = links
- if link.args != [v_result]:
- return
- if not link.target.is_final_block():
- return
-
- if c_fieldname is not None:
- fielddescr = self.cpu.fielddescrof(PSTRUCT.TO, c_fieldname.value)
- else:
- fielddescr = None
- return CallShortcut(argnum, fielddescr)
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
@@ -117,8 +117,7 @@
can_invalidate=False,
call_release_gil_target=_NO_CALL_RELEASE_GIL_TARGET,
extradescrs=None,
- can_collect=True,
- call_shortcut=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(
@@ -136,8 +135,7 @@
extraeffect,
oopspecindex,
can_invalidate,
- can_collect,
- call_shortcut)
+ can_collect)
tgt_func, tgt_saveerr = call_release_gil_target
if tgt_func:
key += (object(),) # don't care about caching in this case
@@ -192,7 +190,6 @@
result.oopspecindex = oopspecindex
result.extradescrs = extradescrs
result.call_release_gil_target = call_release_gil_target
- result.call_shortcut = call_shortcut
if result.check_can_raise(ignore_memoryerror=True):
assert oopspecindex in cls._OS_CANRAISE
@@ -278,8 +275,7 @@
call_release_gil_target=
EffectInfo._NO_CALL_RELEASE_GIL_TARGET,
extradescr=None,
- can_collect=True,
- call_shortcut=None):
+ can_collect=True):
from rpython.translator.backendopt.writeanalyze import top_set
if effects is top_set or extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
readonly_descrs_fields = None
@@ -368,8 +364,7 @@
can_invalidate,
call_release_gil_target,
extradescr,
- can_collect,
- call_shortcut)
+ can_collect)
def consider_struct(TYPE, fieldname):
if fieldType(TYPE, fieldname) is lltype.Void:
@@ -392,24 +387,6 @@
# ____________________________________________________________
-
-class CallShortcut(object):
- def __init__(self, argnum, fielddescr):
- self.argnum = argnum
- self.fielddescr = fielddescr
-
- def __eq__(self, other):
- return (isinstance(other, CallShortcut) and
- self.argnum == other.argnum and
- self.fielddescr == other.fielddescr)
- def __ne__(self, other):
- return not (self == other)
- def __hash__(self):
- return hash((self.argnum, self.fielddescr))
-
-# ____________________________________________________________
-
-
class VirtualizableAnalyzer(BoolGraphAnalyzer):
def analyze_simple_operation(self, op, graphinfo):
return op.opname in ('jit_force_virtualizable',
diff --git a/rpython/jit/codewriter/jtransform.py
b/rpython/jit/codewriter/jtransform.py
--- a/rpython/jit/codewriter/jtransform.py
+++ b/rpython/jit/codewriter/jtransform.py
@@ -789,12 +789,20 @@
arrayfielddescr,
arraydescr)
return []
+ # check for the string or unicode hash field
+ STRUCT = v_inst.concretetype.TO
+ if STRUCT == rstr.STR:
+ assert c_fieldname.value == 'hash'
+ return SpaceOperation('strhash', [v_inst], op.result)
+ elif STRUCT == rstr.UNICODE:
+ assert c_fieldname.value == 'hash'
+ return SpaceOperation('unicodehash', [v_inst], op.result)
# check for _immutable_fields_ hints
- immut = v_inst.concretetype.TO._immutable_field(c_fieldname.value)
+ immut = STRUCT._immutable_field(c_fieldname.value)
need_live = False
if immut:
if (self.callcontrol is not None and
- self.callcontrol.could_be_green_field(v_inst.concretetype.TO,
+ self.callcontrol.could_be_green_field(STRUCT,
c_fieldname.value)):
pure = '_greenfield'
need_live = True
@@ -802,10 +810,9 @@
pure = '_pure'
else:
pure = ''
- self.check_field_access(v_inst.concretetype.TO)
- argname = getattr(v_inst.concretetype.TO, '_gckind', 'gc')
- descr = self.cpu.fielddescrof(v_inst.concretetype.TO,
- c_fieldname.value)
+ self.check_field_access(STRUCT)
+ argname = getattr(STRUCT, '_gckind', 'gc')
+ descr = self.cpu.fielddescrof(STRUCT, c_fieldname.value)
kind = getkind(RESULT)[0]
if argname != 'gc':
assert argname == 'raw'
@@ -822,7 +829,7 @@
if immut in (IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY):
op1.opname += "_pure"
descr1 = self.cpu.fielddescrof(
- v_inst.concretetype.TO,
+ STRUCT,
quasiimmut.get_mutate_field_name(c_fieldname.value))
return [SpaceOperation('-live-', [], None),
SpaceOperation('record_quasiimmut_field',
@@ -1569,7 +1576,7 @@
return []
return getattr(self, 'handle_jit_marker__%s' % key)(op, jitdriver)
- def rewrite_op_jit_conditional_call(self, op):
+ def _rewrite_op_cond_call(self, op, rewritten_opname):
have_floats = False
for arg in op.args:
if getkind(arg.concretetype) == 'float':
@@ -1580,13 +1587,18 @@
callop = SpaceOperation('direct_call', op.args[1:], op.result)
calldescr = self.callcontrol.getcalldescr(callop)
assert not
calldescr.get_extra_info().check_forces_virtual_or_virtualizable()
- op1 = self.rewrite_call(op, 'conditional_call',
+ op1 = self.rewrite_call(op, rewritten_opname,
op.args[:2], args=op.args[2:],
calldescr=calldescr, force_ir=True)
if self.callcontrol.calldescr_canraise(calldescr):
op1 = [op1, SpaceOperation('-live-', [], None)]
return op1
+ def rewrite_op_jit_conditional_call(self, op):
+ return self._rewrite_op_cond_call(op, 'conditional_call')
+ def rewrite_op_jit_conditional_call_value(self, op):
+ return self._rewrite_op_cond_call(op, 'conditional_call_value')
+
def handle_jit_marker__jit_merge_point(self, op, jitdriver):
assert self.portal_jd is not None, (
"'jit_merge_point' in non-portal graph!")
diff --git a/rpython/jit/codewriter/test/test_call.py
b/rpython/jit/codewriter/test/test_call.py
--- a/rpython/jit/codewriter/test/test_call.py
+++ b/rpython/jit/codewriter/test/test_call.py
@@ -6,7 +6,7 @@
from rpython.rlib import jit
from rpython.jit.codewriter import support, call
from rpython.jit.codewriter.call import CallControl
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit