[pypy-commit] pypy stm: hg merge default
arigo
noreply at buildbot.pypy.org
Mon Nov 28 18:38:38 CET 2011
Author: Armin Rigo <arigo at tunes.org>
Branch: stm
Changeset: r49926:1b1707eedf87
Date: 2011-11-28 18:37 +0100
http://bitbucket.org/pypy/pypy/changeset/1b1707eedf87/
Log: hg merge default
diff too long, truncating to 10000 out of 19561 lines
diff --git a/.hgtags b/.hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -1,3 +1,4 @@
b590cf6de4190623aad9aa698694c22e614d67b9 release-1.5
b48df0bf4e75b81d98f19ce89d4a7dc3e1dab5e5 benchmarked
d8ac7d23d3ec5f9a0fa1264972f74a010dbfd07f release-1.6
+ff4af8f318821f7f5ca998613a60fca09aa137da release-1.7
diff --git a/lib-python/2.7/test/test_os.py b/lib-python/2.7/test/test_os.py
--- a/lib-python/2.7/test/test_os.py
+++ b/lib-python/2.7/test/test_os.py
@@ -74,7 +74,8 @@
self.assertFalse(os.path.exists(name),
"file already exists for temporary file")
# make sure we can create the file
- open(name, "w")
+ f = open(name, "w")
+ f.close()
self.files.append(name)
def test_tempnam(self):
diff --git a/lib-python/conftest.py b/lib-python/conftest.py
--- a/lib-python/conftest.py
+++ b/lib-python/conftest.py
@@ -201,7 +201,7 @@
RegrTest('test_difflib.py'),
RegrTest('test_dircache.py', core=True),
RegrTest('test_dis.py'),
- RegrTest('test_distutils.py'),
+ RegrTest('test_distutils.py', skip=True),
RegrTest('test_dl.py', skip=True),
RegrTest('test_doctest.py', usemodules="thread"),
RegrTest('test_doctest2.py'),
diff --git a/lib-python/modified-2.7/ctypes/test/test_simplesubclasses.py b/lib-python/modified-2.7/ctypes/test/test_simplesubclasses.py
--- a/lib-python/modified-2.7/ctypes/test/test_simplesubclasses.py
+++ b/lib-python/modified-2.7/ctypes/test/test_simplesubclasses.py
@@ -1,6 +1,5 @@
import unittest
from ctypes import *
-from ctypes.test import xfail
class MyInt(c_int):
def __cmp__(self, other):
@@ -27,7 +26,6 @@
self.assertEqual(None, cb())
- @xfail
def test_int_callback(self):
args = []
def func(arg):
diff --git a/lib-python/2.7/pkgutil.py b/lib-python/modified-2.7/pkgutil.py
copy from lib-python/2.7/pkgutil.py
copy to lib-python/modified-2.7/pkgutil.py
--- a/lib-python/2.7/pkgutil.py
+++ b/lib-python/modified-2.7/pkgutil.py
@@ -244,7 +244,8 @@
return mod
def get_data(self, pathname):
- return open(pathname, "rb").read()
+ with open(pathname, "rb") as f:
+ return f.read()
def _reopen(self):
if self.file and self.file.closed:
diff --git a/lib-python/modified-2.7/test/test_import.py b/lib-python/modified-2.7/test/test_import.py
--- a/lib-python/modified-2.7/test/test_import.py
+++ b/lib-python/modified-2.7/test/test_import.py
@@ -64,6 +64,7 @@
except ImportError, err:
self.fail("import from %s failed: %s" % (ext, err))
else:
+ # XXX importing .pyw is missing on Windows
self.assertEqual(mod.a, a,
"module loaded (%s) but contents invalid" % mod)
self.assertEqual(mod.b, b,
diff --git a/lib-python/modified-2.7/test/test_repr.py b/lib-python/modified-2.7/test/test_repr.py
--- a/lib-python/modified-2.7/test/test_repr.py
+++ b/lib-python/modified-2.7/test/test_repr.py
@@ -254,8 +254,14 @@
eq = self.assertEqual
touch(os.path.join(self.subpkgname, self.pkgname + os.extsep + 'py'))
from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import areallylongpackageandmodulenametotestreprtruncation
- eq(repr(areallylongpackageandmodulenametotestreprtruncation),
- "<module '%s' from '%s'>" % (areallylongpackageandmodulenametotestreprtruncation.__name__, areallylongpackageandmodulenametotestreprtruncation.__file__))
+ # On PyPy, we use %r to format the file name; on CPython it is done
+ # with '%s'. It seems to me that %r is safer <arigo>.
+ if '__pypy__' in sys.builtin_module_names:
+ eq(repr(areallylongpackageandmodulenametotestreprtruncation),
+ "<module %r from %r>" % (areallylongpackageandmodulenametotestreprtruncation.__name__, areallylongpackageandmodulenametotestreprtruncation.__file__))
+ else:
+ eq(repr(areallylongpackageandmodulenametotestreprtruncation),
+ "<module '%s' from '%s'>" % (areallylongpackageandmodulenametotestreprtruncation.__name__, areallylongpackageandmodulenametotestreprtruncation.__file__))
eq(repr(sys), "<module 'sys' (built-in)>")
def test_type(self):
diff --git a/lib-python/2.7/test/test_subprocess.py b/lib-python/modified-2.7/test/test_subprocess.py
copy from lib-python/2.7/test/test_subprocess.py
copy to lib-python/modified-2.7/test/test_subprocess.py
--- a/lib-python/2.7/test/test_subprocess.py
+++ b/lib-python/modified-2.7/test/test_subprocess.py
@@ -16,11 +16,11 @@
# Depends on the following external programs: Python
#
-if mswindows:
- SETBINARY = ('import msvcrt; msvcrt.setmode(sys.stdout.fileno(), '
- 'os.O_BINARY);')
-else:
- SETBINARY = ''
+#if mswindows:
+# SETBINARY = ('import msvcrt; msvcrt.setmode(sys.stdout.fileno(), '
+# 'os.O_BINARY);')
+#else:
+# SETBINARY = ''
try:
@@ -420,8 +420,9 @@
self.assertStderrEqual(stderr, "")
def test_universal_newlines(self):
- p = subprocess.Popen([sys.executable, "-c",
- 'import sys,os;' + SETBINARY +
+ # NB. replaced SETBINARY with the -u flag
+ p = subprocess.Popen([sys.executable, "-u", "-c",
+ 'import sys,os;' + #SETBINARY +
'sys.stdout.write("line1\\n");'
'sys.stdout.flush();'
'sys.stdout.write("line2\\r");'
@@ -448,8 +449,9 @@
def test_universal_newlines_communicate(self):
# universal newlines through communicate()
- p = subprocess.Popen([sys.executable, "-c",
- 'import sys,os;' + SETBINARY +
+ # NB. replaced SETBINARY with the -u flag
+ p = subprocess.Popen([sys.executable, "-u", "-c",
+ 'import sys,os;' + #SETBINARY +
'sys.stdout.write("line1\\n");'
'sys.stdout.flush();'
'sys.stdout.write("line2\\r");'
diff --git a/lib_pypy/_ctypes/pointer.py b/lib_pypy/_ctypes/pointer.py
--- a/lib_pypy/_ctypes/pointer.py
+++ b/lib_pypy/_ctypes/pointer.py
@@ -124,7 +124,8 @@
# for now, we always allow types.pointer, else a lot of tests
# break. We need to rethink how pointers are represented, though
if my_ffitype is not ffitype and ffitype is not _ffi.types.void_p:
- raise ArgumentError, "expected %s instance, got %s" % (type(value), ffitype)
+ raise ArgumentError("expected %s instance, got %s" % (type(value),
+ ffitype))
return value._get_buffer_value()
def _cast_addr(obj, _, tp):
diff --git a/lib_pypy/_ctypes/structure.py b/lib_pypy/_ctypes/structure.py
--- a/lib_pypy/_ctypes/structure.py
+++ b/lib_pypy/_ctypes/structure.py
@@ -17,7 +17,7 @@
if len(f) == 3:
if (not hasattr(tp, '_type_')
or not isinstance(tp._type_, str)
- or tp._type_ not in "iIhHbBlL"):
+ or tp._type_ not in "iIhHbBlLqQ"):
#XXX: are those all types?
# we just dont get the type name
# in the interp levle thrown TypeError
diff --git a/lib_pypy/_sqlite3.py b/lib_pypy/_sqlite3.py
--- a/lib_pypy/_sqlite3.py
+++ b/lib_pypy/_sqlite3.py
@@ -231,6 +231,9 @@
sqlite.sqlite3_result_text.argtypes = [c_void_p, c_char_p, c_int, c_void_p]
sqlite.sqlite3_result_text.restype = None
+sqlite.sqlite3_enable_load_extension.argtypes = [c_void_p, c_int]
+sqlite.sqlite3_enable_load_extension.restype = c_int
+
##########################################
# END Wrapped SQLite C API and constants
##########################################
@@ -705,6 +708,14 @@
from sqlite3.dump import _iterdump
return _iterdump(self)
+ def enable_load_extension(self, enabled):
+ self._check_thread()
+ self._check_closed()
+
+ rc = sqlite.sqlite3_enable_load_extension(self.db, int(enabled))
+ if rc != SQLITE_OK:
+ raise OperationalError("Error enabling load extension")
+
DML, DQL, DDL = range(3)
class Cursor(object):
diff --git a/lib_pypy/pyrepl/commands.py b/lib_pypy/pyrepl/commands.py
--- a/lib_pypy/pyrepl/commands.py
+++ b/lib_pypy/pyrepl/commands.py
@@ -33,10 +33,9 @@
class Command(object):
finish = 0
kills_digit_arg = 1
- def __init__(self, reader, (event_name, event)):
+ def __init__(self, reader, cmd):
self.reader = reader
- self.event = event
- self.event_name = event_name
+ self.event_name, self.event = cmd
def do(self):
pass
diff --git a/lib_pypy/pyrepl/pygame_console.py b/lib_pypy/pyrepl/pygame_console.py
--- a/lib_pypy/pyrepl/pygame_console.py
+++ b/lib_pypy/pyrepl/pygame_console.py
@@ -130,7 +130,7 @@
s.fill(c, [0, 600 - bmargin, 800, bmargin])
s.fill(c, [800 - rmargin, 0, lmargin, 600])
- def refresh(self, screen, (cx, cy)):
+ def refresh(self, screen, cxy):
self.screen = screen
self.pygame_screen.fill(colors.bg,
[0, tmargin + self.cur_top + self.scroll,
@@ -139,8 +139,8 @@
line_top = self.cur_top
width, height = self.fontsize
- self.cxy = (cx, cy)
- cp = self.char_pos(cx, cy)
+ self.cxy = cxy
+ cp = self.char_pos(*cxy)
if cp[1] < tmargin:
self.scroll = - (cy*self.fh + self.cur_top)
self.repaint()
@@ -148,7 +148,7 @@
self.scroll += (600 - bmargin) - (cp[1] + self.fh)
self.repaint()
if self.curs_vis:
- self.pygame_screen.blit(self.cursor, self.char_pos(cx, cy))
+ self.pygame_screen.blit(self.cursor, self.char_pos(*cxy))
for line in screen:
if 0 <= line_top + self.scroll <= (600 - bmargin - tmargin - self.fh):
if line:
diff --git a/lib_pypy/pyrepl/unix_console.py b/lib_pypy/pyrepl/unix_console.py
--- a/lib_pypy/pyrepl/unix_console.py
+++ b/lib_pypy/pyrepl/unix_console.py
@@ -163,7 +163,7 @@
def change_encoding(self, encoding):
self.encoding = encoding
- def refresh(self, screen, (cx, cy)):
+ def refresh(self, screen, cxy):
# this function is still too long (over 90 lines)
if not self.__gone_tall:
@@ -198,6 +198,7 @@
# we make sure the cursor is on the screen, and that we're
# using all of the screen if we can
+ cx, cy = cxy
if cy < offset:
offset = cy
elif cy >= offset + height:
@@ -411,7 +412,12 @@
e.args[4] == 'unexpected end of data':
pass
else:
- raise
+ # was: "raise". But it crashes pyrepl, and by extension the
+ # pypy currently running, in which we are e.g. in the middle
+ # of some debugging session. Argh. Instead just print an
+ # error message to stderr and continue running, for now.
+ self.partial_char = ''
+ sys.stderr.write('\n%s: %s\n' % (e.__class__.__name__, e))
else:
self.partial_char = ''
self.event_queue.push(c)
diff --git a/lib_pypy/syslog.py b/lib_pypy/syslog.py
--- a/lib_pypy/syslog.py
+++ b/lib_pypy/syslog.py
@@ -38,9 +38,27 @@
_setlogmask.argtypes = (c_int,)
_setlogmask.restype = c_int
+_S_log_open = False
+_S_ident_o = None
+
+def _get_argv():
+ try:
+ import sys
+ script = sys.argv[0]
+ if isinstance(script, str):
+ return script[script.rfind('/')+1:] or None
+ except Exception:
+ pass
+ return None
+
@builtinify
-def openlog(ident, option, facility):
- _openlog(ident, option, facility)
+def openlog(ident=None, logoption=0, facility=LOG_USER):
+ global _S_ident_o, _S_log_open
+ if ident is None:
+ ident = _get_argv()
+ _S_ident_o = c_char_p(ident) # keepalive
+ _openlog(_S_ident_o, logoption, facility)
+ _S_log_open = True
@builtinify
def syslog(arg1, arg2=None):
@@ -48,11 +66,18 @@
priority, message = arg1, arg2
else:
priority, message = LOG_INFO, arg1
+ # if log is not opened, open it now
+ if not _S_log_open:
+ openlog()
_syslog(priority, "%s", message)
@builtinify
def closelog():
- _closelog()
+ global _S_log_open, S_ident_o
+ if _S_log_open:
+ _closelog()
+ _S_log_open = False
+ _S_ident_o = None
@builtinify
def setlogmask(mask):
diff --git a/py/_code/code.py b/py/_code/code.py
--- a/py/_code/code.py
+++ b/py/_code/code.py
@@ -307,7 +307,7 @@
self._striptext = 'AssertionError: '
self._excinfo = tup
self.type, self.value, tb = self._excinfo
- self.typename = self.type.__name__
+ self.typename = getattr(self.type, "__name__", "???")
self.traceback = py.code.Traceback(tb)
def __repr__(self):
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -281,6 +281,9 @@
"actually create the full list until the resulting "
"list is mutated",
default=False),
+ BoolOption("withliststrategies",
+ "enable optimized ways to store lists of primitives ",
+ default=True),
BoolOption("withtypeversion",
"version type objects when changing them",
diff --git a/pypy/config/test/test_translationoption.py b/pypy/config/test/test_translationoption.py
new file mode 100644
--- /dev/null
+++ b/pypy/config/test/test_translationoption.py
@@ -0,0 +1,10 @@
+import py
+from pypy.config.translationoption import get_combined_translation_config
+from pypy.config.translationoption import set_opt_level
+from pypy.config.config import ConflictConfigError
+
+
+def test_no_gcrootfinder_with_boehm():
+ config = get_combined_translation_config()
+ config.translation.gcrootfinder = "shadowstack"
+ py.test.raises(ConflictConfigError, set_opt_level, config, '0')
diff --git a/pypy/config/translationoption.py b/pypy/config/translationoption.py
--- a/pypy/config/translationoption.py
+++ b/pypy/config/translationoption.py
@@ -69,8 +69,8 @@
"statistics": [("translation.gctransformer", "framework")],
"generation": [("translation.gctransformer", "framework")],
"hybrid": [("translation.gctransformer", "framework")],
- "boehm": [("translation.gctransformer", "boehm"),
- ("translation.continuation", False)], # breaks
+ "boehm": [("translation.continuation", False), # breaks
+ ("translation.gctransformer", "boehm")],
"markcompact": [("translation.gctransformer", "framework")],
"minimark": [("translation.gctransformer", "framework")],
},
@@ -400,6 +400,10 @@
# make_sure_not_resized often relies on it, so we always enable them
config.translation.suggest(list_comprehension_operations=True)
+ # finally, make the choice of the gc definitive. This will fail
+ # if we have specified strange inconsistent settings.
+ config.translation.gc = config.translation.gc
+
# ----------------------------------------------------------------
def set_platform(config):
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
@@ -270,7 +270,12 @@
- *slicing*:
the slice start must be within bounds. The stop doesn't need to, but it must
not be smaller than the start. All negative indexes are disallowed, except for
- the [:-1] special case. No step.
+ the [:-1] special case. No step. Slice deletion follows the same rules.
+
+ - *slice assignment*:
+ only supports ``lst[x:y] = sublist``, if ``len(sublist) == y - x``.
+ In other words, slice assignment cannot change the total length of the list,
+ but just replace items.
- *other operators*:
``+``, ``+=``, ``in``, ``*``, ``*=``, ``==``, ``!=`` work as expected.
diff --git a/pypy/doc/config/objspace.std.withliststrategies.txt b/pypy/doc/config/objspace.std.withliststrategies.txt
new file mode 100644
--- /dev/null
+++ b/pypy/doc/config/objspace.std.withliststrategies.txt
@@ -0,0 +1,2 @@
+Enable list strategies: Use specialized representations for lists of primitive
+objects, such as ints.
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
@@ -262,6 +262,26 @@
documented as such (as e.g. for hasattr()), in most cases PyPy
lets the exception propagate instead.
+Object Identity of Primitive Values, ``is`` and ``id``
+-------------------------------------------------------
+
+Object identity of primitive values works by value equality, not by identity of
+the wrapper. This means that ``x + 1 is x + 1`` is always true, for arbitrary
+integers ``x``. The rule applies for the following types:
+
+ - ``int``
+
+ - ``float``
+
+ - ``long``
+
+ - ``complex``
+
+This change requires some changes to ``id`` as well. ``id`` fulfills the
+following condition: ``x is y <=> id(x) == id(y)``. Therefore ``id`` of the
+above types will return a value that is computed from the argument, and can
+thus be larger than ``sys.maxint`` (i.e. it can be an arbitrary long).
+
Miscellaneous
-------------
@@ -284,14 +304,5 @@
never a dictionary as it sometimes is in CPython. Assigning to
``__builtins__`` has no effect.
-* Do not compare immutable objects with ``is``. For example on CPython
- it is true that ``x is 0`` works, i.e. does the same as ``type(x) is
- int and x == 0``, but it is so by accident. If you do instead
- ``x is 1000``, then it stops working, because 1000 is too large and
- doesn't come from the internal cache. In PyPy it fails to work in
- both cases, because we have no need for a cache at all.
-
-* Also, object identity of immutable keys in dictionaries is not necessarily
- preserved.
.. include:: _ref.txt
diff --git a/pypy/doc/how-to-release.rst b/pypy/doc/how-to-release.rst
--- a/pypy/doc/how-to-release.rst
+++ b/pypy/doc/how-to-release.rst
@@ -1,6 +1,3 @@
-.. include:: needswork.txt
-
-.. needs work, it talks about svn. also, it is not really user documentation
Making a PyPy Release
=======================
@@ -12,11 +9,8 @@
forgetting things. A set of todo files may also work.
Check and prioritize all issues for the release, postpone some if necessary,
-create new issues also as necessary. A meeting (or meetings) should be
-organized to decide what things are priorities, should go in and work for
-the release.
-
-An important thing is to get the documentation into an up-to-date state!
+create new issues also as necessary. An important thing is to get
+the documentation into an up-to-date state!
Release Steps
----------------
diff --git a/pypy/doc/project-ideas.rst b/pypy/doc/project-ideas.rst
--- a/pypy/doc/project-ideas.rst
+++ b/pypy/doc/project-ideas.rst
@@ -23,17 +23,20 @@
PyPy's implementation of the Python ``long`` type is slower than CPython's.
Find out why and optimize them.
+Make bytearray type fast
+------------------------
+
+PyPy's bytearray type is very inefficient. It would be an interesting
+task to look into possible optimizations on this.
+
Numpy improvements
------------------
-This is more of a project-container than a single project. Possible ideas:
+The numpy is rapidly progressing in pypy, so feel free to come to IRC and
+ask for proposed topic. A not necesarilly up-to-date `list of topics`_
+is also available.
-* experiment with auto-vectorization using SSE or implement vectorization
- without automatically detecting it for array operations.
-
-* improve numpy, for example implement memory views.
-
-* interface with fortran/C libraries.
+.. _`list of topics`: https://bitbucket.org/pypy/extradoc/src/extradoc/planning/micronumpy.txt
Improving the jitviewer
------------------------
diff --git a/pypy/doc/release-1.7.0.rst b/pypy/doc/release-1.7.0.rst
new file mode 100644
--- /dev/null
+++ b/pypy/doc/release-1.7.0.rst
@@ -0,0 +1,94 @@
+==================================
+PyPy 1.7 - widening the sweet spot
+==================================
+
+We're pleased to announce the 1.7 release of PyPy. As became a habit, this
+release brings a lot of bugfixes and performance improvements over the 1.6
+release. However, unlike the previous releases, the focus has been on widening
+the "sweet spot" of PyPy. That is, classes of Python code that PyPy can greatly
+speed up should be vastly improved with this release. You can download the 1.7
+release here:
+
+ http://pypy.org/download.html
+
+What is PyPy?
+=============
+
+PyPy is a very compliant Python interpreter, almost a drop-in replacement for
+CPython 2.7. It's fast (`pypy 1.7 and cpython 2.7.1`_ performance comparison)
+due to its integrated tracing JIT compiler.
+
+This release supports x86 machines running Linux 32/64, Mac OS X 32/64 or
+Windows 32. Windows 64 work is ongoing, but not yet natively supported.
+
+The main topic of this release is widening the range of code which PyPy
+can greatly speed up. On average on
+our benchmark suite, PyPy 1.7 is around **30%** faster than PyPy 1.6 and up
+to **20 times** faster on some benchmarks.
+
+.. _`pypy 1.7 and cpython 2.7.1`: http://speed.pypy.org
+
+
+Highlights
+==========
+
+* Numerous performance improvements. There are too many examples which python
+ constructs now should behave faster to list them.
+
+* Bugfixes and compatibility fixes with CPython.
+
+* Windows fixes.
+
+* PyPy now comes with stackless features enabled by default. However,
+ any loop using stackless features will interrupt the JIT for now, so no real
+ performance improvement for stackless-based programs. Contact pypy-dev for
+ info how to help on removing this restriction.
+
+* NumPy effort in PyPy was renamed numpypy. In order to try using it, simply
+ write::
+
+ import numpypy as numpy
+
+ at the beginning of your program. There is a huge progress on numpy in PyPy
+ since 1.6, the main feature being implementation of dtypes.
+
+* JSON encoder (but not decoder) has been replaced with a new one. This one
+ is written in pure Python, but is known to outperform CPython's C extension
+ up to **2 times** in some cases. It's about **20 times** faster than
+ the one that we had in 1.6.
+
+* The memory footprint of some of our RPython modules has been drastically
+ improved. This should impact any applications using for example cryptography,
+ like tornado.
+
+* There was some progress in exposing even more CPython C API via cpyext.
+
+Things that didn't make it, expect in 1.8 soon
+==============================================
+
+There is an ongoing work, which while didn't make it to the release, is
+probably worth mentioning here. This is what you should probably expect in
+1.8 some time soon:
+
+* Specialized list implementation. There is a branch that implements lists of
+ integers/floats/strings as compactly as array.array. This should drastically
+ improve performance/memory impact of some applications
+
+* NumPy effort is progressing forward, with multi-dimensional arrays coming
+ soon.
+
+* There are two brand new JIT assembler backends, notably for the PowerPC and
+ ARM processors.
+
+Fundraising
+===========
+
+It's maybe worth mentioning that we're running fundraising campaigns for
+NumPy effort in PyPy and for Python 3 in PyPy. In case you want to see any
+of those happen faster, we urge you to donate to `numpy proposal`_ or
+`py3k proposal`_. In case you want PyPy to progress, but you trust us with
+the general direction, you can always donate to the `general pot`_.
+
+.. _`numpy proposal`: http://pypy.org/numpydonate.html
+.. _`py3k proposal`: http://pypy.org/py3donate.html
+.. _`general pot`: http://pypy.org
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -188,6 +188,12 @@
# -------------------------------------------------------------------
+ def is_w(self, space, w_other):
+ return self is w_other
+
+ def unique_id(self, space):
+ return space.wrap(compute_unique_id(self))
+
def str_w(self, space):
w_msg = typed_unwrap_error_msg(space, "string", self)
raise OperationError(space.w_TypeError, w_msg)
@@ -681,9 +687,17 @@
"""shortcut for space.is_true(space.eq(w_obj1, w_obj2))"""
return self.is_w(w_obj1, w_obj2) or self.is_true(self.eq(w_obj1, w_obj2))
- def is_w(self, w_obj1, w_obj2):
- """shortcut for space.is_true(space.is_(w_obj1, w_obj2))"""
- return self.is_true(self.is_(w_obj1, w_obj2))
+ def is_(self, w_one, w_two):
+ return self.newbool(self.is_w(w_one, w_two))
+
+ def is_w(self, w_one, w_two):
+ # done by a method call on w_two (and not on w_one, because of the
+ # expected programming style where we say "if x is None" or
+ # "if x is object").
+ return w_two.is_w(self, w_one)
+
+ def id(self, w_obj):
+ return w_obj.unique_id(self)
def hash_w(self, w_obj):
"""shortcut for space.int_w(space.hash(w_obj))"""
@@ -879,6 +893,16 @@
"""
return self.unpackiterable(w_iterable, expected_length)
+ def listview_str(self, w_list):
+ """ Return a list of unwrapped strings out of a list of strings. If the
+ argument is not a list or does not contain only strings, return None.
+ May return None anyway.
+ """
+ return None
+
+ def newlist_str(self, list_s):
+ return self.newlist([self.wrap(s) for s in list_s])
+
@jit.unroll_safe
def exception_match(self, w_exc_type, w_check_class):
"""Checks if the given exception type matches 'w_check_class'."""
@@ -1013,9 +1037,6 @@
def isinstance_w(self, w_obj, w_type):
return self.is_true(self.isinstance(w_obj, w_type))
- def id(self, w_obj):
- return self.wrap(compute_unique_id(w_obj))
-
# The code below only works
# for the simple case (new-style instance).
# These methods are patched with the full logic by the __builtin__
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -1,8 +1,9 @@
+from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.error import OperationError
-from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.gateway import NoneNotWrapped
+from pypy.interpreter.pyopcode import LoopBlock
from pypy.rlib import jit
-from pypy.interpreter.pyopcode import LoopBlock
+from pypy.rlib.objectmodel import specialize
class GeneratorIterator(Wrappable):
@@ -156,38 +157,43 @@
break
block = block.previous
- def unpack_into(self, results_w):
- """This is a hack for performance: runs the generator and collects
- all produced items in a list."""
- # XXX copied and simplified version of send_ex()
- space = self.space
- if self.running:
- raise OperationError(space.w_ValueError,
- space.wrap('generator already executing'))
- frame = self.frame
- if frame is None: # already finished
- return
- self.running = True
- try:
- pycode = self.pycode
- while True:
- jitdriver.jit_merge_point(self=self, frame=frame,
- results_w=results_w,
- pycode=pycode)
- try:
- w_result = frame.execute_frame(space.w_None)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- break
- # if the frame is now marked as finished, it was RETURNed from
- if frame.frame_finished_execution:
- break
- results_w.append(w_result) # YIELDed
- finally:
- frame.f_backref = jit.vref_None
- self.running = False
- self.frame = None
-
-jitdriver = jit.JitDriver(greens=['pycode'],
- reds=['self', 'frame', 'results_w'])
+ # Results can be either an RPython list of W_Root, or it can be an
+ # app-level W_ListObject, which also has an append() method, that's why we
+ # generate 2 versions of the function and 2 jit drivers.
+ def _create_unpack_into():
+ jitdriver = jit.JitDriver(greens=['pycode'],
+ reds=['self', 'frame', 'results'])
+ def unpack_into(self, results):
+ """This is a hack for performance: runs the generator and collects
+ all produced items in a list."""
+ # XXX copied and simplified version of send_ex()
+ space = self.space
+ if self.running:
+ raise OperationError(space.w_ValueError,
+ space.wrap('generator already executing'))
+ frame = self.frame
+ if frame is None: # already finished
+ return
+ self.running = True
+ try:
+ pycode = self.pycode
+ while True:
+ jitdriver.jit_merge_point(self=self, frame=frame,
+ results=results, pycode=pycode)
+ try:
+ w_result = frame.execute_frame(space.w_None)
+ except OperationError, e:
+ if not e.match(space, space.w_StopIteration):
+ raise
+ break
+ # if the frame is now marked as finished, it was RETURNed from
+ if frame.frame_finished_execution:
+ break
+ results.append(w_result) # YIELDed
+ finally:
+ frame.f_backref = jit.vref_None
+ self.running = False
+ self.frame = None
+ return unpack_into
+ unpack_into = _create_unpack_into()
+ unpack_into_w = _create_unpack_into()
\ No newline at end of file
diff --git a/pypy/interpreter/test/test_executioncontext.py b/pypy/interpreter/test/test_executioncontext.py
--- a/pypy/interpreter/test/test_executioncontext.py
+++ b/pypy/interpreter/test/test_executioncontext.py
@@ -292,7 +292,7 @@
import os, sys
print sys.executable, self.tmpfile
if sys.platform == "win32":
- cmdformat = '""%s" "%s""' # excellent! tons of "!
+ cmdformat = '"%s" "%s"'
else:
cmdformat = "'%s' '%s'"
g = os.popen(cmdformat % (sys.executable, self.tmpfile), 'r')
diff --git a/pypy/interpreter/test/test_function.py b/pypy/interpreter/test/test_function.py
--- a/pypy/interpreter/test/test_function.py
+++ b/pypy/interpreter/test/test_function.py
@@ -587,7 +587,7 @@
assert isinstance(meth2, Method)
assert meth2.call_args(args) == obj1
# Check method returned from unbound_method.__get__()
- w_meth3 = descr_function_get(space, func, None, space.type(obj2))
+ w_meth3 = descr_function_get(space, func, space.w_None, space.type(obj2))
meth3 = space.unwrap(w_meth3)
w_meth4 = meth3.descr_method_get(obj2, space.w_None)
meth4 = space.unwrap(w_meth4)
diff --git a/pypy/interpreter/test/test_objspace.py b/pypy/interpreter/test/test_objspace.py
--- a/pypy/interpreter/test/test_objspace.py
+++ b/pypy/interpreter/test/test_objspace.py
@@ -63,10 +63,13 @@
def test_unpackiterable(self):
space = self.space
w = space.wrap
- l = [w(1), w(2), w(3), w(4)]
+ l = [space.newlist([]) for l in range(4)]
w_l = space.newlist(l)
- assert space.unpackiterable(w_l) == l
- assert space.unpackiterable(w_l, 4) == l
+ l1 = space.unpackiterable(w_l)
+ l2 = space.unpackiterable(w_l, 4)
+ for i in range(4):
+ assert space.is_w(l1[i], l[i])
+ assert space.is_w(l2[i], l[i])
err = raises(OperationError, space.unpackiterable, w_l, 3)
assert err.value.match(space, space.w_ValueError)
err = raises(OperationError, space.unpackiterable, w_l, 5)
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
@@ -2,7 +2,7 @@
from pypy.interpreter import typedef
from pypy.tool.udir import udir
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.gateway import ObjSpace
+from pypy.interpreter.gateway import ObjSpace, interp2app
# this test isn't so much to test that the objspace interface *works*
# -- it's more to test that it's *there*
@@ -260,6 +260,50 @@
gc.collect(); gc.collect()
assert space.unwrap(w_seen) == [6, 2]
+ def test_multiple_inheritance(self):
+ class W_A(Wrappable):
+ a = 1
+ b = 2
+ class W_C(W_A):
+ b = 3
+ W_A.typedef = typedef.TypeDef("A",
+ a = typedef.interp_attrproperty("a", cls=W_A),
+ b = typedef.interp_attrproperty("b", cls=W_A),
+ )
+ class W_B(Wrappable):
+ pass
+ def standalone_method(space, w_obj):
+ if isinstance(w_obj, W_A):
+ return space.w_True
+ else:
+ return space.w_False
+ W_B.typedef = typedef.TypeDef("B",
+ c = interp2app(standalone_method)
+ )
+ W_C.typedef = typedef.TypeDef("C", (W_A.typedef, W_B.typedef,))
+
+ w_o1 = self.space.wrap(W_C())
+ w_o2 = self.space.wrap(W_B())
+ w_c = self.space.gettypefor(W_C)
+ w_b = self.space.gettypefor(W_B)
+ w_a = self.space.gettypefor(W_A)
+ assert w_c.mro_w == [
+ w_c,
+ w_a,
+ w_b,
+ self.space.w_object,
+ ]
+ for w_tp in w_c.mro_w:
+ assert self.space.isinstance_w(w_o1, w_tp)
+ def assert_attr(w_obj, name, value):
+ assert self.space.unwrap(self.space.getattr(w_obj, self.space.wrap(name))) == value
+ def assert_method(w_obj, name, value):
+ assert self.space.unwrap(self.space.call_method(w_obj, name)) == value
+ assert_attr(w_o1, "a", 1)
+ assert_attr(w_o1, "b", 3)
+ assert_method(w_o1, "c", True)
+ assert_method(w_o2, "c", False)
+
class AppTestTypeDef:
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -15,13 +15,19 @@
def __init__(self, __name, __base=None, **rawdict):
"NOT_RPYTHON: initialization-time only"
self.name = __name
- self.base = __base
+ if __base is None:
+ bases = []
+ elif isinstance(__base, tuple):
+ bases = list(__base)
+ else:
+ bases = [__base]
+ self.bases = bases
self.hasdict = '__dict__' in rawdict
self.weakrefable = '__weakref__' in rawdict
self.doc = rawdict.pop('__doc__', None)
- if __base is not None:
- self.hasdict |= __base.hasdict
- self.weakrefable |= __base.weakrefable
+ for base in bases:
+ self.hasdict |= base.hasdict
+ self.weakrefable |= base.weakrefable
self.rawdict = {}
self.acceptable_as_base_class = '__new__' in rawdict
self.applevel_subclasses_base = None
diff --git a/pypy/jit/backend/conftest.py b/pypy/jit/backend/conftest.py
--- a/pypy/jit/backend/conftest.py
+++ b/pypy/jit/backend/conftest.py
@@ -12,7 +12,7 @@
help="choose a fixed random seed")
group.addoption('--backend', action="store",
default='llgraph',
- choices=['llgraph', 'x86'],
+ choices=['llgraph', 'cpu'],
dest="backend",
help="select the backend to run the functions with")
group.addoption('--block-length', action="store", type="int",
diff --git a/pypy/jit/backend/llgraph/llimpl.py b/pypy/jit/backend/llgraph/llimpl.py
--- a/pypy/jit/backend/llgraph/llimpl.py
+++ b/pypy/jit/backend/llgraph/llimpl.py
@@ -20,6 +20,7 @@
from pypy.jit.backend.llgraph import symbolic
from pypy.jit.codewriter import longlong
+from pypy.rlib import libffi
from pypy.rlib.objectmodel import ComputedIntSymbolic, we_are_translated
from pypy.rlib.rarithmetic import ovfcheck
from pypy.rlib.rarithmetic import r_longlong, r_ulonglong, r_uint
@@ -325,12 +326,12 @@
loop = _from_opaque(loop)
loop.operations.append(Operation(opnum))
-def compile_add_descr(loop, ofs, type, arg_types):
+def compile_add_descr(loop, ofs, type, arg_types, extrainfo, width):
from pypy.jit.backend.llgraph.runner import Descr
loop = _from_opaque(loop)
op = loop.operations[-1]
assert isinstance(type, str) and len(type) == 1
- op.descr = Descr(ofs, type, arg_types=arg_types)
+ op.descr = Descr(ofs, type, arg_types=arg_types, extrainfo=extrainfo, width=width)
def compile_add_descr_arg(loop, ofs, type, arg_types):
from pypy.jit.backend.llgraph.runner import Descr
@@ -825,6 +826,16 @@
else:
raise NotImplementedError
+ def op_getinteriorfield_raw(self, descr, array, index):
+ if descr.typeinfo == REF:
+ return do_getinteriorfield_raw_ptr(array, index, descr.width, descr.ofs)
+ elif descr.typeinfo == INT:
+ return do_getinteriorfield_raw_int(array, index, descr.width, descr.ofs)
+ elif descr.typeinfo == FLOAT:
+ return do_getinteriorfield_raw_float(array, index, descr.width, descr.ofs)
+ else:
+ raise NotImplementedError
+
def op_setinteriorfield_gc(self, descr, array, index, newvalue):
if descr.typeinfo == REF:
return do_setinteriorfield_gc_ptr(array, index, descr.ofs,
@@ -838,6 +849,16 @@
else:
raise NotImplementedError
+ def op_setinteriorfield_raw(self, descr, array, index, newvalue):
+ if descr.typeinfo == REF:
+ return do_setinteriorfield_raw_ptr(array, index, newvalue, descr.width, descr.ofs)
+ elif descr.typeinfo == INT:
+ return do_setinteriorfield_raw_int(array, index, newvalue, descr.width, descr.ofs)
+ elif descr.typeinfo == FLOAT:
+ return do_setinteriorfield_raw_float(array, index, newvalue, descr.width, descr.ofs)
+ else:
+ raise NotImplementedError
+
def op_setfield_gc(self, fielddescr, struct, newvalue):
if fielddescr.typeinfo == REF:
do_setfield_gc_ptr(struct, fielddescr.ofs, newvalue)
@@ -1403,6 +1424,14 @@
struct = array._obj.container.getitem(index)
return cast_to_ptr(_getinteriorfield_gc(struct, fieldnum))
+def _getinteriorfield_raw(ffitype, array, index, width, ofs):
+ addr = rffi.cast(rffi.VOIDP, array)
+ return libffi.array_getitem(ffitype, width, addr, index, ofs)
+
+def do_getinteriorfield_raw_int(array, index, width, ofs):
+ res = _getinteriorfield_raw(libffi.types.slong, array, index, width, ofs)
+ return res
+
def _getfield_raw(struct, fieldnum):
STRUCT, fieldname = symbolic.TokenToField[fieldnum]
ptr = cast_from_int(lltype.Ptr(STRUCT), struct)
@@ -1479,7 +1508,14 @@
return do_setinteriorfield_gc
do_setinteriorfield_gc_int = new_setinteriorfield_gc(cast_from_int)
do_setinteriorfield_gc_float = new_setinteriorfield_gc(cast_from_floatstorage)
-do_setinteriorfield_gc_ptr = new_setinteriorfield_gc(cast_from_ptr)
+do_setinteriorfield_gc_ptr = new_setinteriorfield_gc(cast_from_ptr)
+
+def new_setinteriorfield_raw(ffitype):
+ def do_setinteriorfield_raw(array, index, newvalue, width, ofs):
+ addr = rffi.cast(rffi.VOIDP, array)
+ return libffi.array_setitem(ffitype, width, addr, index, ofs, newvalue)
+ return do_setinteriorfield_raw
+do_setinteriorfield_raw_int = new_setinteriorfield_raw(libffi.types.slong)
def do_setfield_raw_int(struct, fieldnum, newvalue):
STRUCT, fieldname = symbolic.TokenToField[fieldnum]
diff --git a/pypy/jit/backend/llgraph/runner.py b/pypy/jit/backend/llgraph/runner.py
--- a/pypy/jit/backend/llgraph/runner.py
+++ b/pypy/jit/backend/llgraph/runner.py
@@ -23,8 +23,10 @@
class Descr(history.AbstractDescr):
def __init__(self, ofs, typeinfo, extrainfo=None, name=None,
- arg_types=None, count_fields_if_immut=-1, ffi_flags=0):
+ arg_types=None, count_fields_if_immut=-1, ffi_flags=0, width=-1):
+
self.ofs = ofs
+ self.width = width
self.typeinfo = typeinfo
self.extrainfo = extrainfo
self.name = name
@@ -119,14 +121,14 @@
return False
def getdescr(self, ofs, typeinfo='?', extrainfo=None, name=None,
- arg_types=None, count_fields_if_immut=-1, ffi_flags=0):
+ arg_types=None, count_fields_if_immut=-1, ffi_flags=0, width=-1):
key = (ofs, typeinfo, extrainfo, name, arg_types,
- count_fields_if_immut, ffi_flags)
+ count_fields_if_immut, ffi_flags, width)
try:
return self._descrs[key]
except KeyError:
descr = Descr(ofs, typeinfo, extrainfo, name, arg_types,
- count_fields_if_immut, ffi_flags)
+ count_fields_if_immut, ffi_flags, width)
self._descrs[key] = descr
return descr
@@ -179,7 +181,8 @@
descr = op.getdescr()
if isinstance(descr, Descr):
llimpl.compile_add_descr(c, descr.ofs, descr.typeinfo,
- descr.arg_types)
+ descr.arg_types, descr.extrainfo,
+ descr.width)
if (isinstance(descr, history.LoopToken) and
op.getopnum() != rop.JUMP):
llimpl.compile_add_loop_token(c, descr)
@@ -324,10 +327,22 @@
def interiorfielddescrof(self, A, fieldname):
S = A.OF
- ofs2 = symbolic.get_size(A)
+ width = symbolic.get_size(A)
ofs, size = symbolic.get_field_token(S, fieldname)
token = history.getkind(getattr(S, fieldname))
- return self.getdescr(ofs, token[0], name=fieldname, extrainfo=ofs2)
+ return self.getdescr(ofs, token[0], name=fieldname, width=width)
+
+ def interiorfielddescrof_dynamic(self, offset, width, fieldsize,
+ is_pointer, is_float, is_signed):
+
+ if is_pointer:
+ typeinfo = REF
+ elif is_float:
+ typeinfo = FLOAT
+ else:
+ typeinfo = INT
+ # we abuse the arg_types field to distinguish dynamic and static descrs
+ return Descr(offset, typeinfo, arg_types='dynamic', name='<dynamic interior field>', width=width)
def calldescrof(self, FUNC, ARGS, RESULT, extrainfo):
arg_types = []
diff --git a/pypy/jit/backend/llsupport/descr.py b/pypy/jit/backend/llsupport/descr.py
--- a/pypy/jit/backend/llsupport/descr.py
+++ b/pypy/jit/backend/llsupport/descr.py
@@ -111,6 +111,16 @@
def repr_of_descr(self):
return '<%s %s %s>' % (self._clsname, self.name, self.offset)
+class DynamicFieldDescr(BaseFieldDescr):
+ def __init__(self, offset, fieldsize, is_pointer, is_float, is_signed):
+ self.offset = offset
+ self._fieldsize = fieldsize
+ self._is_pointer_field = is_pointer
+ self._is_float_field = is_float
+ self._is_field_signed = is_signed
+
+ def get_field_size(self, translate_support_code):
+ return self._fieldsize
class NonGcPtrFieldDescr(BaseFieldDescr):
_clsname = 'NonGcPtrFieldDescr'
@@ -182,6 +192,7 @@
def repr_of_descr(self):
return '<%s>' % self._clsname
+
class NonGcPtrArrayDescr(BaseArrayDescr):
_clsname = 'NonGcPtrArrayDescr'
def get_item_size(self, translate_support_code):
@@ -211,6 +222,13 @@
def get_ofs_length(self, translate_support_code):
return -1
+class DynamicArrayNoLengthDescr(BaseArrayNoLengthDescr):
+ def __init__(self, itemsize):
+ self.itemsize = itemsize
+
+ def get_item_size(self, translate_support_code):
+ return self.itemsize
+
class NonGcPtrArrayNoLengthDescr(BaseArrayNoLengthDescr):
_clsname = 'NonGcPtrArrayNoLengthDescr'
def get_item_size(self, translate_support_code):
@@ -305,12 +323,16 @@
_clsname = ''
loop_token = None
arg_classes = '' # <-- annotation hack
- ffi_flags = 0
+ ffi_flags = 1
- def __init__(self, arg_classes, extrainfo=None, ffi_flags=0):
+ def __init__(self, arg_classes, extrainfo=None, ffi_flags=1):
self.arg_classes = arg_classes # string of "r" and "i" (ref/int)
self.extrainfo = extrainfo
self.ffi_flags = ffi_flags
+ # NB. the default ffi_flags is 1, meaning FUNCFLAG_CDECL, which
+ # makes sense on Windows as it's the one for all the C functions
+ # we are compiling together with the JIT. On non-Windows platforms
+ # it is just ignored anyway.
def __repr__(self):
res = '%s(%s)' % (self.__class__.__name__, self.arg_classes)
@@ -351,6 +373,10 @@
return False # unless overridden
def create_call_stub(self, rtyper, RESULT):
+ from pypy.rlib.clibffi import FFI_DEFAULT_ABI
+ assert self.get_call_conv() == FFI_DEFAULT_ABI, (
+ "%r: create_call_stub() with a non-default call ABI" % (self,))
+
def process(c):
if c == 'L':
assert longlong.supports_longlong
@@ -445,7 +471,7 @@
"""
_clsname = 'DynamicIntCallDescr'
- def __init__(self, arg_classes, result_size, result_sign, extrainfo=None, ffi_flags=0):
+ def __init__(self, arg_classes, result_size, result_sign, extrainfo, ffi_flags):
BaseIntCallDescr.__init__(self, arg_classes, extrainfo, ffi_flags)
assert isinstance(result_sign, bool)
self._result_size = chr(result_size)
diff --git a/pypy/jit/backend/llsupport/ffisupport.py b/pypy/jit/backend/llsupport/ffisupport.py
--- a/pypy/jit/backend/llsupport/ffisupport.py
+++ b/pypy/jit/backend/llsupport/ffisupport.py
@@ -8,7 +8,7 @@
class UnsupportedKind(Exception):
pass
-def get_call_descr_dynamic(cpu, ffi_args, ffi_result, extrainfo=None, ffi_flags=0):
+def get_call_descr_dynamic(cpu, ffi_args, ffi_result, extrainfo, ffi_flags):
"""Get a call descr: the types of result and args are represented by
rlib.libffi.types.*"""
try:
diff --git a/pypy/jit/backend/llsupport/gc.py b/pypy/jit/backend/llsupport/gc.py
--- a/pypy/jit/backend/llsupport/gc.py
+++ b/pypy/jit/backend/llsupport/gc.py
@@ -648,14 +648,10 @@
# make a malloc function, with two arguments
def malloc_basic(size, tid):
type_id = llop.extract_ushort(llgroup.HALFWORD, tid)
- has_finalizer = bool(tid & (1<<llgroup.HALFSHIFT))
- has_light_finalizer = bool(tid & (1<<(llgroup.HALFSHIFT + 1)))
check_typeid(type_id)
res = llop1.do_malloc_fixedsize_clear(llmemory.GCREF,
type_id, size,
- has_finalizer,
- has_light_finalizer,
- False)
+ False, False, False)
# In case the operation above failed, we are returning NULL
# from this function to assembler. There is also an RPython
# exception set, typically MemoryError; but it's easier and
@@ -749,11 +745,8 @@
def init_size_descr(self, S, descr):
type_id = self.layoutbuilder.get_type_id(S)
assert not self.layoutbuilder.is_weakref_type(S)
- has_finalizer = bool(self.layoutbuilder.has_finalizer(S))
- has_light_finalizer = bool(self.layoutbuilder.has_light_finalizer(S))
- flags = (int(has_finalizer) << llgroup.HALFSHIFT |
- int(has_light_finalizer) << (llgroup.HALFSHIFT + 1))
- descr.tid = llop.combine_ushort(lltype.Signed, type_id, flags)
+ assert not self.layoutbuilder.has_finalizer(S)
+ descr.tid = llop.combine_ushort(lltype.Signed, type_id, 0)
def init_array_descr(self, A, descr):
type_id = self.layoutbuilder.get_type_id(A)
diff --git a/pypy/jit/backend/llsupport/llmodel.py b/pypy/jit/backend/llsupport/llmodel.py
--- a/pypy/jit/backend/llsupport/llmodel.py
+++ b/pypy/jit/backend/llsupport/llmodel.py
@@ -9,9 +9,10 @@
from pypy.jit.backend.llsupport import symbolic
from pypy.jit.backend.llsupport.symbolic import WORD, unroll_basic_sizes
from pypy.jit.backend.llsupport.descr import (get_size_descr,
- get_field_descr, BaseFieldDescr, get_array_descr, BaseArrayDescr,
- get_call_descr, BaseIntCallDescr, GcPtrCallDescr, FloatCallDescr,
- VoidCallDescr, InteriorFieldDescr, get_interiorfield_descr)
+ get_field_descr, BaseFieldDescr, DynamicFieldDescr, get_array_descr,
+ BaseArrayDescr, DynamicArrayNoLengthDescr, get_call_descr,
+ BaseIntCallDescr, GcPtrCallDescr, FloatCallDescr, VoidCallDescr,
+ InteriorFieldDescr, get_interiorfield_descr)
from pypy.jit.backend.llsupport.asmmemmgr import AsmMemoryManager
@@ -238,6 +239,12 @@
def interiorfielddescrof(self, A, fieldname):
return get_interiorfield_descr(self.gc_ll_descr, A, A.OF, fieldname)
+ def interiorfielddescrof_dynamic(self, offset, width, fieldsize,
+ is_pointer, is_float, is_signed):
+ arraydescr = DynamicArrayNoLengthDescr(width)
+ fielddescr = DynamicFieldDescr(offset, fieldsize, is_pointer, is_float, is_signed)
+ return InteriorFieldDescr(arraydescr, fielddescr)
+
def unpack_arraydescr(self, arraydescr):
assert isinstance(arraydescr, BaseArrayDescr)
return arraydescr.get_base_size(self.translate_support_code)
diff --git a/pypy/jit/backend/llsupport/test/test_ffisupport.py b/pypy/jit/backend/llsupport/test/test_ffisupport.py
--- a/pypy/jit/backend/llsupport/test/test_ffisupport.py
+++ b/pypy/jit/backend/llsupport/test/test_ffisupport.py
@@ -13,44 +13,46 @@
def test_call_descr_dynamic():
args = [types.sint, types.pointer]
- descr = get_call_descr_dynamic(FakeCPU(), args, types.sint, ffi_flags=42)
+ descr = get_call_descr_dynamic(FakeCPU(), args, types.sint, None,
+ ffi_flags=42)
assert isinstance(descr, DynamicIntCallDescr)
assert descr.arg_classes == 'ii'
assert descr.get_ffi_flags() == 42
args = [types.sint, types.double, types.pointer]
- descr = get_call_descr_dynamic(FakeCPU(), args, types.void)
+ descr = get_call_descr_dynamic(FakeCPU(), args, types.void, None, 42)
assert descr is None # missing floats
descr = get_call_descr_dynamic(FakeCPU(supports_floats=True),
- args, types.void, ffi_flags=43)
+ args, types.void, None, ffi_flags=43)
assert isinstance(descr, VoidCallDescr)
assert descr.arg_classes == 'ifi'
assert descr.get_ffi_flags() == 43
- descr = get_call_descr_dynamic(FakeCPU(), [], types.sint8)
+ descr = get_call_descr_dynamic(FakeCPU(), [], types.sint8, None, 42)
assert isinstance(descr, DynamicIntCallDescr)
assert descr.get_result_size(False) == 1
assert descr.is_result_signed() == True
- descr = get_call_descr_dynamic(FakeCPU(), [], types.uint8)
+ descr = get_call_descr_dynamic(FakeCPU(), [], types.uint8, None, 42)
assert isinstance(descr, DynamicIntCallDescr)
assert descr.get_result_size(False) == 1
assert descr.is_result_signed() == False
if not is_64_bit:
- descr = get_call_descr_dynamic(FakeCPU(), [], types.slonglong)
+ descr = get_call_descr_dynamic(FakeCPU(), [], types.slonglong,
+ None, 42)
assert descr is None # missing longlongs
descr = get_call_descr_dynamic(FakeCPU(supports_longlong=True),
- [], types.slonglong, ffi_flags=43)
+ [], types.slonglong, None, ffi_flags=43)
assert isinstance(descr, LongLongCallDescr)
assert descr.get_ffi_flags() == 43
else:
assert types.slonglong is types.slong
- descr = get_call_descr_dynamic(FakeCPU(), [], types.float)
+ descr = get_call_descr_dynamic(FakeCPU(), [], types.float, None, 42)
assert descr is None # missing singlefloats
descr = get_call_descr_dynamic(FakeCPU(supports_singlefloats=True),
- [], types.float, ffi_flags=44)
+ [], types.float, None, ffi_flags=44)
SingleFloatCallDescr = getCallDescrClass(rffi.FLOAT)
assert isinstance(descr, SingleFloatCallDescr)
assert descr.get_ffi_flags() == 44
diff --git a/pypy/jit/backend/model.py b/pypy/jit/backend/model.py
--- a/pypy/jit/backend/model.py
+++ b/pypy/jit/backend/model.py
@@ -183,38 +183,35 @@
lst[n] = None
self.fail_descr_free_list.extend(faildescr_indices)
- @staticmethod
- def sizeof(S):
+ def sizeof(self, S):
raise NotImplementedError
- @staticmethod
- def fielddescrof(S, fieldname):
+ def fielddescrof(self, S, fieldname):
"""Return the Descr corresponding to field 'fieldname' on the
structure 'S'. It is important that this function (at least)
caches the results."""
raise NotImplementedError
- @staticmethod
- def arraydescrof(A):
+ def interiorfielddescrof(self, A, fieldname):
raise NotImplementedError
- @staticmethod
- def calldescrof(FUNC, ARGS, RESULT):
+ def interiorfielddescrof_dynamic(self, offset, width, fieldsize, is_pointer,
+ is_float, is_signed):
+ raise NotImplementedError
+
+ def arraydescrof(self, A):
+ raise NotImplementedError
+
+ def calldescrof(self, FUNC, ARGS, RESULT):
# FUNC is the original function type, but ARGS is a list of types
# with Voids removed
raise NotImplementedError
- @staticmethod
- def methdescrof(SELFTYPE, methname):
+ def methdescrof(self, SELFTYPE, methname):
# must return a subclass of history.AbstractMethDescr
raise NotImplementedError
- @staticmethod
- def typedescrof(TYPE):
- raise NotImplementedError
-
- @staticmethod
- def interiorfielddescrof(A, fieldname):
+ def typedescrof(self, TYPE):
raise NotImplementedError
# ---------- the backend-dependent operations ----------
diff --git a/pypy/jit/backend/test/test_random.py b/pypy/jit/backend/test/test_random.py
--- a/pypy/jit/backend/test/test_random.py
+++ b/pypy/jit/backend/test/test_random.py
@@ -495,9 +495,9 @@
if pytest.config.option.backend == 'llgraph':
from pypy.jit.backend.llgraph.runner import LLtypeCPU
return LLtypeCPU(None)
- elif pytest.config.option.backend == 'x86':
- from pypy.jit.backend.x86.runner import CPU386
- return CPU386(None, None)
+ elif pytest.config.option.backend == 'cpu':
+ from pypy.jit.backend.detect_cpu import getcpuclass
+ return getcpuclass()(None, None)
else:
assert 0, "unknown backend %r" % pytest.config.option.backend
diff --git a/pypy/jit/backend/x86/test/test_zll_random.py b/pypy/jit/backend/test/test_zll_stress.py
rename from pypy/jit/backend/x86/test/test_zll_random.py
rename to pypy/jit/backend/test/test_zll_stress.py
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
@@ -8,8 +8,8 @@
from pypy.rpython.lltypesystem.lloperation import llop
from pypy.rpython.annlowlevel import llhelper
from pypy.jit.backend.model import CompiledLoopToken
-from pypy.jit.backend.x86.regalloc import (RegAlloc, get_ebp_ofs,
- _get_scale, gpr_reg_mgr_cls)
+from pypy.jit.backend.x86.regalloc import (RegAlloc, get_ebp_ofs, _get_scale,
+ gpr_reg_mgr_cls, _valid_addressing_size)
from pypy.jit.backend.x86.arch import (FRAME_FIXED_SIZE, FORCE_INDEX_OFS, WORD,
IS_X86_32, IS_X86_64)
@@ -1601,8 +1601,10 @@
assert isinstance(itemsize_loc, ImmedLoc)
if isinstance(index_loc, ImmedLoc):
temp_loc = imm(index_loc.value * itemsize_loc.value)
+ elif _valid_addressing_size(itemsize_loc.value):
+ return AddressLoc(base_loc, index_loc, _get_scale(itemsize_loc.value), ofs_loc.value)
else:
- # XXX should not use IMUL in most cases
+ # XXX should not use IMUL in more cases, it can use a clever LEA
assert isinstance(temp_loc, RegLoc)
assert isinstance(index_loc, RegLoc)
assert not temp_loc.is_xmm
@@ -1619,6 +1621,8 @@
ofs_loc)
self.load_from_mem(resloc, src_addr, fieldsize_loc, sign_loc)
+ genop_getinteriorfield_raw = genop_getinteriorfield_gc
+
def genop_discard_setfield_gc(self, op, arglocs):
base_loc, ofs_loc, size_loc, value_loc = arglocs
@@ -1634,6 +1638,8 @@
ofs_loc)
self.save_into_mem(dest_addr, value_loc, fieldsize_loc)
+ genop_discard_setinteriorfield_raw = genop_discard_setinteriorfield_gc
+
def genop_discard_setarrayitem_gc(self, op, arglocs):
base_loc, ofs_loc, value_loc, size_loc, baseofs = arglocs
assert isinstance(baseofs, ImmedLoc)
diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py
--- a/pypy/jit/backend/x86/regalloc.py
+++ b/pypy/jit/backend/x86/regalloc.py
@@ -1067,6 +1067,8 @@
self.PerformDiscard(op, [base_loc, ofs, itemsize, fieldsize,
index_loc, temp_loc, value_loc])
+ consider_setinteriorfield_raw = consider_setinteriorfield_gc
+
def consider_strsetitem(self, op):
args = op.getarglist()
base_loc = self.rm.make_sure_var_in_reg(op.getarg(0), args)
@@ -1158,6 +1160,8 @@
self.Perform(op, [base_loc, ofs, itemsize, fieldsize,
index_loc, temp_loc, sign_loc], result_loc)
+ consider_getinteriorfield_raw = consider_getinteriorfield_gc
+
def consider_int_is_true(self, op, guard_op):
# doesn't need arg to be in a register
argloc = self.loc(op.getarg(0))
@@ -1430,8 +1434,11 @@
# i.e. the n'th word beyond the fixed frame size.
return -WORD * (FRAME_FIXED_SIZE + position)
+def _valid_addressing_size(size):
+ return size == 1 or size == 2 or size == 4 or size == 8
+
def _get_scale(size):
- assert size == 1 or size == 2 or size == 4 or size == 8
+ assert _valid_addressing_size(size)
if size < 4:
return size - 1 # 1, 2 => 0, 1
else:
diff --git a/pypy/jit/backend/x86/test/test_fficall.py b/pypy/jit/backend/x86/test/test_fficall.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/backend/x86/test/test_fficall.py
@@ -0,0 +1,8 @@
+import py
+from pypy.jit.metainterp.test import test_fficall
+from pypy.jit.backend.x86.test.test_basic import Jit386Mixin
+
+class TestFfiLookups(Jit386Mixin, test_fficall.FfiLookupTests):
+ # for the individual tests see
+ # ====> ../../../metainterp/test/test_fficall.py
+ supports_all = True
diff --git a/pypy/jit/backend/x86/test/test_runner.py b/pypy/jit/backend/x86/test/test_runner.py
--- a/pypy/jit/backend/x86/test/test_runner.py
+++ b/pypy/jit/backend/x86/test/test_runner.py
@@ -455,6 +455,9 @@
EffectInfo.MOST_GENERAL,
ffi_flags=-1)
calldescr.get_call_conv = lambda: ffi # <==== hack
+ # ^^^ we patch get_call_conv() so that the test also makes sense
+ # on Linux, because clibffi.get_call_conv() would always
+ # return FFI_DEFAULT_ABI on non-Windows platforms.
funcbox = ConstInt(rawstart)
i1 = BoxInt()
i2 = BoxInt()
diff --git a/pypy/jit/backend/x86/test/test_ztranslation.py b/pypy/jit/backend/x86/test/test_ztranslation.py
--- a/pypy/jit/backend/x86/test/test_ztranslation.py
+++ b/pypy/jit/backend/x86/test/test_ztranslation.py
@@ -1,6 +1,6 @@
import py, os, sys
from pypy.tool.udir import udir
-from pypy.rlib.jit import JitDriver, unroll_parameters
+from pypy.rlib.jit import JitDriver, unroll_parameters, set_param
from pypy.rlib.jit import PARAMETERS, dont_look_inside
from pypy.rlib.jit import promote
from pypy.jit.metainterp.jitprof import Profiler
@@ -47,9 +47,9 @@
def f(i, j):
for param, _ in unroll_parameters:
defl = PARAMETERS[param]
- jitdriver.set_param(param, defl)
- jitdriver.set_param("threshold", 3)
- jitdriver.set_param("trace_eagerness", 2)
+ set_param(jitdriver, param, defl)
+ set_param(jitdriver, "threshold", 3)
+ set_param(jitdriver, "trace_eagerness", 2)
total = 0
frame = Frame(i)
while frame.i > 3:
@@ -213,8 +213,8 @@
else:
return Base()
def myportal(i):
- jitdriver.set_param("threshold", 3)
- jitdriver.set_param("trace_eagerness", 2)
+ set_param(jitdriver, "threshold", 3)
+ set_param(jitdriver, "trace_eagerness", 2)
total = 0
n = i
while True:
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
@@ -212,7 +212,10 @@
elidable = False
loopinvariant = False
if op.opname == "direct_call":
- func = getattr(get_funcobj(op.args[0].value), '_callable', None)
+ funcobj = get_funcobj(op.args[0].value)
+ assert getattr(funcobj, 'calling_conv', 'c') == 'c', (
+ "%r: getcalldescr() with a non-default call ABI" % (op,))
+ func = getattr(funcobj, '_callable', None)
elidable = getattr(func, "_elidable_function_", False)
loopinvariant = getattr(func, "_jit_loop_invariant_", False)
if loopinvariant:
diff --git a/pypy/jit/codewriter/codewriter.py b/pypy/jit/codewriter/codewriter.py
--- a/pypy/jit/codewriter/codewriter.py
+++ b/pypy/jit/codewriter/codewriter.py
@@ -104,6 +104,8 @@
else:
name = 'unnamed' % id(ssarepr)
i = 1
+ # escape <lambda> names for windows
+ name = name.replace('<lambda>', '_(lambda)_')
extra = ''
while name+extra in self._seen_files:
i += 1
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
@@ -48,6 +48,8 @@
OS_LIBFFI_PREPARE = 60
OS_LIBFFI_PUSH_ARG = 61
OS_LIBFFI_CALL = 62
+ OS_LIBFFI_GETARRAYITEM = 63
+ OS_LIBFFI_SETARRAYITEM = 64
#
OS_LLONG_INVERT = 69
OS_LLONG_ADD = 70
@@ -78,6 +80,9 @@
#
OS_MATH_SQRT = 100
+ # for debugging:
+ _OS_CANRAISE = set([OS_NONE, OS_STR2UNICODE, OS_LIBFFI_CALL])
+
def __new__(cls, readonly_descrs_fields, readonly_descrs_arrays,
write_descrs_fields, write_descrs_arrays,
extraeffect=EF_CAN_RAISE,
@@ -116,6 +121,8 @@
result.extraeffect = extraeffect
result.can_invalidate = can_invalidate
result.oopspecindex = oopspecindex
+ if result.check_can_raise():
+ assert oopspecindex in cls._OS_CANRAISE
cls._cache[key] = result
return result
@@ -125,6 +132,10 @@
def check_can_invalidate(self):
return self.can_invalidate
+ def check_is_elidable(self):
+ return (self.extraeffect == self.EF_ELIDABLE_CAN_RAISE or
+ self.extraeffect == self.EF_ELIDABLE_CANNOT_RAISE)
+
def check_forces_virtual_or_virtualizable(self):
return self.extraeffect >= self.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE
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
@@ -1615,6 +1615,12 @@
elif oopspec_name.startswith('libffi_call_'):
oopspecindex = EffectInfo.OS_LIBFFI_CALL
extraeffect = EffectInfo.EF_RANDOM_EFFECTS
+ elif oopspec_name == 'libffi_array_getitem':
+ oopspecindex = EffectInfo.OS_LIBFFI_GETARRAYITEM
+ extraeffect = EffectInfo.EF_CANNOT_RAISE
+ elif oopspec_name == 'libffi_array_setitem':
+ oopspecindex = EffectInfo.OS_LIBFFI_SETARRAYITEM
+ extraeffect = EffectInfo.EF_CANNOT_RAISE
else:
assert False, 'unsupported oopspec: %s' % oopspec_name
return self._handle_oopspec_call(op, args, oopspecindex, extraeffect)
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
@@ -5,7 +5,7 @@
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.rpython.lltypesystem import lltype, rclass, rstr, rffi
from pypy.objspace.flow.model import SpaceOperation, Variable, Constant
from pypy.translator.unsimplify import varoftype
from pypy.rlib.rarithmetic import ovfcheck, r_uint, r_longlong, r_ulonglong
@@ -743,7 +743,6 @@
""", transform=True)
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
@@ -849,7 +848,6 @@
transform=True)
def test_force_cast_pointer(self):
- from pypy.rpython.lltypesystem import rffi
def h(p):
return rffi.cast(rffi.VOIDP, p)
self.encoding_test(h, [lltype.nullptr(rffi.CCHARP.TO)], """
@@ -857,7 +855,6 @@
""", transform=True)
def test_force_cast_floats(self):
- from pypy.rpython.lltypesystem import rffi
# Caststs to lltype.Float
def f(n):
return rffi.cast(lltype.Float, n)
@@ -964,7 +961,6 @@
""", 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], """
@@ -975,7 +971,6 @@
def check_force_cast(FROM, TO, operations, value):
"""Check that the test is correctly written..."""
- from pypy.rpython.lltypesystem import rffi
import re
r = re.compile('(\w+) \%i\d, \$(-?\d+)')
#
diff --git a/pypy/jit/metainterp/executor.py b/pypy/jit/metainterp/executor.py
--- a/pypy/jit/metainterp/executor.py
+++ b/pypy/jit/metainterp/executor.py
@@ -340,6 +340,8 @@
rop.DEBUG_MERGE_POINT,
rop.JIT_DEBUG,
rop.SETARRAYITEM_RAW,
+ rop.GETINTERIORFIELD_RAW,
+ rop.SETINTERIORFIELD_RAW,
rop.CALL_RELEASE_GIL,
rop.QUASIIMMUT_FIELD,
): # list of opcodes never executed by pyjitpl
diff --git a/pypy/jit/metainterp/history.py b/pypy/jit/metainterp/history.py
--- a/pypy/jit/metainterp/history.py
+++ b/pypy/jit/metainterp/history.py
@@ -999,12 +999,9 @@
"found %d %r, expected %d" % (found, insn, expected_count))
return insns
- def check_loops(self, expected=None, everywhere=False, **check):
+ def check_resops(self, expected=None, **check):
insns = {}
for loop in self.loops:
- if not everywhere:
- if getattr(loop, '_ignore_during_counting', False):
- continue
insns = loop.summary(adding_insns=insns)
if expected is not None:
insns.pop('debug_merge_point', None)
@@ -1015,7 +1012,7 @@
assert found == expected_count, (
"found %d %r, expected %d" % (found, insn, expected_count))
return insns
-
+
def check_consistency(self):
"NOT_RPYTHON"
for loop in self.loops:
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
@@ -55,7 +55,7 @@
def optimize_loop_1(metainterp_sd, loop, enable_opts,
- inline_short_preamble=True, retraced=False, bridge=False):
+ inline_short_preamble=True, retraced=False):
"""Optimize loop.operations to remove internal overheadish operations.
"""
@@ -64,7 +64,7 @@
if unroll:
optimize_unroll(metainterp_sd, loop, optimizations)
else:
- optimizer = Optimizer(metainterp_sd, loop, optimizations, bridge)
+ optimizer = Optimizer(metainterp_sd, loop, optimizations)
optimizer.propagate_all_forward()
def optimize_bridge_1(metainterp_sd, bridge, enable_opts,
@@ -76,7 +76,7 @@
except KeyError:
pass
optimize_loop_1(metainterp_sd, bridge, enable_opts,
- inline_short_preamble, retraced, bridge=True)
+ inline_short_preamble, retraced)
if __name__ == '__main__':
print ALL_OPTS_NAMES
diff --git a/pypy/jit/metainterp/optimizeopt/fficall.py b/pypy/jit/metainterp/optimizeopt/fficall.py
--- a/pypy/jit/metainterp/optimizeopt/fficall.py
+++ b/pypy/jit/metainterp/optimizeopt/fficall.py
@@ -1,11 +1,13 @@
+from pypy.jit.codewriter.effectinfo import EffectInfo
+from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
+from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
+from pypy.jit.metainterp.resoperation import rop, ResOperation
+from pypy.rlib import clibffi, libffi
+from pypy.rlib.debug import debug_print
+from pypy.rlib.libffi import Func
+from pypy.rlib.objectmodel import we_are_translated
from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
-from pypy.rlib.objectmodel import we_are_translated
-from pypy.rlib.libffi import Func
-from pypy.rlib.debug import debug_print
-from pypy.jit.codewriter.effectinfo import EffectInfo
-from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
-from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
+from pypy.rpython.lltypesystem import llmemory
class FuncInfo(object):
@@ -78,7 +80,7 @@
def new(self):
return OptFfiCall()
-
+
def begin_optimization(self, funcval, op):
self.rollback_maybe('begin_optimization', op)
self.funcinfo = FuncInfo(funcval, self.optimizer.cpu, op)
@@ -116,6 +118,9 @@
ops = self.do_push_arg(op)
elif oopspec == EffectInfo.OS_LIBFFI_CALL:
ops = self.do_call(op)
+ elif (oopspec == EffectInfo.OS_LIBFFI_GETARRAYITEM or
+ oopspec == EffectInfo.OS_LIBFFI_SETARRAYITEM):
+ ops = self.do_getsetarrayitem(op, oopspec)
#
for op in ops:
self.emit_operation(op)
@@ -190,6 +195,53 @@
ops.append(newop)
return ops
+ def do_getsetarrayitem(self, op, oopspec):
+ ffitypeval = self.getvalue(op.getarg(1))
+ widthval = self.getvalue(op.getarg(2))
+ offsetval = self.getvalue(op.getarg(5))
+ if not ffitypeval.is_constant() or not widthval.is_constant() or not offsetval.is_constant():
+ return [op]
+
+ ffitypeaddr = ffitypeval.box.getaddr()
+ ffitype = llmemory.cast_adr_to_ptr(ffitypeaddr, clibffi.FFI_TYPE_P)
+ offset = offsetval.box.getint()
+ width = widthval.box.getint()
+ descr = self._get_interior_descr(ffitype, width, offset)
+
+ arglist = [
+ self.getvalue(op.getarg(3)).force_box(self.optimizer),
+ self.getvalue(op.getarg(4)).force_box(self.optimizer),
+ ]
+ if oopspec == EffectInfo.OS_LIBFFI_GETARRAYITEM:
+ opnum = rop.GETINTERIORFIELD_RAW
+ elif oopspec == EffectInfo.OS_LIBFFI_SETARRAYITEM:
+ opnum = rop.SETINTERIORFIELD_RAW
+ arglist.append(self.getvalue(op.getarg(6)).force_box(self.optimizer))
+ else:
+ assert False
+ return [
+ ResOperation(opnum, arglist, op.result, descr=descr),
+ ]
+
+ def _get_interior_descr(self, ffitype, width, offset):
+ kind = libffi.types.getkind(ffitype)
+ is_pointer = is_float = is_signed = False
+ if ffitype is libffi.types.pointer:
+ is_pointer = True
+ elif kind == 'i':
+ is_signed = True
+ elif kind == 'f' or kind == 'I' or kind == 'U':
+ # longlongs are treated as floats, see
+ # e.g. llsupport/descr.py:getDescrClass
+ is_float = True
+ else:
+ assert False, "unsupported ffitype or kind"
+ #
+ fieldsize = ffitype.c_size
+ return self.optimizer.cpu.interiorfielddescrof_dynamic(
+ offset, width, fieldsize, is_pointer, is_float, is_signed
+ )
+
def propagate_forward(self, op):
if self.logops is not None:
debug_print(self.logops.repr_of_resop(op))
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
@@ -234,7 +234,7 @@
or op.is_ovf()):
self.posponedop = op
else:
- self.next_optimization.propagate_forward(op)
+ Optimization.emit_operation(self, op)
def emitting_operation(self, op):
if op.has_no_side_effect():
diff --git a/pypy/jit/metainterp/optimizeopt/intbounds.py b/pypy/jit/metainterp/optimizeopt/intbounds.py
--- a/pypy/jit/metainterp/optimizeopt/intbounds.py
+++ b/pypy/jit/metainterp/optimizeopt/intbounds.py
@@ -6,6 +6,7 @@
IntUpperBound)
from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
from pypy.jit.metainterp.resoperation import rop
+from pypy.jit.metainterp.optimize import InvalidLoop
from pypy.rlib.rarithmetic import LONG_BIT
@@ -13,30 +14,10 @@
"""Keeps track of the bounds placed on integers by guards and remove
redundant guards"""
- def setup(self):
- self.posponedop = None
- self.nextop = None
-
def new(self):
- assert self.posponedop is None
return OptIntBounds()
-
- def flush(self):
- assert self.posponedop is None
-
- def setup(self):
- self.posponedop = None
- self.nextop = None
def propagate_forward(self, op):
- if op.is_ovf():
- self.posponedop = op
- return
- if self.posponedop:
- self.nextop = op
- op = self.posponedop
- self.posponedop = None
-
dispatch_opt(self, op)
def opt_default(self, op):
@@ -179,68 +160,75 @@
r = self.getvalue(op.result)
r.intbound.intersect(b)
+ def optimize_GUARD_NO_OVERFLOW(self, op):
+ lastop = self.last_emitted_operation
+ if lastop is not None:
+ opnum = lastop.getopnum()
+ args = lastop.getarglist()
+ result = lastop.result
+ # If the INT_xxx_OVF was replaced with INT_xxx, then we can kill
+ # the GUARD_NO_OVERFLOW.
+ if (opnum == rop.INT_ADD or
+ opnum == rop.INT_SUB or
+ opnum == rop.INT_MUL):
+ return
+ # Else, synthesize the non overflowing op for optimize_default to
+ # reuse, as well as the reverse op
+ elif opnum == rop.INT_ADD_OVF:
+ self.pure(rop.INT_ADD, args[:], result)
+ self.pure(rop.INT_SUB, [result, args[1]], args[0])
+ self.pure(rop.INT_SUB, [result, args[0]], args[1])
+ elif opnum == rop.INT_SUB_OVF:
+ self.pure(rop.INT_SUB, args[:], result)
+ self.pure(rop.INT_ADD, [result, args[1]], args[0])
+ self.pure(rop.INT_SUB, [args[0], result], args[1])
+ elif opnum == rop.INT_MUL_OVF:
+ self.pure(rop.INT_MUL, args[:], result)
+ self.emit_operation(op)
+
+ def optimize_GUARD_OVERFLOW(self, op):
+ # If INT_xxx_OVF was replaced by INT_xxx, *but* we still see
+ # GUARD_OVERFLOW, then the loop is invalid.
+ lastop = self.last_emitted_operation
+ if lastop is None:
+ raise InvalidLoop
+ opnum = lastop.getopnum()
+ if opnum not in (rop.INT_ADD_OVF, rop.INT_SUB_OVF, rop.INT_MUL_OVF):
+ raise InvalidLoop
+ self.emit_operation(op)
+
def optimize_INT_ADD_OVF(self, op):
v1 = self.getvalue(op.getarg(0))
v2 = self.getvalue(op.getarg(1))
resbound = v1.intbound.add_bound(v2.intbound)
- if resbound.has_lower and resbound.has_upper and \
- self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW:
- # Transform into INT_ADD and remove guard
+ if resbound.bounded():
+ # Transform into INT_ADD. The following guard will be killed
+ # by optimize_GUARD_NO_OVERFLOW; if we see instead an
+ # optimize_GUARD_OVERFLOW, then InvalidLoop.
op = op.copy_and_change(rop.INT_ADD)
- self.optimize_INT_ADD(op) # emit the op
- else:
- self.emit_operation(op)
- r = self.getvalue(op.result)
- r.intbound.intersect(resbound)
- self.emit_operation(self.nextop)
- if self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW:
- # Synthesize the non overflowing op for optimize_default to reuse
- self.pure(rop.INT_ADD, op.getarglist()[:], op.result)
- # Synthesize the reverse op for optimize_default to reuse
- self.pure(rop.INT_SUB, [op.result, op.getarg(1)], op.getarg(0))
- self.pure(rop.INT_SUB, [op.result, op.getarg(0)], op.getarg(1))
-
+ self.emit_operation(op) # emit the op
+ r = self.getvalue(op.result)
+ r.intbound.intersect(resbound)
def optimize_INT_SUB_OVF(self, op):
v1 = self.getvalue(op.getarg(0))
v2 = self.getvalue(op.getarg(1))
resbound = v1.intbound.sub_bound(v2.intbound)
- if resbound.has_lower and resbound.has_upper and \
- self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW:
- # Transform into INT_SUB and remove guard
+ if resbound.bounded():
op = op.copy_and_change(rop.INT_SUB)
- self.optimize_INT_SUB(op) # emit the op
- else:
- self.emit_operation(op)
- r = self.getvalue(op.result)
- r.intbound.intersect(resbound)
- self.emit_operation(self.nextop)
- if self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW:
- # Synthesize the non overflowing op for optimize_default to reuse
- self.pure(rop.INT_SUB, op.getarglist()[:], op.result)
- # Synthesize the reverse ops for optimize_default to reuse
- self.pure(rop.INT_ADD, [op.result, op.getarg(1)], op.getarg(0))
- self.pure(rop.INT_SUB, [op.getarg(0), op.result], op.getarg(1))
-
+ self.emit_operation(op) # emit the op
+ r = self.getvalue(op.result)
+ r.intbound.intersect(resbound)
def optimize_INT_MUL_OVF(self, op):
v1 = self.getvalue(op.getarg(0))
v2 = self.getvalue(op.getarg(1))
resbound = v1.intbound.mul_bound(v2.intbound)
- if resbound.has_lower and resbound.has_upper and \
- self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW:
- # Transform into INT_MUL and remove guard
+ if resbound.bounded():
op = op.copy_and_change(rop.INT_MUL)
- self.optimize_INT_MUL(op) # emit the op
- else:
- self.emit_operation(op)
- r = self.getvalue(op.result)
- r.intbound.intersect(resbound)
- self.emit_operation(self.nextop)
- if self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW:
- # Synthesize the non overflowing op for optimize_default to reuse
- self.pure(rop.INT_MUL, op.getarglist()[:], op.result)
-
+ self.emit_operation(op)
+ r = self.getvalue(op.result)
+ r.intbound.intersect(resbound)
def optimize_INT_LT(self, op):
v1 = self.getvalue(op.getarg(0))
diff --git a/pypy/jit/metainterp/optimizeopt/intutils.py b/pypy/jit/metainterp/optimizeopt/intutils.py
--- a/pypy/jit/metainterp/optimizeopt/intutils.py
+++ b/pypy/jit/metainterp/optimizeopt/intutils.py
@@ -1,4 +1,4 @@
-from pypy.rlib.rarithmetic import ovfcheck, ovfcheck_lshift, LONG_BIT
+from pypy.rlib.rarithmetic import ovfcheck, LONG_BIT
from pypy.rlib.objectmodel import we_are_translated
from pypy.jit.metainterp.resoperation import rop, ResOperation
from pypy.jit.metainterp.history import BoxInt, ConstInt
@@ -174,10 +174,10 @@
other.known_ge(IntBound(0, 0)) and \
other.known_lt(IntBound(LONG_BIT, LONG_BIT)):
try:
- vals = (ovfcheck_lshift(self.upper, other.upper),
- ovfcheck_lshift(self.upper, other.lower),
- ovfcheck_lshift(self.lower, other.upper),
- ovfcheck_lshift(self.lower, other.lower))
+ vals = (ovfcheck(self.upper << other.upper),
+ ovfcheck(self.upper << other.lower),
+ ovfcheck(self.lower << other.upper),
+ ovfcheck(self.lower << other.lower))
return IntBound(min4(vals), max4(vals))
except (OverflowError, ValueError):
return IntUnbounded()
diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py b/pypy/jit/metainterp/optimizeopt/optimizer.py
--- a/pypy/jit/metainterp/optimizeopt/optimizer.py
+++ b/pypy/jit/metainterp/optimizeopt/optimizer.py
@@ -6,7 +6,7 @@
IntLowerBound, MININT, MAXINT
from pypy.jit.metainterp.optimizeopt.util import (make_dispatcher_method,
args_dict)
-from pypy.jit.metainterp.resoperation import rop, ResOperation
+from pypy.jit.metainterp.resoperation import rop, ResOperation, AbstractResOp
from pypy.jit.metainterp.typesystem import llhelper, oohelper
from pypy.tool.pairtype import extendabletype
from pypy.rlib.debug import debug_start, debug_stop, debug_print
@@ -247,9 +247,10 @@
CONST_1 = ConstInt(1)
CVAL_ZERO = ConstantValue(CONST_0)
CVAL_ZERO_FLOAT = ConstantValue(Const._new(0.0))
-CVAL_UNINITIALIZED_ZERO = ConstantValue(CONST_0)
llhelper.CVAL_NULLREF = ConstantValue(llhelper.CONST_NULL)
oohelper.CVAL_NULLREF = ConstantValue(oohelper.CONST_NULL)
+REMOVED = AbstractResOp(None)
+
class Optimization(object):
next_optimization = None
@@ -261,6 +262,7 @@
raise NotImplementedError
def emit_operation(self, op):
+ self.last_emitted_operation = op
self.next_optimization.propagate_forward(op)
# FIXME: Move some of these here?
@@ -328,13 +330,13 @@
def forget_numberings(self, box):
self.optimizer.forget_numberings(box)
+
class Optimizer(Optimization):
- def __init__(self, metainterp_sd, loop, optimizations=None, bridge=False):
+ def __init__(self, metainterp_sd, loop, optimizations=None):
self.metainterp_sd = metainterp_sd
self.cpu = metainterp_sd.cpu
self.loop = loop
- self.bridge = bridge
self.values = {}
self.interned_refs = self.cpu.ts.new_ref_dict()
self.interned_ints = {}
@@ -342,11 +344,11 @@
self.bool_boxes = {}
self.producer = {}
self.pendingfields = []
- self.exception_might_have_happened = False
self.quasi_immutable_deps = None
self.opaque_pointers = {}
self.replaces_guard = {}
self._newoperations = []
+ self.seen_results = {}
self.optimizer = self
self.optpure = None
self.optearlyforce = None
@@ -364,6 +366,7 @@
optimizations[-1].next_optimization = self
for o in optimizations:
o.optimizer = self
+ o.last_emitted_operation = None
o.setup()
else:
optimizations = []
@@ -498,7 +501,6 @@
return CVAL_ZERO
def propagate_all_forward(self):
- self.exception_might_have_happened = self.bridge
self.clear_newoperations()
for op in self.loop.operations:
self.first_optimization.propagate_forward(op)
@@ -541,6 +543,10 @@
op = self.store_final_boxes_in_guard(op)
elif op.can_raise():
self.exception_might_have_happened = True
+ if op.result:
+ if op.result in self.seen_results:
+ raise ValueError, "invalid optimization"
+ self.seen_results[op.result] = None
self._newoperations.append(op)
def replace_op(self, old_op, new_op):
@@ -558,9 +564,12 @@
descr = op.getdescr()
assert isinstance(descr, compile.ResumeGuardDescr)
modifier = resume.ResumeDataVirtualAdder(descr, self.resumedata_memo)
- newboxes = modifier.finish(self.values, self.pendingfields)
- if len(newboxes) > self.metainterp_sd.options.failargs_limit: # XXX be careful here
- compile.giveup()
+ try:
+ newboxes = modifier.finish(self.values, self.pendingfields)
+ if len(newboxes) > self.metainterp_sd.options.failargs_limit:
+ raise resume.TagOverflow
+ except resume.TagOverflow:
+ raise compile.giveup()
descr.store_final_boxes(op, newboxes)
#
if op.getopnum() == rop.GUARD_VALUE:
diff --git a/pypy/jit/metainterp/optimizeopt/pure.py b/pypy/jit/metainterp/optimizeopt/pure.py
--- a/pypy/jit/metainterp/optimizeopt/pure.py
+++ b/pypy/jit/metainterp/optimizeopt/pure.py
@@ -1,4 +1,4 @@
-from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
+from pypy.jit.metainterp.optimizeopt.optimizer import Optimization, REMOVED
from pypy.jit.metainterp.resoperation import rop, ResOperation
from pypy.jit.metainterp.optimizeopt.util import (make_dispatcher_method,
args_dict)
@@ -61,7 +61,10 @@
oldop = self.pure_operations.get(args, None)
if oldop is not None and oldop.getdescr() is op.getdescr():
assert oldop.getopnum() == op.getopnum()
+ # this removes a CALL_PURE that has the same (non-constant)
+ # arguments as a previous CALL_PURE.
self.make_equal_to(op.result, self.getvalue(oldop.result))
+ self.last_emitted_operation = REMOVED
return
else:
self.pure_operations[args] = op
@@ -72,6 +75,13 @@
self.emit_operation(ResOperation(rop.CALL, args, op.result,
op.getdescr()))
+ def optimize_GUARD_NO_EXCEPTION(self, op):
+ if self.last_emitted_operation is REMOVED:
+ # it was a CALL_PURE that was killed; so we also kill the
+ # following GUARD_NO_EXCEPTION
+ return
+ self.emit_operation(op)
+
def flush(self):
assert self.posponedop is None
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
@@ -294,12 +294,6 @@
raise InvalidLoop
self.optimize_GUARD_CLASS(op)
- def optimize_GUARD_NO_EXCEPTION(self, op):
- if not self.optimizer.exception_might_have_happened:
- return
- self.emit_operation(op)
- self.optimizer.exception_might_have_happened = False
-
def optimize_CALL_LOOPINVARIANT(self, op):
arg = op.getarg(0)
# 'arg' must be a Const, because residual_call in codewriter
@@ -310,6 +304,7 @@
resvalue = self.loop_invariant_results.get(key, None)
if resvalue is not None:
self.make_equal_to(op.result, resvalue)
+ self.last_emitted_operation = REMOVED
return
# change the op to be a normal call, from the backend's point of view
# there is no reason to have a separate operation for this
@@ -444,10 +439,19 @@
except KeyError:
pass
else:
+ # this removes a CALL_PURE with all constant arguments.
self.make_constant(op.result, result)
+ self.last_emitted_operation = REMOVED
return
self.emit_operation(op)
+ def optimize_GUARD_NO_EXCEPTION(self, op):
+ if self.last_emitted_operation is REMOVED:
+ # it was a CALL_PURE or a CALL_LOOPINVARIANT that was killed;
+ # so we also kill the following GUARD_NO_EXCEPTION
+ return
+ self.emit_operation(op)
+
def optimize_INT_FLOORDIV(self, op):
v1 = self.getvalue(op.getarg(0))
v2 = self.getvalue(op.getarg(1))
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
@@ -681,25 +681,60 @@
# ----------
- def test_fold_guard_no_exception(self):
- ops = """
- [i]
- guard_no_exception() []
- i1 = int_add(i, 3)
- guard_no_exception() []
+ def test_keep_guard_no_exception(self):
+ ops = """
+ [i1]
i2 = call(i1, descr=nonwritedescr)
guard_no_exception() [i1, i2]
- guard_no_exception() []
- i3 = call(i2, descr=nonwritedescr)
- jump(i1) # the exception is considered lost when we loop back
- """
- expected = """
- [i]
- i1 = int_add(i, 3)
- i2 = call(i1, descr=nonwritedescr)
+ jump(i2)
+ """
+ self.optimize_loop(ops, ops)
+
+ def test_keep_guard_no_exception_with_call_pure_that_is_not_folded(self):
+ ops = """
+ [i1]
+ i2 = call_pure(123456, i1, descr=nonwritedescr)
guard_no_exception() [i1, i2]
- i3 = call(i2, descr=nonwritedescr)
- jump(i1)
+ jump(i2)
+ """
+ expected = """
+ [i1]
+ i2 = call(123456, i1, descr=nonwritedescr)
+ guard_no_exception() [i1, i2]
+ jump(i2)
+ """
+ self.optimize_loop(ops, expected)
+
+ def test_remove_guard_no_exception_with_call_pure_on_constant_args(self):
+ arg_consts = [ConstInt(i) for i in (123456, 81)]
+ call_pure_results = {tuple(arg_consts): ConstInt(5)}
+ ops = """
+ [i1]
+ i3 = same_as(81)
+ i2 = call_pure(123456, i3, descr=nonwritedescr)
+ guard_no_exception() [i1, i2]
+ jump(i2)
+ """
+ expected = """
+ [i1]
+ jump(5)
+ """
+ self.optimize_loop(ops, expected, call_pure_results)
+
+ def test_remove_guard_no_exception_with_duplicated_call_pure(self):
+ ops = """
+ [i1]
+ i2 = call_pure(123456, i1, descr=nonwritedescr)
+ guard_no_exception() [i1, i2]
+ i3 = call_pure(123456, i1, descr=nonwritedescr)
+ guard_no_exception() [i1, i2, i3]
+ jump(i3)
+ """
+ expected = """
+ [i1]
+ i2 = call(123456, i1, descr=nonwritedescr)
+ guard_no_exception() [i1, i2]
+ jump(i2)
"""
self.optimize_loop(ops, expected)
@@ -4123,6 +4158,38 @@
"""
self.optimize_strunicode_loop(ops, expected)
+ def test_str_concat_constant_lengths(self):
+ ops = """
+ [i0]
+ p0 = newstr(1)
+ strsetitem(p0, 0, i0)
+ p1 = newstr(0)
+ p2 = call(0, p0, p1, descr=strconcatdescr)
+ i1 = call(0, p2, p0, descr=strequaldescr)
+ finish(i1)
+ """
+ expected = """
+ [i0]
+ finish(1)
+ """
+ self.optimize_strunicode_loop(ops, expected)
+
+ def test_str_concat_constant_lengths_2(self):
+ ops = """
+ [i0]
+ p0 = newstr(0)
+ p1 = newstr(1)
+ strsetitem(p1, 0, i0)
+ p2 = call(0, p0, p1, descr=strconcatdescr)
+ i1 = call(0, p2, p1, descr=strequaldescr)
+ finish(i1)
+ """
+ expected = """
+ [i0]
+ finish(1)
+ """
+ self.optimize_strunicode_loop(ops, expected)
+
def test_str_slice_1(self):
ops = """
[p1, i1, i2]
@@ -4883,6 +4950,27 @@
def test_plain_virtual_string_copy_content(self):
ops = """
+ [i1]
+ p0 = newstr(6)
+ copystrcontent(s"hello!", p0, 0, 0, 6)
+ p1 = call(0, p0, s"abc123", descr=strconcatdescr)
+ i0 = strgetitem(p1, i1)
+ finish(i0)
+ """
+ expected = """
+ [i1]
+ p0 = newstr(6)
+ copystrcontent(s"hello!", p0, 0, 0, 6)
+ p1 = newstr(12)
+ copystrcontent(p0, p1, 0, 0, 6)
+ copystrcontent(s"abc123", p1, 0, 6, 6)
+ i0 = strgetitem(p1, i1)
+ finish(i0)
+ """
+ self.optimize_strunicode_loop(ops, expected)
+
+ def test_plain_virtual_string_copy_content_2(self):
+ ops = """
[]
p0 = newstr(6)
copystrcontent(s"hello!", p0, 0, 0, 6)
@@ -4894,10 +4982,7 @@
[]
p0 = newstr(6)
copystrcontent(s"hello!", p0, 0, 0, 6)
- p1 = newstr(12)
- copystrcontent(p0, p1, 0, 0, 6)
- copystrcontent(s"abc123", p1, 0, 6, 6)
- i0 = strgetitem(p1, 0)
+ i0 = strgetitem(p0, 0)
finish(i0)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -4914,6 +4999,34 @@
"""
self.optimize_loop(ops, expected)
+ def test_known_equal_ints(self):
+ py.test.skip("in-progress")
+ ops = """
+ [i0, i1, i2, p0]
+ i3 = int_eq(i0, i1)
+ guard_true(i3) []
+
+ i4 = int_lt(i2, i0)
+ guard_true(i4) []
+ i5 = int_lt(i2, i1)
+ guard_true(i5) []
+
+ i6 = getarrayitem_gc(p0, i2)
+ finish(i6)
+ """
+ expected = """
+ [i0, i1, i2, p0]
+ i3 = int_eq(i0, i1)
+ guard_true(i3) []
+
+ i4 = int_lt(i2, i0)
+ guard_true(i4) []
+
+ i6 = getarrayitem_gc(p0, i3)
+ finish(i6)
+ """
+ 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
@@ -931,17 +931,14 @@
[i]
guard_no_exception() []
i1 = int_add(i, 3)
- guard_no_exception() []
i2 = call(i1, descr=nonwritedescr)
guard_no_exception() [i1, i2]
- guard_no_exception() []
i3 = call(i2, descr=nonwritedescr)
jump(i1) # the exception is considered lost when we loop back
"""
- # note that 'guard_no_exception' at the very start is kept around
- # for bridges, but not for loops
preamble = """
[i]
+ guard_no_exception() [] # occurs at the start of bridges, so keep it
i1 = int_add(i, 3)
i2 = call(i1, descr=nonwritedescr)
guard_no_exception() [i1, i2]
@@ -950,6 +947,7 @@
"""
expected = """
[i]
+ guard_no_exception() [] # occurs at the start of bridges, so keep it
i1 = int_add(i, 3)
i2 = call(i1, descr=nonwritedescr)
guard_no_exception() [i1, i2]
@@ -958,6 +956,23 @@
"""
self.optimize_loop(ops, expected, preamble)
+ def test_bug_guard_no_exception(self):
+ ops = """
+ []
+ i0 = call(123, descr=nonwritedescr)
+ p0 = call(0, "xy", descr=s2u_descr) # string -> unicode
+ guard_no_exception() []
+ escape(p0)
+ jump()
+ """
+ expected = """
+ []
+ i0 = call(123, descr=nonwritedescr)
+ escape(u"xy")
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+
# ----------
def test_call_loopinvariant(self):
@@ -1176,6 +1191,75 @@
"""
self.optimize_loop(ops, expected, preamble)
+ def test_virtual_recursive(self):
+ ops = """
+ [p0]
+ p41 = getfield_gc(p0, descr=nextdescr)
+ i0 = getfield_gc(p41, descr=valuedescr)
+ p1 = new_with_vtable(ConstClass(node_vtable2))
+ p2 = new_with_vtable(ConstClass(node_vtable2))
+ setfield_gc(p2, p1, descr=nextdescr)
+ setfield_gc(p1, p2, descr=nextdescr)
+ i1 = int_add(i0, 1)
+ setfield_gc(p2, i1, descr=valuedescr)
+ jump(p1)
+ """
+ preamble = """
+ [p0]
+ p41 = getfield_gc(p0, descr=nextdescr)
+ i0 = getfield_gc(p41, descr=valuedescr)
+ i3 = int_add(i0, 1)
+ jump(i3)
+ """
+ expected = """
+ [i0]
+ i1 = int_add(i0, 1)
+ jump(i1)
+ """
+ self.optimize_loop(ops, expected, preamble)
+
+ def test_virtual_recursive_forced(self):
+ ops = """
+ [p0]
+ p41 = getfield_gc(p0, descr=nextdescr)
+ i0 = getfield_gc(p41, descr=valuedescr)
+ p1 = new_with_vtable(ConstClass(node_vtable2))
+ p2 = new_with_vtable(ConstClass(node_vtable2))
+ setfield_gc(p2, p1, descr=nextdescr)
+ setfield_gc(p1, p2, descr=nextdescr)
+ i1 = int_add(i0, 1)
+ setfield_gc(p2, i1, descr=valuedescr)
+ setfield_gc(p0, p1, descr=nextdescr)
+ jump(p1)
+ """
+ preamble = """
+ [p0]
+ p41 = getfield_gc(p0, descr=nextdescr)
+ i0 = getfield_gc(p41, descr=valuedescr)
+ i1 = int_add(i0, 1)
+ p1 = new_with_vtable(ConstClass(node_vtable2))
+ p2 = new_with_vtable(ConstClass(node_vtable2))
+ setfield_gc(p2, i1, descr=valuedescr)
+ setfield_gc(p2, p1, descr=nextdescr)
+ setfield_gc(p1, p2, descr=nextdescr)
+ setfield_gc(p0, p1, descr=nextdescr)
+ jump(p1)
+ """
+ loop = """
+ [p0]
+ p41 = getfield_gc(p0, descr=nextdescr)
+ i0 = getfield_gc(p41, descr=valuedescr)
+ i1 = int_add(i0, 1)
+ p1 = new_with_vtable(ConstClass(node_vtable2))
+ p2 = new_with_vtable(ConstClass(node_vtable2))
+ setfield_gc(p0, p1, descr=nextdescr)
+ setfield_gc(p2, p1, descr=nextdescr)
+ setfield_gc(p1, p2, descr=nextdescr)
+ setfield_gc(p2, i1, descr=valuedescr)
+ jump(p1)
+ """
+ self.optimize_loop(ops, loop, preamble)
+
def test_virtual_constant_isnull(self):
ops = """
[i0]
@@ -2168,13 +2252,13 @@
ops = """
[p0, i0, p1, i1, i2]
setfield_gc(p0, i1, descr=valuedescr)
- copystrcontent(p0, i0, p1, i1, i2)
+ copystrcontent(p0, p1, i0, i1, i2)
escape()
jump(p0, i0, p1, i1, i2)
"""
expected = """
[p0, i0, p1, i1, i2]
- copystrcontent(p0, i0, p1, i1, i2)
+ copystrcontent(p0, p1, i0, i1, i2)
setfield_gc(p0, i1, descr=valuedescr)
escape()
jump(p0, i0, p1, i1, i2)
@@ -5423,6 +5507,96 @@
jump()
"""
self.optimize_loop(ops, expected)
+ # ----------
+ ops = """
+ [p1]
+ p0 = new_with_vtable(ConstClass(ptrobj_immut_vtable))
+ setfield_gc(p0, p1, descr=immut_ptrval)
+ escape(p0)
+ jump(p1)
+ """
+ self.optimize_loop(ops, ops)
+ # ----------
+ ops = """
+ []
+ p0 = new_with_vtable(ConstClass(ptrobj_immut_vtable))
+ p1 = new_with_vtable(ConstClass(intobj_immut_vtable))
+ setfield_gc(p1, 1242, descr=immut_intval)
+ setfield_gc(p0, p1, descr=immut_ptrval)
+ escape(p0)
+ jump()
+ """
+ class PtrObj1242(object):
+ _TYPE = llmemory.GCREF.TO
+ def __eq__(slf, other):
+ if slf is other:
+ return 1
+ p1 = other.container.ptrval
+ p1cast = lltype.cast_pointer(lltype.Ptr(self.INTOBJ_IMMUT), p1)
+ return p1cast.intval == 1242
+ self.namespace['ptrobj1242'] = lltype._ptr(llmemory.GCREF,
+ PtrObj1242())
+ expected = """
+ []
+ escape(ConstPtr(ptrobj1242))
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+
+ def test_immutable_constantfold_recursive(self):
+ ops = """
+ []
+ p0 = new_with_vtable(ConstClass(ptrobj_immut_vtable))
+ setfield_gc(p0, p0, descr=immut_ptrval)
+ escape(p0)
+ jump()
+ """
+ from pypy.rpython.lltypesystem import lltype, llmemory
+ class PtrObjSelf(object):
+ _TYPE = llmemory.GCREF.TO
+ def __eq__(slf, other):
+ if slf is other:
+ return 1
+ p1 = other.container.ptrval
+ p1cast = lltype.cast_pointer(lltype.Ptr(self.PTROBJ_IMMUT), p1)
+ return p1cast.ptrval == p1
+ self.namespace['ptrobjself'] = lltype._ptr(llmemory.GCREF,
+ PtrObjSelf())
+ expected = """
+ []
+ escape(ConstPtr(ptrobjself))
+ jump()
+ """
+ self.optimize_loop(ops, expected)
+ #
+ ops = """
+ []
+ p0 = new_with_vtable(ConstClass(ptrobj_immut_vtable))
+ p1 = new_with_vtable(ConstClass(ptrobj_immut_vtable))
+ setfield_gc(p0, p1, descr=immut_ptrval)
+ setfield_gc(p1, p0, descr=immut_ptrval)
+ escape(p0)
+ jump()
+ """
+ class PtrObjSelf2(object):
+ _TYPE = llmemory.GCREF.TO
+ def __eq__(slf, other):
+ if slf is other:
+ return 1
+ p1 = other.container.ptrval
+ p1cast = lltype.cast_pointer(lltype.Ptr(self.PTROBJ_IMMUT), p1)
+ p2 = p1cast.ptrval
+ assert p2 != p1
+ p2cast = lltype.cast_pointer(lltype.Ptr(self.PTROBJ_IMMUT), p2)
+ return p2cast.ptrval == p1
+ self.namespace['ptrobjself2'] = lltype._ptr(llmemory.GCREF,
+ PtrObjSelf2())
+ expected = """
+ []
+ escape(ConstPtr(ptrobjself2))
+ jump()
+ """
+ self.optimize_loop(ops, expected)
# ----------
def optimize_strunicode_loop(self, ops, optops, preamble):
@@ -6281,12 +6455,15 @@
def test_str2unicode_constant(self):
ops = """
[]
+ escape(1213)
p0 = call(0, "xy", descr=s2u_descr) # string -> unicode
+ guard_no_exception() []
escape(p0)
jump()
"""
expected = """
[]
+ escape(1213)
escape(u"xy")
jump()
"""
@@ -6296,6 +6473,7 @@
ops = """
[p0]
p1 = call(0, p0, descr=s2u_descr) # string -> unicode
+ guard_no_exception() []
escape(p1)
jump(p1)
"""
@@ -7407,7 +7585,7 @@
expected = """
[p22, p18, i1, i2]
call(i2, descr=nonwritedescr)
- setfield_gc(p22, i1, descr=valuedescr)
+ setfield_gc(p22, i1, descr=valuedescr)
jump(p22, p18, i1, i1)
"""
self.optimize_loop(ops, expected, preamble, expected_short=short)
@@ -7434,7 +7612,7 @@
def test_cache_setarrayitem_across_loop_boundaries(self):
ops = """
[p1]
- p2 = getarrayitem_gc(p1, 3, descr=arraydescr)
+ p2 = getarrayitem_gc(p1, 3, descr=arraydescr)
guard_nonnull_class(p2, ConstClass(node_vtable)) []
call(p2, descr=nonwritedescr)
p3 = new_with_vtable(ConstClass(node_vtable))
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_util.py b/pypy/jit/metainterp/optimizeopt/test/test_util.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_util.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_util.py
@@ -139,6 +139,12 @@
noimmut_intval = cpu.fielddescrof(INTOBJ_NOIMMUT, 'intval')
immut_intval = cpu.fielddescrof(INTOBJ_IMMUT, 'intval')
+ PTROBJ_IMMUT = lltype.GcStruct('PTROBJ_IMMUT', ('parent', OBJECT),
+ ('ptrval', lltype.Ptr(OBJECT)),
+ hints={'immutable': True})
+ ptrobj_immut_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True)
+ immut_ptrval = cpu.fielddescrof(PTROBJ_IMMUT, 'ptrval')
+
arraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Signed))
floatarraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Float))
@@ -183,6 +189,7 @@
can_invalidate=True))
arraycopydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
EffectInfo([], [arraydescr], [], [arraydescr],
+ EffectInfo.EF_CANNOT_RAISE,
oopspecindex=EffectInfo.OS_ARRAYCOPY))
@@ -212,12 +219,14 @@
_oopspecindex = getattr(EffectInfo, _os)
locals()[_name] = \
cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], [], oopspecindex=_oopspecindex))
+ EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+ oopspecindex=_oopspecindex))
#
_oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI'))
locals()[_name.replace('str', 'unicode')] = \
cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], [], oopspecindex=_oopspecindex))
+ EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+ oopspecindex=_oopspecindex))
s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
EffectInfo([], [], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE))
@@ -243,6 +252,7 @@
register_known_gctype(cpu, jit_virtual_ref_vtable,vrefinfo.JIT_VIRTUAL_REF)
register_known_gctype(cpu, intobj_noimmut_vtable, INTOBJ_NOIMMUT)
register_known_gctype(cpu, intobj_immut_vtable, INTOBJ_IMMUT)
+ register_known_gctype(cpu, ptrobj_immut_vtable, PTROBJ_IMMUT)
namespace = locals()
diff --git a/pypy/jit/metainterp/optimizeopt/virtualize.py b/pypy/jit/metainterp/optimizeopt/virtualize.py
--- a/pypy/jit/metainterp/optimizeopt/virtualize.py
+++ b/pypy/jit/metainterp/optimizeopt/virtualize.py
@@ -87,14 +87,36 @@
def _get_descr(self):
raise NotImplementedError
- def _is_immutable_and_filled_with_constants(self, optforce):
+ def _is_immutable_and_filled_with_constants(self, memo=None):
+ # check if it is possible to force the given structure into a
+ # compile-time constant: this is allowed only if it is declared
+ # immutable, if all fields are already filled, and if each field
+ # is either a compile-time constant or (recursively) a structure
+ # which also answers True to the same question.
+ #
+ # check that all fields are filled. The following equality check
+ # also fails if count == -1, meaning "not an immutable at all".
count = self._get_descr().count_fields_if_immutable()
- if count != len(self._fields): # always the case if count == -1
+ if count != len(self._fields):
return False
+ #
+ # initialize 'memo'
+ if memo is None:
+ memo = {}
+ elif self in memo:
+ return True # recursive case: assume yes
+ memo[self] = None
+ #
for value in self._fields.itervalues():
- subbox = value.force_box(optforce)
- if not isinstance(subbox, Const):
- return False
+ if value.is_constant():
+ pass # it is a constant value: ok
+ elif (isinstance(value, AbstractVirtualStructValue)
+ and value.is_virtual()):
+ # recursive check
+ if not value._is_immutable_and_filled_with_constants(memo):
+ return False
+ else:
+ return False # not a constant at all
return True
def force_at_end_of_preamble(self, already_forced, optforce):
@@ -114,7 +136,7 @@
if not we_are_translated():
op.name = 'FORCE ' + self.source_op.name
- if self._is_immutable_and_filled_with_constants(optforce):
+ if self._is_immutable_and_filled_with_constants():
box = optforce.optimizer.constant_fold(op)
self.make_constant(box)
for ofs, value in self._fields.iteritems():
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
@@ -1,8 +1,9 @@
from pypy.jit.codewriter.effectinfo import EffectInfo
from pypy.jit.metainterp.history import (BoxInt, Const, ConstInt, ConstPtr,
- get_const_ptr_for_string, get_const_ptr_for_unicode)
+ get_const_ptr_for_string, get_const_ptr_for_unicode, BoxPtr, REF, INT)
from pypy.jit.metainterp.optimizeopt import optimizer, virtualize
-from pypy.jit.metainterp.optimizeopt.optimizer import CONST_0, CONST_1, llhelper
+from pypy.jit.metainterp.optimizeopt.optimizer import CONST_0, CONST_1
+from pypy.jit.metainterp.optimizeopt.optimizer import llhelper, REMOVED
from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
from pypy.jit.metainterp.resoperation import rop, ResOperation
from pypy.rlib.objectmodel import specialize, we_are_translated
@@ -106,7 +107,12 @@
if not we_are_translated():
op.name = 'FORCE'
optforce.emit_operation(op)
- self.string_copy_parts(optforce, box, CONST_0, self.mode)
+ self.initialize_forced_string(optforce, box, CONST_0, self.mode)
+
+ def initialize_forced_string(self, string_optimizer, targetbox,
+ offsetbox, mode):
+ return self.string_copy_parts(string_optimizer, targetbox,
+ offsetbox, mode)
class VStringPlainValue(VAbstractStringValue):
@@ -114,11 +120,20 @@
_lengthbox = None # cache only
def setup(self, size):
- self._chars = [optimizer.CVAL_UNINITIALIZED_ZERO] * size
+ # in this list, None means: "it's probably uninitialized so far,
+ # but maybe it was actually filled." So to handle this case,
+ # strgetitem cannot be virtual-ized and must be done as a residual
+ # operation. By contrast, any non-None value means: we know it
+ # is initialized to this value; strsetitem() there makes no sense.
+ # Also, as long as self.is_virtual(), then we know that no-one else
+ # could have written to the string, so we know that in this case
+ # "None" corresponds to "really uninitialized".
+ self._chars = [None] * size
def setup_slice(self, longerlist, start, stop):
assert 0 <= start <= stop <= len(longerlist)
self._chars = longerlist[start:stop]
+ # slice the 'longerlist', which may also contain Nones
def getstrlen(self, _, mode):
if self._lengthbox is None:
@@ -126,42 +141,66 @@
return self._lengthbox
def getitem(self, index):
- return self._chars[index]
+ return self._chars[index] # may return None!
def setitem(self, index, charvalue):
assert isinstance(charvalue, optimizer.OptValue)
+ assert self._chars[index] is None, (
+ "setitem() on an already-initialized location")
self._chars[index] = charvalue
+ def is_completely_initialized(self):
+ for c in self._chars:
+ if c is None:
+ return False
+ return True
+
@specialize.arg(1)
def get_constant_string_spec(self, mode):
for c in self._chars:
- if c is optimizer.CVAL_UNINITIALIZED_ZERO or not c.is_constant():
+ if c is None or not c.is_constant():
return None
return mode.emptystr.join([mode.chr(c.box.getint())
for c in self._chars])
def string_copy_parts(self, string_optimizer, targetbox, offsetbox, mode):
- if not self.is_virtual() and targetbox is not self.box:
- lengthbox = self.getstrlen(string_optimizer, mode)
- srcbox = self.force_box(string_optimizer)
- return copy_str_content(string_optimizer, srcbox, targetbox,
- CONST_0, offsetbox, lengthbox, mode)
+ if not self.is_virtual() and not self.is_completely_initialized():
+ return VAbstractStringValue.string_copy_parts(
+ self, string_optimizer, targetbox, offsetbox, mode)
+ else:
+ return self.initialize_forced_string(string_optimizer, targetbox,
+ offsetbox, mode)
+
+ def initialize_forced_string(self, string_optimizer, targetbox,
+ offsetbox, mode):
for i in range(len(self._chars)):
- charbox = self._chars[i].force_box(string_optimizer)
- if not (isinstance(charbox, Const) and charbox.same_constant(CONST_0)):
- string_optimizer.emit_operation(ResOperation(mode.STRSETITEM, [targetbox,
- offsetbox,
- charbox],
- None))
+ assert isinstance(targetbox, BoxPtr) # ConstPtr never makes sense
+ charvalue = self.getitem(i)
+ if charvalue is not None:
+ charbox = charvalue.force_box(string_optimizer)
+ if not (isinstance(charbox, Const) and
+ charbox.same_constant(CONST_0)):
+ op = ResOperation(mode.STRSETITEM, [targetbox,
+ offsetbox,
+ charbox],
+ None)
+ string_optimizer.emit_operation(op)
offsetbox = _int_add(string_optimizer, offsetbox, CONST_1)
return offsetbox
def get_args_for_fail(self, modifier):
if self.box is None and not modifier.already_seen_virtual(self.keybox):
- charboxes = [value.get_key_box() for value in self._chars]
+ charboxes = []
+ for value in self._chars:
+ if value is not None:
+ box = value.get_key_box()
+ else:
+ box = None
+ charboxes.append(box)
modifier.register_virtual_fields(self.keybox, charboxes)
for value in self._chars:
- value.get_args_for_fail(modifier)
+ if value is not None:
+ value.get_args_for_fail(modifier)
def _make_virtual(self, modifier):
return modifier.make_vstrplain(self.mode is mode_unicode)
@@ -169,6 +208,7 @@
class VStringConcatValue(VAbstractStringValue):
"""The concatenation of two other strings."""
+ _attrs_ = ('left', 'right', 'lengthbox')
lengthbox = None # or the computed length
@@ -277,6 +317,7 @@
for i in range(lengthbox.value):
charbox = _strgetitem(string_optimizer, srcbox, srcoffsetbox, mode)
srcoffsetbox = _int_add(string_optimizer, srcoffsetbox, CONST_1)
+ assert isinstance(targetbox, BoxPtr) # ConstPtr never makes sense
string_optimizer.emit_operation(ResOperation(mode.STRSETITEM, [targetbox,
offsetbox,
charbox],
@@ -287,6 +328,7 @@
nextoffsetbox = _int_add(string_optimizer, offsetbox, lengthbox)
else:
nextoffsetbox = None
+ assert isinstance(targetbox, BoxPtr) # ConstPtr never makes sense
op = ResOperation(mode.COPYSTRCONTENT, [srcbox, targetbox,
srcoffsetbox, offsetbox,
lengthbox], None)
@@ -373,6 +415,7 @@
def optimize_STRSETITEM(self, op):
value = self.getvalue(op.getarg(0))
+ assert not value.is_constant() # strsetitem(ConstPtr) never makes sense
if value.is_virtual() and isinstance(value, VStringPlainValue):
indexbox = self.get_constant_box(op.getarg(1))
if indexbox is not None:
@@ -406,11 +449,20 @@
#
if isinstance(value, VStringPlainValue): # even if no longer virtual
if vindex.is_constant():
- res = value.getitem(vindex.box.getint())
- # If it is uninitialized we can't return it, it was set by a
- # COPYSTRCONTENT, not a STRSETITEM
- if res is not optimizer.CVAL_UNINITIALIZED_ZERO:
- return res
+ result = value.getitem(vindex.box.getint())
+ if result is not None:
+ return result
+ #
+ if isinstance(value, VStringConcatValue) and vindex.is_constant():
+ len1box = value.left.getstrlen(self, mode)
+ if isinstance(len1box, ConstInt):
+ index = vindex.box.getint()
+ len1 = len1box.getint()
+ if index < len1:
+ return self.strgetitem(value.left, vindex, mode)
+ else:
+ vindex = optimizer.ConstantValue(ConstInt(index - len1))
+ return self.strgetitem(value.right, vindex, mode)
#
resbox = _strgetitem(self, value.force_box(self), vindex.force_box(self), mode)
return self.getvalue(resbox)
@@ -432,6 +484,11 @@
def _optimize_COPYSTRCONTENT(self, op, mode):
# args: src dst srcstart dststart length
+ assert op.getarg(0).type == REF
+ assert op.getarg(1).type == REF
+ assert op.getarg(2).type == INT
+ assert op.getarg(3).type == INT
+ assert op.getarg(4).type == INT
src = self.getvalue(op.getarg(0))
dst = self.getvalue(op.getarg(1))
srcstart = self.getvalue(op.getarg(2))
@@ -473,6 +530,11 @@
optimize_CALL_PURE = optimize_CALL
+ def optimize_GUARD_NO_EXCEPTION(self, op):
+ if self.last_emitted_operation is REMOVED:
+ return
+ self.emit_operation(op)
+
def opt_call_str_STR2UNICODE(self, op):
# Constant-fold unicode("constant string").
# More generally, supporting non-constant but virtual cases is
@@ -487,6 +549,7 @@
except UnicodeDecodeError:
return False
self.make_constant(op.result, get_const_ptr_for_unicode(u))
+ self.last_emitted_operation = REMOVED
return True
def opt_call_stroruni_STR_CONCAT(self, op, mode):
@@ -503,19 +566,12 @@
vstart = self.getvalue(op.getarg(2))
vstop = self.getvalue(op.getarg(3))
#
- if (isinstance(vstr, VStringPlainValue) and vstart.is_constant()
- and vstop.is_constant()):
- # slicing with constant bounds of a VStringPlainValue, if any of
- # the characters is unitialized we don't do this special slice, we
- # do the regular copy contents.
- for i in range(vstart.box.getint(), vstop.box.getint()):
- if vstr.getitem(i) is optimizer.CVAL_UNINITIALIZED_ZERO:
- break
- else:
- value = self.make_vstring_plain(op.result, op, mode)
- value.setup_slice(vstr._chars, vstart.box.getint(),
- vstop.box.getint())
- return True
+ #if (isinstance(vstr, VStringPlainValue) and vstart.is_constant()
+ # and vstop.is_constant()):
+ # value = self.make_vstring_plain(op.result, op, mode)
+ # value.setup_slice(vstr._chars, vstart.box.getint(),
+ # vstop.box.getint())
+ # return True
#
vstr.ensure_nonnull()
lengthbox = _int_sub(self, vstop.force_box(self),
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
@@ -1345,10 +1345,8 @@
if effect == effectinfo.EF_LOOPINVARIANT:
return self.execute_varargs(rop.CALL_LOOPINVARIANT, allboxes,
descr, False, False)
- exc = (effect != effectinfo.EF_CANNOT_RAISE and
- effect != effectinfo.EF_ELIDABLE_CANNOT_RAISE)
- pure = (effect == effectinfo.EF_ELIDABLE_CAN_RAISE or
- effect == effectinfo.EF_ELIDABLE_CANNOT_RAISE)
+ exc = effectinfo.check_can_raise()
+ pure = effectinfo.check_is_elidable()
return self.execute_varargs(rop.CALL, allboxes, descr, exc, pure)
def do_residual_or_indirect_call(self, funcbox, calldescr, argboxes):
diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py
--- a/pypy/jit/metainterp/resoperation.py
+++ b/pypy/jit/metainterp/resoperation.py
@@ -90,7 +90,10 @@
return op
def __repr__(self):
- return self.repr()
+ try:
+ return self.repr()
+ except NotImplementedError:
+ return object.__repr__(self)
def repr(self, graytext=False):
# RPython-friendly version
@@ -458,6 +461,7 @@
'GETARRAYITEM_GC/2d',
'GETARRAYITEM_RAW/2d',
'GETINTERIORFIELD_GC/2d',
+ 'GETINTERIORFIELD_RAW/2d',
'GETFIELD_GC/1d',
'GETFIELD_RAW/1d',
'_MALLOC_FIRST',
@@ -476,6 +480,7 @@
'SETARRAYITEM_GC/3d',
'SETARRAYITEM_RAW/3d',
'SETINTERIORFIELD_GC/3d',
+ 'SETINTERIORFIELD_RAW/3d',
'SETFIELD_GC/2d',
'SETFIELD_RAW/2d',
'STRSETITEM/3',
diff --git a/pypy/jit/metainterp/resume.py b/pypy/jit/metainterp/resume.py
--- a/pypy/jit/metainterp/resume.py
+++ b/pypy/jit/metainterp/resume.py
@@ -93,12 +93,14 @@
TAGMASK = 3
+class TagOverflow(Exception):
+ pass
+
def tag(value, tagbits):
- if tagbits >> 2:
- raise ValueError
+ assert 0 <= tagbits <= 3
sx = value >> 13
if sx != 0 and sx != -1:
- raise ValueError
+ raise TagOverflow
return rffi.r_short(value<<2|tagbits)
def untag(value):
@@ -126,6 +128,7 @@
UNASSIGNED = tag(-1<<13, TAGBOX)
UNASSIGNEDVIRTUAL = tag(-1<<13, TAGVIRTUAL)
NULLREF = tag(-1, TAGCONST)
+UNINITIALIZED = tag(-2, TAGCONST) # used for uninitialized string characters
class ResumeDataLoopMemo(object):
@@ -152,7 +155,7 @@
return self._newconst(const)
try:
return tag(val, TAGINT)
- except ValueError:
+ except TagOverflow:
pass
tagged = self.large_ints.get(val, UNASSIGNED)
if not tagged_eq(tagged, UNASSIGNED):
@@ -428,8 +431,7 @@
fieldnum = self._gettagged(fieldbox)
# the index is limited to 2147483647 (64-bit machines only)
if itemindex > 2147483647:
- from pypy.jit.metainterp import compile
- compile.giveup()
+ raise TagOverflow
itemindex = rffi.cast(rffi.INT, itemindex)
#
rd_pendingfields[i].lldescr = lldescr
@@ -439,6 +441,8 @@
self.storage.rd_pendingfields = rd_pendingfields
def _gettagged(self, box):
+ if box is None:
+ return UNINITIALIZED
if isinstance(box, Const):
return self.memo.getconst(box)
else:
@@ -572,7 +576,9 @@
string = decoder.allocate_string(length)
decoder.virtuals_cache[index] = string
for i in range(length):
- decoder.string_setitem(string, i, self.fieldnums[i])
+ charnum = self.fieldnums[i]
+ if not tagged_eq(charnum, UNINITIALIZED):
+ decoder.string_setitem(string, i, charnum)
return string
def debug_prints(self):
@@ -625,7 +631,9 @@
string = decoder.allocate_unicode(length)
decoder.virtuals_cache[index] = string
for i in range(length):
- decoder.unicode_setitem(string, i, self.fieldnums[i])
+ charnum = self.fieldnums[i]
+ if not tagged_eq(charnum, UNINITIALIZED):
+ decoder.unicode_setitem(string, i, charnum)
return string
def debug_prints(self):
diff --git a/pypy/jit/metainterp/test/support.py b/pypy/jit/metainterp/test/support.py
--- a/pypy/jit/metainterp/test/support.py
+++ b/pypy/jit/metainterp/test/support.py
@@ -155,9 +155,9 @@
class JitMixin:
basic = True
- def check_loops(self, expected=None, everywhere=False, **check):
- get_stats().check_loops(expected=expected, everywhere=everywhere,
- **check)
+ def check_resops(self, expected=None, **check):
+ get_stats().check_resops(expected=expected, **check)
+
def check_loop_count(self, count):
"""NB. This is a hack; use check_tree_loop_count() or
check_enter_count() for the real thing.
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
@@ -14,7 +14,7 @@
from pypy.rlib.jit import (JitDriver, we_are_jitted, hint, dont_look_inside,
loop_invariant, elidable, promote, jit_debug, assert_green,
AssertGreenFailed, unroll_safe, current_trace_length, look_inside_iff,
- isconstant, isvirtual, promote_string)
+ isconstant, isvirtual, promote_string, set_param)
from pypy.rlib.rarithmetic import ovfcheck
from pypy.rpython.lltypesystem import lltype, llmemory, rffi
from pypy.rpython.ootypesystem import ootype
@@ -79,9 +79,8 @@
res = self.meta_interp(f, [6, 7])
assert res == 42
self.check_loop_count(1)
- self.check_loops({'guard_true': 1,
- 'int_add': 1, 'int_sub': 1, 'int_gt': 1,
- 'jump': 1})
+ self.check_resops({'jump': 2, 'int_gt': 2, 'int_add': 2, 'guard_true': 2, 'int_sub': 2})
+
if self.basic:
found = 0
for op in get_stats().loops[0]._all_operations():
@@ -108,7 +107,7 @@
res = self.meta_interp(f, [6, 7])
assert res == 1323
self.check_loop_count(1)
- self.check_loops(int_mul=1)
+ self.check_resops(int_mul=3)
def test_loop_variant_mul_ovf(self):
myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
@@ -125,7 +124,7 @@
res = self.meta_interp(f, [6, 7])
assert res == 1323
self.check_loop_count(1)
- self.check_loops(int_mul_ovf=1)
+ self.check_resops(int_mul_ovf=3)
def test_loop_invariant_mul1(self):
myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
@@ -140,9 +139,9 @@
res = self.meta_interp(f, [6, 7])
assert res == 252
self.check_loop_count(1)
- self.check_loops({'guard_true': 1,
- 'int_add': 1, 'int_sub': 1, 'int_gt': 1,
- 'jump': 1})
+ self.check_resops({'jump': 2, 'int_gt': 2, 'int_add': 2,
+ 'int_mul': 1, 'guard_true': 2, 'int_sub': 2})
+
def test_loop_invariant_mul_ovf(self):
myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
@@ -158,10 +157,10 @@
res = self.meta_interp(f, [6, 7])
assert res == 308
self.check_loop_count(1)
- self.check_loops({'guard_true': 1,
- 'int_add': 2, 'int_sub': 1, 'int_gt': 1,
- 'int_lshift': 1,
- 'jump': 1})
+ self.check_resops({'jump': 2, 'int_lshift': 2, 'int_gt': 2,
+ 'int_mul_ovf': 1, 'int_add': 4,
+ 'guard_true': 2, 'guard_no_overflow': 1,
+ 'int_sub': 2})
def test_loop_invariant_mul_bridge1(self):
myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
@@ -194,11 +193,9 @@
res = self.meta_interp(f, [6, 32])
assert res == 1167
self.check_loop_count(3)
- self.check_loops({'int_add': 3, 'int_lt': 2,
- 'int_sub': 2, 'guard_false': 1,
- 'jump': 2,
- 'int_gt': 1, 'guard_true': 2})
-
+ self.check_resops({'int_lt': 3, 'int_gt': 2, 'int_add': 5,
+ 'guard_true': 3, 'int_sub': 4, 'jump': 4,
+ 'int_mul': 2, 'guard_false': 2})
def test_loop_invariant_mul_bridge_maintaining2(self):
myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
@@ -216,10 +213,9 @@
res = self.meta_interp(f, [6, 32])
assert res == 1692
self.check_loop_count(3)
- self.check_loops({'int_add': 3, 'int_lt': 2,
- 'int_sub': 2, 'guard_false': 1,
- 'jump': 2,
- 'int_gt': 1, 'guard_true': 2})
+ self.check_resops({'int_lt': 3, 'int_gt': 2, 'int_add': 5,
+ 'guard_true': 3, 'int_sub': 4, 'jump': 4,
+ 'int_mul': 2, 'guard_false': 2})
def test_loop_invariant_mul_bridge_maintaining3(self):
myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x', 'm'])
@@ -237,10 +233,9 @@
res = self.meta_interp(f, [6, 32, 16])
assert res == 1692
self.check_loop_count(3)
- self.check_loops({'int_add': 2, 'int_lt': 1,
- 'int_sub': 2, 'guard_false': 1,
- 'jump': 2, 'int_mul': 1,
- 'int_gt': 2, 'guard_true': 2})
+ self.check_resops({'int_lt': 2, 'int_gt': 4, 'guard_false': 2,
+ 'guard_true': 4, 'int_sub': 4, 'jump': 4,
+ 'int_mul': 3, 'int_add': 4})
def test_loop_invariant_intbox(self):
myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
@@ -261,9 +256,9 @@
res = self.meta_interp(f, [6, 7])
assert res == 252
self.check_loop_count(1)
- self.check_loops({'guard_true': 1,
- 'int_add': 1, 'int_sub': 1, 'int_gt': 1,
- 'jump': 1})
+ self.check_resops({'jump': 2, 'int_gt': 2, 'int_add': 2,
+ 'getfield_gc_pure': 1, 'int_mul': 1,
+ 'guard_true': 2, 'int_sub': 2})
def test_loops_are_transient(self):
import gc, weakref
@@ -381,7 +376,7 @@
assert res == 0
# CALL_PURE is recorded in the history, but turned into a CALL
# by optimizeopt.py
- self.check_loops(int_sub=0, call=1, call_pure=0)
+ self.check_resops(call_pure=0, call=2, int_sub=0)
def test_constfold_call_elidable(self):
myjitdriver = JitDriver(greens = ['m'], reds = ['n'])
@@ -397,7 +392,7 @@
res = self.meta_interp(f, [21, 5])
assert res == -1
# the CALL_PURE is constant-folded away by optimizeopt.py
- self.check_loops(int_sub=1, call=0, call_pure=0)
+ self.check_resops(call_pure=0, call=0, int_sub=2)
def test_constfold_call_elidable_2(self):
myjitdriver = JitDriver(greens = ['m'], reds = ['n'])
@@ -417,7 +412,7 @@
res = self.meta_interp(f, [21, 5])
assert res == -1
# the CALL_PURE is constant-folded away by optimizeopt.py
- self.check_loops(int_sub=1, call=0, call_pure=0)
+ self.check_resops(call_pure=0, call=0, int_sub=2)
def test_elidable_function_returning_object(self):
myjitdriver = JitDriver(greens = ['m'], reds = ['n'])
@@ -442,7 +437,7 @@
res = self.meta_interp(f, [21, 5])
assert res == -1
# the CALL_PURE is constant-folded away by optimizeopt.py
- self.check_loops(int_sub=1, call=0, call_pure=0, getfield_gc=0)
+ self.check_resops(call_pure=0, call=0, getfield_gc=1, int_sub=2)
def test_elidable_raising(self):
myjitdriver = JitDriver(greens = ['m'], reds = ['n'])
@@ -463,12 +458,12 @@
res = self.meta_interp(f, [22, 6])
assert res == -3
# the CALL_PURE is constant-folded away during tracing
- self.check_loops(int_sub=1, call=0, call_pure=0)
+ self.check_resops(call_pure=0, call=0, int_sub=2)
#
res = self.meta_interp(f, [22, -5])
assert res == 0
# raises: becomes CALL and is not constant-folded away
- self.check_loops(int_sub=1, call=1, call_pure=0)
+ self.check_resops(call_pure=0, call=2, int_sub=2)
def test_elidable_raising_2(self):
myjitdriver = JitDriver(greens = ['m'], reds = ['n'])
@@ -489,12 +484,12 @@
res = self.meta_interp(f, [22, 6])
assert res == -3
# the CALL_PURE is constant-folded away by optimizeopt.py
- self.check_loops(int_sub=1, call=0, call_pure=0)
+ self.check_resops(call_pure=0, call=0, int_sub=2)
#
res = self.meta_interp(f, [22, -5])
assert res == 0
# raises: becomes CALL and is not constant-folded away
- self.check_loops(int_sub=1, call=1, call_pure=0)
+ self.check_resops(call_pure=0, call=2, int_sub=2)
def test_constant_across_mp(self):
myjitdriver = JitDriver(greens = [], reds = ['n'])
@@ -533,7 +528,7 @@
policy = StopAtXPolicy(externfn)
res = self.meta_interp(f, [31], policy=policy)
assert res == 42
- self.check_loops(int_mul=1, int_mod=0)
+ self.check_resops(int_mul=2, int_mod=0)
def test_we_are_jitted(self):
myjitdriver = JitDriver(greens = [], reds = ['y'])
@@ -835,7 +830,7 @@
return n
res = self.meta_interp(f, [20, 1, 2])
assert res == 0
- self.check_loops(call=0)
+ self.check_resops(call=0)
def test_abs(self):
myjitdriver = JitDriver(greens = [], reds = ['i', 't'])
@@ -865,9 +860,8 @@
res = self.meta_interp(f, [6, 7])
assert res == 42.0
self.check_loop_count(1)
- self.check_loops({'guard_true': 1,
- 'float_add': 1, 'float_sub': 1, 'float_gt': 1,
- 'jump': 1})
+ self.check_resops({'jump': 2, 'float_gt': 2, 'float_add': 2,
+ 'float_sub': 2, 'guard_true': 2})
def test_print(self):
myjitdriver = JitDriver(greens = [], reds = ['n'])
@@ -1038,7 +1032,7 @@
return x
res = self.meta_interp(f, [20], enable_opts='')
assert res == f(20)
- self.check_loops(call=0)
+ self.check_resops(call=0)
def test_zerodivisionerror(self):
# test the case of exception-raising operation that is not delegated
@@ -1256,15 +1250,18 @@
n -= 1
x += n
return x
- def f(n, threshold):
- myjitdriver.set_param('threshold', threshold)
+ def f(n, threshold, arg):
+ if arg:
+ set_param(myjitdriver, 'threshold', threshold)
+ else:
+ set_param(None, 'threshold', threshold)
return g(n)
- res = self.meta_interp(f, [10, 3])
+ res = self.meta_interp(f, [10, 3, 1])
assert res == 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0
self.check_tree_loop_count(2)
- res = self.meta_interp(f, [10, 13])
+ res = self.meta_interp(f, [10, 13, 0])
assert res == 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0
self.check_tree_loop_count(0)
@@ -1348,7 +1345,7 @@
res = self.meta_interp(f, [6, 7])
assert res == 42
self.check_loop_count(1)
- self.check_loops(call=1)
+ self.check_resops(call=2)
def test_merge_guardclass_guardvalue(self):
from pypy.rlib.objectmodel import instantiate
@@ -1375,8 +1372,7 @@
return x
res = self.meta_interp(f, [299], listops=True)
assert res == f(299)
- self.check_loops(guard_class=0, guard_value=3)
- self.check_loops(guard_class=0, guard_value=6, everywhere=True)
+ self.check_resops(guard_class=0, guard_value=6)
def test_merge_guardnonnull_guardclass(self):
from pypy.rlib.objectmodel import instantiate
@@ -1404,11 +1400,9 @@
return x
res = self.meta_interp(f, [299], listops=True)
assert res == f(299)
- self.check_loops(guard_class=0, guard_nonnull=2,
- guard_nonnull_class=2, guard_isnull=1)
- self.check_loops(guard_class=0, guard_nonnull=4,
- guard_nonnull_class=4, guard_isnull=2,
- everywhere=True)
+ self.check_resops(guard_class=0, guard_nonnull=4,
+ guard_nonnull_class=4, guard_isnull=2)
+
def test_merge_guardnonnull_guardvalue(self):
from pypy.rlib.objectmodel import instantiate
@@ -1435,11 +1429,9 @@
return x
res = self.meta_interp(f, [299], listops=True)
assert res == f(299)
- self.check_loops(guard_class=0, guard_nonnull=2, guard_value=2,
- guard_nonnull_class=0, guard_isnull=1)
- self.check_loops(guard_class=0, guard_nonnull=4, guard_value=4,
- guard_nonnull_class=0, guard_isnull=2,
- everywhere=True)
+ self.check_resops(guard_value=4, guard_class=0, guard_nonnull=4,
+ guard_nonnull_class=0, guard_isnull=2)
+
def test_merge_guardnonnull_guardvalue_2(self):
from pypy.rlib.objectmodel import instantiate
@@ -1466,11 +1458,9 @@
return x
res = self.meta_interp(f, [299], listops=True)
assert res == f(299)
- self.check_loops(guard_class=0, guard_nonnull=2, guard_value=2,
- guard_nonnull_class=0, guard_isnull=1)
- self.check_loops(guard_class=0, guard_nonnull=4, guard_value=4,
- guard_nonnull_class=0, guard_isnull=2,
- everywhere=True)
+ self.check_resops(guard_value=4, guard_class=0, guard_nonnull=4,
+ guard_nonnull_class=0, guard_isnull=2)
+
def test_merge_guardnonnull_guardclass_guardvalue(self):
from pypy.rlib.objectmodel import instantiate
@@ -1500,11 +1490,9 @@
return x
res = self.meta_interp(f, [399], listops=True)
assert res == f(399)
- self.check_loops(guard_class=0, guard_nonnull=3, guard_value=3,
- guard_nonnull_class=0, guard_isnull=1)
- self.check_loops(guard_class=0, guard_nonnull=6, guard_value=6,
- guard_nonnull_class=0, guard_isnull=2,
- everywhere=True)
+ self.check_resops(guard_class=0, guard_nonnull=6, guard_value=6,
+ guard_nonnull_class=0, guard_isnull=2)
+
def test_residual_call_doesnt_lose_info(self):
myjitdriver = JitDriver(greens = [], reds = ['x', 'y', 'l'])
@@ -1530,8 +1518,7 @@
y.v = g(y.v) - y.v/y.v + lc/l[0] - 1
return y.v
res = self.meta_interp(f, [20], listops=True)
- self.check_loops(getfield_gc=0, getarrayitem_gc=0)
- self.check_loops(getfield_gc=1, getarrayitem_gc=0, everywhere=True)
+ self.check_resops(getarrayitem_gc=0, getfield_gc=1)
def test_guard_isnull_nonnull(self):
myjitdriver = JitDriver(greens = [], reds = ['x', 'res'])
@@ -1559,7 +1546,7 @@
return res
res = self.meta_interp(f, [21])
assert res == 42
- self.check_loops(guard_nonnull=1, guard_isnull=1)
+ self.check_resops(guard_nonnull=2, guard_isnull=2)
def test_loop_invariant1(self):
myjitdriver = JitDriver(greens = [], reds = ['x', 'res'])
@@ -1586,8 +1573,7 @@
return res
res = self.meta_interp(g, [21])
assert res == 3 * 21
- self.check_loops(call=0)
- self.check_loops(call=1, everywhere=True)
+ self.check_resops(call=1)
def test_bug_optimizeopt_mutates_ops(self):
myjitdriver = JitDriver(greens = [], reds = ['x', 'res', 'const', 'a'])
@@ -1707,7 +1693,7 @@
return x
res = self.meta_interp(f, [8])
assert res == 0
- self.check_loops(jit_debug=2)
+ self.check_resops(jit_debug=4)
def test_assert_green(self):
def f(x, promote_flag):
@@ -1749,9 +1735,10 @@
res = self.meta_interp(g, [6, 7])
assert res == 6*8 + 6**8
self.check_loop_count(5)
- self.check_loops({'guard_true': 2,
- 'int_add': 1, 'int_mul': 1, 'int_sub': 2,
- 'int_gt': 2, 'jump': 2})
+ self.check_resops({'guard_class': 2, 'int_gt': 4,
+ 'getfield_gc': 4, 'guard_true': 4,
+ 'int_sub': 4, 'jump': 4, 'int_mul': 2,
+ 'int_add': 2})
def test_multiple_specialied_versions_array(self):
myjitdriver = JitDriver(greens = [], reds = ['idx', 'y', 'x', 'res',
@@ -1792,7 +1779,7 @@
res = self.meta_interp(g, [6, 14])
assert res == g(6, 14)
self.check_loop_count(9)
- self.check_loops(getarrayitem_gc=8, everywhere=True)
+ self.check_resops(getarrayitem_gc=8)
def test_multiple_specialied_versions_bridge(self):
myjitdriver = JitDriver(greens = [], reds = ['y', 'x', 'z', 'res'])
@@ -1980,8 +1967,8 @@
res = self.meta_interp(g, [3, 23])
assert res == 7068153
self.check_loop_count(7)
- self.check_loops(guard_true=4, guard_class=0, int_add=2, int_mul=2,
- guard_false=2)
+ self.check_resops(guard_true=6, guard_class=2, int_mul=3,
+ int_add=3, guard_false=3)
def test_dont_trace_every_iteration(self):
myjitdriver = JitDriver(greens = [], reds = ['a', 'b', 'i', 'sa'])
@@ -2225,27 +2212,27 @@
return sa
assert self.meta_interp(f1, [5, 5]) == 50
- self.check_loops(int_rshift=0, everywhere=True)
+ self.check_resops(int_rshift=0)
for f in (f1, f2):
assert self.meta_interp(f, [5, 6]) == 50
- self.check_loops(int_rshift=3, everywhere=True)
+ self.check_resops(int_rshift=3)
assert self.meta_interp(f, [10, 5]) == 100
- self.check_loops(int_rshift=3, everywhere=True)
+ self.check_resops(int_rshift=3)
assert self.meta_interp(f, [10, 6]) == 100
- self.check_loops(int_rshift=3, everywhere=True)
+ self.check_resops(int_rshift=3)
assert self.meta_interp(f, [5, 31]) == 0
- self.check_loops(int_rshift=3, everywhere=True)
+ self.check_resops(int_rshift=3)
bigval = 1
while (bigval << 3).__class__ is int:
bigval = bigval << 1
assert self.meta_interp(f, [bigval, 5]) == 0
- self.check_loops(int_rshift=3, everywhere=True)
+ self.check_resops(int_rshift=3)
def test_overflowing_shift_neg(self):
myjitdriver = JitDriver(greens = [], reds = ['a', 'b', 'n', 'sa'])
@@ -2270,27 +2257,27 @@
return sa
assert self.meta_interp(f1, [-5, 5]) == -50
- self.check_loops(int_rshift=0, everywhere=True)
+ self.check_resops(int_rshift=0)
for f in (f1, f2):
assert self.meta_interp(f, [-5, 6]) == -50
- self.check_loops(int_rshift=3, everywhere=True)
+ self.check_resops(int_rshift=3)
assert self.meta_interp(f, [-10, 5]) == -100
- self.check_loops(int_rshift=3, everywhere=True)
+ self.check_resops(int_rshift=3)
assert self.meta_interp(f, [-10, 6]) == -100
- self.check_loops(int_rshift=3, everywhere=True)
+ self.check_resops(int_rshift=3)
assert self.meta_interp(f, [-5, 31]) == 0
- self.check_loops(int_rshift=3, everywhere=True)
+ self.check_resops(int_rshift=3)
bigval = 1
while (bigval << 3).__class__ is int:
bigval = bigval << 1
assert self.meta_interp(f, [bigval, 5]) == 0
- self.check_loops(int_rshift=3, everywhere=True)
+ self.check_resops(int_rshift=3)
def test_pure_op_not_to_be_propagated(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'sa'])
@@ -2328,8 +2315,8 @@
get_printable_location=get_printable_location)
bytecode = "0j10jc20a3"
def f():
- myjitdriver.set_param('threshold', 7)
- myjitdriver.set_param('trace_eagerness', 1)
+ set_param(myjitdriver, 'threshold', 7)
+ set_param(myjitdriver, 'trace_eagerness', 1)
i = j = c = a = 1
while True:
myjitdriver.jit_merge_point(i=i, j=j, c=c, a=a)
@@ -2430,8 +2417,7 @@
if counter > 10:
return 7
assert self.meta_interp(build, []) == 7
- self.check_loops(getfield_gc_pure=0)
- self.check_loops(getfield_gc_pure=2, everywhere=True)
+ self.check_resops(getfield_gc_pure=2)
def test_args_becomming_equal(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'a', 'b'])
@@ -2564,7 +2550,7 @@
i += 1
return sa
assert self.meta_interp(f, [20]) == f(20)
- self.check_loops(int_gt=1, int_lt=2, int_ge=0, int_le=0)
+ self.check_resops(int_lt=4, int_le=0, int_ge=0, int_gt=2)
def test_intbounds_not_generalized1(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa'])
@@ -2581,7 +2567,8 @@
i += 1
return sa
assert self.meta_interp(f, [20]) == f(20)
- self.check_loops(int_gt=1, int_lt=3, int_ge=2, int_le=1)
+ self.check_resops(int_lt=6, int_le=2, int_ge=4, int_gt=3)
+
def test_intbounds_not_generalized2(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'node'])
@@ -2601,13 +2588,13 @@
i += 1
return sa
assert self.meta_interp(f, [20]) == f(20)
- self.check_loops(int_gt=1, int_lt=2, int_ge=1, int_le=1)
+ self.check_resops(int_lt=4, int_le=3, int_ge=3, int_gt=2)
def test_retrace_limit1(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'a'])
def f(n, limit):
- myjitdriver.set_param('retrace_limit', limit)
+ set_param(myjitdriver, 'retrace_limit', limit)
sa = i = a = 0
while i < n:
myjitdriver.jit_merge_point(n=n, i=i, sa=sa, a=a)
@@ -2625,8 +2612,8 @@
myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'a'])
def f(n, limit):
- myjitdriver.set_param('retrace_limit', 3)
- myjitdriver.set_param('max_retrace_guards', limit)
+ set_param(myjitdriver, 'retrace_limit', 3)
+ set_param(myjitdriver, 'max_retrace_guards', limit)
sa = i = a = 0
while i < n:
myjitdriver.jit_merge_point(n=n, i=i, sa=sa, a=a)
@@ -2645,7 +2632,7 @@
myjitdriver = JitDriver(greens = [], reds = ['n', 'i', 'sa', 'a',
'node'])
def f(n, limit):
- myjitdriver.set_param('retrace_limit', limit)
+ set_param(myjitdriver, 'retrace_limit', limit)
sa = i = a = 0
node = [1, 2, 3]
node[1] = n
@@ -2668,10 +2655,10 @@
myjitdriver = JitDriver(greens = ['pc'], reds = ['n', 'i', 'sa'])
bytecode = "0+sI0+SI"
def f(n):
- myjitdriver.set_param('threshold', 3)
- myjitdriver.set_param('trace_eagerness', 1)
- myjitdriver.set_param('retrace_limit', 5)
- myjitdriver.set_param('function_threshold', -1)
+ set_param(None, 'threshold', 3)
+ set_param(None, 'trace_eagerness', 1)
+ set_param(None, 'retrace_limit', 5)
+ set_param(None, 'function_threshold', -1)
pc = sa = i = 0
while pc < len(bytecode):
myjitdriver.jit_merge_point(pc=pc, n=n, sa=sa, i=i)
@@ -2728,9 +2715,9 @@
myjitdriver = JitDriver(greens = ['pc'], reds = ['n', 'a', 'i', 'j', 'sa'])
bytecode = "ij+Jj+JI"
def f(n, a):
- myjitdriver.set_param('threshold', 5)
- myjitdriver.set_param('trace_eagerness', 1)
- myjitdriver.set_param('retrace_limit', 2)
+ set_param(None, 'threshold', 5)
+ set_param(None, 'trace_eagerness', 1)
+ set_param(None, 'retrace_limit', 2)
pc = sa = i = j = 0
while pc < len(bytecode):
myjitdriver.jit_merge_point(pc=pc, n=n, sa=sa, i=i, j=j, a=a)
@@ -2793,8 +2780,8 @@
return B(self.val + 1)
myjitdriver = JitDriver(greens = [], reds = ['sa', 'a'])
def f():
- myjitdriver.set_param('threshold', 3)
- myjitdriver.set_param('trace_eagerness', 2)
+ set_param(None, 'threshold', 3)
+ set_param(None, 'trace_eagerness', 2)
a = A(0)
sa = 0
while a.val < 8:
@@ -2824,8 +2811,8 @@
return B(self.val + 1)
myjitdriver = JitDriver(greens = [], reds = ['sa', 'b', 'a'])
def f(b):
- myjitdriver.set_param('threshold', 6)
- myjitdriver.set_param('trace_eagerness', 4)
+ set_param(None, 'threshold', 6)
+ set_param(None, 'trace_eagerness', 4)
a = A(0)
sa = 0
while a.val < 15:
@@ -2855,17 +2842,17 @@
return a[0].intvalue
res = self.meta_interp(f, [100])
assert res == -2
- #self.check_loops(getarrayitem_gc=0, setarrayitem_gc=0) -- xxx?
+ self.check_resops(setarrayitem_gc=2, getarrayitem_gc=1)
def test_retrace_ending_up_retracing_another_loop(self):
myjitdriver = JitDriver(greens = ['pc'], reds = ['n', 'i', 'sa'])
bytecode = "0+sI0+SI"
def f(n):
- myjitdriver.set_param('threshold', 3)
- myjitdriver.set_param('trace_eagerness', 1)
- myjitdriver.set_param('retrace_limit', 5)
- myjitdriver.set_param('function_threshold', -1)
+ set_param(None, 'threshold', 3)
+ set_param(None, 'trace_eagerness', 1)
+ set_param(None, 'retrace_limit', 5)
+ set_param(None, 'function_threshold', -1)
pc = sa = i = 0
while pc < len(bytecode):
myjitdriver.jit_merge_point(pc=pc, n=n, sa=sa, i=i)
@@ -2955,7 +2942,7 @@
i += 1
res = self.meta_interp(f, [32])
assert res == f(32)
- self.check_loops(arraylen_gc=2)
+ self.check_resops(arraylen_gc=3)
def test_ulonglong_mod(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'sa', 'i'])
@@ -3142,9 +3129,9 @@
a = A(a.i + 1)
self.meta_interp(f, [])
- self.check_loops(new_with_vtable=0)
+ self.check_resops(new_with_vtable=0)
self.meta_interp(f, [], enable_opts='')
- self.check_loops(new_with_vtable=1)
+ self.check_resops(new_with_vtable=1)
def test_two_loopinvariant_arrays1(self):
from pypy.rpython.lltypesystem import lltype, llmemory, rffi
@@ -3236,7 +3223,7 @@
return sa
res = self.meta_interp(f, [32])
assert res == f(32)
- self.check_loops(arraylen_gc=2, everywhere=True)
+ self.check_resops(arraylen_gc=2)
def test_release_gil_flush_heap_cache(self):
if sys.platform == "win32":
@@ -3273,7 +3260,7 @@
lock.release()
return n
res = self.meta_interp(f, [10, 1])
- self.check_loops(getfield_gc=2)
+ self.check_resops(getfield_gc=4)
assert res == f(10, 1)
def test_jit_merge_point_with_raw_pointer(self):
@@ -3337,10 +3324,10 @@
res = self.meta_interp(main, [0, 10, 2], enable_opts='')
assert res == main(0, 10, 2)
- self.check_loops(call=1)
+ self.check_resops(call=1)
res = self.meta_interp(main, [1, 10, 2], enable_opts='')
assert res == main(1, 10, 2)
- self.check_loops(call=0)
+ self.check_resops(call=0)
def test_look_inside_iff_virtual(self):
# There's no good reason for this to be look_inside_iff, but it's a test!
@@ -3365,10 +3352,10 @@
i += f(A(2), n)
res = self.meta_interp(main, [0], enable_opts='')
assert res == main(0)
- self.check_loops(call=1, getfield_gc=0)
+ self.check_resops(call=1, getfield_gc=0)
res = self.meta_interp(main, [1], enable_opts='')
assert res == main(1)
- self.check_loops(call=0, getfield_gc=0)
+ self.check_resops(call=0, getfield_gc=0)
def test_reuse_elidable_result(self):
driver = JitDriver(reds=['n', 's'], greens = [])
@@ -3381,10 +3368,9 @@
return s
res = self.meta_interp(main, [10])
assert res == main(10)
- self.check_loops({
- 'call': 1, 'guard_no_exception': 1, 'guard_true': 1, 'int_add': 2,
- 'int_gt': 1, 'int_sub': 1, 'strlen': 1, 'jump': 1,
- })
+ self.check_resops({'int_gt': 2, 'strlen': 2, 'guard_true': 2,
+ 'int_sub': 2, 'jump': 2, 'call': 2,
+ 'guard_no_exception': 2, 'int_add': 4})
def test_look_inside_iff_const_getarrayitem_gc_pure(self):
driver = JitDriver(greens=['unroll'], reds=['s', 'n'])
@@ -3416,10 +3402,10 @@
res = self.meta_interp(main, [0, 10])
assert res == main(0, 10)
# 2 calls, one for f() and one for char_mul
- self.check_loops(call=2)
+ self.check_resops(call=4)
res = self.meta_interp(main, [1, 10])
assert res == main(1, 10)
- self.check_loops(call=0)
+ self.check_resops(call=0)
def test_setarrayitem_followed_by_arraycopy(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'sa', 'x', 'y'])
@@ -3520,7 +3506,8 @@
res = self.meta_interp(f, [10])
assert res == 0
- self.check_loops({"int_sub": 1, "int_gt": 1, "guard_true": 1, "jump": 1})
+ self.check_resops({'jump': 2, 'guard_true': 2, 'int_gt': 2,
+ 'int_sub': 2})
def test_virtual_opaque_ptr(self):
myjitdriver = JitDriver(greens = [], reds = ["n"])
@@ -3539,7 +3526,9 @@
return n
res = self.meta_interp(f, [10])
assert res == 0
- self.check_loops({"int_sub": 1, "int_gt": 1, "guard_true": 1, "jump": 1})
+ self.check_resops({'jump': 2, 'guard_true': 2, 'int_gt': 2,
+ 'int_sub': 2})
+
def test_virtual_opaque_dict(self):
myjitdriver = JitDriver(greens = [], reds = ["n"])
@@ -3559,7 +3548,10 @@
return n
res = self.meta_interp(f, [10])
assert res == 0
- self.check_loops({"int_sub": 1, "int_gt": 1, "guard_true": 1, "jump": 1})
+ self.check_resops({'int_gt': 2, 'getfield_gc': 1, 'int_eq': 1,
+ 'guard_true': 2, 'int_sub': 2, 'jump': 2,
+ 'guard_false': 1})
+
def test_convert_from_SmallFunctionSetPBCRepr_to_FunctionsPBCRepr(self):
f1 = lambda n: n+1
@@ -3678,3 +3670,16 @@
assert x == -42
x = self.interp_operations(f, [1000, 1], translationoptions=topt)
assert x == 999
+
+ def test_ll_arraycopy(self):
+ from pypy.rlib import rgc
+ A = lltype.GcArray(lltype.Char)
+ a = lltype.malloc(A, 10)
+ for i in range(10): a[i] = chr(i)
+ b = lltype.malloc(A, 10)
+ #
+ def f(c, d, e):
+ rgc.ll_arraycopy(a, b, c, d, e)
+ return 42
+ self.interp_operations(f, [1, 2, 3])
+ self.check_operations_history(call=1, guard_no_exception=0)
diff --git a/pypy/jit/metainterp/test/test_del.py b/pypy/jit/metainterp/test/test_del.py
--- a/pypy/jit/metainterp/test/test_del.py
+++ b/pypy/jit/metainterp/test/test_del.py
@@ -20,12 +20,12 @@
n -= 1
return 42
self.meta_interp(f, [20])
- self.check_loops({'call': 2, # calls to a helper function
- 'guard_no_exception': 2, # follows the calls
- 'int_sub': 1,
- 'int_gt': 1,
- 'guard_true': 1,
- 'jump': 1})
+ self.check_resops({'call': 4, # calls to a helper function
+ 'guard_no_exception': 4, # follows the calls
+ 'int_sub': 2,
+ 'int_gt': 2,
+ 'guard_true': 2,
+ 'jump': 2})
def test_class_of_allocated(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'x'])
@@ -78,7 +78,7 @@
return 1
res = self.meta_interp(f, [20], enable_opts='')
assert res == 1
- self.check_loops(call=1) # for the case B(), but not for the case A()
+ self.check_resops(call=1) # for the case B(), but not for the case A()
class TestLLtype(DelTests, LLJitMixin):
@@ -103,7 +103,7 @@
break
return 42
self.meta_interp(f, [20])
- self.check_loops(getfield_raw=1, setfield_raw=1, call=0, call_pure=0)
+ self.check_resops(call_pure=0, setfield_raw=2, call=0, getfield_raw=2)
class TestOOtype(DelTests, OOJitMixin):
def setup_class(cls):
diff --git a/pypy/jit/metainterp/test/test_dict.py b/pypy/jit/metainterp/test/test_dict.py
--- a/pypy/jit/metainterp/test/test_dict.py
+++ b/pypy/jit/metainterp/test/test_dict.py
@@ -91,7 +91,7 @@
res1 = f(100)
res2 = self.meta_interp(f, [100], listops=True)
assert res1 == res2
- self.check_loops(int_mod=1) # the hash was traced and eq, but cached
+ self.check_resops(int_mod=2) # the hash was traced and eq, but cached
def test_dict_setdefault(self):
myjitdriver = JitDriver(greens = [], reds = ['total', 'dct'])
@@ -107,7 +107,7 @@
assert f(100) == 50
res = self.meta_interp(f, [100], listops=True)
assert res == 50
- self.check_loops(new=0, new_with_vtable=0)
+ self.check_resops(new=0, new_with_vtable=0)
def test_dict_as_counter(self):
myjitdriver = JitDriver(greens = [], reds = ['total', 'dct'])
@@ -128,7 +128,7 @@
assert f(100) == 50
res = self.meta_interp(f, [100], listops=True)
assert res == 50
- self.check_loops(int_mod=1) # key + eq, but cached
+ self.check_resops(int_mod=2) # key + eq, but cached
def test_repeated_lookup(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'd'])
@@ -153,12 +153,13 @@
res = self.meta_interp(f, [100], listops=True)
assert res == f(50)
- self.check_loops({"call": 5, "getfield_gc": 1, "getinteriorfield_gc": 1,
- "guard_false": 1, "guard_no_exception": 4,
- "guard_true": 1, "int_and": 1, "int_gt": 1,
- "int_is_true": 1, "int_sub": 1, "jump": 1,
- "new_with_vtable": 1, "new": 1, "new_array": 1,
- "setfield_gc": 3, })
+ self.check_resops({'new_array': 2, 'getfield_gc': 2,
+ 'guard_true': 2, 'jump': 2,
+ 'new_with_vtable': 2, 'getinteriorfield_gc': 2,
+ 'setfield_gc': 6, 'int_gt': 2, 'int_sub': 2,
+ 'call': 10, 'int_and': 2,
+ 'guard_no_exception': 8, 'new': 2,
+ 'guard_false': 2, 'int_is_true': 2})
class TestOOtype(DictTests, OOJitMixin):
diff --git a/pypy/jit/metainterp/test/test_exception.py b/pypy/jit/metainterp/test/test_exception.py
--- a/pypy/jit/metainterp/test/test_exception.py
+++ b/pypy/jit/metainterp/test/test_exception.py
@@ -35,10 +35,8 @@
return n
res = self.meta_interp(f, [10])
assert res == 0
- self.check_loops({'jump': 1,
- 'int_gt': 1, 'guard_true': 1,
- 'int_sub': 1})
-
+ self.check_resops({'jump': 2, 'guard_true': 2,
+ 'int_gt': 2, 'int_sub': 2})
def test_bridge_from_guard_exception(self):
myjitdriver = JitDriver(greens = [], reds = ['n'])
diff --git a/pypy/jit/metainterp/test/test_fficall.py b/pypy/jit/metainterp/test/test_fficall.py
--- a/pypy/jit/metainterp/test/test_fficall.py
+++ b/pypy/jit/metainterp/test/test_fficall.py
@@ -1,19 +1,18 @@
+import py
-import py
+from pypy.jit.metainterp.test.support import LLJitMixin
+from pypy.rlib.jit import JitDriver, promote, dont_look_inside
+from pypy.rlib.libffi import (ArgChain, IS_32_BIT, array_getitem, array_setitem,
+ types)
+from pypy.rlib.objectmodel import specialize
from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong
-from pypy.rlib.jit import JitDriver, promote, dont_look_inside
+from pypy.rlib.test.test_libffi import TestLibffiCall as _TestLibffiCall
from pypy.rlib.unroll import unrolling_iterable
-from pypy.rlib.libffi import ArgChain
-from pypy.rlib.libffi import IS_32_BIT
-from pypy.rlib.test.test_libffi import TestLibffiCall as _TestLibffiCall
from pypy.rpython.lltypesystem import lltype, rffi
-from pypy.rlib.objectmodel import specialize
from pypy.tool.sourcetools import func_with_new_name
-from pypy.jit.metainterp.test.support import LLJitMixin
-class TestFfiCall(LLJitMixin, _TestLibffiCall):
- supports_all = False # supports_{floats,longlong,singlefloats}
+class FfiCallTests(_TestLibffiCall):
# ===> ../../../rlib/test/test_libffi.py
def call(self, funcspec, args, RESULT, is_struct=False, jitif=[]):
@@ -68,23 +67,23 @@
'byval': False}
supported = all(d[check] for check in jitif)
if supported:
- self.check_loops(
- call_release_gil=1, # a CALL_RELEASE_GIL, and no other CALLs
+ self.check_resops(
+ call_release_gil=2, # a CALL_RELEASE_GIL, and no other CALLs
call=0,
call_may_force=0,
- guard_no_exception=1,
- guard_not_forced=1,
- int_add=1,
- int_lt=1,
- guard_true=1,
- jump=1)
+ guard_no_exception=2,
+ guard_not_forced=2,
+ int_add=2,
+ int_lt=2,
+ guard_true=2,
+ jump=2)
else:
- self.check_loops(
+ self.check_resops(
call_release_gil=0, # no CALL_RELEASE_GIL
- int_add=1,
- int_lt=1,
- guard_true=1,
- jump=1)
+ int_add=2,
+ int_lt=2,
+ guard_true=2,
+ jump=2)
return res
def test_byval_result(self):
@@ -92,6 +91,67 @@
test_byval_result.__doc__ = _TestLibffiCall.test_byval_result.__doc__
test_byval_result.dont_track_allocations = True
+class FfiLookupTests(object):
+ def test_array_fields(self):
+ myjitdriver = JitDriver(
+ greens = [],
+ reds = ["n", "i", "points", "result_point"],
+ )
-class TestFfiCallSupportAll(TestFfiCall):
+ POINT = lltype.Struct("POINT",
+ ("x", lltype.Signed),
+ ("y", lltype.Signed),
+ )
+ def f(points, result_point, n):
+ i = 0
+ while i < n:
+ myjitdriver.jit_merge_point(i=i, points=points, n=n,
+ result_point=result_point)
+ x = array_getitem(
+ types.slong, rffi.sizeof(lltype.Signed) * 2, points, i, 0
+ )
+ y = array_getitem(
+ types.slong, rffi.sizeof(lltype.Signed) * 2, points, i, rffi.sizeof(lltype.Signed)
+ )
+
+ cur_x = array_getitem(
+ types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, 0
+ )
+ cur_y = array_getitem(
+ types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, rffi.sizeof(lltype.Signed)
+ )
+
+ array_setitem(
+ types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, 0, cur_x + x
+ )
+ array_setitem(
+ types.slong, rffi.sizeof(lltype.Signed) * 2, result_point, 0, rffi.sizeof(lltype.Signed), cur_y + y
+ )
+ i += 1
+
+ def main(n):
+ with lltype.scoped_alloc(rffi.CArray(POINT), n) as points:
+ with lltype.scoped_alloc(rffi.CArray(POINT), 1) as result_point:
+ for i in xrange(n):
+ points[i].x = i * 2
+ points[i].y = i * 2 + 1
+ points = rffi.cast(rffi.CArrayPtr(lltype.Char), points)
+ result_point[0].x = 0
+ result_point[0].y = 0
+ result_point = rffi.cast(rffi.CArrayPtr(lltype.Char), result_point)
+ f(points, result_point, n)
+ result_point = rffi.cast(rffi.CArrayPtr(POINT), result_point)
+ return result_point[0].x * result_point[0].y
+
+ assert self.meta_interp(main, [10]) == main(10) == 9000
+ self.check_resops({'jump': 2, 'int_lt': 2, 'setinteriorfield_raw': 4,
+ 'getinteriorfield_raw': 8, 'int_add': 6, 'guard_true': 2})
+
+class TestFfiCall(FfiCallTests, LLJitMixin):
+ supports_all = False
+
+class TestFfiCallSupportAll(FfiCallTests, LLJitMixin):
supports_all = True # supports_{floats,longlong,singlefloats}
+
+class TestFfiLookup(FfiLookupTests, LLJitMixin):
+ pass
diff --git a/pypy/jit/metainterp/test/test_greenfield.py b/pypy/jit/metainterp/test/test_greenfield.py
--- a/pypy/jit/metainterp/test/test_greenfield.py
+++ b/pypy/jit/metainterp/test/test_greenfield.py
@@ -25,7 +25,7 @@
res = self.meta_interp(g, [7])
assert res == -2
self.check_loop_count(2)
- self.check_loops(guard_value=0)
+ self.check_resops(guard_value=0)
def test_green_field_2(self):
myjitdriver = JitDriver(greens=['ctx.x'], reds=['ctx'])
@@ -50,7 +50,7 @@
res = self.meta_interp(g, [7])
assert res == -22
self.check_loop_count(6)
- self.check_loops(guard_value=0)
+ self.check_resops(guard_value=0)
class TestLLtypeGreenFieldsTests(GreenFieldsTests, LLJitMixin):
diff --git a/pypy/jit/metainterp/test/test_jitdriver.py b/pypy/jit/metainterp/test/test_jitdriver.py
--- a/pypy/jit/metainterp/test/test_jitdriver.py
+++ b/pypy/jit/metainterp/test/test_jitdriver.py
@@ -1,5 +1,5 @@
"""Tests for multiple JitDrivers."""
-from pypy.rlib.jit import JitDriver, unroll_safe
+from pypy.rlib.jit import JitDriver, unroll_safe, set_param
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
from pypy.jit.metainterp.warmspot import get_stats
@@ -88,7 +88,7 @@
assert res == loop2(4, 40)
# we expect only one int_sub, corresponding to the single
# compiled instance of loop1()
- self.check_loops(int_sub=1)
+ self.check_resops(int_sub=2)
# the following numbers are not really expectations of the test
# itself, but just the numbers that we got after looking carefully
# at the generated machine code
@@ -113,7 +113,7 @@
return n
#
def loop2(g, r):
- myjitdriver1.set_param('function_threshold', 0)
+ set_param(None, 'function_threshold', 0)
while r > 0:
myjitdriver2.can_enter_jit(g=g, r=r)
myjitdriver2.jit_merge_point(g=g, r=r)
@@ -154,7 +154,7 @@
res = self.meta_interp(loop2, [4, 40], repeat=7, inline=True)
assert res == loop2(4, 40)
# we expect no int_sub, but a residual call
- self.check_loops(int_sub=0, call=1)
+ self.check_resops(call=2, int_sub=0)
def test_multiple_jits_trace_too_long(self):
myjitdriver1 = JitDriver(greens=["n"], reds=["i", "box"])
diff --git a/pypy/jit/metainterp/test/test_list.py b/pypy/jit/metainterp/test/test_list.py
--- a/pypy/jit/metainterp/test/test_list.py
+++ b/pypy/jit/metainterp/test/test_list.py
@@ -6,8 +6,8 @@
class ListTests:
def check_all_virtualized(self):
- self.check_loops(new_array=0, setarrayitem_gc=0, getarrayitem_gc=0,
- arraylen_gc=0)
+ self.check_resops(setarrayitem_gc=0, new_array=0, arraylen_gc=0,
+ getarrayitem_gc=0)
def test_simple_array(self):
jitdriver = JitDriver(greens = [], reds = ['n'])
@@ -20,7 +20,7 @@
return n
res = self.meta_interp(f, [10], listops=True)
assert res == 0
- self.check_loops(int_sub=1)
+ self.check_resops(int_sub=2)
self.check_all_virtualized()
def test_list_pass_around(self):
@@ -56,7 +56,8 @@
res = self.meta_interp(f, [10], listops=True)
assert res == f(10)
# one setitem should be gone by now
- self.check_loops(call=1, setarrayitem_gc=2, getarrayitem_gc=1)
+ self.check_resops(setarrayitem_gc=4, getarrayitem_gc=2, call=2)
+
def test_ll_fixed_setitem_fast(self):
jitdriver = JitDriver(greens = [], reds = ['n', 'l'])
@@ -93,7 +94,7 @@
res = self.meta_interp(f, [10], listops=True)
assert res == f(10)
- self.check_loops(setarrayitem_gc=0, getarrayitem_gc=0, call=0)
+ self.check_resops(setarrayitem_gc=0, call=0, getarrayitem_gc=0)
def test_vlist_alloc_and_set(self):
# the check_loops fails, because [non-null] * n is not supported yet
@@ -141,7 +142,7 @@
res = self.meta_interp(f, [5], listops=True)
assert res == 7
- self.check_loops(call=0)
+ self.check_resops(call=0)
def test_fold_getitem_1(self):
jitdriver = JitDriver(greens = ['pc', 'n', 'l'], reds = ['total'])
@@ -161,7 +162,7 @@
res = self.meta_interp(f, [4], listops=True)
assert res == f(4)
- self.check_loops(call=0)
+ self.check_resops(call=0)
def test_fold_getitem_2(self):
jitdriver = JitDriver(greens = ['pc', 'n', 'l'], reds = ['total', 'x'])
@@ -186,7 +187,7 @@
res = self.meta_interp(f, [4], listops=True)
assert res == f(4)
- self.check_loops(call=0, getfield_gc=0)
+ self.check_resops(call=0, getfield_gc=0)
def test_fold_indexerror(self):
jitdriver = JitDriver(greens = [], reds = ['total', 'n', 'lst'])
@@ -206,7 +207,7 @@
res = self.meta_interp(f, [15], listops=True)
assert res == f(15)
- self.check_loops(guard_exception=0)
+ self.check_resops(guard_exception=0)
def test_virtual_resize(self):
jitdriver = JitDriver(greens = [], reds = ['n', 's'])
@@ -224,9 +225,8 @@
return s
res = self.meta_interp(f, [15], listops=True)
assert res == f(15)
- self.check_loops({"int_add": 1, "int_sub": 1, "int_gt": 1,
- "guard_true": 1, "jump": 1})
-
+ self.check_resops({'jump': 2, 'int_gt': 2, 'int_add': 2,
+ 'guard_true': 2, 'int_sub': 2})
class TestOOtype(ListTests, OOJitMixin):
pass
@@ -258,4 +258,4 @@
assert res == f(37)
# There is the one actual field on a, plus several fields on the list
# itself
- self.check_loops(getfield_gc=10, everywhere=True)
+ self.check_resops(getfield_gc=10)
diff --git a/pypy/jit/metainterp/test/test_loop.py b/pypy/jit/metainterp/test/test_loop.py
--- a/pypy/jit/metainterp/test/test_loop.py
+++ b/pypy/jit/metainterp/test/test_loop.py
@@ -1,5 +1,5 @@
import py
-from pypy.rlib.jit import JitDriver, hint
+from pypy.rlib.jit import JitDriver, hint, set_param
from pypy.rlib.objectmodel import compute_hash
from pypy.jit.metainterp.warmspot import ll_meta_interp, get_stats
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
@@ -60,7 +60,8 @@
assert res == f(6, 13)
self.check_loop_count(1)
if self.enable_opts:
- self.check_loops(getfield_gc = 0, setfield_gc = 1)
+ self.check_resops(setfield_gc=2, getfield_gc=0)
+
def test_loop_with_two_paths(self):
from pypy.rpython.lltypesystem import lltype
@@ -180,7 +181,10 @@
assert res == 42
self.check_loop_count(1)
# the 'int_eq' and following 'guard' should be constant-folded
- self.check_loops(int_eq=0, guard_true=1, guard_false=0)
+ if 'unroll' in self.enable_opts:
+ self.check_resops(int_eq=0, guard_true=2, guard_false=0)
+ else:
+ self.check_resops(int_eq=0, guard_true=1, guard_false=0)
if self.basic:
found = 0
for op in get_stats().loops[0]._all_operations():
@@ -364,7 +368,7 @@
myjitdriver = JitDriver(greens = ['pos'], reds = ['i', 'j', 'n', 'x'])
bytecode = "IzJxji"
def f(n, threshold):
- myjitdriver.set_param('threshold', threshold)
+ set_param(myjitdriver, 'threshold', threshold)
i = j = x = 0
pos = 0
op = '-'
@@ -411,7 +415,7 @@
myjitdriver = JitDriver(greens = ['pos'], reds = ['i', 'j', 'n', 'x'])
bytecode = "IzJxji"
def f(nval, threshold):
- myjitdriver.set_param('threshold', threshold)
+ set_param(myjitdriver, 'threshold', threshold)
i, j, x = A(0), A(0), A(0)
n = A(nval)
pos = 0
@@ -643,8 +647,12 @@
res = self.meta_interp(main_interpreter_loop, [1])
assert res == 102
self.check_loop_count(1)
- self.check_loops({'int_add' : 3, 'int_gt' : 1,
- 'guard_false' : 1, 'jump' : 1})
+ if 'unroll' in self.enable_opts:
+ self.check_resops({'int_add' : 6, 'int_gt' : 2,
+ 'guard_false' : 2, 'jump' : 2})
+ else:
+ self.check_resops({'int_add' : 3, 'int_gt' : 1,
+ 'guard_false' : 1, 'jump' : 1})
def test_automatic_promotion(self):
myjitdriver = JitDriver(greens = ['i'],
@@ -686,7 +694,7 @@
self.check_loop_count(1)
# These loops do different numbers of ops based on which optimizer we
# are testing with.
- self.check_loops(self.automatic_promotion_result)
+ self.check_resops(self.automatic_promotion_result)
def test_can_enter_jit_outside_main_loop(self):
myjitdriver = JitDriver(greens=[], reds=['i', 'j', 'a'])
diff --git a/pypy/jit/metainterp/test/test_loop_unroll.py b/pypy/jit/metainterp/test/test_loop_unroll.py
--- a/pypy/jit/metainterp/test/test_loop_unroll.py
+++ b/pypy/jit/metainterp/test/test_loop_unroll.py
@@ -8,7 +8,8 @@
enable_opts = ALL_OPTS_NAMES
automatic_promotion_result = {
- 'int_add' : 3, 'int_gt' : 1, 'guard_false' : 1, 'jump' : 1,
+ 'int_gt': 2, 'guard_false': 2, 'jump': 2, 'int_add': 6,
+ 'guard_value': 1
}
# ====> test_loop.py
diff --git a/pypy/jit/metainterp/test/test_quasiimmut.py b/pypy/jit/metainterp/test/test_quasiimmut.py
--- a/pypy/jit/metainterp/test/test_quasiimmut.py
+++ b/pypy/jit/metainterp/test/test_quasiimmut.py
@@ -73,8 +73,7 @@
#
res = self.meta_interp(f, [100, 7])
assert res == 700
- self.check_loops(guard_not_invalidated=2, getfield_gc=0,
- everywhere=True)
+ self.check_resops(guard_not_invalidated=2, getfield_gc=0)
#
from pypy.jit.metainterp.warmspot import get_stats
loops = get_stats().loops
@@ -103,7 +102,7 @@
assert f(100, 7) == 721
res = self.meta_interp(f, [100, 7])
assert res == 721
- self.check_loops(guard_not_invalidated=0, getfield_gc=1)
+ self.check_resops(guard_not_invalidated=0, getfield_gc=3)
#
from pypy.jit.metainterp.warmspot import get_stats
loops = get_stats().loops
@@ -134,8 +133,7 @@
#
res = self.meta_interp(f, [100, 7])
assert res == 700
- self.check_loops(guard_not_invalidated=2, getfield_gc=0,
- everywhere=True)
+ self.check_resops(guard_not_invalidated=2, getfield_gc=0)
def test_change_during_tracing_1(self):
myjitdriver = JitDriver(greens=['foo'], reds=['x', 'total'])
@@ -160,7 +158,7 @@
assert f(100, 7) == 721
res = self.meta_interp(f, [100, 7])
assert res == 721
- self.check_loops(guard_not_invalidated=0, getfield_gc=1)
+ self.check_resops(guard_not_invalidated=0, getfield_gc=2)
def test_change_during_tracing_2(self):
myjitdriver = JitDriver(greens=['foo'], reds=['x', 'total'])
@@ -186,7 +184,7 @@
assert f(100, 7) == 700
res = self.meta_interp(f, [100, 7])
assert res == 700
- self.check_loops(guard_not_invalidated=0, getfield_gc=1)
+ self.check_resops(guard_not_invalidated=0, getfield_gc=2)
def test_change_invalidate_reentering(self):
myjitdriver = JitDriver(greens=['foo'], reds=['x', 'total'])
@@ -212,7 +210,7 @@
assert g(100, 7) == 700707
res = self.meta_interp(g, [100, 7])
assert res == 700707
- self.check_loops(guard_not_invalidated=2, getfield_gc=0)
+ self.check_resops(guard_not_invalidated=4, getfield_gc=0)
def test_invalidate_while_running(self):
jitdriver = JitDriver(greens=['foo'], reds=['i', 'total'])
@@ -324,8 +322,8 @@
assert f(100, 15) == 3009
res = self.meta_interp(f, [100, 15])
assert res == 3009
- self.check_loops(guard_not_invalidated=4, getfield_gc=0,
- call_may_force=0, guard_not_forced=0)
+ self.check_resops(guard_not_invalidated=8, guard_not_forced=0,
+ call_may_force=0, getfield_gc=0)
def test_list_simple_1(self):
myjitdriver = JitDriver(greens=['foo'], reds=['x', 'total'])
@@ -347,9 +345,8 @@
#
res = self.meta_interp(f, [100, 7])
assert res == 700
- self.check_loops(guard_not_invalidated=2, getfield_gc=0,
- getarrayitem_gc=0, getarrayitem_gc_pure=0,
- everywhere=True)
+ self.check_resops(getarrayitem_gc_pure=0, guard_not_invalidated=2,
+ getarrayitem_gc=0, getfield_gc=0)
#
from pypy.jit.metainterp.warmspot import get_stats
loops = get_stats().loops
@@ -385,9 +382,8 @@
#
res = self.meta_interp(f, [100, 7])
assert res == 714
- self.check_loops(guard_not_invalidated=2, getfield_gc=0,
- getarrayitem_gc=0, getarrayitem_gc_pure=0,
- arraylen_gc=0, everywhere=True)
+ self.check_resops(getarrayitem_gc_pure=0, guard_not_invalidated=2,
+ arraylen_gc=0, getarrayitem_gc=0, getfield_gc=0)
#
from pypy.jit.metainterp.warmspot import get_stats
loops = get_stats().loops
@@ -421,9 +417,8 @@
#
res = self.meta_interp(f, [100, 7])
assert res == 700
- self.check_loops(guard_not_invalidated=2, getfield_gc=0,
- getarrayitem_gc=0, getarrayitem_gc_pure=0,
- everywhere=True)
+ self.check_resops(guard_not_invalidated=2, getfield_gc=0,
+ getarrayitem_gc=0, getarrayitem_gc_pure=0)
#
from pypy.jit.metainterp.warmspot import get_stats
loops = get_stats().loops
@@ -460,9 +455,9 @@
assert f(100, 15) == 3009
res = self.meta_interp(f, [100, 15])
assert res == 3009
- self.check_loops(guard_not_invalidated=4, getfield_gc=0,
- getarrayitem_gc=0, getarrayitem_gc_pure=0,
- call_may_force=0, guard_not_forced=0)
+ self.check_resops(call_may_force=0, getfield_gc=0,
+ getarrayitem_gc_pure=0, guard_not_forced=0,
+ getarrayitem_gc=0, guard_not_invalidated=8)
def test_invalidated_loop_is_not_used_any_more_as_target(self):
myjitdriver = JitDriver(greens=['foo'], reds=['x'])
diff --git a/pypy/jit/metainterp/test/test_recursive.py b/pypy/jit/metainterp/test/test_recursive.py
--- a/pypy/jit/metainterp/test/test_recursive.py
+++ b/pypy/jit/metainterp/test/test_recursive.py
@@ -1,5 +1,5 @@
import py
-from pypy.rlib.jit import JitDriver, we_are_jitted, hint
+from pypy.rlib.jit import JitDriver, hint, set_param
from pypy.rlib.jit import unroll_safe, dont_look_inside, promote
from pypy.rlib.objectmodel import we_are_translated
from pypy.rlib.debug import fatalerror
@@ -143,11 +143,11 @@
f = self.get_interpreter(codes)
assert self.meta_interp(f, [0, 0, 0], enable_opts='') == 42
- self.check_loops(int_add = 1, call_may_force = 1, call = 0)
+ self.check_resops(call_may_force=1, int_add=1, call=0)
assert self.meta_interp(f, [0, 0, 0], enable_opts='',
inline=True) == 42
- self.check_loops(int_add = 2, call_may_force = 0, call = 0,
- guard_no_exception = 0)
+ self.check_resops(call=0, int_add=2, call_may_force=0,
+ guard_no_exception=0)
def test_inline_jitdriver_check(self):
code = "021"
@@ -160,7 +160,7 @@
inline=True) == 42
# the call is fully inlined, because we jump to subcode[1], thus
# skipping completely the JUMP_BACK in subcode[0]
- self.check_loops(call_may_force = 0, call_assembler = 0, call = 0)
+ self.check_resops(call=0, call_may_force=0, call_assembler=0)
def test_guard_failure_in_inlined_function(self):
def p(pc, code):
@@ -308,8 +308,8 @@
pc += 1
return n
def main(n):
- myjitdriver.set_param('threshold', 3)
- myjitdriver.set_param('trace_eagerness', 5)
+ set_param(None, 'threshold', 3)
+ set_param(None, 'trace_eagerness', 5)
return f("c-l", n)
expected = main(100)
res = self.meta_interp(main, [100], enable_opts='', inline=True)
@@ -329,7 +329,7 @@
return recursive(n - 1) + 1
return 0
def loop(n):
- myjitdriver.set_param("threshold", 10)
+ set_param(myjitdriver, "threshold", 10)
pc = 0
while n:
myjitdriver.can_enter_jit(n=n)
@@ -351,8 +351,8 @@
return 0
myjitdriver = JitDriver(greens=[], reds=['n'])
def loop(n):
- myjitdriver.set_param("threshold", 4)
- myjitdriver.set_param("trace_eagerness", 2)
+ set_param(None, "threshold", 4)
+ set_param(None, "trace_eagerness", 2)
while n:
myjitdriver.can_enter_jit(n=n)
myjitdriver.jit_merge_point(n=n)
@@ -482,19 +482,19 @@
TRACE_LIMIT = 66
def main(inline):
- myjitdriver.set_param("threshold", 10)
- myjitdriver.set_param('function_threshold', 60)
+ set_param(None, "threshold", 10)
+ set_param(None, 'function_threshold', 60)
if inline:
- myjitdriver.set_param('inlining', True)
+ set_param(None, 'inlining', True)
else:
- myjitdriver.set_param('inlining', False)
+ set_param(None, 'inlining', False)
return loop(100)
res = self.meta_interp(main, [0], enable_opts='', trace_limit=TRACE_LIMIT)
- self.check_loops(call_may_force=1, call=0)
+ self.check_resops(call=0, call_may_force=1)
res = self.meta_interp(main, [1], enable_opts='', trace_limit=TRACE_LIMIT)
- self.check_loops(call_may_force=0, call=0)
+ self.check_resops(call=0, call_may_force=0)
def test_trace_from_start(self):
def p(pc, code):
@@ -564,11 +564,11 @@
pc += 1
return n
def g(m):
- myjitdriver.set_param('inlining', True)
+ set_param(None, 'inlining', True)
# carefully chosen threshold to make sure that the inner function
# cannot be inlined, but the inner function on its own is small
# enough
- myjitdriver.set_param('trace_limit', 40)
+ set_param(None, 'trace_limit', 40)
if m > 1000000:
f('', 0)
result = 0
@@ -576,7 +576,7 @@
result += f('-c-----------l-', i+100)
self.meta_interp(g, [10], backendopt=True)
self.check_aborted_count(1)
- self.check_loops(call_assembler=1, call=0)
+ self.check_resops(call=0, call_assembler=2)
self.check_tree_loop_count(3)
def test_directly_call_assembler(self):
@@ -625,8 +625,7 @@
try:
compile.compile_tmp_callback = my_ctc
self.meta_interp(portal, [2, 5], inline=True)
- self.check_loops(call_assembler=2, call_may_force=0,
- everywhere=True)
+ self.check_resops(call_may_force=0, call_assembler=2)
finally:
compile.compile_tmp_callback = original_ctc
# check that we made a temporary callback
@@ -681,8 +680,7 @@
try:
compile.compile_tmp_callback = my_ctc
self.meta_interp(main, [2, 5], inline=True)
- self.check_loops(call_assembler=2, call_may_force=0,
- everywhere=True)
+ self.check_resops(call_may_force=0, call_assembler=2)
finally:
compile.compile_tmp_callback = original_ctc
# check that we made a temporary callback
@@ -1021,7 +1019,7 @@
res = self.meta_interp(portal, [2, 0], inline=True,
policy=StopAtXPolicy(residual))
assert res == portal(2, 0)
- self.check_loops(call_assembler=4, everywhere=True)
+ self.check_resops(call_assembler=4)
def test_inline_without_hitting_the_loop(self):
driver = JitDriver(greens = ['codeno'], reds = ['i'],
@@ -1045,7 +1043,7 @@
assert portal(0) == 70
res = self.meta_interp(portal, [0], inline=True)
assert res == 70
- self.check_loops(call_assembler=0)
+ self.check_resops(call_assembler=0)
def test_inline_with_hitting_the_loop_sometimes(self):
driver = JitDriver(greens = ['codeno'], reds = ['i', 'k'],
@@ -1071,7 +1069,7 @@
assert portal(0, 1) == 2095
res = self.meta_interp(portal, [0, 1], inline=True)
assert res == 2095
- self.check_loops(call_assembler=12, everywhere=True)
+ self.check_resops(call_assembler=12)
def test_inline_with_hitting_the_loop_sometimes_exc(self):
driver = JitDriver(greens = ['codeno'], reds = ['i', 'k'],
@@ -1109,7 +1107,7 @@
assert main(0, 1) == 2095
res = self.meta_interp(main, [0, 1], inline=True)
assert res == 2095
- self.check_loops(call_assembler=12, everywhere=True)
+ self.check_resops(call_assembler=12)
def test_handle_jitexception_in_portal(self):
# a test for _handle_jitexception_in_portal in blackhole.py
@@ -1207,9 +1205,9 @@
driver.can_enter_jit(c=c, i=i, v=v)
break
- def main(c, i, set_param, v):
- if set_param:
- driver.set_param('function_threshold', 0)
+ def main(c, i, _set_param, v):
+ if _set_param:
+ set_param(driver, 'function_threshold', 0)
portal(c, i, v)
self.meta_interp(main, [10, 10, False, False], inline=True)
@@ -1238,7 +1236,7 @@
i += 1
self.meta_interp(portal, [0, 0, 0], inline=True)
- self.check_loops(call=0, call_may_force=0)
+ self.check_resops(call_may_force=0, call=0)
class TestLLtype(RecursiveTests, LLJitMixin):
pass
diff --git a/pypy/jit/metainterp/test/test_resume.py b/pypy/jit/metainterp/test/test_resume.py
--- a/pypy/jit/metainterp/test/test_resume.py
+++ b/pypy/jit/metainterp/test/test_resume.py
@@ -23,11 +23,11 @@
assert tag(-3, 2) == rffi.r_short(-3<<2|2)
assert tag((1<<13)-1, 3) == rffi.r_short(((1<<15)-1)|3)
assert tag(-1<<13, 3) == rffi.r_short((-1<<15)|3)
- py.test.raises(ValueError, tag, 3, 5)
- py.test.raises(ValueError, tag, 1<<13, 0)
- py.test.raises(ValueError, tag, (1<<13)+1, 0)
- py.test.raises(ValueError, tag, (-1<<13)-1, 0)
- py.test.raises(ValueError, tag, (-1<<13)-5, 0)
+ py.test.raises(AssertionError, tag, 3, 5)
+ py.test.raises(TagOverflow, tag, 1<<13, 0)
+ py.test.raises(TagOverflow, tag, (1<<13)+1, 0)
+ py.test.raises(TagOverflow, tag, (-1<<13)-1, 0)
+ py.test.raises(TagOverflow, tag, (-1<<13)-5, 0)
def test_untag():
assert untag(tag(3, 1)) == (3, 1)
@@ -1135,16 +1135,11 @@
assert ptr2.parent.next == ptr
class CompareableConsts(object):
- def __init__(self):
- self.oldeq = None
-
def __enter__(self):
- assert self.oldeq is None
- self.oldeq = Const.__eq__
Const.__eq__ = Const.same_box
-
+
def __exit__(self, type, value, traceback):
- Const.__eq__ = self.oldeq
+ del Const.__eq__
def test_virtual_adder_make_varray():
b2s, b4s = [BoxPtr(), BoxInt(4)]
@@ -1323,8 +1318,7 @@
assert rffi.cast(lltype.Signed, pf[1].fieldnum) == 1062
assert rffi.cast(lltype.Signed, pf[1].itemindex) == 2147483647
#
- from pypy.jit.metainterp.pyjitpl import SwitchToBlackhole
- py.test.raises(SwitchToBlackhole, modifier._add_pending_fields,
+ py.test.raises(TagOverflow, modifier._add_pending_fields,
[(array_a, 42, 63, 2147483648)])
def test_resume_reader_fields_and_arrayitems():
diff --git a/pypy/jit/metainterp/test/test_send.py b/pypy/jit/metainterp/test/test_send.py
--- a/pypy/jit/metainterp/test/test_send.py
+++ b/pypy/jit/metainterp/test/test_send.py
@@ -20,9 +20,8 @@
return c
res = self.meta_interp(f, [1])
assert res == 2
- self.check_loops({'jump': 1,
- 'int_sub': 1, 'int_gt' : 1,
- 'guard_true': 1}) # all folded away
+ self.check_resops({'jump': 2, 'guard_true': 2, 'int_gt': 2,
+ 'int_sub': 2}) # all folded away
def test_red_builtin_send(self):
myjitdriver = JitDriver(greens = [], reds = ['i', 'counter'])
@@ -41,12 +40,9 @@
return res
res = self.meta_interp(f, [1], policy=StopAtXPolicy(externfn))
assert res == 2
- if self.type_system == 'ootype':
- self.check_loops(call=1, oosend=1) # 'len' remains
- else:
- # 'len' becomes a getfield('num_items') for now in lltype,
- # which is itself encoded as a 'getfield_gc'
- self.check_loops(call=1, getfield_gc=1)
+ # 'len' becomes a getfield('num_items') for now in lltype,
+ # which is itself encoded as a 'getfield_gc'
+ self.check_resops(call=2, getfield_gc=2)
def test_send_to_single_target_method(self):
myjitdriver = JitDriver(greens = [], reds = ['i', 'counter'])
@@ -70,11 +66,10 @@
res = self.meta_interp(f, [1], policy=StopAtXPolicy(externfn),
backendopt=True)
assert res == 43
- self.check_loops({'call': 1, 'guard_no_exception': 1,
- 'getfield_gc': 1,
- 'int_add': 1,
- 'jump': 1, 'int_gt' : 1, 'guard_true' : 1,
- 'int_sub' : 1})
+ self.check_resops({'int_gt': 2, 'getfield_gc': 2,
+ 'guard_true': 2, 'int_sub': 2, 'jump': 2,
+ 'call': 2, 'guard_no_exception': 2,
+ 'int_add': 2})
def test_red_send_to_green_receiver(self):
myjitdriver = JitDriver(greens = ['i'], reds = ['counter', 'j'])
@@ -97,7 +92,7 @@
return res
res = self.meta_interp(f, [4, -1])
assert res == 145
- self.check_loops(int_add = 1, everywhere=True)
+ self.check_resops(int_add=1)
def test_oosend_base(self):
myjitdriver = JitDriver(greens = [], reds = ['x', 'y', 'w'])
@@ -132,7 +127,7 @@
assert res == 17
res = self.meta_interp(f, [4, 14])
assert res == 1404
- self.check_loops(guard_class=0, new_with_vtable=0, new=0)
+ self.check_resops(guard_class=1, new=0, new_with_vtable=0)
def test_three_receivers(self):
myjitdriver = JitDriver(greens = [], reds = ['y'])
@@ -205,8 +200,7 @@
# of the body in a single bigger loop with no failing guard except
# the final one.
self.check_loop_count(1)
- self.check_loops(guard_class=0,
- int_add=2, int_sub=2)
+ self.check_resops(guard_class=1, int_add=4, int_sub=4)
self.check_jumps(14)
def test_oosend_guard_failure_2(self):
@@ -247,8 +241,7 @@
res = self.meta_interp(f, [4, 28])
assert res == f(4, 28)
self.check_loop_count(1)
- self.check_loops(guard_class=0,
- int_add=2, int_sub=2)
+ self.check_resops(guard_class=1, int_add=4, int_sub=4)
self.check_jumps(14)
def test_oosend_different_initial_class(self):
@@ -285,8 +278,8 @@
# However, this doesn't match the initial value of 'w'.
# XXX This not completely easy to check...
self.check_loop_count(1)
- self.check_loops(int_add=0, int_lshift=1, guard_class=0,
- new_with_vtable=0, new=0)
+ self.check_resops(guard_class=1, new_with_vtable=0, int_lshift=2,
+ int_add=0, new=0)
def test_indirect_call_unknown_object_1(self):
myjitdriver = JitDriver(greens = [], reds = ['x', 'y'])
@@ -566,10 +559,7 @@
policy = StopAtXPolicy(new, A.foo.im_func, B.foo.im_func)
res = self.meta_interp(fn, [0, 20], policy=policy)
assert res == 42
- if self.type_system == 'ootype':
- self.check_loops(oosend=1)
- else:
- self.check_loops(call=1)
+ self.check_resops(call=2)
def test_residual_oosend_with_void(self):
@@ -597,10 +587,7 @@
policy = StopAtXPolicy(new, A.foo.im_func)
res = self.meta_interp(fn, [1, 20], policy=policy)
assert res == 41
- if self.type_system == 'ootype':
- self.check_loops(oosend=1)
- else:
- self.check_loops(call=1)
+ self.check_resops(call=2)
def test_constfold_pure_oosend(self):
myjitdriver = JitDriver(greens=[], reds = ['i', 'obj'])
@@ -621,10 +608,7 @@
policy = StopAtXPolicy(A.foo.im_func)
res = self.meta_interp(fn, [1, 20], policy=policy)
assert res == 42
- if self.type_system == 'ootype':
- self.check_loops(oosend=0)
- else:
- self.check_loops(call=0)
+ self.check_resops(call=0)
def test_generalize_loop(self):
myjitdriver = JitDriver(greens=[], reds = ['i', 'obj'])
diff --git a/pypy/jit/metainterp/test/test_slist.py b/pypy/jit/metainterp/test/test_slist.py
--- a/pypy/jit/metainterp/test/test_slist.py
+++ b/pypy/jit/metainterp/test/test_slist.py
@@ -76,7 +76,7 @@
return lst[i]
res = self.meta_interp(f, [21], listops=True)
assert res == f(21)
- self.check_loops(call=0)
+ self.check_resops(call=0)
def test_getitem_neg(self):
myjitdriver = JitDriver(greens = [], reds = ['i', 'n'])
@@ -92,7 +92,7 @@
return x
res = self.meta_interp(f, [-2], listops=True)
assert res == 41
- self.check_loops(call=0, guard_value=0)
+ self.check_resops(call=0, guard_value=0)
# we don't support resizable lists on ootype
#class TestOOtype(ListTests, OOJitMixin):
diff --git a/pypy/jit/metainterp/test/test_string.py b/pypy/jit/metainterp/test/test_string.py
--- a/pypy/jit/metainterp/test/test_string.py
+++ b/pypy/jit/metainterp/test/test_string.py
@@ -30,7 +30,7 @@
return i
res = self.meta_interp(f, [10, True, _str('h')], listops=True)
assert res == 5
- self.check_loops(**{self.CALL: 1, self.CALL_PURE: 0, 'everywhere': True})
+ self.check_resops(**{self.CALL: 1, self.CALL_PURE: 0})
def test_eq_folded(self):
_str = self._str
@@ -50,7 +50,7 @@
return i
res = self.meta_interp(f, [10, True, _str('h')], listops=True)
assert res == 5
- self.check_loops(**{self.CALL: 0, self.CALL_PURE: 0})
+ self.check_resops(**{self.CALL: 0, self.CALL_PURE: 0})
def test_newstr(self):
_str, _chr = self._str, self._chr
@@ -85,7 +85,7 @@
n -= 1
return 42
self.meta_interp(f, [6])
- self.check_loops(newstr=0, strsetitem=0, strlen=0,
+ self.check_resops(newstr=0, strsetitem=0, strlen=0,
newunicode=0, unicodesetitem=0, unicodelen=0)
def test_char2string_escape(self):
@@ -126,7 +126,7 @@
return total
res = self.meta_interp(f, [6])
assert res == 21
- self.check_loops(newstr=0, strgetitem=0, strsetitem=0, strlen=0,
+ self.check_resops(newstr=0, strgetitem=0, strsetitem=0, strlen=0,
newunicode=0, unicodegetitem=0, unicodesetitem=0,
unicodelen=0)
@@ -147,7 +147,7 @@
m -= 1
return 42
self.meta_interp(f, [6, 7])
- self.check_loops(newstr=0, strsetitem=0,
+ self.check_resops(newstr=0, strsetitem=0,
newunicode=0, unicodesetitem=0,
call=0, call_pure=0)
@@ -168,12 +168,11 @@
return 42
self.meta_interp(f, [6, 7])
if _str is str:
- self.check_loops(newstr=1, strsetitem=0, copystrcontent=2,
- call=1, call_pure=0) # escape
+ self.check_resops(call_pure=0, copystrcontent=4,
+ strsetitem=0, call=2, newstr=2)
else:
- self.check_loops(newunicode=1, unicodesetitem=0,
- copyunicodecontent=2,
- call=1, call_pure=0) # escape
+ self.check_resops(call_pure=0, unicodesetitem=0, call=2,
+ copyunicodecontent=4, newunicode=2)
def test_strconcat_escape_str_char(self):
_str, _chr = self._str, self._chr
@@ -192,12 +191,11 @@
return 42
self.meta_interp(f, [6, 7])
if _str is str:
- self.check_loops(newstr=1, strsetitem=1, copystrcontent=1,
- call=1, call_pure=0) # escape
+ self.check_resops(call_pure=0, copystrcontent=2, strsetitem=2,
+ call=2, newstr=2)
else:
- self.check_loops(newunicode=1, unicodesetitem=1,
- copyunicodecontent=1,
- call=1, call_pure=0) # escape
+ self.check_resops(call_pure=0, unicodesetitem=2, call=2,
+ copyunicodecontent=2, newunicode=2)
def test_strconcat_escape_char_str(self):
_str, _chr = self._str, self._chr
@@ -216,12 +214,11 @@
return 42
self.meta_interp(f, [6, 7])
if _str is str:
- self.check_loops(newstr=1, strsetitem=1, copystrcontent=1,
- call=1, call_pure=0) # escape
+ self.check_resops(call_pure=0, copystrcontent=2,
+ strsetitem=2, call=2, newstr=2)
else:
- self.check_loops(newunicode=1, unicodesetitem=1,
- copyunicodecontent=1,
- call=1, call_pure=0) # escape
+ self.check_resops(call_pure=0, unicodesetitem=2, call=2,
+ copyunicodecontent=2, newunicode=2)
def test_strconcat_escape_char_char(self):
_str, _chr = self._str, self._chr
@@ -239,12 +236,11 @@
return 42
self.meta_interp(f, [6, 7])
if _str is str:
- self.check_loops(newstr=1, strsetitem=2, copystrcontent=0,
- call=1, call_pure=0) # escape
+ self.check_resops(call_pure=0, copystrcontent=0,
+ strsetitem=4, call=2, newstr=2)
else:
- self.check_loops(newunicode=1, unicodesetitem=2,
- copyunicodecontent=0,
- call=1, call_pure=0) # escape
+ self.check_resops(call_pure=0, unicodesetitem=4, call=2,
+ copyunicodecontent=0, newunicode=2)
def test_strconcat_escape_str_char_str(self):
_str, _chr = self._str, self._chr
@@ -263,12 +259,11 @@
return 42
self.meta_interp(f, [6, 7])
if _str is str:
- self.check_loops(newstr=1, strsetitem=1, copystrcontent=2,
- call=1, call_pure=0) # escape
+ self.check_resops(call_pure=0, copystrcontent=4, strsetitem=2,
+ call=2, newstr=2)
else:
- self.check_loops(newunicode=1, unicodesetitem=1,
- copyunicodecontent=2,
- call=1, call_pure=0) # escape
+ self.check_resops(call_pure=0, unicodesetitem=2, call=2,
+ copyunicodecontent=4, newunicode=2)
def test_strconcat_guard_fail(self):
_str = self._str
@@ -325,7 +320,7 @@
m -= 1
return 42
self.meta_interp(f, [6, 7])
- self.check_loops(newstr=0, newunicode=0)
+ self.check_resops(newunicode=0, newstr=0)
def test_str_slice_len_surviving(self):
_str = self._str
@@ -491,7 +486,7 @@
def __init__(self, s):
self.defaultencoding = s
_str = self._str
- sys = Sys(_str('ascii'))
+ sys = Sys(_str('ascii'))
mydriver = JitDriver(reds = ['n', 'sa'], greens = [])
def f(n):
sa = 0
@@ -504,13 +499,13 @@
sys.defaultencoding = _str('utf-8')
return sa
assert self.meta_interp(f, [8]) == f(8)
- self.check_loops({'int_add': 1, 'guard_true': 1, 'int_sub': 1,
- 'jump': 1, 'int_is_true': 1,
- 'guard_not_invalidated': 1})
+ self.check_resops({'jump': 2, 'int_is_true': 2, 'int_add': 2,
+ 'guard_true': 2, 'guard_not_invalidated': 2,
+ 'int_sub': 2})
def test_promote_string(self):
driver = JitDriver(greens = [], reds = ['n'])
-
+
def f(n):
while n < 21:
driver.jit_merge_point(n=n)
@@ -519,7 +514,7 @@
return 0
self.meta_interp(f, [0])
- self.check_loops(call=3 + 1) # one for int2str
+ self.check_resops(call=7)
#class TestOOtype(StringTests, OOJitMixin):
# CALL = "oosend"
@@ -552,9 +547,8 @@
m -= 1
return 42
self.meta_interp(f, [6, 7])
- self.check_loops(call=1, # escape()
- newunicode=1, unicodegetitem=0,
- unicodesetitem=1, copyunicodecontent=1)
+ self.check_resops(unicodesetitem=2, newunicode=2, call=4,
+ copyunicodecontent=2, unicodegetitem=0)
def test_str2unicode_fold(self):
_str = self._str
@@ -572,9 +566,9 @@
m -= 1
return 42
self.meta_interp(f, [6, 7])
- self.check_loops(call_pure=0, call=1,
- newunicode=0, unicodegetitem=0,
- unicodesetitem=0, copyunicodecontent=0)
+ self.check_resops(call_pure=0, unicodesetitem=0, call=2,
+ newunicode=0, unicodegetitem=0,
+ copyunicodecontent=0)
def test_join_chars(self):
jitdriver = JitDriver(reds=['a', 'b', 'c', 'i'], greens=[])
@@ -596,9 +590,8 @@
# The "".join should be unrolled, since the length of x is known since
# it is virtual, ensure there are no calls to ll_join_chars, or
# allocations.
- self.check_loops({
- "guard_true": 5, "int_is_true": 3, "int_lt": 2, "int_add": 2, "jump": 2,
- }, everywhere=True)
+ self.check_resops({'jump': 2, 'guard_true': 5, 'int_lt': 2,
+ 'int_add': 2, 'int_is_true': 3})
def test_virtual_copystringcontent(self):
jitdriver = JitDriver(reds=['n', 'result'], greens=[])
diff --git a/pypy/jit/metainterp/test/test_tl.py b/pypy/jit/metainterp/test/test_tl.py
--- a/pypy/jit/metainterp/test/test_tl.py
+++ b/pypy/jit/metainterp/test/test_tl.py
@@ -72,16 +72,16 @@
res = self.meta_interp(main, [0, 6], listops=True,
backendopt=True)
assert res == 5040
- self.check_loops({'int_mul':1, 'jump':1,
- 'int_sub':1, 'int_le':1, 'guard_false':1})
+ self.check_resops({'jump': 2, 'int_le': 2, 'guard_value': 1,
+ 'int_mul': 2, 'guard_false': 2, 'int_sub': 2})
def test_tl_2(self):
main = self._get_main()
res = self.meta_interp(main, [1, 10], listops=True,
backendopt=True)
assert res == main(1, 10)
- self.check_loops({'int_sub':1, 'int_le':1,
- 'guard_false':1, 'jump':1})
+ self.check_resops({'int_le': 2, 'int_sub': 2, 'jump': 2,
+ 'guard_false': 2, 'guard_value': 1})
def test_tl_call(self, listops=True, policy=None):
from pypy.jit.tl.tl import interp
diff --git a/pypy/jit/metainterp/test/test_tracingopts.py b/pypy/jit/metainterp/test/test_tracingopts.py
--- a/pypy/jit/metainterp/test/test_tracingopts.py
+++ b/pypy/jit/metainterp/test/test_tracingopts.py
@@ -593,6 +593,32 @@
res = self.interp_operations(fn, [sys.maxint])
assert res == 12
+ def test_opaque_list(self):
+ from pypy.rlib.rerased import new_erasing_pair
+ erase, unerase = new_erasing_pair("test_opaque_list")
+ def fn(n, ca, cb):
+ l1 = [n]
+ l2 = [n]
+ a1 = erase(l1)
+ a2 = erase(l1)
+ a = a1
+ if ca:
+ a = a2
+ if n < -100:
+ unerase(a).append(5)
+ b = a1
+ if cb:
+ b = a
+ return unerase(a)[0] + unerase(b)[0]
+ res = self.interp_operations(fn, [7, 0, 1])
+ assert res == 7 * 2
+ self.check_operations_history(getarrayitem_gc=0,
+ getfield_gc=0)
+ res = self.interp_operations(fn, [-7, 1, 1])
+ assert res == -7 * 2
+ self.check_operations_history(getarrayitem_gc=0,
+ getfield_gc=0)
+
def test_copy_str_content(self):
def fn(n):
a = StringBuilder()
@@ -601,4 +627,4 @@
return x[0]
res = self.interp_operations(fn, [0])
assert res == 1
- self.check_operations_history(getarrayitem_gc=0, getarrayitem_gc_pure=0 )
\ No newline at end of file
+ self.check_operations_history(getarrayitem_gc=0, getarrayitem_gc_pure=0)
diff --git a/pypy/jit/metainterp/test/test_virtual.py b/pypy/jit/metainterp/test/test_virtual.py
--- a/pypy/jit/metainterp/test/test_virtual.py
+++ b/pypy/jit/metainterp/test/test_virtual.py
@@ -31,8 +31,9 @@
res = self.meta_interp(f, [10])
assert res == 55 * 10
self.check_loop_count(1)
- self.check_loops(new=0, new_with_vtable=0,
- getfield_gc=0, setfield_gc=0)
+ self.check_resops(new_with_vtable=0, setfield_gc=0,
+ getfield_gc=2, new=0)
+
def test_virtualized2(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'node1', 'node2'])
@@ -53,8 +54,8 @@
n -= 1
return node1.value * node2.value
assert f(10) == self.meta_interp(f, [10])
- self.check_loops(new=0, new_with_vtable=0,
- getfield_gc=0, setfield_gc=0)
+ self.check_resops(new_with_vtable=0, setfield_gc=0, getfield_gc=2,
+ new=0)
def test_virtualized_circular1(self):
class MyNode():
@@ -79,8 +80,8 @@
res = self.meta_interp(f, [10])
assert res == 55 * 10
self.check_loop_count(1)
- self.check_loops(new=0, new_with_vtable=0,
- getfield_gc=0, setfield_gc=0)
+ self.check_resops(new_with_vtable=0, setfield_gc=0,
+ getfield_gc=3, new=0)
def test_virtualized_float(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'node'])
@@ -97,7 +98,7 @@
res = self.meta_interp(f, [10])
assert res == f(10)
self.check_loop_count(1)
- self.check_loops(new=0, float_add=0)
+ self.check_resops(new=0, float_add=1)
def test_virtualized_float2(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'node'])
@@ -115,7 +116,8 @@
res = self.meta_interp(f, [10])
assert res == f(10)
self.check_loop_count(1)
- self.check_loops(new=0, float_add=1)
+ self.check_resops(new=0, float_add=2)
+
def test_virtualized_2(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'node'])
@@ -139,8 +141,8 @@
res = self.meta_interp(f, [10])
assert res == 55 * 30
self.check_loop_count(1)
- self.check_loops(new=0, new_with_vtable=0,
- getfield_gc=0, setfield_gc=0)
+ self.check_resops(new_with_vtable=0, setfield_gc=0, getfield_gc=2,
+ new=0)
def test_nonvirtual_obj_delays_loop(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'node'])
@@ -160,8 +162,8 @@
res = self.meta_interp(f, [500])
assert res == 640
self.check_loop_count(1)
- self.check_loops(new=0, new_with_vtable=0,
- getfield_gc=0, setfield_gc=0)
+ self.check_resops(new_with_vtable=0, setfield_gc=0,
+ getfield_gc=1, new=0)
def test_two_loops_with_virtual(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'node'])
@@ -184,8 +186,9 @@
res = self.meta_interp(f, [18])
assert res == f(18)
self.check_loop_count(2)
- self.check_loops(new=0, new_with_vtable=0,
- getfield_gc=0, setfield_gc=0)
+ self.check_resops(new_with_vtable=0, setfield_gc=0,
+ getfield_gc=2, new=0)
+
def test_two_loops_with_escaping_virtual(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'node'])
@@ -212,8 +215,8 @@
res = self.meta_interp(f, [20], policy=StopAtXPolicy(externfn))
assert res == f(20)
self.check_loop_count(3)
- self.check_loops(**{self._new_op: 1})
- self.check_loops(int_mul=0, call=1)
+ self.check_resops(**{self._new_op: 1})
+ self.check_resops(int_mul=0, call=1)
def test_two_virtuals(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'prev'])
@@ -236,7 +239,7 @@
res = self.meta_interp(f, [12])
assert res == 78
- self.check_loops(new_with_vtable=0, new=0)
+ self.check_resops(new_with_vtable=0, new=0)
def test_specialied_bridge(self):
myjitdriver = JitDriver(greens = [], reds = ['y', 'x', 'res'])
@@ -281,7 +284,7 @@
res = self.meta_interp(f, [20])
assert res == 9
- self.check_loops(new_with_vtable=0, new=0)
+ self.check_resops(new_with_vtable=0, new=0)
def test_immutable_constant_getfield(self):
myjitdriver = JitDriver(greens = ['stufflist'], reds = ['n', 'i'])
@@ -307,7 +310,7 @@
res = self.meta_interp(f, [10, 1, 0], listops=True)
assert res == 0
- self.check_loops(getfield_gc=0)
+ self.check_resops(getfield_gc=0)
def test_escapes(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'parent'])
@@ -336,7 +339,7 @@
res = self.meta_interp(f, [10], policy=StopAtXPolicy(g))
assert res == 3
- self.check_loops(**{self._new_op: 1})
+ self.check_resops(**{self._new_op: 1})
def test_virtual_on_virtual(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'parent'])
@@ -366,7 +369,7 @@
res = self.meta_interp(f, [10])
assert res == 2
- self.check_loops(new=0, new_with_vtable=0)
+ self.check_resops(new=0, new_with_vtable=0)
def test_bridge_from_interpreter(self):
mydriver = JitDriver(reds = ['n', 'f'], greens = [])
@@ -841,7 +844,7 @@
del t2
return i
assert self.meta_interp(f, []) == 10
- self.check_loops(new_array=0)
+ self.check_resops(new_array=0)
def test_virtual_streq_bug(self):
mydriver = JitDriver(reds = ['i', 's', 'a'], greens = [])
@@ -942,8 +945,8 @@
res = self.meta_interp(f, [16])
assert res == f(16)
- self.check_loops(getfield_gc=2)
-
+ self.check_resops(getfield_gc=7)
+
# ____________________________________________________________
# Run 1: all the tests instantiate a real RPython class
@@ -985,10 +988,8 @@
res = self.meta_interp(f, [10])
assert res == 20
self.check_loop_count(1)
- self.check_loops(new=0, new_with_vtable=0,
- getfield_gc=0, setfield_gc=0)
-
-
+ self.check_resops(new_with_vtable=0, setfield_gc=0, getfield_gc=0,
+ new=0)
class TestOOtype_Instance(VirtualTests, OOJitMixin):
_new_op = 'new_with_vtable'
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
@@ -77,7 +77,7 @@
return xy.inst_x
res = self.meta_interp(f, [20])
assert res == 30
- self.check_loops(getfield_gc=0, setfield_gc=0)
+ self.check_resops(setfield_gc=0, getfield_gc=0)
def test_preexisting_access_2(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'xy'],
@@ -102,7 +102,7 @@
assert f(5) == 185
res = self.meta_interp(f, [5])
assert res == 185
- self.check_loops(getfield_gc=0, setfield_gc=0)
+ self.check_resops(setfield_gc=0, getfield_gc=0)
def test_two_paths_access(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'xy'],
@@ -124,7 +124,7 @@
return xy.inst_x
res = self.meta_interp(f, [18])
assert res == 10118
- self.check_loops(getfield_gc=0, setfield_gc=0)
+ self.check_resops(setfield_gc=0, getfield_gc=0)
def test_synchronize_in_return(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'xy'],
@@ -146,7 +146,7 @@
return xy.inst_x
res = self.meta_interp(f, [18])
assert res == 10180
- self.check_loops(getfield_gc=0, setfield_gc=0)
+ self.check_resops(setfield_gc=0, getfield_gc=0)
def test_virtualizable_and_greens(self):
myjitdriver = JitDriver(greens = ['m'], reds = ['n', 'xy'],
@@ -174,7 +174,7 @@
return res
res = self.meta_interp(f, [40])
assert res == 50 * 4
- self.check_loops(getfield_gc=0, setfield_gc=0)
+ self.check_resops(setfield_gc=0, getfield_gc=0)
def test_double_frame(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'xy', 'other'],
@@ -197,8 +197,7 @@
return xy.inst_x
res = self.meta_interp(f, [20])
assert res == 134
- self.check_loops(getfield_gc=0, setfield_gc=1)
- self.check_loops(getfield_gc=1, setfield_gc=2, everywhere=True)
+ self.check_resops(setfield_gc=2, getfield_gc=1)
# ------------------------------
@@ -248,8 +247,8 @@
return xy2.inst_l1[2]
res = self.meta_interp(f, [16])
assert res == 3001 + 16 * 80
- self.check_loops(getfield_gc=0, setfield_gc=0,
- getarrayitem_gc=0, setarrayitem_gc=0)
+ self.check_resops(setarrayitem_gc=0, setfield_gc=0,
+ getarrayitem_gc=0, getfield_gc=0)
def test_synchronize_arrays_in_return(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2'],
@@ -279,8 +278,7 @@
assert f(18) == 10360
res = self.meta_interp(f, [18])
assert res == 10360
- self.check_loops(getfield_gc=0, setfield_gc=0,
- getarrayitem_gc=0)
+ self.check_resops(setfield_gc=0, getarrayitem_gc=0, getfield_gc=0)
def test_array_length(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2'],
@@ -306,8 +304,8 @@
return xy2.inst_l1[1]
res = self.meta_interp(f, [18])
assert res == 2941309 + 18
- self.check_loops(getfield_gc=0, setfield_gc=0,
- getarrayitem_gc=0, arraylen_gc=0)
+ self.check_resops(setfield_gc=0, getarrayitem_gc=0,
+ arraylen_gc=0, getfield_gc=0)
def test_residual_function(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2'],
@@ -340,8 +338,8 @@
return xy2.inst_l1[1]
res = self.meta_interp(f, [18])
assert res == 2941309 + 18
- self.check_loops(getfield_gc=0, setfield_gc=0,
- getarrayitem_gc=0, arraylen_gc=1, call=1)
+ self.check_resops(call=2, setfield_gc=0, getarrayitem_gc=0,
+ arraylen_gc=2, getfield_gc=0)
def test_double_frame_array(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'xy2', 'other'],
@@ -377,8 +375,8 @@
expected = f(20)
res = self.meta_interp(f, [20], enable_opts='')
assert res == expected
- self.check_loops(getfield_gc=1, setfield_gc=0,
- arraylen_gc=1, getarrayitem_gc=1, setarrayitem_gc=1)
+ self.check_resops(setarrayitem_gc=1, setfield_gc=0,
+ getarrayitem_gc=1, arraylen_gc=1, getfield_gc=1)
# ------------------------------
@@ -425,8 +423,7 @@
assert f(18) == 10360
res = self.meta_interp(f, [18])
assert res == 10360
- self.check_loops(getfield_gc=0, setfield_gc=0,
- getarrayitem_gc=0)
+ self.check_resops(setfield_gc=0, getarrayitem_gc=0, getfield_gc=0)
# ------------------------------
@@ -460,8 +457,7 @@
res = self.meta_interp(f, [10])
assert res == 55
- self.check_loops(getfield_gc=0, setfield_gc=0)
-
+ self.check_resops(setfield_gc=0, getfield_gc=0)
def test_virtualizable_with_array(self):
myjitdriver = JitDriver(greens = [], reds = ['n', 'x', 'frame'],
@@ -495,8 +491,7 @@
res = self.meta_interp(f, [10, 1], listops=True)
assert res == f(10, 1)
- self.check_loops(getarrayitem_gc=0)
-
+ self.check_resops(getarrayitem_gc=0)
def test_subclass_of_virtualizable(self):
myjitdriver = JitDriver(greens = [], reds = ['frame'],
@@ -524,8 +519,7 @@
res = self.meta_interp(f, [10])
assert res == 55
- self.check_loops(getfield_gc=0, setfield_gc=0)
-
+ self.check_resops(setfield_gc=0, getfield_gc=0)
def test_external_pass(self):
jitdriver = JitDriver(greens = [], reds = ['n', 'z', 'frame'],
@@ -1011,8 +1005,8 @@
res = self.meta_interp(f, [70], listops=True)
assert res == intmask(42 ** 70)
- self.check_loops(int_add=0,
- int_sub=1) # for 'n -= 1' only
+ self.check_resops(int_add=0,
+ int_sub=2) # for 'n -= 1' only
def test_simple_access_directly(self):
myjitdriver = JitDriver(greens = [], reds = ['frame'],
@@ -1043,7 +1037,7 @@
res = self.meta_interp(f, [10])
assert res == 55
- self.check_loops(getfield_gc=0, setfield_gc=0)
+ self.check_resops(setfield_gc=0, getfield_gc=0)
from pypy.jit.backend.test.support import BaseCompiledMixin
if isinstance(self, BaseCompiledMixin):
@@ -1098,42 +1092,42 @@
res = self.meta_interp(f, [10])
assert res == 55
- self.check_loops(new_with_vtable=0)
+ self.check_resops(new_with_vtable=0)
def test_check_for_nonstandardness_only_once(self):
- myjitdriver = JitDriver(greens = [], reds = ['frame'],
- virtualizables = ['frame'])
+ myjitdriver = JitDriver(greens = [], reds = ['frame'],
+ virtualizables = ['frame'])
- class Frame(object):
- _virtualizable2_ = ['x', 'y', 'z']
+ class Frame(object):
+ _virtualizable2_ = ['x', 'y', 'z']
- def __init__(self, x, y, z=1):
- self = hint(self, access_directly=True)
- self.x = x
- self.y = y
- self.z = z
+ def __init__(self, x, y, z=1):
+ self = hint(self, access_directly=True)
+ self.x = x
+ self.y = y
+ self.z = z
- class SomewhereElse:
- pass
- somewhere_else = SomewhereElse()
+ class SomewhereElse:
+ pass
+ somewhere_else = SomewhereElse()
- def f(n):
- frame = Frame(n, 0)
- somewhere_else.top_frame = frame # escapes
- frame = hint(frame, access_directly=True)
- while frame.x > 0:
- myjitdriver.can_enter_jit(frame=frame)
- myjitdriver.jit_merge_point(frame=frame)
- top_frame = somewhere_else.top_frame
- child_frame = Frame(frame.x, top_frame.z, 17)
- frame.y += child_frame.x
- frame.x -= top_frame.z
- return somewhere_else.top_frame.y
-
- 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 f(n):
+ frame = Frame(n, 0)
+ somewhere_else.top_frame = frame # escapes
+ frame = hint(frame, access_directly=True)
+ while frame.x > 0:
+ myjitdriver.can_enter_jit(frame=frame)
+ myjitdriver.jit_merge_point(frame=frame)
+ top_frame = somewhere_else.top_frame
+ child_frame = Frame(frame.x, top_frame.z, 17)
+ frame.y += child_frame.x
+ frame.x -= top_frame.z
+ return somewhere_else.top_frame.y
+
+ res = self.meta_interp(f, [10])
+ assert res == 55
+ self.check_resops(new_with_vtable=0, ptr_eq=1)
+ self.check_history(ptr_eq=2)
def test_virtual_child_frame_with_arrays(self):
myjitdriver = JitDriver(greens = [], reds = ['frame'],
@@ -1165,7 +1159,7 @@
res = self.meta_interp(f, [10], listops=True)
assert res == 55
- self.check_loops(new_with_vtable=0)
+ self.check_resops(new_with_vtable=0)
def test_blackhole_should_not_pay_attention(self):
myjitdriver = JitDriver(greens = [], reds = ['frame'],
@@ -1203,7 +1197,7 @@
res = self.meta_interp(f, [10])
assert res == 155
- self.check_loops(getfield_gc=0, setfield_gc=0)
+ self.check_resops(setfield_gc=0, getfield_gc=0)
def test_blackhole_should_synchronize(self):
myjitdriver = JitDriver(greens = [], reds = ['frame'],
@@ -1239,7 +1233,7 @@
res = self.meta_interp(f, [10])
assert res == 155
- self.check_loops(getfield_gc=0, setfield_gc=0)
+ self.check_resops(setfield_gc=0, getfield_gc=0)
def test_blackhole_should_not_reenter(self):
if not self.basic:
diff --git a/pypy/jit/metainterp/test/test_virtualref.py b/pypy/jit/metainterp/test/test_virtualref.py
--- a/pypy/jit/metainterp/test/test_virtualref.py
+++ b/pypy/jit/metainterp/test/test_virtualref.py
@@ -171,7 +171,7 @@
return 1
#
self.meta_interp(f, [10])
- self.check_loops(new_with_vtable=1) # the vref
+ self.check_resops(new_with_vtable=2) # the vref
self.check_aborted_count(0)
def test_simple_all_removed(self):
@@ -205,8 +205,7 @@
virtual_ref_finish(vref, xy)
#
self.meta_interp(f, [15])
- self.check_loops(new_with_vtable=0, # all virtualized
- new_array=0)
+ self.check_resops(new_with_vtable=0, new_array=0)
self.check_aborted_count(0)
def test_simple_no_access(self):
@@ -242,7 +241,7 @@
virtual_ref_finish(vref, xy)
#
self.meta_interp(f, [15])
- self.check_loops(new_with_vtable=1, # the vref: xy doesn't need to be forced
+ self.check_resops(new_with_vtable=2, # the vref: xy doesn't need to be forced
new_array=0) # and neither xy.next1/2/3
self.check_aborted_count(0)
@@ -280,8 +279,8 @@
exctx.topframeref = vref_None
#
self.meta_interp(f, [15])
- self.check_loops(new_with_vtable=2, # XY(), the vref
- new_array=3) # next1/2/3
+ self.check_resops(new_with_vtable=4, # XY(), the vref
+ new_array=6) # next1/2/3
self.check_aborted_count(0)
def test_simple_force_sometimes(self):
@@ -320,8 +319,8 @@
#
res = self.meta_interp(f, [30])
assert res == 13
- self.check_loops(new_with_vtable=1, # the vref, but not XY()
- new_array=0) # and neither next1/2/3
+ self.check_resops(new_with_vtable=2, # the vref, but not XY()
+ new_array=0) # and neither next1/2/3
self.check_loop_count(1)
self.check_aborted_count(0)
@@ -362,7 +361,7 @@
#
res = self.meta_interp(f, [30])
assert res == 13
- self.check_loops(new_with_vtable=0, # all virtualized in the n!=13 loop
+ self.check_resops(new_with_vtable=0, # all virtualized in the n!=13 loop
new_array=0)
self.check_loop_count(1)
self.check_aborted_count(0)
@@ -412,7 +411,7 @@
res = self.meta_interp(f, [72])
assert res == 6
self.check_loop_count(2) # the loop and the bridge
- self.check_loops(new_with_vtable=2, # loop: nothing; bridge: vref, xy
+ self.check_resops(new_with_vtable=2, # loop: nothing; bridge: vref, xy
new_array=2) # bridge: next4, next5
self.check_aborted_count(0)
@@ -442,8 +441,8 @@
#
res = self.meta_interp(f, [15])
assert res == 1
- self.check_loops(new_with_vtable=2, # vref, xy
- new_array=1) # next1
+ self.check_resops(new_with_vtable=4, # vref, xy
+ new_array=2) # next1
self.check_aborted_count(0)
def test_recursive_call_1(self):
@@ -543,7 +542,7 @@
#
res = self.meta_interp(f, [15])
assert res == 1
- self.check_loops(new_with_vtable=2) # vref, xy
+ self.check_resops(new_with_vtable=4) # vref, xy
def test_cannot_use_invalid_virtualref(self):
myjitdriver = JitDriver(greens = [], reds = ['n'])
diff --git a/pypy/jit/metainterp/test/test_virtualstate.py b/pypy/jit/metainterp/test/test_virtualstate.py
--- a/pypy/jit/metainterp/test/test_virtualstate.py
+++ b/pypy/jit/metainterp/test/test_virtualstate.py
@@ -847,7 +847,8 @@
i5 = arraylen_gc(p2, descr=arraydescr)
i6 = int_ge(i5, 1)
guard_true(i6) []
- jump(p0, p1, p2)
+ p3 = getarrayitem_gc(p2, 0, descr=arraydescr)
+ jump(p0, p1, p3, p2)
"""
self.optimize_bridge(loop, bridge, expected, p0=self.myptr)
diff --git a/pypy/jit/metainterp/test/test_warmspot.py b/pypy/jit/metainterp/test/test_warmspot.py
--- a/pypy/jit/metainterp/test/test_warmspot.py
+++ b/pypy/jit/metainterp/test/test_warmspot.py
@@ -1,10 +1,7 @@
import py
-from pypy.jit.metainterp.warmspot import ll_meta_interp
from pypy.jit.metainterp.warmspot import get_stats
-from pypy.rlib.jit import JitDriver
-from pypy.rlib.jit import unroll_safe
+from pypy.rlib.jit import JitDriver, set_param, unroll_safe
from pypy.jit.backend.llgraph import runner
-from pypy.jit.metainterp.history import BoxInt
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
from pypy.jit.metainterp.optimizeopt import ALL_OPTS_NAMES
@@ -97,18 +94,18 @@
n = A().m(n)
return n
def f(n, enable_opts):
- myjitdriver.set_param('enable_opts', hlstr(enable_opts))
+ set_param(None, 'enable_opts', hlstr(enable_opts))
return g(n)
# check that the set_param will override the default
res = self.meta_interp(f, [10, llstr('')])
assert res == 0
- self.check_loops(new_with_vtable=1)
+ self.check_resops(new_with_vtable=1)
res = self.meta_interp(f, [10, llstr(ALL_OPTS_NAMES)],
enable_opts='')
assert res == 0
- self.check_loops(new_with_vtable=0)
+ self.check_resops(new_with_vtable=0)
def test_unwanted_loops(self):
mydriver = JitDriver(reds = ['n', 'total', 'm'], greens = [])
@@ -163,7 +160,7 @@
return n
self.meta_interp(f, [50], backendopt=True)
self.check_enter_count_at_most(2)
- self.check_loops(call=0)
+ self.check_resops(call=0)
def test_loop_header(self):
# artificial test: we enter into the JIT only when can_enter_jit()
@@ -187,7 +184,7 @@
assert f(15) == 1
res = self.meta_interp(f, [15], backendopt=True)
assert res == 1
- self.check_loops(int_add=1) # I get 13 without the loop_header()
+ self.check_resops(int_add=2) # I get 13 without the loop_header()
def test_omit_can_enter_jit(self):
# Simple test comparing the effects of always giving a can_enter_jit(),
@@ -249,8 +246,8 @@
m = m - 1
self.meta_interp(f1, [8])
self.check_loop_count(1)
- self.check_loops({'int_sub': 1, 'int_gt': 1, 'guard_true': 1,
- 'jump': 1})
+ self.check_resops({'jump': 2, 'guard_true': 2, 'int_gt': 2,
+ 'int_sub': 2})
def test_void_red_variable(self):
mydriver = JitDriver(greens=[], reds=['a', 'm'])
diff --git a/pypy/jit/metainterp/test/test_ztranslation.py b/pypy/jit/metainterp/test/test_ztranslation.py
--- a/pypy/jit/metainterp/test/test_ztranslation.py
+++ b/pypy/jit/metainterp/test/test_ztranslation.py
@@ -1,7 +1,7 @@
import py
from pypy.jit.metainterp.warmspot import rpython_ll_meta_interp, ll_meta_interp
from pypy.jit.backend.llgraph import runner
-from pypy.rlib.jit import JitDriver, unroll_parameters
+from pypy.rlib.jit import JitDriver, unroll_parameters, set_param
from pypy.rlib.jit import PARAMETERS, dont_look_inside, hint
from pypy.jit.metainterp.jitprof import Profiler
from pypy.rpython.lltypesystem import lltype, llmemory
@@ -57,9 +57,9 @@
get_printable_location=get_printable_location)
def f(i):
for param, defl in unroll_parameters:
- jitdriver.set_param(param, defl)
- jitdriver.set_param("threshold", 3)
- jitdriver.set_param("trace_eagerness", 2)
+ set_param(jitdriver, param, defl)
+ set_param(jitdriver, "threshold", 3)
+ set_param(jitdriver, "trace_eagerness", 2)
total = 0
frame = Frame(i)
while frame.l[0] > 3:
@@ -117,8 +117,8 @@
raise ValueError
return 2
def main(i):
- jitdriver.set_param("threshold", 3)
- jitdriver.set_param("trace_eagerness", 2)
+ set_param(jitdriver, "threshold", 3)
+ set_param(jitdriver, "trace_eagerness", 2)
total = 0
n = i
while n > 3:
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
@@ -120,7 +120,8 @@
op = block.operations[i]
if (op.opname == 'jit_marker' and
op.args[0].value == marker_name and
- op.args[1].value.active): # the jitdriver
+ (op.args[1].value is None or
+ op.args[1].value.active)): # the jitdriver
results.append((graph, block, i))
return results
@@ -846,11 +847,18 @@
_, PTR_SET_PARAM_STR_FUNCTYPE = self.cpu.ts.get_FuncType(
[lltype.Ptr(STR)], lltype.Void)
def make_closure(jd, fullfuncname, is_string):
- state = jd.warmstate
- def closure(i):
- if is_string:
- i = hlstr(i)
- getattr(state, fullfuncname)(i)
+ if jd is None:
+ def closure(i):
+ if is_string:
+ i = hlstr(i)
+ for jd in self.jitdrivers_sd:
+ getattr(jd.warmstate, fullfuncname)(i)
+ else:
+ state = jd.warmstate
+ def closure(i):
+ if is_string:
+ i = hlstr(i)
+ getattr(state, fullfuncname)(i)
if is_string:
TP = PTR_SET_PARAM_STR_FUNCTYPE
else:
@@ -859,12 +867,16 @@
return Constant(funcptr, TP)
#
for graph, block, i in find_set_param(graphs):
+
op = block.operations[i]
- for jd in self.jitdrivers_sd:
- if jd.jitdriver is op.args[1].value:
- break
+ if op.args[1].value is not None:
+ for jd in self.jitdrivers_sd:
+ if jd.jitdriver is op.args[1].value:
+ break
+ else:
+ assert 0, "jitdriver of set_param() not found"
else:
- assert 0, "jitdriver of set_param() not found"
+ jd = None
funcname = op.args[2].value
key = jd, funcname
if key not in closures:
diff --git a/pypy/jit/tl/spli/test/test_jit.py b/pypy/jit/tl/spli/test/test_jit.py
--- a/pypy/jit/tl/spli/test/test_jit.py
+++ b/pypy/jit/tl/spli/test/test_jit.py
@@ -36,7 +36,7 @@
i = i + 1
return i
self.interpret(f, [])
- self.check_loops(new_with_vtable=0)
+ self.check_resops(new_with_vtable=0)
def test_bridge(self):
py.test.skip('We currently cant virtualize across bridges')
@@ -52,7 +52,7 @@
return total
self.interpret(f, [1, 10])
- self.check_loops(new_with_vtable=0)
+ self.check_resops(new_with_vtable=0)
def test_bridge_bad_case(self):
py.test.skip('We currently cant virtualize across bridges')
@@ -67,7 +67,7 @@
return a + b
self.interpret(f, [1, 10])
- self.check_loops(new_with_vtable=1) # XXX should eventually be 0?
+ self.check_resops(new_with_vtable=1) # XXX should eventually be 0?
# I think it should be either 0 or 2, 1 makes little sense
# If the loop after entering goes first time to the bridge, a
# is rewrapped again, without preserving the identity. I'm not
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
@@ -95,17 +95,17 @@
return space.newlist(res_w)
-def range_withspecialized_implementation(space, start, step, howmany):
+def range_withspecialized_implementation(space, start, step, length):
assert space.config.objspace.std.withrangelist
- from pypy.objspace.std.rangeobject import W_RangeListObject
- return W_RangeListObject(start, step, howmany)
+ from pypy.objspace.std.listobject import make_range_list
+ return make_range_list(space, start, step, length)
bigint_one = rbigint.fromint(1)
def range_with_longs(space, w_start, w_stop, w_step):
start = lo = space.bigint_w(w_start)
- stop = hi = space.bigint_w(w_stop)
+ hi = space.bigint_w(w_stop)
step = st = space.bigint_w(w_step)
if not step.tobool():
@@ -312,11 +312,10 @@
class W_XRange(Wrappable):
- def __init__(self, space, start, stop, step):
+ def __init__(self, space, start, len, step):
self.space = space
self.start = start
- self.stop = stop
- self.len = get_len_of_range(space, start, stop, step)
+ self.len = len
self.step = step
def descr_new(space, w_subtype, w_start, w_stop=None, w_step=1):
@@ -326,8 +325,9 @@
start, stop = 0, start
else:
stop = _toint(space, w_stop)
+ howmany = get_len_of_range(space, start, stop, step)
obj = space.allocate_instance(W_XRange, w_subtype)
- W_XRange.__init__(obj, space, start, stop, step)
+ W_XRange.__init__(obj, space, start, howmany, step)
return space.wrap(obj)
def descr_repr(self):
@@ -357,12 +357,12 @@
def descr_iter(self):
return self.space.wrap(W_XRangeIterator(self.space, self.start,
- self.stop, self.step))
+ self.len, self.step))
def descr_reversed(self):
lastitem = self.start + (self.len-1) * self.step
return self.space.wrap(W_XRangeIterator(self.space, lastitem,
- self.start - 1, -self.step))
+ self.len, -self.step))
def descr_reduce(self):
space = self.space
@@ -389,24 +389,25 @@
)
class W_XRangeIterator(Wrappable):
- def __init__(self, space, start, stop, step):
+ def __init__(self, space, current, remaining, step):
self.space = space
- self.current = start
- self.stop = stop
+ self.current = current
+ self.remaining = remaining
self.step = step
def descr_iter(self):
return self.space.wrap(self)
def descr_next(self):
- if (self.step > 0 and self.current < self.stop) or (self.step < 0 and self.current > self.stop):
+ if self.remaining > 0:
item = self.current
self.current = item + self.step
+ self.remaining -= 1
return self.space.wrap(item)
raise OperationError(self.space.w_StopIteration, self.space.w_None)
- #def descr_len(self):
- # return self.space.wrap(self.remaining)
+ def descr_len(self):
+ return self.space.wrap(self.remaining)
def descr_reduce(self):
from pypy.interpreter.mixedmodule import MixedModule
@@ -417,7 +418,7 @@
w = space.wrap
nt = space.newtuple
- tup = [w(self.current), w(self.stop), w(self.step)]
+ tup = [w(self.current), w(self.remaining), w(self.step)]
return nt([new_inst, nt(tup)])
W_XRangeIterator.typedef = TypeDef("rangeiterator",
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
@@ -206,24 +206,28 @@
@unwrap_spec(size=int)
def direct_readlines(self, size=0):
stream = self.getstream()
- # NB. this implementation is very inefficient for unbuffered
- # streams, but ok if stream.readline() is efficient.
+ # this is implemented as: .read().split('\n')
+ # except that it keeps the \n in the resulting strings
if size <= 0:
- result = []
- while True:
- line = stream.readline()
- if not line:
- break
- result.append(line)
- size -= len(line)
+ data = stream.readall()
else:
- result = []
- while size > 0:
- line = stream.readline()
- if not line:
- break
- result.append(line)
- size -= len(line)
+ data = stream.read(size)
+ result = []
+ splitfrom = 0
+ for i in range(len(data)):
+ if data[i] == '\n':
+ result.append(data[splitfrom : i + 1])
+ splitfrom = i + 1
+ #
+ if splitfrom < len(data):
+ # there is a partial line at the end. If size > 0, it is likely
+ # to be because the 'read(size)' returned data up to the middle
+ # of a line. In that case, use 'readline()' to read until the
+ # end of the current line.
+ data = data[splitfrom:]
+ if size > 0:
+ data += stream.readline()
+ result.append(data)
return result
@unwrap_spec(offset=r_longlong, whence=int)
diff --git a/pypy/module/_hashlib/interp_hashlib.py b/pypy/module/_hashlib/interp_hashlib.py
--- a/pypy/module/_hashlib/interp_hashlib.py
+++ b/pypy/module/_hashlib/interp_hashlib.py
@@ -4,32 +4,44 @@
from pypy.interpreter.error import OperationError
from pypy.tool.sourcetools import func_renamer
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rlib import rgc, ropenssl
from pypy.rlib.objectmodel import keepalive_until_here
-from pypy.rlib import ropenssl
from pypy.rlib.rstring import StringBuilder
from pypy.module.thread.os_lock import Lock
algorithms = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
+# HASH_MALLOC_SIZE is the size of EVP_MD, EVP_MD_CTX plus their points
+# Used for adding memory pressure. Last number is an (under?)estimate of
+# EVP_PKEY_CTX's size.
+# XXX: Make a better estimate here
+HASH_MALLOC_SIZE = ropenssl.EVP_MD_SIZE + ropenssl.EVP_MD_CTX_SIZE \
+ + rffi.sizeof(ropenssl.EVP_MD) * 2 + 208
+
class W_Hash(Wrappable):
ctx = lltype.nullptr(ropenssl.EVP_MD_CTX.TO)
+ _block_size = -1
def __init__(self, space, name):
self.name = name
+ self.digest_size = self.compute_digest_size()
# Allocate a lock for each HASH object.
# An optimization would be to not release the GIL on small requests,
# and use a custom lock only when needed.
self.lock = Lock(space)
+ ctx = lltype.malloc(ropenssl.EVP_MD_CTX.TO, flavor='raw')
+ rgc.add_memory_pressure(HASH_MALLOC_SIZE + self.digest_size)
+ self.ctx = ctx
+
+ def initdigest(self, space, name):
digest = ropenssl.EVP_get_digestbyname(name)
if not digest:
raise OperationError(space.w_ValueError,
space.wrap("unknown hash function"))
- ctx = lltype.malloc(ropenssl.EVP_MD_CTX.TO, flavor='raw')
- ropenssl.EVP_DigestInit(ctx, digest)
- self.ctx = ctx
+ ropenssl.EVP_DigestInit(self.ctx, digest)
def __del__(self):
# self.lock.free()
@@ -65,33 +77,29 @@
"Return the digest value as a string of hexadecimal digits."
digest = self._digest(space)
hexdigits = '0123456789abcdef'
- result = StringBuilder(self._digest_size() * 2)
+ result = StringBuilder(self.digest_size * 2)
for c in digest:
result.append(hexdigits[(ord(c) >> 4) & 0xf])
result.append(hexdigits[ ord(c) & 0xf])
return space.wrap(result.build())
def get_digest_size(self, space):
- return space.wrap(self._digest_size())
+ return space.wrap(self.digest_size)
def get_block_size(self, space):
- return space.wrap(self._block_size())
+ return space.wrap(self.compute_block_size())
def _digest(self, space):
- copy = self.copy(space)
- ctx = copy.ctx
- digest_size = self._digest_size()
- digest = lltype.malloc(rffi.CCHARP.TO, digest_size, flavor='raw')
+ with lltype.scoped_alloc(ropenssl.EVP_MD_CTX.TO) as ctx:
+ with self.lock:
+ ropenssl.EVP_MD_CTX_copy(ctx, self.ctx)
+ digest_size = self.digest_size
+ with lltype.scoped_alloc(rffi.CCHARP.TO, digest_size) as digest:
+ ropenssl.EVP_DigestFinal(ctx, digest, None)
+ ropenssl.EVP_MD_CTX_cleanup(ctx)
+ return rffi.charpsize2str(digest, digest_size)
- try:
- ropenssl.EVP_DigestFinal(ctx, digest, None)
- return rffi.charpsize2str(digest, digest_size)
- finally:
- keepalive_until_here(copy)
- lltype.free(digest, flavor='raw')
-
-
- def _digest_size(self):
+ def compute_digest_size(self):
# XXX This isn't the nicest way, but the EVP_MD_size OpenSSL
# XXX function is defined as a C macro on OS X and would be
# XXX significantly harder to implement in another way.
@@ -105,12 +113,14 @@
'sha512': 64, 'SHA512': 64,
}.get(self.name, 0)
- def _block_size(self):
+ def compute_block_size(self):
+ if self._block_size != -1:
+ return self._block_size
# XXX This isn't the nicest way, but the EVP_MD_CTX_block_size
# XXX OpenSSL function is defined as a C macro on some systems
# XXX and would be significantly harder to implement in
# XXX another way.
- return {
+ self._block_size = {
'md5': 64, 'MD5': 64,
'sha1': 64, 'SHA1': 64,
'sha224': 64, 'SHA224': 64,
@@ -118,6 +128,7 @@
'sha384': 128, 'SHA384': 128,
'sha512': 128, 'SHA512': 128,
}.get(self.name, 0)
+ return self._block_size
W_Hash.typedef = TypeDef(
'HASH',
@@ -135,6 +146,7 @@
@unwrap_spec(name=str, string='bufferstr')
def new(space, name, string=''):
w_hash = W_Hash(space, name)
+ w_hash.initdigest(space, name)
w_hash.update(space, string)
return space.wrap(w_hash)
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
@@ -4,6 +4,7 @@
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.error import wrap_oserror, OperationError
from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.rlib import rgc
from pypy.rlib.rarithmetic import r_uint
from pypy.translator.tool.cbuild import ExternalCompilationInfo
from pypy.rpython.tool import rffi_platform as platform
@@ -23,6 +24,8 @@
_CreateSemaphore = rwin32.winexternal(
'CreateSemaphoreA', [rffi.VOIDP, rffi.LONG, rffi.LONG, rwin32.LPCSTR],
rwin32.HANDLE)
+ _CloseHandle = rwin32.winexternal('CloseHandle', [rwin32.HANDLE],
+ rwin32.BOOL, threadsafe=False)
_ReleaseSemaphore = rwin32.winexternal(
'ReleaseSemaphore', [rwin32.HANDLE, rffi.LONG, rffi.LONGP],
rwin32.BOOL)
@@ -51,6 +54,7 @@
SEM_FAILED = platform.ConstantInteger('SEM_FAILED')
SEM_VALUE_MAX = platform.ConstantInteger('SEM_VALUE_MAX')
SEM_TIMED_WAIT = platform.Has('sem_timedwait')
+ SEM_T_SIZE = platform.SizeOf('sem_t')
config = platform.configure(CConfig)
TIMEVAL = config['TIMEVAL']
@@ -61,18 +65,21 @@
SEM_FAILED = config['SEM_FAILED'] # rffi.cast(SEM_T, config['SEM_FAILED'])
SEM_VALUE_MAX = config['SEM_VALUE_MAX']
SEM_TIMED_WAIT = config['SEM_TIMED_WAIT']
+ SEM_T_SIZE = config['SEM_T_SIZE']
if sys.platform == 'darwin':
HAVE_BROKEN_SEM_GETVALUE = True
else:
HAVE_BROKEN_SEM_GETVALUE = False
- def external(name, args, result):
+ def external(name, args, result, **kwargs):
return rffi.llexternal(name, args, result,
- compilation_info=eci)
+ compilation_info=eci, **kwargs)
_sem_open = external('sem_open',
[rffi.CCHARP, rffi.INT, rffi.INT, rffi.UINT],
SEM_T)
+ # tread sem_close as not threadsafe for now to be able to use the __del__
+ _sem_close = external('sem_close', [SEM_T], rffi.INT, threadsafe=False)
_sem_unlink = external('sem_unlink', [rffi.CCHARP], rffi.INT)
_sem_wait = external('sem_wait', [SEM_T], rffi.INT)
_sem_trywait = external('sem_trywait', [SEM_T], rffi.INT)
@@ -90,6 +97,11 @@
raise OSError(rposix.get_errno(), "sem_open failed")
return res
+ def sem_close(handle):
+ res = _sem_close(handle)
+ if res < 0:
+ raise OSError(rposix.get_errno(), "sem_close failed")
+
def sem_unlink(name):
res = _sem_unlink(name)
if res < 0:
@@ -205,6 +217,11 @@
raise WindowsError(err, "CreateSemaphore")
return handle
+ def delete_semaphore(handle):
+ if not _CloseHandle(handle):
+ err = rwin32.GetLastError()
+ raise WindowsError(err, "CloseHandle")
+
def semlock_acquire(self, space, block, w_timeout):
if not block:
full_msecs = 0
@@ -291,8 +308,13 @@
sem_unlink(name)
except OSError:
pass
+ else:
+ rgc.add_memory_pressure(SEM_T_SIZE)
return sem
+ def delete_semaphore(handle):
+ sem_close(handle)
+
def semlock_acquire(self, space, block, w_timeout):
if not block:
deadline = lltype.nullptr(TIMESPECP.TO)
@@ -483,6 +505,9 @@
def exit(self, space, __args__):
self.release(space)
+ def __del__(self):
+ delete_semaphore(self.handle)
+
@unwrap_spec(kind=int, value=int, maxvalue=int)
def descr_new(space, w_subtype, kind, value, maxvalue):
if kind != RECURSIVE_MUTEX and kind != SEMAPHORE:
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
@@ -66,10 +66,10 @@
new_generator.running = running
return space.wrap(new_generator)
- at unwrap_spec(current=int, stop=int, step=int)
-def xrangeiter_new(space, current, stop, step):
+ at unwrap_spec(current=int, remaining=int, step=int)
+def xrangeiter_new(space, current, remaining, step):
from pypy.module.__builtin__.functional import W_XRangeIterator
- new_iter = W_XRangeIterator(space, current, stop, step)
+ new_iter = W_XRangeIterator(space, current, remaining, step)
return space.wrap(new_iter)
@unwrap_spec(identifier=str)
diff --git a/pypy/module/_rawffi/structure.py b/pypy/module/_rawffi/structure.py
--- a/pypy/module/_rawffi/structure.py
+++ b/pypy/module/_rawffi/structure.py
@@ -212,6 +212,8 @@
while count + basic_size <= total_size:
fieldtypes.append(basic_ffi_type)
count += basic_size
+ if basic_size == 0: # corner case. get out of this infinite
+ break # loop after 1 iteration ("why not")
self.ffi_struct = clibffi.make_struct_ffitype_e(self.size,
self.alignment,
fieldtypes)
diff --git a/pypy/module/_rawffi/test/test__rawffi.py b/pypy/module/_rawffi/test/test__rawffi.py
--- a/pypy/module/_rawffi/test/test__rawffi.py
+++ b/pypy/module/_rawffi/test/test__rawffi.py
@@ -1022,6 +1022,12 @@
assert ret.y == 1234500, "ret.y == %d" % (ret.y,)
s.free()
+ def test_ffi_type(self):
+ import _rawffi
+ EMPTY = _rawffi.Structure([])
+ S2E = _rawffi.Structure([('bah', (EMPTY, 1))])
+ S2E.get_ffi_type() # does not hang
+
class AppTestAutoFree:
def setup_class(cls):
space = gettestobjspace(usemodules=('_rawffi', 'struct'))
diff --git a/pypy/module/_weakref/test/test_weakref.py b/pypy/module/_weakref/test/test_weakref.py
--- a/pypy/module/_weakref/test/test_weakref.py
+++ b/pypy/module/_weakref/test/test_weakref.py
@@ -324,6 +324,7 @@
class A(object): pass
a = A()
assert _weakref.proxy(a) is _weakref.proxy(a)
+ assert _weakref.proxy(a) is _weakref.proxy(a, None)
def test_callable_proxy(self):
import _weakref, gc
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -392,6 +392,7 @@
'Slice': 'space.gettypeobject(W_SliceObject.typedef)',
'StaticMethod': 'space.gettypeobject(StaticMethod.typedef)',
'CFunction': 'space.gettypeobject(cpyext.methodobject.W_PyCFunctionObject.typedef)',
+ 'WrapperDescr': 'space.gettypeobject(cpyext.methodobject.W_PyCMethodObject.typedef)'
}.items():
GLOBALS['Py%s_Type#' % (cpyname, )] = ('PyTypeObject*', pypyexpr)
diff --git a/pypy/module/cpyext/include/eval.h b/pypy/module/cpyext/include/eval.h
--- a/pypy/module/cpyext/include/eval.h
+++ b/pypy/module/cpyext/include/eval.h
@@ -14,8 +14,8 @@
PyObject * PyEval_CallFunction(PyObject *obj, const char *format, ...);
PyObject * PyEval_CallMethod(PyObject *obj, const char *name, const char *format, ...);
-PyObject * PyObject_CallFunction(PyObject *obj, char *format, ...);
-PyObject * PyObject_CallMethod(PyObject *obj, char *name, char *format, ...);
+PyObject * PyObject_CallFunction(PyObject *obj, const char *format, ...);
+PyObject * PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...);
PyObject * PyObject_CallFunctionObjArgs(PyObject *callable, ...);
PyObject * PyObject_CallMethodObjArgs(PyObject *callable, PyObject *name, ...);
diff --git a/pypy/module/cpyext/include/modsupport.h b/pypy/module/cpyext/include/modsupport.h
--- a/pypy/module/cpyext/include/modsupport.h
+++ b/pypy/module/cpyext/include/modsupport.h
@@ -48,7 +48,11 @@
/*
* This is from pyport.h. Perhaps it belongs elsewhere.
*/
+#ifdef __cplusplus
+#define PyMODINIT_FUNC extern "C" void
+#else
#define PyMODINIT_FUNC void
+#endif
#ifdef __cplusplus
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.6.1"
+#define PYPY_VERSION "1.7.1"
/* Subversion Revision number of this file (not of the repository).
* Empty since Mercurial migration. */
diff --git a/pypy/module/cpyext/include/pycobject.h b/pypy/module/cpyext/include/pycobject.h
--- a/pypy/module/cpyext/include/pycobject.h
+++ b/pypy/module/cpyext/include/pycobject.h
@@ -33,7 +33,7 @@
PyAPI_FUNC(void *) PyCObject_GetDesc(PyObject *);
/* Import a pointer to a C object from a module using a PyCObject. */
-PyAPI_FUNC(void *) PyCObject_Import(char *module_name, char *cobject_name);
+PyAPI_FUNC(void *) PyCObject_Import(const char *module_name, const char *cobject_name);
/* Modify a C object. Fails (==0) if object has a destructor. */
PyAPI_FUNC(int) PyCObject_SetVoidPtr(PyObject *self, void *cobj);
diff --git a/pypy/module/cpyext/include/pyerrors.h b/pypy/module/cpyext/include/pyerrors.h
--- a/pypy/module/cpyext/include/pyerrors.h
+++ b/pypy/module/cpyext/include/pyerrors.h
@@ -11,8 +11,8 @@
(PyClass_Check((x)) || (PyType_Check((x)) && \
PyObject_IsSubclass((x), PyExc_BaseException)))
-PyObject *PyErr_NewException(char *name, PyObject *base, PyObject *dict);
-PyObject *PyErr_NewExceptionWithDoc(char *name, char *doc, PyObject *base, PyObject *dict);
+PyObject *PyErr_NewException(const char *name, PyObject *base, PyObject *dict);
+PyObject *PyErr_NewExceptionWithDoc(const char *name, const char *doc, PyObject *base, PyObject *dict);
PyObject *PyErr_Format(PyObject *exception, const char *format, ...);
/* These APIs aren't really part of the error implementation, but
diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py
--- a/pypy/module/cpyext/listobject.py
+++ b/pypy/module/cpyext/listobject.py
@@ -32,7 +32,7 @@
Py_DecRef(space, w_item)
if not isinstance(w_list, W_ListObject):
PyErr_BadInternalCall(space)
- wrappeditems = w_list.wrappeditems
+ wrappeditems = w_list.getitems()
if index < 0 or index >= len(wrappeditems):
raise OperationError(space.w_IndexError, space.wrap(
"list assignment index out of range"))
@@ -47,7 +47,7 @@
IndexError exception."""
if not isinstance(w_list, W_ListObject):
PyErr_BadInternalCall(space)
- wrappeditems = w_list.wrappeditems
+ wrappeditems = w_list.getitems()
if index < 0 or index >= len(wrappeditems):
raise OperationError(space.w_IndexError, space.wrap(
"list index out of range"))
@@ -74,7 +74,7 @@
"""Macro form of PyList_Size() without error checking.
"""
assert isinstance(w_list, W_ListObject)
- return len(w_list.wrappeditems)
+ return len(w_list.getitems())
@cpython_api([PyObject], Py_ssize_t, error=-1)
diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -240,6 +240,7 @@
def PyStaticMethod_New(space, w_func):
return space.wrap(StaticMethod(w_func))
+ at cpython_api([PyObject, lltype.Ptr(PyMethodDef)], PyObject)
def PyDescr_NewMethod(space, w_type, method):
return space.wrap(W_PyCMethodObject(space, method, w_type))
diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py
--- a/pypy/module/cpyext/modsupport.py
+++ b/pypy/module/cpyext/modsupport.py
@@ -54,9 +54,15 @@
modname = rffi.charp2str(name)
state = space.fromcache(State)
f_name, f_path = state.package_context
- w_mod = PyImport_AddModule(space, f_name)
+ if f_name is not None:
+ modname = f_name
+ w_mod = PyImport_AddModule(space, modname)
+ state.package_context = None, None
- dict_w = {'__file__': space.wrap(f_path)}
+ if f_path is not None:
+ dict_w = {'__file__': space.wrap(f_path)}
+ else:
+ dict_w = {}
convert_method_defs(space, dict_w, methods, None, w_self, modname)
for key, w_value in dict_w.items():
space.setattr(w_mod, space.wrap(key), w_value)
diff --git a/pypy/module/cpyext/presetup.py b/pypy/module/cpyext/presetup.py
--- a/pypy/module/cpyext/presetup.py
+++ b/pypy/module/cpyext/presetup.py
@@ -42,4 +42,4 @@
patch_distutils()
del sys.argv[0]
-execfile(sys.argv[0], {'__file__': sys.argv[0]})
+execfile(sys.argv[0], {'__file__': sys.argv[0], '__name__': '__main__'})
diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py
--- a/pypy/module/cpyext/pyobject.py
+++ b/pypy/module/cpyext/pyobject.py
@@ -116,8 +116,8 @@
try:
return typedescr_cache[typedef]
except KeyError:
- if typedef.base is not None:
- return _get_typedescr_1(typedef.base)
+ if typedef.bases:
+ return _get_typedescr_1(typedef.bases[0])
return typedescr_cache[None]
def get_typedescr(typedef):
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
@@ -56,7 +56,7 @@
PySequence_Fast(), o is not NULL, and that i is within bounds.
"""
if isinstance(w_obj, listobject.W_ListObject):
- w_res = w_obj.wrappeditems[index]
+ w_res = w_obj.getitem(index)
else:
assert isinstance(w_obj, tupleobject.W_TupleObject)
w_res = w_obj.wrappeditems[index]
@@ -70,7 +70,7 @@
PySequence_Fast_GET_SIZE() is faster because it can assume o is a list
or tuple."""
if isinstance(w_obj, listobject.W_ListObject):
- return len(w_obj.wrappeditems)
+ return w_obj.length()
assert isinstance(w_obj, tupleobject.W_TupleObject)
return len(w_obj.wrappeditems)
diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py
--- a/pypy/module/cpyext/slotdefs.py
+++ b/pypy/module/cpyext/slotdefs.py
@@ -9,7 +9,8 @@
unaryfunc, wrapperfunc, ternaryfunc, PyTypeObjectPtr, binaryfunc,
getattrfunc, getattrofunc, setattrofunc, lenfunc, ssizeargfunc,
ssizessizeargfunc, ssizeobjargproc, iternextfunc, initproc, richcmpfunc,
- cmpfunc, hashfunc, descrgetfunc, descrsetfunc, objobjproc, readbufferproc)
+ cmpfunc, hashfunc, descrgetfunc, descrsetfunc, objobjproc, objobjargproc,
+ readbufferproc)
from pypy.module.cpyext.pyobject import from_ref
from pypy.module.cpyext.pyerrors import PyErr_Occurred
from pypy.module.cpyext.state import State
@@ -175,6 +176,15 @@
space.fromcache(State).check_and_raise_exception(always=True)
return space.wrap(res)
+def wrap_objobjargproc(space, w_self, w_args, func):
+ func_target = rffi.cast(objobjargproc, func)
+ check_num_args(space, w_args, 2)
+ w_key, w_value = space.fixedview(w_args)
+ res = generic_cpy_call(space, func_target, w_self, w_key, w_value)
+ if rffi.cast(lltype.Signed, res) == -1:
+ space.fromcache(State).check_and_raise_exception(always=True)
+ return space.wrap(res)
+
def wrap_ssizessizeargfunc(space, w_self, w_args, func):
func_target = rffi.cast(ssizessizeargfunc, func)
check_num_args(space, w_args, 2)
diff --git a/pypy/module/cpyext/src/cobject.c b/pypy/module/cpyext/src/cobject.c
--- a/pypy/module/cpyext/src/cobject.c
+++ b/pypy/module/cpyext/src/cobject.c
@@ -77,7 +77,7 @@
}
void *
-PyCObject_Import(char *module_name, char *name)
+PyCObject_Import(const char *module_name, const char *name)
{
PyObject *m, *c;
void *r = NULL;
diff --git a/pypy/module/cpyext/src/modsupport.c b/pypy/module/cpyext/src/modsupport.c
--- a/pypy/module/cpyext/src/modsupport.c
+++ b/pypy/module/cpyext/src/modsupport.c
@@ -541,7 +541,7 @@
}
PyObject *
-PyObject_CallFunction(PyObject *callable, char *format, ...)
+PyObject_CallFunction(PyObject *callable, const char *format, ...)
{
va_list va;
PyObject *args;
@@ -558,7 +558,7 @@
}
PyObject *
-PyObject_CallMethod(PyObject *o, char *name, char *format, ...)
+PyObject_CallMethod(PyObject *o, const char *name, const char *format, ...)
{
va_list va;
PyObject *args;
diff --git a/pypy/module/cpyext/src/pyerrors.c b/pypy/module/cpyext/src/pyerrors.c
--- a/pypy/module/cpyext/src/pyerrors.c
+++ b/pypy/module/cpyext/src/pyerrors.c
@@ -21,7 +21,7 @@
}
PyObject *
-PyErr_NewException(char *name, PyObject *base, PyObject *dict)
+PyErr_NewException(const char *name, PyObject *base, PyObject *dict)
{
char *dot;
PyObject *modulename = NULL;
@@ -72,7 +72,7 @@
/* Create an exception with docstring */
PyObject *
-PyErr_NewExceptionWithDoc(char *name, char *doc, PyObject *base, PyObject *dict)
+PyErr_NewExceptionWithDoc(const char *name, const char *doc, PyObject *base, PyObject *dict)
{
int result;
PyObject *ret = NULL;
diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py
--- a/pypy/module/cpyext/stubs.py
+++ b/pypy/module/cpyext/stubs.py
@@ -586,10 +586,6 @@
def PyDescr_NewMember(space, type, meth):
raise NotImplementedError
- at cpython_api([PyTypeObjectPtr, PyMethodDef], PyObject)
-def PyDescr_NewMethod(space, type, meth):
- raise NotImplementedError
-
@cpython_api([PyTypeObjectPtr, wrapperbase, rffi.VOIDP], PyObject)
def PyDescr_NewWrapper(space, type, wrapper, wrapped):
raise NotImplementedError
@@ -610,14 +606,6 @@
def PyWrapper_New(space, w_d, w_self):
raise NotImplementedError
- at cpython_api([PyObject], PyObject)
-def PyDictProxy_New(space, dict):
- """Return a proxy object for a mapping which enforces read-only behavior.
- This is normally used to create a proxy to prevent modification of the
- dictionary for non-dynamic class types.
- """
- raise NotImplementedError
-
@cpython_api([PyObject, PyObject, rffi.INT_real], rffi.INT_real, error=-1)
def PyDict_Merge(space, a, b, override):
"""Iterate over mapping object b adding key-value pairs to dictionary a.
@@ -2293,15 +2281,6 @@
changes in your code for properly supporting 64-bit systems."""
raise NotImplementedError
- at cpython_api([rffi.CWCHARP, Py_ssize_t, rffi.CCHARP], PyObject)
-def PyUnicode_EncodeUTF8(space, s, size, errors):
- """Encode the Py_UNICODE buffer of the given size using UTF-8 and return a
- Python string object. Return NULL if an exception was raised by the codec.
-
- This function used an int type for size. This might require
- changes in your code for properly supporting 64-bit systems."""
- raise NotImplementedError
-
@cpython_api([rffi.CCHARP, Py_ssize_t, rffi.CCHARP, rffi.INTP], PyObject)
def PyUnicode_DecodeUTF32(space, s, size, errors, byteorder):
"""Decode length bytes from a UTF-32 encoded buffer string and return the
@@ -2481,31 +2460,6 @@
was raised by the codec."""
raise NotImplementedError
- at cpython_api([rffi.CCHARP, Py_ssize_t, rffi.CCHARP], PyObject)
-def PyUnicode_DecodeLatin1(space, s, size, errors):
- """Create a Unicode object by decoding size bytes of the Latin-1 encoded string
- s. Return NULL if an exception was raised by the codec.
-
- This function used an int type for size. This might require
- changes in your code for properly supporting 64-bit systems."""
- raise NotImplementedError
-
- at cpython_api([rffi.CWCHARP, Py_ssize_t, rffi.CCHARP], PyObject)
-def PyUnicode_EncodeLatin1(space, s, size, errors):
- """Encode the Py_UNICODE buffer of the given size using Latin-1 and return
- a Python string object. Return NULL if an exception was raised by the codec.
-
- This function used an int type for size. This might require
- changes in your code for properly supporting 64-bit systems."""
- raise NotImplementedError
-
- at cpython_api([PyObject], PyObject)
-def PyUnicode_AsLatin1String(space, unicode):
- """Encode a Unicode object using Latin-1 and return the result as Python string
- object. Error handling is "strict". Return NULL if an exception was raised
- by the codec."""
- raise NotImplementedError
-
@cpython_api([rffi.CCHARP, Py_ssize_t, PyObject, rffi.CCHARP], PyObject)
def PyUnicode_DecodeCharmap(space, s, size, mapping, errors):
"""Create a Unicode object by decoding size bytes of the encoded string s using
@@ -2564,13 +2518,6 @@
"""
raise NotImplementedError
- at cpython_api([PyObject], PyObject)
-def PyUnicode_AsMBCSString(space, unicode):
- """Encode a Unicode object using MBCS and return the result as Python string
- object. Error handling is "strict". Return NULL if an exception was raised
- by the codec."""
- raise NotImplementedError
-
@cpython_api([PyObject, PyObject], PyObject)
def PyUnicode_Concat(space, left, right):
"""Concat two strings giving a new Unicode string."""
@@ -2912,16 +2859,3 @@
"""Return true if ob is a proxy object.
"""
raise NotImplementedError
-
- at cpython_api([PyObject, PyObject], PyObject)
-def PyWeakref_NewProxy(space, ob, callback):
- """Return a weak reference proxy object for the object ob. This will always
- return a new reference, but is not guaranteed to create a new object; an
- existing proxy object may be returned. The second parameter, callback, can
- be a callable object that receives notification when ob is garbage
- collected; it should accept a single parameter, which will be the weak
- reference object itself. callback may also be None or NULL. If ob
- is not a weakly-referencable object, or if callback is not callable,
- None, or NULL, this will return NULL and raise TypeError.
- """
- raise NotImplementedError
diff --git a/pypy/module/cpyext/test/test_methodobject.py b/pypy/module/cpyext/test/test_methodobject.py
--- a/pypy/module/cpyext/test/test_methodobject.py
+++ b/pypy/module/cpyext/test/test_methodobject.py
@@ -79,7 +79,7 @@
raises(TypeError, mod.isSameFunction, 1)
class TestPyCMethodObject(BaseApiTest):
- def test_repr(self, space):
+ def test_repr(self, space, api):
"""
W_PyCMethodObject has a repr string which describes it as a method
and gives its name and the name of its class.
@@ -94,7 +94,7 @@
ml.c_ml_meth = rffi.cast(PyCFunction_typedef,
c_func.get_llhelper(space))
- method = PyDescr_NewMethod(space, space.w_str, ml)
+ method = api.PyDescr_NewMethod(space.w_str, ml)
assert repr(method).startswith(
"<built-in method 'func' of 'str' object ")
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -397,3 +397,31 @@
def __str__(self):
return "text"
assert module.tp_str(C()) == "text"
+
+ def test_mp_ass_subscript(self):
+ module = self.import_extension('foo', [
+ ("new_obj", "METH_NOARGS",
+ '''
+ PyObject *obj;
+ Foo_Type.tp_as_mapping = &tp_as_mapping;
+ tp_as_mapping.mp_ass_subscript = mp_ass_subscript;
+ if (PyType_Ready(&Foo_Type) < 0) return NULL;
+ obj = PyObject_New(PyObject, &Foo_Type);
+ return obj;
+ '''
+ )],
+ '''
+ static int
+ mp_ass_subscript(PyObject *self, PyObject *key, PyObject *value)
+ {
+ PyErr_SetNone(PyExc_ZeroDivisionError);
+ return -1;
+ }
+ PyMappingMethods tp_as_mapping;
+ static PyTypeObject Foo_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "foo.foo",
+ };
+ ''')
+ obj = module.new_obj()
+ raises(ZeroDivisionError, obj.__setitem__, 5, None)
diff --git a/pypy/module/cpyext/test/test_unicodeobject.py b/pypy/module/cpyext/test/test_unicodeobject.py
--- a/pypy/module/cpyext/test/test_unicodeobject.py
+++ b/pypy/module/cpyext/test/test_unicodeobject.py
@@ -188,6 +188,12 @@
assert space.unwrap(w_u) == 'sp'
rffi.free_charp(u)
+ def test_encode_utf8(self, space, api):
+ u = rffi.unicode2wcharp(u'sp�m')
+ w_s = api.PyUnicode_EncodeUTF8(u, 4, None)
+ assert space.unwrap(w_s) == u'sp�m'.encode('utf-8')
+ rffi.free_wcharp(u)
+
def test_IS(self, space, api):
for char in [0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x85, 0xa0, 0x1680, 0x2000, 0x2001, 0x2002,
@@ -385,6 +391,24 @@
data, len(u), lltype.nullptr(rffi.CCHARP.TO))
rffi.free_wcharp(data)
+ def test_latin1(self, space, api):
+ s = 'abcdefg'
+ data = rffi.str2charp(s)
+ w_u = api.PyUnicode_DecodeLatin1(data, len(s), lltype.nullptr(rffi.CCHARP.TO))
+ assert space.eq_w(w_u, space.wrap(u"abcdefg"))
+ rffi.free_charp(data)
+
+ uni = u'abcdefg'
+ data = rffi.unicode2wcharp(uni)
+ w_s = api.PyUnicode_EncodeLatin1(data, len(uni), lltype.nullptr(rffi.CCHARP.TO))
+ assert space.eq_w(space.wrap("abcdefg"), w_s)
+ rffi.free_wcharp(data)
+
+ ustr = "abcdef"
+ w_ustr = space.wrap(ustr.decode("ascii"))
+ result = api.PyUnicode_AsLatin1String(w_ustr)
+ assert space.eq_w(space.wrap(ustr), result)
+
def test_format(self, space, api):
w_format = space.wrap(u'hi %s')
w_args = space.wrap((u'test',))
diff --git a/pypy/module/cpyext/test/test_weakref.py b/pypy/module/cpyext/test/test_weakref.py
--- a/pypy/module/cpyext/test/test_weakref.py
+++ b/pypy/module/cpyext/test/test_weakref.py
@@ -15,6 +15,12 @@
assert api.PyErr_Occurred() is space.w_TypeError
api.PyErr_Clear()
+ def test_proxy(self, space, api):
+ w_obj = space.w_Warning # some weakrefable object
+ w_proxy = api.PyWeakref_NewProxy(w_obj, None)
+ assert space.unwrap(space.str(w_proxy)) == "<type 'exceptions.Warning'>"
+ assert space.unwrap(space.repr(w_proxy)).startswith('<weak')
+
def test_weakref_lockobject(self, space, api):
# some new weakrefable object
w_obj = space.call_function(space.w_type, space.wrap("newtype"),
diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py
--- a/pypy/module/cpyext/unicodeobject.py
+++ b/pypy/module/cpyext/unicodeobject.py
@@ -14,6 +14,7 @@
from pypy.module.sys.interp_encoding import setdefaultencoding
from pypy.objspace.std import unicodeobject, unicodetype
from pypy.rlib import runicode
+from pypy.tool.sourcetools import func_renamer
import sys
## See comment in stringobject.py.
@@ -417,26 +418,49 @@
ref[0] = rffi.cast(PyObject, py_newuni)
return 0
- at cpython_api([PyObject], PyObject)
-def PyUnicode_AsUTF8String(space, w_unicode):
- """Encode a Unicode object using UTF-8 and return the result as Python string
- object. Error handling is "strict". Return NULL if an exception was raised
- by the codec."""
- if not PyUnicode_Check(space, w_unicode):
- PyErr_BadArgument(space)
- return unicodetype.encode_object(space, w_unicode, "utf-8", "strict")
+def make_conversion_functions(suffix, encoding):
+ @cpython_api([PyObject], PyObject)
+ @func_renamer('PyUnicode_As%sString' % suffix)
+ def PyUnicode_AsXXXString(space, w_unicode):
+ """Encode a Unicode object and return the result as Python
+ string object. Error handling is "strict". Return NULL if an
+ exception was raised by the codec."""
+ if not PyUnicode_Check(space, w_unicode):
+ PyErr_BadArgument(space)
+ return unicodetype.encode_object(space, w_unicode, encoding, "strict")
- at cpython_api([CONST_STRING, Py_ssize_t, CONST_STRING], PyObject)
-def PyUnicode_DecodeUTF8(space, s, size, errors):
- """Create a Unicode object by decoding size bytes of the UTF-8 encoded string
- s. Return NULL if an exception was raised by the codec.
- """
- w_str = space.wrap(rffi.charpsize2str(s, size))
- if errors:
- w_errors = space.wrap(rffi.charp2str(errors))
- else:
- w_errors = space.w_None
- return space.call_method(w_str, 'decode', space.wrap("utf-8"), w_errors)
+ @cpython_api([CONST_STRING, Py_ssize_t, CONST_STRING], PyObject)
+ @func_renamer('PyUnicode_Decode%s' % suffix)
+ def PyUnicode_DecodeXXX(space, s, size, errors):
+ """Create a Unicode object by decoding size bytes of the
+ encoded string s. Return NULL if an exception was raised by
+ the codec.
+ """
+ w_s = space.wrap(rffi.charpsize2str(s, size))
+ if errors:
+ w_errors = space.wrap(rffi.charp2str(errors))
+ else:
+ w_errors = space.w_None
+ return space.call_method(w_s, 'decode', space.wrap(encoding), w_errors)
+
+ @cpython_api([CONST_WSTRING, Py_ssize_t, CONST_STRING], PyObject)
+ @func_renamer('PyUnicode_Encode%s' % suffix)
+ def PyUnicode_EncodeXXX(space, s, size, errors):
+ """Encode the Py_UNICODE buffer of the given size and return a
+ Python string object. Return NULL if an exception was raised
+ by the codec."""
+ w_u = space.wrap(rffi.wcharpsize2unicode(s, size))
+ if errors:
+ w_errors = space.wrap(rffi.charp2str(errors))
+ else:
+ w_errors = space.w_None
+ return space.call_method(w_u, 'encode', space.wrap(encoding), w_errors)
+
+make_conversion_functions('UTF8', 'utf-8')
+make_conversion_functions('ASCII', 'ascii')
+make_conversion_functions('Latin1', 'latin-1')
+if sys.platform == 'win32':
+ make_conversion_functions('MBCS', 'mbcs')
@cpython_api([rffi.CCHARP, Py_ssize_t, rffi.CCHARP, rffi.INTP], PyObject)
def PyUnicode_DecodeUTF16(space, s, size, llerrors, pbyteorder):
@@ -493,56 +517,6 @@
return space.wrap(result)
- at cpython_api([PyObject], PyObject)
-def PyUnicode_AsASCIIString(space, w_unicode):
- """Encode a Unicode object using ASCII and return the result as Python string
- object. Error handling is "strict". Return NULL if an exception was raised
- by the codec."""
- return space.call_method(w_unicode, 'encode', space.wrap('ascii')) #space.w_None for errors?
-
- at cpython_api([rffi.CCHARP, Py_ssize_t, rffi.CCHARP], PyObject)
-def PyUnicode_DecodeASCII(space, s, size, errors):
- """Create a Unicode object by decoding size bytes of the ASCII encoded string
- s. Return NULL if an exception was raised by the codec."""
- w_s = space.wrap(rffi.charpsize2str(s, size))
- return space.call_method(w_s, 'decode', space.wrap('ascii'))
-
- at cpython_api([rffi.CWCHARP, Py_ssize_t, rffi.CCHARP], PyObject)
-def PyUnicode_EncodeASCII(space, s, size, errors):
- """Encode the Py_UNICODE buffer of the given size using ASCII and return a
- Python string object. Return NULL if an exception was raised by the codec.
- """
-
- w_s = space.wrap(rffi.wcharpsize2unicode(s, size))
- return space.call_method(w_s, 'encode', space.wrap('ascii'))
-
-if sys.platform == 'win32':
- @cpython_api([CONST_WSTRING, Py_ssize_t, CONST_STRING], PyObject)
- def PyUnicode_EncodeMBCS(space, wchar_p, length, errors):
- """Encode the Py_UNICODE buffer of the given size using MBCS and return a
- Python string object. Return NULL if an exception was raised by the codec.
- """
- w_unicode = space.wrap(rffi.wcharpsize2unicode(wchar_p, length))
- if errors:
- w_errors = space.wrap(rffi.charp2str(errors))
- else:
- w_errors = space.w_None
- return space.call_method(w_unicode, "encode",
- space.wrap("mbcs"), w_errors)
-
- @cpython_api([CONST_STRING, Py_ssize_t, CONST_STRING], PyObject)
- def PyUnicode_DecodeMBCS(space, s, size, errors):
- """Create a Unicode object by decoding size bytes of the MBCS encoded string s.
- Return NULL if an exception was raised by the codec.
- """
- w_str = space.wrap(rffi.charpsize2str(s, size))
- w_encoding = space.wrap("mbcs")
- if errors:
- w_errors = space.wrap(rffi.charp2str(errors))
- else:
- w_errors = space.w_None
- return space.call_method(w_str, 'decode', w_encoding, w_errors)
-
@cpython_api([PyObject, PyObject], rffi.INT_real, error=-2)
def PyUnicode_Compare(space, w_left, w_right):
"""Compare two strings and return -1, 0, 1 for less than, equal, and greater
diff --git a/pypy/module/cpyext/weakrefobject.py b/pypy/module/cpyext/weakrefobject.py
--- a/pypy/module/cpyext/weakrefobject.py
+++ b/pypy/module/cpyext/weakrefobject.py
@@ -1,6 +1,6 @@
from pypy.module.cpyext.api import cpython_api
from pypy.module.cpyext.pyobject import PyObject, borrow_from
-from pypy.module._weakref.interp__weakref import W_Weakref
+from pypy.module._weakref.interp__weakref import W_Weakref, proxy
@cpython_api([PyObject, PyObject], PyObject)
def PyWeakref_NewRef(space, w_obj, w_callback):
@@ -16,6 +16,20 @@
w_weakref = space.gettypeobject(W_Weakref.typedef)
return space.call_function(w_weakref, w_obj, w_callback)
+ at cpython_api([PyObject, PyObject], PyObject)
+def PyWeakref_NewProxy(space, w_obj, w_callback):
+ """Return a weak reference proxy object for the object *ob*. This will
+ alwas return a new reference, but is not guaranteed to create a new
+ object; an existing proxy object may be returned. The second parameter,
+ *callback*, can be a callable object that receives notification when *ob*
+ is garbage collected; it should accept a single parameter, which will be
+ the weak reference object itself. *callback* may also be ``None`` or
+ *NULL*. If *ob* is not a weakly-referencable object, or if *callback* is
+ not callable, ``None``, or *NULL*, this will return *NULL* and raise
+ :exc:`TypeError`.
+ """
+ return proxy(space, w_obj, w_callback)
+
@cpython_api([PyObject], PyObject)
def PyWeakref_GetObject(space, w_ref):
"""Return the referenced object from a weak reference. If the referent is
diff --git a/pypy/module/gc/test/test_referents.py b/pypy/module/gc/test/test_referents.py
--- a/pypy/module/gc/test/test_referents.py
+++ b/pypy/module/gc/test/test_referents.py
@@ -7,9 +7,13 @@
from pypy.rlib import rgc
cls._backup = [rgc.get_rpy_roots]
w = cls.space.wrap
+ space = cls.space
class RandomRPythonObject(object):
pass
- cls.ALL_ROOTS = [w(4), w([2, 7]), RandomRPythonObject()]
+ l4 = space.newlist([w(4)])
+ l2 = space.newlist([w(2)])
+ l7 = space.newlist([w(7)])
+ cls.ALL_ROOTS = [l4, space.newlist([l2, l7]), RandomRPythonObject()]
cls.w_ALL_ROOTS = cls.space.newlist(cls.ALL_ROOTS)
rgc.get_rpy_roots = lambda: (
map(rgc._GcRef, cls.ALL_ROOTS) + [rgc.NULL_GCREF]*17)
@@ -41,14 +45,14 @@
if self.runappdirect:
pass # unsure what to test
else:
- assert lst[0] == 4
- assert lst[1] == [2, 7]
+ assert lst[0] == [4]
+ assert lst[1] == [[2], [7]]
assert type(lst[2]) is gc.GcRef
assert len(lst) == 3
def test_get_rpy_referents(self):
import gc
- y = 12345
+ y = [12345]
x = [y]
lst = gc.get_rpy_referents(x)
# After translation, 'lst' should contain the RPython-level list
@@ -88,8 +92,8 @@
def test_get_referents(self):
import gc
- y = 12345
- z = 23456
+ y = [12345]
+ z = [23456]
x = [y, z]
lst = gc.get_referents(x)
assert y in lst and z in lst
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
@@ -513,7 +513,7 @@
space.warn(msg, space.w_ImportWarning)
modtype, suffix, filemode = find_modtype(space, filepart)
try:
- if modtype in (PY_SOURCE, PY_COMPILED):
+ if modtype in (PY_SOURCE, PY_COMPILED, C_EXTENSION):
assert suffix is not None
filename = filepart + suffix
stream = streamio.open_file_as_stream(filename, filemode)
@@ -522,9 +522,6 @@
except:
stream.close()
raise
- if modtype == C_EXTENSION:
- filename = filepart + suffix
- return FindInfo(modtype, filename, None, suffix, filemode)
except StreamErrors:
pass # XXX! must not eat all exceptions, e.g.
# Out of file descriptors.
diff --git a/pypy/module/math/test/test_translated.py b/pypy/module/math/test/test_translated.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/math/test/test_translated.py
@@ -0,0 +1,10 @@
+import py
+from pypy.translator.c.test.test_genc import compile
+from pypy.module.math.interp_math import _gamma
+
+
+def test_gamma_overflow():
+ f = compile(_gamma, [float])
+ assert f(10.0) == 362880.0
+ py.test.raises(OverflowError, f, 1720.0)
+ py.test.raises(OverflowError, f, 172.0)
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
@@ -2,17 +2,19 @@
class Module(MixedModule):
- applevel_name = 'numpy'
+ applevel_name = 'numpypy'
interpleveldefs = {
- 'array': 'interp_numarray.SingleDimArray',
+ 'array': 'interp_numarray.NDimArray',
'dtype': 'interp_dtype.W_Dtype',
'ufunc': 'interp_ufuncs.W_Ufunc',
'zeros': 'interp_numarray.zeros',
'empty': 'interp_numarray.zeros',
'ones': 'interp_numarray.ones',
+ 'dot': 'interp_numarray.dot',
'fromstring': 'interp_support.fromstring',
+ 'flatiter': 'interp_numarray.W_FlatIterator',
'True_': 'space.w_True',
'False_': 'space.w_False',
@@ -48,6 +50,7 @@
("sign", "sign"),
("sin", "sin"),
("subtract", "subtract"),
+ ('sqrt', 'sqrt'),
("tan", "tan"),
]:
interpleveldefs[exposed] = "interp_ufuncs.get(space).%s" % impl
@@ -57,4 +60,5 @@
'mean': 'app_numpy.mean',
'inf': 'app_numpy.inf',
'e': 'app_numpy.e',
+ 'arange': 'app_numpy.arange',
}
diff --git a/pypy/module/micronumpy/app_numpy.py b/pypy/module/micronumpy/app_numpy.py
--- a/pypy/module/micronumpy/app_numpy.py
+++ b/pypy/module/micronumpy/app_numpy.py
@@ -1,17 +1,38 @@
import math
-import numpy
+import numpypy
inf = float("inf")
e = math.e
+
def average(a):
# This implements a weighted average, for now we don't implement the
# weighting, just the average part!
return mean(a)
+
def mean(a):
if not hasattr(a, "mean"):
- a = numpy.array(a)
+ a = numpypy.array(a)
return a.mean()
+
+
+def arange(start, stop=None, step=1, dtype=None):
+ '''arange([start], stop[, step], dtype=None)
+ Generate values in the half-interval [start, stop).
+ '''
+ if stop is None:
+ stop = start
+ start = 0
+ if dtype is None:
+ test = numpypy.array([start, stop, step, 0])
+ dtype = test.dtype
+ arr = numpypy.zeros(int(math.ceil((stop - start) / step)), dtype=dtype)
+ i = start
+ for j in range(arr.size):
+ arr[j] = i
+ j += 1
+ i += step
+ return arr
diff --git a/pypy/module/micronumpy/bench/add.py b/pypy/module/micronumpy/bench/add.py
--- a/pypy/module/micronumpy/bench/add.py
+++ b/pypy/module/micronumpy/bench/add.py
@@ -1,5 +1,8 @@
-import numpy
+try:
+ import numpypy as numpy
+except:
+ import numpy
def f():
a = numpy.zeros(10000000)
diff --git a/pypy/module/micronumpy/bench/iterate.py b/pypy/module/micronumpy/bench/iterate.py
--- a/pypy/module/micronumpy/bench/iterate.py
+++ b/pypy/module/micronumpy/bench/iterate.py
@@ -1,5 +1,8 @@
-import numpy
+try:
+ import numpypy as numpy
+except:
+ import numpy
def f():
sum = 0
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
@@ -6,10 +6,10 @@
from pypy.interpreter.baseobjspace import InternalSpaceCache, W_Root
from pypy.module.micronumpy.interp_dtype import W_Float64Dtype, W_BoolDtype
from pypy.module.micronumpy.interp_numarray import (Scalar, BaseArray,
- descr_new_array, scalar_w, SingleDimArray)
+ descr_new_array, scalar_w, NDimArray)
from pypy.module.micronumpy import interp_ufuncs
from pypy.rlib.objectmodel import specialize
-
+import re
class BogusBytecode(Exception):
pass
@@ -23,11 +23,18 @@
class WrongFunctionName(Exception):
pass
+class TokenizerError(Exception):
+ pass
+
+class BadToken(Exception):
+ pass
+
SINGLE_ARG_FUNCTIONS = ["sum", "prod", "max", "min", "all", "any", "unegative"]
class FakeSpace(object):
w_ValueError = None
w_TypeError = None
+ w_IndexError = None
w_None = None
w_bool = "bool"
@@ -36,6 +43,7 @@
w_list = "list"
w_long = "long"
w_tuple = 'tuple'
+ w_slice = "slice"
def __init__(self):
"""NOT_RPYTHON"""
@@ -43,13 +51,26 @@
self.w_float64dtype = W_Float64Dtype(self)
def issequence_w(self, w_obj):
- return isinstance(w_obj, ListObject) or isinstance(w_obj, SingleDimArray)
+ return isinstance(w_obj, ListObject) or isinstance(w_obj, NDimArray)
def isinstance_w(self, w_obj, w_tp):
+ if w_obj.tp == w_tp:
+ return True
return False
def decode_index4(self, w_idx, size):
- return (self.int_w(w_idx), 0, 0, 1)
+ if isinstance(w_idx, IntObject):
+ return (self.int_w(w_idx), 0, 0, 1)
+ else:
+ assert isinstance(w_idx, SliceObject)
+ start, stop, step = w_idx.start, w_idx.stop, w_idx.step
+ if step == 0:
+ return (0, size, 1, size)
+ if start < 0:
+ start += size
+ if stop < 0:
+ stop += size
+ return (start, stop, step, size//step)
@specialize.argtype(1)
def wrap(self, obj):
@@ -59,7 +80,9 @@
return BoolObject(obj)
elif isinstance(obj, int):
return IntObject(obj)
- raise Exception
+ elif isinstance(obj, W_Root):
+ return obj
+ raise NotImplementedError
def newlist(self, items):
return ListObject(items)
@@ -67,6 +90,7 @@
def listview(self, obj):
assert isinstance(obj, ListObject)
return obj.items
+ fixedview = listview
def float(self, w_obj):
assert isinstance(w_obj, FloatObject)
@@ -107,6 +131,12 @@
assert isinstance(what, tp)
return what
+ def len_w(self, w_obj):
+ if isinstance(w_obj, ListObject):
+ return len(w_obj.items)
+ # XXX array probably
+ assert False
+
class FloatObject(W_Root):
tp = FakeSpace.w_float
def __init__(self, floatval):
@@ -127,6 +157,13 @@
def __init__(self, items):
self.items = items
+class SliceObject(W_Root):
+ tp = FakeSpace.w_slice
+ def __init__(self, start, stop, step):
+ self.start = start
+ self.stop = stop
+ self.step = step
+
class InterpreterState(object):
def __init__(self, code):
self.code = code
@@ -161,7 +198,7 @@
interp.variables[self.name] = self.expr.execute(interp)
def __repr__(self):
- return "%% = %r" % (self.name, self.expr)
+ return "%r = %r" % (self.name, self.expr)
class ArrayAssignment(Node):
def __init__(self, name, index, expr):
@@ -171,8 +208,11 @@
def execute(self, interp):
arr = interp.variables[self.name]
- w_index = self.index.execute(interp).eval(0).wrap(interp.space)
- w_val = self.expr.execute(interp).eval(0).wrap(interp.space)
+ w_index = self.index.execute(interp).eval(arr.start_iter()).wrap(interp.space)
+ # cast to int
+ if isinstance(w_index, FloatObject):
+ w_index = IntObject(int(w_index.floatval))
+ w_val = self.expr.execute(interp).eval(arr.start_iter()).wrap(interp.space)
arr.descr_setitem(interp.space, w_index, w_val)
def __repr__(self):
@@ -180,7 +220,7 @@
class Variable(Node):
def __init__(self, name):
- self.name = name
+ self.name = name.strip(" ")
def execute(self, interp):
return interp.variables[self.name]
@@ -196,11 +236,11 @@
def execute(self, interp):
w_lhs = self.lhs.execute(interp)
+ if isinstance(self.rhs, SliceConstant):
+ w_rhs = self.rhs.wrap(interp.space)
+ else:
+ w_rhs = self.rhs.execute(interp)
assert isinstance(w_lhs, BaseArray)
- if isinstance(self.rhs, SliceConstant):
- # XXX interface has changed on multidim branch
- raise NotImplementedError
- w_rhs = self.rhs.execute(interp)
if self.name == '+':
w_res = w_lhs.descr_add(interp.space, w_rhs)
elif self.name == '*':
@@ -209,12 +249,10 @@
w_res = w_lhs.descr_sub(interp.space, w_rhs)
elif self.name == '->':
if isinstance(w_rhs, Scalar):
- index = int(interp.space.float_w(
- w_rhs.value.wrap(interp.space)))
- dtype = interp.space.fromcache(W_Float64Dtype)
- return Scalar(dtype, w_lhs.get_concrete().eval(index))
- else:
- raise NotImplementedError
+ w_rhs = w_rhs.eval(w_rhs.start_iter()).wrap(interp.space)
+ assert isinstance(w_rhs, FloatObject)
+ w_rhs = IntObject(int(w_rhs.floatval))
+ w_res = w_lhs.descr_getitem(interp.space, w_rhs)
else:
raise NotImplementedError
if not isinstance(w_res, BaseArray):
@@ -248,7 +286,8 @@
w_list = interp.space.newlist(
[interp.space.wrap(float(i)) for i in range(self.v)])
dtype = interp.space.fromcache(W_Float64Dtype)
- return descr_new_array(interp.space, None, w_list, w_dtype=dtype)
+ return descr_new_array(interp.space, None, w_list, w_dtype=dtype,
+ w_order=None)
def __repr__(self):
return 'Range(%s)' % self.v
@@ -270,17 +309,24 @@
def execute(self, interp):
w_list = self.wrap(interp.space)
dtype = interp.space.fromcache(W_Float64Dtype)
- return descr_new_array(interp.space, None, w_list, w_dtype=dtype)
+ return descr_new_array(interp.space, None, w_list, w_dtype=dtype,
+ w_order=None)
def __repr__(self):
return "[" + ", ".join([repr(item) for item in self.items]) + "]"
class SliceConstant(Node):
- def __init__(self):
- pass
+ def __init__(self, start, stop, step):
+ # no negative support for now
+ self.start = start
+ self.stop = stop
+ self.step = step
+
+ def wrap(self, space):
+ return SliceObject(self.start, self.stop, self.step)
def __repr__(self):
- return 'slice()'
+ return 'slice(%s,%s,%s)' % (self.start, self.stop, self.step)
class Execute(Node):
def __init__(self, expr):
@@ -294,7 +340,7 @@
class FunctionCall(Node):
def __init__(self, name, args):
- self.name = name
+ self.name = name.strip(" ")
self.args = args
def __repr__(self):
@@ -337,95 +383,172 @@
else:
raise WrongFunctionName
+_REGEXES = [
+ ('-?[\d\.]+', 'number'),
+ ('\[', 'array_left'),
+ (':', 'colon'),
+ ('\w+', 'identifier'),
+ ('\]', 'array_right'),
+ ('(->)|[\+\-\*\/]', 'operator'),
+ ('=', 'assign'),
+ (',', 'coma'),
+ ('\|', 'pipe'),
+ ('\(', 'paren_left'),
+ ('\)', 'paren_right'),
+]
+REGEXES = []
+
+for r, name in _REGEXES:
+ REGEXES.append((re.compile(r' *(' + r + ')'), name))
+del _REGEXES
+
+class Token(object):
+ def __init__(self, name, v):
+ self.name = name
+ self.v = v
+
+ def __repr__(self):
+ return '(%s, %s)' % (self.name, self.v)
+
+empty = Token('', '')
+
+class TokenStack(object):
+ def __init__(self, tokens):
+ self.tokens = tokens
+ self.c = 0
+
+ def pop(self):
+ token = self.tokens[self.c]
+ self.c += 1
+ return token
+
+ def get(self, i):
+ if self.c + i >= len(self.tokens):
+ return empty
+ return self.tokens[self.c + i]
+
+ def remaining(self):
+ return len(self.tokens) - self.c
+
+ def push(self):
+ self.c -= 1
+
+ def __repr__(self):
+ return repr(self.tokens[self.c:])
+
class Parser(object):
- def parse_identifier(self, id):
- id = id.strip(" ")
- #assert id.isalpha()
- return Variable(id)
+ def tokenize(self, line):
+ tokens = []
+ while True:
+ for r, name in REGEXES:
+ m = r.match(line)
+ if m is not None:
+ g = m.group(0)
+ tokens.append(Token(name, g))
+ line = line[len(g):]
+ if not line:
+ return TokenStack(tokens)
+ break
+ else:
+ raise TokenizerError(line)
- def parse_expression(self, expr):
- tokens = [i for i in expr.split(" ") if i]
- if len(tokens) == 1:
- return self.parse_constant_or_identifier(tokens[0])
+ def parse_number_or_slice(self, tokens):
+ start_tok = tokens.pop()
+ if start_tok.name == 'colon':
+ start = 0
+ else:
+ if tokens.get(0).name != 'colon':
+ return FloatConstant(start_tok.v)
+ start = int(start_tok.v)
+ tokens.pop()
+ if not tokens.get(0).name in ['colon', 'number']:
+ stop = -1
+ step = 1
+ else:
+ next = tokens.pop()
+ if next.name == 'colon':
+ stop = -1
+ step = int(tokens.pop().v)
+ else:
+ stop = int(next.v)
+ if tokens.get(0).name == 'colon':
+ tokens.pop()
+ step = int(tokens.pop().v)
+ else:
+ step = 1
+ return SliceConstant(start, stop, step)
+
+
+ def parse_expression(self, tokens):
stack = []
- tokens.reverse()
- while tokens:
+ while tokens.remaining():
token = tokens.pop()
- if token == ')':
- raise NotImplementedError
- elif self.is_identifier_or_const(token):
- if stack:
- name = stack.pop().name
- lhs = stack.pop()
- rhs = self.parse_constant_or_identifier(token)
- stack.append(Operator(lhs, name, rhs))
+ if token.name == 'identifier':
+ if tokens.remaining() and tokens.get(0).name == 'paren_left':
+ stack.append(self.parse_function_call(token.v, tokens))
else:
- stack.append(self.parse_constant_or_identifier(token))
+ stack.append(Variable(token.v))
+ elif token.name == 'array_left':
+ stack.append(ArrayConstant(self.parse_array_const(tokens)))
+ elif token.name == 'operator':
+ stack.append(Variable(token.v))
+ elif token.name == 'number' or token.name == 'colon':
+ tokens.push()
+ stack.append(self.parse_number_or_slice(tokens))
+ elif token.name == 'pipe':
+ stack.append(RangeConstant(tokens.pop().v))
+ end = tokens.pop()
+ assert end.name == 'pipe'
else:
- stack.append(Variable(token))
- assert len(stack) == 1
- return stack[-1]
+ tokens.push()
+ break
+ stack.reverse()
+ lhs = stack.pop()
+ while stack:
+ op = stack.pop()
+ assert isinstance(op, Variable)
+ rhs = stack.pop()
+ lhs = Operator(lhs, op.name, rhs)
+ return lhs
- def parse_constant(self, v):
- lgt = len(v)-1
- assert lgt >= 0
- if ':' in v:
- # a slice
- assert v == ':'
- return SliceConstant()
- if v[0] == '[':
- return ArrayConstant([self.parse_constant(elem)
- for elem in v[1:lgt].split(",")])
- if v[0] == '|':
- return RangeConstant(v[1:lgt])
- return FloatConstant(v)
-
- def is_identifier_or_const(self, v):
- c = v[0]
- if ((c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z') or
- (c >= '0' and c <= '9') or c in '-.[|:'):
- if v == '-' or v == "->":
- return False
- return True
- return False
-
- def parse_function_call(self, v):
- l = v.split('(')
- assert len(l) == 2
- name = l[0]
- cut = len(l[1]) - 1
- assert cut >= 0
- args = [self.parse_constant_or_identifier(id)
- for id in l[1][:cut].split(",")]
+ def parse_function_call(self, name, tokens):
+ args = []
+ tokens.pop() # lparen
+ while tokens.get(0).name != 'paren_right':
+ args.append(self.parse_expression(tokens))
return FunctionCall(name, args)
- def parse_constant_or_identifier(self, v):
- c = v[0]
- if (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z'):
- if '(' in v:
- return self.parse_function_call(v)
- return self.parse_identifier(v)
- return self.parse_constant(v)
-
- def parse_array_subscript(self, v):
- v = v.strip(" ")
- l = v.split("[")
- lgt = len(l[1]) - 1
- assert lgt >= 0
- rhs = self.parse_constant_or_identifier(l[1][:lgt])
- return l[0], rhs
+ def parse_array_const(self, tokens):
+ elems = []
+ while True:
+ token = tokens.pop()
+ if token.name == 'number':
+ elems.append(FloatConstant(token.v))
+ elif token.name == 'array_left':
+ elems.append(ArrayConstant(self.parse_array_const(tokens)))
+ else:
+ raise BadToken()
+ token = tokens.pop()
+ if token.name == 'array_right':
+ return elems
+ assert token.name == 'coma'
- def parse_statement(self, line):
- if '=' in line:
- lhs, rhs = line.split("=")
- lhs = lhs.strip(" ")
- if '[' in lhs:
- name, index = self.parse_array_subscript(lhs)
- return ArrayAssignment(name, index, self.parse_expression(rhs))
- else:
- return Assignment(lhs, self.parse_expression(rhs))
- else:
- return Execute(self.parse_expression(line))
+ def parse_statement(self, tokens):
+ if (tokens.get(0).name == 'identifier' and
+ tokens.get(1).name == 'assign'):
+ lhs = tokens.pop().v
+ tokens.pop()
+ rhs = self.parse_expression(tokens)
+ return Assignment(lhs, rhs)
+ elif (tokens.get(0).name == 'identifier' and
+ tokens.get(1).name == 'array_left'):
+ name = tokens.pop().v
+ tokens.pop()
+ index = self.parse_expression(tokens)
+ tokens.pop()
+ tokens.pop()
+ return ArrayAssignment(name, index, self.parse_expression(tokens))
+ return Execute(self.parse_expression(tokens))
def parse(self, code):
statements = []
@@ -434,7 +557,8 @@
line = line.split('#', 1)[0]
line = line.strip(" ")
if line:
- statements.append(self.parse_statement(line))
+ tokens = self.tokenize(line)
+ statements.append(self.parse_statement(tokens))
return Code(statements)
def numpy_compile(code):
diff --git a/pypy/module/micronumpy/interp_dtype.py b/pypy/module/micronumpy/interp_dtype.py
--- a/pypy/module/micronumpy/interp_dtype.py
+++ b/pypy/module/micronumpy/interp_dtype.py
@@ -131,6 +131,7 @@
def binop(func):
+ func._annspecialcase_ = "specialize:call_location"
@functools.wraps(func)
def impl(self, v1, v2):
return self.adapt_val(func(self,
@@ -221,6 +222,7 @@
@binop
def div(self, v1, v2):
+ # XXX this won't work after translation, probably requires ovfcheck
try:
return v1 / v2
except ZeroDivisionError:
@@ -292,6 +294,12 @@
if not -1.0 < v < 1.0:
return rfloat.NAN
return math.atanh(v)
+ @unaryop
+ def sqrt(self, v):
+ try:
+ return math.sqrt(v)
+ except ValueError:
+ return rfloat.NAN
class IntegerArithmeticDtype(ArithmeticTypeMixin):
_mixin_ = True
@@ -313,6 +321,18 @@
@binop
def mod(self, v1, v2):
return v1 % v2
+ @binop
+ def pow(self, v1, v2):
+ res = 1
+ while v2 > 0:
+ if v2 & 1:
+ res *= v1
+ v2 >>= 1
+ if v2 == 0:
+ break
+ v1 *= v1
+ return res
+
class SignedIntegerArithmeticDtype(IntegerArithmeticDtype):
_mixin_ = True
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,45 +1,370 @@
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.error import OperationError
-from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.gateway import interp2app, unwrap_spec, NoneNotWrapped
from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.module.micronumpy import interp_ufuncs, interp_dtype, signature
from pypy.rlib import jit
-from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.lltypesystem import lltype, rffi
from pypy.tool.sourcetools import func_with_new_name
+from pypy.rlib.rstring import StringBuilder
+from pypy.rlib.objectmodel import instantiate
-numpy_driver = jit.JitDriver(greens = ['signature'],
- reds = ['result_size', 'i', 'self', 'result'])
-all_driver = jit.JitDriver(greens=['signature'], reds=['i', 'size', 'self', 'dtype'])
-any_driver = jit.JitDriver(greens=['signature'], reds=['i', 'size', 'self', 'dtype'])
-slice_driver = jit.JitDriver(greens=['signature'], reds=['i', 'j', 'step', 'stop', 'source', 'dest'])
+numpy_driver = jit.JitDriver(
+ greens=['shapelen', 'signature'],
+ reds=['result_size', 'i', 'ri', 'self', 'result']
+)
+all_driver = jit.JitDriver(
+ greens=['shapelen', 'signature'],
+ reds=['i', 'self', 'dtype']
+)
+any_driver = jit.JitDriver(
+ greens=['shapelen', 'signature'],
+ reds=['i', 'self', 'dtype']
+)
+slice_driver = jit.JitDriver(
+ greens=['shapelen', 'signature'],
+ reds=['self', 'source', 'source_iter', 'res_iter']
+)
-def descr_new_array(space, w_subtype, w_size_or_iterable, w_dtype=None):
- l = space.listview(w_size_or_iterable)
+def _find_shape_and_elems(space, w_iterable):
+ shape = [space.len_w(w_iterable)]
+ batch = space.listview(w_iterable)
+ while True:
+ new_batch = []
+ if not batch:
+ return shape, []
+ if not space.issequence_w(batch[0]):
+ for elem in batch:
+ if space.issequence_w(elem):
+ raise OperationError(space.w_ValueError, space.wrap(
+ "setting an array element with a sequence"))
+ return shape, batch
+ size = space.len_w(batch[0])
+ for w_elem in batch:
+ if not space.issequence_w(w_elem) or space.len_w(w_elem) != size:
+ raise OperationError(space.w_ValueError, space.wrap(
+ "setting an array element with a sequence"))
+ new_batch += space.listview(w_elem)
+ shape.append(size)
+ batch = new_batch
+
+def shape_agreement(space, shape1, shape2):
+ ret = _shape_agreement(shape1, shape2)
+ if len(ret) < max(len(shape1), len(shape2)):
+ raise OperationError(space.w_ValueError,
+ space.wrap("operands could not be broadcast together with shapes (%s) (%s)" % (
+ ",".join([str(x) for x in shape1]),
+ ",".join([str(x) for x in shape2]),
+ ))
+ )
+ return ret
+
+def _shape_agreement(shape1, shape2):
+ """ Checks agreement about two shapes with respect to broadcasting. Returns
+ the resulting shape.
+ """
+ lshift = 0
+ rshift = 0
+ if len(shape1) > len(shape2):
+ m = len(shape1)
+ n = len(shape2)
+ rshift = len(shape2) - len(shape1)
+ remainder = shape1
+ else:
+ m = len(shape2)
+ n = len(shape1)
+ lshift = len(shape1) - len(shape2)
+ remainder = shape2
+ endshape = [0] * m
+ indices1 = [True] * m
+ indices2 = [True] * m
+ for i in range(m - 1, m - n - 1, -1):
+ left = shape1[i + lshift]
+ right = shape2[i + rshift]
+ if left == right:
+ endshape[i] = left
+ elif left == 1:
+ endshape[i] = right
+ indices1[i + lshift] = False
+ elif right == 1:
+ endshape[i] = left
+ indices2[i + rshift] = False
+ else:
+ return []
+ #raise OperationError(space.w_ValueError, space.wrap(
+ # "frames are not aligned"))
+ for i in range(m - n):
+ endshape[i] = remainder[i]
+ return endshape
+
+def descr_new_array(space, w_subtype, w_item_or_iterable, w_dtype=None,
+ w_order=NoneNotWrapped):
+ # find scalar
+ if not space.issequence_w(w_item_or_iterable):
+ if space.is_w(w_dtype, space.w_None):
+ w_dtype = interp_ufuncs.find_dtype_for_scalar(space,
+ w_item_or_iterable)
+ dtype = space.interp_w(interp_dtype.W_Dtype,
+ space.call_function(space.gettypefor(interp_dtype.W_Dtype), w_dtype)
+ )
+ return scalar_w(space, dtype, w_item_or_iterable)
+ if w_order is None:
+ order = 'C'
+ else:
+ order = space.str_w(w_order)
+ if order != 'C': # or order != 'F':
+ raise operationerrfmt(space.w_ValueError, "Unknown order: %s",
+ order)
+ shape, elems_w = _find_shape_and_elems(space, w_item_or_iterable)
+ # they come back in C order
+ size = len(elems_w)
if space.is_w(w_dtype, space.w_None):
w_dtype = None
- for w_item in l:
- w_dtype = interp_ufuncs.find_dtype_for_scalar(space, w_item, w_dtype)
+ for w_elem in elems_w:
+ w_dtype = interp_ufuncs.find_dtype_for_scalar(space, w_elem,
+ w_dtype)
if w_dtype is space.fromcache(interp_dtype.W_Float64Dtype):
break
- if w_dtype is None:
- w_dtype = space.w_None
-
+ if w_dtype is None:
+ w_dtype = space.w_None
dtype = space.interp_w(interp_dtype.W_Dtype,
space.call_function(space.gettypefor(interp_dtype.W_Dtype), w_dtype)
)
- arr = SingleDimArray(len(l), dtype=dtype)
- i = 0
- for w_elem in l:
- dtype.setitem_w(space, arr.storage, i, w_elem)
- i += 1
+ arr = NDimArray(size, shape[:], dtype=dtype, order=order)
+ shapelen = len(shape)
+ arr_iter = arr.start_iter(arr.shape)
+ for i in range(len(elems_w)):
+ w_elem = elems_w[i]
+ dtype.setitem_w(space, arr.storage, arr_iter.offset, w_elem)
+ arr_iter = arr_iter.next(shapelen)
return arr
+# Iterators for arrays
+# --------------------
+# all those iterators with the exception of BroadcastIterator iterate over the
+# entire array in C order (the last index changes the fastest). This will
+# yield all elements. Views iterate over indices and look towards strides and
+# backstrides to find the correct position. Notably the offset between
+# x[..., i + 1] and x[..., i] will be strides[-1]. Offset between
+# x[..., k + 1, 0] and x[..., k, i_max] will be backstrides[-2] etc.
+
+# BroadcastIterator works like that, but for indexes that don't change source
+# in the original array, strides[i] == backstrides[i] == 0
+
+class BaseIterator(object):
+ def next(self, shapelen):
+ raise NotImplementedError
+
+ def done(self):
+ raise NotImplementedError
+
+ def get_offset(self):
+ raise NotImplementedError
+
+class ArrayIterator(BaseIterator):
+ def __init__(self, size):
+ self.offset = 0
+ self.size = size
+
+ def next(self, shapelen):
+ arr = instantiate(ArrayIterator)
+ arr.size = self.size
+ arr.offset = self.offset + 1
+ return arr
+
+ def done(self):
+ return self.offset >= self.size
+
+ def get_offset(self):
+ return self.offset
+
+class OneDimIterator(BaseIterator):
+ def __init__(self, start, step, size):
+ self.offset = start
+ self.step = step
+ self.size = size
+
+ def next(self, shapelen):
+ arr = instantiate(OneDimIterator)
+ arr.size = self.size
+ arr.step = self.step
+ arr.offset = self.offset + self.step
+ return arr
+
+ def done(self):
+ return self.offset >= self.size
+
+ def get_offset(self):
+ return self.offset
+
+class ViewIterator(BaseIterator):
+ def __init__(self, arr):
+ self.indices = [0] * len(arr.shape)
+ self.offset = arr.start
+ self.arr = arr
+ self._done = False
+
+ @jit.unroll_safe
+ def next(self, shapelen):
+ offset = self.offset
+ indices = [0] * shapelen
+ for i in range(shapelen):
+ indices[i] = self.indices[i]
+ done = False
+ for i in range(shapelen - 1, -1, -1):
+ if indices[i] < self.arr.shape[i] - 1:
+ indices[i] += 1
+ offset += self.arr.strides[i]
+ break
+ else:
+ indices[i] = 0
+ offset -= self.arr.backstrides[i]
+ else:
+ done = True
+ res = instantiate(ViewIterator)
+ res.offset = offset
+ res.indices = indices
+ res.arr = self.arr
+ res._done = done
+ return res
+
+ def done(self):
+ return self._done
+
+ def get_offset(self):
+ return self.offset
+
+class BroadcastIterator(BaseIterator):
+ '''Like a view iterator, but will repeatedly access values
+ for all iterations across a res_shape, folding the offset
+ using mod() arithmetic
+ '''
+ def __init__(self, arr, res_shape):
+ self.indices = [0] * len(res_shape)
+ self.offset = arr.start
+ #strides are 0 where original shape==1
+ self.strides = []
+ self.backstrides = []
+ for i in range(len(arr.shape)):
+ if arr.shape[i] == 1:
+ self.strides.append(0)
+ self.backstrides.append(0)
+ else:
+ self.strides.append(arr.strides[i])
+ self.backstrides.append(arr.backstrides[i])
+ self.res_shape = res_shape
+ self.strides = [0] * (len(res_shape) - len(arr.shape)) + self.strides
+ self.backstrides = [0] * (len(res_shape) - len(arr.shape)) + self.backstrides
+ self._done = False
+
+ @jit.unroll_safe
+ def next(self, shapelen):
+ offset = self.offset
+ indices = [0] * shapelen
+ _done = False
+ for i in range(shapelen):
+ indices[i] = self.indices[i]
+ for i in range(shapelen - 1, -1, -1):
+ if indices[i] < self.res_shape[i] - 1:
+ indices[i] += 1
+ offset += self.strides[i]
+ break
+ else:
+ indices[i] = 0
+ offset -= self.backstrides[i]
+ else:
+ _done = True
+ res = instantiate(BroadcastIterator)
+ res.indices = indices
+ res.offset = offset
+ res._done = _done
+ res.strides = self.strides
+ res.backstrides = self.backstrides
+ res.res_shape = self.res_shape
+ return res
+
+ def done(self):
+ return self._done
+
+ def get_offset(self):
+ return self.offset
+
+class Call2Iterator(BaseIterator):
+ def __init__(self, left, right):
+ self.left = left
+ self.right = right
+
+ def next(self, shapelen):
+ return Call2Iterator(self.left.next(shapelen),
+ self.right.next(shapelen))
+
+ def done(self):
+ if isinstance(self.left, ConstantIterator):
+ return self.right.done()
+ return self.left.done()
+
+ def get_offset(self):
+ if isinstance(self.left, ConstantIterator):
+ return self.right.get_offset()
+ return self.left.get_offset()
+
+class Call1Iterator(BaseIterator):
+ def __init__(self, child):
+ self.child = child
+
+ def next(self, shapelen):
+ return Call1Iterator(self.child.next(shapelen))
+
+ def done(self):
+ return self.child.done()
+
+ def get_offset(self):
+ return self.child.get_offset()
+
+class ConstantIterator(BaseIterator):
+ def next(self, shapelen):
+ return self
+
+ def done(self):
+ return False
+
+ def get_offset(self):
+ return 0
+
+
class BaseArray(Wrappable):
- _attrs_ = ["invalidates", "signature"]
+ _attrs_ = ["invalidates", "signature", "shape", "strides", "backstrides",
+ "start", 'order']
- def __init__(self):
+ _immutable_fields_ = ['start', "order"]
+
+ strides = None
+ start = 0
+
+ def __init__(self, shape, order):
self.invalidates = []
+ self.shape = shape
+ self.order = order
+ if self.strides is None:
+ self.calc_strides(shape)
+
+ def calc_strides(self, shape):
+ strides = []
+ backstrides = []
+ s = 1
+ shape_rev = shape[:]
+ if self.order == 'C':
+ shape_rev.reverse()
+ for sh in shape_rev:
+ strides.append(s)
+ backstrides.append(s * (sh - 1))
+ s *= sh
+ if self.order == 'C':
+ strides.reverse()
+ backstrides.reverse()
+ self.strides = strides[:]
+ self.backstrides = backstrides[:]
def invalidated(self):
if self.invalidates:
@@ -99,7 +424,7 @@
def _reduce_ufunc_impl(ufunc_name):
def impl(self, space):
- return getattr(interp_ufuncs.get(space), ufunc_name).descr_reduce(space, self)
+ return getattr(interp_ufuncs.get(space), ufunc_name).reduce(space, self, multidim=True)
return func_with_new_name(impl, "reduce_%s_impl" % ufunc_name)
descr_sum = _reduce_ufunc_impl("add")
@@ -108,23 +433,30 @@
descr_min = _reduce_ufunc_impl("minimum")
def _reduce_argmax_argmin_impl(op_name):
- reduce_driver = jit.JitDriver(greens=['signature'],
- reds = ['i', 'size', 'result', 'self', 'cur_best', 'dtype'])
- def loop(self, size):
+ reduce_driver = jit.JitDriver(
+ greens=['shapelen', 'signature'],
+ reds=['result', 'idx', 'i', 'self', 'cur_best', 'dtype']
+ )
+ def loop(self):
+ i = self.start_iter()
+ cur_best = self.eval(i)
+ shapelen = len(self.shape)
+ i = i.next(shapelen)
+ dtype = self.find_dtype()
result = 0
- cur_best = self.eval(0)
- i = 1
- dtype = self.find_dtype()
- while i < size:
+ idx = 1
+ while not i.done():
reduce_driver.jit_merge_point(signature=self.signature,
+ shapelen=shapelen,
self=self, dtype=dtype,
- size=size, i=i, result=result,
+ i=i, result=result, idx=idx,
cur_best=cur_best)
new_best = getattr(dtype, op_name)(cur_best, self.eval(i))
if dtype.ne(new_best, cur_best):
- result = i
+ result = idx
cur_best = new_best
- i += 1
+ i = i.next(shapelen)
+ idx += 1
return result
def impl(self, space):
size = self.find_size()
@@ -132,31 +464,35 @@
raise OperationError(space.w_ValueError,
space.wrap("Can't call %s on zero-size arrays" \
% op_name))
- return space.wrap(loop(self, size))
+ return space.wrap(loop(self))
return func_with_new_name(impl, "reduce_arg%s_impl" % op_name)
def _all(self):
- size = self.find_size()
dtype = self.find_dtype()
- i = 0
- while i < size:
- all_driver.jit_merge_point(signature=self.signature, self=self, dtype=dtype, size=size, i=i)
+ i = self.start_iter()
+ shapelen = len(self.shape)
+ while not i.done():
+ all_driver.jit_merge_point(signature=self.signature,
+ shapelen=shapelen, self=self,
+ dtype=dtype, i=i)
if not dtype.bool(self.eval(i)):
return False
- i += 1
+ i = i.next(shapelen)
return True
def descr_all(self, space):
return space.wrap(self._all())
def _any(self):
- size = self.find_size()
dtype = self.find_dtype()
- i = 0
- while i < size:
- any_driver.jit_merge_point(signature=self.signature, self=self, size=size, dtype=dtype, i=i)
+ i = self.start_iter()
+ shapelen = len(self.shape)
+ while not i.done():
+ any_driver.jit_merge_point(signature=self.signature,
+ shapelen=shapelen, self=self,
+ dtype=dtype, i=i)
if dtype.bool(self.eval(i)):
return True
- i += 1
+ i = i.next(shapelen)
return False
def descr_any(self, space):
return space.wrap(self._any())
@@ -173,25 +509,6 @@
assert isinstance(w_res, BaseArray)
return w_res.descr_sum(space)
- def _getnums(self, comma):
- dtype = self.find_dtype()
- if self.find_size() > 1000:
- nums = [
- dtype.str_format(self.eval(index))
- for index in range(3)
- ]
- nums.append("..." + "," * comma)
- nums.extend([
- dtype.str_format(self.eval(index))
- for index in range(self.find_size() - 3, self.find_size())
- ])
- else:
- nums = [
- dtype.str_format(self.eval(index))
- for index in range(self.find_size())
- ]
- return nums
-
def get_concrete(self):
raise NotImplementedError
@@ -199,98 +516,304 @@
return space.wrap(self.find_dtype())
def descr_get_shape(self, space):
- return space.newtuple([self.descr_len(space)])
+ return space.newtuple([space.wrap(i) for i in self.shape])
+
+ def descr_get_size(self, space):
+ return space.wrap(self.find_size())
def descr_copy(self, space):
- return space.call_function(space.gettypefor(BaseArray), self, self.find_dtype())
+ return self.get_concrete().copy()
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.
+ res = StringBuilder()
+ res.append("array(")
concrete = self.get_concrete()
- res = "array([" + ", ".join(concrete._getnums(False)) + "]"
dtype = concrete.find_dtype()
+ if not concrete.find_size():
+ res.append('[]')
+ if len(self.shape) > 1:
+ # An empty slice reports its shape
+ res.append(", shape=(")
+ self_shape = str(self.shape)
+ res.append_slice(str(self_shape), 1, len(self_shape) - 1)
+ res.append(')')
+ else:
+ concrete.to_str(space, 1, res, indent=' ')
if (dtype is not space.fromcache(interp_dtype.W_Float64Dtype) and
- dtype is not space.fromcache(interp_dtype.W_Int64Dtype)) or not self.find_size():
- res += ", dtype=" + dtype.name
- res += ")"
- return space.wrap(res)
+ dtype is not space.fromcache(interp_dtype.W_Int64Dtype)) or \
+ not self.find_size():
+ res.append(", dtype=" + dtype.name)
+ res.append(")")
+ return space.wrap(res.build())
+
+ def to_str(self, space, comma, builder, indent=' ', use_ellipsis=False):
+ '''Modifies builder with a representation of the array/slice
+ The items will be seperated by a comma if comma is 1
+ Multidimensional arrays/slices will span a number of lines,
+ each line will begin with indent.
+ '''
+ size = self.find_size()
+ if size < 1:
+ builder.append('[]')
+ return
+ if size > 1000:
+ # Once this goes True it does not go back to False for recursive
+ # calls
+ use_ellipsis = True
+ dtype = self.find_dtype()
+ ndims = len(self.shape)
+ i = 0
+ start = True
+ builder.append('[')
+ if ndims > 1:
+ if use_ellipsis:
+ for i in range(3):
+ if start:
+ start = False
+ else:
+ builder.append(',' * comma + '\n')
+ if ndims == 3:
+ builder.append('\n' + indent)
+ else:
+ builder.append(indent)
+ # create_slice requires len(chunks) > 1 in order to reduce
+ # shape
+ view = self.create_slice(space, [(i, 0, 0, 1), (0, self.shape[1], 1, self.shape[1])])
+ view.to_str(space, comma, builder, indent=indent + ' ', use_ellipsis=use_ellipsis)
+ builder.append('\n' + indent + '..., ')
+ i = self.shape[0] - 3
+ while i < self.shape[0]:
+ if start:
+ start = False
+ else:
+ builder.append(',' * comma + '\n')
+ if ndims == 3:
+ builder.append('\n' + indent)
+ else:
+ builder.append(indent)
+ # create_slice requires len(chunks) > 1 in order to reduce
+ # shape
+ view = self.create_slice(space, [(i, 0, 0, 1), (0, self.shape[1], 1, self.shape[1])])
+ view.to_str(space, comma, builder, indent=indent + ' ', use_ellipsis=use_ellipsis)
+ i += 1
+ elif ndims == 1:
+ spacer = ',' * comma + ' '
+ item = self.start
+ # An iterator would be a nicer way to walk along the 1d array, but
+ # how do I reset it if printing ellipsis? iterators have no
+ # "set_offset()"
+ i = 0
+ if use_ellipsis:
+ for i in range(3):
+ if start:
+ start = False
+ else:
+ builder.append(spacer)
+ builder.append(dtype.str_format(self.getitem(item)))
+ item += self.strides[0]
+ # Add a comma only if comma is False - this prevents adding two
+ # commas
+ builder.append(spacer + '...' + ',' * (1 - comma))
+ # Ugly, but can this be done with an iterator?
+ item = self.start + self.backstrides[0] - 2 * self.strides[0]
+ i = self.shape[0] - 3
+ while i < self.shape[0]:
+ if start:
+ start = False
+ else:
+ builder.append(spacer)
+ builder.append(dtype.str_format(self.getitem(item)))
+ item += self.strides[0]
+ i += 1
+ else:
+ builder.append('[')
+ builder.append(']')
def descr_str(self, space):
- # Simple implementation so that we can see the array. Needs work.
+ ret = StringBuilder()
concrete = self.get_concrete()
- return space.wrap("[" + " ".join(concrete._getnums(True)) + "]")
+ concrete.to_str(space, 0, ret, ' ')
+ return space.wrap(ret.build())
+
+ @jit.unroll_safe
+ def _index_of_single_item(self, space, w_idx):
+ if space.isinstance_w(w_idx, space.w_int):
+ idx = space.int_w(w_idx)
+ if not self.shape:
+ if idx != 0:
+ raise OperationError(space.w_IndexError,
+ space.wrap("index out of range"))
+ return 0
+ if idx < 0:
+ idx = self.shape[0] + idx
+ if idx < 0 or idx >= self.shape[0]:
+ raise OperationError(space.w_IndexError,
+ space.wrap("index out of range"))
+ return self.start + idx * self.strides[0]
+ index = [space.int_w(w_item)
+ for w_item in space.fixedview(w_idx)]
+ item = self.start
+ for i in range(len(index)):
+ v = index[i]
+ if v < 0:
+ v += self.shape[i]
+ if v < 0 or v >= self.shape[i]:
+ raise operationerrfmt(space.w_IndexError,
+ "index (%d) out of range (0<=index<%d", i, self.shape[i],
+ )
+ item += v * self.strides[i]
+ return item
+
+ @jit.unroll_safe
+ def _single_item_result(self, space, w_idx):
+ """ The result of getitem/setitem is a single item if w_idx
+ is a list of scalars that match the size of shape
+ """
+ shape_len = len(self.shape)
+ if shape_len == 0:
+ if not space.isinstance_w(w_idx, space.w_int):
+ raise OperationError(space.w_IndexError, space.wrap(
+ "wrong index"))
+ return True
+ if shape_len == 1:
+ if space.isinstance_w(w_idx, space.w_int):
+ return True
+ if space.isinstance_w(w_idx, space.w_slice):
+ return False
+ elif (space.isinstance_w(w_idx, space.w_slice) or
+ space.isinstance_w(w_idx, space.w_int)):
+ return False
+ lgt = space.len_w(w_idx)
+ if lgt > shape_len:
+ raise OperationError(space.w_IndexError,
+ space.wrap("invalid index"))
+ if lgt < shape_len:
+ return False
+ for w_item in space.fixedview(w_idx):
+ if space.isinstance_w(w_item, space.w_slice):
+ return False
+ return True
+
+ @jit.unroll_safe
+ def _prepare_slice_args(self, space, w_idx):
+ if (space.isinstance_w(w_idx, space.w_int) or
+ space.isinstance_w(w_idx, space.w_slice)):
+ return [space.decode_index4(w_idx, self.shape[0])]
+ return [space.decode_index4(w_item, self.shape[i]) for i, w_item in
+ enumerate(space.fixedview(w_idx))]
def descr_getitem(self, space, w_idx):
- # TODO: indexing by arrays and lists
- if space.isinstance_w(w_idx, space.w_tuple):
- length = space.len_w(w_idx)
- if length == 0:
- return space.wrap(self)
- if length > 1: # only one dimension for now.
- raise OperationError(space.w_IndexError,
- space.wrap("invalid index"))
- w_idx = space.getitem(w_idx, space.wrap(0))
- start, stop, step, slice_length = space.decode_index4(w_idx, self.find_size())
- if step == 0:
- # Single index
- return self.get_concrete().eval(start).wrap(space)
- else:
- # Slice
- new_sig = signature.Signature.find_sig([
- SingleDimSlice.signature, self.signature
- ])
- res = SingleDimSlice(start, stop, step, slice_length, self, new_sig)
- return space.wrap(res)
+ if self._single_item_result(space, w_idx):
+ concrete = self.get_concrete()
+ if len(concrete.shape) < 1:
+ raise OperationError(space.w_IndexError, space.wrap(
+ "0-d arrays can't be indexed"))
+ item = concrete._index_of_single_item(space, w_idx)
+ return concrete.getitem(item).wrap(space)
+ chunks = self._prepare_slice_args(space, w_idx)
+ return space.wrap(self.create_slice(space, chunks))
def descr_setitem(self, space, w_idx, w_value):
- # TODO: indexing by arrays and lists
self.invalidated()
- if space.isinstance_w(w_idx, space.w_tuple):
- length = space.len_w(w_idx)
- if length > 1: # only one dimension for now.
- raise OperationError(space.w_IndexError,
- space.wrap("invalid index"))
- if length == 0:
- w_idx = space.newslice(space.wrap(0),
- space.wrap(self.find_size()),
- space.wrap(1))
+ concrete = self.get_concrete()
+ if self._single_item_result(space, w_idx):
+ if len(concrete.shape) < 1:
+ raise OperationError(space.w_IndexError, space.wrap(
+ "0-d arrays can't be indexed"))
+ item = concrete._index_of_single_item(space, w_idx)
+ concrete.setitem_w(space, item, w_value)
+ return
+ 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 = w_value.descr_copy(space)
+ else:
+ w_value = convert_to_array(space, w_value)
+ chunks = self._prepare_slice_args(space, w_idx)
+ view = self.create_slice(space, chunks)
+ view.setslice(space, w_value)
+
+ @jit.unroll_safe
+ def create_slice(self, space, chunks):
+ if len(chunks) == 1:
+ start, stop, step, lgt = chunks[0]
+ if step == 0:
+ shape = self.shape[1:]
+ strides = self.strides[1:]
+ backstrides = self.backstrides[1:]
else:
- w_idx = space.getitem(w_idx, space.wrap(0))
- start, stop, step, slice_length = space.decode_index4(w_idx,
- self.find_size())
- if step == 0:
- # Single index
- self.get_concrete().setitem_w(space, start, w_value)
+ shape = [lgt] + self.shape[1:]
+ strides = [self.strides[0] * step] + self.strides[1:]
+ backstrides = [(lgt - 1) * self.strides[0] * step] + self.backstrides[1:]
+ start *= self.strides[0]
+ start += self.start
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 = space.call_function(space.gettypefor(BaseArray), w_value)
- assert isinstance(w_value, BaseArray)
- else:
- w_value = convert_to_array(space, w_value)
- concrete.setslice(space, start, stop, step,
- slice_length, w_value)
+ shape = []
+ strides = []
+ backstrides = []
+ start = self.start
+ i = -1
+ for i, (start_, stop, step, lgt) in enumerate(chunks):
+ if step != 0:
+ shape.append(lgt)
+ strides.append(self.strides[i] * step)
+ backstrides.append(self.strides[i] * (lgt - 1) * step)
+ start += self.strides[i] * start_
+ # add a reminder
+ s = i + 1
+ assert s >= 0
+ shape += self.shape[s:]
+ strides += self.strides[s:]
+ backstrides += self.backstrides[s:]
+ new_sig = signature.Signature.find_sig([
+ NDimSlice.signature, self.signature,
+ ])
+ return NDimSlice(self, new_sig, start, strides[:], backstrides[:],
+ shape[:])
def descr_mean(self, space):
- return space.wrap(space.float_w(self.descr_sum(space))/self.find_size())
+ return space.wrap(space.float_w(self.descr_sum(space)) / self.find_size())
- def _sliceloop(self, start, stop, step, source, dest):
- i = start
- j = 0
- while (step > 0 and i < stop) or (step < 0 and i > stop):
- slice_driver.jit_merge_point(signature=source.signature, step=step,
- stop=stop, i=i, j=j, source=source,
- dest=dest)
- dest.setitem(i, source.eval(j).convert_to(dest.find_dtype()))
- j += 1
- i += step
+ def descr_nonzero(self, space):
+ if self.find_size() > 1:
+ raise OperationError(space.w_ValueError, space.wrap(
+ "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"))
+ return space.wrap(space.is_true(self.get_concrete().eval(
+ self.start_iter(self.shape)).wrap(space)))
+
+ def descr_get_transpose(self, space):
+ concrete = self.get_concrete()
+ if len(concrete.shape) < 2:
+ return space.wrap(self)
+ new_sig = signature.Signature.find_sig([
+ NDimSlice.signature, self.signature
+ ])
+ strides = []
+ backstrides = []
+ shape = []
+ for i in range(len(concrete.shape) - 1, -1, -1):
+ strides.append(concrete.strides[i])
+ backstrides.append(concrete.backstrides[i])
+ shape.append(concrete.shape[i])
+ return space.wrap(NDimSlice(concrete, new_sig, self.start, strides[:],
+ backstrides[:], shape[:]))
+
+ def descr_get_flatiter(self, space):
+ return space.wrap(W_FlatIterator(self))
+
+ def getitem(self, item):
+ raise NotImplementedError
+
+ def start_iter(self, res_shape=None):
+ raise NotImplementedError
+
+ def descr_debug_repr(self, space):
+ return space.wrap(self.debug_repr())
def convert_to_array(space, w_obj):
if isinstance(w_obj, BaseArray):
@@ -306,36 +829,55 @@
return scalar_w(space, dtype, w_obj)
def scalar_w(space, dtype, w_obj):
+ assert isinstance(dtype, interp_dtype.W_Dtype)
return Scalar(dtype, dtype.unwrap(space, w_obj))
class Scalar(BaseArray):
"""
- Intermediate class representing a float literal.
+ Intermediate class representing a literal.
"""
signature = signature.BaseSignature()
- _attrs_ = ["dtype", "value"]
+ _attrs_ = ["dtype", "value", "shape"]
def __init__(self, dtype, value):
- BaseArray.__init__(self)
+ BaseArray.__init__(self, [], 'C')
self.dtype = dtype
self.value = value
def find_size(self):
- raise ValueError
+ return 1
+
+ def get_concrete(self):
+ return self
def find_dtype(self):
return self.dtype
- def eval(self, i):
+ def getitem(self, item):
+ raise NotImplementedError
+
+ def eval(self, iter):
return self.value
+ def start_iter(self, res_shape=None):
+ return ConstantIterator()
+
+ def to_str(self, space, comma, builder, indent=' ', use_ellipsis=False):
+ builder.append(self.dtype.str_format(self.value))
+
+ def copy(self):
+ return Scalar(self.dtype, self.value)
+
+ def debug_repr(self):
+ return 'Scalar'
+
class VirtualArray(BaseArray):
"""
Class for representing virtual arrays, such as binary ops or ufuncs
"""
- def __init__(self, signature, res_dtype):
- BaseArray.__init__(self)
+ def __init__(self, signature, shape, res_dtype, order):
+ BaseArray.__init__(self, shape, order)
self.forced_result = None
self.signature = signature
self.res_dtype = res_dtype
@@ -348,13 +890,18 @@
i = 0
signature = self.signature
result_size = self.find_size()
- result = SingleDimArray(result_size, self.find_dtype())
- while i < result_size:
+ result = NDimArray(result_size, self.shape, self.find_dtype())
+ shapelen = len(self.shape)
+ i = self.start_iter()
+ ri = result.start_iter()
+ while not ri.done():
numpy_driver.jit_merge_point(signature=signature,
- result_size=result_size, i=i,
+ shapelen=shapelen,
+ result_size=result_size, i=i, ri=ri,
self=self, result=result)
- result.dtype.setitem(result.storage, i, self.eval(i))
- i += 1
+ result.dtype.setitem(result.storage, ri.offset, self.eval(i))
+ i = i.next(shapelen)
+ ri = ri.next(shapelen)
return result
def force_if_needed(self):
@@ -366,10 +913,13 @@
self.force_if_needed()
return self.forced_result
- def eval(self, i):
+ def eval(self, iter):
if self.forced_result is not None:
- return self.forced_result.eval(i)
- return self._eval(i)
+ return self.forced_result.eval(iter)
+ return self._eval(iter)
+
+ def getitem(self, item):
+ return self.get_concrete().getitem(item)
def setitem(self, item, value):
return self.get_concrete().setitem(item, value)
@@ -385,8 +935,9 @@
class Call1(VirtualArray):
- def __init__(self, signature, res_dtype, values):
- VirtualArray.__init__(self, signature, res_dtype)
+ def __init__(self, signature, shape, res_dtype, values, order):
+ VirtualArray.__init__(self, signature, shape, res_dtype,
+ values.order)
self.values = values
def _del_sources(self):
@@ -398,53 +949,91 @@
def _find_dtype(self):
return self.res_dtype
- def _eval(self, i):
- val = self.values.eval(i).convert_to(self.res_dtype)
-
+ def _eval(self, iter):
+ assert isinstance(iter, Call1Iterator)
+ val = self.values.eval(iter.child).convert_to(self.res_dtype)
sig = jit.promote(self.signature)
assert isinstance(sig, signature.Signature)
call_sig = sig.components[0]
assert isinstance(call_sig, signature.Call1)
return call_sig.func(self.res_dtype, val)
+ def start_iter(self, res_shape=None):
+ if self.forced_result is not None:
+ return self.forced_result.start_iter(res_shape)
+ return Call1Iterator(self.values.start_iter(res_shape))
+
+ def debug_repr(self):
+ sig = self.signature
+ assert isinstance(sig, signature.Signature)
+ call_sig = sig.components[0]
+ assert isinstance(call_sig, signature.Call1)
+ if self.forced_result is not None:
+ return 'Call1(%s, forced=%s)' % (call_sig.name,
+ self.forced_result.debug_repr())
+ return 'Call1(%s, %s)' % (call_sig.name,
+ self.values.debug_repr())
+
class Call2(VirtualArray):
"""
Intermediate class for performing binary operations.
"""
- def __init__(self, signature, calc_dtype, res_dtype, left, right):
- VirtualArray.__init__(self, signature, res_dtype)
+ def __init__(self, signature, shape, calc_dtype, res_dtype, left, right):
+ # XXX do something if left.order != right.order
+ VirtualArray.__init__(self, signature, shape, res_dtype, left.order)
self.left = left
self.right = right
self.calc_dtype = calc_dtype
+ self.size = 1
+ for s in self.shape:
+ self.size *= s
def _del_sources(self):
self.left = None
self.right = None
def _find_size(self):
- try:
- return self.left.find_size()
- except ValueError:
- pass
- return self.right.find_size()
+ return self.size
- def _eval(self, i):
- lhs = self.left.eval(i).convert_to(self.calc_dtype)
- rhs = self.right.eval(i).convert_to(self.calc_dtype)
+ def start_iter(self, res_shape=None):
+ if self.forced_result is not None:
+ return self.forced_result.start_iter(res_shape)
+ if res_shape is None:
+ res_shape = self.shape # we still force the shape on children
+ return Call2Iterator(self.left.start_iter(res_shape),
+ self.right.start_iter(res_shape))
+ def _eval(self, iter):
+ assert isinstance(iter, Call2Iterator)
+ lhs = self.left.eval(iter.left).convert_to(self.calc_dtype)
+ rhs = self.right.eval(iter.right).convert_to(self.calc_dtype)
sig = jit.promote(self.signature)
assert isinstance(sig, signature.Signature)
call_sig = sig.components[0]
assert isinstance(call_sig, signature.Call2)
return call_sig.func(self.calc_dtype, lhs, rhs)
+ def debug_repr(self):
+ sig = self.signature
+ assert isinstance(sig, signature.Signature)
+ call_sig = sig.components[0]
+ assert isinstance(call_sig, signature.Call2)
+ if self.forced_result is not None:
+ return 'Call2(%s, forced=%s)' % (call_sig.name,
+ self.forced_result.debug_repr())
+ return 'Call2(%s, %s, %s)' % (call_sig.name,
+ self.left.debug_repr(),
+ self.right.debug_repr())
+
class ViewArray(BaseArray):
"""
Class for representing views of arrays, they will reflect changes of parent
arrays. Example: slices
"""
- def __init__(self, parent, signature):
- BaseArray.__init__(self)
+ def __init__(self, parent, signature, strides, backstrides, shape):
+ self.strides = strides
+ self.backstrides = backstrides
+ BaseArray.__init__(self, shape, parent.order)
self.signature = signature
self.parent = parent
self.invalidates = parent.invalidates
@@ -456,39 +1045,38 @@
self.parent.get_concrete()
return self
- def eval(self, i):
- return self.parent.eval(self.calc_index(i))
+ def getitem(self, item):
+ return self.parent.getitem(item)
+
+ def eval(self, iter):
+ return self.parent.getitem(iter.get_offset())
@unwrap_spec(item=int)
def setitem_w(self, space, item, w_value):
- return self.parent.setitem_w(space, self.calc_index(item), w_value)
+ return self.parent.setitem_w(space, item, w_value)
def setitem(self, item, value):
# This is currently not possible to be called from anywhere.
raise NotImplementedError
def descr_len(self, space):
- return space.wrap(self.find_size())
+ if self.shape:
+ return space.wrap(self.shape[0])
+ return space.wrap(1)
- def calc_index(self, item):
- raise NotImplementedError
-class SingleDimSlice(ViewArray):
+class NDimSlice(ViewArray):
signature = signature.BaseSignature()
- 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 __init__(self, parent, signature, start, strides, backstrides,
+ shape):
+ if isinstance(parent, NDimSlice):
+ parent = parent.parent
+ ViewArray.__init__(self, parent, signature, strides, backstrides, shape)
+ self.start = start
+ self.size = 1
+ for sh in shape:
+ self.size *= sh
def get_root_storage(self):
return self.parent.get_concrete().get_root_storage()
@@ -499,20 +1087,44 @@
def find_dtype(self):
return self.parent.find_dtype()
- 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
- self._sliceloop(start, stop, step, arr, self.parent)
+ def setslice(self, space, w_value):
+ res_shape = shape_agreement(space, self.shape, w_value.shape)
+ self._sliceloop(w_value, res_shape)
- def calc_index(self, item):
- return (self.start + item * self.step)
+ def _sliceloop(self, source, res_shape):
+ source_iter = source.start_iter(res_shape)
+ res_iter = self.start_iter(res_shape)
+ shapelen = len(res_shape)
+ while not res_iter.done():
+ slice_driver.jit_merge_point(signature=source.signature,
+ shapelen=shapelen,
+ self=self, source=source,
+ res_iter=res_iter,
+ source_iter=source_iter)
+ self.setitem(res_iter.offset, source.eval(source_iter).convert_to(
+ self.find_dtype()))
+ source_iter = source_iter.next(shapelen)
+ res_iter = res_iter.next(shapelen)
+ def start_iter(self, res_shape=None):
+ if res_shape is not None and res_shape != self.shape:
+ return BroadcastIterator(self, res_shape)
+ # XXX there is a possible optimization here with SingleDimViewIterator
+ # ignore for now
+ return ViewIterator(self)
-class SingleDimArray(BaseArray):
- def __init__(self, size, dtype):
- BaseArray.__init__(self)
+ def setitem(self, item, value):
+ self.parent.setitem(item, value)
+
+ def debug_repr(self):
+ return 'Slice(%s)' % self.parent.debug_repr()
+
+class NDimArray(BaseArray):
+ """ A class representing contiguous array. We know that each iteration
+ by say ufunc will increase the data index by one
+ """
+ def __init__(self, size, shape, dtype, order='C'):
+ BaseArray.__init__(self, shape, order)
self.size = size
self.dtype = dtype
self.storage = dtype.malloc(size)
@@ -530,11 +1142,26 @@
def find_dtype(self):
return self.dtype
- def eval(self, i):
- return self.dtype.getitem(self.storage, i)
+ def getitem(self, item):
+ return self.dtype.getitem(self.storage, item)
+
+ def eval(self, iter):
+ return self.dtype.getitem(self.storage, iter.get_offset())
+
+ def copy(self):
+ array = NDimArray(self.size, self.shape[:], self.dtype, self.order)
+ rffi.c_memcpy(
+ rffi.cast(rffi.VOIDP, array.storage),
+ rffi.cast(rffi.VOIDP, self.storage),
+ self.size * self.dtype.num_bytes
+ )
+ return array
def descr_len(self, space):
- return space.wrap(self.size)
+ if len(self.shape):
+ return space.wrap(self.shape[0])
+ raise OperationError(space.w_TypeError, space.wrap(
+ "len() of unsized object"))
def setitem_w(self, space, item, w_value):
self.invalidated()
@@ -544,30 +1171,55 @@
self.invalidated()
self.dtype.setitem(self.storage, item, value)
- def setslice(self, space, start, stop, step, slice_length, arr):
- self._sliceloop(start, stop, step, arr, self)
+ def start_iter(self, res_shape=None):
+ if self.order == 'C':
+ if res_shape is not None and res_shape != self.shape:
+ return BroadcastIterator(self, res_shape)
+ return ArrayIterator(self.size)
+ raise NotImplementedError # use ViewIterator simply, test it
+
+ def debug_repr(self):
+ return 'Array'
def __del__(self):
lltype.free(self.storage, flavor='raw', track_allocation=False)
- at unwrap_spec(size=int)
-def zeros(space, size, w_dtype=None):
+def _find_size_and_shape(space, w_size):
+ if space.isinstance_w(w_size, space.w_int):
+ size = space.int_w(w_size)
+ shape = [size]
+ else:
+ size = 1
+ shape = []
+ for w_item in space.fixedview(w_size):
+ item = space.int_w(w_item)
+ size *= item
+ shape.append(item)
+ return size, shape
+
+def zeros(space, w_size, w_dtype=None):
dtype = space.interp_w(interp_dtype.W_Dtype,
space.call_function(space.gettypefor(interp_dtype.W_Dtype), w_dtype)
)
- return space.wrap(SingleDimArray(size, dtype=dtype))
+ size, shape = _find_size_and_shape(space, w_size)
+ return space.wrap(NDimArray(size, shape[:], dtype=dtype))
- at unwrap_spec(size=int)
-def ones(space, size, w_dtype=None):
+def ones(space, w_size, w_dtype=None):
dtype = space.interp_w(interp_dtype.W_Dtype,
space.call_function(space.gettypefor(interp_dtype.W_Dtype), w_dtype)
)
-
- arr = SingleDimArray(size, dtype=dtype)
+ size, shape = _find_size_and_shape(space, w_size)
+ arr = NDimArray(size, shape[:], dtype=dtype)
one = dtype.adapt_val(1)
arr.dtype.fill(arr.storage, one, 0, size)
return space.wrap(arr)
+def dot(space, w_obj, w_obj2):
+ w_arr = convert_to_array(space, w_obj)
+ if isinstance(w_arr, Scalar):
+ return convert_to_array(space, w_obj2).descr_dot(space, w_arr)
+ return w_arr.descr_dot(space, w_obj2)
+
BaseArray.typedef = TypeDef(
'numarray',
__new__ = interp2app(descr_new_array),
@@ -580,6 +1232,7 @@
__pos__ = interp2app(BaseArray.descr_pos),
__neg__ = interp2app(BaseArray.descr_neg),
__abs__ = interp2app(BaseArray.descr_abs),
+ __nonzero__ = interp2app(BaseArray.descr_nonzero),
__add__ = interp2app(BaseArray.descr_add),
__sub__ = interp2app(BaseArray.descr_sub),
@@ -604,9 +1257,14 @@
__repr__ = interp2app(BaseArray.descr_repr),
__str__ = interp2app(BaseArray.descr_str),
+ __debug_repr__ = interp2app(BaseArray.descr_debug_repr),
dtype = GetSetProperty(BaseArray.descr_get_dtype),
shape = GetSetProperty(BaseArray.descr_get_shape),
+ size = GetSetProperty(BaseArray.descr_get_size),
+
+ T = GetSetProperty(BaseArray.descr_get_transpose),
+ flat = GetSetProperty(BaseArray.descr_get_flatiter),
mean = interp2app(BaseArray.descr_mean),
sum = interp2app(BaseArray.descr_sum),
@@ -621,3 +1279,54 @@
copy = interp2app(BaseArray.descr_copy),
)
+
+
+class W_FlatIterator(ViewArray):
+ signature = signature.BaseSignature()
+
+ @jit.unroll_safe
+ def __init__(self, arr):
+ size = 1
+ for sh in arr.shape:
+ size *= sh
+ new_sig = signature.Signature.find_sig([
+ W_FlatIterator.signature, arr.signature
+ ])
+ ViewArray.__init__(self, arr, new_sig, [arr.strides[-1]],
+ [arr.backstrides[-1]], [size])
+ self.shapelen = len(arr.shape)
+ self.arr = arr
+ self.iter = self.start_iter()
+
+ def start_iter(self, res_shape=None):
+ if res_shape is not None and res_shape != self.shape:
+ return BroadcastIterator(self, res_shape)
+ return OneDimIterator(self.arr.start, self.strides[0],
+ self.shape[0])
+
+ def find_dtype(self):
+ return self.arr.find_dtype()
+
+ def find_size(self):
+ return self.shape[0]
+
+ def descr_next(self, space):
+ if self.iter.done():
+ raise OperationError(space.w_StopIteration, space.wrap(''))
+ result = self.eval(self.iter)
+ self.iter = self.iter.next(self.shapelen)
+ return result.wrap(space)
+
+ def descr_iter(self):
+ return self
+
+ def debug_repr(self):
+ return 'FlatIter(%s)' % self.arr.debug_repr()
+
+
+W_FlatIterator.typedef = TypeDef(
+ 'flatiter',
+ next = interp2app(W_FlatIterator.descr_next),
+ __iter__ = interp2app(W_FlatIterator.descr_iter),
+)
+W_FlatIterator.acceptable_as_base_class = False
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
@@ -9,7 +9,7 @@
@unwrap_spec(s=str)
def fromstring(space, s):
- from pypy.module.micronumpy.interp_numarray import SingleDimArray
+ from pypy.module.micronumpy.interp_numarray import NDimArray
length = len(s)
if length % FLOAT_SIZE == 0:
@@ -19,7 +19,7 @@
"string length %d not divisable by %d" % (length, FLOAT_SIZE)))
dtype = space.fromcache(W_Float64Dtype)
- a = SingleDimArray(number, dtype=dtype)
+ a = NDimArray(number, [number], dtype=dtype)
start = 0
end = FLOAT_SIZE
@@ -31,4 +31,4 @@
start += FLOAT_SIZE
end += FLOAT_SIZE
- return space.wrap(a)
\ No newline at end of file
+ return space.wrap(a)
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,6 +1,6 @@
from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.interpreter.gateway import interp2app
+from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty
from pypy.module.micronumpy import interp_dtype, signature
from pypy.rlib import jit
@@ -9,8 +9,8 @@
reduce_driver = jit.JitDriver(
- greens = ["signature"],
- reds = ["i", "size", "self", "dtype", "value", "obj"]
+ greens = ['shapelen', "signature"],
+ reds = ["i", "self", "dtype", "value", "obj"]
)
class W_Ufunc(Wrappable):
@@ -45,8 +45,10 @@
return self.call(space, __args__.arguments_w)
def descr_reduce(self, space, w_obj):
+ return self.reduce(space, w_obj, multidim=False)
+
+ def reduce(self, space, w_obj, multidim):
from pypy.module.micronumpy.interp_numarray import convert_to_array, Scalar
-
if self.argcount != 2:
raise OperationError(space.w_ValueError, space.wrap("reduce only "
"supported for binary functions"))
@@ -62,28 +64,33 @@
space, obj.find_dtype(),
promote_to_largest=True
)
- start = 0
+ start = obj.start_iter(obj.shape)
+ shapelen = len(obj.shape)
+ if shapelen > 1 and not multidim:
+ raise OperationError(space.w_NotImplementedError,
+ space.wrap("not implemented yet"))
if self.identity is None:
if size == 0:
raise operationerrfmt(space.w_ValueError, "zero-size array to "
"%s.reduce without identity", self.name)
- value = obj.eval(0).convert_to(dtype)
- start += 1
+ value = obj.eval(start).convert_to(dtype)
+ start = start.next(shapelen)
else:
value = self.identity.convert_to(dtype)
new_sig = signature.Signature.find_sig([
self.reduce_signature, obj.signature
])
- return self.reduce(new_sig, start, value, obj, dtype, size).wrap(space)
+ return self.reduce_loop(new_sig, shapelen, start, value, obj,
+ dtype).wrap(space)
- def reduce(self, signature, start, value, obj, dtype, size):
- i = start
- while i < size:
- reduce_driver.jit_merge_point(signature=signature, self=self,
+ def reduce_loop(self, signature, shapelen, i, value, obj, dtype):
+ while not i.done():
+ reduce_driver.jit_merge_point(signature=signature,
+ shapelen=shapelen, self=self,
value=value, obj=obj, i=i,
- dtype=dtype, size=size)
+ dtype=dtype)
value = self.func(dtype, value, obj.eval(i).convert_to(dtype))
- i += 1
+ i = i.next(shapelen)
return value
class W_Ufunc1(W_Ufunc):
@@ -111,7 +118,7 @@
return self.func(res_dtype, w_obj.value.convert_to(res_dtype)).wrap(space)
new_sig = signature.Signature.find_sig([self.signature, w_obj.signature])
- w_res = Call1(new_sig, res_dtype, w_obj)
+ w_res = Call1(new_sig, w_obj.shape, res_dtype, w_obj, w_obj.order)
w_obj.add_invalidates(w_res)
return w_res
@@ -130,7 +137,7 @@
def call(self, space, args_w):
from pypy.module.micronumpy.interp_numarray import (Call2,
- convert_to_array, Scalar)
+ convert_to_array, Scalar, shape_agreement)
[w_lhs, w_rhs] = args_w
w_lhs = convert_to_array(space, w_lhs)
@@ -153,7 +160,9 @@
new_sig = signature.Signature.find_sig([
self.signature, w_lhs.signature, w_rhs.signature
])
- w_res = Call2(new_sig, calc_dtype, res_dtype, w_lhs, w_rhs)
+ new_shape = shape_agreement(space, w_lhs.shape, w_rhs.shape)
+ w_res = Call2(new_sig, new_shape, calc_dtype,
+ res_dtype, w_lhs, w_rhs)
w_lhs.add_invalidates(w_res)
w_rhs.add_invalidates(w_res)
return w_res
@@ -310,6 +319,8 @@
("floor", "floor", 1, {"promote_to_float": True}),
("exp", "exp", 1, {"promote_to_float": True}),
+ ('sqrt', 'sqrt', 1, {'promote_to_float': True}),
+
("sin", "sin", 1, {"promote_to_float": True}),
("cos", "cos", 1, {"promote_to_float": True}),
("tan", "tan", 1, {"promote_to_float": True}),
diff --git a/pypy/module/micronumpy/signature.py b/pypy/module/micronumpy/signature.py
--- a/pypy/module/micronumpy/signature.py
+++ b/pypy/module/micronumpy/signature.py
@@ -40,13 +40,15 @@
return Signature._known_sigs.setdefault(components, Signature(components))
class Call1(BaseSignature):
- _immutable_fields_ = ["func"]
+ _immutable_fields_ = ["func", "name"]
def __init__(self, func):
self.func = func
+ self.name = func.func_name
class Call2(BaseSignature):
- _immutable_fields_ = ["func"]
+ _immutable_fields_ = ["func", "name"]
def __init__(self, func):
- self.func = func
\ No newline at end of file
+ self.func = func
+ self.name = func.func_name
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,6 +1,6 @@
from pypy.conftest import gettestobjspace
from pypy.module.micronumpy import interp_dtype
-from pypy.module.micronumpy.interp_numarray import SingleDimArray, Scalar
+from pypy.module.micronumpy.interp_numarray import NDimArray, Scalar
from pypy.module.micronumpy.interp_ufuncs import (find_binop_result_dtype,
find_unaryop_result_dtype)
@@ -13,7 +13,7 @@
def test_binop_signature(self, space):
float64_dtype = space.fromcache(interp_dtype.W_Float64Dtype)
- ar = SingleDimArray(10, dtype=float64_dtype)
+ ar = NDimArray(10, [10], dtype=float64_dtype)
v1 = ar.descr_add(space, ar)
v2 = ar.descr_add(space, Scalar(float64_dtype, 2.0))
assert v1.signature is not v2.signature
@@ -22,7 +22,7 @@
v4 = ar.descr_add(space, ar)
assert v1.signature is v4.signature
- bool_ar = SingleDimArray(10, dtype=space.fromcache(interp_dtype.W_BoolDtype))
+ bool_ar = NDimArray(10, [10], dtype=space.fromcache(interp_dtype.W_BoolDtype))
v5 = ar.descr_add(space, bool_ar)
assert v5.signature is not v1.signature
assert v5.signature is not v2.signature
@@ -30,13 +30,13 @@
assert v5.signature is v6.signature
def test_slice_signature(self, space):
- ar = SingleDimArray(10, dtype=space.fromcache(interp_dtype.W_Float64Dtype))
- v1 = ar.descr_getitem(space, space.wrap(slice(1, 5, 1)))
+ ar = NDimArray(10, [10], dtype=space.fromcache(interp_dtype.W_Float64Dtype))
+ v1 = ar.descr_getitem(space, space.wrap(slice(1, 3, 1)))
v2 = ar.descr_getitem(space, space.wrap(slice(4, 6, 1)))
assert v1.signature is v2.signature
- v3 = ar.descr_add(space, v1)
- v4 = ar.descr_add(space, v2)
+ v3 = v2.descr_add(space, v1)
+ v4 = v1.descr_add(space, v2)
assert v3.signature is v4.signature
class TestUfuncCoerscion(object):
diff --git a/pypy/module/micronumpy/test/test_compile.py b/pypy/module/micronumpy/test/test_compile.py
--- a/pypy/module/micronumpy/test/test_compile.py
+++ b/pypy/module/micronumpy/test/test_compile.py
@@ -5,7 +5,7 @@
class TestCompiler(object):
def compile(self, code):
return numpy_compile(code)
-
+
def test_vars(self):
code = """
a = 2
@@ -25,7 +25,7 @@
st = interp.code.statements[0]
assert st.expr.items == [FloatConstant(1), FloatConstant(2),
FloatConstant(3)]
-
+
def test_array_literal2(self):
code = "a = [[1],[2],[3]]"
interp = self.compile(code)
@@ -102,10 +102,11 @@
code = """
a = [1,2,3,4]
b = [4,5,6,5]
- a + b
+ c = a + b
+ c -> 3
"""
interp = self.run(code)
- assert interp.results[0]._getnums(False) == ["5.0", "7.0", "9.0", "9.0"]
+ assert interp.results[-1].value.val == 9
def test_array_getitem(self):
code = """
@@ -115,7 +116,7 @@
"""
interp = self.run(code)
assert interp.results[0].value.val == 3 + 6
-
+
def test_range_getitem(self):
code = """
r = |20| + 3
@@ -161,10 +162,32 @@
assert interp.results[0].value.val == 256
def test_slice(self):
- py.test.skip("in progress")
interp = self.run("""
a = [1,2,3,4]
b = a -> :
b -> 3
""")
- assert interp.results[0].value.val == 3
+ assert interp.results[0].value.val == 4
+
+ def test_slice_step(self):
+ interp = self.run("""
+ a = |30|
+ b = a -> ::2
+ b -> 3
+ """)
+ assert interp.results[0].value.val == 6
+
+ def test_multidim_getitem(self):
+ interp = self.run("""
+ a = [[1,2]]
+ a -> 0 -> 1
+ """)
+ assert interp.results[0].value.val == 2
+
+ def test_multidim_getitem_2(self):
+ interp = self.run("""
+ a = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
+ b = a + a
+ b -> 1 -> 1
+ """)
+ assert interp.results[0].value.val == 8
diff --git a/pypy/module/micronumpy/test/test_dtypes.py b/pypy/module/micronumpy/test/test_dtypes.py
--- a/pypy/module/micronumpy/test/test_dtypes.py
+++ b/pypy/module/micronumpy/test/test_dtypes.py
@@ -3,7 +3,7 @@
class AppTestDtypes(BaseNumpyAppTest):
def test_dtype(self):
- from numpy import dtype
+ from numpypy import dtype
d = dtype('?')
assert d.num == 0
@@ -14,7 +14,7 @@
raises(TypeError, dtype, 1042)
def test_dtype_with_types(self):
- from numpy import dtype
+ from numpypy import dtype
assert dtype(bool).num == 0
assert dtype(int).num == 7
@@ -22,13 +22,13 @@
assert dtype(float).num == 12
def test_array_dtype_attr(self):
- from numpy import array, dtype
+ from numpypy import array, dtype
a = array(range(5), long)
assert a.dtype is dtype(long)
def test_repr_str(self):
- from numpy import dtype
+ from numpypy import dtype
assert repr(dtype) == "<type 'numpy.dtype'>"
d = dtype('?')
@@ -36,57 +36,57 @@
assert str(d) == "bool"
def test_bool_array(self):
- import numpy
+ from numpypy import array, False_, True_
- a = numpy.array([0, 1, 2, 2.5], dtype='?')
- assert a[0] is numpy.False_
+ a = array([0, 1, 2, 2.5], dtype='?')
+ assert a[0] is False_
for i in xrange(1, 4):
- assert a[i] is numpy.True_
+ assert a[i] is True_
def test_copy_array_with_dtype(self):
- import numpy
+ from numpypy import array, False_, True_
- a = numpy.array([0, 1, 2, 3], dtype=long)
+ a = array([0, 1, 2, 3], dtype=long)
# int on 64-bit, long in 32-bit
assert isinstance(a[0], (int, long))
b = a.copy()
assert isinstance(b[0], (int, long))
- a = numpy.array([0, 1, 2, 3], dtype=bool)
- assert a[0] is numpy.False_
+ a = array([0, 1, 2, 3], dtype=bool)
+ assert a[0] is False_
b = a.copy()
- assert b[0] is numpy.False_
+ assert b[0] is False_
def test_zeros_bool(self):
- import numpy
+ from numpypy import zeros, False_
- a = numpy.zeros(10, dtype=bool)
+ a = zeros(10, dtype=bool)
for i in range(10):
- assert a[i] is numpy.False_
+ assert a[i] is False_
def test_ones_bool(self):
- import numpy
+ from numpypy import ones, True_
- a = numpy.ones(10, dtype=bool)
+ a = ones(10, dtype=bool)
for i in range(10):
- assert a[i] is numpy.True_
+ assert a[i] is True_
def test_zeros_long(self):
- from numpy import zeros
+ from numpypy import zeros
a = zeros(10, dtype=long)
for i in range(10):
assert isinstance(a[i], (int, long))
assert a[1] == 0
def test_ones_long(self):
- from numpy import ones
+ from numpypy import ones
a = ones(10, dtype=long)
for i in range(10):
assert isinstance(a[i], (int, long))
assert a[1] == 1
def test_overflow(self):
- from numpy import array, dtype
+ from numpypy import array, dtype
assert array([128], 'b')[0] == -128
assert array([256], 'B')[0] == 0
assert array([32768], 'h')[0] == -32768
@@ -98,7 +98,7 @@
raises(OverflowError, "array([2**64], 'Q')")
def test_bool_binop_types(self):
- from numpy import array, dtype
+ from numpypy import array, dtype
types = [
'?', 'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q', 'f', 'd'
]
@@ -107,7 +107,7 @@
assert (a + array([0], t)).dtype is dtype(t)
def test_binop_types(self):
- from numpy import array, dtype
+ from numpypy import array, dtype
tests = [('b','B','h'), ('b','h','h'), ('b','H','i'), ('b','i','i'),
('b','l','l'), ('b','q','q'), ('b','Q','d'), ('B','h','h'),
('B','H','H'), ('B','i','i'), ('B','I','I'), ('B','l','l'),
@@ -129,7 +129,7 @@
assert (array([1], d1) + array([1], d2)).dtype is dtype(dout)
def test_add_int8(self):
- from numpy import array, dtype
+ from numpypy import array, dtype
a = array(range(5), dtype="int8")
b = a + a
@@ -138,7 +138,7 @@
assert b[i] == i * 2
def test_add_int16(self):
- from numpy import array, dtype
+ from numpypy import array, dtype
a = array(range(5), dtype="int16")
b = a + a
@@ -147,7 +147,7 @@
assert b[i] == i * 2
def test_add_uint32(self):
- from numpy import array, dtype
+ from numpypy import array, dtype
a = array(range(5), dtype="I")
b = a + a
@@ -156,12 +156,12 @@
assert b[i] == i * 2
def test_shape(self):
- from numpy import dtype
+ from numpypy import dtype
assert dtype(long).shape == ()
def test_cant_subclass(self):
- from numpy import dtype
+ from numpypy import dtype
# You can't subclass dtype
raises(TypeError, type, "Foo", (dtype,), {})
diff --git a/pypy/module/micronumpy/test/test_module.py b/pypy/module/micronumpy/test/test_module.py
--- a/pypy/module/micronumpy/test/test_module.py
+++ b/pypy/module/micronumpy/test/test_module.py
@@ -3,19 +3,19 @@
class AppTestNumPyModule(BaseNumpyAppTest):
def test_mean(self):
- from numpy import array, mean
+ from numpypy import array, mean
assert mean(array(range(5))) == 2.0
assert mean(range(5)) == 2.0
def test_average(self):
- from numpy import array, average
+ from numpypy import array, average
assert average(range(10)) == 4.5
assert average(array(range(10))) == 4.5
def test_constants(self):
import math
- from numpy import inf, e
+ from numpypy import inf, e
assert type(inf) is float
assert inf == float("inf")
assert e == math.e
- assert type(e) is float
\ No newline at end of file
+ assert type(e) is float
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
@@ -1,15 +1,172 @@
+
+import py
from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
+from pypy.module.micronumpy.interp_numarray import NDimArray, shape_agreement
+from pypy.module.micronumpy import signature
+from pypy.interpreter.error import OperationError
from pypy.conftest import gettestobjspace
+class MockDtype(object):
+ signature = signature.BaseSignature()
+
+ def malloc(self, size):
+ return None
+
+
+class TestNumArrayDirect(object):
+ def newslice(self, *args):
+ return self.space.newslice(*[self.space.wrap(arg) for arg in args])
+
+ def newtuple(self, *args):
+ args_w = []
+ for arg in args:
+ if isinstance(arg, int):
+ args_w.append(self.space.wrap(arg))
+ else:
+ args_w.append(arg)
+ return self.space.newtuple(args_w)
+
+ def test_strides_f(self):
+ a = NDimArray(100, [10, 5, 3], MockDtype(), 'F')
+ assert a.strides == [1, 10, 50]
+ assert a.backstrides == [9, 40, 100]
+
+ def test_strides_c(self):
+ a = NDimArray(100, [10, 5, 3], MockDtype(), 'C')
+ assert a.strides == [15, 3, 1]
+ assert a.backstrides == [135, 12, 2]
+
+ def test_create_slice_f(self):
+ space = self.space
+ a = NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'F')
+ s = a.create_slice(space, [(3, 0, 0, 1)])
+ assert s.start == 3
+ assert s.strides == [10, 50]
+ assert s.backstrides == [40, 100]
+ s = a.create_slice(space, [(1, 9, 2, 4)])
+ assert s.start == 1
+ assert s.strides == [2, 10, 50]
+ assert s.backstrides == [6, 40, 100]
+ s = a.create_slice(space, [(1, 5, 3, 2), (1, 2, 1, 1), (1, 0, 0, 1)])
+ assert s.shape == [2, 1]
+ assert s.strides == [3, 10]
+ assert s.backstrides == [3, 0]
+ s = a.create_slice(space, [(0, 10, 1, 10), (2, 0, 0, 1)])
+ assert s.start == 20
+ assert s.shape == [10, 3]
+
+ def test_create_slice_c(self):
+ space = self.space
+ a = NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'C')
+ s = a.create_slice(space, [(3, 0, 0, 1)])
+ assert s.start == 45
+ assert s.strides == [3, 1]
+ assert s.backstrides == [12, 2]
+ s = a.create_slice(space, [(1, 9, 2, 4)])
+ assert s.start == 15
+ assert s.strides == [30, 3, 1]
+ assert s.backstrides == [90, 12, 2]
+ s = a.create_slice(space, [(1, 5, 3, 2), (1, 2, 1, 1), (1, 0, 0, 1)])
+ assert s.start == 19
+ assert s.shape == [2, 1]
+ assert s.strides == [45, 3]
+ assert s.backstrides == [45, 0]
+ s = a.create_slice(space, [(0, 10, 1, 10), (2, 0, 0, 1)])
+ assert s.start == 6
+ assert s.shape == [10, 3]
+
+ def test_slice_of_slice_f(self):
+ space = self.space
+ a = NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'F')
+ s = a.create_slice(space, [(5, 0, 0, 1)])
+ assert s.start == 5
+ s2 = s.create_slice(space, [(3, 0, 0, 1)])
+ assert s2.shape == [3]
+ assert s2.strides == [50]
+ assert s2.parent is a
+ assert s2.backstrides == [100]
+ assert s2.start == 35
+ s = a.create_slice(space, [(1, 5, 3, 2)])
+ s2 = s.create_slice(space, [(0, 2, 1, 2), (2, 0, 0, 1)])
+ assert s2.shape == [2, 3]
+ assert s2.strides == [3, 50]
+ assert s2.backstrides == [3, 100]
+ assert s2.start == 1 * 15 + 2 * 3
+
+ def test_slice_of_slice_c(self):
+ space = self.space
+ a = NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), order='C')
+ s = a.create_slice(space, [(5, 0, 0, 1)])
+ assert s.start == 15 * 5
+ s2 = s.create_slice(space, [(3, 0, 0, 1)])
+ assert s2.shape == [3]
+ assert s2.strides == [1]
+ assert s2.parent is a
+ assert s2.backstrides == [2]
+ assert s2.start == 5 * 15 + 3 * 3
+ s = a.create_slice(space, [(1, 5, 3, 2)])
+ s2 = s.create_slice(space, [(0, 2, 1, 2), (2, 0, 0, 1)])
+ assert s2.shape == [2, 3]
+ assert s2.strides == [45, 1]
+ assert s2.backstrides == [45, 2]
+ assert s2.start == 1 * 15 + 2 * 3
+
+ def test_negative_step_f(self):
+ space = self.space
+ a = NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'F')
+ s = a.create_slice(space, [(9, -1, -2, 5)])
+ assert s.start == 9
+ assert s.strides == [-2, 10, 50]
+ assert s.backstrides == [-8, 40, 100]
+
+ def test_negative_step_c(self):
+ space = self.space
+ a = NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), order='C')
+ s = a.create_slice(space, [(9, -1, -2, 5)])
+ assert s.start == 135
+ assert s.strides == [-30, 3, 1]
+ assert s.backstrides == [-120, 12, 2]
+
+ def test_index_of_single_item_f(self):
+ a = NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'F')
+ r = a._index_of_single_item(self.space, self.newtuple(1, 2, 2))
+ assert r == 1 + 2 * 10 + 2 * 50
+ s = a.create_slice(self.space, [(0, 10, 1, 10), (2, 0, 0, 1)])
+ r = s._index_of_single_item(self.space, self.newtuple(1, 0))
+ assert r == a._index_of_single_item(self.space, self.newtuple(1, 2, 0))
+ r = s._index_of_single_item(self.space, self.newtuple(1, 1))
+ assert r == a._index_of_single_item(self.space, self.newtuple(1, 2, 1))
+
+ def test_index_of_single_item_c(self):
+ a = NDimArray(10 * 5 * 3, [10, 5, 3], MockDtype(), 'C')
+ r = a._index_of_single_item(self.space, self.newtuple(1, 2, 2))
+ assert r == 1 * 3 * 5 + 2 * 3 + 2
+ s = a.create_slice(self.space, [(0, 10, 1, 10), (2, 0, 0, 1)])
+ r = s._index_of_single_item(self.space, self.newtuple(1, 0))
+ assert r == a._index_of_single_item(self.space, self.newtuple(1, 2, 0))
+ r = s._index_of_single_item(self.space, self.newtuple(1, 1))
+ assert r == a._index_of_single_item(self.space, self.newtuple(1, 2, 1))
+
+ def test_shape_agreement(self):
+ assert shape_agreement(self.space, [3], [3]) == [3]
+ assert shape_agreement(self.space, [1, 2, 3], [1, 2, 3]) == [1, 2, 3]
+ py.test.raises(OperationError, shape_agreement, self.space, [2], [3])
+ assert shape_agreement(self.space, [4, 4], []) == [4, 4]
+ assert shape_agreement(self.space,
+ [8, 1, 6, 1], [7, 1, 5]) == [8, 7, 6, 5]
+ assert shape_agreement(self.space,
+ [5, 2], [4, 3, 5, 2]) == [4, 3, 5, 2]
+
+
class AppTestNumArray(BaseNumpyAppTest):
def test_type(self):
- from numpy import array
+ from numpypy import array
ar = array(range(5))
assert type(ar) is type(ar + ar)
def test_init(self):
- from numpy import zeros
+ from numpypy import zeros
a = zeros(15)
# Check that storage was actually zero'd.
assert a[10] == 0.0
@@ -17,18 +174,25 @@
a[13] = 5.3
assert a[13] == 5.3
+ def test_size(self):
+ from numpypy import array
+ assert array(3).size == 1
+ a = array([1, 2, 3])
+ assert a.size == 3
+ assert (a + a).size == 3
+
def test_empty(self):
"""
Test that empty() works.
"""
- from numpy import empty
+ from numpypy import empty
a = empty(2)
a[1] = 1.0
assert a[1] == 1.0
def test_ones(self):
- from numpy import ones
+ from numpypy import ones
a = ones(3)
assert len(a) == 3
assert a[0] == 1
@@ -37,71 +201,24 @@
assert a[2] == 4
def test_copy(self):
- from numpy import array
+ from numpypy import array
a = array(range(5))
b = a.copy()
for i in xrange(5):
assert b[i] == a[i]
+ a[3] = 22
+ assert b[3] == 3
+
+ a = array(1)
+ assert a.copy() == a
def test_iterator_init(self):
- from numpy import array
+ from numpypy import array
a = array(range(5))
assert a[3] == 3
- def test_repr(self):
- from numpy import array, zeros
- a = array(range(5), float)
- assert repr(a) == "array([0.0, 1.0, 2.0, 3.0, 4.0])"
- a = array([], float)
- assert repr(a) == "array([], dtype=float64)"
- a = zeros(1001)
- assert repr(a) == "array([0.0, 0.0, 0.0, ..., 0.0, 0.0, 0.0])"
- a = array(range(5), long)
- assert repr(a) == "array([0, 1, 2, 3, 4])"
- a = array([], long)
- assert repr(a) == "array([], dtype=int64)"
- a = array([True, False, True, False], "?")
- assert repr(a) == "array([True, False, True, False], dtype=bool)"
-
- def test_repr_slice(self):
- from numpy import array, zeros
- a = array(range(5), float)
- 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), float)
- assert str(a) == "[0.0 1.0 2.0 3.0 4.0]"
- assert str((2*a)[:]) == "[0.0 2.0 4.0 6.0 8.0]"
- a = zeros(1001)
- assert str(a) == "[0.0 0.0 0.0 ..., 0.0 0.0 0.0]"
-
- a = array(range(5), dtype=long)
- assert str(a) == "[0 1 2 3 4]"
- a = array([True, False, True, False], dtype="?")
- assert str(a) == "[True False True False]"
-
- a = array(range(5), dtype="int8")
- assert str(a) == "[0 1 2 3 4]"
-
- a = array(range(5), dtype="int16")
- assert str(a) == "[0 1 2 3 4]"
-
- def test_str_slice(self):
- from numpy import array, zeros
- a = array(range(5), float)
- 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
+ from numpypy import array
a = array(range(5))
raises(IndexError, "a[5]")
a = a + a
@@ -110,7 +227,7 @@
raises(IndexError, "a[-6]")
def test_getitem_tuple(self):
- from numpy import array
+ from numpypy import array
a = array(range(5))
raises(IndexError, "a[(1,2)]")
for i in xrange(5):
@@ -120,7 +237,7 @@
assert a[i] == b[i]
def test_setitem(self):
- from numpy import array
+ from numpypy import array
a = array(range(5))
a[-1] = 5.0
assert a[4] == 5.0
@@ -128,18 +245,18 @@
raises(IndexError, "a[-6] = 3.0")
def test_setitem_tuple(self):
- from numpy import array
+ from numpypy import array
a = array(range(5))
More information about the pypy-commit
mailing list