[pypy-commit] pypy inline-dict-ops: merge default
fijal
noreply at buildbot.pypy.org
Sat Jul 23 19:50:33 CEST 2011
Author: Maciej Fijalkowski <fijall at gmail.com>
Branch: inline-dict-ops
Changeset: r45902:fd39e9d2bdcf
Date: 2011-07-23 19:14 +0200
http://bitbucket.org/pypy/pypy/changeset/fd39e9d2bdcf/
Log: merge default
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -185,6 +185,7 @@
Jim Baker
Philip Jenvey
Rodrigo Araújo
+ Brett Cannon
Heinrich-Heine University, Germany
Open End AB (formerly AB Strakt), Sweden
diff --git a/lib-python/modified-2.7/distutils/sysconfig_pypy.py b/lib-python/modified-2.7/distutils/sysconfig_pypy.py
--- a/lib-python/modified-2.7/distutils/sysconfig_pypy.py
+++ b/lib-python/modified-2.7/distutils/sysconfig_pypy.py
@@ -116,6 +116,12 @@
if compiler.compiler_type == "unix":
compiler.compiler_so.extend(['-fPIC', '-Wimplicit'])
compiler.shared_lib_extension = get_config_var('SO')
+ if "CFLAGS" in os.environ:
+ cflags = os.environ["CFLAGS"]
+ compiler.compiler.append(cflags)
+ compiler.compiler_so.append(cflags)
+ compiler.linker_so.append(cflags)
+
from sysconfig_cpython import (
parse_makefile, _variable_rx, expand_makefile_vars)
diff --git a/lib-python/modified-2.7/pickle.py b/lib-python/modified-2.7/pickle.py
--- a/lib-python/modified-2.7/pickle.py
+++ b/lib-python/modified-2.7/pickle.py
@@ -168,7 +168,7 @@
# Pickling machinery
-class Pickler:
+class Pickler(object):
def __init__(self, file, protocol=None):
"""This takes a file-like object for writing a pickle data stream.
diff --git a/lib-python/2.7/test/test_tarfile.py b/lib-python/modified-2.7/test/test_tarfile.py
copy from lib-python/2.7/test/test_tarfile.py
copy to lib-python/modified-2.7/test/test_tarfile.py
--- a/lib-python/2.7/test/test_tarfile.py
+++ b/lib-python/modified-2.7/test/test_tarfile.py
@@ -169,6 +169,7 @@
except tarfile.ReadError:
self.fail("tarfile.open() failed on empty archive")
self.assertListEqual(tar.getmembers(), [])
+ tar.close()
def test_null_tarfile(self):
# Test for issue6123: Allow opening empty archives.
@@ -207,16 +208,21 @@
fobj = open(self.tarname, "rb")
tar = tarfile.open(fileobj=fobj, mode=self.mode)
self.assertEqual(tar.name, os.path.abspath(fobj.name))
+ tar.close()
def test_no_name_attribute(self):
- data = open(self.tarname, "rb").read()
+ f = open(self.tarname, "rb")
+ data = f.read()
+ f.close()
fobj = StringIO.StringIO(data)
self.assertRaises(AttributeError, getattr, fobj, "name")
tar = tarfile.open(fileobj=fobj, mode=self.mode)
self.assertEqual(tar.name, None)
def test_empty_name_attribute(self):
- data = open(self.tarname, "rb").read()
+ f = open(self.tarname, "rb")
+ data = f.read()
+ f.close()
fobj = StringIO.StringIO(data)
fobj.name = ""
tar = tarfile.open(fileobj=fobj, mode=self.mode)
@@ -515,6 +521,7 @@
self.tar = tarfile.open(self.tarname, mode=self.mode, encoding="iso8859-1")
tarinfo = self.tar.getmember("pax/umlauts-�������")
self._test_member(tarinfo, size=7011, chksum=md5_regtype)
+ self.tar.close()
class LongnameTest(ReadTest):
@@ -675,6 +682,7 @@
tar = tarfile.open(tmpname, self.mode)
tarinfo = tar.gettarinfo(path)
self.assertEqual(tarinfo.size, 0)
+ tar.close()
finally:
os.rmdir(path)
@@ -692,6 +700,7 @@
tar.gettarinfo(target)
tarinfo = tar.gettarinfo(link)
self.assertEqual(tarinfo.size, 0)
+ tar.close()
finally:
os.remove(target)
os.remove(link)
@@ -704,6 +713,7 @@
tar = tarfile.open(tmpname, self.mode)
tarinfo = tar.gettarinfo(path)
self.assertEqual(tarinfo.size, 0)
+ tar.close()
finally:
os.remove(path)
@@ -722,6 +732,7 @@
tar.add(dstname)
os.chdir(cwd)
self.assertTrue(tar.getnames() == [], "added the archive to itself")
+ tar.close()
def test_exclude(self):
tempdir = os.path.join(TEMPDIR, "exclude")
@@ -742,6 +753,7 @@
tar = tarfile.open(tmpname, "r")
self.assertEqual(len(tar.getmembers()), 1)
self.assertEqual(tar.getnames()[0], "empty_dir")
+ tar.close()
finally:
shutil.rmtree(tempdir)
@@ -859,7 +871,9 @@
fobj.close()
elif self.mode.endswith("bz2"):
dec = bz2.BZ2Decompressor()
- data = open(tmpname, "rb").read()
+ f = open(tmpname, "rb")
+ data = f.read()
+ f.close()
data = dec.decompress(data)
self.assertTrue(len(dec.unused_data) == 0,
"found trailing data")
@@ -938,6 +952,7 @@
"unable to read longname member")
self.assertEqual(tarinfo.linkname, member.linkname,
"unable to read longname member")
+ tar.close()
def test_longname_1023(self):
self._test(("longnam/" * 127) + "longnam")
@@ -1030,6 +1045,7 @@
else:
n = tar.getmembers()[0].name
self.assertTrue(name == n, "PAX longname creation failed")
+ tar.close()
def test_pax_global_header(self):
pax_headers = {
@@ -1058,6 +1074,7 @@
tarfile.PAX_NUMBER_FIELDS[key](val)
except (TypeError, ValueError):
self.fail("unable to convert pax header field")
+ tar.close()
def test_pax_extended_header(self):
# The fields from the pax header have priority over the
@@ -1077,6 +1094,7 @@
self.assertEqual(t.pax_headers, pax_headers)
self.assertEqual(t.name, "foo")
self.assertEqual(t.uid, 123)
+ tar.close()
class UstarUnicodeTest(unittest.TestCase):
@@ -1120,6 +1138,7 @@
tarinfo.name = "foo"
tarinfo.uname = u"���"
self.assertRaises(UnicodeError, tar.addfile, tarinfo)
+ tar.close()
def test_unicode_argument(self):
tar = tarfile.open(tarname, "r", encoding="iso8859-1", errors="strict")
@@ -1174,6 +1193,7 @@
tar = tarfile.open(tmpname, format=self.format, encoding="ascii",
errors=handler)
self.assertEqual(tar.getnames()[0], name)
+ tar.close()
self.assertRaises(UnicodeError, tarfile.open, tmpname,
encoding="ascii", errors="strict")
@@ -1186,6 +1206,7 @@
tar = tarfile.open(tmpname, format=self.format, encoding="iso8859-1",
errors="utf-8")
self.assertEqual(tar.getnames()[0], "���/" + u"�".encode("utf8"))
+ tar.close()
class AppendTest(unittest.TestCase):
@@ -1213,6 +1234,7 @@
def _test(self, names=["bar"], fileobj=None):
tar = tarfile.open(self.tarname, fileobj=fileobj)
self.assertEqual(tar.getnames(), names)
+ tar.close()
def test_non_existing(self):
self._add_testfile()
@@ -1231,7 +1253,9 @@
def test_fileobj(self):
self._create_testtar()
- data = open(self.tarname).read()
+ f = open(self.tarname)
+ data = f.read()
+ f.close()
fobj = StringIO.StringIO(data)
self._add_testfile(fobj)
fobj.seek(0)
@@ -1257,7 +1281,9 @@
# Append mode is supposed to fail if the tarfile to append to
# does not end with a zero block.
def _test_error(self, data):
- open(self.tarname, "wb").write(data)
+ f = open(self.tarname, "wb")
+ f.write(data)
+ f.close()
self.assertRaises(tarfile.ReadError, self._add_testfile)
def test_null(self):
diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py
--- a/lib_pypy/_ctypes/basics.py
+++ b/lib_pypy/_ctypes/basics.py
@@ -48,7 +48,8 @@
return self.from_param(as_parameter)
def get_ffi_param(self, value):
- return self.from_param(value)._to_ffi_param()
+ cdata = self.from_param(value)
+ return cdata, cdata._to_ffi_param()
def get_ffi_argtype(self):
if self._ffiargtype:
diff --git a/lib_pypy/_ctypes/function.py b/lib_pypy/_ctypes/function.py
--- a/lib_pypy/_ctypes/function.py
+++ b/lib_pypy/_ctypes/function.py
@@ -328,12 +328,14 @@
"native COM method call without 'this' parameter"
)
thisarg = cast(args[0], POINTER(POINTER(c_void_p)))
- newargs, argtypes, outargs = self._convert_args(argtypes, args[1:], kwargs)
+ keepalives, newargs, argtypes, outargs = self._convert_args(argtypes,
+ args[1:], kwargs)
newargs.insert(0, args[0].value)
argtypes.insert(0, c_void_p)
else:
thisarg = None
- newargs, argtypes, outargs = self._convert_args(argtypes, args, kwargs)
+ keepalives, newargs, argtypes, outargs = self._convert_args(argtypes,
+ args, kwargs)
funcptr = self._getfuncptr(argtypes, self._restype_, thisarg)
result = self._call_funcptr(funcptr, *newargs)
@@ -437,16 +439,15 @@
@classmethod
def _conv_param(cls, argtype, arg):
if isinstance(argtype, _CDataMeta):
- #arg = argtype.from_param(arg)
- arg = argtype.get_ffi_param(arg)
- return arg, argtype
+ cobj, ffiparam = argtype.get_ffi_param(arg)
+ return cobj, ffiparam, argtype
if argtype is not None:
arg = argtype.from_param(arg)
if hasattr(arg, '_as_parameter_'):
arg = arg._as_parameter_
if isinstance(arg, _CData):
- return arg._to_ffi_param(), type(arg)
+ return arg, arg._to_ffi_param(), type(arg)
#
# non-usual case: we do the import here to save a lot of code in the
# jit trace of the normal case
@@ -463,11 +464,12 @@
else:
raise TypeError("Don't know how to handle %s" % (arg,))
- return cobj._to_ffi_param(), type(cobj)
+ return cobj, cobj._to_ffi_param(), type(cobj)
def _convert_args(self, argtypes, args, kwargs, marker=object()):
newargs = []
outargs = []
+ keepalives = []
newargtypes = []
total = len(args)
paramflags = self._paramflags
@@ -495,7 +497,8 @@
val = defval
if val is marker:
val = 0
- newarg, newargtype = self._conv_param(argtype, val)
+ keepalive, newarg, newargtype = self._conv_param(argtype, val)
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
elif flag in (0, PARAMFLAG_FIN):
@@ -511,28 +514,32 @@
raise TypeError("required argument '%s' missing" % name)
else:
raise TypeError("not enough arguments")
- newarg, newargtype = self._conv_param(argtype, val)
+ keepalive, newarg, newargtype = self._conv_param(argtype, val)
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
elif flag == PARAMFLAG_FOUT:
if defval is not marker:
outargs.append(defval)
- newarg, newargtype = self._conv_param(argtype, defval)
+ keepalive, newarg, newargtype = self._conv_param(argtype, defval)
else:
import ctypes
val = argtype._type_()
outargs.append(val)
+ keepalive = None
newarg = ctypes.byref(val)
newargtype = type(newarg)
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
else:
raise ValueError("paramflag %d not yet implemented" % flag)
else:
try:
- newarg, newargtype = self._conv_param(argtype, args[i])
+ keepalive, newarg, newargtype = self._conv_param(argtype, args[i])
except (UnicodeError, TypeError, ValueError), e:
raise ArgumentError(str(e))
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
inargs_idx += 1
@@ -541,12 +548,13 @@
extra = args[len(newargs):]
for i, arg in enumerate(extra):
try:
- newarg, newargtype = self._conv_param(None, arg)
+ keepalive, newarg, newargtype = self._conv_param(None, arg)
except (UnicodeError, TypeError, ValueError), e:
raise ArgumentError(str(e))
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
- return newargs, newargtypes, outargs
+ return keepalives, newargs, newargtypes, outargs
def _wrap_result(self, restype, result):
diff --git a/lib_pypy/cPickle.py b/lib_pypy/cPickle.py
--- a/lib_pypy/cPickle.py
+++ b/lib_pypy/cPickle.py
@@ -29,7 +29,7 @@
PythonPickler.__init__(self, *args, **kw)
def memoize(self, obj):
- self.memo[None] = None # cPickle starts counting at one
+ self.memo[id(None)] = None # cPickle starts counting at one
return PythonPickler.memoize(self, obj)
def getvalue(self):
diff --git a/pypy/config/config.py b/pypy/config/config.py
--- a/pypy/config/config.py
+++ b/pypy/config/config.py
@@ -81,6 +81,12 @@
(self.__class__, name))
return self._cfgimpl_values[name]
+ def __dir__(self):
+ from_type = dir(type(self))
+ from_dict = list(self.__dict__)
+ extras = list(self._cfgimpl_values)
+ return sorted(set(extras + from_type + from_dict))
+
def __delattr__(self, name):
# XXX if you use delattr you are responsible for all bad things
# happening
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -327,6 +327,9 @@
BoolOption("mutable_builtintypes",
"Allow the changing of builtin types", default=False,
requires=[("objspace.std.builtinshortcut", True)]),
+ BoolOption("withidentitydict",
+ "track types that override __hash__, __eq__ or __cmp__ and use a special dict strategy for those which do not",
+ default=True),
]),
])
diff --git a/pypy/config/test/test_config.py b/pypy/config/test/test_config.py
--- a/pypy/config/test/test_config.py
+++ b/pypy/config/test/test_config.py
@@ -63,6 +63,20 @@
py.test.raises(ConfigError, 'config.gc.name = "ref"')
config.gc.name = "framework"
+def test___dir__():
+ descr = make_description()
+ config = Config(descr, bool=False)
+ attrs = dir(config)
+ assert '__repr__' in attrs # from the type
+ assert '_cfgimpl_values' in attrs # from self
+ assert 'gc' in attrs # custom attribute
+ assert 'objspace' in attrs # custom attribute
+ #
+ attrs = dir(config.gc)
+ assert 'name' in attrs
+ assert 'dummy' in attrs
+ assert 'float' in attrs
+
def test_arbitrary_option():
descr = OptionDescription("top", "", [
ArbitraryOption("a", "no help", default=None)
diff --git a/pypy/config/translationoption.py b/pypy/config/translationoption.py
--- a/pypy/config/translationoption.py
+++ b/pypy/config/translationoption.py
@@ -140,7 +140,10 @@
["annotate", "rtype", "backendopt", "database", "source",
"pyjitpl"],
default=None, cmdline="--fork-before"),
-
+ BoolOption("dont_write_c_files",
+ "Make the C backend write everyting to /dev/null. " +
+ "Useful for benchmarking, so you don't actually involve the disk",
+ default=False, cmdline="--dont-write-c-files"),
ArbitraryOption("instrumentctl", "internal",
default=None),
StrOption("output", "Output file name", cmdline="--output"),
diff --git a/pypy/doc/coding-guide.rst b/pypy/doc/coding-guide.rst
--- a/pypy/doc/coding-guide.rst
+++ b/pypy/doc/coding-guide.rst
@@ -929,6 +929,19 @@
located in the ``py/bin/`` directory. For switches to
modify test execution pass the ``-h`` option.
+Coverage reports
+----------------
+
+In order to get coverage reports the `pytest-cov`_ plugin is included.
+it adds some extra requirements ( coverage_ and `cov-core`_ )
+and can once they are installed coverage testing can be invoked via::
+
+ python test_all.py --cov file_or_direcory_to_cover file_or_directory
+
+.. _`pytest-cov`: http://pypi.python.org/pypi/pytest-cov
+.. _`coverage`: http://pypi.python.org/pypi/coverage
+.. _`cov-core`: http://pypi.python.org/pypi/cov-core
+
Test conventions
----------------
diff --git a/pypy/doc/config/objspace.std.withidentitydict.txt b/pypy/doc/config/objspace.std.withidentitydict.txt
new file mode 100644
--- /dev/null
+++ b/pypy/doc/config/objspace.std.withidentitydict.txt
@@ -0,0 +1,21 @@
+=============================
+objspace.std.withidentitydict
+=============================
+
+* **name:** withidentitydict
+
+* **description:** enable a dictionary strategy for "by identity" comparisons
+
+* **command-line:** --objspace-std-withidentitydict
+
+* **command-line for negation:** --no-objspace-std-withidentitydict
+
+* **option type:** boolean option
+
+* **default:** True
+
+
+Enable a dictionary strategy specialized for instances of classes which
+compares "by identity", which is the default unless you override ``__hash__``,
+``__eq__`` or ``__cmp__``. This strategy will be used only with new-style
+classes.
diff --git a/pypy/doc/config/translation.dont_write_c_files.txt b/pypy/doc/config/translation.dont_write_c_files.txt
new file mode 100644
--- /dev/null
+++ b/pypy/doc/config/translation.dont_write_c_files.txt
@@ -0,0 +1,4 @@
+write the generated C files to ``/dev/null`` instead of to the disk. Useful if
+you want to use translate.py as a benchmark and don't want to access the disk.
+
+.. _`translation documentation`: ../translation.html
diff --git a/pypy/doc/config/translation.gc.txt b/pypy/doc/config/translation.gc.txt
--- a/pypy/doc/config/translation.gc.txt
+++ b/pypy/doc/config/translation.gc.txt
@@ -1,4 +1,6 @@
-Choose the Garbage Collector used by the translated program:
+Choose the Garbage Collector used by the translated program.
+The good performing collectors are "hybrid" and "minimark".
+The default is "minimark".
- "ref": reference counting. Takes very long to translate and the result is
slow.
@@ -11,3 +13,12 @@
older generation.
- "boehm": use the Boehm conservative GC.
+
+ - "hybrid": a hybrid collector of "generation" together with a
+ mark-n-sweep old space
+
+ - "markcompact": a slow, but memory-efficient collector,
+ influenced e.g. by Smalltalk systems.
+
+ - "minimark": a generational mark-n-sweep collector with good
+ performance. Includes page marking for large arrays.
diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst
--- a/pypy/doc/cpython_differences.rst
+++ b/pypy/doc/cpython_differences.rst
@@ -211,6 +211,38 @@
>>>> print d1['a']
42
+Mutating classes of objects which are already used as dictionary keys
+---------------------------------------------------------------------
+
+Consider the following snippet of code::
+
+ class X(object):
+ pass
+
+ def __evil_eq__(self, other):
+ print 'hello world'
+ return False
+
+ def evil(y):
+ d = {x(): 1}
+ X.__eq__ = __evil_eq__
+ d[y] # might trigger a call to __eq__?
+
+In CPython, __evil_eq__ **might** be called, although there is no way to write
+a test which reliably calls it. It happens if ``y is not x`` and ``hash(y) ==
+hash(x)``, where ``hash(x)`` is computed when ``x`` is inserted into the
+dictionary. If **by chance** the condition is satisfied, then ``__evil_eq__``
+is called.
+
+PyPy uses a special strategy to optimize dictionaries whose keys are instances
+of user-defined classes which do not override the default ``__hash__``,
+``__eq__`` and ``__cmp__``: when using this strategy, ``__eq__`` and
+``__cmp__`` are never called, but instead the lookup is done by identity, so
+in the case above it is guaranteed that ``__eq__`` won't be called.
+
+Note that in all other cases (e.g., if you have a custom ``__hash__`` and
+``__eq__`` in ``y``) the behavior is exactly the same as CPython.
+
Ignored exceptions
-----------------------
diff --git a/pypy/doc/extending.rst b/pypy/doc/extending.rst
--- a/pypy/doc/extending.rst
+++ b/pypy/doc/extending.rst
@@ -19,12 +19,12 @@
section
* Write them in pure python and use direct libffi low-level bindings, See
- \_rawffi_ module description.
+ \_ffi_ module description.
* Write them in RPython as mixedmodule_, using *rffi* as bindings.
.. _ctypes: #CTypes
-.. _\_rawffi: #LibFFI
+.. _\_ffi: #LibFFI
.. _mixedmodule: #Mixed Modules
CTypes
@@ -42,41 +42,50 @@
platform-dependent details (compiling small snippets of C code and running
them), so it'll benefit not pypy-related ctypes-based modules as well.
+ctypes call are optimized by the JIT and the resulting machine code contains a
+direct call to the target C function. However, due to the very dynamic nature
+of ctypes, some overhead over a bare C call is still present, in particular to
+check/convert the types of the parameters. Moreover, even if most calls are
+optimized, some cannot and thus need to follow the slow path, not optimized by
+the JIT.
+
.. _`ctypes-configure`: ctypes-implementation.html#ctypes-configure
+.. _`CPython ctypes`: http://docs.python.org/library/ctypes.html
Pros
----
-Stable, CPython-compatible API
+Stable, CPython-compatible API. Most calls are fast, optimized by JIT.
Cons
----
-Only pure-python code (slow), problems with platform-dependency (although
-we partially solve those). PyPy implementation is now very slow.
+Problems with platform-dependency (although we partially solve
+those). Although the JIT optimizes ctypes calls, some overhead is still
+present. The slow-path is very slow.
-_`CPython ctypes`: http://python.net/crew/theller/ctypes/
LibFFI
======
Mostly in order to be able to write a ctypes module, we developed a very
-low-level libffi bindings. (libffi is a C-level library for dynamic calling,
+low-level libffi bindings called ``_ffi``. (libffi is a C-level library for dynamic calling,
which is used by CPython ctypes). This library provides stable and usable API,
although it's API is a very low-level one. It does not contain any
-magic.
+magic. It is also optimized by the JIT, but has much less overhead than ctypes.
Pros
----
-Works. Combines disadvantages of using ctypes with disadvantages of
-using mixed modules. Probably more suitable for a delicate code
-where ctypes magic goes in a way.
+It Works. Probably more suitable for a delicate code where ctypes magic goes
+in a way. All calls are optimized by the JIT, there is no slow path as in
+ctypes.
Cons
----
-Slow. CPython-incompatible API, very rough and low-level
+It combines disadvantages of using ctypes with disadvantages of using mixed
+modules. CPython-incompatible API, very rough and low-level.
Mixed Modules
=============
@@ -87,15 +96,15 @@
* a mixed module needs to be written in RPython, which is far more
complicated than Python (XXX link)
-* due to lack of separate compilation (as of April 2008), each
+* due to lack of separate compilation (as of July 2011), each
compilation-check requires to recompile whole PyPy python interpreter,
which takes 0.5-1h. We plan to solve this at some point in near future.
* although rpython is a garbage-collected language, the border between
C and RPython needs to be managed by hand (each object that goes into the
- C level must be explicitly freed) XXX we try to solve this
+ C level must be explicitly freed).
-Some document is available `here`_
+Some documentation is available `here`_
.. _`here`: rffi.html
diff --git a/pypy/doc/windows.rst b/pypy/doc/windows.rst
--- a/pypy/doc/windows.rst
+++ b/pypy/doc/windows.rst
@@ -32,6 +32,24 @@
modules that relies on third-party libraries. See below how to get
and build them.
+Preping Windows for the Large Build
+-----------------------------------
+
+Normally 32bit programs are limited to 2GB of memory on Windows. It is
+possible to raise this limit, to 3GB on Windows 32bit, and almost 4GB
+on Windows 64bit.
+
+On Windows 32bit, it is necessary to modify the system: follow
+http://usa.autodesk.com/adsk/servlet/ps/dl/item?siteID=123112&id=9583842&linkID=9240617
+to enable the "3GB" feature, and reboot. This step is not necessary on
+Windows 64bit.
+
+Then you need to execute::
+
+ editbin /largeaddressaware pypy.exe
+
+on the pypy.exe file you compiled.
+
Installing external packages
----------------------------
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
@@ -27,9 +27,10 @@
_emit_syntax_warning(space, w_msg, w_filename, w_lineno, w_offset)
-def parse_future(tree):
+def parse_future(tree, feature_flags):
future_lineno = 0
future_column = 0
+ flags = 0
have_docstring = False
body = None
if isinstance(tree, ast.Module):
@@ -37,7 +38,7 @@
elif isinstance(tree, ast.Interactive):
body = tree.body
if body is None:
- return 0, 0
+ return 0, 0, 0
for stmt in body:
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Str):
if have_docstring:
@@ -48,11 +49,16 @@
if stmt.module == "__future__":
future_lineno = stmt.lineno
future_column = stmt.col_offset
+ for alias in stmt.names:
+ assert isinstance(alias, ast.alias)
+ # If this is an invalid flag, it will be caught later in
+ # codegen.py.
+ flags |= feature_flags.get(alias.name, 0)
else:
break
else:
break
- return future_lineno, future_column
+ return flags, future_lineno, future_column
class ForbiddenNameAssignment(Exception):
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -130,6 +130,9 @@
raise operationerrfmt(space.w_TypeError,
"cannot create weak reference to '%s' object", typename)
+ def delweakref(self):
+ pass
+
def clear_all_weakrefs(self):
"""Call this at the beginning of interp-level __del__() methods
in subclasses. It ensures that weakrefs (if any) are cleared
@@ -143,29 +146,28 @@
# app-level, e.g. a user-defined __del__(), and this code
# tries to use weakrefs again, it won't reuse the broken
# (already-cleared) weakrefs from this lifeline.
- self.setweakref(lifeline.space, None)
+ self.delweakref()
lifeline.clear_all_weakrefs()
- __already_enqueued_for_destruction = False
+ __already_enqueued_for_destruction = ()
- def _enqueue_for_destruction(self, space, call_user_del=True):
+ def enqueue_for_destruction(self, space, callback, descrname):
"""Put the object in the destructor queue of the space.
- At a later, safe point in time, UserDelAction will use
- space.userdel() to call the object's app-level __del__ method.
+ At a later, safe point in time, UserDelAction will call
+ callback(self). If that raises OperationError, prints it
+ to stderr with the descrname string.
+
+ Note that 'callback' will usually need to start with:
+ assert isinstance(self, W_SpecificClass)
"""
# this function always resurect the object, so when
# running on top of CPython we must manually ensure that
# we enqueue it only once
if not we_are_translated():
- if self.__already_enqueued_for_destruction:
+ if callback in self.__already_enqueued_for_destruction:
return
- self.__already_enqueued_for_destruction = True
- self.clear_all_weakrefs()
- if call_user_del:
- space.user_del_action.register_dying_object(self)
-
- def _call_builtin_destructor(self):
- pass # method overridden in typedef.py
+ self.__already_enqueued_for_destruction += (callback,)
+ space.user_del_action.register_callback(self, callback, descrname)
# hooks that the mapdict implementations needs:
def _get_mapdict_map(self):
@@ -925,6 +927,9 @@
return self.w_True
return self.w_False
+ def issequence_w(self, w_obj):
+ return (self.findattr(w_obj, self.wrap("__getitem__")) is not None)
+
def isinstance_w(self, w_obj, w_type):
return self.is_true(self.isinstance(w_obj, w_type))
@@ -1279,6 +1284,17 @@
self.wrap("expected a 32-bit integer"))
return value
+ def truncatedint(self, w_obj):
+ # Like space.gateway_int_w(), but return the integer truncated
+ # instead of raising OverflowError. For obscure cases only.
+ try:
+ return self.int_w(w_obj)
+ except OperationError, e:
+ if not e.match(self, self.w_OverflowError):
+ raise
+ from pypy.rlib.rarithmetic import intmask
+ return intmask(self.bigint_w(w_obj).uintmask())
+
def c_filedescriptor_w(self, w_fd):
# This is only used sometimes in CPython, e.g. for os.fsync() but
# not os.close(). It's likely designed for 'select'. It's irregular
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -484,44 +484,31 @@
def __init__(self, space):
AsyncAction.__init__(self, space)
- self.dying_objects_w = []
- self.weakrefs_w = []
+ self.dying_objects = []
self.finalizers_lock_count = 0
- def register_dying_object(self, w_obj):
- self.dying_objects_w.append(w_obj)
- self.fire()
-
- def register_weakref_callback(self, w_ref):
- self.weakrefs_w.append(w_ref)
+ def register_callback(self, w_obj, callback, descrname):
+ self.dying_objects.append((w_obj, callback, descrname))
self.fire()
def perform(self, executioncontext, frame):
if self.finalizers_lock_count > 0:
return
- # Each call to perform() first grabs the self.dying_objects_w
+ # Each call to perform() first grabs the self.dying_objects
# and replaces it with an empty list. We do this to try to
# avoid too deep recursions of the kind of __del__ being called
# while in the middle of another __del__ call.
- pending_w = self.dying_objects_w
- self.dying_objects_w = []
+ pending = self.dying_objects
+ self.dying_objects = []
space = self.space
- for i in range(len(pending_w)):
- w_obj = pending_w[i]
- pending_w[i] = None
+ for i in range(len(pending)):
+ w_obj, callback, descrname = pending[i]
+ pending[i] = (None, None, None)
try:
- space.userdel(w_obj)
+ callback(w_obj)
except OperationError, e:
- e.write_unraisable(space, 'method __del__ of ', w_obj)
+ e.write_unraisable(space, descrname, w_obj)
e.clear(space) # break up reference cycles
- # finally, this calls the interp-level destructor for the
- # cases where there is both an app-level and a built-in __del__.
- w_obj._call_builtin_destructor()
- pending_w = self.weakrefs_w
- self.weakrefs_w = []
- for i in range(len(pending_w)):
- w_ref = pending_w[i]
- w_ref.activate_callback()
class FrameTraceAction(AsyncAction):
"""An action that calls the local trace functions (w_f_trace)."""
diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py
--- a/pypy/interpreter/function.py
+++ b/pypy/interpreter/function.py
@@ -31,7 +31,8 @@
_immutable_fields_ = ['code?',
'w_func_globals?',
'closure?',
- 'defs_w?[*]']
+ 'defs_w?[*]',
+ 'name?']
def __init__(self, space, code, w_globals=None, defs_w=[], closure=None,
forcename=None):
diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py
--- a/pypy/interpreter/gateway.py
+++ b/pypy/interpreter/gateway.py
@@ -140,6 +140,9 @@
def visit_c_nonnegint(self, el, app_sig):
self.checked_space_method(el, app_sig)
+ def visit_truncatedint(self, el, app_sig):
+ self.checked_space_method(el, app_sig)
+
def visit__Wrappable(self, el, app_sig):
name = el.__name__
argname = self.orig_arg()
@@ -257,6 +260,9 @@
def visit_c_nonnegint(self, typ):
self.run_args.append("space.c_nonnegint_w(%s)" % (self.scopenext(),))
+ def visit_truncatedint(self, typ):
+ self.run_args.append("space.truncatedint(%s)" % (self.scopenext(),))
+
def _make_unwrap_activation_class(self, unwrap_spec, cache={}):
try:
key = tuple(unwrap_spec)
@@ -387,6 +393,9 @@
def visit_c_nonnegint(self, typ):
self.unwrap.append("space.c_nonnegint_w(%s)" % (self.nextarg(),))
+ def visit_truncatedint(self, typ):
+ self.unwrap.append("space.truncatedint(%s)" % (self.nextarg(),))
+
def make_fastfunc(unwrap_spec, func):
unwrap_info = UnwrapSpec_FastFunc_Unwrap()
unwrap_info.apply_over(unwrap_spec)
@@ -396,11 +405,14 @@
fastfunc = func
else:
# try to avoid excessive bloat
- if func.__module__ == 'pypy.interpreter.astcompiler.ast':
+ mod = func.__module__
+ if mod is None:
+ mod = ""
+ if mod == 'pypy.interpreter.astcompiler.ast':
raise FastFuncNotSupported
- if (not func.__module__.startswith('pypy.module.__builtin__') and
- not func.__module__.startswith('pypy.module.sys') and
- not func.__module__.startswith('pypy.module.math')):
+ if (not mod.startswith('pypy.module.__builtin__') and
+ not mod.startswith('pypy.module.sys') and
+ not mod.startswith('pypy.module.math')):
if not func.__name__.startswith('descr'):
raise FastFuncNotSupported
d = {}
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -114,6 +114,7 @@
def descr_close(self):
"""x.close(arg) -> raise GeneratorExit inside generator."""
+ assert isinstance(self, GeneratorIterator)
space = self.space
try:
w_retval = self.throw(space.w_GeneratorExit, space.w_None,
@@ -141,22 +142,16 @@
code_name = self.pycode.co_name
return space.wrap(code_name)
- def descr__del__(self):
- """
- applevel __del__, which is called at a safe point after the
- interp-level __del__ enqueued the object for destruction
- """
- self.descr_close()
-
def __del__(self):
# Only bother enqueuing self to raise an exception if the frame is
# still not finished and finally or except blocks are present.
- must_call_close = False
+ self.clear_all_weakrefs()
if self.frame is not None:
block = self.frame.lastblock
while block is not None:
if not isinstance(block, LoopBlock):
- must_call_close = True
+ self.enqueue_for_destruction(self.space,
+ GeneratorIterator.descr_close,
+ "interrupting generator of ")
break
block = block.previous
- self._enqueue_for_destruction(self.space, must_call_close)
diff --git a/pypy/interpreter/module.py b/pypy/interpreter/module.py
--- a/pypy/interpreter/module.py
+++ b/pypy/interpreter/module.py
@@ -9,6 +9,8 @@
class Module(Wrappable):
"""A module."""
+ _immutable_fields_ = ["w_dict?"]
+
_frozen = False
def __init__(self, space, w_name, w_dict=None, add_package=True):
diff --git a/pypy/interpreter/pycompiler.py b/pypy/interpreter/pycompiler.py
--- a/pypy/interpreter/pycompiler.py
+++ b/pypy/interpreter/pycompiler.py
@@ -119,7 +119,10 @@
raise OperationError(self.space.w_TypeError, self.space.wrap(
"invalid node type"))
- future_pos = misc.parse_future(node)
+ fut = misc.parse_future(node, self.future_flags.compiler_features)
+ f_flags, f_lineno, f_col = fut
+ future_pos = f_lineno, f_col
+ flags |= f_flags
info = pyparse.CompileInfo(filename, mode, flags, future_pos)
return self._compile_ast(node, info)
diff --git a/pypy/interpreter/test/test_typedef.py b/pypy/interpreter/test/test_typedef.py
--- a/pypy/interpreter/test/test_typedef.py
+++ b/pypy/interpreter/test/test_typedef.py
@@ -1,3 +1,4 @@
+import gc
from pypy.interpreter import typedef
from pypy.tool.udir import udir
from pypy.interpreter.baseobjspace import Wrappable
@@ -180,6 +181,85 @@
assert err.value.message == "'some_type' objects are unhashable"
""")
+ def test_destructor(self):
+ space = self.space
+ class W_Level1(Wrappable):
+ def __init__(self, space1):
+ assert space1 is space
+ def __del__(self):
+ space.call_method(w_seen, 'append', space.wrap(1))
+ class W_Level2(Wrappable):
+ def __init__(self, space1):
+ assert space1 is space
+ def __del__(self):
+ self.enqueue_for_destruction(space, W_Level2.destructormeth,
+ 'FOO ')
+ def destructormeth(self):
+ space.call_method(w_seen, 'append', space.wrap(2))
+ W_Level1.typedef = typedef.TypeDef(
+ 'level1',
+ __new__ = typedef.generic_new_descr(W_Level1))
+ W_Level2.typedef = typedef.TypeDef(
+ 'level2',
+ __new__ = typedef.generic_new_descr(W_Level2))
+ #
+ w_seen = space.newlist([])
+ W_Level1(space)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [1]
+ #
+ w_seen = space.newlist([])
+ W_Level2(space)
+ gc.collect(); gc.collect()
+ assert space.str_w(space.repr(w_seen)) == "[]" # not called yet
+ ec = space.getexecutioncontext()
+ self.space.user_del_action.perform(ec, None)
+ assert space.unwrap(w_seen) == [2]
+ #
+ w_seen = space.newlist([])
+ self.space.appexec([self.space.gettypeobject(W_Level1.typedef)],
+ """(level1):
+ class A3(level1):
+ pass
+ A3()
+ """)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [1]
+ #
+ w_seen = space.newlist([])
+ self.space.appexec([self.space.gettypeobject(W_Level1.typedef),
+ w_seen],
+ """(level1, seen):
+ class A4(level1):
+ def __del__(self):
+ seen.append(4)
+ A4()
+ """)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [4, 1]
+ #
+ w_seen = space.newlist([])
+ self.space.appexec([self.space.gettypeobject(W_Level2.typedef)],
+ """(level2):
+ class A5(level2):
+ pass
+ A5()
+ """)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [2]
+ #
+ w_seen = space.newlist([])
+ self.space.appexec([self.space.gettypeobject(W_Level2.typedef),
+ w_seen],
+ """(level2, seen):
+ class A6(level2):
+ def __del__(self):
+ seen.append(6)
+ A6()
+ """)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [6, 2]
+
class AppTestTypeDef:
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -23,7 +23,7 @@
self.hasdict |= __base.hasdict
self.weakrefable |= __base.weakrefable
self.rawdict = {}
- self.acceptable_as_base_class = True
+ self.acceptable_as_base_class = '__new__' in rawdict
self.applevel_subclasses_base = None
# xxx used by faking
self.fakedcpytype = None
@@ -228,21 +228,26 @@
return self._lifeline_
def setweakref(self, space, weakreflifeline):
self._lifeline_ = weakreflifeline
+ def delweakref(self):
+ self._lifeline_ = None
add(Proto)
if "del" in features:
+ parent_destructor = getattr(supercls, '__del__', None)
+ def call_parent_del(self):
+ assert isinstance(self, subcls)
+ parent_destructor(self)
+ def call_applevel_del(self):
+ assert isinstance(self, subcls)
+ self.space.userdel(self)
class Proto(object):
def __del__(self):
- self._enqueue_for_destruction(self.space)
- # if the base class needs its own interp-level __del__,
- # we override the _call_builtin_destructor() method to invoke it
- # after the app-level destructor.
- parent_destructor = getattr(supercls, '__del__', None)
+ self.clear_all_weakrefs()
+ self.enqueue_for_destruction(self.space, call_applevel_del,
+ 'method __del__ of ')
if parent_destructor is not None:
- def _call_builtin_destructor(self):
- parent_destructor(self)
- Proto._call_builtin_destructor = _call_builtin_destructor
-
+ self.enqueue_for_destruction(self.space, call_parent_del,
+ 'internal destructor of ')
add(Proto)
if "slots" in features:
@@ -630,9 +635,12 @@
return self._lifeline_
def setweakref(self, space, weakreflifeline):
self._lifeline_ = weakreflifeline
+ def delweakref(self):
+ self._lifeline_ = None
cls._lifeline_ = None
cls.getweakref = getweakref
cls.setweakref = setweakref
+ cls.delweakref = delweakref
return weakref_descr
@@ -858,8 +866,6 @@
descrmismatch='close'),
__iter__ = interp2app(GeneratorIterator.descr__iter__,
descrmismatch='__iter__'),
- __del__ = interp2app(GeneratorIterator.descr__del__,
- descrmismatch='__del__'),
gi_running = interp_attrproperty('running', cls=GeneratorIterator),
gi_frame = GetSetProperty(GeneratorIterator.descr_gi_frame),
gi_code = GetSetProperty(GeneratorIterator.descr_gi_code),
diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py
--- a/pypy/jit/backend/x86/assembler.py
+++ b/pypy/jit/backend/x86/assembler.py
@@ -481,7 +481,7 @@
#
rawstart = self.materialize_loop(original_loop_token)
debug_start("jit-backend-addr")
- debug_print("Bridge out of Guard %d has address %x to %x" %
+ debug_print("bridge out of Guard %d has address %x to %x" %
(descr_number, rawstart, rawstart + codeendpos))
debug_stop("jit-backend-addr")
self._patch_stackadjust(rawstart + stackadjustpos,
diff --git a/pypy/jit/backend/x86/rx86.py b/pypy/jit/backend/x86/rx86.py
--- a/pypy/jit/backend/x86/rx86.py
+++ b/pypy/jit/backend/x86/rx86.py
@@ -464,7 +464,7 @@
# ------------------------------ MOV ------------------------------
- MOV_ri = insn(rex_w, register(1), '\xB8', immediate(2, 'q'))
+ MOV_ri = insn(register(1), '\xB8', immediate(2))
MOV8_ri = insn(rex_fw, byte_register(1), '\xB0', immediate(2, 'b'))
# ------------------------------ Arithmetic ------------------------------
@@ -632,16 +632,20 @@
CQO = insn(rex_w, '\x99')
- # MOV_ri from the parent class is not wrong, but here is a better encoding
- # for the common case where the immediate fits in 32 bits
+ # Three different encodings... following what gcc does. From the
+ # shortest encoding to the longest one.
+ MOV_riu32 = insn(rex_nw, register(1), '\xB8', immediate(2, 'i'))
MOV_ri32 = insn(rex_w, '\xC7', register(1), '\xC0', immediate(2, 'i'))
- MOV_ri64 = AbstractX86CodeBuilder.MOV_ri
+ MOV_ri64 = insn(rex_w, register(1), '\xB8', immediate(2, 'q'))
def MOV_ri(self, reg, immed):
- if fits_in_32bits(immed):
+ if 0 <= immed <= 4294967295:
+ immed = intmask(rffi.cast(rffi.INT, immed))
+ self.MOV_riu32(reg, immed)
+ elif fits_in_32bits(immed): # for negative values that fit in 32 bit
self.MOV_ri32(reg, immed)
else:
- AbstractX86CodeBuilder.MOV_ri(self, reg, immed)
+ self.MOV_ri64(reg, immed)
def define_modrm_modes(insnname_template, before_modrm, after_modrm=[], regtype='GPR'):
def add_insn(code, *modrm):
diff --git a/pypy/jit/backend/x86/test/test_regloc.py b/pypy/jit/backend/x86/test/test_regloc.py
--- a/pypy/jit/backend/x86/test/test_regloc.py
+++ b/pypy/jit/backend/x86/test/test_regloc.py
@@ -24,9 +24,14 @@
assert_encodes_as(cb64, "MOV16", (r8, ebx), '\x66\x41\x89\xD8') # 11 011 000
assert_encodes_as(cb64, "MOV16", (ebx, r8), '\x66\x44\x89\xC3') # 11 000 011
assert_encodes_as(cb64, "MOV16", (ecx, ebx), '\x66\x40\x89\xD9')
- # XXX: What we are testing for here is actually not the most compact
- # encoding.
- assert_encodes_as(cb64, "MOV16", (ecx, ImmedLoc(12345)), '\x66\x40\xC7\xC1\x39\x30')
+ assert_encodes_as(cb64, "MOV16", (ecx, ImmedLoc(12345)), '\x66\xB9\x39\x30')
+ # for the next case we don't pick the most efficient encoding, but well
+ expected = '\x66\x40\xC7\xC1\xC7\xCF' # could be '\x66\xB9\xC7\xCF'
+ assert_encodes_as(cb64, "MOV16", (ecx, ImmedLoc(-12345)), expected)
+ assert_encodes_as(cb64, "MOV16", (r9, ImmedLoc(12345)), '\x66\x41\xB9\x39\x30')
+ # for the next case we don't pick the most efficient encoding, but well
+ expected = '\x66\x41\xC7\xC1\xC7\xCF' # could be '\x66\x41\xB9\xC7\xCF'
+ assert_encodes_as(cb64, "MOV16", (r9, ImmedLoc(-12345)), expected)
assert_encodes_as(cb64, "MOV16", (AddressLoc(r13, ImmedLoc(0), 0, 0), ImmedLoc(12345)), '\x66\x41\xC7\x45\x00\x39\x30')
def test_cmp_16():
@@ -44,7 +49,7 @@
def test_relocation():
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.jit.backend.x86 import codebuf
- for target in [0x01020304, 0x0102030405060708]:
+ for target in [0x01020304, -0x05060708, 0x0102030405060708]:
if target > sys.maxint:
continue
mc = codebuf.MachineCodeBlockWrapper()
@@ -58,10 +63,15 @@
expected = "\xE8" + struct.pack('<i', target - (rawstart + 5))
elif IS_X86_64:
assert mc.relocations == []
- if target <= 0x7fffffff:
+ if 0 <= target <= 0xffffffff:
+ assert length == 9
+ expected = (
+ "\x41\xBB\x04\x03\x02\x01" # MOV %r11, target
+ "\x41\xFF\xD3") # CALL *%r11
+ elif -0x80000000 <= target < 0:
assert length == 10
expected = (
- "\x49\xC7\xC3\x04\x03\x02\x01" # MOV %r11, target
+ "\x49\xC7\xC3\xF8\xF8\xF9\xFA" # MOV %r11, target
"\x41\xFF\xD3") # CALL *%r11
else:
assert length == 13
diff --git a/pypy/jit/backend/x86/test/test_rx86.py b/pypy/jit/backend/x86/test/test_rx86.py
--- a/pypy/jit/backend/x86/test/test_rx86.py
+++ b/pypy/jit/backend/x86/test/test_rx86.py
@@ -198,9 +198,19 @@
def test_mov_ri_64():
s = CodeBuilder64()
s.MOV_ri(ecx, -2)
+ s.MOV_ri(r15, -3)
+ s.MOV_ri(ebx, -0x80000003)
+ s.MOV_ri(r13, -0x80000002)
+ s.MOV_ri(ecx, 42)
s.MOV_ri(r12, 0x80000042)
+ s.MOV_ri(r12, 0x100000007)
assert s.getvalue() == ('\x48\xC7\xC1\xFE\xFF\xFF\xFF' +
- '\x49\xBC\x42\x00\x00\x80\x00\x00\x00\x00')
+ '\x49\xC7\xC7\xFD\xFF\xFF\xFF' +
+ '\x48\xBB\xFD\xFF\xFF\x7F\xFF\xFF\xFF\xFF' +
+ '\x49\xBD\xFE\xFF\xFF\x7F\xFF\xFF\xFF\xFF' +
+ '\xB9\x2A\x00\x00\x00' +
+ '\x41\xBC\x42\x00\x00\x80' +
+ '\x49\xBC\x07\x00\x00\x00\x01\x00\x00\x00')
def test_mov_rm_64():
s = CodeBuilder64()
diff --git a/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py b/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py
--- a/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py
+++ b/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py
@@ -212,6 +212,17 @@
for mode, v in zip(argmodes, args):
ops.append(assembler_operand[mode](v))
ops.reverse()
+ #
+ if (instrname.lower() == 'mov' and suffix == 'q' and
+ ops[0].startswith('$') and 0 <= int(ops[0][1:]) <= 4294967295
+ and ops[1].startswith('%r')):
+ # movq $xxx, %rax => movl $xxx, %eax
+ suffix = 'l'
+ if ops[1][2:].isdigit():
+ ops[1] += 'd'
+ else:
+ ops[1] = '%e' + ops[1][2:]
+ #
op = '\t%s%s %s%s' % (instrname.lower(), suffix,
', '.join(ops), following)
g.write('%s\n' % op)
diff --git a/pypy/jit/codewriter/call.py b/pypy/jit/codewriter/call.py
--- a/pypy/jit/codewriter/call.py
+++ b/pypy/jit/codewriter/call.py
@@ -5,10 +5,9 @@
from pypy.jit.codewriter import support
from pypy.jit.codewriter.jitcode import JitCode
-from pypy.jit.codewriter.effectinfo import VirtualizableAnalyzer
-from pypy.jit.codewriter.effectinfo import QuasiImmutAnalyzer
-from pypy.jit.codewriter.effectinfo import effectinfo_from_writeanalyze
-from pypy.jit.codewriter.effectinfo import EffectInfo, CallInfoCollection
+from pypy.jit.codewriter.effectinfo import (VirtualizableAnalyzer,
+ QuasiImmutAnalyzer, CanReleaseGILAnalyzer, effectinfo_from_writeanalyze,
+ EffectInfo, CallInfoCollection)
from pypy.translator.simplify import get_funcobj, get_functype
from pypy.rpython.lltypesystem import lltype, llmemory
from pypy.translator.backendopt.canraise import RaiseAnalyzer
@@ -32,6 +31,7 @@
self.readwrite_analyzer = ReadWriteAnalyzer(translator)
self.virtualizable_analyzer = VirtualizableAnalyzer(translator)
self.quasiimmut_analyzer = QuasiImmutAnalyzer(translator)
+ self.canreleasegil_analyzer = CanReleaseGILAnalyzer(translator)
#
for index, jd in enumerate(jitdrivers_sd):
jd.index = index
@@ -219,7 +219,9 @@
assert not NON_VOID_ARGS, ("arguments not supported for "
"loop-invariant function!")
# build the extraeffect
- can_invalidate = self.quasiimmut_analyzer.analyze(op)
+ can_release_gil = self.canreleasegil_analyzer.analyze(op)
+ # can_release_gil implies can_invalidate
+ can_invalidate = can_release_gil or self.quasiimmut_analyzer.analyze(op)
if extraeffect is None:
if self.virtualizable_analyzer.analyze(op):
extraeffect = EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE
@@ -235,7 +237,7 @@
#
effectinfo = effectinfo_from_writeanalyze(
self.readwrite_analyzer.analyze(op), self.cpu, extraeffect,
- oopspecindex, can_invalidate)
+ oopspecindex, can_invalidate, can_release_gil)
#
if oopspecindex != EffectInfo.OS_NONE:
assert effectinfo is not None
diff --git a/pypy/jit/codewriter/effectinfo.py b/pypy/jit/codewriter/effectinfo.py
--- a/pypy/jit/codewriter/effectinfo.py
+++ b/pypy/jit/codewriter/effectinfo.py
@@ -79,13 +79,15 @@
write_descrs_fields, write_descrs_arrays,
extraeffect=EF_CAN_RAISE,
oopspecindex=OS_NONE,
- can_invalidate=False):
+ can_invalidate=False, can_release_gil=False):
key = (frozenset(readonly_descrs_fields),
frozenset(readonly_descrs_arrays),
frozenset(write_descrs_fields),
frozenset(write_descrs_arrays),
extraeffect,
- oopspecindex)
+ oopspecindex,
+ can_invalidate,
+ can_release_gil)
if key in cls._cache:
return cls._cache[key]
result = object.__new__(cls)
@@ -100,6 +102,7 @@
result.write_descrs_arrays = write_descrs_arrays
result.extraeffect = extraeffect
result.can_invalidate = can_invalidate
+ result.can_release_gil = can_release_gil
result.oopspecindex = oopspecindex
cls._cache[key] = result
return result
@@ -111,12 +114,13 @@
return self.extraeffect >= self.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE
def has_random_effects(self):
- return self.oopspecindex == self.OS_LIBFFI_CALL
+ return self.oopspecindex == self.OS_LIBFFI_CALL or self.can_release_gil
def effectinfo_from_writeanalyze(effects, cpu,
extraeffect=EffectInfo.EF_CAN_RAISE,
oopspecindex=EffectInfo.OS_NONE,
- can_invalidate=False):
+ can_invalidate=False,
+ can_release_gil=False):
from pypy.translator.backendopt.writeanalyze import top_set
if effects is top_set:
return None
@@ -158,7 +162,8 @@
write_descrs_arrays,
extraeffect,
oopspecindex,
- can_invalidate)
+ can_invalidate,
+ can_release_gil)
def consider_struct(TYPE, fieldname):
if fieldType(TYPE, fieldname) is lltype.Void:
@@ -194,6 +199,16 @@
def analyze_simple_operation(self, op, graphinfo):
return op.opname == 'jit_force_quasi_immutable'
+class CanReleaseGILAnalyzer(BoolGraphAnalyzer):
+ def analyze_direct_call(self, graph, seen=None):
+ releases_gil = False
+ if hasattr(graph, "func") and hasattr(graph.func, "_ptr"):
+ releases_gil = graph.func._ptr._obj.releases_gil
+ return releases_gil or super(CanReleaseGILAnalyzer, self).analyze_direct_call(graph, seen)
+
+ def analyze_simple_operation(self, op, graphinfo):
+ return False
+
# ____________________________________________________________
class CallInfoCollection(object):
diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -788,13 +788,65 @@
raise NotImplementedError("cast_ptr_to_int")
def rewrite_op_force_cast(self, op):
- from pypy.rpython.lltypesystem.rffi import size_and_sign, sizeof
+ assert not self._is_gc(op.args[0])
+ fromll = longlong.is_longlong(op.args[0].concretetype)
+ toll = longlong.is_longlong(op.result.concretetype)
+ if fromll and toll:
+ return
+ if fromll:
+ args = op.args
+ opname = 'truncate_longlong_to_int'
+ RESULT = lltype.Signed
+ v = varoftype(RESULT)
+ op1 = SpaceOperation(opname, args, v)
+ op2 = self.rewrite_operation(op1)
+ oplist = self.force_cast_without_longlong(op2.result, op.result)
+ if oplist:
+ return [op2] + oplist
+ #
+ # force a renaming to put the correct result in place, even though
+ # it might be slightly mistyped (e.g. Signed versus Unsigned)
+ assert op2.result is v
+ op2.result = op.result
+ return op2
+ elif toll:
+ from pypy.rpython.lltypesystem import rffi
+ size, unsigned = rffi.size_and_sign(op.args[0].concretetype)
+ if unsigned:
+ INTERMEDIATE = lltype.Unsigned
+ else:
+ INTERMEDIATE = lltype.Signed
+ v = varoftype(INTERMEDIATE)
+ oplist = self.force_cast_without_longlong(op.args[0], v)
+ if not oplist:
+ v = op.args[0]
+ oplist = []
+ if unsigned:
+ opname = 'cast_uint_to_longlong'
+ else:
+ opname = 'cast_int_to_longlong'
+ op1 = SpaceOperation(opname, [v], op.result)
+ op2 = self.rewrite_operation(op1)
+ return oplist + [op2]
+ else:
+ return self.force_cast_without_longlong(op.args[0], op.result)
+
+ def force_cast_without_longlong(self, v_arg, v_result):
+ from pypy.rpython.lltypesystem.rffi import size_and_sign, sizeof, FLOAT
from pypy.rlib.rarithmetic import intmask
- assert not self._is_gc(op.args[0])
- size2, unsigned2 = size_and_sign(op.result.concretetype)
- if size2 >= sizeof(lltype.Signed):
+ #
+ if (v_result.concretetype in (FLOAT, lltype.Float) or
+ v_arg.concretetype in (FLOAT, lltype.Float)):
+ assert (v_result.concretetype == lltype.Float and
+ v_arg.concretetype == lltype.Float), "xxx unsupported cast"
+ return
+ #
+ size2, unsigned2 = size_and_sign(v_result.concretetype)
+ assert size2 <= sizeof(lltype.Signed)
+ if size2 == sizeof(lltype.Signed):
return # the target type is LONG or ULONG
- size1, unsigned1 = size_and_sign(op.args[0].concretetype)
+ size1, unsigned1 = size_and_sign(v_arg.concretetype)
+ assert size1 <= sizeof(lltype.Signed)
#
def bounds(size, unsigned):
if unsigned:
@@ -807,22 +859,28 @@
return # the target type includes the source range
#
result = []
- v1 = op.args[0]
if min2:
c_min2 = Constant(min2, lltype.Signed)
- v2 = Variable(); v2.concretetype = lltype.Signed
- result.append(SpaceOperation('int_sub', [v1, c_min2], v2))
+ v2 = varoftype(lltype.Signed)
+ result.append(SpaceOperation('int_sub', [v_arg, c_min2], v2))
else:
- v2 = v1
+ v2 = v_arg
c_mask = Constant(int((1<<(8*size2))-1), lltype.Signed)
- v3 = Variable(); v3.concretetype = lltype.Signed
+ v3 = varoftype(lltype.Signed)
result.append(SpaceOperation('int_and', [v2, c_mask], v3))
if min2:
- result.append(SpaceOperation('int_add', [v3, c_min2], op.result))
+ result.append(SpaceOperation('int_add', [v3, c_min2], v_result))
else:
- result[-1].result = op.result
+ result[-1].result = v_result
return result
+ def rewrite_op_direct_ptradd(self, op):
+ from pypy.rpython.lltypesystem import rffi
+ # xxx otherwise, not implemented:
+ assert op.args[0].concretetype == rffi.CCHARP
+ #
+ return SpaceOperation('int_add', [op.args[0], op.args[1]], op.result)
+
# ----------
# Long longs, for 32-bit only. Supported operations are left unmodified,
# and unsupported ones are turned into a call to a function from
@@ -906,30 +964,7 @@
rewrite_op_ullong_is_true = rewrite_op_llong_is_true
def rewrite_op_cast_primitive(self, op):
- fromll = longlong.is_longlong(op.args[0].concretetype)
- toll = longlong.is_longlong(op.result.concretetype)
- if fromll != toll:
- args = op.args
- if fromll:
- opname = 'truncate_longlong_to_int'
- RESULT = lltype.Signed
- else:
- from pypy.rpython.lltypesystem import rffi
- if rffi.cast(op.args[0].concretetype, -1) < 0:
- opname = 'cast_int_to_longlong'
- else:
- opname = 'cast_uint_to_longlong'
- RESULT = lltype.SignedLongLong
- v = varoftype(RESULT)
- op1 = SpaceOperation(opname, args, v)
- op2 = self.rewrite_operation(op1)
- #
- # force a renaming to put the correct result in place, even though
- # it might be slightly mistyped (e.g. Signed versus Unsigned)
- assert op2.result is v
- op2.result = op.result
- #
- return op2
+ return self.rewrite_op_force_cast(op)
# ----------
# Renames, from the _old opname to the _new one.
@@ -1106,6 +1141,9 @@
return meth(op, args, *descrs)
def _get_list_nonneg_canraise_flags(self, op):
+ # XXX as far as I can see, this function will always return True
+ # because functions that are neither nonneg nor fast don't have an
+ # oopspec any more
# xxx break of abstraction:
func = get_funcobj(op.args[0].value)._callable
# base hints on the name of the ll function, which is a bit xxx-ish
@@ -1263,7 +1301,7 @@
calldescr = self.callcontrol.getcalldescr(op, oopspecindex,
extraeffect)
if extraeffect is not None:
- assert (type(calldescr) is str # for tests
+ assert (is_test_calldescr(calldescr) # for tests
or calldescr.get_extra_info().extraeffect == extraeffect)
if isinstance(op.args[0].value, str):
pass # for tests only
@@ -1424,6 +1462,9 @@
return "using virtualizable array in illegal way in %r" % (
self.args[0],)
+def is_test_calldescr(calldescr):
+ return type(calldescr) is str or getattr(calldescr, '_for_tests_only', False)
+
def _with_prefix(prefix):
result = {}
for name in dir(Transformer):
diff --git a/pypy/jit/codewriter/regalloc.py b/pypy/jit/codewriter/regalloc.py
--- a/pypy/jit/codewriter/regalloc.py
+++ b/pypy/jit/codewriter/regalloc.py
@@ -96,6 +96,7 @@
def _try_coalesce(self, v, w):
if isinstance(v, Variable) and getkind(v.concretetype) == self.kind:
+ assert getkind(w.concretetype) == self.kind
dg = self._depgraph
uf = self._unionfind
v0 = uf.find_rep(v)
diff --git a/pypy/jit/codewriter/support.py b/pypy/jit/codewriter/support.py
--- a/pypy/jit/codewriter/support.py
+++ b/pypy/jit/codewriter/support.py
@@ -390,7 +390,7 @@
('int_lshift_ovf', [lltype.Signed, lltype.Signed], lltype.Signed),
('int_abs', [lltype.Signed], lltype.Signed),
('ll_math.ll_math_sqrt', [lltype.Float], lltype.Float),
- ]
+]
class LLtypeHelpers:
diff --git a/pypy/jit/codewriter/test/test_call.py b/pypy/jit/codewriter/test/test_call.py
--- a/pypy/jit/codewriter/test/test_call.py
+++ b/pypy/jit/codewriter/test/test_call.py
@@ -1,6 +1,6 @@
import py
from pypy.objspace.flow.model import SpaceOperation, Constant, Variable
-from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.lltypesystem import lltype, rffi
from pypy.translator.unsimplify import varoftype
from pypy.rlib import jit
from pypy.jit.codewriter.call import CallControl
@@ -171,3 +171,24 @@
def test_jit_force_virtualizable_effectinfo():
py.test.skip("XXX add a test for CallControl.getcalldescr() -> EF_xxx")
+
+def test_releases_gil_analyzer():
+ from pypy.jit.backend.llgraph.runner import LLtypeCPU
+
+ T = rffi.CArrayPtr(rffi.TIME_T)
+ external = rffi.llexternal("time", [T], rffi.TIME_T, threadsafe=True)
+
+ @jit.dont_look_inside
+ def f():
+ return external(lltype.nullptr(T.TO))
+
+ rtyper = support.annotate(f, [])
+ jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
+ cc = CallControl(LLtypeCPU(rtyper), jitdrivers_sd=[jitdriver_sd])
+ res = cc.find_all_graphs(FakePolicy())
+
+ [f_graph] = [x for x in res if x.func is f]
+ [block, _] = list(f_graph.iterblocks())
+ [op] = block.operations
+ call_descr = cc.getcalldescr(op)
+ assert call_descr.extrainfo.can_release_gil
\ No newline at end of file
diff --git a/pypy/jit/codewriter/test/test_flatten.py b/pypy/jit/codewriter/test/test_flatten.py
--- a/pypy/jit/codewriter/test/test_flatten.py
+++ b/pypy/jit/codewriter/test/test_flatten.py
@@ -3,6 +3,7 @@
from pypy.jit.codewriter.flatten import flatten_graph, reorder_renaming_list
from pypy.jit.codewriter.flatten import GraphFlattener, ListOfKind, Register
from pypy.jit.codewriter.format import assert_format
+from pypy.jit.codewriter import longlong
from pypy.jit.metainterp.history import AbstractDescr
from pypy.rpython.lltypesystem import lltype, rclass, rstr
from pypy.objspace.flow.model import SpaceOperation, Variable, Constant
@@ -30,6 +31,9 @@
'float': FakeRegAlloc()}
class FakeDescr(AbstractDescr):
+ _for_tests_only = True
+ def __init__(self, oopspecindex=None):
+ self.oopspecindex = oopspecindex
def __repr__(self):
return '<Descr>'
def as_vtable_size_descr(self):
@@ -55,19 +59,24 @@
def arraydescrof(self, ARRAY):
return FakeDescr()
+class FakeCallInfoCollection:
+ def add(self, *args):
+ pass
+
class FakeCallControl:
_descr_cannot_raise = FakeDescr()
+ callinfocollection = FakeCallInfoCollection()
def guess_call_kind(self, op):
return 'residual'
- def getcalldescr(self, op):
+ def getcalldescr(self, op, oopspecindex=None, extraeffect=None):
try:
if 'cannot_raise' in op.args[0].value._obj.graph.name:
return self._descr_cannot_raise
except AttributeError:
pass
- return FakeDescr()
+ return FakeDescr(oopspecindex)
def calldescr_canraise(self, calldescr):
- return calldescr is not self._descr_cannot_raise
+ return calldescr is not self._descr_cannot_raise and calldescr.oopspecindex is None
def get_vinfo(self, VTYPEPTR):
return None
@@ -734,7 +743,9 @@
def test_force_cast(self):
from pypy.rpython.lltypesystem import rffi
-
+ # NB: we don't need to test for INT here, the logic in jtransform is
+ # general enough so that if we have the below cases it should
+ # generalize also to INT
for FROM, TO, expected in [
(rffi.SIGNEDCHAR, rffi.SIGNEDCHAR, ""),
(rffi.SIGNEDCHAR, rffi.UCHAR, "int_and %i0, $255 -> %i1"),
@@ -797,12 +808,42 @@
expected = [s.strip() for s in expected.splitlines()]
check_force_cast(FROM, TO, expected, 42)
check_force_cast(FROM, TO, expected, -42)
- expected.append('int_return %i' + str(len(expected)))
- expected = '\n'.join(expected)
+ returnvar = "%i" + str(len(expected))
+ expected.append('int_return ' + returnvar)
+ expectedstr = '\n'.join(expected)
#
def f(n):
return rffi.cast(TO, n)
- self.encoding_test(f, [rffi.cast(FROM, 42)], expected,
+ self.encoding_test(f, [rffi.cast(FROM, 42)], expectedstr,
+ transform=True)
+
+ if not longlong.is_64_bit:
+ if FROM in (rffi.LONG, rffi.ULONG):
+ if FROM == rffi.LONG:
+ FROM = rffi.LONGLONG
+ else:
+ FROM = rffi.ULONGLONG
+ expected.insert(0,
+ "residual_call_irf_i $<* fn llong_to_int>, <Descr>, I[], R[], F[%f0] -> %i0")
+ expectedstr = '\n'.join(expected)
+ self.encoding_test(f, [rffi.cast(FROM, 42)], expectedstr,
+ transform=True)
+ elif TO in (rffi.LONG, rffi.ULONG):
+ if TO == rffi.LONG:
+ TO = rffi.LONGLONG
+ else:
+ TO = rffi.ULONGLONG
+ if rffi.cast(FROM, -1) < 0:
+ fnname = "llong_from_int"
+ else:
+ fnname = "llong_from_uint"
+ expected.pop() # remove int_return
+ expected.append(
+ "residual_call_irf_f $<* fn %s>, <Descr>, I[%s], R[], F[] -> %%f0"
+ % (fnname, returnvar))
+ expected.append("float_return %f0")
+ expectedstr = '\n'.join(expected)
+ self.encoding_test(f, [rffi.cast(FROM, 42)], expectedstr,
transform=True)
def test_force_cast_pointer(self):
@@ -813,6 +854,23 @@
int_return %i0
""", transform=True)
+ def test_force_cast_float(self):
+ from pypy.rpython.lltypesystem import rffi
+ def f(n):
+ return rffi.cast(lltype.Float, n)
+ self.encoding_test(f, [12.456], """
+ float_return %f0
+ """, transform=True)
+
+ def test_direct_ptradd(self):
+ from pypy.rpython.lltypesystem import rffi
+ def f(p, n):
+ return lltype.direct_ptradd(p, n)
+ self.encoding_test(f, [lltype.nullptr(rffi.CCHARP.TO), 123], """
+ int_add %i0, %i1 -> %i2
+ int_return %i2
+ """, transform=True)
+
def check_force_cast(FROM, TO, operations, value):
"""Check that the test is correctly written..."""
diff --git a/pypy/jit/codewriter/test/test_longlong.py b/pypy/jit/codewriter/test/test_longlong.py
--- a/pypy/jit/codewriter/test/test_longlong.py
+++ b/pypy/jit/codewriter/test/test_longlong.py
@@ -37,7 +37,7 @@
class TestLongLong:
def setup_class(cls):
- if sys.maxint > 2147483647:
+ if longlong.is_64_bit:
py.test.skip("only for 32-bit platforms")
def do_check(self, opname, oopspecindex, ARGS, RESULT):
@@ -46,6 +46,8 @@
op = SpaceOperation(opname, vlist, v_result)
tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
op1 = tr.rewrite_operation(op)
+ if isinstance(op1, list):
+ [op1] = op1
#
def is_llf(TYPE):
return (TYPE == lltype.SignedLongLong or
@@ -196,6 +198,23 @@
for T2 in [lltype.Signed, lltype.Unsigned]:
self.do_check('cast_primitive', EffectInfo.OS_LLONG_TO_INT,
[T1], T2)
+ self.do_check('force_cast', EffectInfo.OS_LLONG_TO_INT,
+ [T1], T2)
+ if T2 == lltype.Signed:
+ expected = EffectInfo.OS_LLONG_FROM_INT
+ else:
+ expected = EffectInfo.OS_LLONG_FROM_UINT
+ self.do_check('cast_primitive', expected, [T2], T1)
+ self.do_check('force_cast', expected, [T2], T1)
+ #
+ for T1 in [lltype.SignedLongLong, lltype.UnsignedLongLong]:
+ for T2 in [lltype.SignedLongLong, lltype.UnsignedLongLong]:
+ vlist = [varoftype(T1)]
+ v_result = varoftype(T2)
+ op = SpaceOperation('force_cast', vlist, v_result)
+ tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
+ op1 = tr.rewrite_operation(op)
+ assert op1 is None
def test_constants(self):
for TYPE in [lltype.SignedLongLong, lltype.UnsignedLongLong]:
diff --git a/pypy/jit/metainterp/optimizeopt/__init__.py b/pypy/jit/metainterp/optimizeopt/__init__.py
--- a/pypy/jit/metainterp/optimizeopt/__init__.py
+++ b/pypy/jit/metainterp/optimizeopt/__init__.py
@@ -61,7 +61,6 @@
optimizations, unroll = build_opt_chain(metainterp_sd, enable_opts,
inline_short_preamble, retraced)
-
if unroll:
optimize_unroll(metainterp_sd, loop, optimizations)
else:
diff --git a/pypy/jit/metainterp/optimizeopt/heap.py b/pypy/jit/metainterp/optimizeopt/heap.py
--- a/pypy/jit/metainterp/optimizeopt/heap.py
+++ b/pypy/jit/metainterp/optimizeopt/heap.py
@@ -73,7 +73,7 @@
assert self._lazy_setfield is None
self._cached_fields[structvalue] = fieldvalue
- def force_lazy_setfield(self, optheap):
+ def force_lazy_setfield(self, optheap, can_cache=True):
op = self._lazy_setfield
if op is not None:
# This is the way _lazy_setfield is usually reset to None.
@@ -83,12 +83,16 @@
self._cached_fields.clear()
self._lazy_setfield = None
optheap.next_optimization.propagate_forward(op)
+ if not can_cache:
+ return
# Once it is done, we can put at least one piece of information
# back in the cache: the value of this particular structure's
# field.
structvalue = optheap.getvalue(op.getarg(0))
fieldvalue = optheap.getvalue(op.getarglist()[-1])
self.remember_field_value(structvalue, fieldvalue)
+ elif not can_cache:
+ self._cached_fields.clear()
def get_reconstructed(self, optimizer, valuemap):
assert self._lazy_setfield is None
@@ -202,20 +206,9 @@
for arraydescr in effectinfo.readonly_descrs_arrays:
self.force_lazy_setarrayitem(arraydescr)
for fielddescr in effectinfo.write_descrs_fields:
- self.force_lazy_setfield(fielddescr)
- try:
- cf = self.cached_fields[fielddescr]
- cf._cached_fields.clear()
- except KeyError:
- pass
+ self.force_lazy_setfield(fielddescr, can_cache=False)
for arraydescr in effectinfo.write_descrs_arrays:
- self.force_lazy_setarrayitem(arraydescr)
- try:
- submap = self.cached_arrayitems[arraydescr]
- for cf in submap.itervalues():
- cf._cached_fields.clear()
- except KeyError:
- pass
+ self.force_lazy_setarrayitem(arraydescr, can_cache=False)
if effectinfo.check_forces_virtual_or_virtualizable():
vrefinfo = self.optimizer.metainterp_sd.virtualref_info
self.force_lazy_setfield(vrefinfo.descr_forced)
@@ -238,20 +231,20 @@
if value in cf._cached_fields:
cf._cached_fields[newvalue] = cf._cached_fields[value]
- def force_lazy_setfield(self, descr):
+ def force_lazy_setfield(self, descr, can_cache=True):
try:
cf = self.cached_fields[descr]
except KeyError:
return
- cf.force_lazy_setfield(self)
+ cf.force_lazy_setfield(self, can_cache)
- def force_lazy_setarrayitem(self, arraydescr):
+ def force_lazy_setarrayitem(self, arraydescr, can_cache=True):
try:
submap = self.cached_arrayitems[arraydescr]
except KeyError:
return
for cf in submap.values():
- cf.force_lazy_setfield(self)
+ cf.force_lazy_setfield(self, can_cache)
def fixup_guard_situation(self):
# hackish: reverse the order of the last two operations if it makes
@@ -387,7 +380,7 @@
cf.do_setfield(self, op)
else:
# variable index, so make sure the lazy setarrayitems are done
- self.force_lazy_setarrayitem(op.getdescr())
+ self.force_lazy_setarrayitem(op.getdescr(), can_cache=False)
# and then emit the operation
self.emit_operation(op)
diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py b/pypy/jit/metainterp/optimizeopt/rewrite.py
--- a/pypy/jit/metainterp/optimizeopt/rewrite.py
+++ b/pypy/jit/metainterp/optimizeopt/rewrite.py
@@ -199,6 +199,7 @@
))
return
self.emit_operation(op)
+ self.pure(rop.FLOAT_MUL, [arg2, arg1], op.result)
def optimize_FLOAT_NEG(self, op):
v1 = op.getarg(0)
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -1755,6 +1755,48 @@
"""
self.optimize_loop(ops, expected)
+ def test_duplicate_getarrayitem_after_setarrayitem_bug(self):
+ ops = """
+ [p0, i0, i1]
+ setarrayitem_gc(p0, 0, i0, descr=arraydescr)
+ i6 = int_add(i0, 1)
+ setarrayitem_gc(p0, i1, i6, descr=arraydescr)
+ i10 = getarrayitem_gc(p0, 0, descr=arraydescr)
+ i11 = int_add(i10, i0)
+ jump(p0, i11, i1)
+ """
+ expected = """
+ [p0, i0, i1]
+ i6 = int_add(i0, 1)
+ setarrayitem_gc(p0, 0, i0, descr=arraydescr)
+ setarrayitem_gc(p0, i1, i6, descr=arraydescr)
+ i10 = getarrayitem_gc(p0, 0, descr=arraydescr)
+ i11 = int_add(i10, i0)
+ jump(p0, i11, i1)
+ """
+ self.optimize_loop(ops, expected)
+
+ def test_duplicate_getarrayitem_after_setarrayitem_bug2(self):
+ ops = """
+ [p0, i0, i1]
+ i2 = getarrayitem_gc(p0, 0, descr=arraydescr)
+ i6 = int_add(i0, 1)
+ setarrayitem_gc(p0, i1, i6, descr=arraydescr)
+ i10 = getarrayitem_gc(p0, 0, descr=arraydescr)
+ i11 = int_add(i10, i2)
+ jump(p0, i11, i1)
+ """
+ expected = """
+ [p0, i0, i1]
+ i2 = getarrayitem_gc(p0, 0, descr=arraydescr)
+ i6 = int_add(i0, 1)
+ setarrayitem_gc(p0, i1, i6, descr=arraydescr)
+ i10 = getarrayitem_gc(p0, 0, descr=arraydescr)
+ i11 = int_add(i10, i2)
+ jump(p0, i11, i1)
+ """
+ self.optimize_loop(ops, expected)
+
def test_bug_1(self):
ops = """
[i0, p1]
@@ -3916,11 +3958,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p3 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p3, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p3, 0, i4, i5)
+ copystrcontent(p1, p3, 0, 0, i1)
+ copystrcontent(p2, p3, 0, i1, i2)
jump(p2, p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -3941,9 +3980,7 @@
p3 = newstr(i3)
strsetitem(p3, 0, i0)
strsetitem(p3, 1, i1)
- i4 = strlen(p2)
- i5 = int_add(2, i4) # will be killed by the backend
- copystrcontent(p2, p3, 0, 2, i4)
+ copystrcontent(p2, p3, 0, 2, i2)
jump(i1, i0, p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -3962,10 +3999,9 @@
i2 = strlen(p2)
i3 = int_add(i2, 2)
p3 = newstr(i3)
- i4 = strlen(p2)
- copystrcontent(p2, p3, 0, 0, i4)
- strsetitem(p3, i4, i0)
- i5 = int_add(i4, 1)
+ copystrcontent(p2, p3, 0, 0, i2)
+ strsetitem(p3, i2, i0)
+ i5 = int_add(i2, 1)
strsetitem(p3, i5, i1)
i6 = int_add(i5, 1) # will be killed by the backend
jump(i1, i0, p3)
@@ -3987,14 +4023,9 @@
i3 = strlen(p3)
i123 = int_add(i12, i3)
p5 = newstr(i123)
- i1b = strlen(p1)
- copystrcontent(p1, p5, 0, 0, i1b)
- i2b = strlen(p2)
- i12b = int_add(i1b, i2b)
- copystrcontent(p2, p5, 0, i1b, i2b)
- i3b = strlen(p3)
- i123b = int_add(i12b, i3b) # will be killed by the backend
- copystrcontent(p3, p5, 0, i12b, i3b)
+ copystrcontent(p1, p5, 0, 0, i1)
+ copystrcontent(p2, p5, 0, i1, i2)
+ copystrcontent(p3, p5, 0, i12, i3)
jump(p2, p3, p5)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -4010,10 +4041,8 @@
i2 = strlen(p2)
i3 = int_add(i2, 1)
p3 = newstr(i3)
- i4 = strlen(p2)
- copystrcontent(p2, p3, 0, 0, i4)
- strsetitem(p3, i4, 120) # == ord('x')
- i5 = int_add(i4, 1) # will be killed by the backend
+ copystrcontent(p2, p3, 0, 0, i2)
+ strsetitem(p3, i2, 120) # == ord('x')
jump(p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -4131,9 +4160,7 @@
i5 = int_add(i3, i4)
p4 = newstr(i5)
copystrcontent(p1, p4, i1, 0, i3)
- i4b = strlen(p2)
- i6 = int_add(i3, i4b) # killed by the backend
- copystrcontent(p2, p4, 0, i3, i4b)
+ copystrcontent(p2, p4, 0, i3, i4)
jump(p4, i1, i2, p2)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -4178,11 +4205,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p4 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p4, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p4, 0, i4, i5)
+ copystrcontent(p1, p4, 0, 0, i1)
+ copystrcontent(p2, p4, 0, i1, i2)
i0 = call(0, p3, p4, descr=strequaldescr)
escape(i0)
jump(p1, p2, p3)
@@ -4374,11 +4398,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p4 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p4, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p4, 0, i4, i5)
+ copystrcontent(p1, p4, 0, 0, i1)
+ copystrcontent(p2, p4, 0, i1, i2)
i0 = call(0, s"hello world", p4, descr=streq_nonnull_descr)
escape(i0)
jump(p1, p2)
@@ -4532,6 +4553,39 @@
"""
self.optimize_loop(ops, expected)
+ def test_strslice_subtraction_folds(self):
+ ops = """
+ [p0, i0]
+ i1 = int_add(i0, 1)
+ p1 = call(0, p0, i0, i1, descr=strslicedescr)
+ escape(p1)
+ jump(p0, i1)
+ """
+ expected = """
+ [p0, i0]
+ i1 = int_add(i0, 1)
+ p1 = newstr(1)
+ i2 = strgetitem(p0, i0)
+ strsetitem(p1, 0, i2)
+ escape(p1)
+ jump(p0, i1)
+ """
+ self.optimize_strunicode_loop(ops, expected)
+
+ def test_float_mul_reversed(self):
+ ops = """
+ [f0, f1]
+ f2 = float_mul(f0, f1)
+ f3 = float_mul(f1, f0)
+ jump(f2, f3)
+ """
+ expected = """
+ [f0, f1]
+ f2 = float_mul(f0, f1)
+ jump(f2, f2)
+ """
+ self.optimize_loop(ops, expected)
+
class TestLLtype(BaseTestOptimizeBasic, LLtypeMixin):
pass
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -2820,11 +2820,11 @@
def test_residual_call_invalidate_some_arrays(self):
ops = """
[p1, p2, i1]
- p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p3 = getarrayitem_gc(p2, 0, descr=arraydescr2)
p4 = getarrayitem_gc(p2, 1, descr=arraydescr2)
i2 = getarrayitem_gc(p1, 1, descr=arraydescr)
i3 = call(i1, descr=writearraydescr)
- p5 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p5 = getarrayitem_gc(p2, 0, descr=arraydescr2)
p6 = getarrayitem_gc(p2, 1, descr=arraydescr2)
i4 = getarrayitem_gc(p1, 1, descr=arraydescr)
escape(p3)
@@ -2837,7 +2837,7 @@
"""
expected = """
[p1, p2, i1]
- p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p3 = getarrayitem_gc(p2, 0, descr=arraydescr2)
p4 = getarrayitem_gc(p2, 1, descr=arraydescr2)
i2 = getarrayitem_gc(p1, 1, descr=arraydescr)
i3 = call(i1, descr=writearraydescr)
@@ -5082,11 +5082,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p3 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p3, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p3, 0, i4, i5)
+ copystrcontent(p1, p3, 0, 0, i1)
+ copystrcontent(p2, p3, 0, i1, i2)
jump(p2, p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5107,9 +5104,7 @@
p3 = newstr(i3)
strsetitem(p3, 0, i0)
strsetitem(p3, 1, i1)
- i4 = strlen(p2)
- i5 = int_add(2, i4) # will be killed by the backend
- copystrcontent(p2, p3, 0, 2, i4)
+ copystrcontent(p2, p3, 0, 2, i2)
jump(i1, i0, p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5128,10 +5123,9 @@
i2 = strlen(p2)
i3 = int_add(i2, 2)
p3 = newstr(i3)
- i4 = strlen(p2)
- copystrcontent(p2, p3, 0, 0, i4)
- strsetitem(p3, i4, i0)
- i5 = int_add(i4, 1)
+ copystrcontent(p2, p3, 0, 0, i2)
+ strsetitem(p3, i2, i0)
+ i5 = int_add(i2, 1)
strsetitem(p3, i5, i1)
i6 = int_add(i5, 1) # will be killed by the backend
jump(i1, i0, p3)
@@ -5153,14 +5147,9 @@
i3 = strlen(p3)
i123 = int_add(i12, i3)
p5 = newstr(i123)
- i1b = strlen(p1)
- copystrcontent(p1, p5, 0, 0, i1b)
- i2b = strlen(p2)
- i12b = int_add(i1b, i2b)
- copystrcontent(p2, p5, 0, i1b, i2b)
- i3b = strlen(p3)
- i123b = int_add(i12b, i3b) # will be killed by the backend
- copystrcontent(p3, p5, 0, i12b, i3b)
+ copystrcontent(p1, p5, 0, 0, i1)
+ copystrcontent(p2, p5, 0, i1, i2)
+ copystrcontent(p3, p5, 0, i12, i3)
jump(p2, p3, p5)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5176,10 +5165,8 @@
i2 = strlen(p2)
i3 = int_add(i2, 1)
p3 = newstr(i3)
- i4 = strlen(p2)
- copystrcontent(p2, p3, 0, 0, i4)
- strsetitem(p3, i4, 120) # == ord('x')
- i5 = int_add(i4, 1) # will be killed by the backend
+ copystrcontent(p2, p3, 0, 0, i2)
+ strsetitem(p3, i2, 120) # == ord('x')
jump(p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5303,9 +5290,7 @@
i5 = int_add(i3, i4)
p4 = newstr(i5)
copystrcontent(p1, p4, i1, 0, i3)
- i4b = strlen(p2)
- i6 = int_add(i3, i4b) # killed by the backend
- copystrcontent(p2, p4, 0, i3, i4b)
+ copystrcontent(p2, p4, 0, i3, i4)
jump(p4, i1, i2, p2)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5411,11 +5396,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p4 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p4, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p4, 0, i4, i5)
+ copystrcontent(p1, p4, 0, 0, i1)
+ copystrcontent(p2, p4, 0, i1, i2)
i0 = call(0, p3, p4, descr=strequaldescr)
escape(i0)
jump(p1, p2, p3)
@@ -5609,11 +5591,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p4 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p4, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p4, 0, i4, i5)
+ copystrcontent(p1, p4, 0, 0, i1)
+ copystrcontent(p2, p4, 0, i1, i2)
i0 = call(0, s"hello world", p4, descr=streq_nonnull_descr)
escape(i0)
jump(p1, p2)
diff --git a/pypy/jit/metainterp/optimizeopt/util.py b/pypy/jit/metainterp/optimizeopt/util.py
--- a/pypy/jit/metainterp/optimizeopt/util.py
+++ b/pypy/jit/metainterp/optimizeopt/util.py
@@ -21,6 +21,7 @@
continue
if hasattr(Class, name_prefix + name):
opclass = resoperation.opclasses[getattr(rop, name)]
+ assert name in opclass.__name__
result.append((value, opclass, getattr(Class, name_prefix + name)))
return unrolling_iterable(result)
diff --git a/pypy/jit/metainterp/optimizeopt/vstring.py b/pypy/jit/metainterp/optimizeopt/vstring.py
--- a/pypy/jit/metainterp/optimizeopt/vstring.py
+++ b/pypy/jit/metainterp/optimizeopt/vstring.py
@@ -61,7 +61,7 @@
self.ensure_nonnull()
box = self.force_box()
lengthbox = BoxInt()
- optimization.emit_operation(ResOperation(mode.STRLEN, [box], lengthbox))
+ optimization.optimize_default(ResOperation(mode.STRLEN, [box], lengthbox))
return lengthbox
@specialize.arg(1)
@@ -72,13 +72,13 @@
else:
return None
- def string_copy_parts(self, optimization, targetbox, offsetbox, mode):
+ def string_copy_parts(self, optimizer, targetbox, offsetbox, mode):
# Copies the pointer-to-string 'self' into the target string
# given by 'targetbox', at the specified offset. Returns the offset
# at the end of the copy.
- lengthbox = self.getstrlen(optimization, mode)
+ lengthbox = self.getstrlen(optimizer, mode)
srcbox = self.force_box()
- return copy_str_content(optimization, srcbox, targetbox,
+ return copy_str_content(optimizer, srcbox, targetbox,
CONST_0, offsetbox, lengthbox, mode)
@@ -335,7 +335,7 @@
if optimizer is None:
return None
resbox = BoxInt()
- optimizer.emit_operation(ResOperation(rop.INT_ADD, [box1, box2], resbox))
+ optimizer.optimize_default(ResOperation(rop.INT_ADD, [box1, box2], resbox))
return resbox
def _int_sub(optimizer, box1, box2):
@@ -345,10 +345,10 @@
if isinstance(box1, ConstInt):
return ConstInt(box1.value - box2.value)
resbox = BoxInt()
- optimizer.emit_operation(ResOperation(rop.INT_SUB, [box1, box2], resbox))
+ optimizer.optimize_default(ResOperation(rop.INT_SUB, [box1, box2], resbox))
return resbox
-def _strgetitem(optimization, strbox, indexbox, mode):
+def _strgetitem(optimizer, strbox, indexbox, mode):
if isinstance(strbox, ConstPtr) and isinstance(indexbox, ConstInt):
if mode is mode_string:
s = strbox.getref(lltype.Ptr(rstr.STR))
@@ -357,7 +357,7 @@
s = strbox.getref(lltype.Ptr(rstr.UNICODE))
return ConstInt(ord(s.chars[indexbox.getint()]))
resbox = BoxInt()
- optimization.emit_operation(ResOperation(mode.STRGETITEM, [strbox, indexbox],
+ optimizer.optimize_default(ResOperation(mode.STRGETITEM, [strbox, indexbox],
resbox))
return resbox
@@ -440,7 +440,7 @@
if vindex.is_constant():
return value.getitem(vindex.box.getint())
#
- resbox = _strgetitem(self, value.force_box(), vindex.force_box(), mode)
+ resbox = _strgetitem(self.optimizer, value.force_box(), vindex.force_box(), mode)
return self.getvalue(resbox)
def optimize_STRLEN(self, op):
@@ -450,7 +450,7 @@
def _optimize_STRLEN(self, op, mode):
value = self.getvalue(op.getarg(0))
- lengthbox = value.getstrlen(self, mode)
+ lengthbox = value.getstrlen(self.optimizer, mode)
self.make_equal_to(op.result, self.getvalue(lengthbox))
def optimize_CALL(self, op):
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -391,8 +391,21 @@
@arguments("box", "descr", "box")
def _opimpl_getarrayitem_gc_any(self, arraybox, arraydescr, indexbox):
- return self.execute_with_descr(rop.GETARRAYITEM_GC,
+ cache = self.metainterp.heap_array_cache.get(arraydescr, None)
+ if cache and isinstance(indexbox, ConstInt):
+ index = indexbox.getint()
+ frombox, tobox = cache.get(index, (None, None))
+ if frombox is arraybox:
+ return tobox
+ resbox = self.execute_with_descr(rop.GETARRAYITEM_GC,
arraydescr, arraybox, indexbox)
+ if isinstance(indexbox, ConstInt):
+ if not cache:
+ cache = self.metainterp.heap_array_cache[arraydescr] = {}
+ index = indexbox.getint()
+ cache[index] = arraybox, resbox
+ return resbox
+
opimpl_getarrayitem_gc_i = _opimpl_getarrayitem_gc_any
opimpl_getarrayitem_gc_r = _opimpl_getarrayitem_gc_any
@@ -420,6 +433,13 @@
indexbox, itembox):
self.execute_with_descr(rop.SETARRAYITEM_GC, arraydescr, arraybox,
indexbox, itembox)
+ if isinstance(indexbox, ConstInt):
+ cache = self.metainterp.heap_array_cache.setdefault(arraydescr, {})
+ cache[indexbox.getint()] = arraybox, itembox
+ else:
+ cache = self.metainterp.heap_array_cache.get(arraydescr, None)
+ if cache:
+ cache.clear()
opimpl_setarrayitem_gc_i = _opimpl_setarrayitem_gc_any
opimpl_setarrayitem_gc_r = _opimpl_setarrayitem_gc_any
@@ -455,21 +475,17 @@
def opimpl_newlist(self, structdescr, lengthdescr, itemsdescr, arraydescr,
sizebox):
sbox = self.metainterp.execute_and_record(rop.NEW, structdescr)
- self.metainterp.execute_and_record(rop.SETFIELD_GC, lengthdescr,
- sbox, sizebox)
+ self._opimpl_setfield_gc_any(sbox, lengthdescr, sizebox)
abox = self.metainterp.execute_and_record(rop.NEW_ARRAY, arraydescr,
sizebox)
- self.metainterp.execute_and_record(rop.SETFIELD_GC, itemsdescr,
- sbox, abox)
+ self._opimpl_setfield_gc_any(sbox, itemsdescr, abox)
return sbox
@arguments("box", "descr", "descr", "box")
def _opimpl_getlistitem_gc_any(self, listbox, itemsdescr, arraydescr,
indexbox):
- arraybox = self.metainterp.execute_and_record(rop.GETFIELD_GC,
- itemsdescr, listbox)
- return self.execute_with_descr(rop.GETARRAYITEM_GC,
- arraydescr, arraybox, indexbox)
+ arraybox = self._opimpl_getfield_gc_any(listbox, itemsdescr)
+ return self._opimpl_getarrayitem_gc_any(arraybox, arraydescr, indexbox)
opimpl_getlistitem_gc_i = _opimpl_getlistitem_gc_any
opimpl_getlistitem_gc_r = _opimpl_getlistitem_gc_any
@@ -478,10 +494,9 @@
@arguments("box", "descr", "descr", "box", "box")
def _opimpl_setlistitem_gc_any(self, listbox, itemsdescr, arraydescr,
indexbox, valuebox):
- arraybox = self.metainterp.execute_and_record(rop.GETFIELD_GC,
- itemsdescr, listbox)
- self.execute_with_descr(rop.SETARRAYITEM_GC, arraydescr, arraybox,
- indexbox, valuebox)
+ arraybox = self._opimpl_getfield_gc_any(listbox, itemsdescr)
+ self._opimpl_setarrayitem_gc_any(arraybox, arraydescr, indexbox,
+ valuebox)
opimpl_setlistitem_gc_i = _opimpl_setlistitem_gc_any
opimpl_setlistitem_gc_r = _opimpl_setlistitem_gc_any
@@ -503,14 +518,16 @@
@arguments("box", "descr")
def _opimpl_getfield_gc_any(self, box, fielddescr):
- return self.execute_with_descr(rop.GETFIELD_GC, fielddescr, box)
+ return self._opimpl_getfield_gc_any_pureornot(
+ rop.GETFIELD_GC, box, fielddescr)
opimpl_getfield_gc_i = _opimpl_getfield_gc_any
opimpl_getfield_gc_r = _opimpl_getfield_gc_any
opimpl_getfield_gc_f = _opimpl_getfield_gc_any
@arguments("box", "descr")
def _opimpl_getfield_gc_pure_any(self, box, fielddescr):
- return self.execute_with_descr(rop.GETFIELD_GC_PURE, fielddescr, box)
+ return self._opimpl_getfield_gc_any_pureornot(
+ rop.GETFIELD_GC_PURE, box, fielddescr)
opimpl_getfield_gc_i_pure = _opimpl_getfield_gc_pure_any
opimpl_getfield_gc_r_pure = _opimpl_getfield_gc_pure_any
opimpl_getfield_gc_f_pure = _opimpl_getfield_gc_pure_any
@@ -523,6 +540,15 @@
opimpl_getinteriorfield_gc_f = _opimpl_getinteriorfield_gc_any
opimpl_getinteriorfield_gc_r = _opimpl_getinteriorfield_gc_any
+ @specialize.arg(1)
+ def _opimpl_getfield_gc_any_pureornot(self, opnum, box, fielddescr):
+ frombox, tobox = self.metainterp.heap_cache.get(fielddescr, (None, None))
+ if frombox is box:
+ return tobox
+ resbox = self.execute_with_descr(opnum, fielddescr, box)
+ self.metainterp.heap_cache[fielddescr] = (box, resbox)
+ return resbox
+
@arguments("orgpc", "box", "descr")
def _opimpl_getfield_gc_greenfield_any(self, pc, box, fielddescr):
ginfo = self.metainterp.jitdriver_sd.greenfield_info
@@ -541,7 +567,11 @@
@arguments("box", "descr", "box")
def _opimpl_setfield_gc_any(self, box, fielddescr, valuebox):
+ frombox, tobox = self.metainterp.heap_cache.get(fielddescr, (None, None))
+ if frombox is box and tobox is valuebox:
+ return
self.execute_with_descr(rop.SETFIELD_GC, fielddescr, box, valuebox)
+ self.metainterp.heap_cache[fielddescr] = (box, valuebox)
opimpl_setfield_gc_i = _opimpl_setfield_gc_any
opimpl_setfield_gc_r = _opimpl_setfield_gc_any
opimpl_setfield_gc_f = _opimpl_setfield_gc_any
@@ -614,12 +644,16 @@
standard_box = self.metainterp.virtualizable_boxes[-1]
if standard_box is box:
return False
+ if box in self.metainterp.nonstandard_virtualizables:
+ return True
eqbox = self.metainterp.execute_and_record(rop.PTR_EQ, None,
box, standard_box)
eqbox = self.implement_guard_value(pc, eqbox)
isstandard = eqbox.getint()
if isstandard:
self.metainterp.replace_box(box, standard_box)
+ else:
+ self.metainterp.nonstandard_virtualizables[box] = None
return not isstandard
def _get_virtualizable_field_index(self, fielddescr):
@@ -631,7 +665,7 @@
@arguments("orgpc", "box", "descr")
def _opimpl_getfield_vable(self, pc, box, fielddescr):
if self._nonstandard_virtualizable(pc, box):
- return self.execute_with_descr(rop.GETFIELD_GC, fielddescr, box)
+ return self._opimpl_getfield_gc_any(box, fielddescr)
self.metainterp.check_synchronized_virtualizable()
index = self._get_virtualizable_field_index(fielddescr)
return self.metainterp.virtualizable_boxes[index]
@@ -643,8 +677,7 @@
@arguments("orgpc", "box", "descr", "box")
def _opimpl_setfield_vable(self, pc, box, fielddescr, valuebox):
if self._nonstandard_virtualizable(pc, box):
- self.execute_with_descr(rop.SETFIELD_GC, fielddescr, box, valuebox)
- return
+ return self._opimpl_setfield_gc_any(box, fielddescr, valuebox)
index = self._get_virtualizable_field_index(fielddescr)
self.metainterp.virtualizable_boxes[index] = valuebox
self.metainterp.synchronize_virtualizable()
@@ -674,10 +707,8 @@
@arguments("orgpc", "box", "descr", "descr", "box")
def _opimpl_getarrayitem_vable(self, pc, box, fdescr, adescr, indexbox):
if self._nonstandard_virtualizable(pc, box):
- arraybox = self.metainterp.execute_and_record(rop.GETFIELD_GC,
- fdescr, box)
- return self.execute_with_descr(rop.GETARRAYITEM_GC, adescr,
- arraybox, indexbox)
+ arraybox = self._opimpl_getfield_gc_any(box, fdescr)
+ return self._opimpl_getarrayitem_gc_any(arraybox, adescr, indexbox)
self.metainterp.check_synchronized_virtualizable()
index = self._get_arrayitem_vable_index(pc, fdescr, indexbox)
return self.metainterp.virtualizable_boxes[index]
@@ -690,10 +721,9 @@
def _opimpl_setarrayitem_vable(self, pc, box, fdescr, adescr, indexbox,
valuebox):
if self._nonstandard_virtualizable(pc, box):
- arraybox = self.metainterp.execute_and_record(rop.GETFIELD_GC,
- fdescr, box)
- self.execute_with_descr(rop.SETARRAYITEM_GC, adescr,
- arraybox, indexbox, valuebox)
+ arraybox = self._opimpl_getfield_gc_any(box, fdescr)
+ self._opimpl_setarrayitem_gc_any(arraybox, adescr,
+ indexbox, valuebox)
return
index = self._get_arrayitem_vable_index(pc, fdescr, indexbox)
self.metainterp.virtualizable_boxes[index] = valuebox
@@ -707,8 +737,7 @@
@arguments("orgpc", "box", "descr", "descr")
def opimpl_arraylen_vable(self, pc, box, fdescr, adescr):
if self._nonstandard_virtualizable(pc, box):
- arraybox = self.metainterp.execute_and_record(rop.GETFIELD_GC,
- fdescr, box)
+ arraybox = self._opimpl_getfield_gc_any(box, fdescr)
return self.execute_with_descr(rop.ARRAYLEN_GC, adescr, arraybox)
vinfo = self.metainterp.jitdriver_sd.virtualizable_info
virtualizable_box = self.metainterp.virtualizable_boxes[-1]
@@ -1474,6 +1503,14 @@
self.call_pure_results = args_dict_box()
# contains boxes where the class is already known
self.known_class_boxes = {}
+ # contains frame boxes that are not virtualizables
+ self.nonstandard_virtualizables = {}
+ # heap cache
+ # maps descrs to (from_box, to_box) tuples
+ self.heap_cache = {}
+ # heap array cache
+ # maps descrs to {index: (from_box, to_box)} dicts
+ self.heap_array_cache = {}
def perform_call(self, jitcode, boxes, greenkey=None):
# causes the metainterp to enter the given subfunction
@@ -1649,10 +1686,27 @@
# record the operation
profiler = self.staticdata.profiler
profiler.count_ops(opnum, RECORDED_OPS)
+ self._invalidate_caches(opnum, descr)
op = self.history.record(opnum, argboxes, resbox, descr)
self.attach_debug_info(op)
return resbox
+ def _invalidate_caches(self, opnum, descr):
+ if opnum == rop.SETFIELD_GC:
+ return
+ if opnum == rop.SETARRAYITEM_GC:
+ return
+ if rop._NOSIDEEFFECT_FIRST <= opnum <= rop._NOSIDEEFFECT_LAST:
+ return
+ if opnum == rop.CALL:
+ effectinfo = descr.get_extra_info()
+ if effectinfo.extraeffect == effectinfo.EF_ELIDABLE:
+ return
+ if self.heap_cache:
+ self.heap_cache.clear()
+ if self.heap_array_cache:
+ self.heap_array_cache.clear()
+
def attach_debug_info(self, op):
if (not we_are_translated() and op is not None
and getattr(self, 'framestack', None)):
@@ -1815,6 +1869,9 @@
def reached_loop_header(self, greenboxes, redboxes, resumedescr):
self.known_class_boxes = {}
+ self.nonstandard_virtualizables = {} # XXX maybe not needed?
+ self.heap_cache = {}
+ self.heap_array_cache = {}
duplicates = {}
self.remove_consts_and_duplicates(redboxes, len(redboxes),
@@ -2322,6 +2379,16 @@
for i in range(len(boxes)):
if boxes[i] is oldbox:
boxes[i] = newbox
+ for descr, (frombox, tobox) in self.heap_cache.iteritems():
+ change = False
+ if frombox is oldbox:
+ change = True
+ frombox = newbox
+ if tobox is oldbox:
+ change = True
+ tobox = newbox
+ if change:
+ self.heap_cache[descr] = frombox, tobox
def find_biggest_function(self):
start_stack = []
diff --git a/pypy/jit/metainterp/test/test_ajit.py b/pypy/jit/metainterp/test/test_ajit.py
--- a/pypy/jit/metainterp/test/test_ajit.py
+++ b/pypy/jit/metainterp/test/test_ajit.py
@@ -11,7 +11,7 @@
from pypy import conftest
from pypy.rlib.rarithmetic import ovfcheck
from pypy.jit.metainterp.typesystem import LLTypeHelper, OOTypeHelper
-from pypy.rpython.lltypesystem import lltype, llmemory
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
from pypy.rpython.ootypesystem import ootype
from pypy.jit.metainterp.optimizeopt import ALL_OPTS_DICT
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
@@ -984,11 +984,14 @@
pass
class B(A):
pass
+ @dont_look_inside
+ def extern(n):
+ if n:
+ return A()
+ else:
+ return B()
def fn(n):
- if n:
- obj = A()
- else:
- obj = B()
+ obj = extern(n)
return isinstance(obj, B)
res = self.interp_operations(fn, [0])
assert res
@@ -1021,68 +1024,6 @@
res = self.meta_interp(main, [])
assert res == 55
- def test_dont_record_repeated_guard_class(self):
- class A:
- pass
- class B(A):
- pass
- a = A()
- b = B()
- def fn(n):
- if n == -7:
- obj = None
- elif n:
- obj = a
- else:
- obj = b
- return isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B)
- res = self.interp_operations(fn, [0])
- assert res == 4
- self.check_operations_history(guard_class=1, guard_nonnull=1)
- res = self.interp_operations(fn, [1])
- assert not res
-
- def test_dont_record_guard_class_after_new(self):
- class A:
- pass
- class B(A):
- pass
- def fn(n):
- if n == -7:
- obj = None
- elif n:
- obj = A()
- else:
- obj = B()
- return isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B)
- res = self.interp_operations(fn, [0])
- assert res == 4
- self.check_operations_history(guard_class=0, guard_nonnull=0)
- res = self.interp_operations(fn, [1])
- assert not res
-
- def test_guard_isnull_nullifies(self):
- class A:
- pass
- a = A()
- a.x = None
- def fn(n):
- if n == -7:
- a.x = ""
- obj = a.x
- res = 0
- if not obj:
- res += 1
- if obj:
- res += 1
- if obj is None:
- res += 1
- if obj is not None:
- res += 1
- return res
- res = self.interp_operations(fn, [0])
- assert res == 2
- self.check_operations_history(guard_isnull=1)
def test_assert_isinstance(self):
class A:
@@ -1244,7 +1185,7 @@
return tup[1]
res = self.interp_operations(f, [3, 5])
assert res == 5
- self.check_operations_history(setfield_gc=2, getfield_gc_pure=1)
+ self.check_operations_history(setfield_gc=2, getfield_gc_pure=0)
def test_oosend_look_inside_only_one(self):
class A:
@@ -1612,8 +1553,6 @@
assert res == 1
def test_raw_malloc_and_access(self):
- from pypy.rpython.lltypesystem import rffi
-
TP = rffi.CArray(lltype.Signed)
def f(n):
@@ -1627,8 +1566,6 @@
assert res == 10
def test_raw_malloc_and_access_float(self):
- from pypy.rpython.lltypesystem import rffi
-
TP = rffi.CArray(lltype.Float)
def f(n, f):
@@ -2377,7 +2314,7 @@
assert res == -2
#self.check_loops(getarrayitem_gc=0, setarrayitem_gc=0) -- xxx?
- def test_retrace_ending_up_retrazing_another_loop(self):
+ def test_retrace_ending_up_retracing_another_loop(self):
myjitdriver = JitDriver(greens = ['pc'], reds = ['n', 'i', 'sa'])
bytecode = "0+sI0+SI"
@@ -2615,5 +2552,57 @@
self.meta_interp(f, [], enable_opts='')
self.check_loops(new_with_vtable=1)
+ def test_release_gil_flush_heap_cache(self):
+ T = rffi.CArrayPtr(rffi.TIME_T)
+
+ external = rffi.llexternal("time", [T], rffi.TIME_T, threadsafe=True)
+ # Not a real lock, has all the same properties with respect to GIL
+ # release though, so good for this test.
+ class Lock(object):
+ @dont_look_inside
+ def acquire(self):
+ external(lltype.nullptr(T.TO))
+ @dont_look_inside
+ def release(self):
+ external(lltype.nullptr(T.TO))
+ class X(object):
+ def __init__(self, idx):
+ self.field = idx
+ @dont_look_inside
+ def get_obj(z):
+ return X(z)
+ myjitdriver = JitDriver(greens=[], reds=["n", "l", "z", "lock"])
+ def f(n, z):
+ lock = Lock()
+ l = 0
+ while n > 0:
+ myjitdriver.jit_merge_point(lock=lock, l=l, n=n, z=z)
+ x = get_obj(z)
+ l += x.field
+ lock.acquire()
+ # This must not reuse the previous one.
+ n -= x.field
+ lock.release()
+ return n
+ res = self.meta_interp(f, [10, 1])
+ self.check_loops(getfield_gc=2)
+ assert res == f(10, 1)
+
+ def test_jit_merge_point_with_raw_pointer(self):
+ driver = JitDriver(greens = [], reds = ['n', 'x'])
+
+ TP = lltype.Array(lltype.Signed, hints={'nolength': True})
+
+ def f(n):
+ x = lltype.malloc(TP, 10, flavor='raw')
+ x[0] = 1
+ while n > 0:
+ driver.jit_merge_point(n=n, x=x)
+ n -= x[0]
+ lltype.free(x, flavor='raw')
+ return n
+
+ self.meta_interp(f, [10], repeat=3)
+
class TestLLtype(BaseLLtypeTests, LLJitMixin):
pass
diff --git a/pypy/jit/metainterp/test/test_immutable.py b/pypy/jit/metainterp/test/test_immutable.py
--- a/pypy/jit/metainterp/test/test_immutable.py
+++ b/pypy/jit/metainterp/test/test_immutable.py
@@ -1,5 +1,9 @@
+from pypy.rlib import jit
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
+ at jit.dont_look_inside
+def escape(x):
+ return x
class ImmutableFieldsTests:
@@ -11,7 +15,7 @@
self.x = x
def f(x):
- y = X(x)
+ y = escape(X(x))
return y.x + 5
res = self.interp_operations(f, [23])
assert res == 28
@@ -33,7 +37,7 @@
def f(x, y):
X(x) # force the field 'x' to be on class 'X'
- z = Y(x, y)
+ z = escape(Y(x, y))
return z.x + z.y + 5
res = self.interp_operations(f, [23, 11])
assert res == 39
@@ -42,7 +46,7 @@
def f(x, y):
# this time, the field 'x' only shows up on subclass 'Y'
- z = Y(x, y)
+ z = escape(Y(x, y))
return z.x + z.y + 5
res = self.interp_operations(f, [23, 11])
assert res == 39
@@ -58,7 +62,7 @@
def f(index):
l = [1, 2, 3, 4]
l[2] = 30
- a = X(l)
+ a = escape(X(l))
return a.y[index]
res = self.interp_operations(f, [2], listops=True)
assert res == 30
@@ -76,7 +80,7 @@
self.y = y
def f(x, index):
- y = X([x], x+1)
+ y = escape(X([x], x+1))
return y.lst[index] + y.y + 5
res = self.interp_operations(f, [23, 0], listops=True)
assert res == 23 + 24 + 5
diff --git a/pypy/jit/metainterp/test/test_tracingopts.py b/pypy/jit/metainterp/test/test_tracingopts.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/test/test_tracingopts.py
@@ -0,0 +1,407 @@
+import py
+import sys
+from pypy.rlib import jit
+from pypy.jit.metainterp.test.support import LLJitMixin
+
+
+class TestLLtype(LLJitMixin):
+ def test_dont_record_repeated_guard_class(self):
+ class A:
+ pass
+ class B(A):
+ pass
+ @jit.dont_look_inside
+ def extern(n):
+ if n == -7:
+ return None
+ elif n:
+ return A()
+ else:
+ return B()
+ def fn(n):
+ obj = extern(n)
+ return isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B)
+ res = self.interp_operations(fn, [0])
+ assert res == 4
+ self.check_operations_history(guard_class=1, guard_nonnull=1)
+ res = self.interp_operations(fn, [1])
+ assert not res
+
+ def test_dont_record_guard_class_after_new(self):
+ class A:
+ pass
+ class B(A):
+ pass
+ def fn(n):
+ if n == -7:
+ obj = None
+ elif n:
+ obj = A()
+ else:
+ obj = B()
+ return isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B)
+ res = self.interp_operations(fn, [0])
+ assert res == 4
+ self.check_operations_history(guard_class=0, guard_nonnull=0)
+ res = self.interp_operations(fn, [1])
+ assert not res
+
+ def test_guard_isnull_nullifies(self):
+ class A:
+ pass
+ a = A()
+ a.x = None
+ def fn(n):
+ if n == -7:
+ a.x = ""
+ obj = a.x
+ res = 0
+ if not obj:
+ res += 1
+ if obj:
+ res += 1
+ if obj is None:
+ res += 1
+ if obj is not None:
+ res += 1
+ return res
+ res = self.interp_operations(fn, [0])
+ assert res == 2
+ self.check_operations_history(guard_isnull=1)
+
+ def test_heap_caching_while_tracing(self):
+ class A:
+ pass
+ a1 = A()
+ a2 = A()
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a.x = n
+ return a.x
+ res = self.interp_operations(fn, [7])
+ assert res == 7
+ self.check_operations_history(getfield_gc=0)
+ res = self.interp_operations(fn, [-7])
+ assert res == -7
+ self.check_operations_history(getfield_gc=0)
+
+ def fn(n, ca, cb):
+ a1.x = n
+ a2.x = n
+ a = a1
+ if ca:
+ a = a2
+ b = a1
+ if cb:
+ b = a
+ return a.x + b.x
+ res = self.interp_operations(fn, [7, 0, 1])
+ assert res == 7 * 2
+ self.check_operations_history(getfield_gc=1)
+ res = self.interp_operations(fn, [-7, 1, 1])
+ assert res == -7 * 2
+ self.check_operations_history(getfield_gc=1)
+
+ def test_heap_caching_while_tracing_invalidation(self):
+ class A:
+ pass
+ a1 = A()
+ a2 = A()
+ @jit.dont_look_inside
+ def f(a):
+ a.x = 5
+ l = [1]
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a.x = n
+ x1 = a.x
+ f(a)
+ x2 = a.x
+ l[0] = x2
+ return a.x + x1 + x2
+ res = self.interp_operations(fn, [7])
+ assert res == 5 * 2 + 7
+ self.check_operations_history(getfield_gc=1)
+
+ def test_heap_caching_dont_store_same(self):
+ class A:
+ pass
+ a1 = A()
+ a2 = A()
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a.x = n
+ a.x = n
+ return a.x
+ res = self.interp_operations(fn, [7])
+ assert res == 7
+ self.check_operations_history(getfield_gc=0, setfield_gc=1)
+ res = self.interp_operations(fn, [-7])
+ assert res == -7
+ self.check_operations_history(getfield_gc=0)
+
+ def test_array_caching(self):
+ a1 = [0, 0]
+ a2 = [0, 0]
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a[0] = n
+ x1 = a[0]
+ a[n - n] = n + 1
+ return a[0] + x1
+ res = self.interp_operations(fn, [7])
+ assert res == 7 + 7 + 1
+ self.check_operations_history(getarrayitem_gc=1)
+ res = self.interp_operations(fn, [-7])
+ assert res == -7 - 7 + 1
+ self.check_operations_history(getarrayitem_gc=1)
+
+ def fn(n, ca, cb):
+ a1[0] = n
+ a2[0] = n
+ a = a1
+ if ca:
+ a = a2
+ b = a1
+ if cb:
+ b = a
+ return a[0] + b[0]
+ res = self.interp_operations(fn, [7, 0, 1])
+ assert res == 7 * 2
+ self.check_operations_history(getarrayitem_gc=1)
+ res = self.interp_operations(fn, [-7, 1, 1])
+ assert res == -7 * 2
+ self.check_operations_history(getarrayitem_gc=1)
+
+ def test_array_caching_while_tracing_invalidation(self):
+ a1 = [0, 0]
+ a2 = [0, 0]
+ @jit.dont_look_inside
+ def f(a):
+ a[0] = 5
+ class A: pass
+ l = A()
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a[0] = n
+ x1 = a[0]
+ f(a)
+ x2 = a[0]
+ l.x = x2
+ return a[0] + x1 + x2
+ res = self.interp_operations(fn, [7])
+ assert res == 5 * 2 + 7
+ self.check_operations_history(getarrayitem_gc=1)
+
+ def test_array_and_getfield_interaction(self):
+ class A: pass
+ a1 = A()
+ a2 = A()
+ a1.l = a2.l = [0, 0]
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a.l = [0, 0]
+ a.x = 0
+ a.l[a.x] = n
+ a.x += 1
+ a.l[a.x] = n + 1
+ x1 = a.l[a.x]
+ a.x -= 1
+ x2 = a.l[a.x]
+ return x1 + x2
+ res = self.interp_operations(fn, [7])
+ assert res == 7 * 2 + 1
+ self.check_operations_history(setarrayitem_gc=2, setfield_gc=3,
+ getarrayitem_gc=0, getfield_gc=1)
+
+ def test_promote_changes_heap_cache(self):
+ class A: pass
+ a1 = A()
+ a2 = A()
+ a1.l = a2.l = [0, 0]
+ a1.x = a2.x = 0
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a.l = [0, 0]
+ jit.promote(a.x)
+ a.l[a.x] = n
+ a.x += 1
+ a.l[a.x] = n + 1
+ x1 = a.l[a.x]
+ a.x -= 1
+ x2 = a.l[a.x]
+ return x1 + x2
+ res = self.interp_operations(fn, [7])
+ assert res == 7 * 2 + 1
+ self.check_operations_history(setarrayitem_gc=2, setfield_gc=2,
+ getarrayitem_gc=0, getfield_gc=2)
+
+ def test_list_caching(self):
+ a1 = [0, 0]
+ a2 = [0, 0]
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ if n < -1000:
+ a.append(5)
+ a[0] = n
+ x1 = a[0]
+ a[n - n] = n + 1
+ return a[0] + x1
+ res = self.interp_operations(fn, [7])
+ assert res == 7 + 7 + 1
+ self.check_operations_history(getarrayitem_gc=1,
+ getfield_gc=1)
+ res = self.interp_operations(fn, [-7])
+ assert res == -7 - 7 + 1
+ self.check_operations_history(getarrayitem_gc=1,
+ getfield_gc=1)
+
+ def fn(n, ca, cb):
+ a1[0] = n
+ a2[0] = n
+ a = a1
+ if ca:
+ a = a2
+ if n < -100:
+ a.append(5)
+ b = a1
+ if cb:
+ b = a
+ return a[0] + b[0]
+ res = self.interp_operations(fn, [7, 0, 1])
+ assert res == 7 * 2
+ self.check_operations_history(getarrayitem_gc=1,
+ getfield_gc=3)
+ res = self.interp_operations(fn, [-7, 1, 1])
+ assert res == -7 * 2
+ self.check_operations_history(getarrayitem_gc=1,
+ getfield_gc=3)
+
+ def test_list_caching_negative(self):
+ def fn(n):
+ a = [0] * n
+ if n > 1000:
+ a.append(0)
+ a[-1] = n
+ x1 = a[-1]
+ a[n - n - 1] = n + 1
+ return a[-1] + x1
+ res = self.interp_operations(fn, [7])
+ assert res == 7 + 7 + 1
+ self.check_operations_history(setarrayitem_gc=2,
+ setfield_gc=2)
+
+ def test_virtualizable_with_array_heap_cache(self):
+ myjitdriver = jit.JitDriver(greens = [], reds = ['n', 'x', 'i', 'frame'],
+ virtualizables = ['frame'])
+
+ class Frame(object):
+ _virtualizable2_ = ['l[*]', 's']
+
+ def __init__(self, a, s):
+ self = jit.hint(self, access_directly=True, fresh_virtualizable=True)
+ self.l = [0] * (4 + a)
+ self.s = s
+
+ def f(n, a, i):
+ frame = Frame(a, 0)
+ frame.l[0] = a
+ frame.l[1] = a + 1
+ frame.l[2] = a + 2
+ frame.l[3] = a + 3
+ if not i:
+ return frame.l[0] + len(frame.l)
+ x = 0
+ while n > 0:
+ myjitdriver.can_enter_jit(frame=frame, n=n, x=x, i=i)
+ myjitdriver.jit_merge_point(frame=frame, n=n, x=x, i=i)
+ frame.s = jit.promote(frame.s)
+ n -= 1
+ s = frame.s
+ assert s >= 0
+ x += frame.l[s]
+ frame.s += 1
+ s = frame.s
+ assert s >= 0
+ x += frame.l[s]
+ x += len(frame.l)
+ x += f(n, n, 0)
+ frame.s -= 1
+ return x
+
+ res = self.meta_interp(f, [10, 1, 1], listops=True)
+ assert res == f(10, 1, 1)
+ self.check_history(getarrayitem_gc=0, getfield_gc=0)
+
+ def test_heap_caching_pure(self):
+ class A(object):
+ pass
+ p1 = A()
+ p2 = A()
+ def fn(n):
+ if n >= 0:
+ a = (n, n + 1)
+ p = p1
+ else:
+ a = (n + 1, n)
+ p = p2
+ p.x = a
+
+ return p.x[0] + p.x[1]
+ res = self.interp_operations(fn, [7])
+ assert res == 7 + 7 + 1
+ self.check_operations_history(getfield_gc=0, getfield_gc_pure=0)
+ res = self.interp_operations(fn, [-7])
+ assert res == -7 - 7 + 1
+ self.check_operations_history(getfield_gc=0, getfield_gc_pure=0)
+
+ def test_heap_caching_and_elidable_function(self):
+ class A:
+ pass
+ class B: pass
+ a1 = A()
+ a1.y = 6
+ a2 = A()
+ a2.y = 13
+ @jit.elidable
+ def f(b):
+ return b + 1
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = A()
+ a.x = n
+ z = f(6)
+ return z + a.x
+ res = self.interp_operations(fn, [7])
+ assert res == 7 + 7
+ self.check_operations_history(getfield_gc=0)
+ res = self.interp_operations(fn, [-7])
+ assert res == -7 + 7
+ self.check_operations_history(getfield_gc=0)
+ return
diff --git a/pypy/jit/metainterp/test/test_virtualizable.py b/pypy/jit/metainterp/test/test_virtualizable.py
--- a/pypy/jit/metainterp/test/test_virtualizable.py
+++ b/pypy/jit/metainterp/test/test_virtualizable.py
@@ -377,7 +377,7 @@
expected = f(20)
res = self.meta_interp(f, [20], enable_opts='')
assert res == expected
- self.check_loops(getfield_gc=3, setfield_gc=0,
+ self.check_loops(getfield_gc=1, setfield_gc=0,
arraylen_gc=1, getarrayitem_gc=1, setarrayitem_gc=1)
# ------------------------------
@@ -1133,6 +1133,7 @@
res = self.meta_interp(f, [10])
assert res == 55
self.check_loops(new_with_vtable=0, ptr_eq=1, everywhere=True)
+ self.check_history(ptr_eq=2)
def test_virtual_child_frame_with_arrays(self):
myjitdriver = JitDriver(greens = [], reds = ['frame'],
diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py
--- a/pypy/jit/metainterp/warmspot.py
+++ b/pypy/jit/metainterp/warmspot.py
@@ -10,6 +10,7 @@
from pypy.rlib.objectmodel import we_are_translated
from pypy.rlib.unroll import unrolling_iterable
from pypy.rlib.debug import fatalerror
+from pypy.rlib.rstackovf import StackOverflow
from pypy.translator.simplify import get_functype
from pypy.translator.unsimplify import call_final_function
@@ -408,6 +409,15 @@
jd.warmstate = state
def crash_in_jit(e):
+ try:
+ raise e
+ except JitException:
+ raise # go through
+ except MemoryError:
+ raise # go through
+ except StackOverflow:
+ raise # go through
+ except Exception, e:
if not we_are_translated():
print "~~~ Crash in JIT!"
print '~~~ %s: %s' % (e.__class__, e)
@@ -421,8 +431,6 @@
def maybe_enter_jit(*args):
try:
maybe_compile_and_run(state.increment_threshold, *args)
- except JitException:
- raise # go through
except Exception, e:
crash_in_jit(e)
maybe_enter_jit._always_inline_ = True
diff --git a/pypy/jit/metainterp/warmstate.py b/pypy/jit/metainterp/warmstate.py
--- a/pypy/jit/metainterp/warmstate.py
+++ b/pypy/jit/metainterp/warmstate.py
@@ -138,6 +138,9 @@
refvalue = cpu.ts.cast_to_ref(value)
cpu.set_future_value_ref(j, refvalue)
elif typecode == 'int':
+ if isinstance(lltype.typeOf(value), lltype.Ptr):
+ intvalue = llmemory.AddressAsInt(llmemory.cast_ptr_to_adr(value))
+ else:
intvalue = lltype.cast_primitive(lltype.Signed, value)
cpu.set_future_value_int(j, intvalue)
elif typecode == 'float':
diff --git a/pypy/module/__builtin__/compiling.py b/pypy/module/__builtin__/compiling.py
--- a/pypy/module/__builtin__/compiling.py
+++ b/pypy/module/__builtin__/compiling.py
@@ -5,7 +5,7 @@
from pypy.interpreter.pycode import PyCode
from pypy.interpreter.error import OperationError
from pypy.interpreter.astcompiler import consts, ast
-from pypy.interpreter.gateway import NoneNotWrapped, unwrap_spec
+from pypy.interpreter.gateway import unwrap_spec
@unwrap_spec(filename=str, mode=str, flags=int, dont_inherit=int)
def compile(space, w_source, filename, mode, flags=0, dont_inherit=0):
diff --git a/pypy/module/__builtin__/descriptor.py b/pypy/module/__builtin__/descriptor.py
--- a/pypy/module/__builtin__/descriptor.py
+++ b/pypy/module/__builtin__/descriptor.py
@@ -1,12 +1,10 @@
-
-from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.error import OperationError
+from pypy.interpreter.function import StaticMethod, ClassMethod
from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.objspace.descroperation import object_getattribute, object_setattr
-from pypy.interpreter.function import StaticMethod, ClassMethod
-from pypy.interpreter.typedef import GetSetProperty, descr_get_dict, \
- descr_set_dict, interp_attrproperty_w, generic_new_descr
+from pypy.interpreter.typedef import (TypeDef, interp_attrproperty_w,
+ generic_new_descr)
+from pypy.objspace.descroperation import object_getattribute
class W_Super(Wrappable):
def __init__(self, space, w_starttype, w_objtype, w_self):
diff --git a/pypy/module/__builtin__/functional.py b/pypy/module/__builtin__/functional.py
--- a/pypy/module/__builtin__/functional.py
+++ b/pypy/module/__builtin__/functional.py
@@ -4,13 +4,12 @@
"""
from pypy.interpreter.error import OperationError
-from pypy.interpreter.gateway import NoneNotWrapped, applevel
+from pypy.interpreter.gateway import NoneNotWrapped
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.baseobjspace import Wrappable
from pypy.rlib.rarithmetic import r_uint, intmask
from pypy.rlib.objectmodel import specialize
-from inspect import getsource, getfile
from pypy.rlib.rbigint import rbigint
@@ -662,7 +661,6 @@
def descr_reduce(self):
from pypy.interpreter.mixedmodule import MixedModule
- from pypy.module._pickle_support import maker # helper fns
space = self.space
w_mod = space.getbuiltinmodule('_pickle_support')
mod = space.interp_w(MixedModule, w_mod)
diff --git a/pypy/module/__builtin__/interp_classobj.py b/pypy/module/__builtin__/interp_classobj.py
--- a/pypy/module/__builtin__/interp_classobj.py
+++ b/pypy/module/__builtin__/interp_classobj.py
@@ -1,11 +1,9 @@
import new
from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.interpreter.gateway import NoneNotWrapped, applevel, interp2app
+from pypy.interpreter.gateway import interp2app
from pypy.interpreter.typedef import TypeDef, make_weakref_descr
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.typedef import GetSetProperty, descr_get_dict
-from pypy.interpreter.typedef import descr_set_dict
-from pypy.rlib.rarithmetic import r_uint, intmask
+from pypy.interpreter.typedef import GetSetProperty, descr_get_dict, descr_set_dict
from pypy.rlib.objectmodel import compute_identity_hash
from pypy.rlib.debug import make_sure_not_resized
from pypy.rlib import jit
diff --git a/pypy/module/__builtin__/interp_memoryview.py b/pypy/module/__builtin__/interp_memoryview.py
--- a/pypy/module/__builtin__/interp_memoryview.py
+++ b/pypy/module/__builtin__/interp_memoryview.py
@@ -2,7 +2,7 @@
Implementation of the 'buffer' and 'memoryview' types.
"""
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter import gateway, buffer
+from pypy.interpreter import buffer
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.interpreter.error import OperationError
diff --git a/pypy/module/__builtin__/operation.py b/pypy/module/__builtin__/operation.py
--- a/pypy/module/__builtin__/operation.py
+++ b/pypy/module/__builtin__/operation.py
@@ -4,12 +4,10 @@
from pypy.interpreter import gateway
from pypy.interpreter.error import OperationError
-from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.gateway import unwrap_spec
from pypy.rlib.runicode import UNICHR
from pypy.rlib.rfloat import isnan, isinf, round_double
from pypy.rlib import rfloat
-import math
import __builtin__
NoneNotWrapped = gateway.NoneNotWrapped
diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py
--- a/pypy/module/__pypy__/__init__.py
+++ b/pypy/module/__pypy__/__init__.py
@@ -25,6 +25,7 @@
'debug_print_once' : 'interp_debug.debug_print_once',
'builtinify' : 'interp_magic.builtinify',
'lookup_special' : 'interp_magic.lookup_special',
+ 'do_what_I_mean' : 'interp_magic.do_what_I_mean',
}
submodules = {
diff --git a/pypy/module/__pypy__/interp_debug.py b/pypy/module/__pypy__/interp_debug.py
--- a/pypy/module/__pypy__/interp_debug.py
+++ b/pypy/module/__pypy__/interp_debug.py
@@ -1,5 +1,4 @@
-from pypy.interpreter.gateway import interp2app, NoneNotWrapped, unwrap_spec
-from pypy.interpreter.error import OperationError
+from pypy.interpreter.gateway import unwrap_spec
from pypy.rlib import debug, jit
diff --git a/pypy/module/__pypy__/interp_identitydict.py b/pypy/module/__pypy__/interp_identitydict.py
--- a/pypy/module/__pypy__/interp_identitydict.py
+++ b/pypy/module/__pypy__/interp_identitydict.py
@@ -1,6 +1,6 @@
from pypy.interpreter.error import OperationError
from pypy.interpreter.typedef import TypeDef
-from pypy.interpreter.gateway import NoneNotWrapped, interp2app, unwrap_spec
+from pypy.interpreter.gateway import interp2app
from pypy.interpreter.baseobjspace import Wrappable
class W_IdentityDict(Wrappable):
diff --git a/pypy/module/__pypy__/interp_magic.py b/pypy/module/__pypy__/interp_magic.py
--- a/pypy/module/__pypy__/interp_magic.py
+++ b/pypy/module/__pypy__/interp_magic.py
@@ -70,3 +70,6 @@
if w_descr is None:
return space.w_None
return space.get(w_descr, w_obj)
+
+def do_what_I_mean(space):
+ return space.wrap(42)
diff --git a/pypy/module/__pypy__/test/test_special.py b/pypy/module/__pypy__/test/test_special.py
--- a/pypy/module/__pypy__/test/test_special.py
+++ b/pypy/module/__pypy__/test/test_special.py
@@ -49,3 +49,8 @@
class X:
pass
raises(TypeError, lookup_special, X(), "foo")
+
+ def test_do_what_I_mean(self):
+ from __pypy__ import do_what_I_mean
+ x = do_what_I_mean()
+ assert x == 42
diff --git a/pypy/module/_ast/test/test_ast.py b/pypy/module/_ast/test/test_ast.py
--- a/pypy/module/_ast/test/test_ast.py
+++ b/pypy/module/_ast/test/test_ast.py
@@ -186,6 +186,11 @@
mod = self.get_ast("from __future__ import with_statement; import y; " \
"from __future__ import nested_scopes")
raises(SyntaxError, compile, mod, "<test>", "exec")
+ mod = self.get_ast("from __future__ import division\nx = 1/2")
+ co = compile(mod, "<test>", "exec")
+ ns = {}
+ exec co in ns
+ assert ns["x"] == .5
def test_field_attr_writable(self):
import _ast as ast
diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py
--- a/pypy/module/_codecs/interp_codecs.py
+++ b/pypy/module/_codecs/interp_codecs.py
@@ -1,6 +1,6 @@
from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.interpreter.gateway import NoneNotWrapped, interp2app, unwrap_spec
-from pypy.rlib.rstring import StringBuilder, UnicodeBuilder
+from pypy.rlib.rstring import UnicodeBuilder
from pypy.rlib.objectmodel import we_are_translated
class CodecState(object):
diff --git a/pypy/module/_ffi/interp_ffi.py b/pypy/module/_ffi/interp_ffi.py
--- a/pypy/module/_ffi/interp_ffi.py
+++ b/pypy/module/_ffi/interp_ffi.py
@@ -1,9 +1,8 @@
-import sys
-from pypy.interpreter.baseobjspace import Wrappable, Arguments
+from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.error import OperationError, wrap_oserror, \
operationerrfmt
-from pypy.interpreter.gateway import interp2app, NoneNotWrapped, unwrap_spec
-from pypy.interpreter.typedef import TypeDef, GetSetProperty
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.typedef import TypeDef
from pypy.module._rawffi.structure import W_StructureInstance, W_Structure
#
from pypy.rpython.lltypesystem import lltype, rffi
@@ -83,7 +82,6 @@
def build_ffi_types():
- from pypy.rlib.clibffi import FFI_TYPE_P
types = [
# note: most of the type name directly come from the C equivalent,
# with the exception of bytes: in C, ubyte and char are equivalent,
@@ -149,6 +147,12 @@
raise OperationError(space.w_TypeError, space.wrap(msg))
return res
+def unwrap_truncate_int(TP, space, w_arg):
+ if space.is_true(space.isinstance(w_arg, space.w_int)):
+ return rffi.cast(TP, space.int_w(w_arg))
+ else:
+ return rffi.cast(TP, space.bigint_w(w_arg).ulonglongmask())
+unwrap_truncate_int._annspecialcase_ = 'specialize:arg(0)'
# ========================================================================
@@ -181,15 +185,14 @@
# note that we must check for longlong first, because either
# is_signed or is_unsigned returns true anyway
assert libffi.IS_32_BIT
- kind = libffi.types.getkind(w_argtype.ffitype) # XXX: remove the kind
- self.arg_longlong(space, argchain, kind, w_arg)
+ self.arg_longlong(space, argchain, w_arg)
elif w_argtype.is_signed():
- argchain.arg(space.int_w(w_arg))
+ argchain.arg(unwrap_truncate_int(rffi.LONG, space, w_arg))
elif w_argtype.is_pointer():
w_arg = self.convert_pointer_arg_maybe(space, w_arg, w_argtype)
argchain.arg(intmask(space.uint_w(w_arg)))
elif w_argtype.is_unsigned():
- argchain.arg(intmask(space.uint_w(w_arg)))
+ argchain.arg(unwrap_truncate_int(rffi.ULONG, space, w_arg))
elif w_argtype.is_char():
w_arg = space.ord(w_arg)
argchain.arg(space.int_w(w_arg))
@@ -220,15 +223,10 @@
return w_arg
@jit.dont_look_inside
- def arg_longlong(self, space, argchain, kind, w_arg):
+ def arg_longlong(self, space, argchain, w_arg):
bigarg = space.bigint_w(w_arg)
- if kind == 'I':
- llval = bigarg.tolonglong()
- elif kind == 'U':
- ullval = bigarg.toulonglong()
+ ullval = bigarg.ulonglongmask()
llval = rffi.cast(rffi.LONGLONG, ullval)
- else:
- assert False
# this is a hack: we store the 64 bits of the long long into the
# 64 bits of a float (i.e., a C double)
floatval = libffi.longlong2float(llval)
diff --git a/pypy/module/_ffi/test/test__ffi.py b/pypy/module/_ffi/test/test__ffi.py
--- a/pypy/module/_ffi/test/test__ffi.py
+++ b/pypy/module/_ffi/test/test__ffi.py
@@ -111,7 +111,6 @@
types.double)
assert pow(2, 3) == 8
-
def test_int_args(self):
"""
DLLEXPORT int sum_xy(int x, int y)
@@ -119,10 +118,12 @@
return x+y;
}
"""
+ import sys
from _ffi import CDLL, types
libfoo = CDLL(self.libfoo_name)
sum_xy = libfoo.getfunc('sum_xy', [types.sint, types.sint], types.sint)
assert sum_xy(30, 12) == 42
+ assert sum_xy(sys.maxint*2, 0) == -2
def test_void_result(self):
"""
@@ -247,6 +248,9 @@
types.ulong)
assert sum_xy(sys.maxint, 12) == sys.maxint+12
assert sum_xy(sys.maxint+1, 12) == sys.maxint+13
+ #
+ res = sum_xy(sys.maxint*2+3, 0)
+ assert res == 1
def test_unsigned_short_args(self):
"""
@@ -375,6 +379,9 @@
res = sum_xy(x, y)
expected = maxint64 + 3
assert res == expected
+ #
+ res = sum_xy(maxint64*2+3, 0)
+ assert res == 1
def test_byval_argument(self):
"""
diff --git a/pypy/module/_file/interp_file.py b/pypy/module/_file/interp_file.py
--- a/pypy/module/_file/interp_file.py
+++ b/pypy/module/_file/interp_file.py
@@ -43,11 +43,17 @@
# assume that the file and stream objects are only visible in the
# thread that runs __del__, so no race condition should be possible
self.clear_all_weakrefs()
+ if self.stream is not None:
+ self.enqueue_for_destruction(self.space, W_File.destructor,
+ 'close() method of ')
+
+ def destructor(self):
+ assert isinstance(self, W_File)
try:
self.direct_close()
except StreamErrors, e:
operr = wrap_streamerror(self.space, e, self.w_name)
- operr.write_unraisable(self.space, '__del__ of ', self)
+ raise operr
def fdopenstream(self, stream, fd, mode, w_name=None):
self.fd = fd
diff --git a/pypy/module/_file/interp_stream.py b/pypy/module/_file/interp_stream.py
--- a/pypy/module/_file/interp_stream.py
+++ b/pypy/module/_file/interp_stream.py
@@ -3,12 +3,10 @@
from pypy.rlib.streamio import StreamErrors
from pypy.interpreter.error import OperationError, wrap_oserror2
-from pypy.interpreter.gateway import ObjSpace
-from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.baseobjspace import ObjSpace, Wrappable
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.gateway import interp2app
-import os
def wrap_streamerror(space, e, w_filename=None):
if isinstance(e, streamio.StreamError):
diff --git a/pypy/module/_io/interp_bufferedio.py b/pypy/module/_io/interp_bufferedio.py
--- a/pypy/module/_io/interp_bufferedio.py
+++ b/pypy/module/_io/interp_bufferedio.py
@@ -4,7 +4,6 @@
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.interpreter.buffer import RWBuffer
-from pypy.rpython.lltypesystem import lltype, rffi
from pypy.rlib.rstring import StringBuilder
from pypy.rlib.rarithmetic import r_longlong, intmask
from pypy.tool.sourcetools import func_renamer
diff --git a/pypy/module/_io/interp_fileio.py b/pypy/module/_io/interp_fileio.py
--- a/pypy/module/_io/interp_fileio.py
+++ b/pypy/module/_io/interp_fileio.py
@@ -1,5 +1,4 @@
-from pypy.interpreter.typedef import (
- TypeDef, interp_attrproperty, interp_attrproperty_w, GetSetProperty)
+from pypy.interpreter.typedef import TypeDef, interp_attrproperty, GetSetProperty
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.error import OperationError, wrap_oserror, wrap_oserror2
from pypy.rlib.rarithmetic import r_longlong
diff --git a/pypy/module/_io/interp_io.py b/pypy/module/_io/interp_io.py
--- a/pypy/module/_io/interp_io.py
+++ b/pypy/module/_io/interp_io.py
@@ -6,7 +6,6 @@
TypeDef, interp_attrproperty, generic_new_descr)
from pypy.module.exceptions.interp_exceptions import W_IOError
from pypy.module._io.interp_fileio import W_FileIO
-from pypy.module._io.interp_iobase import W_IOBase
from pypy.module._io.interp_textio import W_TextIOWrapper
from pypy.rpython.module.ll_os_stat import STAT_FIELD_TYPES
diff --git a/pypy/module/_io/interp_iobase.py b/pypy/module/_io/interp_iobase.py
--- a/pypy/module/_io/interp_iobase.py
+++ b/pypy/module/_io/interp_iobase.py
@@ -57,6 +57,11 @@
def __del__(self):
self.clear_all_weakrefs()
+ self.enqueue_for_destruction(self.space, W_IOBase.destructor,
+ 'internal __del__ of ')
+
+ def destructor(self):
+ assert isinstance(self, W_IOBase)
space = self.space
w_closed = space.findattr(self, space.wrap('closed'))
try:
diff --git a/pypy/module/_io/interp_textio.py b/pypy/module/_io/interp_textio.py
--- a/pypy/module/_io/interp_textio.py
+++ b/pypy/module/_io/interp_textio.py
@@ -4,7 +4,7 @@
generic_new_descr)
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.error import OperationError
from pypy.rlib.rarithmetic import intmask, r_ulonglong, r_uint
from pypy.rlib.rbigint import rbigint
from pypy.rlib.rstring import UnicodeBuilder
diff --git a/pypy/module/_locale/interp_locale.py b/pypy/module/_locale/interp_locale.py
--- a/pypy/module/_locale/interp_locale.py
+++ b/pypy/module/_locale/interp_locale.py
@@ -1,4 +1,3 @@
-from pypy.rpython.tool import rffi_platform as platform
from pypy.rlib import rposix
from pypy.rlib.rarithmetic import intmask
diff --git a/pypy/module/_minimal_curses/fficurses.py b/pypy/module/_minimal_curses/fficurses.py
--- a/pypy/module/_minimal_curses/fficurses.py
+++ b/pypy/module/_minimal_curses/fficurses.py
@@ -2,12 +2,10 @@
""" The ffi for rpython, need to be imported for side effects
"""
-import sys
from pypy.rpython.lltypesystem import rffi
from pypy.rpython.lltypesystem import lltype
from pypy.rpython.tool import rffi_platform
from pypy.rpython.extfunc import register_external
-from pypy.rpython.extregistry import ExtRegistryEntry
from pypy.module._minimal_curses import interp_curses
from pypy.translator.tool.cbuild import ExternalCompilationInfo
diff --git a/pypy/module/_multibytecodec/c_codecs.py b/pypy/module/_multibytecodec/c_codecs.py
--- a/pypy/module/_multibytecodec/c_codecs.py
+++ b/pypy/module/_multibytecodec/c_codecs.py
@@ -1,4 +1,4 @@
-import py, sys
+import py
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.translator.tool.cbuild import ExternalCompilationInfo
from pypy.tool.autopath import pypydir
@@ -55,10 +55,12 @@
"pypy_cjk_dec_init", "pypy_cjk_dec_free", "pypy_cjk_dec_chunk",
"pypy_cjk_dec_outbuf", "pypy_cjk_dec_outlen",
"pypy_cjk_dec_inbuf_remaining", "pypy_cjk_dec_inbuf_consumed",
+ "pypy_cjk_dec_replace_on_error",
"pypy_cjk_enc_init", "pypy_cjk_enc_free", "pypy_cjk_enc_chunk",
"pypy_cjk_enc_reset", "pypy_cjk_enc_outbuf", "pypy_cjk_enc_outlen",
"pypy_cjk_enc_inbuf_remaining", "pypy_cjk_enc_inbuf_consumed",
+ "pypy_cjk_enc_replace_on_error",
] + ["pypy_cjkcodec_%s" % codec for codec in codecs],
)
diff --git a/pypy/module/_multibytecodec/interp_multibytecodec.py b/pypy/module/_multibytecodec/interp_multibytecodec.py
--- a/pypy/module/_multibytecodec/interp_multibytecodec.py
+++ b/pypy/module/_multibytecodec/interp_multibytecodec.py
@@ -1,5 +1,5 @@
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.gateway import ObjSpace, interp2app, unwrap_spec
+from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.error import OperationError
from pypy.module._multibytecodec import c_codecs
diff --git a/pypy/module/_multiprocessing/interp_connection.py b/pypy/module/_multiprocessing/interp_connection.py
--- a/pypy/module/_multiprocessing/interp_connection.py
+++ b/pypy/module/_multiprocessing/interp_connection.py
@@ -4,7 +4,7 @@
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.error import (
OperationError, wrap_oserror, operationerrfmt)
-from pypy.rpython.lltypesystem import rffi, lltype, llmemory
+from pypy.rpython.lltypesystem import rffi, lltype
from pypy.rlib.rarithmetic import intmask
from pypy.rlib import rpoll
import sys
diff --git a/pypy/module/_multiprocessing/interp_semaphore.py b/pypy/module/_multiprocessing/interp_semaphore.py
--- a/pypy/module/_multiprocessing/interp_semaphore.py
+++ b/pypy/module/_multiprocessing/interp_semaphore.py
@@ -15,7 +15,6 @@
if sys.platform == 'win32':
from pypy.rlib import rwin32
- from pypy.interpreter.error import wrap_windowserror
from pypy.module._multiprocessing.interp_win32 import (
handle_w, _GetTickCount)
diff --git a/pypy/module/_pickle_support/maker.py b/pypy/module/_pickle_support/maker.py
--- a/pypy/module/_pickle_support/maker.py
+++ b/pypy/module/_pickle_support/maker.py
@@ -8,7 +8,6 @@
from pypy.interpreter.generator import GeneratorIterator
from pypy.rlib.objectmodel import instantiate
from pypy.interpreter.gateway import unwrap_spec
-from pypy.objspace.std.dicttype import dictiter_typedef
from pypy.objspace.std.iterobject import W_SeqIterObject, W_ReverseSeqIterObject
diff --git a/pypy/module/_rawffi/callback.py b/pypy/module/_rawffi/callback.py
--- a/pypy/module/_rawffi/callback.py
+++ b/pypy/module/_rawffi/callback.py
@@ -2,10 +2,10 @@
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.rpython.lltypesystem import lltype, rffi
-from pypy.module._rawffi.array import get_elem, push_elem
+from pypy.module._rawffi.array import push_elem
from pypy.module._rawffi.structure import W_Structure
-from pypy.module._rawffi.interp_rawffi import W_DataInstance, letter2tp, \
- wrap_value, unwrap_value, unwrap_truncate_int, unpack_argshapes
+from pypy.module._rawffi.interp_rawffi import (W_DataInstance, letter2tp,
+ unwrap_value, unpack_argshapes)
from pypy.rlib.clibffi import USERDATA_P, CallbackFuncPtr, FUNCFLAG_CDECL
from pypy.rlib.clibffi import ffi_type_void
from pypy.rlib import rweakref
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
@@ -1,7 +1,6 @@
-import sys
from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.error import OperationError, wrap_oserror, operationerrfmt
-from pypy.interpreter.gateway import interp2app, NoneNotWrapped, unwrap_spec
+from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.rlib.clibffi import *
@@ -15,7 +14,7 @@
from pypy.rlib import rwin32
from pypy.tool.sourcetools import func_with_new_name
-from pypy.rlib.rarithmetic import intmask, r_uint, r_singlefloat
+from pypy.rlib.rarithmetic import intmask, r_uint
from pypy.module._rawffi.tracker import tracker
TYPEMAP = {
diff --git a/pypy/module/_socket/interp_func.py b/pypy/module/_socket/interp_func.py
--- a/pypy/module/_socket/interp_func.py
+++ b/pypy/module/_socket/interp_func.py
@@ -1,9 +1,8 @@
-from pypy.interpreter.gateway import NoneNotWrapped, unwrap_spec
+from pypy.interpreter.gateway import unwrap_spec
from pypy.module._socket.interp_socket import converted_error, W_RSocket
from pypy.rlib import rsocket
from pypy.rlib.rsocket import SocketError
-from pypy.rlib.rarithmetic import r_uint
-from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.error import OperationError
def gethostname(space):
"""gethostname() -> string
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
@@ -1,7 +1,7 @@
from __future__ import with_statement
from pypy.rpython.lltypesystem import rffi, lltype
from pypy.interpreter.error import OperationError
-from pypy.interpreter.baseobjspace import W_Root, ObjSpace, Wrappable
+from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.gateway import interp2app, unwrap_spec
@@ -11,7 +11,6 @@
from pypy.module._socket import interp_socket
-import sys
## user defined constants
X509_NAME_MAXLEN = 256
diff --git a/pypy/module/_stackless/interp_coroutine.py b/pypy/module/_stackless/interp_coroutine.py
--- a/pypy/module/_stackless/interp_coroutine.py
+++ b/pypy/module/_stackless/interp_coroutine.py
@@ -15,13 +15,10 @@
experience to decide where to set the limits.
"""
-from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.argument import Arguments
from pypy.interpreter.typedef import GetSetProperty, TypeDef
-from pypy.interpreter.typedef import interp_attrproperty, interp_attrproperty_w
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.interpreter.function import StaticMethod
from pypy.module._stackless.stackless_flags import StacklessFlags
from pypy.module._stackless.rcoroutine import Coroutine, BaseCoState, AbstractThunk, CoroutineExit
diff --git a/pypy/module/_stackless/interp_stackless.py b/pypy/module/_stackless/interp_stackless.py
--- a/pypy/module/_stackless/interp_stackless.py
+++ b/pypy/module/_stackless/interp_stackless.py
@@ -1,9 +1,6 @@
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.typedef import GetSetProperty, TypeDef
-from pypy.interpreter.typedef import interp_attrproperty, interp_attrproperty_w
+from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.gateway import interp2app
-from pypy.interpreter.error import OperationError
-from pypy.rlib.rarithmetic import intmask
import os
diff --git a/pypy/module/_stackless/rclonable.py b/pypy/module/_stackless/rclonable.py
--- a/pypy/module/_stackless/rclonable.py
+++ b/pypy/module/_stackless/rclonable.py
@@ -1,7 +1,6 @@
from pypy.module._stackless.interp_coroutine import AbstractThunk, Coroutine
from pypy.rlib.rgc import gc_swap_pool, gc_clone
from pypy.rlib.objectmodel import we_are_translated
-from pypy.interpreter.error import OperationError
class InterpClonableMixin:
diff --git a/pypy/module/_weakref/interp__weakref.py b/pypy/module/_weakref/interp__weakref.py
--- a/pypy/module/_weakref/interp__weakref.py
+++ b/pypy/module/_weakref/interp__weakref.py
@@ -1,16 +1,15 @@
import py
-from pypy.interpreter.argument import Arguments
from pypy.interpreter.baseobjspace import Wrappable, W_Root
from pypy.interpreter.error import OperationError
from pypy.interpreter.gateway import interp2app, ObjSpace
-from pypy.interpreter.typedef import GetSetProperty, TypeDef
+from pypy.interpreter.typedef import TypeDef
from pypy.rlib import jit
import weakref
class WeakrefLifeline(W_Root):
def __init__(self, space):
- self.space = space # this is here for W_Root.clear_all_weakrefs()
+ self.space = space
self.refs_weak = []
self.cached_weakref_index = -1
self.cached_proxy_index = -1
@@ -23,8 +22,10 @@
"""
for i in range(len(self.refs_weak) - 1, -1, -1):
w_ref = self.refs_weak[i]()
- if w_ref is not None:
- self.space.user_del_action.register_weakref_callback(w_ref)
+ if w_ref is not None and w_ref.w_callable is not None:
+ w_ref.enqueue_for_destruction(self.space,
+ W_WeakrefBase.activate_callback,
+ 'weakref callback of ')
def clear_all_weakrefs(self):
"""Clear all weakrefs. This is called when an app-level object has
@@ -118,11 +119,8 @@
self.w_obj_weak = dead_ref
def activate_callback(w_self):
- if not w_self.w_callable is None:
- try:
+ assert isinstance(w_self, W_WeakrefBase)
w_self.space.call_function(w_self.w_callable, w_self)
- except OperationError, e:
- e.write_unraisable(w_self.space, 'weakref callback ', w_self.w_callable)
def descr__repr__(self, space):
w_obj = self.dereference()
diff --git a/pypy/module/_winreg/interp_winreg.py b/pypy/module/_winreg/interp_winreg.py
--- a/pypy/module/_winreg/interp_winreg.py
+++ b/pypy/module/_winreg/interp_winreg.py
@@ -1,6 +1,5 @@
from __future__ import with_statement
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.baseobjspace import ObjSpace, W_Root
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.interpreter.error import OperationError, wrap_windowserror
diff --git a/pypy/module/array/interp_array.py b/pypy/module/array/interp_array.py
--- a/pypy/module/array/interp_array.py
+++ b/pypy/module/array/interp_array.py
@@ -1,10 +1,9 @@
from __future__ import with_statement
-from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.buffer import RWBuffer
from pypy.interpreter.error import OperationError
from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.interpreter.typedef import TypeDef, GetSetProperty, make_weakref_descr
+from pypy.interpreter.typedef import GetSetProperty, make_weakref_descr
from pypy.module._file.interp_file import W_File
from pypy.objspace.std.model import W_Object
from pypy.objspace.std.multimethod import FailedToImplement
@@ -572,10 +571,7 @@
self.fromsequence(w_ustr)
def array_tounicode__Array(space, self):
- u = u""
- for i in range(self.len):
- u += self.buffer[i]
- return space.wrap(u)
+ return space.wrap(rffi.wcharpsize2unicode(self.buffer, self.len))
else:
def array_fromunicode__Array_Unicode(space, self, w_ustr):
diff --git a/pypy/module/binascii/interp_crc32.py b/pypy/module/binascii/interp_crc32.py
--- a/pypy/module/binascii/interp_crc32.py
+++ b/pypy/module/binascii/interp_crc32.py
@@ -61,7 +61,7 @@
crc_32_tab = map(r_uint, crc_32_tab)
- at unwrap_spec(data='bufferstr', oldcrc='c_int')
+ at unwrap_spec(data='bufferstr', oldcrc='truncatedint')
def crc32(space, data, oldcrc=0):
"Compute the CRC-32 incrementally."
diff --git a/pypy/module/binascii/test/test_binascii.py b/pypy/module/binascii/test/test_binascii.py
--- a/pypy/module/binascii/test/test_binascii.py
+++ b/pypy/module/binascii/test/test_binascii.py
@@ -374,6 +374,8 @@
('x', 10000, -1855256896),
('y', 10000, -429115818),
('z', 10000, 2137352172),
+ ('foo', 99999999999999999999999999, -1932704816),
+ ('bar', -99999999999999999999999999, 2000545409),
]:
assert self.binascii.crc32(input, initial) == expected
diff --git a/pypy/module/bz2/interp_bz2.py b/pypy/module/bz2/interp_bz2.py
--- a/pypy/module/bz2/interp_bz2.py
+++ b/pypy/module/bz2/interp_bz2.py
@@ -4,9 +4,8 @@
from pypy.rpython.lltypesystem import lltype
from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.typedef import TypeDef, GetSetProperty
-from pypy.interpreter.typedef import interp_attrproperty
-from pypy.interpreter.gateway import NoneNotWrapped, interp2app, unwrap_spec
+from pypy.interpreter.typedef import TypeDef, interp_attrproperty
+from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.rlib.streamio import Stream
from pypy.translator.tool.cbuild import ExternalCompilationInfo
from pypy.translator.platform import platform as compiler
diff --git a/pypy/module/bz2/test/test_bz2_file.py b/pypy/module/bz2/test/test_bz2_file.py
--- a/pypy/module/bz2/test/test_bz2_file.py
+++ b/pypy/module/bz2/test/test_bz2_file.py
@@ -133,6 +133,7 @@
bz2f.seek(0)
assert bz2f.tell() == 0
+ del bz2f # delete from this frame, which is captured in the traceback
def test_open_close_del(self):
from bz2 import BZ2File
@@ -246,11 +247,18 @@
assert text_read == self.TEXT
bz2f.close()
+ def test_silently_closes(self):
+ from bz2 import BZ2File
+ self.create_broken_temp_file()
+ BZ2File(self.temppath)
+ # check that no C-level malloc is left behind
+
def test_read_broken_file(self):
from bz2 import BZ2File
self.create_broken_temp_file()
bz2f = BZ2File(self.temppath)
raises(EOFError, bz2f.read)
+ del bz2f # delete from this frame, which is captured in the traceback
def test_subsequent_read_broken_file(self):
from bz2 import BZ2File
@@ -264,6 +272,7 @@
raise Exception("should generate EOFError earlier")
except EOFError:
pass
+ del bz2f # delete from this frame, which is captured in the traceback
def test_read_chunk10(self):
from bz2 import BZ2File
@@ -416,6 +425,7 @@
bz2f.close()
bz2f = BZ2File(self.temppath, 'r')
assert bz2f.read() == self.random_data
+ del bz2f # delete from this frame, which is captured in the traceback
def test_context_manager(self):
from bz2 import BZ2File
diff --git a/pypy/module/cStringIO/interp_stringio.py b/pypy/module/cStringIO/interp_stringio.py
--- a/pypy/module/cStringIO/interp_stringio.py
+++ b/pypy/module/cStringIO/interp_stringio.py
@@ -1,4 +1,3 @@
-import sys
from pypy.interpreter.error import OperationError
from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.typedef import TypeDef, GetSetProperty
diff --git a/pypy/module/cmath/interp_cmath.py b/pypy/module/cmath/interp_cmath.py
--- a/pypy/module/cmath/interp_cmath.py
+++ b/pypy/module/cmath/interp_cmath.py
@@ -5,7 +5,7 @@
from pypy.tool.sourcetools import func_with_new_name
from pypy.interpreter.error import OperationError
from pypy.interpreter.gateway import NoneNotWrapped
-from pypy.module.cmath import Module, names_and_docstrings
+from pypy.module.cmath import names_and_docstrings
from pypy.module.cmath.constant import DBL_MIN, CM_SCALE_UP, CM_SCALE_DOWN
from pypy.module.cmath.constant import CM_LARGE_DOUBLE, DBL_MANT_DIG
from pypy.module.cmath.constant import M_LN2, M_LN10
diff --git a/pypy/module/cpyext/cdatetime.py b/pypy/module/cpyext/cdatetime.py
--- a/pypy/module/cpyext/cdatetime.py
+++ b/pypy/module/cpyext/cdatetime.py
@@ -1,8 +1,7 @@
from pypy.rpython.lltypesystem import rffi, lltype
-from pypy.rlib.objectmodel import we_are_translated
-from pypy.module.cpyext.pyobject import PyObject, make_ref, Py_DecRef
-from pypy.module.cpyext.api import (
- cpython_api, CANNOT_FAIL, cpython_struct, PyObjectFields)
+from pypy.module.cpyext.pyobject import PyObject, make_ref
+from pypy.module.cpyext.api import (cpython_api, CANNOT_FAIL, cpython_struct,
+ PyObjectFields)
from pypy.module.cpyext.import_ import PyImport_Import
from pypy.module.cpyext.typeobject import PyTypeObjectPtr
from pypy.interpreter.error import OperationError
diff --git a/pypy/module/cpyext/complexobject.py b/pypy/module/cpyext/complexobject.py
--- a/pypy/module/cpyext/complexobject.py
+++ b/pypy/module/cpyext/complexobject.py
@@ -1,7 +1,6 @@
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.module.cpyext.api import (
cpython_api, cpython_struct, PyObject, build_type_checkers)
-from pypy.module.cpyext.pyerrors import PyErr_BadArgument
from pypy.module.cpyext.floatobject import PyFloat_AsDouble
from pypy.objspace.std.complexobject import W_ComplexObject
from pypy.interpreter.error import OperationError
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 "2.7.1"
/* PyPy version as a string */
-#define PYPY_VERSION "1.5.0"
+#define PYPY_VERSION "1.6.0"
/* Subversion Revision number of this file (not of the repository) */
#define PY_PATCHLEVEL_REVISION "$Revision: 77872 $"
diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py
--- a/pypy/module/cpyext/sequence.py
+++ b/pypy/module/cpyext/sequence.py
@@ -22,7 +22,7 @@
def PySequence_Check(space, w_obj):
"""Return 1 if the object provides sequence protocol, and 0 otherwise.
This function always succeeds."""
- return int(space.findattr(w_obj, space.wrap("__getitem__")) is not None)
+ return int(space.issequence_w(w_obj))
@cpython_api([PyObject], Py_ssize_t, error=-1)
def PySequence_Size(space, w_obj):
diff --git a/pypy/module/cpyext/stringobject.py b/pypy/module/cpyext/stringobject.py
--- a/pypy/module/cpyext/stringobject.py
+++ b/pypy/module/cpyext/stringobject.py
@@ -268,3 +268,7 @@
if errors:
w_errors = space.wrap(rffi.charp2str(errors))
return space.call_method(w_str, 'encode', w_encoding, w_errors)
+
+ at cpython_api([PyObject, PyObject], PyObject)
+def _PyString_Join(space, w_sep, w_seq):
+ return space.call_method(w_sep, 'join', w_seq)
diff --git a/pypy/module/cpyext/test/test_stringobject.py b/pypy/module/cpyext/test/test_stringobject.py
--- a/pypy/module/cpyext/test/test_stringobject.py
+++ b/pypy/module/cpyext/test/test_stringobject.py
@@ -287,3 +287,9 @@
def test_eq(self, space, api):
assert 1 == api._PyString_Eq(space.wrap("hello"), space.wrap("hello"))
assert 0 == api._PyString_Eq(space.wrap("hello"), space.wrap("world"))
+
+ def test_join(self, space, api):
+ w_sep = space.wrap('<sep>')
+ w_seq = space.wrap(['a', 'b'])
+ w_joined = api._PyString_Join(w_sep, w_seq)
+ assert space.unwrap(w_joined) == 'a<sep>b'
diff --git a/pypy/module/crypt/interp_crypt.py b/pypy/module/crypt/interp_crypt.py
--- a/pypy/module/crypt/interp_crypt.py
+++ b/pypy/module/crypt/interp_crypt.py
@@ -1,6 +1,5 @@
-from pypy.interpreter.error import OperationError
from pypy.interpreter.gateway import unwrap_spec
-from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.rpython.lltypesystem import rffi
from pypy.translator.tool.cbuild import ExternalCompilationInfo
import sys
diff --git a/pypy/module/exceptions/interp_exceptions.py b/pypy/module/exceptions/interp_exceptions.py
--- a/pypy/module/exceptions/interp_exceptions.py
+++ b/pypy/module/exceptions/interp_exceptions.py
@@ -73,9 +73,8 @@
"""
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.typedef import TypeDef, interp_attrproperty_w,\
- GetSetProperty, interp_attrproperty, descr_get_dict, descr_set_dict,\
- descr_del_dict
+from pypy.interpreter.typedef import (TypeDef, GetSetProperty, descr_get_dict,
+ descr_set_dict, descr_del_dict)
from pypy.interpreter.gateway import interp2app
from pypy.interpreter.error import OperationError
from pypy.rlib import rwin32
diff --git a/pypy/module/imp/importing.py b/pypy/module/imp/importing.py
--- a/pypy/module/imp/importing.py
+++ b/pypy/module/imp/importing.py
@@ -11,9 +11,8 @@
from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.eval import Code
from pypy.interpreter.pycode import PyCode
-from pypy.rlib import streamio, jit, rposix
+from pypy.rlib import streamio, jit
from pypy.rlib.streamio import StreamErrors
-from pypy.rlib.rarithmetic import intmask
from pypy.rlib.objectmodel import we_are_translated, specialize
from pypy.module.sys.version import PYPY_VERSION
diff --git a/pypy/module/imp/interp_imp.py b/pypy/module/imp/interp_imp.py
--- a/pypy/module/imp/interp_imp.py
+++ b/pypy/module/imp/interp_imp.py
@@ -3,9 +3,9 @@
from pypy.rlib import streamio
from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.interpreter.module import Module
-from pypy.interpreter.gateway import NoneNotWrapped, unwrap_spec
+from pypy.interpreter.gateway import unwrap_spec
from pypy.module._file.interp_stream import StreamErrors, wrap_streamerror
-import struct
+
def get_suffixes(space):
w = space.wrap
diff --git a/pypy/module/itertools/interp_itertools.py b/pypy/module/itertools/interp_itertools.py
--- a/pypy/module/itertools/interp_itertools.py
+++ b/pypy/module/itertools/interp_itertools.py
@@ -2,7 +2,6 @@
from pypy.interpreter.error import OperationError
from pypy.interpreter.typedef import TypeDef, make_weakref_descr
from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.rlib.rarithmetic import ovfcheck
class W_Count(Wrappable):
diff --git a/pypy/module/marshal/interp_marshal.py b/pypy/module/marshal/interp_marshal.py
--- a/pypy/module/marshal/interp_marshal.py
+++ b/pypy/module/marshal/interp_marshal.py
@@ -1,10 +1,8 @@
-from pypy.interpreter.baseobjspace import ObjSpace
from pypy.interpreter.error import OperationError
from pypy.rlib.rarithmetic import intmask
from pypy.rlib import rstackovf
from pypy.module._file.interp_file import W_File
-from pypy.module._file.interp_stream import StreamErrors, wrap_streamerror
-import sys
+
Py_MARSHAL_VERSION = 2
diff --git a/pypy/module/micronumpy/__init__.py b/pypy/module/micronumpy/__init__.py
--- a/pypy/module/micronumpy/__init__.py
+++ b/pypy/module/micronumpy/__init__.py
@@ -15,14 +15,22 @@
# ufuncs
'abs': 'interp_ufuncs.absolute',
'absolute': 'interp_ufuncs.absolute',
+ 'add': 'interp_ufuncs.add',
'copysign': 'interp_ufuncs.copysign',
+ 'divide': 'interp_ufuncs.divide',
'exp': 'interp_ufuncs.exp',
+ 'fabs': 'interp_ufuncs.fabs',
'floor': 'interp_ufuncs.floor',
'maximum': 'interp_ufuncs.maximum',
'minimum': 'interp_ufuncs.minimum',
+ 'multiply': 'interp_ufuncs.multiply',
'negative': 'interp_ufuncs.negative',
'reciprocal': 'interp_ufuncs.reciprocal',
'sign': 'interp_ufuncs.sign',
+ 'subtract': 'interp_ufuncs.subtract',
+ 'sin': 'interp_ufuncs.sin',
+ 'cos': 'interp_ufuncs.cos',
+ 'tan': 'interp_ufuncs.tan',
}
appleveldefs = {
diff --git a/pypy/module/micronumpy/compile.py b/pypy/module/micronumpy/compile.py
--- a/pypy/module/micronumpy/compile.py
+++ b/pypy/module/micronumpy/compile.py
@@ -3,7 +3,7 @@
It should not be imported by the module itself
"""
-from pypy.module.micronumpy.interp_numarray import FloatWrapper, SingleDimArray
+from pypy.module.micronumpy.interp_numarray import FloatWrapper, SingleDimArray, BaseArray
class BogusBytecode(Exception):
pass
@@ -18,6 +18,14 @@
def wrap(self, x):
return x
+ def issequence_w(self, w_obj):
+ # Completley wrong in the general case, but good enough for this.
+ return isinstance(w_obj, BaseArray)
+
+ def float_w(self, w_obj):
+ assert isinstance(w_obj, float)
+ return w_obj
+
def numpy_compile(bytecode, array_size):
space = TrivialSpace()
stack = []
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -1,54 +1,36 @@
-from pypy.interpreter.baseobjspace import ObjSpace, W_Root, Wrappable
-from pypy.interpreter.error import operationerrfmt
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef, GetSetProperty
+from pypy.module.micronumpy.interp_support import Signature
+from pypy.module.micronumpy import interp_ufuncs
+from pypy.objspace.std.floatobject import float2string as float2string_orig
from pypy.rlib import jit
+from pypy.rlib.rfloat import DTSF_STR_PRECISION
from pypy.rpython.lltypesystem import lltype
from pypy.tool.sourcetools import func_with_new_name
import math
-
-def dummy1(v):
- assert isinstance(v, float)
- return v
-
-def dummy2(v):
- assert isinstance(v, float)
- return v
-
TP = lltype.Array(lltype.Float, hints={'nolength': True})
numpy_driver = jit.JitDriver(greens = ['signature'],
reds = ['result_size', 'i', 'self', 'result'])
+all_driver = jit.JitDriver(greens=['signature'], reds=['i', 'size', 'self'])
+any_driver = jit.JitDriver(greens=['signature'], reds=['i', 'size', 'self'])
+slice_driver1 = jit.JitDriver(greens=['signature'], reds=['i', 'j', 'step', 'stop', 'source', 'dest'])
+slice_driver2 = jit.JitDriver(greens=['signature'], reds=['i', 'j', 'step', 'stop', 'source', 'dest'])
-class Signature(object):
- def __init__(self):
- self.transitions = {}
-
- def transition(self, target):
- if target in self.transitions:
- return self.transitions[target]
- self.transitions[target] = new = Signature()
- return new
-
-def pos(v):
- return v
-def neg(v):
- return -v
-def absolute(v):
- return abs(v)
def add(v1, v2):
return v1 + v2
-def sub(v1, v2):
- return v1 - v2
def mul(v1, v2):
return v1 * v2
-def div(v1, v2):
- return v1 / v2
-def pow(v1, v2):
- return math.pow(v1, v2)
-def mod(v1, v2):
- return math.fmod(v1, v2)
+def maximum(v1, v2):
+ return max(v1, v2)
+def minimum(v1, v2):
+ return min(v1, v2)
+
+def float2string(x):
+ return float2string_orig(x, 'g', DTSF_STR_PRECISION)
class BaseArray(Wrappable):
def __init__(self):
@@ -63,62 +45,185 @@
arr.force_if_needed()
del self.invalidates[:]
- def _unop_impl(function):
- signature = Signature()
+ def _unaryop_impl(w_ufunc):
def impl(self, space):
- new_sig = self.signature.transition(signature)
- res = Call1(
- function,
- self,
- new_sig)
- self.invalidates.append(res)
- return space.wrap(res)
- return func_with_new_name(impl, "uniop_%s_impl" % function.__name__)
+ return w_ufunc(space, self)
+ return func_with_new_name(impl, "unaryop_%s_impl" % w_ufunc.__name__)
- descr_pos = _unop_impl(pos)
- descr_neg = _unop_impl(neg)
- descr_abs = _unop_impl(absolute)
+ descr_pos = _unaryop_impl(interp_ufuncs.positive)
+ descr_neg = _unaryop_impl(interp_ufuncs.negative)
+ descr_abs = _unaryop_impl(interp_ufuncs.absolute)
- def _binop_impl(function):
- signature = Signature()
+ def _binop_impl(w_ufunc):
def impl(self, space, w_other):
- new_sig = self.signature.transition(signature)
+ return w_ufunc(space, self, w_other)
+ return func_with_new_name(impl, "binop_%s_impl" % w_ufunc.__name__)
+
+ descr_add = _binop_impl(interp_ufuncs.add)
+ descr_sub = _binop_impl(interp_ufuncs.subtract)
+ descr_mul = _binop_impl(interp_ufuncs.multiply)
+ descr_div = _binop_impl(interp_ufuncs.divide)
+ descr_pow = _binop_impl(interp_ufuncs.power)
+ descr_mod = _binop_impl(interp_ufuncs.mod)
+
+ def _binop_right_impl(w_ufunc):
+ def impl(self, space, w_other):
+ w_other = FloatWrapper(space.float_w(w_other))
+ return w_ufunc(space, w_other, self)
+ return func_with_new_name(impl, "binop_right_%s_impl" % w_ufunc.__name__)
+
+ descr_radd = _binop_right_impl(interp_ufuncs.add)
+ descr_rsub = _binop_right_impl(interp_ufuncs.subtract)
+ descr_rmul = _binop_right_impl(interp_ufuncs.multiply)
+ descr_rdiv = _binop_right_impl(interp_ufuncs.divide)
+ descr_rpow = _binop_right_impl(interp_ufuncs.power)
+ descr_rmod = _binop_right_impl(interp_ufuncs.mod)
+
+ def _reduce_sum_prod_impl(function, init):
+ reduce_driver = jit.JitDriver(greens=['signature'],
+ reds = ['i', 'size', 'self', 'result'])
+
+ def loop(self, result, size):
+ i = 0
+ while i < size:
+ reduce_driver.jit_merge_point(signature=self.signature,
+ self=self, size=size, i=i,
+ result=result)
+ result = function(result, self.eval(i))
+ i += 1
+ return result
+
+ def impl(self, space):
+ return space.wrap(loop(self, init, self.find_size()))
+ return func_with_new_name(impl, "reduce_%s_impl" % function.__name__)
+
+ def _reduce_max_min_impl(function):
+ reduce_driver = jit.JitDriver(greens=['signature'],
+ reds = ['i', 'size', 'self', 'result'])
+ def loop(self, result, size):
+ i = 1
+ while i < size:
+ reduce_driver.jit_merge_point(signature=self.signature,
+ self=self, size=size, i=i,
+ result=result)
+ result = function(result, self.eval(i))
+ i += 1
+ return result
+
+ def impl(self, space):
+ size = self.find_size()
+ if size == 0:
+ raise OperationError(space.w_ValueError,
+ space.wrap("Can't call %s on zero-size arrays" \
+ % function.__name__))
+ return space.wrap(loop(self, self.eval(0), size))
+ return func_with_new_name(impl, "reduce_%s_impl" % function.__name__)
+
+ def _reduce_argmax_argmin_impl(function):
+ reduce_driver = jit.JitDriver(greens=['signature'],
+ reds = ['i', 'size', 'result', 'self', 'cur_best'])
+ def loop(self, size):
+ result = 0
+ cur_best = self.eval(0)
+ i = 1
+ while i < size:
+ reduce_driver.jit_merge_point(signature=self.signature,
+ self=self, size=size, i=i,
+ result=result, cur_best=cur_best)
+ new_best = function(cur_best, self.eval(i))
+ if new_best != cur_best:
+ result = i
+ cur_best = new_best
+ i += 1
+ return result
+ def impl(self, space):
+ size = self.find_size()
+ if size == 0:
+ raise OperationError(space.w_ValueError,
+ space.wrap("Can't call %s on zero-size arrays" \
+ % function.__name__))
+ return space.wrap(loop(self, size))
+ return func_with_new_name(impl, "reduce_arg%s_impl" % function.__name__)
+
+ def _all(self):
+ size = self.find_size()
+ i = 0
+ while i < size:
+ all_driver.jit_merge_point(signature=self.signature, self=self, size=size, i=i)
+ if not self.eval(i):
+ return False
+ i += 1
+ return True
+ def descr_all(self, space):
+ return space.wrap(self._all())
+
+ def _any(self):
+ size = self.find_size()
+ i = 0
+ while i < size:
+ any_driver.jit_merge_point(signature=self.signature, self=self, size=size, i=i)
+ if self.eval(i):
+ return True
+ i += 1
+ return False
+ def descr_any(self, space):
+ return space.wrap(self._any())
+
+ descr_sum = _reduce_sum_prod_impl(add, 0.0)
+ descr_prod = _reduce_sum_prod_impl(mul, 1.0)
+ descr_max = _reduce_max_min_impl(maximum)
+ descr_min = _reduce_max_min_impl(minimum)
+ descr_argmax = _reduce_argmax_argmin_impl(maximum)
+ descr_argmin = _reduce_argmax_argmin_impl(minimum)
+
+ def descr_dot(self, space, w_other):
if isinstance(w_other, BaseArray):
- res = Call2(
- function,
- self,
- w_other,
- new_sig.transition(w_other.signature)
- )
- w_other.invalidates.append(res)
+ w_res = self.descr_mul(space, w_other)
+ assert isinstance(w_res, BaseArray)
+ return w_res.descr_sum(space)
else:
- w_other = FloatWrapper(space.float_w(w_other))
- res = Call2(
- function,
- self,
- w_other,
- new_sig.transition(w_other.signature)
- )
- self.invalidates.append(res)
- return space.wrap(res)
- return func_with_new_name(impl, "binop_%s_impl" % function.__name__)
+ return self.descr_mul(space, w_other)
- descr_add = _binop_impl(add)
- descr_sub = _binop_impl(sub)
- descr_mul = _binop_impl(mul)
- descr_div = _binop_impl(div)
- descr_pow = _binop_impl(pow)
- descr_mod = _binop_impl(mod)
+ def _getnums(self, comma):
+ if self.find_size() > 1000:
+ nums = [
+ float2string(self.getitem(index))
+ for index in range(3)
+ ]
+ nums.append("..." + "," * comma)
+ nums.extend([
+ float2string(self.getitem(index))
+ for index in range(self.find_size() - 3, self.find_size())
+ ])
+ else:
+ nums = [
+ float2string(self.getitem(index))
+ for index in range(self.find_size())
+ ]
+ return nums
def get_concrete(self):
raise NotImplementedError
+ def descr_copy(self, space):
+ return new_numarray(space, self)
+
def descr_get_shape(self, space):
return space.newtuple([self.descr_len(space)])
def descr_len(self, space):
return self.get_concrete().descr_len(space)
+ def descr_repr(self, space):
+ # Simple implementation so that we can see the array. Needs work.
+ concrete = self.get_concrete()
+ return space.wrap("array([" + ", ".join(concrete._getnums(False)) + "])")
+
+ def descr_str(self, space):
+ # Simple implementation so that we can see the array. Needs work.
+ concrete = self.get_concrete()
+ return space.wrap("[" + " ".join(concrete._getnums(True)) + "]")
+
def descr_getitem(self, space, w_idx):
# TODO: indexing by tuples
start, stop, step, slice_length = space.decode_index4(w_idx, self.find_size())
@@ -130,19 +235,61 @@
res = SingleDimSlice(start, stop, step, slice_length, self, self.signature.transition(SingleDimSlice.static_signature))
return space.wrap(res)
- @unwrap_spec(item=int, value=float)
- def descr_setitem(self, space, item, value):
+ def descr_setitem(self, space, w_idx, w_value):
+ # TODO: indexing by tuples and lists
self.invalidated()
- return self.get_concrete().descr_setitem(space, item, value)
+ start, stop, step, slice_length = space.decode_index4(w_idx,
+ self.find_size())
+ if step == 0:
+ # Single index
+ self.get_concrete().setitem(start, space.float_w(w_value))
+ else:
+ concrete = self.get_concrete()
+ if isinstance(w_value, BaseArray):
+ # for now we just copy if setting part of an array from
+ # part of itself. can be improved.
+ if (concrete.get_root_storage() ==
+ w_value.get_concrete().get_root_storage()):
+ w_value = new_numarray(space, w_value)
+ else:
+ w_value = convert_to_array(space, w_value)
+ concrete.setslice(space, start, stop, step,
+ slice_length, w_value)
def descr_mean(self, space):
- s = 0
- concrete = self.get_concrete()
- size = concrete.find_size()
- for i in xrange(size):
- s += concrete.getitem(i)
- return space.wrap(s / size)
+ return space.wrap(space.float_w(self.descr_sum(space))/self.find_size())
+ def _sliceloop1(self, start, stop, step, source, dest):
+ i = start
+ j = 0
+ while i < stop:
+ slice_driver1.jit_merge_point(signature=source.signature,
+ step=step, stop=stop, i=i, j=j, source=source,
+ dest=dest)
+ dest.storage[i] = source.eval(j)
+ j += 1
+ i += step
+
+ def _sliceloop2(self, start, stop, step, source, dest):
+ i = start
+ j = 0
+ while i > stop:
+ slice_driver2.jit_merge_point(signature=source.signature,
+ step=step, stop=stop, i=i, j=j, source=source,
+ dest=dest)
+ dest.storage[i] = source.eval(j)
+ j += 1
+ i += step
+
+def convert_to_array (space, w_obj):
+ if isinstance(w_obj, BaseArray):
+ return w_obj
+ elif space.issequence_w(w_obj):
+ # Convert to array.
+ return new_numarray(space, w_obj)
+ else:
+ # If it's a scalar
+ return FloatWrapper(space.float_w(w_obj))
class FloatWrapper(BaseArray):
"""
@@ -278,8 +425,8 @@
return self.parent.getitem(self.calc_index(item))
@unwrap_spec(item=int, value=float)
- def descr_setitem(self, space, item, value):
- return self.parent.descr_setitem(space, self.calc_index(item), value)
+ def setitem(self, item, value):
+ return self.parent.setitem(self.calc_index(item), value)
def descr_len(self, space):
return space.wrap(self.find_size())
@@ -293,14 +440,34 @@
def __init__(self, start, stop, step, slice_length, parent, signature):
ViewArray.__init__(self, parent, signature)
+ if isinstance(parent, SingleDimSlice):
+ self.start = parent.calc_index(start)
+ self.stop = parent.calc_index(stop)
+ self.step = parent.step * step
+ self.parent = parent.parent
+ else:
self.start = start
self.stop = stop
self.step = step
+ self.parent = parent
self.size = slice_length
+ def get_root_storage(self):
+ return self.parent.storage
+
def find_size(self):
return self.size
+ def setslice(self, space, start, stop, step, slice_length, arr):
+ start = self.calc_index(start)
+ if stop != -1:
+ stop = self.calc_index(stop)
+ step = self.step * step
+ if step > 0:
+ self._sliceloop1(start, stop, step, arr, self.parent)
+ else:
+ self._sliceloop2(start, stop, step, arr, self.parent)
+
def calc_index(self, item):
return (self.start + item * self.step)
@@ -318,46 +485,45 @@
def get_concrete(self):
return self
+ def get_root_storage(self):
+ return self.storage
+
def find_size(self):
return self.size
def eval(self, i):
return self.storage[i]
- def getindex(self, space, item):
- if item >= self.size:
- raise operationerrfmt(space.w_IndexError,
- '%d above array size', item)
- if item < 0:
- item += self.size
- if item < 0:
- raise operationerrfmt(space.w_IndexError,
- '%d below zero', item)
- return item
-
def descr_len(self, space):
return space.wrap(self.size)
def getitem(self, item):
return self.storage[item]
- @unwrap_spec(item=int, value=float)
- def descr_setitem(self, space, item, value):
- item = self.getindex(space, item)
+ def setitem(self, item, value):
self.invalidated()
self.storage[item] = value
+ def setslice(self, space, start, stop, step, slice_length, arr):
+ if step > 0:
+ self._sliceloop1(start, stop, step, arr, self)
+ else:
+ self._sliceloop2(start, stop, step, arr, self)
+
def __del__(self):
lltype.free(self.storage, flavor='raw')
-def descr_new_numarray(space, w_type, w_size_or_iterable):
+def new_numarray(space, w_size_or_iterable):
l = space.listview(w_size_or_iterable)
arr = SingleDimArray(len(l))
i = 0
for w_elem in l:
arr.storage[i] = space.float_w(space.float(w_elem))
i += 1
- return space.wrap(arr)
+ return arr
+
+def descr_new_numarray(space, w_type, w_size_or_iterable):
+ return space.wrap(new_numarray(space, w_size_or_iterable))
@unwrap_spec(size=int)
def zeros(space, size):
@@ -374,6 +540,7 @@
'numarray',
__new__ = interp2app(descr_new_numarray),
+ copy = interp2app(BaseArray.descr_copy),
shape = GetSetProperty(BaseArray.descr_get_shape),
__len__ = interp2app(BaseArray.descr_len),
@@ -389,6 +556,23 @@
__div__ = interp2app(BaseArray.descr_div),
__pow__ = interp2app(BaseArray.descr_pow),
__mod__ = interp2app(BaseArray.descr_mod),
+ __radd__ = interp2app(BaseArray.descr_radd),
+ __rsub__ = interp2app(BaseArray.descr_rsub),
+ __rmul__ = interp2app(BaseArray.descr_rmul),
+ __rdiv__ = interp2app(BaseArray.descr_rdiv),
+ __rpow__ = interp2app(BaseArray.descr_rpow),
+ __rmod__ = interp2app(BaseArray.descr_rmod),
+ __repr__ = interp2app(BaseArray.descr_repr),
+ __str__ = interp2app(BaseArray.descr_str),
mean = interp2app(BaseArray.descr_mean),
+ sum = interp2app(BaseArray.descr_sum),
+ prod = interp2app(BaseArray.descr_prod),
+ max = interp2app(BaseArray.descr_max),
+ min = interp2app(BaseArray.descr_min),
+ argmax = interp2app(BaseArray.descr_argmax),
+ argmin = interp2app(BaseArray.descr_argmin),
+ all = interp2app(BaseArray.descr_all),
+ any = interp2app(BaseArray.descr_any),
+ dot = interp2app(BaseArray.descr_dot),
)
diff --git a/pypy/module/micronumpy/interp_support.py b/pypy/module/micronumpy/interp_support.py
--- a/pypy/module/micronumpy/interp_support.py
+++ b/pypy/module/micronumpy/interp_support.py
@@ -1,14 +1,14 @@
-
from pypy.rlib.rstruct.runpack import runpack
from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.interpreter.error import OperationError
from pypy.interpreter.gateway import unwrap_spec
-from pypy.interpreter.error import OperationError
-from pypy.module.micronumpy.interp_numarray import SingleDimArray
+
FLOAT_SIZE = rffi.sizeof(lltype.Float)
@unwrap_spec(s=str)
def fromstring(space, s):
+ from pypy.module.micronumpy.interp_numarray import SingleDimArray
length = len(s)
if length % FLOAT_SIZE == 0:
@@ -30,3 +30,13 @@
end += FLOAT_SIZE
return space.wrap(a)
+
+class Signature(object):
+ def __init__(self):
+ self.transitions = {}
+
+ def transition(self, target):
+ if target in self.transitions:
+ return self.transitions[target]
+ self.transitions[target] = new = Signature()
+ return new
\ No newline at end of file
diff --git a/pypy/module/micronumpy/interp_ufuncs.py b/pypy/module/micronumpy/interp_ufuncs.py
--- a/pypy/module/micronumpy/interp_ufuncs.py
+++ b/pypy/module/micronumpy/interp_ufuncs.py
@@ -1,30 +1,35 @@
import math
-from pypy.interpreter.gateway import unwrap_spec
-from pypy.module.micronumpy.interp_numarray import BaseArray, Call1, Call2, Signature
+from pypy.module.micronumpy.interp_support import Signature
from pypy.rlib import rfloat
from pypy.tool.sourcetools import func_with_new_name
-
def ufunc(func):
signature = Signature()
def impl(space, w_obj):
- if isinstance(w_obj, BaseArray):
- w_res = Call1(func, w_obj, w_obj.signature.transition(signature))
- w_obj.invalidates.append(w_res)
+ from pypy.module.micronumpy.interp_numarray import Call1, convert_to_array
+ if space.issequence_w(w_obj):
+ w_obj_arr = convert_to_array(space, w_obj)
+ w_res = Call1(func, w_obj_arr, w_obj_arr.signature.transition(signature))
+ w_obj_arr.invalidates.append(w_res)
return w_res
+ else:
return space.wrap(func(space.float_w(w_obj)))
return func_with_new_name(impl, "%s_dispatcher" % func.__name__)
def ufunc2(func):
signature = Signature()
def impl(space, w_lhs, w_rhs):
- if isinstance(w_lhs, BaseArray) and isinstance(w_rhs, BaseArray):
- new_sig = w_lhs.signature.transition(signature).transition(w_rhs.signature)
- w_res = Call2(func, w_lhs, w_rhs, new_sig)
- w_lhs.invalidates.append(w_res)
- w_rhs.invalidates.append(w_res)
+ from pypy.module.micronumpy.interp_numarray import Call2, convert_to_array
+ if space.issequence_w(w_lhs) or space.issequence_w(w_rhs):
+ w_lhs_arr = convert_to_array(space, w_lhs)
+ w_rhs_arr = convert_to_array(space, w_rhs)
+ new_sig = w_lhs_arr.signature.transition(signature).transition(w_rhs_arr.signature)
+ w_res = Call2(func, w_lhs_arr, w_rhs_arr, new_sig)
+ w_lhs_arr.invalidates.append(w_res)
+ w_rhs_arr.invalidates.append(w_res)
return w_res
+ else:
return space.wrap(func(space.float_w(w_lhs), space.float_w(w_rhs)))
return func_with_new_name(impl, "%s_dispatcher" % func.__name__)
@@ -33,9 +38,17 @@
return abs(value)
@ufunc2
+def add(lvalue, rvalue):
+ return lvalue + rvalue
+
+ at ufunc2
def copysign(lvalue, rvalue):
return rfloat.copysign(lvalue, rvalue)
+ at ufunc2
+def divide(lvalue, rvalue):
+ return lvalue / rvalue
+
@ufunc
def exp(value):
try:
@@ -43,6 +56,10 @@
except OverflowError:
return rfloat.INFINITY
+ at ufunc
+def fabs(value):
+ return math.fabs(value)
+
@ufunc2
def maximum(lvalue, rvalue):
return max(lvalue, rvalue)
@@ -51,6 +68,15 @@
def minimum(lvalue, rvalue):
return min(lvalue, rvalue)
+ at ufunc2
+def multiply(lvalue, rvalue):
+ return lvalue * rvalue
+
+# Used by numarray for __pos__. Not visible from numpy application space.
+ at ufunc
+def positive(value):
+ return value
+
@ufunc
def negative(value):
return -value
@@ -61,6 +87,10 @@
return rfloat.copysign(rfloat.INFINITY, value)
return 1.0 / value
+ at ufunc2
+def subtract(lvalue, rvalue):
+ return lvalue - rvalue
+
@ufunc
def floor(value):
return math.floor(value)
@@ -70,3 +100,23 @@
if value == 0.0:
return 0.0
return rfloat.copysign(1.0, value)
+
+ at ufunc
+def sin(value):
+ return math.sin(value)
+
+ at ufunc
+def cos(value):
+ return math.cos(value)
+
+ at ufunc
+def tan(value):
+ return math.tan(value)
+
+ at ufunc2
+def power(lvalue, rvalue):
+ return math.pow(lvalue, rvalue)
+
+ at ufunc2
+def mod(lvalue, rvalue):
+ return math.fmod(lvalue, rvalue)
diff --git a/pypy/module/micronumpy/test/test_base.py b/pypy/module/micronumpy/test/test_base.py
--- a/pypy/module/micronumpy/test/test_base.py
+++ b/pypy/module/micronumpy/test/test_base.py
@@ -1,12 +1,10 @@
from pypy.conftest import gettestobjspace
from pypy.module.micronumpy.interp_numarray import SingleDimArray, FloatWrapper
-
class BaseNumpyAppTest(object):
def setup_class(cls):
cls.space = gettestobjspace(usemodules=('micronumpy',))
-
class TestSignature(object):
def test_binop_signature(self, space):
ar = SingleDimArray(10)
@@ -26,4 +24,4 @@
v3 = ar.descr_add(space, v1)
v4 = ar.descr_add(space, v2)
- assert v3.signature is v4.signature
\ No newline at end of file
+ assert v3.signature is v4.signature
diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py
--- a/pypy/module/micronumpy/test/test_numarray.py
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -38,11 +38,50 @@
a[2] = 4
assert a[2] == 4
+ def test_copy(self):
+ from numpy import array
+ a = array(range(5))
+ b = a.copy()
+ for i in xrange(5):
+ assert b[i] == a[i]
+
def test_iterator_init(self):
from numpy import array
a = array(range(5))
assert a[3] == 3
+ def test_repr(self):
+ from numpy import array, zeros
+ a = array(range(5))
+ assert repr(a) == "array([0.0, 1.0, 2.0, 3.0, 4.0])"
+ a = zeros(1001)
+ assert repr(a) == "array([0.0, 0.0, 0.0, ..., 0.0, 0.0, 0.0])"
+
+ def test_repr_slice(self):
+ from numpy import array, zeros
+ a = array(range(5))
+ b = a[1::2]
+ assert repr(b) == "array([1.0, 3.0])"
+ a = zeros(2002)
+ b = a[::2]
+ assert repr(b) == "array([0.0, 0.0, 0.0, ..., 0.0, 0.0, 0.0])"
+
+ def test_str(self):
+ from numpy import array, zeros
+ a = array(range(5))
+ assert str(a) == "[0.0 1.0 2.0 3.0 4.0]"
+ a = zeros(1001)
+ assert str(a) == "[0.0 0.0 0.0 ..., 0.0 0.0 0.0]"
+
+ def test_str_slice(self):
+ from numpy import array, zeros
+ a = array(range(5))
+ b = a[1::2]
+ assert str(b) == "[1.0 3.0]"
+ a = zeros(2002)
+ b = a[::2]
+ assert str(b) == "[0.0 0.0 0.0 ..., 0.0 0.0 0.0]"
+
def test_getitem(self):
from numpy import array
a = array(range(5))
@@ -60,6 +99,51 @@
raises(IndexError, "a[5] = 0.0")
raises(IndexError, "a[-6] = 3.0")
+ def test_setslice_array(self):
+ from numpy import array
+ a = array(range(5))
+ b = array(range(2))
+ a[1:4:2] = b
+ assert a[1] == 0.
+ assert a[3] == 1.
+ b[::-1] = b
+ assert b[0] == 1.
+ assert b[1] == 0.
+
+ def test_setslice_of_slice_array(self):
+ from numpy import array, zeros
+ a = zeros(5)
+ a[::2] = array([9., 10., 11.])
+ assert a[0] == 9.
+ assert a[2] == 10.
+ assert a[4] == 11.
+ a[1:4:2][::-1] = array([1., 2.])
+ assert a[0] == 9.
+ assert a[1] == 2.
+ assert a[2] == 10.
+ assert a[3] == 1.
+ assert a[4] == 11.
+ a = zeros(10)
+ a[::2][::-1][::2] = array(range(1,4))
+ assert a[8] == 1.
+ assert a[4] == 2.
+ assert a[0] == 3.
+
+ def test_setslice_list(self):
+ from numpy import array
+ a = array(range(5))
+ b = [0., 1.]
+ a[1:4:2] = b
+ assert a[1] == 0.
+ assert a[3] == 1.
+
+ def test_setslice_constant(self):
+ from numpy import array
+ a = array(range(5))
+ a[1:4:2] = 0.
+ assert a[1] == 0.
+ assert a[3] == 0.
+
def test_len(self):
from numpy import array
a = array(range(5))
@@ -97,6 +181,21 @@
for i in range(5):
assert b[i] == i + 5
+ def test_radd(self):
+ from numpy import array
+ r = 3 + array(range(3))
+ for i in range(3):
+ assert r[i] == i + 3
+
+ def test_add_list(self):
+ from numpy import array
+ a = array(range(5))
+ b = list(reversed(range(5)))
+ c = a + b
+ assert isinstance(c, array)
+ for i in range(5):
+ assert c[i] == 4
+
def test_subtract(self):
from numpy import array
a = array(range(5))
@@ -282,6 +381,82 @@
assert a.mean() == 2.0
assert a[:4].mean() == 1.5
+ def test_sum(self):
+ from numpy import array
+ a = array(range(5))
+ assert a.sum() == 10.0
+ assert a[:4].sum() == 6.0
+
+ def test_prod(self):
+ from numpy import array
+ a = array(range(1,6))
+ assert a.prod() == 120.0
+ assert a[:4].prod() == 24.0
+
+ def test_max(self):
+ from numpy import array
+ a = array([-1.2, 3.4, 5.7, -3.0, 2.7])
+ assert a.max() == 5.7
+ b = array([])
+ raises(ValueError, "b.max()")
+
+ def test_max_add(self):
+ from numpy import array
+ a = array([-1.2, 3.4, 5.7, -3.0, 2.7])
+ assert (a+a).max() == 11.4
+
+ def test_min(self):
+ from numpy import array
+ a = array([-1.2, 3.4, 5.7, -3.0, 2.7])
+ assert a.min() == -3.0
+ b = array([])
+ raises(ValueError, "b.min()")
+
+ def test_argmax(self):
+ from numpy import array
+ a = array([-1.2, 3.4, 5.7, -3.0, 2.7])
+ assert a.argmax() == 2
+ b = array([])
+ raises(ValueError, "b.argmax()")
+
+ def test_argmin(self):
+ from numpy import array
+ a = array([-1.2, 3.4, 5.7, -3.0, 2.7])
+ assert a.argmin() == 3
+ b = array([])
+ raises(ValueError, "b.argmin()")
+
+ def test_all(self):
+ from numpy import array
+ a = array(range(5))
+ assert a.all() == False
+ a[0] = 3.0
+ assert a.all() == True
+ b = array([])
+ assert b.all() == True
+
+ def test_any(self):
+ from numpy import array, zeros
+ a = array(range(5))
+ assert a.any() == True
+ b = zeros(5)
+ assert b.any() == False
+ c = array([])
+ assert c.any() == False
+
+ def test_dot(self):
+ from numpy import array
+ a = array(range(5))
+ assert a.dot(a) == 30.0
+
+ def test_dot_constant(self):
+ from numpy import array
+ a = array(range(5))
+ b = a.dot(2.5)
+ for i in xrange(5):
+ assert b[i] == 2.5*a[i]
+
+
class AppTestSupport(object):
def setup_class(cls):
import struct
diff --git a/pypy/module/micronumpy/test/test_ufuncs.py b/pypy/module/micronumpy/test/test_ufuncs.py
--- a/pypy/module/micronumpy/test/test_ufuncs.py
+++ b/pypy/module/micronumpy/test/test_ufuncs.py
@@ -10,6 +10,40 @@
assert sign(-0.0) == 0.0
assert minimum(2.0, 3.0) == 2.0
+ def test_sequence(self):
+ from numpy import array, negative, minimum
+ a = array(range(3))
+ b = [2.0, 1.0, 0.0]
+ c = 1.0
+ b_neg = negative(b)
+ assert isinstance(b_neg, array)
+ for i in range(3):
+ assert b_neg[i] == -b[i]
+ min_a_b = minimum(a, b)
+ assert isinstance(min_a_b, array)
+ for i in range(3):
+ assert min_a_b[i] == min(a[i], b[i])
+ min_b_a = minimum(b, a)
+ assert isinstance(min_b_a, array)
+ for i in range(3):
+ assert min_b_a[i] == min(a[i], b[i])
+ min_a_c = minimum(a, c)
+ assert isinstance(min_a_c, array)
+ for i in range(3):
+ assert min_a_c[i] == min(a[i], c)
+ min_c_a = minimum(c, a)
+ assert isinstance(min_c_a, array)
+ for i in range(3):
+ assert min_c_a[i] == min(a[i], c)
+ min_b_c = minimum(b, c)
+ assert isinstance(min_b_c, array)
+ for i in range(3):
+ assert min_b_c[i] == min(b[i], c)
+ min_c_b = minimum(c, b)
+ assert isinstance(min_c_b, array)
+ for i in range(3):
+ assert min_c_b[i] == min(b[i], c)
+
def test_negative(self):
from numpy import array, negative
@@ -31,6 +65,33 @@
for i in range(3):
assert b[i] == abs(a[i])
+ def test_add(self):
+ from numpy import array, add
+
+ a = array([-5.0, -0.0, 1.0])
+ b = array([ 3.0, -2.0,-3.0])
+ c = add(a, b)
+ for i in range(3):
+ assert c[i] == a[i] + b[i]
+
+ def test_divide(self):
+ from numpy import array, divide
+
+ a = array([-5.0, -0.0, 1.0])
+ b = array([ 3.0, -2.0,-3.0])
+ c = divide(a, b)
+ for i in range(3):
+ assert c[i] == a[i] / b[i]
+
+ def test_fabs(self):
+ from numpy import array, fabs
+ from math import fabs as math_fabs
+
+ a = array([-5.0, -0.0, 1.0])
+ b = fabs(a)
+ for i in range(3):
+ assert b[i] == math_fabs(a[i])
+
def test_minimum(self):
from numpy import array, minimum
@@ -49,6 +110,15 @@
for i in range(3):
assert c[i] == max(a[i], b[i])
+ def test_multiply(self):
+ from numpy import array, multiply
+
+ a = array([-5.0, -0.0, 1.0])
+ b = array([ 3.0, -2.0,-3.0])
+ c = multiply(a, b)
+ for i in range(3):
+ assert c[i] == a[i] * b[i]
+
def test_sign(self):
from numpy import array, sign
@@ -67,6 +137,15 @@
for i in range(4):
assert b[i] == reference[i]
+ def test_subtract(self):
+ from numpy import array, subtract
+
+ a = array([-5.0, -0.0, 1.0])
+ b = array([ 3.0, -2.0,-3.0])
+ c = subtract(a, b)
+ for i in range(3):
+ assert c[i] == a[i] - b[i]
+
def test_floor(self):
from numpy import array, floor
@@ -99,3 +178,30 @@
except OverflowError:
res = float('inf')
assert b[i] == res
+
+ def test_sin(self):
+ import math
+ from numpy import array, sin
+
+ a = array([0, 1, 2, 3, math.pi, math.pi*1.5, math.pi*2])
+ b = sin(a)
+ for i in range(len(a)):
+ assert b[i] == math.sin(a[i])
+
+ def test_cos(self):
+ import math
+ from numpy import array, cos
+
+ a = array([0, 1, 2, 3, math.pi, math.pi*1.5, math.pi*2])
+ b = cos(a)
+ for i in range(len(a)):
+ assert b[i] == math.cos(a[i])
+
+ def test_tan(self):
+ import math
+ from numpy import array, tan
+
+ a = array([0, 1, 2, 3, math.pi, math.pi*1.5, math.pi*2])
+ b = tan(a)
+ for i in range(len(a)):
+ assert b[i] == math.tan(a[i])
diff --git a/pypy/module/micronumpy/test/test_zjit.py b/pypy/module/micronumpy/test/test_zjit.py
--- a/pypy/module/micronumpy/test/test_zjit.py
+++ b/pypy/module/micronumpy/test/test_zjit.py
@@ -1,12 +1,24 @@
from pypy.jit.metainterp.test.support import LLJitMixin
from pypy.rpython.test.test_llinterp import interpret
from pypy.module.micronumpy.interp_numarray import (SingleDimArray, Signature,
- FloatWrapper, Call2, SingleDimSlice, add, mul, neg, Call1)
+ FloatWrapper, Call2, SingleDimSlice, add, mul, Call1)
from pypy.module.micronumpy.interp_ufuncs import negative
from pypy.module.micronumpy.compile import numpy_compile
+from pypy.rlib.objectmodel import specialize
+from pypy.rlib.nonconst import NonConstant
class FakeSpace(object):
- pass
+ w_ValueError = None
+
+ def issequence_w(self, w_obj):
+ return True
+
+ @specialize.argtype(1)
+ def wrap(self, w_obj):
+ return w_obj
+
+ def float_w(self, w_obj):
+ return float(w_obj)
class TestNumpyJIt(LLJitMixin):
def setup_class(cls):
@@ -36,19 +48,113 @@
"int_lt": 1, "guard_true": 1, "jump": 1})
assert result == f(5)
- def test_neg(self):
+ def test_sum(self):
space = self.space
def f(i):
ar = SingleDimArray(i)
- v = Call1(neg, ar, Signature())
- return v.get_concrete().storage[3]
+ return ar.descr_add(space, ar).descr_sum(space)
result = self.meta_interp(f, [5], listops=True, backendopt=True)
- self.check_loops({"getarrayitem_raw": 1, "float_neg": 1,
- "setarrayitem_raw": 1, "int_add": 1,
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 2,
+ "int_add": 1,
"int_lt": 1, "guard_true": 1, "jump": 1})
+ assert result == f(5)
+ def test_prod(self):
+ space = self.space
+
+ def f(i):
+ ar = SingleDimArray(i)
+ return ar.descr_add(space, ar).descr_prod(space)
+
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 1,
+ "float_mul": 1, "int_add": 1,
+ "int_lt": 1, "guard_true": 1, "jump": 1})
+ assert result == f(5)
+
+ def test_max(self):
+ space = self.space
+
+ def f(i):
+ ar = SingleDimArray(i)
+ j = 0
+ while j < i:
+ ar.get_concrete().storage[j] = float(j)
+ j += 1
+ return ar.descr_add(space, ar).descr_max(space)
+
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 1,
+ "float_gt": 1, "int_add": 1,
+ "int_lt": 1, "guard_true": 1,
+ "guard_false": 1, "jump": 1})
+ assert result == f(5)
+
+ def test_min(self):
+ space = self.space
+
+ def f(i):
+ ar = SingleDimArray(i)
+ j = 0
+ while j < i:
+ ar.get_concrete().storage[j] = float(j)
+ j += 1
+ return ar.descr_add(space, ar).descr_min(space)
+
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 1,
+ "float_lt": 1, "int_add": 1,
+ "int_lt": 1, "guard_true": 2,
+ "jump": 1})
+ assert result == f(5)
+
+ def test_argmin(self):
+ space = self.space
+
+ def f(i):
+ ar = SingleDimArray(i)
+ j = 0
+ while j < i:
+ ar.get_concrete().storage[j] = float(j)
+ j += 1
+ return ar.descr_add(space, ar).descr_argmin(space)
+
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 1,
+ "float_lt": 1, "int_add": 1,
+ "int_lt": 1, "guard_true": 2,
+ "jump": 1})
+ assert result == f(5)
+
+ def test_all(self):
+ space = self.space
+
+ def f(i):
+ ar = SingleDimArray(i)
+ j = 0
+ while j < i:
+ ar.get_concrete().storage[j] = 1.0
+ j += 1
+ return ar.descr_add(space, ar).descr_all(space)
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 1,
+ "int_add": 1, "float_ne": 1,
+ "int_lt": 1, "guard_true": 2, "jump": 1})
+ assert result == f(5)
+
+ def test_any(self):
+ space = self.space
+
+ def f(i):
+ ar = SingleDimArray(i)
+ return ar.descr_add(space, ar).descr_any(space)
+
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 1,
+ "int_add": 1, "float_ne": 1, "guard_false": 1,
+ "int_lt": 1, "guard_true": 1, "jump": 1})
assert result == f(5)
def test_already_forecd(self):
@@ -133,6 +239,28 @@
'int_lt': 1, 'guard_true': 1, 'jump': 1})
assert result == f(5)
+ def test_setslice(self):
+ space = self.space
+
+ def f(i):
+ step = NonConstant(3)
+ ar = SingleDimArray(step*i)
+ ar2 = SingleDimArray(i)
+ ar2.storage[1] = 5.5
+ if NonConstant(False):
+ arg = ar2
+ else:
+ arg = ar2.descr_add(space, ar2)
+ ar.setslice(space, 0, step*i, step, i, arg)
+ return ar.get_concrete().storage[3]
+
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({'getarrayitem_raw': 2,
+ 'float_add' : 1,
+ 'setarrayitem_raw': 1, 'int_add': 2,
+ 'int_lt': 1, 'guard_true': 1, 'jump': 1})
+ assert result == 11.0
+
class TestTranslation(object):
def test_compile(self):
x = numpy_compile('aa+f*f/a-', 10)
diff --git a/pypy/module/mmap/interp_mmap.py b/pypy/module/mmap/interp_mmap.py
--- a/pypy/module/mmap/interp_mmap.py
+++ b/pypy/module/mmap/interp_mmap.py
@@ -1,15 +1,10 @@
-from pypy.rpython.tool import rffi_platform
-from pypy.rpython.lltypesystem import rffi, lltype
from pypy.interpreter.error import OperationError, wrap_oserror
from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.gateway import interp2app, unwrap_spec, NoneNotWrapped
from pypy.rlib import rmmap
from pypy.rlib.rmmap import RValueError, RTypeError, ROverflowError
-import sys
-import os
-import platform
-import stat
+
class W_MMap(Wrappable):
def __init__(self, space, mmap_obj):
diff --git a/pypy/module/pypyjit/interp_jit.py b/pypy/module/pypyjit/interp_jit.py
--- a/pypy/module/pypyjit/interp_jit.py
+++ b/pypy/module/pypyjit/interp_jit.py
@@ -15,7 +15,6 @@
from pypy.interpreter.gateway import unwrap_spec
from pypy.interpreter.baseobjspace import ObjSpace, W_Root
from opcode import opmap
-from pypy.rlib.objectmodel import we_are_translated
from pypy.rlib.nonconst import NonConstant
from pypy.jit.metainterp.resoperation import rop
from pypy.module.pypyjit.interp_resop import debug_merge_point_from_boxes
diff --git a/pypy/module/pypyjit/interp_resop.py b/pypy/module/pypyjit/interp_resop.py
--- a/pypy/module/pypyjit/interp_resop.py
+++ b/pypy/module/pypyjit/interp_resop.py
@@ -1,9 +1,9 @@
from pypy.interpreter.typedef import TypeDef, interp_attrproperty
-from pypy.interpreter.baseobjspace import Wrappable, ObjSpace, W_Root
+from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.gateway import unwrap_spec, interp2app
from pypy.interpreter.pycode import PyCode
-from pypy.rpython.lltypesystem import lltype, llmemory
+from pypy.rpython.lltypesystem import lltype
from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
from pypy.rpython.lltypesystem.rclass import OBJECT
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
@@ -30,7 +30,6 @@
assert res == 8.0 * 300
loop, = log.loops_by_filename(self.filepath)
assert loop.match_by_id('fficall', """
- p16 = getfield_gc(ConstPtr(ptr15), descr=<.* .*Function.inst_name .*>)
guard_not_invalidated(descr=...)
i17 = force_token()
setfield_gc(p0, i17, descr=<.* .*PyFrame.vable_token .*>)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_array.py b/pypy/module/pypyjit/test_pypy_c/test_array.py
--- a/pypy/module/pypyjit/test_pypy_c/test_array.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_array.py
@@ -19,7 +19,7 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i7 = int_lt(i5, i6)
- guard_true(i7, descr=<Guard3>)
+ guard_true(i7, descr=...)
i9 = int_add(i5, 1)
--TICK--
jump(p0, p1, p2, p3, p4, i9, i6, descr=<Loop0>)
@@ -39,11 +39,12 @@
assert log.result == 19507200
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
+ guard_not_invalidated(descr=...)
i13 = int_lt(i7, i9)
- guard_true(i13, descr=<Guard3>)
+ guard_true(i13, descr=...)
i15 = getarrayitem_raw(i10, i7, descr=<.*ArrayNoLengthDescr>)
i16 = int_add_ovf(i8, i15)
- guard_no_overflow(descr=<Guard4>)
+ guard_no_overflow(descr=...)
i18 = int_add(i7, 1)
--TICK--
jump(p0, p1, p2, p3, p4, p5, i18, i16, p8, i9, i10, descr=<Loop0>)
@@ -68,16 +69,17 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i13 = int_lt(i8, 307200)
- guard_true(i13, descr=<Guard3>)
+ guard_true(i13, descr=...)
+ guard_not_invalidated(descr=...)
# the bound check guard on img has been killed (thanks to the asserts)
i14 = getarrayitem_raw(i10, i8, descr=<.*ArrayNoLengthDescr>)
i15 = int_add_ovf(i9, i14)
- guard_no_overflow(descr=<Guard4>)
+ guard_no_overflow(descr=...)
i17 = int_sub(i8, 640)
# the bound check guard on intimg has been killed (thanks to the asserts)
i18 = getarrayitem_raw(i11, i17, descr=<.*ArrayNoLengthDescr>)
i19 = int_add_ovf(i18, i15)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
# on 64bit, there is a guard checking that i19 actually fits into 32bit
...
setarrayitem_raw(i11, i8, _, descr=<.*ArrayNoLengthDescr>)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_call.py b/pypy/module/pypyjit/test_pypy_c/test_call.py
--- a/pypy/module/pypyjit/test_pypy_c/test_call.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_call.py
@@ -80,19 +80,19 @@
#
assert entry_bridge.match_by_id('call', """
p29 = getfield_gc(ConstPtr(ptr28), descr=<GcPtrFieldDescr pypy.objspace.std.celldict.ModuleCell.inst_w_value .*>)
- guard_nonnull_class(p29, ConstClass(Function), descr=<Guard18>)
+ guard_nonnull_class(p29, ConstClass(Function), descr=...)
p33 = getfield_gc(p29, descr=<GcPtrFieldDescr pypy.interpreter.function.Function.inst_code .*>)
- guard_value(p33, ConstPtr(ptr34), descr=<Guard19>)
+ guard_value(p33, ConstPtr(ptr34), descr=...)
p35 = getfield_gc(p29, descr=<GcPtrFieldDescr pypy.interpreter.function.Function.inst_w_func_globals .*>)
p36 = getfield_gc(p29, descr=<GcPtrFieldDescr pypy.interpreter.function.Function.inst_closure .*>)
p38 = call(ConstClass(getexecutioncontext), descr=<GcPtrCallDescr>)
p39 = getfield_gc(p38, descr=<GcPtrFieldDescr pypy.interpreter.executioncontext.ExecutionContext.inst_topframeref .*>)
i40 = force_token()
p41 = getfield_gc(p38, descr=<GcPtrFieldDescr pypy.interpreter.executioncontext.ExecutionContext.inst_w_tracefunc .*>)
- guard_isnull(p41, descr=<Guard20>)
+ guard_isnull(p41, descr=...)
i42 = getfield_gc(p38, descr=<NonGcPtrFieldDescr pypy.interpreter.executioncontext.ExecutionContext.inst_profilefunc .*>)
i43 = int_is_zero(i42)
- guard_true(i43, descr=<Guard21>)
+ guard_true(i43, descr=...)
i50 = force_token()
""")
#
@@ -101,16 +101,16 @@
loop, = log.loops_by_id('call')
assert loop.match("""
i12 = int_lt(i5, i6)
- guard_true(i12, descr=<Guard3>)
+ guard_true(i12, descr=...)
i13 = force_token()
i15 = int_add(i5, 1)
i16 = int_add_ovf(i15, i7)
- guard_no_overflow(descr=<Guard4>)
+ guard_no_overflow(descr=...)
i18 = force_token()
i20 = int_add_ovf(i16, 1)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
i21 = int_add_ovf(i20, i7)
- guard_no_overflow(descr=<Guard6>)
+ guard_no_overflow(descr=...)
--TICK--
jump(p0, p1, p2, p3, p4, i21, i6, i7, p8, p9, p10, p11, descr=<Loop0>)
""")
@@ -146,14 +146,14 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i15 = int_lt(i6, i9)
- guard_true(i15, descr=<Guard3>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_true(i15, descr=...)
+ guard_not_invalidated(descr=...)
i16 = force_token()
i17 = int_add_ovf(i10, i6)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
i18 = force_token()
i19 = int_add_ovf(i10, i17)
- guard_no_overflow(descr=<Guard6>)
+ guard_no_overflow(descr=...)
--TICK--
jump(p0, p1, p2, p3, p4, p5, i19, p7, i17, i9, i10, p11, p12, p13, descr=<Loop0>)
""")
@@ -180,11 +180,11 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i14 = int_lt(i6, i9)
- guard_true(i14, descr=<Guard3>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_true(i14, descr=...)
+ guard_not_invalidated(descr=...)
i15 = force_token()
i17 = int_add_ovf(i8, 1)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
i18 = force_token()
--TICK--
jump(p0, p1, p2, p3, p4, i8, p7, i17, p8, i9, p10, p11, p12, descr=<Loop0>)
@@ -281,25 +281,23 @@
loop0, = log.loops_by_id('g1')
assert loop0.match_by_id('g1', """
i20 = force_token()
- setfield_gc(p4, i19, descr=<.*W_AbstractSeqIterObject.inst_index .*>)
i22 = int_add_ovf(i8, 3)
- guard_no_overflow(descr=<Guard4>)
+ guard_no_overflow(descr=...)
""")
assert loop0.match_by_id('h1', """
i20 = force_token()
i22 = int_add_ovf(i8, 2)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
""")
assert loop0.match_by_id('g2', """
i27 = force_token()
i29 = int_add_ovf(i26, 3)
- guard_no_overflow(descr=<Guard6>)
+ guard_no_overflow(descr=...)
""")
#
loop1, = log.loops_by_id('g3')
assert loop1.match_by_id('g3', """
i21 = force_token()
- setfield_gc(p4, i20, descr=<.* .*W_AbstractSeqIterObject.inst_index .*>)
i23 = int_add_ovf(i9, 3)
guard_no_overflow(descr=...)
""")
@@ -352,7 +350,7 @@
i13 = getfield_gc(p8, descr=<SignedFieldDescr list.length .*>)
i15 = int_add(i13, 1)
call(ConstClass(_ll_list_resize_ge__listPtr_Signed), p8, i15, descr=<VoidCallDescr>)
- guard_no_exception(descr=<Guard4>)
+ guard_no_exception(descr=...)
p17 = getfield_gc(p8, descr=<GcPtrFieldDescr list.items .*>)
p19 = new_with_vtable(ConstClass(W_IntObject))
setfield_gc(p19, i12, descr=<SignedFieldDescr .*W_IntObject.inst_intval .*>)
@@ -404,9 +402,9 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i10 = int_lt(i5, i6)
- guard_true(i10, descr=<Guard3>)
+ guard_true(i10, descr=...)
+ guard_not_invalidated(descr=...)
i120 = int_add(i5, 1)
- guard_not_invalidated(descr=<Guard4>)
--TICK--
jump(..., descr=<Loop0>)
""")
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
@@ -23,3 +23,29 @@
ops = loop.ops_by_id('look')
assert log.opnames(ops) == ['setfield_gc',
'guard_not_invalidated']
+
+ def test_identitydict(self):
+ def fn(n):
+ class X(object):
+ pass
+ x = X()
+ d = {}
+ d[x] = 1
+ res = 0
+ for i in range(300):
+ value = d[x] # ID: getitem
+ res += value
+ return res
+ #
+ log = self.run(fn, [1000])
+ assert log.result == 300
+ loop, = log.loops_by_filename(self.filepath)
+ # check that the call to ll_dict_lookup is not a call_may_force
+ assert loop.match_by_id("getitem", """
+ i25 = call(ConstClass(_ll_1_gc_identityhash__objectPtr), p6, descr=...)
+ ...
+ i28 = call(ConstClass(ll_dict_lookup__dicttablePtr_objectPtr_Signed), p18, p6, i25, descr=...)
+ ...
+ p33 = call(ConstClass(ll_get_value__dicttablePtr_Signed), p18, i28, descr=...)
+ ...
+ """)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_exception.py b/pypy/module/pypyjit/test_pypy_c/test_exception.py
--- a/pypy/module/pypyjit/test_pypy_c/test_exception.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_exception.py
@@ -36,11 +36,11 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i5 = int_is_true(i3)
- guard_true(i5, descr=<Guard3>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_true(i5, descr=...)
+ guard_not_invalidated(descr=...)
--EXC-TICK--
i12 = int_sub_ovf(i3, 1)
- guard_no_overflow(descr=<Guard6>)
+ guard_no_overflow(descr=...)
--TICK--
jump(..., descr=<Loop0>)
""")
@@ -84,8 +84,8 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i7 = int_lt(i4, i5)
- guard_true(i7, descr=<Guard3>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_true(i7, descr=...)
+ guard_not_invalidated(descr=...)
--EXC-TICK--
i14 = int_add(i4, 1)
--TICK--
diff --git a/pypy/module/pypyjit/test_pypy_c/test_globals.py b/pypy/module/pypyjit/test_pypy_c/test_globals.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/pypyjit/test_pypy_c/test_globals.py
@@ -0,0 +1,30 @@
+from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC
+
+
+class TestGlobals(BaseTestPyPyC):
+ def test_load_builtin(self):
+ def main(n):
+ import pypyjit
+
+ i = 0
+ while i < n:
+ l = len # ID: loadglobal
+ i += pypyjit.residual_call(l, "a")
+ return i
+ #
+ log = self.run(main, [500])
+ assert log.result == 500
+ loop, = log.loops_by_filename(self.filepath)
+ assert loop.match_by_id("loadglobal", """
+ p10 = getfield_gc(p0, descr=<GcPtrFieldDescr .*Frame.inst_w_globals .*>)
+ guard_value(p10, ConstPtr(ptr11), descr=...)
+ p12 = getfield_gc(p10, descr=<GcPtrFieldDescr .*W_DictMultiObject.inst_strategy .*>)
+ guard_value(p12, ConstPtr(ptr13), descr=...)
+ p15 = getfield_gc(ConstPtr(ptr14), descr=<GcPtrFieldDescr .*ModuleCell.inst_w_value .*>)
+ guard_isnull(p15, descr=...)
+ guard_not_invalidated(descr=...)
+ p19 = getfield_gc(ConstPtr(p17), descr=<GcPtrFieldDescr .*W_DictMultiObject.inst_strategy .*>)
+ guard_value(p19, ConstPtr(ptr20), descr=...)
+ p22 = getfield_gc(ConstPtr(ptr21), descr=<GcPtrFieldDescr .*ModuleCell.inst_w_value .*>)
+ guard_nonnull(p22, descr=...)
+ """)
\ No newline at end of file
diff --git a/pypy/module/pypyjit/test_pypy_c/test_import.py b/pypy/module/pypyjit/test_pypy_c/test_import.py
--- a/pypy/module/pypyjit/test_pypy_c/test_import.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_import.py
@@ -15,13 +15,13 @@
assert log.result == 500
loop, = log.loops_by_id('import')
assert loop.match_by_id('import', """
+ guard_not_invalidated(descr=...)
p11 = getfield_gc(ConstPtr(ptr10), descr=<GcPtrFieldDescr pypy.objspace.std.celldict.ModuleCell.inst_w_value 8>)
- guard_value(p11, ConstPtr(ptr12), descr=<Guard4>)
- guard_not_invalidated(descr=<Guard5>)
+ guard_value(p11, ConstPtr(ptr12), descr=...)
p14 = getfield_gc(ConstPtr(ptr13), descr=<GcPtrFieldDescr pypy.objspace.std.celldict.ModuleCell.inst_w_value 8>)
p16 = getfield_gc(ConstPtr(ptr15), descr=<GcPtrFieldDescr pypy.objspace.std.celldict.ModuleCell.inst_w_value 8>)
- guard_value(p14, ConstPtr(ptr17), descr=<Guard6>)
- guard_isnull(p16, descr=<Guard7>)
+ guard_value(p14, ConstPtr(ptr17), descr=...)
+ guard_isnull(p16, descr=...)
""")
def test_import_fast_path(self, tmpdir):
diff --git a/pypy/module/pypyjit/test_pypy_c/test_instance.py b/pypy/module/pypyjit/test_pypy_c/test_instance.py
--- a/pypy/module/pypyjit/test_pypy_c/test_instance.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_instance.py
@@ -22,10 +22,10 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i7 = int_lt(i5, i6)
- guard_true(i7, descr=<Guard3>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_true(i7, descr=...)
+ guard_not_invalidated(descr=...)
i9 = int_add_ovf(i5, 2)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
--TICK--
jump(p0, p1, p2, p3, p4, i9, i6, descr=<Loop0>)
""")
@@ -47,10 +47,10 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i9 = int_lt(i5, i6)
- guard_true(i9, descr=<Guard3>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_true(i9, descr=...)
+ guard_not_invalidated(descr=...)
i10 = int_add_ovf(i5, i7)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
--TICK--
jump(p0, p1, p2, p3, p4, i10, i6, p7, i7, p8, descr=<Loop0>)
""")
diff --git a/pypy/module/pypyjit/test_pypy_c/test_math.py b/pypy/module/pypyjit/test_pypy_c/test_math.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/pypyjit/test_pypy_c/test_math.py
@@ -0,0 +1,32 @@
+from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC
+
+
+class TestMath(BaseTestPyPyC):
+ def test_log(self):
+ def main(n):
+ import math
+
+ i = 1
+ s = 0.0
+ while i < n:
+ s += math.log(i) - math.log10(i)
+ i += 1
+ return s
+ log = self.run(main, [500])
+ assert round(log.result, 6) == round(main(500), 6)
+ loop, = log.loops_by_filename(self.filepath)
+ assert loop.match("""
+ i2 = int_lt(i0, i1)
+ guard_true(i2, descr=...)
+ guard_not_invalidated(descr=...)
+ f1 = cast_int_to_float(i0)
+ i3 = float_le(f1, 0)
+ guard_false(i3, descr=...)
+ f2 = call(ConstClass(log), f1, descr=<FloatCallDescr>)
+ f3 = call(ConstClass(log10), f1, descr=<FloatCallDescr>)
+ f4 = float_sub(f2, f3)
+ f5 = float_add(f0, f4)
+ i4 = int_add(i0, 1)
+ --TICK--
+ jump(..., descr=<Loop0>)
+ """)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_min_max.py b/pypy/module/pypyjit/test_pypy_c/test_min_max.py
--- a/pypy/module/pypyjit/test_pypy_c/test_min_max.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_min_max.py
@@ -17,6 +17,7 @@
assert loop.match("""
i7 = int_lt(i4, 300)
guard_true(i7, descr=...)
+ guard_not_invalidated(descr=...)
i9 = int_add_ovf(i5, 3000)
guard_no_overflow(descr=...)
i11 = int_add(i4, 1)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_misc.py b/pypy/module/pypyjit/test_pypy_c/test_misc.py
--- a/pypy/module/pypyjit/test_pypy_c/test_misc.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_misc.py
@@ -84,7 +84,7 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i9 = float_lt(f5, f7)
- guard_true(i9, descr=<Guard3>)
+ guard_true(i9, descr=...)
f10 = float_add(f8, f5)
--TICK--
jump(p0, p1, p2, p3, p4, f10, p6, f7, f8, descr=<Loop0>)
@@ -107,19 +107,19 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i16 = int_ge(i11, i12)
- guard_false(i16, descr=<Guard3>)
+ guard_false(i16, descr=...)
i17 = int_mul(i11, i14)
i18 = int_add(i15, i17)
i20 = int_add(i11, 1)
i21 = force_token()
setfield_gc(p4, i20, descr=<.* .*W_AbstractSeqIterObject.inst_index .*>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_not_invalidated(descr=...)
i23 = int_lt(i18, 0)
- guard_false(i23, descr=<Guard5>)
+ guard_false(i23, descr=...)
i25 = int_ge(i18, i9)
- guard_false(i25, descr=<Guard6>)
+ guard_false(i25, descr=...)
i27 = int_add_ovf(i7, i18)
- guard_no_overflow(descr=<Guard7>)
+ guard_no_overflow(descr=...)
--TICK--
jump(..., descr=<Loop0>)
""")
@@ -164,20 +164,20 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i16 = int_ge(i12, i13)
- guard_false(i16, descr=<Guard3>)
+ guard_false(i16, descr=...)
p17 = getarrayitem_gc(p15, i12, descr=<GcPtrArrayDescr>)
i19 = int_add(i12, 1)
setfield_gc(p9, i19, descr=<SignedFieldDescr .*W_AbstractSeqIterObject.inst_index .*>)
- guard_nonnull_class(p17, 146982464, descr=<Guard4>)
+ guard_nonnull_class(p17, 146982464, descr=...)
i21 = getfield_gc(p17, descr=<SignedFieldDescr .*W_ArrayTypei.inst_len .*>)
i23 = int_lt(0, i21)
- guard_true(i23, descr=<Guard5>)
+ guard_true(i23, descr=...)
i24 = getfield_gc(p17, descr=<NonGcPtrFieldDescr .*W_ArrayTypei.inst_buffer .*>)
i25 = getarrayitem_raw(i24, 0, descr=<.*>)
i27 = int_lt(1, i21)
- guard_false(i27, descr=<Guard6>)
+ guard_false(i27, descr=...)
i28 = int_add_ovf(i10, i25)
- guard_no_overflow(descr=<Guard7>)
+ guard_no_overflow(descr=...)
--TICK--
jump(p0, p1, p2, p3, p4, p5, p6, i28, i25, p9, p10, p11, i19, i13, p14, p15, descr=<Loop0>)
""")
@@ -201,9 +201,9 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i11 = int_lt(i7, 300)
- guard_true(i11, descr=<Guard3>)
+ guard_true(i11, descr=...)
i12 = int_add_ovf(i8, i9)
- guard_no_overflow(descr=<Guard4>)
+ guard_no_overflow(descr=...)
i14 = int_add(i7, 1)
--TICK--
jump(..., descr=...)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py
--- a/pypy/module/pypyjit/test_pypy_c/test_string.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_string.py
@@ -16,27 +16,92 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i14 = int_lt(i6, i9)
- guard_true(i14, descr=<Guard42>)
+ guard_true(i14, descr=...)
+ guard_not_invalidated(descr=...)
i15 = int_mod(i6, i10)
i17 = int_rshift(i15, 63)
i18 = int_and(i10, i17)
i19 = int_add(i15, i18)
i21 = int_lt(i19, 0)
- guard_false(i21, descr=<Guard43>)
+ guard_false(i21, descr=...)
i22 = int_ge(i19, i10)
- guard_false(i22, descr=<Guard44>)
+ guard_false(i22, descr=...)
i23 = strgetitem(p11, i19)
i24 = int_ge(i19, i12)
- guard_false(i24, descr=<Guard45>)
+ guard_false(i24, descr=...)
i25 = unicodegetitem(p13, i19)
- guard_not_invalidated(descr=<Guard46>)
p27 = newstr(1)
strsetitem(p27, 0, i23)
p30 = call(ConstClass(ll_str2unicode__rpy_stringPtr), p27, descr=<GcPtrCallDescr>)
- guard_no_exception(descr=<Guard47>)
+ guard_no_exception(descr=...)
i32 = call(ConstClass(_ll_2_str_eq_checknull_char__rpy_unicodePtr_UniChar), p30, i25, descr=<SignedCallDescr>)
- guard_true(i32, descr=<Guard48>)
+ guard_true(i32, descr=...)
i34 = int_add(i6, 1)
--TICK--
jump(p0, p1, p2, p3, p4, p5, i34, p7, p8, i9, i10, p11, i12, p13, descr=<Loop4>)
- """)
\ No newline at end of file
+ """)
+
+ def test_long(self):
+ def main(n):
+ import string
+ i = 1
+ while i < n:
+ i += int(long(string.digits[i % len(string.digits)], 16))
+ return i
+
+ log = self.run(main, [1000])
+ assert log.result == main(1000)
+ loop, = log.loops_by_filename(self.filepath)
+ assert loop.match("""
+ i11 = int_lt(i6, i7)
+ guard_true(i11, descr=...)
+ guard_not_invalidated(descr=...)
+ i13 = int_eq(i6, -9223372036854775808)
+ guard_false(i13, descr=...)
+ i15 = int_mod(i6, i8)
+ i17 = int_rshift(i15, 63)
+ i18 = int_and(i8, i17)
+ i19 = int_add(i15, i18)
+ i21 = int_lt(i19, 0)
+ guard_false(i21, descr=...)
+ i22 = int_ge(i19, i8)
+ guard_false(i22, descr=...)
+ i23 = strgetitem(p10, i19)
+ p25 = newstr(1)
+ strsetitem(p25, 0, i23)
+ p28 = call(ConstClass(strip_spaces), p25, descr=<GcPtrCallDescr>)
+ guard_no_exception(descr=...)
+ i29 = strlen(p28)
+ i30 = int_is_true(i29)
+ guard_true(i30, descr=...)
+ i32 = int_sub(i29, 1)
+ i33 = strgetitem(p28, i32)
+ i35 = int_eq(i33, 108)
+ guard_false(i35, descr=...)
+ i37 = int_eq(i33, 76)
+ guard_false(i37, descr=...)
+ i39 = strgetitem(p28, 0)
+ i41 = int_eq(i39, 45)
+ guard_false(i41, descr=...)
+ i43 = int_eq(i39, 43)
+ guard_false(i43, descr=...)
+ i43 = call(ConstClass(ll_startswith__rpy_stringPtr_rpy_stringPtr), p28, ConstPtr(ptr42), descr=<BoolCallDescr>)
+ guard_false(i43, descr=...)
+ i46 = call(ConstClass(ll_startswith__rpy_stringPtr_rpy_stringPtr), p28, ConstPtr(ptr45), descr=<BoolCallDescr>)
+ guard_false(i46, descr=...)
+ p51 = new_with_vtable(21136408)
+ setfield_gc(p51, p28, descr=<GcPtrFieldDescr .*NumberStringParser.inst_literal .*>)
+ setfield_gc(p51, ConstPtr(ptr51), descr=<GcPtrFieldDescr pypy.objspace.std.strutil.NumberStringParser.inst_fname .*>)
+ setfield_gc(p51, 1, descr=<SignedFieldDescr .*NumberStringParser.inst_sign .*>)
+ setfield_gc(p51, 16, descr=<SignedFieldDescr .*NumberStringParser.inst_base .*>)
+ setfield_gc(p51, p28, descr=<GcPtrFieldDescr .*NumberStringParser.inst_s .*>)
+ setfield_gc(p51, i29, descr=<SignedFieldDescr .*NumberStringParser.inst_n .*>)
+ p55 = call(ConstClass(parse_digit_string), p51, descr=<GcPtrCallDescr>)
+ guard_no_exception(descr=...)
+ i57 = call(ConstClass(rbigint.toint), p55, descr=<SignedCallDescr>)
+ guard_no_exception(descr=...)
+ i58 = int_add_ovf(i6, i57)
+ guard_no_overflow(descr=...)
+ --TICK--
+ jump(p0, p1, p2, p3, p4, p5, i58, i7, i8, p9, p10, descr=<Loop4>)
+ """)
diff --git a/pypy/module/rctime/interp_time.py b/pypy/module/rctime/interp_time.py
--- a/pypy/module/rctime/interp_time.py
+++ b/pypy/module/rctime/interp_time.py
@@ -6,7 +6,6 @@
from pypy.rlib.rarithmetic import ovfcheck_float_to_int
from pypy.rlib import rposix
from pypy.translator.tool.cbuild import ExternalCompilationInfo
-import math
import os
import sys
import time as pytime
diff --git a/pypy/module/select/interp_select.py b/pypy/module/select/interp_select.py
--- a/pypy/module/select/interp_select.py
+++ b/pypy/module/select/interp_select.py
@@ -1,9 +1,7 @@
-import math
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.interpreter.error import (
- OperationError, operationerrfmt, wrap_oserror)
+from pypy.interpreter.error import OperationError, wrap_oserror
from pypy.rlib import rpoll
import errno
diff --git a/pypy/module/select/test/test_epoll.py b/pypy/module/select/test/test_epoll.py
--- a/pypy/module/select/test/test_epoll.py
+++ b/pypy/module/select/test/test_epoll.py
@@ -138,7 +138,7 @@
expected.sort()
assert events == expected
- assert then - now < 0.01
+ assert then - now < 0.02
now = time.time()
events = ep.poll(timeout=2.1, maxevents=4)
@@ -151,7 +151,7 @@
now = time.time()
events = ep.poll(1, 4)
then = time.time()
- assert then - now < 0.01
+ assert then - now < 0.02
events.sort()
expected = [
@@ -168,7 +168,7 @@
now = time.time()
events = ep.poll(1, 4)
then = time.time()
- assert then - now < 0.01
+ assert then - now < 0.02
expected = [(server.fileno(), select.EPOLLOUT)]
assert events == expected
@@ -192,7 +192,7 @@
now = time.time()
ep.poll(1, 4)
then = time.time()
- assert then - now < 0.01
+ assert then - now < 0.02
server.close()
ep.unregister(fd)
diff --git a/pypy/module/struct/formatiterator.py b/pypy/module/struct/formatiterator.py
--- a/pypy/module/struct/formatiterator.py
+++ b/pypy/module/struct/formatiterator.py
@@ -1,11 +1,10 @@
-
from pypy.interpreter.error import OperationError
from pypy.rlib.objectmodel import specialize
from pypy.rlib.rstruct.error import StructError
from pypy.rlib.rstruct.standardfmttable import PACK_ACCEPTS_BROKEN_INPUT
-from pypy.rlib.rstruct.formatiterator import (FormatIterator,
- CalcSizeFormatIterator)
+from pypy.rlib.rstruct.formatiterator import FormatIterator
+
class PackFormatIterator(FormatIterator):
diff --git a/pypy/module/struct/interp_struct.py b/pypy/module/struct/interp_struct.py
--- a/pypy/module/struct/interp_struct.py
+++ b/pypy/module/struct/interp_struct.py
@@ -1,10 +1,7 @@
from pypy.interpreter.gateway import unwrap_spec
-from pypy.interpreter.error import OperationError
+from pypy.module.struct.formatiterator import PackFormatIterator, UnpackFormatIterator
from pypy.rlib.rstruct.error import StructError
-from pypy.module.struct.formatiterator import CalcSizeFormatIterator
-from pypy.module.struct.formatiterator import PackFormatIterator
-from pypy.module.struct.formatiterator import UnpackFormatIterator
-
+from pypy.rlib.rstruct.formatiterator import CalcSizeFormatIterator
@unwrap_spec(format=str)
def calcsize(space, format):
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
@@ -476,7 +476,7 @@
assert isinstance(vi[0], int)
assert isinstance(vi[1], int)
assert isinstance(vi[2], int)
- assert vi[3] in ("alpha", "beta", "candidate", "final")
+ assert vi[3] in ("alpha", "beta", "candidate", "dev", "final")
assert isinstance(vi[4], int)
def test_allattributes(self):
@@ -523,4 +523,4 @@
# If this ever actually becomes a compilation option this test should
# be changed.
- assert sys.float_repr_style == "short"
\ No newline at end of file
+ assert sys.float_repr_style == "short"
diff --git a/pypy/module/sys/version.py b/pypy/module/sys/version.py
--- a/pypy/module/sys/version.py
+++ b/pypy/module/sys/version.py
@@ -10,7 +10,7 @@
CPYTHON_VERSION = (2, 7, 1, "final", 42) #XXX # sync patchlevel.h
CPYTHON_API_VERSION = 1013 #XXX # sync with include/modsupport.h
-PYPY_VERSION = (1, 5, 0, "alpha", 0) #XXX # sync patchlevel.h
+PYPY_VERSION = (1, 6, 0, "dev", 1) #XXX # sync patchlevel.h
if platform.name == 'msvc':
COMPILER_INFO = 'MSC v.%d 32 bit' % (platform.version * 10 + 600)
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py b/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py
--- a/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py
+++ b/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py
@@ -132,6 +132,16 @@
# You cannot assing character format codes as restype any longer
raises(TypeError, setattr, f, "restype", "i")
+
+ def test_truncate_python_longs(self):
+ f = dll._testfunc_i_bhilfd
+ f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double]
+ f.restype = c_int
+ x = sys.maxint * 2
+ result = f(x, x, x, x, 0, 0)
+ assert result == -8
+
+
def test_floatresult(self):
f = dll._testfunc_f_bhilfd
f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double]
@@ -411,6 +421,23 @@
result = f("abcd", ord("b"))
assert result == "bcd"
+ def test_keepalive_buffers(self, monkeypatch):
+ import gc
+ f = dll.my_strchr
+ f.argtypes = [c_char_p]
+ f.restype = c_char_p
+ #
+ orig__call_funcptr = f._call_funcptr
+ def _call_funcptr(funcptr, *newargs):
+ gc.collect()
+ gc.collect()
+ gc.collect()
+ return orig__call_funcptr(funcptr, *newargs)
+ monkeypatch.setattr(f, '_call_funcptr', _call_funcptr)
+ #
+ result = f("abcd", ord("b"))
+ assert result == "bcd"
+
def test_caching_bug_1(self):
# the same test as test_call_some_args, with two extra lines
# in the middle that trigger caching in f._ptr, which then
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_guess_argtypes.py b/pypy/module/test_lib_pypy/ctypes_tests/test_guess_argtypes.py
--- a/pypy/module/test_lib_pypy/ctypes_tests/test_guess_argtypes.py
+++ b/pypy/module/test_lib_pypy/ctypes_tests/test_guess_argtypes.py
@@ -12,7 +12,7 @@
from _ctypes.function import CFuncPtr
def guess(value):
- cobj, ctype = CFuncPtr._conv_param(None, value)
+ _, cobj, ctype = CFuncPtr._conv_param(None, value)
return ctype
## cobj = CFuncPtr._conv_param(None, value)
## return type(cobj)
diff --git a/pypy/module/thread/ll_thread.py b/pypy/module/thread/ll_thread.py
--- a/pypy/module/thread/ll_thread.py
+++ b/pypy/module/thread/ll_thread.py
@@ -1,9 +1,7 @@
from pypy.rpython.lltypesystem import rffi, lltype, llmemory
-from pypy.rpython.tool import rffi_platform as platform
from pypy.translator.tool.cbuild import ExternalCompilationInfo
-import py, os
-from pypy.rpython.extregistry import ExtRegistryEntry
+import py
from pypy.rlib import jit
from pypy.rlib.debug import ll_assert
from pypy.rlib.objectmodel import we_are_translated
@@ -21,6 +19,7 @@
'RPyThreadAcquireLock', 'RPyThreadReleaseLock',
'RPyThreadYield',
'RPyThreadGetStackSize', 'RPyThreadSetStackSize',
+ 'RPyOpaqueDealloc_ThreadLock',
'RPyThreadAfterFork']
)
@@ -52,6 +51,9 @@
c_thread_lock_init = llexternal('RPyThreadLockInit', [TLOCKP], rffi.INT,
threadsafe=False) # may add in a global list
+c_thread_lock_dealloc = llexternal('RPyOpaqueDealloc_ThreadLock', [TLOCKP],
+ lltype.Void,
+ threadsafe=True)
c_thread_acquirelock = llexternal('RPyThreadAcquireLock', [TLOCKP, rffi.INT],
rffi.INT,
threadsafe=True) # release the GIL
@@ -156,6 +158,9 @@
return ll_lock
def free_ll_lock(ll_lock):
+ c_thread_acquirelock(ll_lock, 0)
+ c_thread_releaselock(ll_lock)
+ c_thread_lock_dealloc(ll_lock)
lltype.free(ll_lock, flavor='raw', track_allocation=False)
def acquire_NOAUTO(ll_lock, flag):
diff --git a/pypy/module/thread/os_local.py b/pypy/module/thread/os_local.py
--- a/pypy/module/thread/os_local.py
+++ b/pypy/module/thread/os_local.py
@@ -1,9 +1,7 @@
from pypy.module.thread import ll_thread as thread
-from pypy.interpreter.error import OperationError
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.typedef import TypeDef, interp2app
-from pypy.interpreter.typedef import GetSetProperty, descr_get_dict
-from pypy.interpreter.typedef import descr_set_dict
+from pypy.interpreter.typedef import (TypeDef, interp2app, GetSetProperty,
+ descr_get_dict)
class Local(Wrappable):
diff --git a/pypy/module/thread/os_thread.py b/pypy/module/thread/os_thread.py
--- a/pypy/module/thread/os_thread.py
+++ b/pypy/module/thread/os_thread.py
@@ -6,7 +6,6 @@
from pypy.module.thread.error import wrap_thread_error
from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.interpreter.gateway import unwrap_spec, NoneNotWrapped, Arguments
-from pypy.rlib.objectmodel import free_non_gc_object
# Here are the steps performed to start a new thread:
#
diff --git a/pypy/module/thread/test/test_import_lock.py b/pypy/module/thread/test/test_import_lock.py
--- a/pypy/module/thread/test/test_import_lock.py
+++ b/pypy/module/thread/test/test_import_lock.py
@@ -66,6 +66,9 @@
def test_lock(self, space, monkeypatch):
from pypy.module.imp.importing import getimportlock, importhook
+ # Force importing the module _file now
+ space.builtin.get('file')
+
# Monkeypatch the import lock and add a counter
importlock = getimportlock(space)
original_acquire = importlock.acquire_lock
diff --git a/pypy/module/unicodedata/generate_unicodedb.py b/pypy/module/unicodedata/generate_unicodedb.py
--- a/pypy/module/unicodedata/generate_unicodedb.py
+++ b/pypy/module/unicodedata/generate_unicodedb.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-import pprint
-
MAXUNICODE = 0x10FFFF # the value of sys.maxunicode of wide Python builds
MANDATORY_LINE_BREAKS = ["BK", "CR", "LF", "NL"] # line break categories
@@ -662,7 +660,7 @@
'''
def main():
- import re, sys
+ import sys
from optparse import OptionParser
infile = None
outfile = sys.stdout
diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py
--- a/pypy/module/zlib/interp_zlib.py
+++ b/pypy/module/zlib/interp_zlib.py
@@ -20,25 +20,15 @@
return intmask((x ^ SIGN_EXTEND2) - SIGN_EXTEND2)
- at unwrap_spec(string='bufferstr')
-def crc32(space, string, w_start = rzlib.CRC32_DEFAULT_START):
+ at unwrap_spec(string='bufferstr', start='truncatedint')
+def crc32(space, string, start = rzlib.CRC32_DEFAULT_START):
"""
crc32(string[, start]) -- Compute a CRC-32 checksum of string.
An optional starting value can be specified. The returned checksum is
an integer.
"""
- if space.is_true(space.isinstance(w_start, space.w_long)):
- num = space.bigint_w(w_start)
- ustart = num.uintmask()
- elif space.is_true(space.isinstance(w_start, space.w_int)):
- start = space.int_w(w_start)
ustart = r_uint(start)
- else:
- raise OperationError(space.w_TypeError,
- space.wrap("crc32() argument 2 must "
- "be integer<k>, not str"))
-
checksum = rzlib.crc32(string, ustart)
# This is, perhaps, a little stupid. zlib returns the checksum unsigned.
@@ -51,7 +41,7 @@
return space.wrap(checksum)
- at unwrap_spec(string='bufferstr', start=r_uint)
+ at unwrap_spec(string='bufferstr', start='truncatedint')
def adler32(space, string, start=rzlib.ADLER32_DEFAULT_START):
"""
adler32(string[, start]) -- Compute an Adler-32 checksum of string.
@@ -59,7 +49,8 @@
An optional starting value can be specified. The returned checksum is
an integer.
"""
- checksum = rzlib.adler32(string, start)
+ ustart = r_uint(start)
+ checksum = rzlib.adler32(string, ustart)
# See comments in crc32() for the following line
checksum = unsigned_to_signed_32bit(checksum)
diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py
--- a/pypy/module/zlib/test/test_zlib.py
+++ b/pypy/module/zlib/test/test_zlib.py
@@ -78,15 +78,17 @@
def test_crc32_negative_long_start(self):
v = self.zlib.crc32('', -1L)
assert v == -1
+ assert self.zlib.crc32('foo', -99999999999999999999999) == 1611238463
def test_crc32_long_start(self):
import sys
v = self.zlib.crc32('', sys.maxint*2)
assert v == -2
+ assert self.zlib.crc32('foo', 99999999999999999999999) == 1635107045
def test_adler32(self):
"""
- When called with a string, zlib.crc32 should compute its adler 32
+ When called with a string, zlib.adler32() should compute its adler 32
checksum and return it as a signed 32 bit integer.
On 64-bit machines too
(it is a bug in CPython < 2.6 to return unsigned values in this case).
@@ -113,6 +115,9 @@
helloworldsum = self.zlib.adler32(world, hellosum)
assert helloworldsum == self.zlib.adler32(hello + world)
+ assert self.zlib.adler32('foo', -1) == 45547858
+ assert self.zlib.adler32('foo', 99999999999999999999999) == -114818734
+
def test_invalidLevel(self):
"""
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -28,6 +28,13 @@
return w_delattr
object_delattr._annspecialcase_ = '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
+object_hash._annspecialcase_ = 'specialize:memo'
+
def raiseattrerror(space, w_obj, name, w_descr=None):
w_type = space.type(w_obj)
typename = w_type.getname(space)
diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py
--- a/pypy/objspace/std/dictmultiobject.py
+++ b/pypy/objspace/std/dictmultiobject.py
@@ -157,11 +157,15 @@
return self.erase(None)
def switch_to_correct_strategy(self, w_dict, w_key):
- #XXX implement other strategies later
+ withidentitydict = self.space.config.objspace.std.withidentitydict
if type(w_key) is self.space.StringObjectCls:
self.switch_to_string_strategy(w_dict)
- elif self.space.is_w(self.space.type(w_key), self.space.w_int):
+ return
+ w_type = self.space.type(w_key)
+ if self.space.is_w(w_type, self.space.w_int):
self.switch_to_int_strategy(w_dict)
+ elif withidentitydict and w_type.compares_by_identity():
+ self.switch_to_identity_strategy(w_dict)
else:
self.switch_to_object_strategy(w_dict)
@@ -177,6 +181,13 @@
w_dict.strategy = strategy
w_dict.dstorage = storage
+ def switch_to_identity_strategy(self, w_dict):
+ from pypy.objspace.std.identitydict import IdentityDictStrategy
+ strategy = self.space.fromcache(IdentityDictStrategy)
+ storage = strategy.get_empty_storage()
+ w_dict.strategy = strategy
+ w_dict.dstorage = storage
+
def switch_to_object_strategy(self, w_dict):
strategy = self.space.fromcache(ObjectDictStrategy)
storage = strategy.get_empty_storage()
@@ -338,7 +349,6 @@
def getitem(self, w_dict, w_key):
space = self.space
-
if self.is_correct_type(w_key):
return self.unerase(w_dict.dstorage).get(self.unwrap(w_key), None)
elif self._never_equal_to(space.type(w_key)):
@@ -404,6 +414,7 @@
def keys(self, w_dict):
return self.unerase(w_dict.dstorage).keys()
+
class StringDictStrategy(AbstractTypedStrategy, DictStrategy):
erase, unerase = rerased.new_erasing_pair("string")
@@ -448,7 +459,9 @@
return StrIteratorImplementation(self.space, self, w_dict)
-class StrIteratorImplementation(IteratorImplementation):
+class _WrappedIteratorMixin(object):
+ _mixin_ = True
+
def __init__(self, space, strategy, dictimplementation):
IteratorImplementation.__init__(self, space, dictimplementation)
self.iterator = strategy.unerase(dictimplementation.dstorage).iteritems()
@@ -460,6 +473,23 @@
else:
return None, None
+class _UnwrappedIteratorMixin:
+ _mixin_ = True
+
+ def __init__(self, space, strategy, dictimplementation):
+ IteratorImplementation.__init__(self, space, dictimplementation)
+ self.iterator = strategy.unerase(dictimplementation.dstorage).iteritems()
+
+ def next_entry(self):
+ # note that this 'for' loop only runs once, at most
+ for w_key, w_value in self.iterator:
+ return w_key, w_value
+ else:
+ return None, None
+
+
+class StrIteratorImplementation(_WrappedIteratorMixin, IteratorImplementation):
+ pass
class IntDictStrategy(AbstractTypedStrategy, DictStrategy):
erase, unerase = rerased.new_erasing_pair("int")
@@ -490,31 +520,11 @@
def iter(self, w_dict):
return IntIteratorImplementation(self.space, self, w_dict)
-class IntIteratorImplementation(IteratorImplementation):
- def __init__(self, space, strategy, dictimplementation):
- IteratorImplementation.__init__(self, space, dictimplementation)
- self.iterator = strategy.unerase(dictimplementation.dstorage).iteritems()
+class IntIteratorImplementation(_WrappedIteratorMixin, IteratorImplementation):
+ pass
- def next_entry(self):
- # note that this 'for' loop only runs once, at most
- for key, w_value in self.iterator:
- return self.space.wrap(key), w_value
- else:
- return None, None
-
-
-class ObjectIteratorImplementation(IteratorImplementation):
- def __init__(self, space, strategy, dictimplementation):
- IteratorImplementation.__init__(self, space, dictimplementation)
- self.iterator = strategy.unerase(dictimplementation.dstorage).iteritems()
-
- def next_entry(self):
- # note that this 'for' loop only runs once, at most
- for w_key, w_value in self.iterator:
- return w_key, w_value
- else:
- return None, None
-
+class ObjectIteratorImplementation(_UnwrappedIteratorMixin, IteratorImplementation):
+ pass
init_signature = Signature(['seq_or_map'], None, 'kwargs')
init_defaults = [None]
@@ -736,6 +746,8 @@
class W_DictMultiIterObject(W_Object):
from pypy.objspace.std.dicttype import dictiter_typedef as typedef
+ _immutable_fields_ = ["iteratorimplementation", "itertype"]
+
def __init__(w_self, space, iteratorimplementation, itertype):
w_self.space = space
w_self.iteratorimplementation = iteratorimplementation
diff --git a/pypy/objspace/std/dictproxyobject.py b/pypy/objspace/std/dictproxyobject.py
--- a/pypy/objspace/std/dictproxyobject.py
+++ b/pypy/objspace/std/dictproxyobject.py
@@ -86,7 +86,7 @@
def clear(self, w_dict):
self.unerase(w_dict.dstorage).dict_w.clear()
- self.unerase(w_dict.dstorage).mutated()
+ self.unerase(w_dict.dstorage).mutated(None)
class DictProxyIteratorImplementation(IteratorImplementation):
def __init__(self, space, strategy, dictimplementation):
diff --git a/pypy/objspace/std/floatobject.py b/pypy/objspace/std/floatobject.py
--- a/pypy/objspace/std/floatobject.py
+++ b/pypy/objspace/std/floatobject.py
@@ -133,8 +133,7 @@
else:
return space.wrap("0x%sp%s%d" % (s, sign, exp))
-def float2string(space, w_float, code, precision):
- x = w_float.floatval
+def float2string(x, code, precision):
# we special-case explicitly inf and nan here
if isfinite(x):
s = formatd(x, code, precision, DTSF_ADD_DOT_0)
@@ -145,13 +144,13 @@
s = "-inf"
else: # isnan(x):
s = "nan"
- return space.wrap(s)
+ return s
def repr__Float(space, w_float):
- return float2string(space, w_float, 'r', 0)
+ return space.wrap(float2string(w_float.floatval, 'r', 0))
def str__Float(space, w_float):
- return float2string(space, w_float, 'g', DTSF_STR_PRECISION)
+ return space.wrap(float2string(w_float.floatval, 'g', DTSF_STR_PRECISION))
def format__Float_ANY(space, w_float, w_spec):
return newformat.run_formatter(space, w_spec, "format_float", w_float)
diff --git a/pypy/objspace/std/identitydict.py b/pypy/objspace/std/identitydict.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/std/identitydict.py
@@ -0,0 +1,85 @@
+## ----------------------------------------------------------------------------
+## dict strategy (see dict_multiobject.py)
+
+from pypy.rlib import rerased
+from pypy.objspace.std.dictmultiobject import (AbstractTypedStrategy,
+ DictStrategy,
+ IteratorImplementation,
+ _UnwrappedIteratorMixin)
+
+
+# this strategy is selected by EmptyDictStrategy.switch_to_correct_strategy
+class IdentityDictStrategy(AbstractTypedStrategy, DictStrategy):
+ """
+ Strategy for custom instances which compares by identity (i.e., the
+ default unless you override __hash__, __eq__ or __cmp__). The storage is
+ just a normal RPython dict, which has already the correct by-identity
+ semantics.
+
+ Note that at a first sight, you might have problems if you mutate the
+ class of an object which is already inside an identitydict. Consider this
+ example::
+
+ class X(object):
+ pass
+ d = {x(): 1}
+ X.__eq__ = ...
+ d[y] # might trigger a call to __eq__?
+
+ We want to be sure that x.__eq__ is called in the same cases as in
+ CPython. However, as long as the strategy is IdentityDictStrategy, the
+ __eq__ will never be called.
+
+ It turns out that it's not a problem. In CPython (and in PyPy without
+ this strategy), the __eq__ is called if ``hash(y) == hash(x)`` and ``x is
+ not y``. Note that hash(x) is computed at the time when we insert x in
+ the dict, not at the time we lookup y.
+
+ Now, how can hash(y) == hash(x)? There are two possibilities:
+
+ 1. we write a custom __hash__ for the class of y, thus making it a not
+ "compares by reference" type
+
+ 2. the class of y is "compares by reference" type, and by chance the
+ hash is the same as x
+
+ In the first case, the getitem immediately notice that y is not of the
+ right type, and switches the strategy to ObjectDictStrategy, then the
+ lookup works as usual.
+
+ The second case is completely non-deterministic, even in CPython.
+ Depending on the phase of the moon, you might call the __eq__ or not, so
+ it is perfectly fine to *never* call it. Morever, in practice with the
+ minimar GC we never have two live objects with the same hash, so it would
+ never happen anyway.
+ """
+
+ erase, unerase = rerased.new_erasing_pair("identitydict")
+ erase = staticmethod(erase)
+ unerase = staticmethod(unerase)
+
+ def wrap(self, unwrapped):
+ return unwrapped
+
+ def unwrap(self, wrapped):
+ return wrapped
+
+ def get_empty_storage(self):
+ return self.erase({})
+
+ def is_correct_type(self, w_obj):
+ w_type = self.space.type(w_obj)
+ return w_type.compares_by_identity()
+
+ def _never_equal_to(self, w_lookup_type):
+ return False
+
+ def iter(self, w_dict):
+ return IdentityDictIteratorImplementation(self.space, self, w_dict)
+
+ def keys(self, w_dict):
+ return self.unerase(w_dict.dstorage).keys()
+
+
+class IdentityDictIteratorImplementation(_UnwrappedIteratorMixin, IteratorImplementation):
+ pass
diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -431,12 +431,17 @@
return None
assert isinstance(lifeline, WeakrefLifeline)
return lifeline
+ getweakref._cannot_really_call_random_things_ = True
def setweakref(self, space, weakreflifeline):
from pypy.module._weakref.interp__weakref import WeakrefLifeline
- assert (isinstance(weakreflifeline, WeakrefLifeline) or
- weakreflifeline is None)
+ assert isinstance(weakreflifeline, WeakrefLifeline)
self._get_mapdict_map().write(self, ("weakref", SPECIAL), weakreflifeline)
+ setweakref._cannot_really_call_random_things_ = True
+
+ def delweakref(self):
+ self._get_mapdict_map().write(self, ("weakref", SPECIAL), None)
+ delweakref._cannot_really_call_random_things_ = True
class ObjectMixin(object):
_mixin_ = True
diff --git a/pypy/objspace/std/objecttype.py b/pypy/objspace/std/objecttype.py
--- a/pypy/objspace/std/objecttype.py
+++ b/pypy/objspace/std/objecttype.py
@@ -6,7 +6,7 @@
from pypy.objspace.descroperation import Object
from pypy.objspace.std.stdtypedef import StdTypeDef
from pypy.objspace.std.register_all import register_all
-
+from pypy.objspace.std import identitydict
def descr__repr__(space, w_obj):
w = space.wrap
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
@@ -39,7 +39,6 @@
from pypy.objspace.std.stringtype import wrapstr
from pypy.objspace.std.unicodetype import wrapunicode
-
class StdObjSpace(ObjSpace, DescrOperation):
"""The standard object space, implementing a general-purpose object
library in Restricted Python."""
diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py
--- a/pypy/objspace/std/setobject.py
+++ b/pypy/objspace/std/setobject.py
@@ -36,6 +36,8 @@
return self._lifeline_
def setweakref(self, space, weakreflifeline):
self._lifeline_ = weakreflifeline
+ def delweakref(self):
+ self._lifeline_ = None
class W_SetObject(W_BaseSetObject):
from pypy.objspace.std.settype import set_typedef as typedef
diff --git a/pypy/objspace/std/test/test_dictmultiobject.py b/pypy/objspace/std/test/test_dictmultiobject.py
--- a/pypy/objspace/std/test/test_dictmultiobject.py
+++ b/pypy/objspace/std/test/test_dictmultiobject.py
@@ -898,6 +898,7 @@
withsmalldicts = False
withcelldict = False
withmethodcache = False
+ withidentitydict = False
FakeSpace.config = Config()
@@ -1105,3 +1106,4 @@
fakespace = FakeSpace()
d = fakespace.newdict(module=True)
assert type(d.strategy) is StringDictStrategy
+
diff --git a/pypy/objspace/std/test/test_identitydict.py b/pypy/objspace/std/test/test_identitydict.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/std/test/test_identitydict.py
@@ -0,0 +1,138 @@
+import py
+from pypy.interpreter.gateway import interp2app
+from pypy.conftest import gettestobjspace
+from pypy.conftest import option
+
+class AppTestComparesByIdentity:
+
+ def setup_class(cls):
+ from pypy.objspace.std import identitydict
+ cls.space = gettestobjspace(
+ **{"objspace.std.withidentitydict": True})
+ if option.runappdirect:
+ py.test.skip("interp2app doesn't work on appdirect")
+
+ def compares_by_identity(space, w_cls):
+ return space.wrap(w_cls.compares_by_identity())
+ cls.w_compares_by_identity = cls.space.wrap(interp2app(compares_by_identity))
+
+ def test_compares_by_identity(self):
+ class Plain(object):
+ pass
+
+ class CustomEq(object):
+ def __eq__(self, other):
+ return True
+
+ class CustomCmp (object):
+ def __cmp__(self, other):
+ return 0
+
+ class CustomHash(object):
+ def __hash__(self):
+ return 0
+
+ assert self.compares_by_identity(Plain)
+ assert not self.compares_by_identity(CustomEq)
+ assert not self.compares_by_identity(CustomCmp)
+ assert not self.compares_by_identity(CustomHash)
+
+ def test_modify_class(self):
+ class X(object):
+ pass
+
+ assert self.compares_by_identity(X)
+ X.__eq__ = lambda x: None
+ assert not self.compares_by_identity(X)
+ del X.__eq__
+ assert self.compares_by_identity(X)
+
+
+class AppTestIdentityDict(object):
+ def setup_class(cls):
+ cls.space = gettestobjspace(**{"objspace.std.withidentitydict": True})
+ if option.runappdirect:
+ py.test.skip("interp2app doesn't work on appdirect")
+
+ def w_uses_identity_strategy(self, obj):
+ import __pypy__
+ return "IdentityDictStrategy" in __pypy__.internal_repr(obj)
+
+ def test_use_strategy(self):
+ class X(object):
+ pass
+ d = {}
+ x = X()
+ d[x] = 1
+ assert self.uses_identity_strategy(d)
+ assert d[x] == 1
+
+ def test_bad_item(self):
+ class X(object):
+ pass
+ class Y(object):
+ def __hash__(self):
+ return 32
+
+ d = {}
+ x = X()
+ y = Y()
+ d[x] = 1
+ assert self.uses_identity_strategy(d)
+ d[y] = 2
+ assert not self.uses_identity_strategy(d)
+ assert d[x] == 1
+ assert d[y] == 2
+
+ def test_bad_key(self):
+ class X(object):
+ pass
+ d = {}
+ x = X()
+
+ class Y(object):
+ def __hash__(self):
+ return hash(x) # to make sure we do x == y
+
+ def __eq__(self, other):
+ return True
+
+ y = Y()
+ d[x] = 1
+ assert self.uses_identity_strategy(d)
+ assert d[y] == 1
+ assert not self.uses_identity_strategy(d)
+
+ def test_iter(self):
+ class X(object):
+ pass
+ x = X()
+ d = {x: 1}
+ assert self.uses_identity_strategy(d)
+ assert list(iter(d)) == [x]
+
+ def test_mutate_class_and_then_compare(self):
+ class X(object):
+ pass
+ class Y(object):
+ pass
+
+ x = X()
+ y = Y()
+ d1 = {x: 1}
+ d2 = {y: 1}
+ assert self.uses_identity_strategy(d1)
+ assert self.uses_identity_strategy(d2)
+ #
+ X.__hash__ = lambda self: hash(y)
+ X.__eq__ = lambda self, other: True
+ #
+ assert d1 == d2
+ assert self.uses_identity_strategy(d1)
+ assert not self.uses_identity_strategy(d2)
+
+ def test_old_style_classes(self):
+ class X:
+ pass
+ d = {X(): 1}
+ assert not self.uses_identity_strategy(d)
diff --git a/pypy/objspace/std/test/test_mapdict.py b/pypy/objspace/std/test/test_mapdict.py
--- a/pypy/objspace/std/test/test_mapdict.py
+++ b/pypy/objspace/std/test/test_mapdict.py
@@ -171,7 +171,7 @@
obj = c.instantiate()
assert obj.getweakref() is None
obj.setweakref(space, lifeline1)
- obj.setweakref(space, None)
+ obj.delweakref()
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -7,6 +7,7 @@
from pypy.interpreter.baseobjspace import W_Root
from pypy.objspace.std.stdtypedef import std_dict_descr, issubtypedef, Member
from pypy.objspace.std.objecttype import object_typedef
+from pypy.objspace.std import identitydict
from pypy.rlib.objectmodel import we_are_translated
from pypy.rlib.objectmodel import current_object_addr_as_int, compute_hash
from pypy.rlib.jit import promote, elidable_promote, we_are_jitted
@@ -76,6 +77,10 @@
for i in range(len(self.lookup_where)):
self.lookup_where[i] = None_None
+# possible values of compares_by_identity_status
+UNKNOWN = 0
+COMPARES_BY_IDENTITY = 1
+OVERRIDES_EQ_CMP_OR_HASH = 2
class W_TypeObject(W_Object):
from pypy.objspace.std.typetype import type_typedef as typedef
@@ -102,6 +107,9 @@
# (False is a conservative default, fixed during real usage)
uses_object_getattribute = False
+ # for config.objspace.std.withidentitydict
+ compares_by_identity_status = UNKNOWN
+
# used to cache the type __new__ function if it comes from a builtin type
# != 'type', in that case call__Type will also assumes the result
# of the __new__ is an instance of the type
@@ -146,11 +154,17 @@
else:
w_self.terminator = NoDictTerminator(space, w_self)
- def mutated(w_self):
+ def mutated(w_self, key):
+ """
+ The type is being mutated. key is either the string containing the
+ specific attribute which is being deleted/set or None to indicate a
+ generic mutation.
+ """
space = w_self.space
assert w_self.is_heaptype() or space.config.objspace.std.mutable_builtintypes
if (not space.config.objspace.std.withtypeversion and
not space.config.objspace.std.getattributeshortcut and
+ not space.config.objspace.std.withidentitydict and
not space.config.objspace.std.newshortcut):
return
@@ -158,6 +172,13 @@
w_self.uses_object_getattribute = False
# ^^^ conservative default, fixed during real usage
+ if space.config.objspace.std.withidentitydict:
+ did_compare_by_identity = (
+ w_self.compares_by_identity_status == COMPARES_BY_IDENTITY)
+ if (key is None or key == '__eq__' or
+ key == '__cmp__' or key == '__hash__'):
+ w_self.compares_by_identity_status = UNKNOWN
+
if space.config.objspace.std.newshortcut:
w_self.w_bltin_new = None
@@ -168,7 +189,7 @@
subclasses_w = w_self.get_subclasses()
for w_subclass in subclasses_w:
assert isinstance(w_subclass, W_TypeObject)
- w_subclass.mutated()
+ w_subclass.mutated(key)
def version_tag(w_self):
if (not we_are_jitted() or w_self.is_heaptype() or
@@ -207,6 +228,25 @@
def has_object_getattribute(w_self):
return w_self.getattribute_if_not_from_object() is None
+ def compares_by_identity(w_self):
+ from pypy.objspace.descroperation import object_hash
+ if not w_self.space.config.objspace.std.withidentitydict:
+ return False # conservative
+ #
+ if w_self.compares_by_identity_status != UNKNOWN:
+ # fast path
+ return w_self.compares_by_identity_status == COMPARES_BY_IDENTITY
+ #
+ default_hash = object_hash(w_self.space)
+ overrides_eq_cmp_or_hash = (w_self.lookup('__eq__') or
+ w_self.lookup('__cmp__') or
+ w_self.lookup('__hash__') is not default_hash)
+ if overrides_eq_cmp_or_hash:
+ w_self.compares_by_identity_status = OVERRIDES_EQ_CMP_OR_HASH
+ else:
+ w_self.compares_by_identity_status = COMPARES_BY_IDENTITY
+ return w_self.compares_by_identity_status == COMPARES_BY_IDENTITY
+
def ready(w_self):
for w_base in w_self.bases_w:
if not isinstance(w_base, W_TypeObject):
@@ -269,7 +309,7 @@
w_curr.w_value = w_value
return True
w_value = TypeCell(w_value)
- w_self.mutated()
+ w_self.mutated(name)
w_self.dict_w[name] = w_value
return True
@@ -286,7 +326,7 @@
except KeyError:
return False
else:
- w_self.mutated()
+ w_self.mutated(key)
return True
def lookup(w_self, name):
@@ -532,6 +572,8 @@
return self._lifeline_
def setweakref(self, space, weakreflifeline):
self._lifeline_ = weakreflifeline
+ def delweakref(self):
+ self._lifeline_ = None
# ____________________________________________________________
# Initialization of type objects
diff --git a/pypy/objspace/std/typetype.py b/pypy/objspace/std/typetype.py
--- a/pypy/objspace/std/typetype.py
+++ b/pypy/objspace/std/typetype.py
@@ -141,7 +141,7 @@
w_oldbestbase.getname(space))
# invalidate the version_tag of all the current subclasses
- w_type.mutated()
+ w_type.mutated(None)
# now we can go ahead and change 'w_type.bases_w'
saved_bases_w = w_type.bases_w
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -482,6 +482,13 @@
key[2:])
cache[key] = s_value
+ # add the attribute _dont_reach_me_in_del_ (see pypy.rpython.rclass)
+ try:
+ graph = self.bookkeeper.position_key[0]
+ graph.func._dont_reach_me_in_del_ = True
+ except (TypeError, AttributeError):
+ pass
+
return annmodel.s_None
def annotate_hooks(self, **kwds_s):
diff --git a/pypy/rlib/streamio.py b/pypy/rlib/streamio.py
--- a/pypy/rlib/streamio.py
+++ b/pypy/rlib/streamio.py
@@ -875,28 +875,32 @@
if bufsize == -1: # Get default from the class
bufsize = self.bufsize
self.bufsize = bufsize # buffer size (hint only)
- self.buf = ""
+ self.buf = []
+ self.buflen = 0
def flush_buffers(self):
if self.buf:
- self.do_write(self.buf)
- self.buf = ""
+ self.do_write(''.join(self.buf))
+ self.buf = []
+ self.buflen = 0
def tell(self):
- return self.do_tell() + len(self.buf)
+ return self.do_tell() + self.buflen
def write(self, data):
- buflen = len(self.buf)
+ buflen = self.buflen
datalen = len(data)
if datalen + buflen < self.bufsize:
- self.buf += data
+ self.buf.append(data)
+ self.buflen += datalen
elif buflen:
- slice = self.bufsize - buflen
- assert slice >= 0
- self.buf += data[:slice]
- self.do_write(self.buf)
- self.buf = ""
- self.write(data[slice:])
+ i = self.bufsize - buflen
+ assert i >= 0
+ self.buf.append(data[:i])
+ self.do_write(''.join(self.buf))
+ self.buf = []
+ self.buflen = 0
+ self.write(data[i:])
else:
self.do_write(data)
@@ -922,11 +926,27 @@
"""
def write(self, data):
- BufferingOutputStream.write(self, data)
- p = self.buf.rfind('\n') + 1
- if p >= 0:
- self.do_write(self.buf[:p])
- self.buf = self.buf[p:]
+ p = data.rfind('\n') + 1
+ assert p >= 0
+ if self.buflen + len(data) < self.bufsize:
+ if p == 0:
+ self.buf.append(data)
+ self.buflen += len(data)
+ else:
+ if self.buflen:
+ self.do_write(''.join(self.buf))
+ self.do_write(data[:p])
+ self.buf = [data[p:]]
+ self.buflen = len(self.buf[0])
+ else:
+ if self.buflen + p < self.bufsize:
+ p = self.bufsize - self.buflen
+ if self.buflen:
+ self.do_write(''.join(self.buf))
+ assert p >= 0
+ self.do_write(data[:p])
+ self.buf = [data[p:]]
+ self.buflen = len(self.buf[0])
# ____________________________________________________________
diff --git a/pypy/rlib/test/test_jit.py b/pypy/rlib/test/test_jit.py
--- a/pypy/rlib/test/test_jit.py
+++ b/pypy/rlib/test/test_jit.py
@@ -83,6 +83,9 @@
t, rtyper, fngraph = self.gengraph(fn, [int])
+ # added by compute_result_annotation()
+ assert fn._dont_reach_me_in_del_ == True
+
def getargs(func):
for graph in t.graphs:
if getattr(graph, 'func', None) is func:
diff --git a/pypy/rpython/extfuncregistry.py b/pypy/rpython/extfuncregistry.py
--- a/pypy/rpython/extfuncregistry.py
+++ b/pypy/rpython/extfuncregistry.py
@@ -1,6 +1,6 @@
# this registry uses the new interface for external functions
-from extfunc import register_external
+from pypy.rpython.extfunc import register_external
# ___________________________
# math functions
@@ -30,24 +30,28 @@
export_name="ll_math.ll_math_%s" % name,
sandboxsafe=True, llimpl=llimpl)
-register_external(rfloat.isinf, [float], bool,
- export_name="ll_math.ll_math_isinf", sandboxsafe=True,
- llimpl=ll_math.ll_math_isinf)
-register_external(rfloat.isnan, [float], bool,
- export_name="ll_math.ll_math_isnan", sandboxsafe=True,
- llimpl=ll_math.ll_math_isnan)
-register_external(rfloat.isfinite, [float], bool,
- export_name="ll_math.ll_math_isfinite", sandboxsafe=True,
- llimpl=ll_math.ll_math_isfinite)
-register_external(rfloat.copysign, [float, float], float,
- export_name="ll_math.ll_math_copysign", sandboxsafe=True,
- llimpl=ll_math.ll_math_copysign)
-register_external(math.floor, [float], float,
- export_name="ll_math.ll_math_floor", sandboxsafe=True,
- llimpl=ll_math.ll_math_floor)
-register_external(math.sqrt, [float], float,
- export_name="ll_math.ll_math_sqrt", sandboxsafe=True,
- llimpl=ll_math.ll_math_sqrt)
+_register = [ # (module, [(method name, arg types, return type), ...], ...)
+ (rfloat, [
+ ('isinf', [float], bool),
+ ('isnan', [float], bool),
+ ('isfinite', [float], bool),
+ ('copysign', [float, float], float),
+ ]),
+ (math, [
+ ('floor', [float], float),
+ ('sqrt', [float], float),
+ ('log', [float], float),
+ ('log10', [float], float),
+ ]),
+]
+for module, methods in _register:
+ for name, arg_types, return_type in methods:
+ method_name = 'll_math_%s' % name
+ register_external(getattr(module, name), arg_types, return_type,
+ export_name='ll_math.%s' % method_name,
+ sandboxsafe=True,
+ llimpl=getattr(ll_math, method_name))
+
complex_math_functions = [
('frexp', [float], (float, int)),
diff --git a/pypy/rpython/lltypesystem/ll2ctypes.py b/pypy/rpython/lltypesystem/ll2ctypes.py
--- a/pypy/rpython/lltypesystem/ll2ctypes.py
+++ b/pypy/rpython/lltypesystem/ll2ctypes.py
@@ -171,17 +171,6 @@
assert max_n >= 0
ITEM = A.OF
ctypes_item = get_ctypes_type(ITEM, delayed_builders)
- # Python 2.5 ctypes can raise OverflowError on 64-bit builds
- for n in [sys.maxint, 2**31]:
- MAX_SIZE = n/64
- try:
- PtrType = ctypes.POINTER(MAX_SIZE * ctypes_item)
- except OverflowError, e:
- pass
- else:
- break
- else:
- raise e
class CArray(ctypes.Structure):
if not A._hints.get('nolength'):
@@ -190,6 +179,7 @@
else:
_fields_ = [('items', max_n * ctypes_item)]
+ @classmethod
def _malloc(cls, n=None):
if not isinstance(n, int):
raise TypeError, "array length must be an int"
@@ -198,10 +188,29 @@
if hasattr(bigarray, 'length'):
bigarray.length = n
return bigarray
- _malloc = classmethod(_malloc)
+
+ _ptrtype = None
+
+ @classmethod
+ def _get_ptrtype(cls):
+ if cls._ptrtype:
+ return cls._ptrtype
+ # ctypes can raise OverflowError on 64-bit builds
+ for n in [sys.maxint, 2**31]:
+ cls.MAX_SIZE = n/64
+ try:
+ cls._ptrtype = ctypes.POINTER(cls.MAX_SIZE * ctypes_item)
+ except OverflowError, e:
+ pass
+ else:
+ break
+ else:
+ raise e
+ return cls._ptrtype
def _indexable(self, index):
- assert index + 1 < MAX_SIZE
+ PtrType = self._get_ptrtype()
+ assert index + 1 < self.MAX_SIZE
p = ctypes.cast(ctypes.pointer(self.items), PtrType)
return p.contents
diff --git a/pypy/rpython/lltypesystem/module/ll_math.py b/pypy/rpython/lltypesystem/module/ll_math.py
--- a/pypy/rpython/lltypesystem/module/ll_math.py
+++ b/pypy/rpython/lltypesystem/module/ll_math.py
@@ -68,7 +68,6 @@
math_hypot = llexternal(underscore + 'hypot',
[rffi.DOUBLE, rffi.DOUBLE], rffi.DOUBLE)
math_floor = llexternal('floor', [rffi.DOUBLE], rffi.DOUBLE, elidable_function=True)
-
math_sqrt = llexternal('sqrt', [rffi.DOUBLE], rffi.DOUBLE)
@jit.elidable
@@ -221,10 +220,6 @@
return (fracpart, intpart)
-def ll_math_copysign(x, y):
- return math_copysign(x, y) # no error checking needed
-
-
def ll_math_fmod(x, y):
if isinf(y):
if isinf(x):
@@ -335,6 +330,16 @@
return x # +inf or nan
+def ll_math_log(x):
+ if x <= 0:
+ raise ValueError("math domain error")
+ return math_log(x)
+
+def ll_math_log10(x):
+ if x <= 0:
+ raise ValueError("math domain error")
+ return math_log10(x)
+
# ____________________________________________________________
#
# Default implementations
@@ -373,7 +378,7 @@
unary_math_functions = [
'acos', 'asin', 'atan',
'ceil', 'cos', 'cosh', 'exp', 'fabs',
- 'sin', 'sinh', 'tan', 'tanh', 'log', 'log10',
+ 'sin', 'sinh', 'tan', 'tanh',
'acosh', 'asinh', 'atanh', 'log1p', 'expm1',
]
unary_math_functions_can_overflow = [
diff --git a/pypy/rpython/lltypesystem/module/test/test_llinterp_math.py b/pypy/rpython/lltypesystem/module/test/test_llinterp_math.py
--- a/pypy/rpython/lltypesystem/module/test/test_llinterp_math.py
+++ b/pypy/rpython/lltypesystem/module/test/test_llinterp_math.py
@@ -1,6 +1,4 @@
-
-""" Just another bunch of tests for llmath, run on top of llinterp
-"""
+"""Just another bunch of tests for llmath, run on top of llinterp."""
from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin
from pypy.rpython.lltypesystem.module import ll_math
@@ -39,7 +37,7 @@
assert self.interpret(f, [0.3, 0.4]) == f(0.3, 0.4)
return next_test
- for name in ll_math.unary_math_functions:
+ for name in ll_math.unary_math_functions + ['log', 'log10', 'sqrt']:
func_name = 'test_%s' % (name,)
next_test = new_unary_test(name)
next_test.func_name = func_name
diff --git a/pypy/rpython/lltypesystem/rclass.py b/pypy/rpython/lltypesystem/rclass.py
--- a/pypy/rpython/lltypesystem/rclass.py
+++ b/pypy/rpython/lltypesystem/rclass.py
@@ -400,6 +400,7 @@
assert len(s_func.descriptions) == 1
funcdesc, = s_func.descriptions
graph = funcdesc.getuniquegraph()
+ self.check_graph_of_del_does_not_call_too_much(graph)
FUNCTYPE = FuncType([Ptr(source_repr.object_type)], Void)
destrptr = functionptr(FUNCTYPE, graph.name,
graph=graph,
diff --git a/pypy/rpython/lltypesystem/rffi.py b/pypy/rpython/lltypesystem/rffi.py
--- a/pypy/rpython/lltypesystem/rffi.py
+++ b/pypy/rpython/lltypesystem/rffi.py
@@ -102,19 +102,6 @@
else:
callbackholder = None
- funcptr = lltype.functionptr(ext_type, name, external='C',
- compilation_info=compilation_info,
- _callable=_callable,
- _safe_not_sandboxed=sandboxsafe,
- _debugexc=True, # on top of llinterp
- canraise=False,
- **kwds)
- if isinstance(_callable, ll2ctypes.LL2CtypesCallable):
- _callable.funcptr = funcptr
-
- if _nowrapper:
- return funcptr
-
if threadsafe in (False, True):
# invoke the around-handlers, which release the GIL, if and only if
# the C function is thread-safe.
@@ -125,6 +112,21 @@
# sandboxsafe is a hint for "too-small-ness" (e.g. math functions).
invoke_around_handlers = not sandboxsafe
+ funcptr = lltype.functionptr(ext_type, name, external='C',
+ compilation_info=compilation_info,
+ _callable=_callable,
+ _safe_not_sandboxed=sandboxsafe,
+ _debugexc=True, # on top of llinterp
+ canraise=False,
+ releases_gil=invoke_around_handlers,
+ **kwds)
+ if isinstance(_callable, ll2ctypes.LL2CtypesCallable):
+ _callable.funcptr = funcptr
+
+ if _nowrapper:
+ return funcptr
+
+
if invoke_around_handlers:
# The around-handlers are releasing the GIL in a threaded pypy.
# We need tons of care to ensure that no GC operation and no
diff --git a/pypy/rpython/lltypesystem/rstr.py b/pypy/rpython/lltypesystem/rstr.py
--- a/pypy/rpython/lltypesystem/rstr.py
+++ b/pypy/rpython/lltypesystem/rstr.py
@@ -486,6 +486,11 @@
return True
+ def ll_startswith_char(s, ch):
+ if not len(s.chars):
+ return False
+ return s.chars[0] == ch
+
@elidable
def ll_endswith(s1, s2):
len1 = len(s1.chars)
@@ -503,6 +508,11 @@
return True
+ def ll_endswith_char(s, ch):
+ if not len(s.chars):
+ return False
+ return s.chars[len(s.chars) - 1] == ch
+
@elidable
def ll_find_char(s, ch, start, end):
i = start
diff --git a/pypy/rpython/lltypesystem/test/test_ll2ctypes.py b/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
--- a/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
+++ b/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
@@ -671,7 +671,7 @@
assert not ALLOCATED # detects memory leaks in the test
def test_arrayofstruct(self):
- S1 = lltype.Struct('S1', ('x', lltype.Signed))
+ S1 = lltype.Struct('S2', ('x', lltype.Signed))
A = lltype.Array(S1, hints={'nolength': True})
a = lltype.malloc(A, 5, flavor='raw')
a[0].x = 100
diff --git a/pypy/rpython/memory/gc/minimark.py b/pypy/rpython/memory/gc/minimark.py
--- a/pypy/rpython/memory/gc/minimark.py
+++ b/pypy/rpython/memory/gc/minimark.py
@@ -34,6 +34,13 @@
the GC in very small programs. Defaults to 8
times the nursery.
+ PYPY_GC_LOSTCARD If between two minor collections we see more than
+ 'PYPY_GC_LOSTCARD * length' writes to the same array,
+ then give up card marking and use the fast write
+ barrier instead. Defaults to 0.3333 for now.
+ Avoid values lower than 0.125: it is the growth
+ factor of list.append().
+
PYPY_GC_DEBUG Enable extra checks around collections that are
too slow for normal use. Values are 0 (off),
1 (on major collections) or 2 (also on minor
@@ -198,6 +205,9 @@
# larger. A value of 0 disables card marking.
"card_page_indices": 128,
+ # See PYPY_GC_LOSTCARD.
+ "lost_card": 1.0 / 3.0,
+
# Objects whose total size is at least 'large_object' bytes are
# allocated out of the nursery immediately, as old objects. The
# minimal allocated size of the nursery is 2x the following
@@ -214,6 +224,7 @@
major_collection_threshold=2.5,
growth_rate_max=2.5, # for tests
card_page_indices=0,
+ lost_card=0.5,
large_object=8*WORD,
ArenaCollectionClass=None,
**kwds):
@@ -235,6 +246,7 @@
self.card_page_shift = 0
while (1 << self.card_page_shift) < self.card_page_indices:
self.card_page_shift += 1
+ self.lost_card = lost_card
#
# 'large_object' limit how big objects can be in the nursery, so
# it gives a lower bound on the allowed size of the nursery.
@@ -355,6 +367,10 @@
else:
self.max_delta = 0.125 * env.get_total_memory()
#
+ lost_card = env.read_float_from_env('PYPY_GC_LOSTCARD')
+ if lost_card > 0.0:
+ self.lost_card = lost_card
+ #
self.minor_collection() # to empty the nursery
llarena.arena_free(self.nursery)
self.nursery_size = newsize
@@ -649,7 +665,7 @@
#
else:
# Reserve N extra words containing card bits before the object.
- extra_words = self.card_marking_words_for_length(length)
+ extra_words = self.card_marking_words_for_length(length) + 1
cardheadersize = WORD * extra_words
extra_flags = GCFLAG_HAS_CARDS | GCFLAG_TRACK_YOUNG_PTRS
# note that if 'can_make_young', then card marking will only
@@ -675,11 +691,15 @@
raise MemoryError("cannot allocate large object")
#
# Reserve the card mark bits as a list of single bytes
- # (the loop is empty in C).
+ # followed by a Signed (the loop is empty in C).
+ if cardheadersize > 0:
i = 0
- while i < cardheadersize:
- llarena.arena_reserve(arena + i, llmemory.sizeof(lltype.Char))
+ while i < cardheadersize - WORD:
+ llarena.arena_reserve(arena + i,
+ llmemory.sizeof(lltype.Char))
i += 1
+ llarena.arena_reserve(arena + i,
+ llmemory.sizeof(lltype.Signed))
#
# Reserve the actual object. (This is also a no-op in C).
result = arena + cardheadersize
@@ -903,14 +923,11 @@
length = (obj + offset_to_length).signed[0]
extra_words = self.card_marking_words_for_length(length)
#
- size_gc_header = self.gcheaderbuilder.size_gc_header
- p = llarena.getfakearenaaddress(obj - size_gc_header)
i = extra_words * WORD
while i > 0:
- p -= 1
- ll_assert(p.char[0] == '\x00',
+ i -= 1
+ ll_assert(self.get_card(obj, i).char[0] == '\x00',
"the card marker bits are not cleared")
- i -= 1
# ----------
# Write barrier
@@ -1008,6 +1025,8 @@
self.prebuilt_root_objects.append(addr_array)
return
#
+ self.set_cards_flag(addr_array)
+ #
# 'addr_array' is a raw_malloc'ed array with card markers
# in front. Compute the index of the bit to set:
bitindex = index >> self.card_page_shift
@@ -1025,10 +1044,6 @@
# it seems more important that remember_young_pointer_from_array2()
# does not take 3 arguments).
addr_byte.char[0] = chr(byte | bitmask)
- #
- if objhdr.tid & GCFLAG_CARDS_SET == 0:
- self.objects_with_cards_set.append(addr_array)
- objhdr.tid |= GCFLAG_CARDS_SET
remember_young_pointer_from_array2._dont_inline_ = True
assert self.card_page_indices > 0
@@ -1057,6 +1072,8 @@
if not self.appears_to_be_young(newvalue):
return
#
+ self.set_cards_flag(addr_array)
+ #
# 'addr_array' is a raw_malloc'ed array with card markers
# in front. Compute the index of the bit to set:
bitindex = index >> self.card_page_shift
@@ -1069,10 +1086,6 @@
if byte & bitmask:
return
addr_byte.char[0] = chr(byte | bitmask)
- #
- if objhdr.tid & GCFLAG_CARDS_SET == 0:
- self.objects_with_cards_set.append(addr_array)
- objhdr.tid |= GCFLAG_CARDS_SET
return
#
# Logic for the no-cards case, put here to minimize the number
@@ -1090,11 +1103,36 @@
self.remember_young_pointer_from_array3 = (
remember_young_pointer_from_array3)
- def get_card(self, obj, byteindex):
+ def get_card_counter_addr(self, obj):
size_gc_header = self.gcheaderbuilder.size_gc_header
addr_byte = obj - size_gc_header
- return llarena.getfakearenaaddress(addr_byte) + (~byteindex)
+ return llarena.getfakearenaaddress(addr_byte) - WORD
+ def get_card(self, obj, byteindex):
+ return self.get_card_counter_addr(obj) + (~byteindex)
+
+ def set_cards_flag(self, obj):
+ hdr = self.header(obj)
+ if hdr.tid & GCFLAG_CARDS_SET == 0:
+ #
+ # first time we set a card bit in this object
+ self.header(obj).tid |= GCFLAG_CARDS_SET
+ self.objects_with_cards_set.append(obj)
+ #
+ # initialize the counter with the array length and self.lost_card
+ typeid = self.get_type_id(obj)
+ offset_to_length = self.varsize_offset_to_length(typeid)
+ length = (obj + offset_to_length).signed[0]
+ counter = int(length * self.lost_card)
+ self.get_card_counter_addr(obj).signed[0] = counter
+ else:
+ # decrement the counter and if zero is reached, give up on
+ # card marking (up to the next collection).
+ addr = self.get_card_counter_addr(obj)
+ addr.signed[0] -= 1
+ if addr.signed[0] < 0:
+ self.objects_pointing_to_young.append(obj)
+ hdr.tid &= ~GCFLAG_TRACK_YOUNG_PTRS
def assume_young_pointers(self, addr_struct):
"""Called occasionally by the JIT to mean ``assume that 'addr_struct'
@@ -1167,10 +1205,7 @@
addr_dstbyte.char[0] = chr(ord(addr_dstbyte.char[0]) | byte)
i += 1
#
- dest_hdr = self.header(dest_addr)
- if dest_hdr.tid & GCFLAG_CARDS_SET == 0:
- self.objects_with_cards_set.append(dest_addr)
- dest_hdr.tid |= GCFLAG_CARDS_SET
+ self.set_cards_flag(dest_addr)
# ----------
# Nursery collection
@@ -1264,6 +1299,7 @@
length = (obj + offset_to_length).signed[0]
bytes = self.card_marking_bytes_for_length(length)
p = llarena.getfakearenaaddress(obj - size_gc_header)
+ p -= WORD
#
# If the object doesn't have GCFLAG_TRACK_YOUNG_PTRS, then it
# means that it is in 'objects_pointing_to_young' and
@@ -1602,7 +1638,7 @@
"GCFLAG_HAS_CARDS but not has_gcptr_in_varsize")
offset_to_length = self.varsize_offset_to_length(typeid)
length = (obj + offset_to_length).signed[0]
- extra_words = self.card_marking_words_for_length(length)
+ extra_words = self.card_marking_words_for_length(length) + 1
arena -= extra_words * WORD
allocsize += extra_words * WORD
#
diff --git a/pypy/rpython/memory/gc/test/test_direct.py b/pypy/rpython/memory/gc/test/test_direct.py
--- a/pypy/rpython/memory/gc/test/test_direct.py
+++ b/pypy/rpython/memory/gc/test/test_direct.py
@@ -525,6 +525,7 @@
def test_writebarrier_before_copy(self):
from pypy.rpython.memory.gc import minimark
largeobj_size = self.gc.nonlarge_max + 1
+ self.gc.next_major_collection_threshold = 99999.0
p_src = self.malloc(VAR, largeobj_size)
p_dst = self.malloc(VAR, largeobj_size)
# make them old
@@ -564,6 +565,7 @@
from pypy.rpython.memory.gc import minimark
tid = self.get_type_id(VAR)
largeobj_size = self.gc.nonlarge_max + 1
+ self.gc.next_major_collection_threshold = 99999.0
addr_src = self.gc.external_malloc(tid, largeobj_size)
addr_dst = self.gc.external_malloc(tid, largeobj_size)
hdr_src = self.gc.header(addr_src)
diff --git a/pypy/rpython/memory/gctransform/asmgcroot.py b/pypy/rpython/memory/gctransform/asmgcroot.py
--- a/pypy/rpython/memory/gctransform/asmgcroot.py
+++ b/pypy/rpython/memory/gctransform/asmgcroot.py
@@ -184,7 +184,9 @@
# old NULL entries
gcdata.dead_threads_count += 1
if (gcdata.dead_threads_count & 511) == 0:
- gcdata.aid2stack = copy_without_null_values(gcdata.aid2stack)
+ copy = copy_without_null_values(gcdata.aid2stack)
+ gcdata.aid2stack.delete()
+ gcdata.aid2stack = copy
def belongs_to_current_thread(framedata):
# xxx obscure: the answer is Yes if, as a pointer, framedata
diff --git a/pypy/rpython/memory/gctransform/framework.py b/pypy/rpython/memory/gctransform/framework.py
--- a/pypy/rpython/memory/gctransform/framework.py
+++ b/pypy/rpython/memory/gctransform/framework.py
@@ -1449,8 +1449,9 @@
# old NULL entries
gcdata.dead_threads_count += 1
if (gcdata.dead_threads_count & 511) == 0:
- gcdata.thread_stacks = copy_without_null_values(
- gcdata.thread_stacks)
+ copy = copy_without_null_values(gcdata.thread_stacks)
+ gcdata.thread_stacks.delete()
+ gcdata.thread_stacks = copy
def switch_shadow_stacks(new_aid):
save_away_current_stack()
diff --git a/pypy/rpython/ootypesystem/ootype.py b/pypy/rpython/ootypesystem/ootype.py
--- a/pypy/rpython/ootypesystem/ootype.py
+++ b/pypy/rpython/ootypesystem/ootype.py
@@ -433,7 +433,9 @@
"ll_streq": Meth([self.SELFTYPE_T], Bool),
"ll_strcmp": Meth([self.SELFTYPE_T], Signed),
"ll_startswith": Meth([self.SELFTYPE_T], Bool),
+ "ll_startswith_char": Meth([self.CHAR], Bool),
"ll_endswith": Meth([self.SELFTYPE_T], Bool),
+ "ll_endswith_char": Meth([self.CHAR], Bool),
"ll_find": Meth([self.SELFTYPE_T, Signed, Signed], Signed),
"ll_rfind": Meth([self.SELFTYPE_T, Signed, Signed], Signed),
"ll_count": Meth([self.SELFTYPE_T, Signed, Signed], Signed),
@@ -1429,10 +1431,18 @@
# NOT_RPYTHON
return self._str.startswith(s._str)
+ def ll_startswith_char(self, s):
+ # NOT_RPYTHON
+ return self._str.startswith(s)
+
def ll_endswith(self, s):
# NOT_RPYTHON
return self._str.endswith(s._str)
+ def ll_endswith_char(self, s):
+ # NOT_RPYTHON
+ return self._str.endswith(s)
+
def ll_find(self, s, start, end):
# NOT_RPYTHON
if start > len(self._str): # workaround to cope with corner case
diff --git a/pypy/rpython/rclass.py b/pypy/rpython/rclass.py
--- a/pypy/rpython/rclass.py
+++ b/pypy/rpython/rclass.py
@@ -374,6 +374,43 @@
def can_ll_be_null(self, s_value):
return s_value.can_be_none()
+ def check_graph_of_del_does_not_call_too_much(self, graph):
+ # RPython-level __del__() methods should not do "too much".
+ # In the PyPy Python interpreter, they usually do simple things
+ # like file.__del__() closing the file descriptor; or if they
+ # want to do more like call an app-level __del__() method, they
+ # enqueue the object instead, and the actual call is done later.
+ #
+ # Here, as a quick way to check "not doing too much", we check
+ # that from no RPython-level __del__() method we can reach a
+ # JitDriver.
+ #
+ # XXX wrong complexity, but good enough because the set of
+ # reachable graphs should be small
+ callgraph = self.rtyper.annotator.translator.callgraph.values()
+ seen = {graph: None}
+ while True:
+ oldlength = len(seen)
+ for caller, callee in callgraph:
+ if caller in seen and callee not in seen:
+ func = getattr(callee, 'func', None)
+ if getattr(func, '_dont_reach_me_in_del_', False):
+ lst = [str(callee)]
+ g = caller
+ while g:
+ lst.append(str(g))
+ g = seen.get(g)
+ lst.append('')
+ raise TyperError("the RPython-level __del__() method "
+ "in %r calls:%s" % (
+ graph, '\n\t'.join(lst[::-1])))
+ if getattr(func, '_cannot_really_call_random_things_',
+ False):
+ continue
+ seen[callee] = caller
+ if len(seen) == oldlength:
+ break
+
# ____________________________________________________________
def rtype_new_instance(rtyper, classdef, llops, classcallhop=None):
diff --git a/pypy/rpython/rlist.py b/pypy/rpython/rlist.py
--- a/pypy/rpython/rlist.py
+++ b/pypy/rpython/rlist.py
@@ -667,7 +667,6 @@
res = l.ll_getitem_fast(index)
ll_delitem_nonneg(dum_nocheck, l, index)
return res
-ll_pop.oopspec = 'list.pop(l, index)'
def ll_reverse(l):
length = l.ll_length()
diff --git a/pypy/rpython/rstr.py b/pypy/rpython/rstr.py
--- a/pypy/rpython/rstr.py
+++ b/pypy/rpython/rstr.py
@@ -81,16 +81,30 @@
return super(AbstractStringRepr, self).rtype_is_true(hop)
def rtype_method_startswith(self, hop):
- str1_repr, str2_repr = self._str_reprs(hop)
- v_str, v_value = hop.inputargs(str1_repr, str2_repr)
+ str1_repr = hop.args_r[0].repr
+ str2_repr = hop.args_r[1]
+ v_str = hop.inputarg(str1_repr, arg=0)
+ if str2_repr == str2_repr.char_repr:
+ v_value = hop.inputarg(str2_repr.char_repr, arg=1)
+ fn = self.ll.ll_startswith_char
+ else:
+ v_value = hop.inputarg(str2_repr, arg=1)
+ fn = self.ll.ll_startswith
hop.exception_cannot_occur()
- return hop.gendirectcall(self.ll.ll_startswith, v_str, v_value)
+ return hop.gendirectcall(fn, v_str, v_value)
def rtype_method_endswith(self, hop):
- str1_repr, str2_repr = self._str_reprs(hop)
- v_str, v_value = hop.inputargs(str1_repr, str2_repr)
+ str1_repr = hop.args_r[0].repr
+ str2_repr = hop.args_r[1]
+ v_str = hop.inputarg(str1_repr, arg=0)
+ if str2_repr == str2_repr.char_repr:
+ v_value = hop.inputarg(str2_repr.char_repr, arg=1)
+ fn = self.ll.ll_endswith_char
+ else:
+ v_value = hop.inputarg(str2_repr, arg=1)
+ fn = self.ll.ll_endswith
hop.exception_cannot_occur()
- return hop.gendirectcall(self.ll.ll_endswith, v_str, v_value)
+ return hop.gendirectcall(fn, v_str, v_value)
def rtype_method_find(self, hop, reverse=False):
# XXX binaryop
diff --git a/pypy/rpython/test/test_rclass.py b/pypy/rpython/test/test_rclass.py
--- a/pypy/rpython/test/test_rclass.py
+++ b/pypy/rpython/test/test_rclass.py
@@ -7,6 +7,7 @@
from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
from pypy.rpython.rclass import IR_IMMUTABLE, IR_IMMUTABLE_ARRAY
from pypy.rpython.rclass import IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY
+from pypy.rpython.error import TyperError
from pypy.objspace.flow.model import summary
class EmptyBase(object):
@@ -1022,6 +1023,24 @@
assert destrptra is not None
assert destrptrb is not None
+ def test_del_forbidden(self):
+ class A(object):
+ def __del__(self):
+ self.foo()
+ def foo(self):
+ self.bar()
+ def bar(self):
+ pass
+ bar._dont_reach_me_in_del_ = True
+ def f():
+ a = A()
+ a.foo()
+ a.bar()
+ t = TranslationContext()
+ t.buildannotator().build_types(f, [])
+ e = py.test.raises(TyperError, t.buildrtyper().specialize)
+ print e.value
+
def test_instance_repr(self):
from pypy.rlib.objectmodel import current_object_addr_as_int
class FooBar(object):
diff --git a/pypy/rpython/test/test_rstr.py b/pypy/rpython/test/test_rstr.py
--- a/pypy/rpython/test/test_rstr.py
+++ b/pypy/rpython/test/test_rstr.py
@@ -227,6 +227,15 @@
res = self.interpret(fn, [i,j])
assert res is fn(i, j)
+ def test_startswith_char(self):
+ const = self.const
+ def fn(i):
+ s = [const(''), const('one'), const('two'), const('o'), const('on'), const('ne'), const('e'), const('twos'), const('foobar'), const('fortytwo')]
+ return s[i].startswith(const('o'))
+ for i in range(10):
+ res = self.interpret(fn, [i])
+ assert res == fn(i)
+
def test_endswith(self):
const = self.const
def fn(i, j):
@@ -238,6 +247,15 @@
res = self.interpret(fn, [i,j])
assert res is fn(i, j)
+ def test_endswith_char(self):
+ const = self.const
+ def fn(i):
+ s = [const(''), const('one'), const('two'), const('o'), const('on'), const('ne'), const('e'), const('twos'), const('foobar'), const('fortytwo')]
+ return s[i].endswith(const('e'))
+ for i in range(10):
+ res = self.interpret(fn, [i])
+ assert res == fn(i)
+
def test_find(self):
const = self.const
def fn(i, j):
diff --git a/pypy/test_all.py b/pypy/test_all.py
--- a/pypy/test_all.py
+++ b/pypy/test_all.py
@@ -18,4 +18,5 @@
if __name__ == '__main__':
import tool.autopath
import pytest
- sys.exit(pytest.main())
+ import pytest_cov
+ sys.exit(pytest.main(plugins=[pytest_cov]))
diff --git a/pypy/tool/jitlogparser/parser.py b/pypy/tool/jitlogparser/parser.py
--- a/pypy/tool/jitlogparser/parser.py
+++ b/pypy/tool/jitlogparser/parser.py
@@ -30,6 +30,9 @@
def getres(self):
return self._getvar(self.res)
+ def getdescr(self):
+ return self.descr
+
def _getvar(self, v):
return v
@@ -39,7 +42,7 @@
def repr(self):
args = self.getargs()
if self.descr is not None:
- args.append('descr=%s' % self.descr)
+ args.append('descr=%s' % self.getdescr())
arglist = ', '.join(args)
if self.res is not None:
return '%s = %s(%s)' % (self.getres(), self.name, arglist)
@@ -89,7 +92,7 @@
while asm[asm_index][0] < op.offset:
asm_index += 1
end_index = asm_index
- while asm[end_index][0] < end:
+ while asm[end_index][0] < end and end_index < len(asm) - 1:
end_index += 1
op.asm = '\n'.join([asm[i][1] for i in range(asm_index, end_index)])
return loop
@@ -145,10 +148,10 @@
if operations[0].name == 'debug_merge_point':
self.inline_level = int(operations[0].args[0])
m = re.search('<code object ([<>\w]+)\. file \'(.+?)\'\. line (\d+)> #(\d+) (\w+)',
- operations[0].getarg(1))
+ operations[0].args[1])
if m is None:
# a non-code loop, like StrLiteralSearch or something
- self.bytecode_name = operations[0].args[1].split(" ")[0][1:]
+ self.bytecode_name = operations[0].args[1][1:-1]
else:
self.name, self.filename, lineno, bytecode_no, self.bytecode_name = m.groups()
self.startlineno = int(lineno)
@@ -336,27 +339,45 @@
log = parse_log_file(logname)
addrs = {}
for entry in extract_category(log, 'jit-backend-addr'):
- m = re.search('bootstrap ([\da-f]+)', entry)
- name = entry[:entry.find('(') - 1]
- addrs[int(m.group(1), 16)] = name
+ m = re.search('bootstrap ([-\da-f]+)', entry)
+ if not m:
+ # a bridge
+ m = re.search('has address ([-\da-f]+)', entry)
+ addr = int(m.group(1), 16)
+ entry = entry.lower()
+ m = re.search('guard \d+', entry)
+ name = m.group(0)
+ else:
+ name = entry[:entry.find('(') - 1].lower()
+ addr = int(m.group(1), 16)
+ addrs.setdefault(addr, []).append(name)
dumps = {}
for entry in extract_category(log, 'jit-backend-dump'):
backend, _, dump, _ = entry.split("\n")
_, addr, _, data = re.split(" +", dump)
backend_name = backend.split(" ")[1]
addr = int(addr[1:], 16)
- if addr in addrs:
- dumps[addrs[addr]] = (backend_name, addr, data)
+ if addr in addrs and addrs[addr]:
+ name = addrs[addr].pop(0) # they should come in order
+ dumps[name] = (backend_name, addr, data)
loops = []
for entry in extract_category(log, 'jit-log-opt'):
parser = ParserCls(entry, None, {}, 'lltype', None,
nonstrict=True)
loop = parser.parse()
comm = loop.comment
+ comm = comm.lower()
+ if comm.startswith('# bridge'):
+ m = re.search('guard \d+', comm)
+ name = m.group(0)
+ else:
name = comm[2:comm.find(':')-1]
if name in dumps:
bname, start_ofs, dump = dumps[name]
- parser.postprocess(loop, backend_tp=bname, backend_dump=dump,
- dump_start=start_ofs)
+ loop.force_asm = (lambda dump=dump, start_ofs=start_ofs,
+ bname=bname, loop=loop:
+ parser.postprocess(loop, backend_tp=bname,
+ backend_dump=dump,
+ dump_start=start_ofs))
loops.append(loop)
return log, loops
diff --git a/pypy/tool/jitlogparser/test/logtest2.log b/pypy/tool/jitlogparser/test/logtest2.log
new file mode 100644
--- /dev/null
+++ b/pypy/tool/jitlogparser/test/logtest2.log
@@ -0,0 +1,301 @@
+[1f5e7f69779] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b000 +0 4157415641554154415341524151415057565554535251504889E349C7C340BC920041FFD34889DF4883E4F049C7C350BC920041FFD3488D65D8415F415E415D415C5B5DC3
+[1f5e7f7fe75] jit-backend-dump}
+[1f5e7f84fc4] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b045 +0 4157415641554154415341524151415057565554535251504889E349C7C3F0BB920041FFD34889DF4883E4F049C7C350BC920041FFD3488D65D8415F415E415D415C5B5DC3
+[1f5e7f87ac1] jit-backend-dump}
+[1f5e7f8a0b4] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b08a +0 4157415641554154415341524151415057565554535251504889E34881EC80000000F20F110424F20F114C2408F20F11542410F20F115C2418F20F11642420F20F116C2428F20F11742430F20F117C2438F2440F11442440F2440F114C2448F2440F11542450F2440F115C2458F2440F11642460F2440F116C2468F2440F11742470F2440F117C247849C7C340BC920041FFD34889DF4883E4F049C7C350BC920041FFD3488D65D8415F415E415D415C5B5DC3
+[1f5e7f8da6b] jit-backend-dump}
+[1f5e7f8f4f6] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b13d +0 4157415641554154415341524151415057565554535251504889E34881EC80000000F20F110424F20F114C2408F20F11542410F20F115C2418F20F11642420F20F116C2428F20F11742430F20F117C2438F2440F11442440F2440F114C2448F2440F11542450F2440F115C2458F2440F11642460F2440F116C2468F2440F11742470F2440F117C247849C7C3F0BB920041FFD34889DF4883E4F049C7C350BC920041FFD3488D65D8415F415E415D415C5B5DC3
+[1f5e7f92b83] jit-backend-dump}
+[1f5e7f95b99] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b210 +0 F20F11442410F20F114C2418F20F11542420F20F115C2428F20F11642430F20F116C2438F20F11742440F20F117C2448F2440F11442450F2440F114C2458F2440F11542460F2440F115C2468F2440F11642470F2440F116C2478F2440F11B42480000000F2440F11BC24880000004829C24889D749C7C350A8920041FFE3
+[1f5e7f988d0] jit-backend-dump}
+[1f5e7fa16fb] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b28e +0 F20F10442410F20F104C2418F20F10542420F20F105C2428F20F10642430F20F106C2438F20F10742440F20F107C2448F2440F10442450F2440F104C2458F2440F10542460F2440F105C2468F2440F10642470F2440F106C2478F2440F10B42480000000F2440F10BC2488000000488B1425704F3D01C3
+[1f5e7fa47ac] jit-backend-dump}
+[1f5e7fab3a4] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b305 +0 57565251415041514883EC40F20F110424F20F114C2408F20F11542410F20F115C2418F20F11642420F20F116C2428F20F11742430F20F117C2438488D7D1049C7C340BA520041FFD3488B042550546B024885C0753CF20F107C2438F20F10742430F20F106C2428F20F10642420F20F105C2418F20F10542410F20F104C2408F20F1004244883C44041594158595A5E5FC3488B042558546B0248C7042550546B020000000048C7042558546B02000000004889042590C2540149C7C340BC920041FFD348C7C0020000004883C478C3
+[1f5e7faf1ca] jit-backend-dump}
+[1f5e7fb0813] {jit-backend-counts
+[1f5e7fb0f61] jit-backend-counts}
+[1f5fd38be3e] {jit-backend
+[1f5fe729336] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b3d5 +0 554889E5534154415541564157488DA500000000488B042590C2540148C7042590C254010000000048898570FFFFFF488B042598C2540148C7042598C254010000000048898568FFFFFF488B0425A0C2540148C70425A0C254010000000048898560FFFFFF488B0425A8C2540148C70425A8C254010000000048898558FFFFFF4C8B3C25D04D5B0149BB30B00C0A897F00004D8B334983C60149BB30B00C0A897F00004D89334981FF102700000F8D000000004D89FE4983E7024983FF000F85000000004983C6034C8B3C25A0536B024983EF014C893C25A0536B024983FF000F8C000000004D89F7E99AFFFFFF488B0425A8536B024829E0483B042580DC3C01760D49BB05B3A007897F000041FFD3554889E5534154415541564157488DA550FFFFFF4889BD70FFFFFF4889B568FFFFFF48899560FFFFFF48898D58FFFFFF4D89C7E940FFFFFF49BB00B0A007897F000041FFD34440484C3D030300000049BB00B0A007897F000041FFD34440484C3D39030400000049BB00B0A007897F000041FFD34440484C3907070305000000
+[1f5fe73276a] jit-backend-dump}
+[1f5fe73438f] {jit-backend-addr
+Loop 0 (<code object f, file 'x.py', line 2> #9 LOAD_FAST) has address 7f8907a0b45d to 7f8907a0b4c3 (bootstrap 7f8907a0b3d5)
+[1f5fe7369af] jit-backend-addr}
+[1f5fe737940] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b3e5 +0 50FFFFFF
+[1f5fe74b40e] jit-backend-dump}
+[1f5fe74c63d] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b484 +0 95000000
+[1f5fe74da6a] jit-backend-dump}
+[1f5fe74e438] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b495 +0 9B000000
+[1f5fe74f513] jit-backend-dump}
+[1f5fe74fd2e] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b4b7 +0 91000000
+[1f5fe750d8c] jit-backend-dump}
+[1f5fe75373f] jit-backend}
+[1f5fe755abc] {jit-log-opt-loop
+# Loop 0 : loop with 26 ops
+[p0, p1, p2, p3, i4]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #9 LOAD_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #12 LOAD_CONST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #15 COMPARE_OP')
++166: i6 = int_lt(i4, 10000)
+guard_true(i6, descr=<Guard3>) [p1, p0, p2, p3, i4]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #18 POP_JUMP_IF_FALSE')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #21 LOAD_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #24 LOAD_CONST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #27 BINARY_AND')
++179: i8 = int_and(i4, 2)
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #28 POP_JUMP_IF_FALSE')
++186: i9 = int_is_true(i8)
+guard_false(i9, descr=<Guard4>) [p1, p0, p2, p3, i8, i4]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #44 LOAD_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #47 LOAD_CONST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #50 INPLACE_ADD')
++196: i11 = int_add(i4, 3)
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #51 STORE_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #54 JUMP_ABSOLUTE')
++200: i13 = getfield_raw(40588192, descr=<SignedFieldDescr pypysig_long_struct.c_value 0>)
++208: i15 = int_sub(i13, 1)
++212: setfield_raw(40588192, i15, descr=<SignedFieldDescr pypysig_long_struct.c_value 0>)
++220: i17 = int_lt(i15, 0)
+guard_false(i17, descr=<Guard5>) [p1, p0, p2, p3, i11, None, None]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #9 LOAD_FAST')
++230: jump(p0, p1, p2, p3, i11, descr=<Loop0>)
++238: --end of the loop--
+[1f5fe92b8af] jit-log-opt-loop}
+[1f5fe944ae5] {jit-backend
+[1f5fee20651] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b565 +0 554889E5534154415541564157488DA5000000004C8B3C2590C2540148C7042590C25401000000004C8B342598C2540148C7042598C25401000000004C8B2C25A0C2540148C70425A0C25401000000004C8B2425A8C2540148C70425A8C25401000000004C8B1425D04D5B014C8B0C25B8C2540148C70425B8C25401000000004C8B0425E04D5B01488B3C25E84D5B01488B3425D0C2540148C70425D0C2540100000000488B1C25D8C2540148C70425D8C2540100000000488B1425E0C2540148C70425E0C254010000000049BB38B00C0A897F0000498B0B4883C10149BB38B00C0A897F000049890B4983F8010F85000000004883FE017206813E980700000F85000000004983FA000F850000000049BBA8F0B407897F00004D39DC0F8500000000488B56084881FA102700000F8D000000004989D44883E2024883FA000F85000000004983C403488B1425A0536B024883EA0148891425A0536B024883FA000F8C000000004C89BD70FFFFFF4C89B568FFFFFF4C89AD60FFFFFF4C898D58FFFFFF4D89E749BB5DB4A007897F000041FFE3488B0425A8536B024829E0483B042580DC3C01760D49BB05B3A007897F000041FFD3554889E5534154415541564157488DA550FFFFFF4989FF4989F64989D54989CC4D89C24C8B5D104D89D84C8B5D184C89DF4C8B5D204C89DE4C8B5D284C89DB4C8B5D304C89DAE9CCFEFFFF49BB00B0A007897F000041FFD321383C343029241D180C08030600000049BB00B0A007897F000041FFD3383C18343029240C08030700000049BB00B0A007897F000041FFD329383C3430241808030800000049BB00B0A007897F000041FFD3383C3034241808030900000049BB00B0A007897F000041FFD3383C183424030A00000049BB00B0A007897F000041FFD3383C34241809030B00000049BB00B0A007897F000041FFD3383C34243107030C000000
+[1f5fee2e673] jit-backend-dump}
+[1f5fee2f38d] {jit-backend-addr
+Loop 1 (<code object f, file 'x.py', line 2> #9 LOAD_FAST) has address 7f8907a0b631 to 7f8907a0b6f8 (bootstrap 7f8907a0b565)
+[1f5fee312e3] jit-backend-addr}
+[1f5fee320ed] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b575 +0 50FFFFFF
+[1f5fee3e903] jit-backend-dump}
+[1f5fee3fbff] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b655 +0 0C010000
+[1f5fee41579] jit-backend-dump}
+[1f5fee421af] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b667 +0 17010000
+[1f5fee43835] jit-backend-dump}
+[1f5fee44261] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b671 +0 28010000
+[1f5fee457c1] jit-backend-dump}
+[1f5fee461a5] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b684 +0 2F010000
+[1f5fee475d3] jit-backend-dump}
+[1f5fee47f57] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b695 +0 37010000
+[1f5fee4933d] jit-backend-dump}
+[1f5fee49cd9] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b6a6 +0 3D010000
+[1f5fee4b0ad] jit-backend-dump}
+[1f5fee4ba4f] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b6c8 +0 33010000
+[1f5fee4cf61] jit-backend-dump}
+[1f5fee4dc45] jit-backend}
+[1f5fee4f3a9] {jit-log-opt-loop
+# Loop 1 : entry bridge with 31 ops
+[p0, p1, p2, p3, i4, p5, i6, i7, p8, p9, p10]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #9 LOAD_FAST')
++234: guard_value(i6, 1, descr=<Guard6>) [i6, p1, p0, p2, p3, i4, p5, i7, p8, p9, p10]
++244: guard_nonnull_class(p8, ConstClass(W_IntObject), descr=<Guard7>) [p1, p0, p8, p2, p3, i4, p5, p9, p10]
++262: guard_value(i4, 0, descr=<Guard8>) [i4, p1, p0, p2, p3, p5, p8, p10]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #12 LOAD_CONST')
++272: guard_value(p3, ConstPtr(ptr14), descr=<Guard9>) [p1, p0, p3, p2, p5, p8, p10]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #15 COMPARE_OP')
++291: i15 = getfield_gc_pure(p8, descr=<SignedFieldDescr pypy.objspace.std.intobject.W_IntObject.inst_intval 8>)
++295: i17 = int_lt(i15, 10000)
+guard_true(i17, descr=<Guard10>) [p1, p0, p8, p2, p5]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #18 POP_JUMP_IF_FALSE')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #21 LOAD_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #24 LOAD_CONST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #27 BINARY_AND')
++308: i19 = int_and(i15, 2)
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #28 POP_JUMP_IF_FALSE')
++315: i20 = int_is_true(i19)
+guard_false(i20, descr=<Guard11>) [p1, p0, p2, p5, p8, i19]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #44 LOAD_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #47 LOAD_CONST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #50 INPLACE_ADD')
++325: i22 = int_add(i15, 3)
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #51 STORE_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #54 JUMP_ABSOLUTE')
++329: i24 = getfield_raw(40588192, descr=<SignedFieldDescr pypysig_long_struct.c_value 0>)
++337: i26 = int_sub(i24, 1)
++341: setfield_raw(40588192, i26, descr=<SignedFieldDescr pypysig_long_struct.c_value 0>)
++349: i28 = int_lt(i26, 0)
+guard_false(i28, descr=<Guard12>) [p1, p0, p2, p5, i22, None]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #9 LOAD_FAST')
++359: jump(p0, p1, p2, p5, i22, descr=<Loop0>)
++403: --end of the loop--
+[1f60036d952] jit-log-opt-loop}
+[1f600719a74] {jit-backend
+[1f600759dac] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b817 +0 554889E5534154415541564157488DA500000000488B042590C2540148C7042590C254010000000048898570FFFFFF488B042598C2540148C7042598C254010000000048898568FFFFFF488B0425A0C2540148C70425A0C254010000000048898560FFFFFF488B0425A8C2540148C70425A8C254010000000048898558FFFFFF4C8B3C25D04D5B0149BB40B00C0A897F00004D8B334983C60149BB40B00C0A897F00004D89334981FF102700000F8D000000004D89FE4983E7024983FF000F85000000004983C6034C8B3C25A0536B024983EF024C893C25A0536B024983FF000F8C000000004D89F7E99AFFFFFF488B0425A8536B024829E0483B042580DC3C01760D49BB05B3A007897F000041FFD3554889E5534154415541564157488DA550FFFFFF4889BD70FFFFFF4889B568FFFFFF48899560FFFFFF48898D58FFFFFF4D89C7E940FFFFFF49BB00B0A007897F000041FFD34440484C3D030D00000049BB00B0A007897F000041FFD34440484C3D39030E00000049BB00B0A007897F000041FFD34440484C390707030F000000
+[1f60076fd90] jit-backend-dump}
+[1f600770f30] {jit-backend-addr
+Loop 2 (<code object f, file 'x.py', line 2> #9 LOAD_FAST) has address 7f8907a0b89f to 7f8907a0b905 (bootstrap 7f8907a0b817)
+[1f6007730fc] jit-backend-addr}
+[1f600773fde] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b827 +0 50FFFFFF
+[1f600775c76] jit-backend-dump}
+[1f600776a38] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b8c6 +0 95000000
+[1f600778112] jit-backend-dump}
+[1f600778b8c] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b8d7 +0 9B000000
+[1f60077a04a] jit-backend-dump}
+[1f60077aa6a] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b8f9 +0 91000000
+[1f60077bf10] jit-backend-dump}
+[1f60077cc24] jit-backend}
+[1f60077e094] {jit-log-opt-loop
+# Loop 2 : loop with 25 ops
+[p0, p1, p2, p3, i4]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #12 LOAD_CONST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #15 COMPARE_OP')
++166: i6 = int_lt(i4, 10000)
+guard_true(i6, descr=<Guard13>) [p1, p0, p2, p3, i4]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #18 POP_JUMP_IF_FALSE')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #21 LOAD_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #24 LOAD_CONST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #27 BINARY_AND')
++179: i8 = int_and(i4, 2)
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #28 POP_JUMP_IF_FALSE')
++186: i9 = int_is_true(i8)
+guard_false(i9, descr=<Guard14>) [p1, p0, p2, p3, i8, i4]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #44 LOAD_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #47 LOAD_CONST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #50 INPLACE_ADD')
++196: i11 = int_add(i4, 3)
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #51 STORE_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #54 JUMP_ABSOLUTE')
++200: i13 = getfield_raw(40588192, descr=<SignedFieldDescr pypysig_long_struct.c_value 0>)
++208: i15 = int_sub(i13, 2)
++212: setfield_raw(40588192, i15, descr=<SignedFieldDescr pypysig_long_struct.c_value 0>)
++220: i17 = int_lt(i15, 0)
+guard_false(i17, descr=<Guard15>) [p1, p0, p2, p3, i11, None, None]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #9 LOAD_FAST')
++230: jump(p0, p1, p2, p3, i11, descr=<Loop2>)
++238: --end of the loop--
+[1f6007a567c] jit-log-opt-loop}
+[1f600802cd6] {jit-backend
+[1f600862dd8] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b9b7 +0 488DA50000000049BB48B00C0A897F00004D8B3B4983C70149BB48B00C0A897F00004D893B4D89F74983C6010F80000000004C8B3C25A0536B024983EF014C893C25A0536B024983FF000F8C00000000488B0425704F3D01488D5010483B1425784F3D01761A49BB10B2A007897F000041FFD349BB8EB2A007897F000041FFD348C7009807000048891425704F3D014C89700848898550FFFFFF4C8BBD70FFFFFF4C8BB568FFFFFF4C8BAD60FFFFFF49BBA8F0B407897F00004D89DC49C7C2000000004C8B8D58FFFFFF49C7C00100000048C7C709000000488BB550FFFFFF48C7C30000000048C7C20000000049BB31B6A007897F000041FFE349BB00B0A007897F000041FFD3444039484C3D031000000049BB00B0A007897F000041FFD34440484C39070311000000
+[1f60086ba5a] jit-backend-dump}
+[1f60086d36e] {jit-backend-addr
+Bridge out of guard 4 has address 7f8907a0b9b7 to 7f8907a0bab1
+[1f60086ffd2] jit-backend-addr}
+[1f600870dca] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b9ba +0 C0FEFFFF
+[1f60087281c] jit-backend-dump}
+[1f600873506] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b3d5 +0 C8000000
+[1f600874b44] jit-backend-dump}
+[1f6008754d4] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0ba03 +0 C2000000
+[1f600876956] jit-backend-dump}
+[1f600877b1a] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f8907a0b495 +0 1E050000
+[1f600878f4e] jit-backend-dump}
+[1f600884c12] jit-backend}
+[1f60088780a] {jit-log-opt-bridge
+# bridge out of Guard 4 with 16 ops
+[p0, p1, p2, p3, i4, i5]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #31 LOAD_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #34 LOAD_CONST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #37 INPLACE_ADD')
++37: i7 = int_add_ovf(i5, 1)
+guard_no_overflow(, descr=<Guard16>) [p0, p1, i7, p2, p3, i5]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #38 STORE_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #41 JUMP_ABSOLUTE')
++50: i9 = getfield_raw(40588192, descr=<SignedFieldDescr pypysig_long_struct.c_value 0>)
++58: i11 = int_sub(i9, 1)
++62: setfield_raw(40588192, i11, descr=<SignedFieldDescr pypysig_long_struct.c_value 0>)
++70: i13 = int_lt(i11, 0)
+guard_false(i13, descr=<Guard17>) [p0, p1, p2, p3, i7, None]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #9 LOAD_FAST')
++80: p16 = new_with_vtable(ConstClass(W_IntObject))
++143: setfield_gc(p16, i7, descr=<SignedFieldDescr pypy.objspace.std.intobject.W_IntObject.inst_intval 8>)
++147: jump(p1, p0, p2, ConstPtr(ptr17), 0, p3, 1, 9, p16, ConstPtr(ptr21), ConstPtr(ptr22), descr=<Loop1>)
++250: --end of the loop--
+[1f6008aa976] jit-log-opt-bridge}
+[1f600912c98] {jit-backend-counts
+0:1982
+1:1985
+2:0
+3:1782
+[1f600916544] jit-backend-counts}
diff --git a/pypy/tool/jitlogparser/test/test_parser.py b/pypy/tool/jitlogparser/test/test_parser.py
--- a/pypy/tool/jitlogparser/test/test_parser.py
+++ b/pypy/tool/jitlogparser/test/test_parser.py
@@ -1,6 +1,6 @@
from pypy.tool.jitlogparser.parser import (SimpleParser, TraceForOpcode,
Function, adjust_bridges,
- import_log)
+ import_log, Op)
from pypy.tool.jitlogparser.storage import LoopStorage
import py, sys
@@ -181,7 +181,7 @@
""")
ops = Function.from_operations(loop.operations, LoopStorage())
chunk = ops.chunks[0]
- assert chunk.bytecode_name == 'StrLiteralSearch'
+ assert chunk.bytecode_name.startswith('StrLiteralSearch')
def test_parsing_assembler():
backend_dump = "554889E5534154415541564157488DA500000000488B042590C5540148C7042590C554010000000048898570FFFFFF488B042598C5540148C7042598C554010000000048898568FFFFFF488B0425A0C5540148C70425A0C554010000000048898560FFFFFF488B0425A8C5540148C70425A8C554010000000048898558FFFFFF4C8B3C2550525B0149BB30E06C96FC7F00004D8B334983C60149BB30E06C96FC7F00004D89334981FF102700000F8D000000004983C7014C8B342580F76A024983EE014C89342580F76A024983FE000F8C00000000E9AEFFFFFF488B042588F76A024829E0483B042580EC3C01760D49BB05F30894FC7F000041FFD3554889E5534154415541564157488DA550FFFFFF4889BD70FFFFFF4889B568FFFFFF48899560FFFFFF48898D58FFFFFF4D89C7E954FFFFFF49BB00F00894FC7F000041FFD34440484C3D030300000049BB00F00894FC7F000041FFD34440484C3D070304000000"
@@ -213,4 +213,21 @@
def test_import_log():
_, loops = import_log(str(py.path.local(__file__).join('..',
'logtest.log')))
+ for loop in loops:
+ loop.force_asm()
assert 'jge' in loops[0].operations[3].asm
+
+def test_import_log_2():
+ _, loops = import_log(str(py.path.local(__file__).join('..',
+ 'logtest2.log')))
+ for loop in loops:
+ loop.force_asm()
+ assert 'cmp' in loops[1].operations[1].asm
+ # bridge
+ assert 'jo' in loops[3].operations[3].asm
+
+def test_Op_repr_is_pure():
+ op = Op('foobar', ['a', 'b'], 'c', 'mydescr')
+ myrepr = 'c = foobar(a, b, descr=mydescr)'
+ assert op.repr() == myrepr
+ assert op.repr() == myrepr # do it twice
diff --git a/pypy/tool/nullpath.py b/pypy/tool/nullpath.py
new file mode 100644
--- /dev/null
+++ b/pypy/tool/nullpath.py
@@ -0,0 +1,12 @@
+import py
+
+class NullPyPathLocal(py.path.local):
+
+ def join(self, *args):
+ return self.__class__(py.path.local.join(self, *args))
+
+ def open(self, mode):
+ return open('/dev/null', mode)
+
+ def __repr__(self):
+ return py.path.local.__repr__(self) + ' [fake]'
diff --git a/pypy/tool/release/win32build.py b/pypy/tool/release/win32build.py
--- a/pypy/tool/release/win32build.py
+++ b/pypy/tool/release/win32build.py
@@ -24,6 +24,6 @@
shutil.copy(str(pypydir.join('..', '..', 'expat-2.0.1', 'win32', 'bin', 'release', 'libexpat.dll')), str(builddir))
make_pypy('', ['-Ojit'])
-make_pypy('-nojit', [])
+make_pypy('-nojit', ['-O2'])
#make_pypy('-stackless', [--stackless])
#make_pypy('-sandbox', [--sandbox])
diff --git a/pypy/tool/test/test_nullpath.py b/pypy/tool/test/test_nullpath.py
new file mode 100644
--- /dev/null
+++ b/pypy/tool/test/test_nullpath.py
@@ -0,0 +1,16 @@
+import sys
+import py
+from pypy.tool.nullpath import NullPyPathLocal
+
+def setup_module():
+ if 'posix' not in sys.builtin_module_names:
+ py.test.skip('posix only')
+
+def test_nullpath(tmpdir):
+ path = NullPyPathLocal(tmpdir)
+ assert repr(path).endswith('[fake]')
+ foo_txt = path.join('foo.txt')
+ assert isinstance(foo_txt, NullPyPathLocal)
+ #
+ f = foo_txt.open('w')
+ assert f.name == '/dev/null'
diff --git a/pypy/translator/c/gcc/trackgcroot.py b/pypy/translator/c/gcc/trackgcroot.py
--- a/pypy/translator/c/gcc/trackgcroot.py
+++ b/pypy/translator/c/gcc/trackgcroot.py
@@ -1824,6 +1824,11 @@
__gccallshapes:
""".replace("__gccallshapes", _globalname("__gccallshapes"))
output.writelines(shapelines)
+ print >> output, """\
+ #if defined(__linux__) && defined(__ELF__)
+ .section .note.GNU-stack,"",%progbits
+ #endif
+ """
def process(self, iterlines, newfile, filename='?'):
parser = PARSERS[format](verbose=self.verbose, shuffle=self.shuffle)
diff --git a/pypy/translator/c/genc.py b/pypy/translator/c/genc.py
--- a/pypy/translator/c/genc.py
+++ b/pypy/translator/c/genc.py
@@ -13,6 +13,7 @@
from pypy.rpython.typesystem import getfunctionptr
from pypy.translator.c import gc
from pypy.rlib import exports
+from pypy.tool.nullpath import NullPyPathLocal
def import_module_from_directory(dir, modname):
file, pathname, description = imp.find_module(modname, [str(dir)])
@@ -237,6 +238,8 @@
self.modulename = uniquemodulename('testing')
modulename = self.modulename
targetdir = udir.ensure(modulename, dir=1)
+ if self.config.translation.dont_write_c_files:
+ targetdir = NullPyPathLocal(targetdir)
self.targetdir = targetdir
defines = defines.copy()
@@ -248,12 +251,8 @@
CBuilder.have___thread = self.translator.platform.check___thread()
if not self.standalone:
assert not self.config.translation.instrument
- self.eci, cfile, extra = gen_source(db, modulename, targetdir,
- self.eci,
- defines = defines,
- split=self.split)
else:
- pfname = db.get(pf)
+ defines['PYPY_STANDALONE'] = db.get(pf)
if self.config.translation.instrument:
defines['INSTRUMENT'] = 1
if CBuilder.have___thread:
@@ -263,11 +262,9 @@
defines['PYPY_MAIN_FUNCTION'] = "pypy_main_startup"
self.eci = self.eci.merge(ExternalCompilationInfo(
export_symbols=["pypy_main_startup"]))
- self.eci, cfile, extra = gen_source_standalone(db, modulename,
- targetdir,
- self.eci,
- entrypointname = pfname,
- defines = defines)
+ self.eci, cfile, extra = gen_source(db, modulename, targetdir,
+ self.eci, defines=defines,
+ split=self.split)
self.c_source_filename = py.path.local(cfile)
self.extrafiles = self.eventually_copy(extra)
self.gen_makefile(targetdir, exe_name=exe_name)
@@ -432,6 +429,7 @@
class CStandaloneBuilder(CBuilder):
standalone = True
+ split = True
executable_name = None
shared_library_name = None
@@ -688,11 +686,37 @@
def getothernodes(self):
return self.othernodes[:]
+ def getbasecfilefornode(self, node, basecname):
+ # For FuncNode instances, use the python source filename (relative to
+ # the top directory):
+ if hasattr(node.obj, 'graph'):
+ g = node.obj.graph
+ # Lookup the filename from the function.
+ # However, not all FunctionGraph objs actually have a "func":
+ if hasattr(g, 'func'):
+ if g.filename.endswith('.py'):
+ localpath = py.path.local(g.filename)
+ pypkgpath = localpath.pypkgpath()
+ if pypkgpath:
+ relpypath = localpath.relto(pypkgpath)
+ return relpypath.replace('.py', '.c')
+ return basecname
+
def splitnodesimpl(self, basecname, nodes, nextra, nbetween,
split_criteria=SPLIT_CRITERIA):
+ # Gather nodes by some criteria:
+ nodes_by_base_cfile = {}
+ for node in nodes:
+ c_filename = self.getbasecfilefornode(node, basecname)
+ if c_filename in nodes_by_base_cfile:
+ nodes_by_base_cfile[c_filename].append(node)
+ else:
+ nodes_by_base_cfile[c_filename] = [node]
+
# produce a sequence of nodes, grouped into files
# which have no more than SPLIT_CRITERIA lines
- iternodes = iter(nodes)
+ for basecname in nodes_by_base_cfile:
+ iternodes = iter(nodes_by_base_cfile[basecname])
done = [False]
def subiter():
used = nextra
@@ -918,64 +942,12 @@
]
return eci.merge(ExternalCompilationInfo(separate_module_files=files))
-def gen_source_standalone(database, modulename, targetdir, eci,
- entrypointname, defines={}):
- assert database.standalone
+
+def gen_source(database, modulename, targetdir,
+ eci, defines={}, split=False):
if isinstance(targetdir, str):
targetdir = py.path.local(targetdir)
- filename = targetdir.join(modulename + '.c')
- f = filename.open('w')
- incfilename = targetdir.join('common_header.h')
- fi = incfilename.open('w')
- #
- # Header
- #
- print >> f, '#include "common_header.h"'
- print >> f
- commondefs(defines)
- defines['PYPY_STANDALONE'] = entrypointname
- for key, value in defines.items():
- print >> fi, '#define %s %s' % (key, value)
-
- eci.write_c_header(fi)
- print >> fi, '#include "src/g_prerequisite.h"'
-
- fi.close()
-
- preimplementationlines = list(
- pre_include_code_lines(database, database.translator.rtyper))
-
- #
- # 1) All declarations
- # 2) Implementation of functions and global structures and arrays
- #
- sg = SourceGenerator(database, preimplementationlines)
- sg.set_strategy(targetdir)
- database.prepare_inline_helpers()
- sg.gen_readable_parts_of_source(f)
-
- # 3) start-up code
- print >> f
- gen_startupcode(f, database)
-
- f.close()
-
- if 'INSTRUMENT' in defines:
- fi = incfilename.open('a')
- n = database.instrument_ncounter
- print >>fi, "#define INSTRUMENT_NCOUNTER %d" % n
- fi.close()
-
- eci = add_extra_files(eci)
- eci = eci.convert_sources_to_files(being_main=True)
- files, eci = eci.get_module_files()
- return eci, filename, sg.getextrafiles() + list(files)
-
-def gen_source(database, modulename, targetdir, eci, defines={}, split=False):
- assert not database.standalone
- if isinstance(targetdir, str):
- targetdir = py.path.local(targetdir)
filename = targetdir.join(modulename + '.c')
f = filename.open('w')
incfilename = targetdir.join('common_header.h')
@@ -1014,6 +986,12 @@
gen_startupcode(f, database)
f.close()
+ if 'INSTRUMENT' in defines:
+ fi = incfilename.open('a')
+ n = database.instrument_ncounter
+ print >>fi, "#define INSTRUMENT_NCOUNTER %d" % n
+ fi.close()
+
eci = add_extra_files(eci)
eci = eci.convert_sources_to_files(being_main=True)
files, eci = eci.get_module_files()
diff --git a/pypy/translator/c/test/test_genc.py b/pypy/translator/c/test/test_genc.py
--- a/pypy/translator/c/test/test_genc.py
+++ b/pypy/translator/c/test/test_genc.py
@@ -4,7 +4,6 @@
from pypy.translator.translator import TranslationContext
from pypy.translator.c.database import LowLevelDatabase
from pypy.translator.c import genc
-from pypy.translator.c.genc import gen_source
from pypy.translator.c.gc import NoneGcPolicy
from pypy.objspace.flow.model import Constant, Variable, SpaceOperation
from pypy.objspace.flow.model import Block, Link, FunctionGraph
@@ -13,6 +12,7 @@
from pypy.translator.backendopt.all import backend_optimizations
from pypy.translator.interactive import Translation
from pypy.rlib.entrypoint import entrypoint
+from pypy.tool.nullpath import NullPyPathLocal
def compile(fn, argtypes, view=False, gcpolicy="ref", backendopt=True,
annotatorpolicy=None):
@@ -63,6 +63,22 @@
py.test.raises(Exception, f1, "world") # check that it's really typed
+
+def test_dont_write_source_files():
+ def f(x):
+ return x*2
+ t = TranslationContext()
+ t.buildannotator().build_types(f, [int])
+ t.buildrtyper().specialize()
+
+ t.config.translation.countmallocs = True
+ t.config.translation.dont_write_c_files = True
+ builder = genc.CExtModuleBuilder(t, f, config=t.config)
+ builder.generate_source()
+ assert isinstance(builder.targetdir, NullPyPathLocal)
+ assert builder.targetdir.listdir() == []
+
+
def test_simple_lambda():
f = lambda x: x*2
t = TranslationContext()
diff --git a/pypy/translator/c/test/test_standalone.py b/pypy/translator/c/test/test_standalone.py
--- a/pypy/translator/c/test/test_standalone.py
+++ b/pypy/translator/c/test/test_standalone.py
@@ -55,6 +55,13 @@
data = cbuilder.cmdexec('hi there')
assert data.startswith('''hello world\nargument count: 2\n 'hi'\n 'there'\n''')
+ # Verify that the generated C files have sane names:
+ gen_c_files = [str(f) for f in cbuilder.extrafiles]
+ for expfile in ('rlib_rposix.c',
+ 'rpython_lltypesystem_rstr.c',
+ 'translator_c_test_test_standalone.c'):
+ assert cbuilder.targetdir.join(expfile) in gen_c_files
+
def test_print(self):
def entry_point(argv):
print "hello simpler world"
diff --git a/pypy/translator/cli/src/pypylib.cs b/pypy/translator/cli/src/pypylib.cs
--- a/pypy/translator/cli/src/pypylib.cs
+++ b/pypy/translator/cli/src/pypylib.cs
@@ -615,11 +615,29 @@
return s1.StartsWith(s2);
}
+ public static bool ll_startswith_char(string s, char c)
+ {
+ if (s.Length == 0)
+ {
+ return false;
+ }
+ return s[0] == c;
+ }
+
public static bool ll_endswith(string s1, string s2)
{
return s1.EndsWith(s2);
}
+ public static bool ll_endswith_char(string s, char c)
+ {
+ if (s.Length == 0)
+ {
+ return false;
+ }
+ return s[s.Length - 1] == c;
+ }
+
public static int ll_find(string s1, string s2, int start, int stop)
{
if (stop > s1.Length)
diff --git a/pypy/translator/jvm/src/pypy/PyPy.java b/pypy/translator/jvm/src/pypy/PyPy.java
--- a/pypy/translator/jvm/src/pypy/PyPy.java
+++ b/pypy/translator/jvm/src/pypy/PyPy.java
@@ -791,6 +791,20 @@
return str.substring(start,start+cnt);
}
+ public static boolean ll_startswith_char(String str, char c) {
+ if (str.length() == 0) {
+ return false;
+ }
+ return str.charAt(0) == c;
+ }
+
+ public static boolean ll_endswith_char(String str, char c) {
+ if (str.length() == 0) {
+ return false;
+ }
+ return str.charAt(str.length() - 1) == c;
+ }
+
// ----------------------------------------------------------------------
// StringBuffer
diff --git a/pytest.py b/pytest.py
--- a/pytest.py
+++ b/pytest.py
@@ -9,6 +9,8 @@
from _pytest import __version__
if __name__ == '__main__': # if run as a script or by 'python -m pytest'
- raise SystemExit(main())
+ #XXX: sync to upstream later
+ import pytest_cov
+ raise SystemExit(main(plugins=[pytest_cov]))
else:
_preloadplugins() # to populate pytest.* namespace so help(pytest) works
diff --git a/pytest_cov.py b/pytest_cov.py
new file mode 100644
--- /dev/null
+++ b/pytest_cov.py
@@ -0,0 +1,353 @@
+"""produce code coverage reports using the 'coverage' package, including support for distributed testing.
+
+This plugin produces coverage reports. It supports centralised testing and distributed testing in
+both load and each modes. It also supports coverage of subprocesses.
+
+All features offered by the coverage package should be available, either through pytest-cov or
+through coverage's config file.
+
+
+Installation
+------------
+
+The `pytest-cov`_ package may be installed with pip or easy_install::
+
+ pip install pytest-cov
+ easy_install pytest-cov
+
+.. _`pytest-cov`: http://pypi.python.org/pypi/pytest-cov/
+
+
+Uninstallation
+--------------
+
+Uninstalling packages is supported by pip::
+
+ pip uninstall pytest-cov
+
+However easy_install does not provide an uninstall facility.
+
+.. IMPORTANT::
+
+ Ensure that you manually delete the init_cov_core.pth file in your site-packages directory.
+
+ This file starts coverage collection of subprocesses if appropriate during site initialisation
+ at python startup.
+
+
+Usage
+-----
+
+Centralised Testing
+~~~~~~~~~~~~~~~~~~~
+
+Centralised testing will report on the combined coverage of the main process and all of it's
+subprocesses.
+
+Running centralised testing::
+
+ py.test --cov myproj tests/
+
+Shows a terminal report::
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+Distributed Testing: Load
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Distributed testing with dist mode set to load will report on the combined coverage of all slaves.
+The slaves may be spread out over any number of hosts and each slave may be located anywhere on the
+file system. Each slave will have it's subprocesses measured.
+
+Running distributed testing with dist mode set to load::
+
+ py.test --cov myproj -n 2 tests/
+
+Shows a terminal report::
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+Again but spread over different hosts and different directories::
+
+ py.test --cov myproj --dist load
+ --tx ssh=memedough at host1//chdir=testenv1
+ --tx ssh=memedough at host2//chdir=/tmp/testenv2//python=/tmp/env1/bin/python
+ --rsyncdir myproj --rsyncdir tests --rsync examples
+ tests/
+
+Shows a terminal report::
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+Distributed Testing: Each
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Distributed testing with dist mode set to each will report on the combined coverage of all slaves.
+Since each slave is running all tests this allows generating a combined coverage report for multiple
+environments.
+
+Running distributed testing with dist mode set to each::
+
+ py.test --cov myproj --dist each
+ --tx popen//chdir=/tmp/testenv3//python=/usr/local/python27/bin/python
+ --tx ssh=memedough at host2//chdir=/tmp/testenv4//python=/tmp/env2/bin/python
+ --rsyncdir myproj --rsyncdir tests --rsync examples
+ tests/
+
+Shows a terminal report::
+
+ ---------------------------------------- coverage ----------------------------------------
+ platform linux2, python 2.6.5-final-0
+ platform linux2, python 2.7.0-final-0
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+Reporting
+---------
+
+It is possible to generate any combination of the reports for a single test run.
+
+The available reports are terminal (with or without missing line numbers shown), HTML, XML and
+annotated source code.
+
+The terminal report without line numbers (default)::
+
+ py.test --cov-report term --cov myproj tests/
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+The terminal report with line numbers::
+
+ py.test --cov-report term-missing --cov myproj tests/
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover Missing
+ --------------------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94% 24-26, 99, 149, 233-236, 297-298, 369-370
+ myproj/feature4286 94 7 92% 183-188, 197
+ --------------------------------------------------
+ TOTAL 353 20 94%
+
+
+The remaining three reports output to files without showing anything on the terminal (useful for
+when the output is going to a continuous integration server)::
+
+ py.test --cov-report html
+ --cov-report xml
+ --cov-report annotate
+ --cov myproj tests/
+
+
+Coverage Data File
+------------------
+
+The data file is erased at the beginning of testing to ensure clean data for each test run.
+
+The data file is left at the end of testing so that it is possible to use normal coverage tools to
+examine it.
+
+
+Coverage Config File
+--------------------
+
+This plugin provides a clean minimal set of command line options that are added to pytest. For
+further control of coverage use a coverage config file.
+
+For example if tests are contained within the directory tree being measured the tests may be
+excluded if desired by using a .coveragerc file with the omit option set::
+
+ py.test --cov-config .coveragerc
+ --cov myproj
+ myproj/tests/
+
+Where the .coveragerc file contains file globs::
+
+ [run]
+ omit = tests/*
+
+For full details refer to the `coverage config file`_ documentation.
+
+.. _`coverage config file`: http://nedbatchelder.com/code/coverage/config.html
+
+Note that this plugin controls some options and setting the option in the config file will have no
+effect. These include specifying source to be measured (source option) and all data file handling
+(data_file and parallel options).
+
+
+Limitations
+-----------
+
+For distributed testing the slaves must have the pytest-cov package installed. This is needed since
+the plugin must be registered through setuptools / distribute for pytest to start the plugin on the
+slave.
+
+For subprocess measurement environment variables must make it from the main process to the
+subprocess. The python used by the subprocess must have pytest-cov installed. The subprocess must
+do normal site initialisation so that the environment variables can be detected and coverage
+started.
+
+
+Acknowledgements
+----------------
+
+Whilst this plugin has been built fresh from the ground up it has been influenced by the work done
+on pytest-coverage (Ross Lawley, James Mills, Holger Krekel) and nose-cover (Jason Pellerin) which are
+other coverage plugins.
+
+Ned Batchelder for coverage and its ability to combine the coverage results of parallel runs.
+
+Holger Krekel for pytest with its distributed testing support.
+
+Jason Pellerin for nose.
+
+Michael Foord for unittest2.
+
+No doubt others have contributed to these tools as well.
+"""
+
+
+def pytest_addoption(parser):
+ """Add options to control coverage."""
+
+ group = parser.getgroup('coverage reporting with distributed testing support')
+ group.addoption('--cov', action='append', default=[], metavar='path',
+ dest='cov_source',
+ help='measure coverage for filesystem path (multi-allowed)')
+ group.addoption('--cov-report', action='append', default=[], metavar='type',
+ choices=['term', 'term-missing', 'annotate', 'html', 'xml'],
+ dest='cov_report',
+ help='type of report to generate: term, term-missing, annotate, html, xml (multi-allowed)')
+ group.addoption('--cov-config', action='store', default='.coveragerc', metavar='path',
+ dest='cov_config',
+ help='config file for coverage, default: .coveragerc')
+
+
+def pytest_configure(config):
+ """Activate coverage plugin if appropriate."""
+
+ if config.getvalue('cov_source'):
+ config.pluginmanager.register(CovPlugin(), '_cov')
+
+
+class CovPlugin(object):
+ """Use coverage package to produce code coverage reports.
+
+ Delegates all work to a particular implementation based on whether
+ this test process is centralised, a distributed master or a
+ distributed slave.
+ """
+
+ def __init__(self):
+ """Creates a coverage pytest plugin.
+
+ We read the rc file that coverage uses to get the data file
+ name. This is needed since we give coverage through it's API
+ the data file name.
+ """
+
+ # Our implementation is unknown at this time.
+ self.cov_controller = None
+
+ def pytest_sessionstart(self, session):
+ """At session start determine our implementation and delegate to it."""
+
+ import cov_core
+
+ cov_source = session.config.getvalue('cov_source')
+ cov_report = session.config.getvalue('cov_report') or ['term']
+ cov_config = session.config.getvalue('cov_config')
+
+ session_name = session.__class__.__name__
+ is_master = (session.config.pluginmanager.hasplugin('dsession') or
+ session_name == 'DSession')
+ is_slave = (hasattr(session.config, 'slaveinput') or
+ session_name == 'SlaveSession')
+ nodeid = None
+
+ if is_master:
+ controller_cls = cov_core.DistMaster
+ elif is_slave:
+ controller_cls = cov_core.DistSlave
+ nodeid = session.config.slaveinput.get('slaveid', getattr(session, 'nodeid'))
+ else:
+ controller_cls = cov_core.Central
+
+ self.cov_controller = controller_cls(cov_source,
+ cov_report,
+ cov_config,
+ session.config,
+ nodeid)
+
+ self.cov_controller.start()
+
+ def pytest_configure_node(self, node):
+ """Delegate to our implementation."""
+
+ self.cov_controller.configure_node(node)
+ pytest_configure_node.optionalhook = True
+
+ def pytest_testnodedown(self, node, error):
+ """Delegate to our implementation."""
+
+ self.cov_controller.testnodedown(node, error)
+ pytest_testnodedown.optionalhook = True
+
+ def pytest_sessionfinish(self, session, exitstatus):
+ """Delegate to our implementation."""
+
+ self.cov_controller.finish()
+
+ def pytest_terminal_summary(self, terminalreporter):
+ """Delegate to our implementation."""
+
+ self.cov_controller.summary(terminalreporter._tw)
+
+
+def pytest_funcarg__cov(request):
+ """A pytest funcarg that provides access to the underlying coverage object."""
+
+ # Check with hasplugin to avoid getplugin exception in older pytest.
+ if request.config.pluginmanager.hasplugin('_cov'):
+ plugin = request.config.pluginmanager.getplugin('_cov')
+ if plugin.cov_controller:
+ return plugin.cov_controller.cov
+ return None
More information about the pypy-commit
mailing list