[pypy-commit] pypy shadowstack-perf: hg merge default
arigo
noreply at buildbot.pypy.org
Wed Jul 27 18:22:48 CEST 2011
Author: Armin Rigo <arigo at tunes.org>
Branch: shadowstack-perf
Changeset: r46019:bf56d77d981a
Date: 2011-07-27 15:52 +0200
http://bitbucket.org/pypy/pypy/changeset/bf56d77d981a/
Log: hg merge default
diff too long, truncating to 10000 out of 21630 lines
diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -1,6 +1,7 @@
syntax: glob
*.py[co]
*~
+.*.swp
syntax: regexp
^testresult$
@@ -38,6 +39,8 @@
^pypy/translator/benchmark/shootout_benchmarks$
^pypy/translator/goal/pypy-translation-snapshot$
^pypy/translator/goal/pypy-c
+^pypy/translator/goal/pypy-jvm
+^pypy/translator/goal/pypy-jvm.jar
^pypy/translator/goal/.+\.exe$
^pypy/translator/goal/.+\.dll$
^pypy/translator/goal/target.+-c$
diff --git a/.hgtags b/.hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -1,1 +1,2 @@
b590cf6de4190623aad9aa698694c22e614d67b9 release-1.5
+b48df0bf4e75b81d98f19ce89d4a7dc3e1dab5e5 benchmarked
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -185,6 +185,7 @@
Jim Baker
Philip Jenvey
Rodrigo Araújo
+ Brett Cannon
Heinrich-Heine University, Germany
Open End AB (formerly AB Strakt), Sweden
diff --git a/lib-python/modified-2.7/ctypes/__init__.py b/lib-python/modified-2.7/ctypes/__init__.py
--- a/lib-python/modified-2.7/ctypes/__init__.py
+++ b/lib-python/modified-2.7/ctypes/__init__.py
@@ -489,9 +489,12 @@
_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
return CFunctionType
-_cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
def cast(obj, typ):
- return _cast(obj, obj, typ)
+ try:
+ c_void_p.from_param(obj)
+ except TypeError, e:
+ raise ArgumentError(str(e))
+ return _cast_addr(obj, obj, typ)
_string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
def string_at(ptr, size=-1):
diff --git a/lib-python/modified-2.7/distutils/cygwinccompiler.py b/lib-python/modified-2.7/distutils/cygwinccompiler.py
--- a/lib-python/modified-2.7/distutils/cygwinccompiler.py
+++ b/lib-python/modified-2.7/distutils/cygwinccompiler.py
@@ -75,6 +75,9 @@
elif msc_ver == '1500':
# VS2008 / MSVC 9.0
return ['msvcr90']
+ elif msc_ver == '1600':
+ # VS2010 / MSVC 10.0
+ return ['msvcr100']
else:
raise ValueError("Unknown MS Compiler version %s " % msc_ver)
diff --git a/lib-python/modified-2.7/distutils/sysconfig_pypy.py b/lib-python/modified-2.7/distutils/sysconfig_pypy.py
--- a/lib-python/modified-2.7/distutils/sysconfig_pypy.py
+++ b/lib-python/modified-2.7/distutils/sysconfig_pypy.py
@@ -116,6 +116,12 @@
if compiler.compiler_type == "unix":
compiler.compiler_so.extend(['-fPIC', '-Wimplicit'])
compiler.shared_lib_extension = get_config_var('SO')
+ if "CFLAGS" in os.environ:
+ cflags = os.environ["CFLAGS"]
+ compiler.compiler.append(cflags)
+ compiler.compiler_so.append(cflags)
+ compiler.linker_so.append(cflags)
+
from sysconfig_cpython import (
parse_makefile, _variable_rx, expand_makefile_vars)
diff --git a/lib-python/modified-2.7/opcode.py b/lib-python/modified-2.7/opcode.py
--- a/lib-python/modified-2.7/opcode.py
+++ b/lib-python/modified-2.7/opcode.py
@@ -189,7 +189,6 @@
def_op('MAP_ADD', 147)
# pypy modification, experimental bytecode
-def_op('CALL_LIKELY_BUILTIN', 200) # #args + (#kwargs << 8)
def_op('LOOKUP_METHOD', 201) # Index in name list
hasname.append(201)
def_op('CALL_METHOD', 202) # #args not including 'self'
diff --git a/lib-python/modified-2.7/pickle.py b/lib-python/modified-2.7/pickle.py
--- a/lib-python/modified-2.7/pickle.py
+++ b/lib-python/modified-2.7/pickle.py
@@ -168,7 +168,7 @@
# Pickling machinery
-class Pickler:
+class Pickler(object):
def __init__(self, file, protocol=None):
"""This takes a file-like object for writing a pickle data stream.
diff --git a/lib-python/modified-2.7/test/test_descr.py b/lib-python/modified-2.7/test/test_descr.py
--- a/lib-python/modified-2.7/test/test_descr.py
+++ b/lib-python/modified-2.7/test/test_descr.py
@@ -4400,7 +4400,10 @@
self.assertTrue(l.__add__ != l.__mul__)
self.assertTrue(l.__add__.__name__ == '__add__')
self.assertTrue(l.__add__.__self__ is l)
- self.assertTrue(l.__add__.__objclass__ is list)
+ if hasattr(l.__add__, '__objclass__'): # CPython
+ self.assertTrue(l.__add__.__objclass__ is list)
+ else: # PyPy
+ self.assertTrue(l.__add__.im_class is list)
self.assertEqual(l.__add__.__doc__, list.__add__.__doc__)
try:
hash(l.__add__)
diff --git a/lib-python/modified-2.7/test/test_dis.py b/lib-python/modified-2.7/test/test_dis.py
deleted file mode 100644
--- a/lib-python/modified-2.7/test/test_dis.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# Minimal tests for dis module
-
-from test.test_support import run_unittest
-import unittest
-import sys
-import dis
-import StringIO
-
-
-def _f(a):
- print a
- return 1
-
-dis_f = """\
- %-4d 0 LOAD_FAST 0 (a)
- 3 PRINT_ITEM
- 4 PRINT_NEWLINE
-
- %-4d 5 LOAD_CONST 1 (1)
- 8 RETURN_VALUE
-"""%(_f.func_code.co_firstlineno + 1,
- _f.func_code.co_firstlineno + 2)
-
-
-# we "call" rangexxx() instead of range() to disable the
-# pypy optimization that turns it into CALL_LIKELY_BUILTIN.
-def bug708901():
- for res in rangexxx(1,
- 10):
- pass
-
-dis_bug708901 = """\
- %-4d 0 SETUP_LOOP 23 (to 26)
- 3 LOAD_GLOBAL 0 (rangexxx)
- 6 LOAD_CONST 1 (1)
-
- %-4d 9 LOAD_CONST 2 (10)
- 12 CALL_FUNCTION 2
- 15 GET_ITER
- >> 16 FOR_ITER 6 (to 25)
- 19 STORE_FAST 0 (res)
-
- %-4d 22 JUMP_ABSOLUTE 16
- >> 25 POP_BLOCK
- >> 26 LOAD_CONST 0 (None)
- 29 RETURN_VALUE
-"""%(bug708901.func_code.co_firstlineno + 1,
- bug708901.func_code.co_firstlineno + 2,
- bug708901.func_code.co_firstlineno + 3)
-
-
-def bug1333982(x=[]):
- assert 0, ([s for s in x] +
- 1)
- pass
-
-dis_bug1333982 = """\
- %-4d 0 LOAD_CONST 1 (0)
- 3 POP_JUMP_IF_TRUE 38
- 6 LOAD_GLOBAL 0 (AssertionError)
- 9 BUILD_LIST 0
- 12 LOAD_FAST 0 (x)
- 15 GET_ITER
- >> 16 FOR_ITER 12 (to 31)
- 19 STORE_FAST 1 (s)
- 22 LOAD_FAST 1 (s)
- 25 LIST_APPEND 2
- 28 JUMP_ABSOLUTE 16
-
- %-4d >> 31 LOAD_CONST 2 (1)
- 34 BINARY_ADD
- 35 RAISE_VARARGS 2
-
- %-4d >> 38 LOAD_CONST 0 (None)
- 41 RETURN_VALUE
-"""%(bug1333982.func_code.co_firstlineno + 1,
- bug1333982.func_code.co_firstlineno + 2,
- bug1333982.func_code.co_firstlineno + 3)
-
-_BIG_LINENO_FORMAT = """\
-%3d 0 LOAD_GLOBAL 0 (spam)
- 3 POP_TOP
- 4 LOAD_CONST 0 (None)
- 7 RETURN_VALUE
-"""
-
-class DisTests(unittest.TestCase):
- def do_disassembly_test(self, func, expected):
- s = StringIO.StringIO()
- save_stdout = sys.stdout
- sys.stdout = s
- dis.dis(func)
- sys.stdout = save_stdout
- got = s.getvalue()
- # Trim trailing blanks (if any).
- lines = got.split('\n')
- lines = [line.rstrip() for line in lines]
- expected = expected.split("\n")
- import difflib
- if expected != lines:
- self.fail(
- "events did not match expectation:\n" +
- "\n".join(difflib.ndiff(expected,
- lines)))
-
- def test_opmap(self):
- self.assertEqual(dis.opmap["STOP_CODE"], 0)
- self.assertIn(dis.opmap["LOAD_CONST"], dis.hasconst)
- self.assertIn(dis.opmap["STORE_NAME"], dis.hasname)
-
- def test_opname(self):
- self.assertEqual(dis.opname[dis.opmap["LOAD_FAST"]], "LOAD_FAST")
-
- def test_boundaries(self):
- self.assertEqual(dis.opmap["EXTENDED_ARG"], dis.EXTENDED_ARG)
- self.assertEqual(dis.opmap["STORE_NAME"], dis.HAVE_ARGUMENT)
-
- def test_dis(self):
- self.do_disassembly_test(_f, dis_f)
-
- def test_bug_708901(self):
- self.do_disassembly_test(bug708901, dis_bug708901)
-
- def test_bug_1333982(self):
- # This one is checking bytecodes generated for an `assert` statement,
- # so fails if the tests are run with -O. Skip this test then.
- if __debug__:
- self.do_disassembly_test(bug1333982, dis_bug1333982)
-
- def test_big_linenos(self):
- def func(count):
- namespace = {}
- func = "def foo():\n " + "".join(["\n "] * count + ["spam\n"])
- exec func in namespace
- return namespace['foo']
-
- # Test all small ranges
- for i in xrange(1, 300):
- expected = _BIG_LINENO_FORMAT % (i + 2)
- self.do_disassembly_test(func(i), expected)
-
- # Test some larger ranges too
- for i in xrange(300, 5000, 10):
- expected = _BIG_LINENO_FORMAT % (i + 2)
- self.do_disassembly_test(func(i), expected)
-
-def test_main():
- run_unittest(DisTests)
-
-
-if __name__ == "__main__":
- test_main()
diff --git a/lib-python/2.7/test/test_sets.py b/lib-python/modified-2.7/test/test_sets.py
copy from lib-python/2.7/test/test_sets.py
copy to lib-python/modified-2.7/test/test_sets.py
--- a/lib-python/2.7/test/test_sets.py
+++ b/lib-python/modified-2.7/test/test_sets.py
@@ -686,7 +686,9 @@
set_list = sorted(self.set)
self.assertEqual(len(dup_list), len(set_list))
for i, el in enumerate(dup_list):
- self.assertIs(el, set_list[i])
+ # Object identity is not guarnteed for immutable objects, so we
+ # can't use assertIs here.
+ self.assertEqual(el, set_list[i])
def test_deep_copy(self):
dup = copy.deepcopy(self.set)
diff --git a/lib-python/2.7/test/test_tarfile.py b/lib-python/modified-2.7/test/test_tarfile.py
copy from lib-python/2.7/test/test_tarfile.py
copy to lib-python/modified-2.7/test/test_tarfile.py
--- a/lib-python/2.7/test/test_tarfile.py
+++ b/lib-python/modified-2.7/test/test_tarfile.py
@@ -169,6 +169,7 @@
except tarfile.ReadError:
self.fail("tarfile.open() failed on empty archive")
self.assertListEqual(tar.getmembers(), [])
+ tar.close()
def test_null_tarfile(self):
# Test for issue6123: Allow opening empty archives.
@@ -207,16 +208,21 @@
fobj = open(self.tarname, "rb")
tar = tarfile.open(fileobj=fobj, mode=self.mode)
self.assertEqual(tar.name, os.path.abspath(fobj.name))
+ tar.close()
def test_no_name_attribute(self):
- data = open(self.tarname, "rb").read()
+ f = open(self.tarname, "rb")
+ data = f.read()
+ f.close()
fobj = StringIO.StringIO(data)
self.assertRaises(AttributeError, getattr, fobj, "name")
tar = tarfile.open(fileobj=fobj, mode=self.mode)
self.assertEqual(tar.name, None)
def test_empty_name_attribute(self):
- data = open(self.tarname, "rb").read()
+ f = open(self.tarname, "rb")
+ data = f.read()
+ f.close()
fobj = StringIO.StringIO(data)
fobj.name = ""
tar = tarfile.open(fileobj=fobj, mode=self.mode)
@@ -515,6 +521,7 @@
self.tar = tarfile.open(self.tarname, mode=self.mode, encoding="iso8859-1")
tarinfo = self.tar.getmember("pax/umlauts-�������")
self._test_member(tarinfo, size=7011, chksum=md5_regtype)
+ self.tar.close()
class LongnameTest(ReadTest):
@@ -675,6 +682,7 @@
tar = tarfile.open(tmpname, self.mode)
tarinfo = tar.gettarinfo(path)
self.assertEqual(tarinfo.size, 0)
+ tar.close()
finally:
os.rmdir(path)
@@ -692,6 +700,7 @@
tar.gettarinfo(target)
tarinfo = tar.gettarinfo(link)
self.assertEqual(tarinfo.size, 0)
+ tar.close()
finally:
os.remove(target)
os.remove(link)
@@ -704,6 +713,7 @@
tar = tarfile.open(tmpname, self.mode)
tarinfo = tar.gettarinfo(path)
self.assertEqual(tarinfo.size, 0)
+ tar.close()
finally:
os.remove(path)
@@ -722,6 +732,7 @@
tar.add(dstname)
os.chdir(cwd)
self.assertTrue(tar.getnames() == [], "added the archive to itself")
+ tar.close()
def test_exclude(self):
tempdir = os.path.join(TEMPDIR, "exclude")
@@ -742,6 +753,7 @@
tar = tarfile.open(tmpname, "r")
self.assertEqual(len(tar.getmembers()), 1)
self.assertEqual(tar.getnames()[0], "empty_dir")
+ tar.close()
finally:
shutil.rmtree(tempdir)
@@ -859,7 +871,9 @@
fobj.close()
elif self.mode.endswith("bz2"):
dec = bz2.BZ2Decompressor()
- data = open(tmpname, "rb").read()
+ f = open(tmpname, "rb")
+ data = f.read()
+ f.close()
data = dec.decompress(data)
self.assertTrue(len(dec.unused_data) == 0,
"found trailing data")
@@ -938,6 +952,7 @@
"unable to read longname member")
self.assertEqual(tarinfo.linkname, member.linkname,
"unable to read longname member")
+ tar.close()
def test_longname_1023(self):
self._test(("longnam/" * 127) + "longnam")
@@ -1030,6 +1045,7 @@
else:
n = tar.getmembers()[0].name
self.assertTrue(name == n, "PAX longname creation failed")
+ tar.close()
def test_pax_global_header(self):
pax_headers = {
@@ -1058,6 +1074,7 @@
tarfile.PAX_NUMBER_FIELDS[key](val)
except (TypeError, ValueError):
self.fail("unable to convert pax header field")
+ tar.close()
def test_pax_extended_header(self):
# The fields from the pax header have priority over the
@@ -1077,6 +1094,7 @@
self.assertEqual(t.pax_headers, pax_headers)
self.assertEqual(t.name, "foo")
self.assertEqual(t.uid, 123)
+ tar.close()
class UstarUnicodeTest(unittest.TestCase):
@@ -1120,6 +1138,7 @@
tarinfo.name = "foo"
tarinfo.uname = u"���"
self.assertRaises(UnicodeError, tar.addfile, tarinfo)
+ tar.close()
def test_unicode_argument(self):
tar = tarfile.open(tarname, "r", encoding="iso8859-1", errors="strict")
@@ -1174,6 +1193,7 @@
tar = tarfile.open(tmpname, format=self.format, encoding="ascii",
errors=handler)
self.assertEqual(tar.getnames()[0], name)
+ tar.close()
self.assertRaises(UnicodeError, tarfile.open, tmpname,
encoding="ascii", errors="strict")
@@ -1186,6 +1206,7 @@
tar = tarfile.open(tmpname, format=self.format, encoding="iso8859-1",
errors="utf-8")
self.assertEqual(tar.getnames()[0], "���/" + u"�".encode("utf8"))
+ tar.close()
class AppendTest(unittest.TestCase):
@@ -1213,6 +1234,7 @@
def _test(self, names=["bar"], fileobj=None):
tar = tarfile.open(self.tarname, fileobj=fileobj)
self.assertEqual(tar.getnames(), names)
+ tar.close()
def test_non_existing(self):
self._add_testfile()
@@ -1231,7 +1253,9 @@
def test_fileobj(self):
self._create_testtar()
- data = open(self.tarname).read()
+ f = open(self.tarname)
+ data = f.read()
+ f.close()
fobj = StringIO.StringIO(data)
self._add_testfile(fobj)
fobj.seek(0)
@@ -1257,7 +1281,9 @@
# Append mode is supposed to fail if the tarfile to append to
# does not end with a zero block.
def _test_error(self, data):
- open(self.tarname, "wb").write(data)
+ f = open(self.tarname, "wb")
+ f.write(data)
+ f.close()
self.assertRaises(tarfile.ReadError, self._add_testfile)
def test_null(self):
diff --git a/lib-python/modified-2.7/test/test_weakref.py b/lib-python/modified-2.7/test/test_weakref.py
--- a/lib-python/modified-2.7/test/test_weakref.py
+++ b/lib-python/modified-2.7/test/test_weakref.py
@@ -993,13 +993,13 @@
self.assertTrue(len(weakdict) == 2)
k, v = weakdict.popitem()
self.assertTrue(len(weakdict) == 1)
- if k is key1:
+ if k == key1:
self.assertTrue(v is value1)
else:
self.assertTrue(v is value2)
k, v = weakdict.popitem()
self.assertTrue(len(weakdict) == 0)
- if k is key1:
+ if k == key1:
self.assertTrue(v is value1)
else:
self.assertTrue(v is value2)
diff --git a/lib_pypy/_ctypes/__init__.py b/lib_pypy/_ctypes/__init__.py
--- a/lib_pypy/_ctypes/__init__.py
+++ b/lib_pypy/_ctypes/__init__.py
@@ -18,7 +18,16 @@
if _os.name in ("nt", "ce"):
from _rawffi import FormatError
from _rawffi import check_HRESULT as _check_HRESULT
- CopyComPointer = None # XXX
+
+ def CopyComPointer(src, dst):
+ from ctypes import c_void_p, cast
+ if src:
+ hr = src[0][0].AddRef(src)
+ if hr & 0x80000000:
+ return hr
+ dst[0] = cast(src, c_void_p).value
+ return 0
+
LoadLibrary = dlopen
from _rawffi import FUNCFLAG_STDCALL, FUNCFLAG_CDECL, FUNCFLAG_PYTHONAPI
diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py
--- a/lib_pypy/_ctypes/basics.py
+++ b/lib_pypy/_ctypes/basics.py
@@ -48,7 +48,8 @@
return self.from_param(as_parameter)
def get_ffi_param(self, value):
- return self.from_param(value)._to_ffi_param()
+ cdata = self.from_param(value)
+ return cdata, cdata._to_ffi_param()
def get_ffi_argtype(self):
if self._ffiargtype:
@@ -139,7 +140,10 @@
return buffer(self._buffer)
def _get_b_base(self):
- return self._base
+ try:
+ return self._base
+ except AttributeError:
+ return None
_b_base_ = property(_get_b_base)
_b_needsfree_ = False
@@ -218,5 +222,7 @@
'z' : _ffi.types.void_p,
'O' : _ffi.types.void_p,
'Z' : _ffi.types.void_p,
+ 'X' : _ffi.types.void_p,
+ 'v' : _ffi.types.sshort,
}
diff --git a/lib_pypy/_ctypes/function.py b/lib_pypy/_ctypes/function.py
--- a/lib_pypy/_ctypes/function.py
+++ b/lib_pypy/_ctypes/function.py
@@ -296,13 +296,12 @@
"This function takes %d argument%s (%s given)"
% (len(self._argtypes_), plural, len(args)))
- # check that arguments are convertible
- ## XXX Not as long as ctypes.cast is a callback function with
- ## py_object arguments...
- ## self._convert_args(self._argtypes_, args, {})
-
try:
- res = self.callable(*args)
+ newargs = self._convert_args_for_callback(argtypes, args)
+ except (UnicodeError, TypeError, ValueError), e:
+ raise ArgumentError(str(e))
+ try:
+ res = self.callable(*newargs)
except:
exc_info = sys.exc_info()
traceback.print_tb(exc_info[2], file=sys.stderr)
@@ -322,20 +321,20 @@
RuntimeWarning, stacklevel=2)
if self._com_index:
- assert False, 'TODO2'
from ctypes import cast, c_void_p, POINTER
if not args:
raise ValueError(
"native COM method call without 'this' parameter"
)
- thisarg = cast(args[0], POINTER(POINTER(c_void_p))).contents
- argtypes = [c_void_p] + list(argtypes)
- args = list(args)
- args[0] = args[0].value
+ thisarg = cast(args[0], POINTER(POINTER(c_void_p)))
+ keepalives, newargs, argtypes, outargs = self._convert_args(argtypes,
+ args[1:], kwargs)
+ newargs.insert(0, args[0].value)
+ argtypes.insert(0, c_void_p)
else:
thisarg = None
-
- newargs, argtypes, outargs = self._convert_args(argtypes, args, kwargs)
+ keepalives, newargs, argtypes, outargs = self._convert_args(argtypes,
+ args, kwargs)
funcptr = self._getfuncptr(argtypes, self._restype_, thisarg)
result = self._call_funcptr(funcptr, *newargs)
@@ -343,6 +342,11 @@
if not outargs:
return result
+
+ simple_cdata = type(c_void_p()).__bases__[0]
+ outargs = [x.value if type(x).__bases__[0] is simple_cdata else x
+ for x in outargs]
+
if len(outargs) == 1:
return outargs[0]
return tuple(outargs)
@@ -398,10 +402,10 @@
# extract the address from the object's virtual table
if not thisarg:
raise ValueError("COM method call without VTable")
- ptr = thisarg[self._com_index - 0x1000]
- argshapes = [arg._ffiargshape for arg in argtypes]
- resshape = restype._ffiargshape
- return _rawffi.FuncPtr(ptr, argshapes, resshape, self._flags_)
+ ptr = thisarg[0][self._com_index - 0x1000]
+ ffiargs = [argtype.get_ffi_argtype() for argtype in argtypes]
+ ffires = restype.get_ffi_argtype()
+ return _ffi.FuncPtr.fromaddr(ptr, '', ffiargs, ffires)
cdll = self.dll._handle
try:
@@ -434,16 +438,15 @@
@classmethod
def _conv_param(cls, argtype, arg):
if isinstance(argtype, _CDataMeta):
- #arg = argtype.from_param(arg)
- arg = argtype.get_ffi_param(arg)
- return arg, argtype
+ cobj, ffiparam = argtype.get_ffi_param(arg)
+ return cobj, ffiparam, argtype
if argtype is not None:
arg = argtype.from_param(arg)
if hasattr(arg, '_as_parameter_'):
arg = arg._as_parameter_
if isinstance(arg, _CData):
- return arg._to_ffi_param(), type(arg)
+ return arg, arg._to_ffi_param(), type(arg)
#
# non-usual case: we do the import here to save a lot of code in the
# jit trace of the normal case
@@ -460,19 +463,28 @@
else:
raise TypeError("Don't know how to handle %s" % (arg,))
- return cobj._to_ffi_param(), type(cobj)
+ return cobj, cobj._to_ffi_param(), type(cobj)
+
+ def _convert_args_for_callback(self, argtypes, args):
+ assert len(argtypes) == len(args)
+ newargs = []
+ for argtype, arg in zip(argtypes, args):
+ param = argtype.from_param(arg)
+ if argtype._type_ == 'P': # special-case for c_void_p
+ param = param._get_buffer_value()
+ elif self._is_primitive(argtype):
+ param = param.value
+ newargs.append(param)
+ return newargs
def _convert_args(self, argtypes, args, kwargs, marker=object()):
newargs = []
outargs = []
+ keepalives = []
newargtypes = []
total = len(args)
paramflags = self._paramflags
-
- if self._com_index:
- inargs_idx = 1
- else:
- inargs_idx = 0
+ inargs_idx = 0
if not paramflags and total < len(argtypes):
raise TypeError("not enough arguments")
@@ -496,7 +508,8 @@
val = defval
if val is marker:
val = 0
- newarg, newargtype = self._conv_param(argtype, val)
+ keepalive, newarg, newargtype = self._conv_param(argtype, val)
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
elif flag in (0, PARAMFLAG_FIN):
@@ -512,28 +525,32 @@
raise TypeError("required argument '%s' missing" % name)
else:
raise TypeError("not enough arguments")
- newarg, newargtype = self._conv_param(argtype, val)
+ keepalive, newarg, newargtype = self._conv_param(argtype, val)
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
elif flag == PARAMFLAG_FOUT:
if defval is not marker:
outargs.append(defval)
- newarg, newargtype = self._conv_param(argtype, defval)
+ keepalive, newarg, newargtype = self._conv_param(argtype, defval)
else:
import ctypes
val = argtype._type_()
outargs.append(val)
+ keepalive = None
newarg = ctypes.byref(val)
newargtype = type(newarg)
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
else:
raise ValueError("paramflag %d not yet implemented" % flag)
else:
try:
- newarg, newargtype = self._conv_param(argtype, args[i])
+ keepalive, newarg, newargtype = self._conv_param(argtype, args[i])
except (UnicodeError, TypeError, ValueError), e:
raise ArgumentError(str(e))
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
inargs_idx += 1
@@ -542,13 +559,17 @@
extra = args[len(newargs):]
for i, arg in enumerate(extra):
try:
- newarg, newargtype = self._conv_param(None, arg)
+ keepalive, newarg, newargtype = self._conv_param(None, arg)
except (UnicodeError, TypeError, ValueError), e:
raise ArgumentError(str(e))
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
- return newargs, newargtypes, outargs
+ return keepalives, newargs, newargtypes, outargs
+ @staticmethod
+ def _is_primitive(argtype):
+ return argtype.__bases__[0] is _SimpleCData
def _wrap_result(self, restype, result):
"""
@@ -557,7 +578,7 @@
"""
# hack for performance: if restype is a "simple" primitive type, don't
# allocate the buffer because it's going to be thrown away immediately
- if restype.__bases__[0] is _SimpleCData and not restype._is_pointer_like():
+ if self._is_primitive(restype) and not restype._is_pointer_like():
return result
#
shape = restype._ffishape
@@ -587,13 +608,7 @@
retval = None
- if self._com_index:
- if resbuffer[0] & 0x80000000:
- raise get_com_error(resbuffer[0],
- self._com_iid, argsandobjs[0])
- else:
- retval = int(resbuffer[0])
- elif restype is not None:
+ if restype is not None:
checker = getattr(self.restype, '_check_retval_', None)
if checker:
val = restype(result)
@@ -601,7 +616,13 @@
# classes defining a new type, and their subclasses
if '_type_' in restype.__dict__:
val = val.value
- retval = checker(val)
+ # XXX Raise a COMError when restype is HRESULT and
+ # checker(val) fails. How to check for restype == HRESULT?
+ if self._com_index:
+ if result & 0x80000000:
+ raise get_com_error(result, None, None)
+ else:
+ retval = checker(val)
elif not isinstance(restype, _CDataMeta):
retval = restype(result)
else:
diff --git a/lib_pypy/_ctypes/primitive.py b/lib_pypy/_ctypes/primitive.py
--- a/lib_pypy/_ctypes/primitive.py
+++ b/lib_pypy/_ctypes/primitive.py
@@ -10,6 +10,8 @@
from _ctypes.builtin import ConvMode
from _ctypes.array import Array
from _ctypes.pointer import _Pointer, as_ffi_pointer
+#from _ctypes.function import CFuncPtr # this import is moved at the bottom
+ # because else it's circular
class NULL(object):
pass
@@ -86,7 +88,7 @@
return res
if isinstance(value, Array):
return value
- if isinstance(value, _Pointer):
+ if isinstance(value, (_Pointer, CFuncPtr)):
return cls.from_address(value._buffer.buffer)
if isinstance(value, (int, long)):
return cls(value)
@@ -216,10 +218,15 @@
result.value = property(_getvalue, _setvalue)
elif tp == 'X':
- from ctypes import windll
- SysAllocStringLen = windll.oleaut32.SysAllocStringLen
- SysStringLen = windll.oleaut32.SysStringLen
- SysFreeString = windll.oleaut32.SysFreeString
+ from ctypes import WinDLL
+ # Use WinDLL("oleaut32") instead of windll.oleaut32
+ # because the latter is a shared (cached) object; and
+ # other code may set their own restypes. We need out own
+ # restype here.
+ oleaut32 = WinDLL("oleaut32")
+ SysAllocStringLen = oleaut32.SysAllocStringLen
+ SysStringLen = oleaut32.SysStringLen
+ SysFreeString = oleaut32.SysFreeString
def _getvalue(self):
addr = self._buffer[0]
if addr == 0:
@@ -333,3 +340,5 @@
def __nonzero__(self):
return self._buffer[0] not in (0, '\x00')
+
+from _ctypes.function import CFuncPtr
diff --git a/lib_pypy/binascii.py b/lib_pypy/binascii.py
--- a/lib_pypy/binascii.py
+++ b/lib_pypy/binascii.py
@@ -659,7 +659,7 @@
crc = crc_32_tab[(crc ^ long(ord(c))) & 0xffL] ^ (crc >> 8)
#/* Note: (crc >> 8) MUST zero fill on left
- result = crc ^ 0xffffffffL
+ result = crc ^ 0xffffffffL
if result > 2**31:
result = ((result + 2**31) % 2**32) - 2**31
diff --git a/lib_pypy/cPickle.py b/lib_pypy/cPickle.py
--- a/lib_pypy/cPickle.py
+++ b/lib_pypy/cPickle.py
@@ -27,9 +27,9 @@
PythonPickler.__init__(self, self.__f, args[0], **kw)
else:
PythonPickler.__init__(self, *args, **kw)
-
+
def memoize(self, obj):
- self.memo[None] = None # cPickle starts counting at one
+ self.memo[id(None)] = None # cPickle starts counting at one
return PythonPickler.memoize(self, obj)
def getvalue(self):
diff --git a/lib_pypy/pwd.py b/lib_pypy/pwd.py
--- a/lib_pypy/pwd.py
+++ b/lib_pypy/pwd.py
@@ -16,6 +16,7 @@
from ctypes_support import standard_c_lib as libc
from ctypes import Structure, POINTER, c_int, c_char_p, c_long
+from _structseq import structseqtype, structseqfield
try: from __pypy__ import builtinify
except ImportError: builtinify = lambda f: f
@@ -68,7 +69,7 @@
yield self.pw_dir
yield self.pw_shell
-class struct_passwd(tuple):
+class struct_passwd:
"""
pwd.struct_passwd: Results from getpw*() routines.
@@ -76,15 +77,15 @@
(pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell)
or via the object attributes as named in the above tuple.
"""
- def __init__(self, passwd):
- self.pw_name = passwd.pw_name
- self.pw_passwd = passwd.pw_passwd
- self.pw_uid = passwd.pw_uid
- self.pw_gid = passwd.pw_gid
- self.pw_gecos = passwd.pw_gecos
- self.pw_dir = passwd.pw_dir
- self.pw_shell = passwd.pw_shell
- tuple.__init__(self, passwd)
+ __metaclass__ = structseqtype
+ name = "pwd.struct_passwd"
+ pw_name = structseqfield(0)
+ pw_passwd = structseqfield(1)
+ pw_uid = structseqfield(2)
+ pw_gid = structseqfield(3)
+ pw_gecos = structseqfield(4)
+ pw_dir = structseqfield(5)
+ pw_shell = structseqfield(6)
passwd_p = POINTER(passwd)
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
@@ -384,15 +384,19 @@
self.__maybe_write_code(self._smkx)
- self.old_sigwinch = signal.signal(
- signal.SIGWINCH, self.__sigwinch)
+ try:
+ self.old_sigwinch = signal.signal(
+ signal.SIGWINCH, self.__sigwinch)
+ except ValueError:
+ pass
def restore(self):
self.__maybe_write_code(self._rmkx)
self.flushoutput()
tcsetattr(self.input_fd, termios.TCSADRAIN, self.__svtermstate)
- signal.signal(signal.SIGWINCH, self.old_sigwinch)
+ if hasattr(self, 'old_sigwinch'):
+ signal.signal(signal.SIGWINCH, self.old_sigwinch)
def __sigwinch(self, signum, frame):
self.height, self.width = self.getheightwidth()
diff --git a/pypy/annotation/builtin.py b/pypy/annotation/builtin.py
--- a/pypy/annotation/builtin.py
+++ b/pypy/annotation/builtin.py
@@ -357,17 +357,6 @@
def llmemory_cast_int_to_adr(s):
return SomeAddress()
-
-##def rarith_ovfcheck(s_obj):
-## if isinstance(s_obj, SomeInteger) and s_obj.unsigned:
-## getbookkeeper().warning("ovfcheck on unsigned")
-## return s_obj
-
-##def rarith_ovfcheck_lshift(s_obj1, s_obj2):
-## if isinstance(s_obj1, SomeInteger) and s_obj1.unsigned:
-## getbookkeeper().warning("ovfcheck_lshift with unsigned")
-## return SomeInteger()
-
def unicodedata_decimal(s_uchr):
raise TypeError, "unicodedate.decimal() calls should not happen at interp-level"
@@ -385,8 +374,6 @@
original = getattr(__builtin__, name[8:])
BUILTIN_ANALYZERS[original] = value
-##BUILTIN_ANALYZERS[pypy.rlib.rarithmetic.ovfcheck] = rarith_ovfcheck
-##BUILTIN_ANALYZERS[pypy.rlib.rarithmetic.ovfcheck_lshift] = rarith_ovfcheck_lshift
BUILTIN_ANALYZERS[pypy.rlib.rarithmetic.intmask] = rarith_intmask
BUILTIN_ANALYZERS[pypy.rlib.objectmodel.instantiate] = robjmodel_instantiate
BUILTIN_ANALYZERS[pypy.rlib.objectmodel.we_are_translated] = (
diff --git a/pypy/config/config.py b/pypy/config/config.py
--- a/pypy/config/config.py
+++ b/pypy/config/config.py
@@ -81,6 +81,12 @@
(self.__class__, name))
return self._cfgimpl_values[name]
+ def __dir__(self):
+ from_type = dir(type(self))
+ from_dict = list(self.__dict__)
+ extras = list(self._cfgimpl_values)
+ return sorted(set(extras + from_type + from_dict))
+
def __delattr__(self, name):
# XXX if you use delattr you are responsible for all bad things
# happening
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -129,9 +129,6 @@
cmdline='--objspace -o'),
OptionDescription("opcodes", "opcodes to enable in the interpreter", [
- BoolOption("CALL_LIKELY_BUILTIN", "emit a special bytecode for likely calls to builtin functions",
- default=False,
- requires=[("translation.stackless", False)]),
BoolOption("CALL_METHOD", "emit a special bytecode for expr.name()",
default=False),
]),
@@ -266,13 +263,7 @@
BoolOption("withcelldict",
"use dictionaries that are optimized for being used as module dicts",
default=False,
- requires=[("objspace.opcodes.CALL_LIKELY_BUILTIN", False),
- ("objspace.honor__builtins__", False)]),
-
- BoolOption("withdictmeasurement",
- "create huge files with masses of information "
- "about dictionaries",
- default=False),
+ requires=[("objspace.honor__builtins__", False)]),
BoolOption("withmapdict",
"make instances really small but slow without the JIT",
@@ -336,6 +327,9 @@
BoolOption("mutable_builtintypes",
"Allow the changing of builtin types", default=False,
requires=[("objspace.std.builtinshortcut", True)]),
+ BoolOption("withidentitydict",
+ "track types that override __hash__, __eq__ or __cmp__ and use a special dict strategy for those which do not",
+ default=True),
]),
])
@@ -355,8 +349,6 @@
backend = config.translation.backend
# all the good optimizations for PyPy should be listed here
- if level in ['2', '3']:
- config.objspace.opcodes.suggest(CALL_LIKELY_BUILTIN=True)
if level in ['2', '3', 'jit']:
config.objspace.opcodes.suggest(CALL_METHOD=True)
config.objspace.std.suggest(withrangelist=True)
diff --git a/pypy/config/support.py b/pypy/config/support.py
--- a/pypy/config/support.py
+++ b/pypy/config/support.py
@@ -9,7 +9,7 @@
return 1 # don't override MAKEFLAGS. This will call 'make' without any '-j' option
if sys.platform == 'darwin':
return darwin_get_cpu_count()
- elif sys.platform != 'linux2':
+ elif not sys.platform.startswith('linux'):
return 1 # implement me
try:
if isinstance(filename_or_file, str):
diff --git a/pypy/config/test/test_config.py b/pypy/config/test/test_config.py
--- a/pypy/config/test/test_config.py
+++ b/pypy/config/test/test_config.py
@@ -63,6 +63,20 @@
py.test.raises(ConfigError, 'config.gc.name = "ref"')
config.gc.name = "framework"
+def test___dir__():
+ descr = make_description()
+ config = Config(descr, bool=False)
+ attrs = dir(config)
+ assert '__repr__' in attrs # from the type
+ assert '_cfgimpl_values' in attrs # from self
+ assert 'gc' in attrs # custom attribute
+ assert 'objspace' in attrs # custom attribute
+ #
+ attrs = dir(config.gc)
+ assert 'name' in attrs
+ assert 'dummy' in attrs
+ assert 'float' in attrs
+
def test_arbitrary_option():
descr = OptionDescription("top", "", [
ArbitraryOption("a", "no help", default=None)
diff --git a/pypy/config/test/test_support.py b/pypy/config/test/test_support.py
--- a/pypy/config/test/test_support.py
+++ b/pypy/config/test/test_support.py
@@ -40,7 +40,7 @@
return self._value
def test_cpuinfo_linux():
- if sys.platform != 'linux2':
+ if not sys.platform.startswith('linux'):
py.test.skip("linux only")
saved = os.environ
try:
diff --git a/pypy/config/translationoption.py b/pypy/config/translationoption.py
--- a/pypy/config/translationoption.py
+++ b/pypy/config/translationoption.py
@@ -140,7 +140,10 @@
["annotate", "rtype", "backendopt", "database", "source",
"pyjitpl"],
default=None, cmdline="--fork-before"),
-
+ BoolOption("dont_write_c_files",
+ "Make the C backend write everyting to /dev/null. " +
+ "Useful for benchmarking, so you don't actually involve the disk",
+ default=False, cmdline="--dont-write-c-files"),
ArbitraryOption("instrumentctl", "internal",
default=None),
StrOption("output", "Output file name", cmdline="--output"),
diff --git a/pypy/doc/coding-guide.rst b/pypy/doc/coding-guide.rst
--- a/pypy/doc/coding-guide.rst
+++ b/pypy/doc/coding-guide.rst
@@ -929,6 +929,19 @@
located in the ``py/bin/`` directory. For switches to
modify test execution pass the ``-h`` option.
+Coverage reports
+----------------
+
+In order to get coverage reports the `pytest-cov`_ plugin is included.
+it adds some extra requirements ( coverage_ and `cov-core`_ )
+and can once they are installed coverage testing can be invoked via::
+
+ python test_all.py --cov file_or_direcory_to_cover file_or_directory
+
+.. _`pytest-cov`: http://pypi.python.org/pypi/pytest-cov
+.. _`coverage`: http://pypi.python.org/pypi/coverage
+.. _`cov-core`: http://pypi.python.org/pypi/cov-core
+
Test conventions
----------------
diff --git a/pypy/doc/config/objspace.opcodes.CALL_LIKELY_BUILTIN.txt b/pypy/doc/config/objspace.opcodes.CALL_LIKELY_BUILTIN.txt
deleted file mode 100644
--- a/pypy/doc/config/objspace.opcodes.CALL_LIKELY_BUILTIN.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Introduce a new opcode called ``CALL_LIKELY_BUILTIN``. It is used when something
-is called, that looks like a builtin function (but could in reality be shadowed
-by a name in the module globals). For all module globals dictionaries it is
-then tracked which builtin name is shadowed in this module. If the
-``CALL_LIKELY_BUILTIN`` opcode is executed, it is checked whether the builtin is
-shadowed. If not, the corresponding builtin is called. Otherwise the object that
-is shadowing it is called instead. If no shadowing is happening, this saves two
-dictionary lookups on calls to builtins.
-
-For more information, see the section in `Standard Interpreter Optimizations`_.
-
-.. _`Standard Interpreter Optimizations`: ../interpreter-optimizations.html#call-likely-builtin
diff --git a/pypy/doc/config/objspace.std.withdictmeasurement.txt b/pypy/doc/config/objspace.std.withdictmeasurement.txt
deleted file mode 100644
--- a/pypy/doc/config/objspace.std.withdictmeasurement.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Internal option.
-
-.. internal
diff --git a/pypy/doc/config/objspace.std.withidentitydict.txt b/pypy/doc/config/objspace.std.withidentitydict.txt
new file mode 100644
--- /dev/null
+++ b/pypy/doc/config/objspace.std.withidentitydict.txt
@@ -0,0 +1,21 @@
+=============================
+objspace.std.withidentitydict
+=============================
+
+* **name:** withidentitydict
+
+* **description:** enable a dictionary strategy for "by identity" comparisons
+
+* **command-line:** --objspace-std-withidentitydict
+
+* **command-line for negation:** --no-objspace-std-withidentitydict
+
+* **option type:** boolean option
+
+* **default:** True
+
+
+Enable a dictionary strategy specialized for instances of classes which
+compares "by identity", which is the default unless you override ``__hash__``,
+``__eq__`` or ``__cmp__``. This strategy will be used only with new-style
+classes.
diff --git a/pypy/doc/config/translation.dont_write_c_files.txt b/pypy/doc/config/translation.dont_write_c_files.txt
new file mode 100644
--- /dev/null
+++ b/pypy/doc/config/translation.dont_write_c_files.txt
@@ -0,0 +1,4 @@
+write the generated C files to ``/dev/null`` instead of to the disk. Useful if
+you want to use translate.py as a benchmark and don't want to access the disk.
+
+.. _`translation documentation`: ../translation.html
diff --git a/pypy/doc/config/translation.gc.txt b/pypy/doc/config/translation.gc.txt
--- a/pypy/doc/config/translation.gc.txt
+++ b/pypy/doc/config/translation.gc.txt
@@ -1,4 +1,6 @@
-Choose the Garbage Collector used by the translated program:
+Choose the Garbage Collector used by the translated program.
+The good performing collectors are "hybrid" and "minimark".
+The default is "minimark".
- "ref": reference counting. Takes very long to translate and the result is
slow.
@@ -11,3 +13,12 @@
older generation.
- "boehm": use the Boehm conservative GC.
+
+ - "hybrid": a hybrid collector of "generation" together with a
+ mark-n-sweep old space
+
+ - "markcompact": a slow, but memory-efficient collector,
+ influenced e.g. by Smalltalk systems.
+
+ - "minimark": a generational mark-n-sweep collector with good
+ performance. Includes page marking for large arrays.
diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst
--- a/pypy/doc/cpython_differences.rst
+++ b/pypy/doc/cpython_differences.rst
@@ -211,6 +211,38 @@
>>>> print d1['a']
42
+Mutating classes of objects which are already used as dictionary keys
+---------------------------------------------------------------------
+
+Consider the following snippet of code::
+
+ class X(object):
+ pass
+
+ def __evil_eq__(self, other):
+ print 'hello world'
+ return False
+
+ def evil(y):
+ d = {x(): 1}
+ X.__eq__ = __evil_eq__
+ d[y] # might trigger a call to __eq__?
+
+In CPython, __evil_eq__ **might** be called, although there is no way to write
+a test which reliably calls it. It happens if ``y is not x`` and ``hash(y) ==
+hash(x)``, where ``hash(x)`` is computed when ``x`` is inserted into the
+dictionary. If **by chance** the condition is satisfied, then ``__evil_eq__``
+is called.
+
+PyPy uses a special strategy to optimize dictionaries whose keys are instances
+of user-defined classes which do not override the default ``__hash__``,
+``__eq__`` and ``__cmp__``: when using this strategy, ``__eq__`` and
+``__cmp__`` are never called, but instead the lookup is done by identity, so
+in the case above it is guaranteed that ``__eq__`` won't be called.
+
+Note that in all other cases (e.g., if you have a custom ``__hash__`` and
+``__eq__`` in ``y``) the behavior is exactly the same as CPython.
+
Ignored exceptions
-----------------------
@@ -248,5 +280,7 @@
never a dictionary as it sometimes is in CPython. Assigning to
``__builtins__`` has no effect.
+* object identity of immutable keys in dictionaries is not necessarily preserved.
+ Never compare immutable objects with ``is``.
+
.. include:: _ref.txt
-
diff --git a/pypy/doc/extending.rst b/pypy/doc/extending.rst
--- a/pypy/doc/extending.rst
+++ b/pypy/doc/extending.rst
@@ -19,12 +19,12 @@
section
* Write them in pure python and use direct libffi low-level bindings, See
- \_rawffi_ module description.
+ \_ffi_ module description.
* Write them in RPython as mixedmodule_, using *rffi* as bindings.
.. _ctypes: #CTypes
-.. _\_rawffi: #LibFFI
+.. _\_ffi: #LibFFI
.. _mixedmodule: #Mixed Modules
CTypes
@@ -42,41 +42,50 @@
platform-dependent details (compiling small snippets of C code and running
them), so it'll benefit not pypy-related ctypes-based modules as well.
+ctypes call are optimized by the JIT and the resulting machine code contains a
+direct call to the target C function. However, due to the very dynamic nature
+of ctypes, some overhead over a bare C call is still present, in particular to
+check/convert the types of the parameters. Moreover, even if most calls are
+optimized, some cannot and thus need to follow the slow path, not optimized by
+the JIT.
+
.. _`ctypes-configure`: ctypes-implementation.html#ctypes-configure
+.. _`CPython ctypes`: http://docs.python.org/library/ctypes.html
Pros
----
-Stable, CPython-compatible API
+Stable, CPython-compatible API. Most calls are fast, optimized by JIT.
Cons
----
-Only pure-python code (slow), problems with platform-dependency (although
-we partially solve those). PyPy implementation is now very slow.
+Problems with platform-dependency (although we partially solve
+those). Although the JIT optimizes ctypes calls, some overhead is still
+present. The slow-path is very slow.
-_`CPython ctypes`: http://python.net/crew/theller/ctypes/
LibFFI
======
Mostly in order to be able to write a ctypes module, we developed a very
-low-level libffi bindings. (libffi is a C-level library for dynamic calling,
+low-level libffi bindings called ``_ffi``. (libffi is a C-level library for dynamic calling,
which is used by CPython ctypes). This library provides stable and usable API,
although it's API is a very low-level one. It does not contain any
-magic.
+magic. It is also optimized by the JIT, but has much less overhead than ctypes.
Pros
----
-Works. Combines disadvantages of using ctypes with disadvantages of
-using mixed modules. Probably more suitable for a delicate code
-where ctypes magic goes in a way.
+It Works. Probably more suitable for a delicate code where ctypes magic goes
+in a way. All calls are optimized by the JIT, there is no slow path as in
+ctypes.
Cons
----
-Slow. CPython-incompatible API, very rough and low-level
+It combines disadvantages of using ctypes with disadvantages of using mixed
+modules. CPython-incompatible API, very rough and low-level.
Mixed Modules
=============
@@ -87,15 +96,15 @@
* a mixed module needs to be written in RPython, which is far more
complicated than Python (XXX link)
-* due to lack of separate compilation (as of April 2008), each
+* due to lack of separate compilation (as of July 2011), each
compilation-check requires to recompile whole PyPy python interpreter,
which takes 0.5-1h. We plan to solve this at some point in near future.
* although rpython is a garbage-collected language, the border between
C and RPython needs to be managed by hand (each object that goes into the
- C level must be explicitly freed) XXX we try to solve this
+ C level must be explicitly freed).
-Some document is available `here`_
+Some documentation is available `here`_
.. _`here`: rffi.html
diff --git a/pypy/doc/getting-started.rst b/pypy/doc/getting-started.rst
--- a/pypy/doc/getting-started.rst
+++ b/pypy/doc/getting-started.rst
@@ -51,7 +51,7 @@
---------------
PyPy is ready to be executed as soon as you unpack the tarball or the zip
-file, with no need install it in any specific location::
+file, with no need to install it in any specific location::
$ tar xf pypy-1.5-linux.tar.bz2
diff --git a/pypy/doc/index.rst b/pypy/doc/index.rst
--- a/pypy/doc/index.rst
+++ b/pypy/doc/index.rst
@@ -11,6 +11,10 @@
Getting into PyPy ...
=============================================
+* `Getting started`_: how to install and run the PyPy Python interpreter
+
+* `FAQ`_: some frequently asked questions.
+
* `Release 1.5`_: the latest official release
* `PyPy Blog`_: news and status info about PyPy
@@ -26,13 +30,6 @@
Documentation for the PyPy Python Interpreter
===============================================
-`getting started`_ provides hands-on instructions
-including a two-liner to run the PyPy Python interpreter
-on your system, examples on advanced features and
-entry points for using the `RPython toolchain`_.
-
-`FAQ`_ contains some frequently asked questions.
-
New features of PyPy's Python Interpreter and
Translation Framework:
diff --git a/pypy/doc/interpreter-optimizations.rst b/pypy/doc/interpreter-optimizations.rst
--- a/pypy/doc/interpreter-optimizations.rst
+++ b/pypy/doc/interpreter-optimizations.rst
@@ -157,32 +157,6 @@
A more advanced version of sharing dicts, called *map dicts,* is available
with the :config:`objspace.std.withmapdict` option.
-Builtin-Shadowing
-+++++++++++++++++
-
-Usually the calling of builtins in Python requires two dictionary lookups: first
-to see whether the current global dictionary contains an object with the same
-name, then a lookup in the ``__builtin__`` dictionary. This is somehow
-circumvented by storing an often used builtin into a local variable to get
-the fast local lookup (which is a rather strange and ugly hack).
-
-The same problem is solved in a different way by "wary" dictionaries. They are
-another dictionary representation used together with multidicts. This
-representation is used only for module dictionaries. The representation checks on
-every setitem whether the key that is used is the name of a builtin. If this is
-the case, the dictionary is marked as shadowing that particular builtin.
-
-To identify calls to builtins easily, a new bytecode (``CALL_LIKELY_BUILTIN``)
-is introduced. Whenever it is executed, the globals dictionary is checked
-to see whether it masks the builtin (which is possible without a dictionary
-lookup). Then the ``__builtin__`` dict is checked in the same way,
-to see whether somebody replaced the real builtin with something else. In the
-common case, the program didn't do any of these; the proper builtin can then
-be called without using any dictionary lookup at all.
-
-You can enable this feature with the
-:config:`objspace.opcodes.CALL_LIKELY_BUILTIN` option.
-
List Optimizations
------------------
@@ -289,34 +263,6 @@
You can enable this feature with the :config:`objspace.opcodes.CALL_METHOD`
option.
-.. _`call likely builtin`:
-
-CALL_LIKELY_BUILTIN
-+++++++++++++++++++
-
-A often heard "tip" for speeding up Python programs is to give an often used
-builtin a local name, since local lookups are faster than lookups of builtins,
-which involve doing two dictionary lookups: one in the globals dictionary and
-one in the the builtins dictionary. PyPy approaches this problem at the
-implementation level, with the introduction of the new ``CALL_LIKELY_BUILTIN``
-bytecode. This bytecode is produced by the compiler for a call whose target is
-the name of a builtin. Since such a syntactic construct is very often actually
-invoking the expected builtin at run-time, this information can be used to make
-the call to the builtin directly, without going through any dictionary lookup.
-
-However, it can occur that the name is shadowed by a global name from the
-current module. To catch this case, a special dictionary implementation for
-multidicts is introduced, which is used for the dictionaries of modules. This
-implementation keeps track which builtin name is shadowed by it. The
-``CALL_LIKELY_BUILTIN`` bytecode asks the dictionary whether it is shadowing the
-builtin that is about to be called and asks the dictionary of ``__builtin__``
-whether the original builtin was changed. These two checks are cheaper than
-full lookups. In the common case, neither of these cases is true, so the
-builtin can be directly invoked.
-
-You can enable this feature with the
-:config:`objspace.opcodes.CALL_LIKELY_BUILTIN` option.
-
.. more here?
Overall Effects
diff --git a/pypy/doc/windows.rst b/pypy/doc/windows.rst
--- a/pypy/doc/windows.rst
+++ b/pypy/doc/windows.rst
@@ -32,6 +32,24 @@
modules that relies on third-party libraries. See below how to get
and build them.
+Preping Windows for the Large Build
+-----------------------------------
+
+Normally 32bit programs are limited to 2GB of memory on Windows. It is
+possible to raise this limit, to 3GB on Windows 32bit, and almost 4GB
+on Windows 64bit.
+
+On Windows 32bit, it is necessary to modify the system: follow
+http://usa.autodesk.com/adsk/servlet/ps/dl/item?siteID=123112&id=9583842&linkID=9240617
+to enable the "3GB" feature, and reboot. This step is not necessary on
+Windows 64bit.
+
+Then you need to execute::
+
+ editbin /largeaddressaware pypy.exe
+
+on the pypy.exe file you compiled.
+
Installing external packages
----------------------------
diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py
--- a/pypy/interpreter/argument.py
+++ b/pypy/interpreter/argument.py
@@ -17,7 +17,7 @@
self.varargname = varargname
self.kwargname = kwargname
- @jit.purefunction
+ @jit.elidable
def find_argname(self, name):
try:
return self.argnames.index(name)
diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -655,9 +655,6 @@
def _compute_CALL_FUNCTION_VAR_KW(arg):
return -_num_args(arg) - 2
-def _compute_CALL_LIKELY_BUILTIN(arg):
- return -(arg & 0xFF) + 1
-
def _compute_CALL_METHOD(arg):
return -_num_args(arg) - 1
diff --git a/pypy/interpreter/astcompiler/ast.py b/pypy/interpreter/astcompiler/ast.py
--- a/pypy/interpreter/astcompiler/ast.py
+++ b/pypy/interpreter/astcompiler/ast.py
@@ -2541,8 +2541,9 @@
class ASTVisitor(object):
def visit_sequence(self, seq):
- for node in seq:
- node.walkabout(self)
+ if seq is not None:
+ for node in seq:
+ node.walkabout(self)
def default_visitor(self, node):
raise NodeVisitorNotImplemented
@@ -2673,46 +2674,36 @@
class GenericASTVisitor(ASTVisitor):
def visit_Module(self, node):
- if node.body:
- self.visit_sequence(node.body)
+ self.visit_sequence(node.body)
def visit_Interactive(self, node):
- if node.body:
- self.visit_sequence(node.body)
+ self.visit_sequence(node.body)
def visit_Expression(self, node):
node.body.walkabout(self)
def visit_Suite(self, node):
- if node.body:
- self.visit_sequence(node.body)
+ self.visit_sequence(node.body)
def visit_FunctionDef(self, node):
node.args.walkabout(self)
- if node.body:
- self.visit_sequence(node.body)
- if node.decorator_list:
- self.visit_sequence(node.decorator_list)
+ self.visit_sequence(node.body)
+ self.visit_sequence(node.decorator_list)
def visit_ClassDef(self, node):
- if node.bases:
- self.visit_sequence(node.bases)
- if node.body:
- self.visit_sequence(node.body)
- if node.decorator_list:
- self.visit_sequence(node.decorator_list)
+ self.visit_sequence(node.bases)
+ self.visit_sequence(node.body)
+ self.visit_sequence(node.decorator_list)
def visit_Return(self, node):
if node.value:
node.value.walkabout(self)
def visit_Delete(self, node):
- if node.targets:
- self.visit_sequence(node.targets)
+ self.visit_sequence(node.targets)
def visit_Assign(self, node):
- if node.targets:
- self.visit_sequence(node.targets)
+ self.visit_sequence(node.targets)
node.value.walkabout(self)
def visit_AugAssign(self, node):
@@ -2722,37 +2713,29 @@
def visit_Print(self, node):
if node.dest:
node.dest.walkabout(self)
- if node.values:
- self.visit_sequence(node.values)
+ self.visit_sequence(node.values)
def visit_For(self, node):
node.target.walkabout(self)
node.iter.walkabout(self)
- if node.body:
- self.visit_sequence(node.body)
- if node.orelse:
- self.visit_sequence(node.orelse)
+ self.visit_sequence(node.body)
+ self.visit_sequence(node.orelse)
def visit_While(self, node):
node.test.walkabout(self)
- if node.body:
- self.visit_sequence(node.body)
- if node.orelse:
- self.visit_sequence(node.orelse)
+ self.visit_sequence(node.body)
+ self.visit_sequence(node.orelse)
def visit_If(self, node):
node.test.walkabout(self)
- if node.body:
- self.visit_sequence(node.body)
- if node.orelse:
- self.visit_sequence(node.orelse)
+ self.visit_sequence(node.body)
+ self.visit_sequence(node.orelse)
def visit_With(self, node):
node.context_expr.walkabout(self)
if node.optional_vars:
node.optional_vars.walkabout(self)
- if node.body:
- self.visit_sequence(node.body)
+ self.visit_sequence(node.body)
def visit_Raise(self, node):
if node.type:
@@ -2763,18 +2746,13 @@
node.tback.walkabout(self)
def visit_TryExcept(self, node):
- if node.body:
- self.visit_sequence(node.body)
- if node.handlers:
- self.visit_sequence(node.handlers)
- if node.orelse:
- self.visit_sequence(node.orelse)
+ self.visit_sequence(node.body)
+ self.visit_sequence(node.handlers)
+ self.visit_sequence(node.orelse)
def visit_TryFinally(self, node):
- if node.body:
- self.visit_sequence(node.body)
- if node.finalbody:
- self.visit_sequence(node.finalbody)
+ self.visit_sequence(node.body)
+ self.visit_sequence(node.finalbody)
def visit_Assert(self, node):
node.test.walkabout(self)
@@ -2782,12 +2760,10 @@
node.msg.walkabout(self)
def visit_Import(self, node):
- if node.names:
- self.visit_sequence(node.names)
+ self.visit_sequence(node.names)
def visit_ImportFrom(self, node):
- if node.names:
- self.visit_sequence(node.names)
+ self.visit_sequence(node.names)
def visit_Exec(self, node):
node.body.walkabout(self)
@@ -2812,8 +2788,7 @@
pass
def visit_BoolOp(self, node):
- if node.values:
- self.visit_sequence(node.values)
+ self.visit_sequence(node.values)
def visit_BinOp(self, node):
node.left.walkabout(self)
@@ -2832,35 +2807,28 @@
node.orelse.walkabout(self)
def visit_Dict(self, node):
- if node.keys:
- self.visit_sequence(node.keys)
- if node.values:
- self.visit_sequence(node.values)
+ self.visit_sequence(node.keys)
+ self.visit_sequence(node.values)
def visit_Set(self, node):
- if node.elts:
- self.visit_sequence(node.elts)
+ self.visit_sequence(node.elts)
def visit_ListComp(self, node):
node.elt.walkabout(self)
- if node.generators:
- self.visit_sequence(node.generators)
+ self.visit_sequence(node.generators)
def visit_SetComp(self, node):
node.elt.walkabout(self)
- if node.generators:
- self.visit_sequence(node.generators)
+ self.visit_sequence(node.generators)
def visit_DictComp(self, node):
node.key.walkabout(self)
node.value.walkabout(self)
- if node.generators:
- self.visit_sequence(node.generators)
+ self.visit_sequence(node.generators)
def visit_GeneratorExp(self, node):
node.elt.walkabout(self)
- if node.generators:
- self.visit_sequence(node.generators)
+ self.visit_sequence(node.generators)
def visit_Yield(self, node):
if node.value:
@@ -2868,15 +2836,12 @@
def visit_Compare(self, node):
node.left.walkabout(self)
- if node.comparators:
- self.visit_sequence(node.comparators)
+ self.visit_sequence(node.comparators)
def visit_Call(self, node):
node.func.walkabout(self)
- if node.args:
- self.visit_sequence(node.args)
- if node.keywords:
- self.visit_sequence(node.keywords)
+ self.visit_sequence(node.args)
+ self.visit_sequence(node.keywords)
if node.starargs:
node.starargs.walkabout(self)
if node.kwargs:
@@ -2902,12 +2867,10 @@
pass
def visit_List(self, node):
- if node.elts:
- self.visit_sequence(node.elts)
+ self.visit_sequence(node.elts)
def visit_Tuple(self, node):
- if node.elts:
- self.visit_sequence(node.elts)
+ self.visit_sequence(node.elts)
def visit_Const(self, node):
pass
@@ -2924,8 +2887,7 @@
node.step.walkabout(self)
def visit_ExtSlice(self, node):
- if node.dims:
- self.visit_sequence(node.dims)
+ self.visit_sequence(node.dims)
def visit_Index(self, node):
node.value.walkabout(self)
@@ -2933,22 +2895,18 @@
def visit_comprehension(self, node):
node.target.walkabout(self)
node.iter.walkabout(self)
- if node.ifs:
- self.visit_sequence(node.ifs)
+ self.visit_sequence(node.ifs)
def visit_ExceptHandler(self, node):
if node.type:
node.type.walkabout(self)
if node.name:
node.name.walkabout(self)
- if node.body:
- self.visit_sequence(node.body)
+ self.visit_sequence(node.body)
def visit_arguments(self, node):
- if node.args:
- self.visit_sequence(node.args)
- if node.defaults:
- self.visit_sequence(node.defaults)
+ self.visit_sequence(node.args)
+ self.visit_sequence(node.defaults)
def visit_keyword(self, node):
node.value.walkabout(self)
@@ -3069,6 +3027,7 @@
raise
w_self.setdictvalue(space, 'body', w_new_value)
return
+ w_self.deldictvalue(space, 'body')
w_self.initialization_state |= 1
_Expression_field_unroller = unrolling_iterable(['body'])
@@ -3157,6 +3116,7 @@
raise
w_self.setdictvalue(space, 'lineno', w_new_value)
return
+ w_self.deldictvalue(space, 'lineno')
w_self.initialization_state |= w_self._lineno_mask
def stmt_get_col_offset(space, w_self):
@@ -3178,6 +3138,7 @@
raise
w_self.setdictvalue(space, 'col_offset', w_new_value)
return
+ w_self.deldictvalue(space, 'col_offset')
w_self.initialization_state |= w_self._col_offset_mask
stmt.typedef = typedef.TypeDef("stmt",
@@ -3208,6 +3169,7 @@
raise
w_self.setdictvalue(space, 'name', w_new_value)
return
+ w_self.deldictvalue(space, 'name')
w_self.initialization_state |= 1
def FunctionDef_get_args(space, w_self):
@@ -3229,6 +3191,7 @@
raise
w_self.setdictvalue(space, 'args', w_new_value)
return
+ w_self.deldictvalue(space, 'args')
w_self.initialization_state |= 2
def FunctionDef_get_body(space, w_self):
@@ -3315,6 +3278,7 @@
raise
w_self.setdictvalue(space, 'name', w_new_value)
return
+ w_self.deldictvalue(space, 'name')
w_self.initialization_state |= 1
def ClassDef_get_bases(space, w_self):
@@ -3420,6 +3384,7 @@
raise
w_self.setdictvalue(space, 'value', w_new_value)
return
+ w_self.deldictvalue(space, 'value')
w_self.initialization_state |= 1
_Return_field_unroller = unrolling_iterable(['value'])
@@ -3526,6 +3491,7 @@
raise
w_self.setdictvalue(space, 'value', w_new_value)
return
+ w_self.deldictvalue(space, 'value')
w_self.initialization_state |= 2
_Assign_field_unroller = unrolling_iterable(['targets', 'value'])
@@ -3573,6 +3539,7 @@
raise
w_self.setdictvalue(space, 'target', w_new_value)
return
+ w_self.deldictvalue(space, 'target')
w_self.initialization_state |= 1
def AugAssign_get_op(space, w_self):
@@ -3590,13 +3557,13 @@
try:
obj = space.interp_w(operator, w_new_value)
w_self.op = obj.to_simple_int(space)
- # need to save the original object too
- w_self.setdictvalue(space, 'op', w_new_value)
except OperationError, e:
if not e.match(space, space.w_TypeError):
raise
w_self.setdictvalue(space, 'op', w_new_value)
return
+ # need to save the original object too
+ w_self.setdictvalue(space, 'op', w_new_value)
w_self.initialization_state |= 2
def AugAssign_get_value(space, w_self):
@@ -3618,6 +3585,7 @@
raise
w_self.setdictvalue(space, 'value', w_new_value)
return
+ w_self.deldictvalue(space, 'value')
w_self.initialization_state |= 4
_AugAssign_field_unroller = unrolling_iterable(['target', 'op', 'value'])
@@ -3665,6 +3633,7 @@
raise
w_self.setdictvalue(space, 'dest', w_new_value)
return
+ w_self.deldictvalue(space, 'dest')
w_self.initialization_state |= 1
def Print_get_values(space, w_self):
@@ -3704,6 +3673,7 @@
raise
w_self.setdictvalue(space, 'nl', w_new_value)
return
+ w_self.deldictvalue(space, 'nl')
w_self.initialization_state |= 4
_Print_field_unroller = unrolling_iterable(['dest', 'values', 'nl'])
@@ -3752,6 +3722,7 @@
raise
w_self.setdictvalue(space, 'target', w_new_value)
return
+ w_self.deldictvalue(space, 'target')
w_self.initialization_state |= 1
def For_get_iter(space, w_self):
@@ -3773,6 +3744,7 @@
raise
w_self.setdictvalue(space, 'iter', w_new_value)
return
+ w_self.deldictvalue(space, 'iter')
w_self.initialization_state |= 2
def For_get_body(space, w_self):
@@ -3859,6 +3831,7 @@
raise
w_self.setdictvalue(space, 'test', w_new_value)
return
+ w_self.deldictvalue(space, 'test')
w_self.initialization_state |= 1
def While_get_body(space, w_self):
@@ -3944,6 +3917,7 @@
raise
w_self.setdictvalue(space, 'test', w_new_value)
return
+ w_self.deldictvalue(space, 'test')
w_self.initialization_state |= 1
def If_get_body(space, w_self):
@@ -4029,6 +4003,7 @@
raise
w_self.setdictvalue(space, 'context_expr', w_new_value)
return
+ w_self.deldictvalue(space, 'context_expr')
w_self.initialization_state |= 1
def With_get_optional_vars(space, w_self):
@@ -4050,6 +4025,7 @@
raise
w_self.setdictvalue(space, 'optional_vars', w_new_value)
return
+ w_self.deldictvalue(space, 'optional_vars')
w_self.initialization_state |= 2
def With_get_body(space, w_self):
@@ -4116,6 +4092,7 @@
raise
w_self.setdictvalue(space, 'type', w_new_value)
return
+ w_self.deldictvalue(space, 'type')
w_self.initialization_state |= 1
def Raise_get_inst(space, w_self):
@@ -4137,6 +4114,7 @@
raise
w_self.setdictvalue(space, 'inst', w_new_value)
return
+ w_self.deldictvalue(space, 'inst')
w_self.initialization_state |= 2
def Raise_get_tback(space, w_self):
@@ -4158,6 +4136,7 @@
raise
w_self.setdictvalue(space, 'tback', w_new_value)
return
+ w_self.deldictvalue(space, 'tback')
w_self.initialization_state |= 4
_Raise_field_unroller = unrolling_iterable(['type', 'inst', 'tback'])
@@ -4351,6 +4330,7 @@
raise
w_self.setdictvalue(space, 'test', w_new_value)
return
+ w_self.deldictvalue(space, 'test')
w_self.initialization_state |= 1
def Assert_get_msg(space, w_self):
@@ -4372,6 +4352,7 @@
raise
w_self.setdictvalue(space, 'msg', w_new_value)
return
+ w_self.deldictvalue(space, 'msg')
w_self.initialization_state |= 2
_Assert_field_unroller = unrolling_iterable(['test', 'msg'])
@@ -4464,6 +4445,7 @@
raise
w_self.setdictvalue(space, 'module', w_new_value)
return
+ w_self.deldictvalue(space, 'module')
w_self.initialization_state |= 1
def ImportFrom_get_names(space, w_self):
@@ -4503,6 +4485,7 @@
raise
w_self.setdictvalue(space, 'level', w_new_value)
return
+ w_self.deldictvalue(space, 'level')
w_self.initialization_state |= 4
_ImportFrom_field_unroller = unrolling_iterable(['module', 'names', 'level'])
@@ -4551,6 +4534,7 @@
raise
w_self.setdictvalue(space, 'body', w_new_value)
return
+ w_self.deldictvalue(space, 'body')
w_self.initialization_state |= 1
def Exec_get_globals(space, w_self):
@@ -4572,6 +4556,7 @@
raise
w_self.setdictvalue(space, 'globals', w_new_value)
return
+ w_self.deldictvalue(space, 'globals')
w_self.initialization_state |= 2
def Exec_get_locals(space, w_self):
@@ -4593,6 +4578,7 @@
raise
w_self.setdictvalue(space, 'locals', w_new_value)
return
+ w_self.deldictvalue(space, 'locals')
w_self.initialization_state |= 4
_Exec_field_unroller = unrolling_iterable(['body', 'globals', 'locals'])
@@ -4683,6 +4669,7 @@
raise
w_self.setdictvalue(space, 'value', w_new_value)
return
+ w_self.deldictvalue(space, 'value')
w_self.initialization_state |= 1
_Expr_field_unroller = unrolling_iterable(['value'])
@@ -4779,6 +4766,7 @@
raise
w_self.setdictvalue(space, 'lineno', w_new_value)
return
+ w_self.deldictvalue(space, 'lineno')
w_self.initialization_state |= w_self._lineno_mask
def expr_get_col_offset(space, w_self):
@@ -4800,6 +4788,7 @@
raise
w_self.setdictvalue(space, 'col_offset', w_new_value)
return
+ w_self.deldictvalue(space, 'col_offset')
w_self.initialization_state |= w_self._col_offset_mask
expr.typedef = typedef.TypeDef("expr",
@@ -4826,13 +4815,13 @@
try:
obj = space.interp_w(boolop, w_new_value)
w_self.op = obj.to_simple_int(space)
- # need to save the original object too
- w_self.setdictvalue(space, 'op', w_new_value)
except OperationError, e:
if not e.match(space, space.w_TypeError):
raise
w_self.setdictvalue(space, 'op', w_new_value)
return
+ # need to save the original object too
+ w_self.setdictvalue(space, 'op', w_new_value)
w_self.initialization_state |= 1
def BoolOp_get_values(space, w_self):
@@ -4898,6 +4887,7 @@
raise
w_self.setdictvalue(space, 'left', w_new_value)
return
+ w_self.deldictvalue(space, 'left')
w_self.initialization_state |= 1
def BinOp_get_op(space, w_self):
@@ -4915,13 +4905,13 @@
try:
obj = space.interp_w(operator, w_new_value)
w_self.op = obj.to_simple_int(space)
- # need to save the original object too
- w_self.setdictvalue(space, 'op', w_new_value)
except OperationError, e:
if not e.match(space, space.w_TypeError):
raise
w_self.setdictvalue(space, 'op', w_new_value)
return
+ # need to save the original object too
+ w_self.setdictvalue(space, 'op', w_new_value)
w_self.initialization_state |= 2
def BinOp_get_right(space, w_self):
@@ -4943,6 +4933,7 @@
raise
w_self.setdictvalue(space, 'right', w_new_value)
return
+ w_self.deldictvalue(space, 'right')
w_self.initialization_state |= 4
_BinOp_field_unroller = unrolling_iterable(['left', 'op', 'right'])
@@ -4986,13 +4977,13 @@
try:
obj = space.interp_w(unaryop, w_new_value)
w_self.op = obj.to_simple_int(space)
- # need to save the original object too
- w_self.setdictvalue(space, 'op', w_new_value)
except OperationError, e:
if not e.match(space, space.w_TypeError):
raise
w_self.setdictvalue(space, 'op', w_new_value)
return
+ # need to save the original object too
+ w_self.setdictvalue(space, 'op', w_new_value)
w_self.initialization_state |= 1
def UnaryOp_get_operand(space, w_self):
@@ -5014,6 +5005,7 @@
raise
w_self.setdictvalue(space, 'operand', w_new_value)
return
+ w_self.deldictvalue(space, 'operand')
w_self.initialization_state |= 2
_UnaryOp_field_unroller = unrolling_iterable(['op', 'operand'])
@@ -5060,6 +5052,7 @@
raise
w_self.setdictvalue(space, 'args', w_new_value)
return
+ w_self.deldictvalue(space, 'args')
w_self.initialization_state |= 1
def Lambda_get_body(space, w_self):
@@ -5081,6 +5074,7 @@
raise
w_self.setdictvalue(space, 'body', w_new_value)
return
+ w_self.deldictvalue(space, 'body')
w_self.initialization_state |= 2
_Lambda_field_unroller = unrolling_iterable(['args', 'body'])
@@ -5127,6 +5121,7 @@
raise
w_self.setdictvalue(space, 'test', w_new_value)
return
+ w_self.deldictvalue(space, 'test')
w_self.initialization_state |= 1
def IfExp_get_body(space, w_self):
@@ -5148,6 +5143,7 @@
raise
w_self.setdictvalue(space, 'body', w_new_value)
return
+ w_self.deldictvalue(space, 'body')
w_self.initialization_state |= 2
def IfExp_get_orelse(space, w_self):
@@ -5169,6 +5165,7 @@
raise
w_self.setdictvalue(space, 'orelse', w_new_value)
return
+ w_self.deldictvalue(space, 'orelse')
w_self.initialization_state |= 4
_IfExp_field_unroller = unrolling_iterable(['test', 'body', 'orelse'])
@@ -5322,6 +5319,7 @@
raise
w_self.setdictvalue(space, 'elt', w_new_value)
return
+ w_self.deldictvalue(space, 'elt')
w_self.initialization_state |= 1
def ListComp_get_generators(space, w_self):
@@ -5387,6 +5385,7 @@
raise
w_self.setdictvalue(space, 'elt', w_new_value)
return
+ w_self.deldictvalue(space, 'elt')
w_self.initialization_state |= 1
def SetComp_get_generators(space, w_self):
@@ -5452,6 +5451,7 @@
raise
w_self.setdictvalue(space, 'key', w_new_value)
return
+ w_self.deldictvalue(space, 'key')
w_self.initialization_state |= 1
def DictComp_get_value(space, w_self):
@@ -5473,6 +5473,7 @@
raise
w_self.setdictvalue(space, 'value', w_new_value)
return
+ w_self.deldictvalue(space, 'value')
w_self.initialization_state |= 2
def DictComp_get_generators(space, w_self):
@@ -5539,6 +5540,7 @@
raise
w_self.setdictvalue(space, 'elt', w_new_value)
return
+ w_self.deldictvalue(space, 'elt')
w_self.initialization_state |= 1
def GeneratorExp_get_generators(space, w_self):
@@ -5604,6 +5606,7 @@
raise
w_self.setdictvalue(space, 'value', w_new_value)
return
+ w_self.deldictvalue(space, 'value')
w_self.initialization_state |= 1
_Yield_field_unroller = unrolling_iterable(['value'])
@@ -5649,6 +5652,7 @@
raise
w_self.setdictvalue(space, 'left', w_new_value)
return
+ w_self.deldictvalue(space, 'left')
w_self.initialization_state |= 1
def Compare_get_ops(space, w_self):
@@ -5734,6 +5738,7 @@
raise
w_self.setdictvalue(space, 'func', w_new_value)
return
+ w_self.deldictvalue(space, 'func')
w_self.initialization_state |= 1
def Call_get_args(space, w_self):
@@ -5791,6 +5796,7 @@
raise
w_self.setdictvalue(space, 'starargs', w_new_value)
return
+ w_self.deldictvalue(space, 'starargs')
w_self.initialization_state |= 8
def Call_get_kwargs(space, w_self):
@@ -5812,6 +5818,7 @@
raise
w_self.setdictvalue(space, 'kwargs', w_new_value)
return
+ w_self.deldictvalue(space, 'kwargs')
w_self.initialization_state |= 16
_Call_field_unroller = unrolling_iterable(['func', 'args', 'keywords', 'starargs', 'kwargs'])
@@ -5863,6 +5870,7 @@
raise
w_self.setdictvalue(space, 'value', w_new_value)
return
+ w_self.deldictvalue(space, 'value')
w_self.initialization_state |= 1
_Repr_field_unroller = unrolling_iterable(['value'])
@@ -5908,6 +5916,7 @@
raise
w_self.setdictvalue(space, 'n', w_new_value)
return
+ w_self.deldictvalue(space, 'n')
w_self.initialization_state |= 1
_Num_field_unroller = unrolling_iterable(['n'])
@@ -5953,6 +5962,7 @@
raise
w_self.setdictvalue(space, 's', w_new_value)
return
+ w_self.deldictvalue(space, 's')
w_self.initialization_state |= 1
_Str_field_unroller = unrolling_iterable(['s'])
@@ -5998,6 +6008,7 @@
raise
w_self.setdictvalue(space, 'value', w_new_value)
return
+ w_self.deldictvalue(space, 'value')
w_self.initialization_state |= 1
def Attribute_get_attr(space, w_self):
@@ -6019,6 +6030,7 @@
raise
w_self.setdictvalue(space, 'attr', w_new_value)
return
+ w_self.deldictvalue(space, 'attr')
w_self.initialization_state |= 2
def Attribute_get_ctx(space, w_self):
@@ -6036,13 +6048,13 @@
try:
obj = space.interp_w(expr_context, w_new_value)
w_self.ctx = obj.to_simple_int(space)
- # need to save the original object too
- w_self.setdictvalue(space, 'ctx', w_new_value)
except OperationError, e:
if not e.match(space, space.w_TypeError):
raise
w_self.setdictvalue(space, 'ctx', w_new_value)
return
+ # need to save the original object too
+ w_self.setdictvalue(space, 'ctx', w_new_value)
w_self.initialization_state |= 4
_Attribute_field_unroller = unrolling_iterable(['value', 'attr', 'ctx'])
@@ -6090,6 +6102,7 @@
raise
w_self.setdictvalue(space, 'value', w_new_value)
return
+ w_self.deldictvalue(space, 'value')
w_self.initialization_state |= 1
def Subscript_get_slice(space, w_self):
@@ -6111,6 +6124,7 @@
raise
w_self.setdictvalue(space, 'slice', w_new_value)
return
+ w_self.deldictvalue(space, 'slice')
w_self.initialization_state |= 2
def Subscript_get_ctx(space, w_self):
@@ -6128,13 +6142,13 @@
try:
obj = space.interp_w(expr_context, w_new_value)
w_self.ctx = obj.to_simple_int(space)
- # need to save the original object too
- w_self.setdictvalue(space, 'ctx', w_new_value)
except OperationError, e:
if not e.match(space, space.w_TypeError):
raise
w_self.setdictvalue(space, 'ctx', w_new_value)
return
+ # need to save the original object too
+ w_self.setdictvalue(space, 'ctx', w_new_value)
w_self.initialization_state |= 4
_Subscript_field_unroller = unrolling_iterable(['value', 'slice', 'ctx'])
@@ -6182,6 +6196,7 @@
raise
w_self.setdictvalue(space, 'id', w_new_value)
return
+ w_self.deldictvalue(space, 'id')
w_self.initialization_state |= 1
def Name_get_ctx(space, w_self):
@@ -6199,13 +6214,13 @@
try:
obj = space.interp_w(expr_context, w_new_value)
w_self.ctx = obj.to_simple_int(space)
- # need to save the original object too
- w_self.setdictvalue(space, 'ctx', w_new_value)
except OperationError, e:
if not e.match(space, space.w_TypeError):
raise
w_self.setdictvalue(space, 'ctx', w_new_value)
return
+ # need to save the original object too
+ w_self.setdictvalue(space, 'ctx', w_new_value)
w_self.initialization_state |= 2
_Name_field_unroller = unrolling_iterable(['id', 'ctx'])
@@ -6266,13 +6281,13 @@
try:
obj = space.interp_w(expr_context, w_new_value)
w_self.ctx = obj.to_simple_int(space)
- # need to save the original object too
- w_self.setdictvalue(space, 'ctx', w_new_value)
except OperationError, e:
if not e.match(space, space.w_TypeError):
raise
w_self.setdictvalue(space, 'ctx', w_new_value)
return
+ # need to save the original object too
+ w_self.setdictvalue(space, 'ctx', w_new_value)
w_self.initialization_state |= 2
_List_field_unroller = unrolling_iterable(['elts', 'ctx'])
@@ -6334,13 +6349,13 @@
try:
obj = space.interp_w(expr_context, w_new_value)
w_self.ctx = obj.to_simple_int(space)
- # need to save the original object too
- w_self.setdictvalue(space, 'ctx', w_new_value)
except OperationError, e:
if not e.match(space, space.w_TypeError):
raise
w_self.setdictvalue(space, 'ctx', w_new_value)
return
+ # need to save the original object too
+ w_self.setdictvalue(space, 'ctx', w_new_value)
w_self.initialization_state |= 2
_Tuple_field_unroller = unrolling_iterable(['elts', 'ctx'])
@@ -6388,6 +6403,7 @@
raise
w_self.setdictvalue(space, 'value', w_new_value)
return
+ w_self.deldictvalue(space, 'value')
w_self.initialization_state |= 1
_Const_field_unroller = unrolling_iterable(['value'])
@@ -6506,6 +6522,7 @@
raise
w_self.setdictvalue(space, 'lower', w_new_value)
return
+ w_self.deldictvalue(space, 'lower')
w_self.initialization_state |= 1
def Slice_get_upper(space, w_self):
@@ -6527,6 +6544,7 @@
raise
w_self.setdictvalue(space, 'upper', w_new_value)
return
+ w_self.deldictvalue(space, 'upper')
w_self.initialization_state |= 2
def Slice_get_step(space, w_self):
@@ -6548,6 +6566,7 @@
raise
w_self.setdictvalue(space, 'step', w_new_value)
return
+ w_self.deldictvalue(space, 'step')
w_self.initialization_state |= 4
_Slice_field_unroller = unrolling_iterable(['lower', 'upper', 'step'])
@@ -6638,6 +6657,7 @@
raise
w_self.setdictvalue(space, 'value', w_new_value)
return
+ w_self.deldictvalue(space, 'value')
w_self.initialization_state |= 1
_Index_field_unroller = unrolling_iterable(['value'])
@@ -6907,6 +6927,7 @@
raise
w_self.setdictvalue(space, 'target', w_new_value)
return
+ w_self.deldictvalue(space, 'target')
w_self.initialization_state |= 1
def comprehension_get_iter(space, w_self):
@@ -6928,6 +6949,7 @@
raise
w_self.setdictvalue(space, 'iter', w_new_value)
return
+ w_self.deldictvalue(space, 'iter')
w_self.initialization_state |= 2
def comprehension_get_ifs(space, w_self):
@@ -6994,6 +7016,7 @@
raise
w_self.setdictvalue(space, 'lineno', w_new_value)
return
+ w_self.deldictvalue(space, 'lineno')
w_self.initialization_state |= w_self._lineno_mask
def excepthandler_get_col_offset(space, w_self):
@@ -7015,6 +7038,7 @@
raise
w_self.setdictvalue(space, 'col_offset', w_new_value)
return
+ w_self.deldictvalue(space, 'col_offset')
w_self.initialization_state |= w_self._col_offset_mask
excepthandler.typedef = typedef.TypeDef("excepthandler",
@@ -7045,6 +7069,7 @@
raise
w_self.setdictvalue(space, 'type', w_new_value)
return
+ w_self.deldictvalue(space, 'type')
w_self.initialization_state |= 1
def ExceptHandler_get_name(space, w_self):
@@ -7066,6 +7091,7 @@
raise
w_self.setdictvalue(space, 'name', w_new_value)
return
+ w_self.deldictvalue(space, 'name')
w_self.initialization_state |= 2
def ExceptHandler_get_body(space, w_self):
@@ -7153,6 +7179,7 @@
raise
w_self.setdictvalue(space, 'vararg', w_new_value)
return
+ w_self.deldictvalue(space, 'vararg')
w_self.initialization_state |= 2
def arguments_get_kwarg(space, w_self):
@@ -7177,6 +7204,7 @@
raise
w_self.setdictvalue(space, 'kwarg', w_new_value)
return
+ w_self.deldictvalue(space, 'kwarg')
w_self.initialization_state |= 4
def arguments_get_defaults(space, w_self):
@@ -7245,6 +7273,7 @@
raise
w_self.setdictvalue(space, 'arg', w_new_value)
return
+ w_self.deldictvalue(space, 'arg')
w_self.initialization_state |= 1
def keyword_get_value(space, w_self):
@@ -7266,6 +7295,7 @@
raise
w_self.setdictvalue(space, 'value', w_new_value)
return
+ w_self.deldictvalue(space, 'value')
w_self.initialization_state |= 2
_keyword_field_unroller = unrolling_iterable(['arg', 'value'])
@@ -7312,6 +7342,7 @@
raise
w_self.setdictvalue(space, 'name', w_new_value)
return
+ w_self.deldictvalue(space, 'name')
w_self.initialization_state |= 1
def alias_get_asname(space, w_self):
@@ -7336,6 +7367,7 @@
raise
w_self.setdictvalue(space, 'asname', w_new_value)
return
+ w_self.deldictvalue(space, 'asname')
w_self.initialization_state |= 2
_alias_field_unroller = unrolling_iterable(['name', 'asname'])
diff --git a/pypy/interpreter/astcompiler/codegen.py b/pypy/interpreter/astcompiler/codegen.py
--- a/pypy/interpreter/astcompiler/codegen.py
+++ b/pypy/interpreter/astcompiler/codegen.py
@@ -12,7 +12,6 @@
from pypy.interpreter.pyparser.error import SyntaxError
from pypy.tool import stdlib_opcode as ops
from pypy.interpreter.error import OperationError
-from pypy.module.__builtin__.__init__ import BUILTIN_TO_INDEX
def compile_ast(space, module, info):
@@ -296,15 +295,11 @@
def visit_FunctionDef(self, func):
self.update_position(func.lineno, True)
# Load decorators first, but apply them after the function is created.
- if func.decorator_list:
- self.visit_sequence(func.decorator_list)
+ self.visit_sequence(func.decorator_list)
args = func.args
assert isinstance(args, ast.arguments)
- if args.defaults:
- self.visit_sequence(args.defaults)
- num_defaults = len(args.defaults)
- else:
- num_defaults = 0
+ self.visit_sequence(args.defaults)
+ num_defaults = len(args.defaults) if args.defaults is not None else 0
code = self.sub_scope(FunctionCodeGenerator, func.name, func,
func.lineno)
self._make_function(code, num_defaults)
@@ -318,24 +313,17 @@
self.update_position(lam.lineno)
args = lam.args
assert isinstance(args, ast.arguments)
- if args.defaults:
- self.visit_sequence(args.defaults)
- default_count = len(args.defaults)
- else:
- default_count = 0
+ self.visit_sequence(args.defaults)
+ default_count = len(args.defaults) if args.defaults is not None else 0
code = self.sub_scope(LambdaCodeGenerator, "<lambda>", lam, lam.lineno)
self._make_function(code, default_count)
def visit_ClassDef(self, cls):
self.update_position(cls.lineno, True)
- if cls.decorator_list:
- self.visit_sequence(cls.decorator_list)
+ self.visit_sequence(cls.decorator_list)
self.load_const(self.space.wrap(cls.name))
- if cls.bases:
- bases_count = len(cls.bases)
- self.visit_sequence(cls.bases)
- else:
- bases_count = 0
+ self.visit_sequence(cls.bases)
+ bases_count = len(cls.bases) if cls.bases is not None else 0
self.emit_op_arg(ops.BUILD_TUPLE, bases_count)
code = self.sub_scope(ClassCodeGenerator, cls.name, cls, cls.lineno)
self._make_function(code, 0)
@@ -447,8 +435,7 @@
end = self.new_block()
test_constant = if_.test.as_constant_truth(self.space)
if test_constant == optimize.CONST_FALSE:
- if if_.orelse:
- self.visit_sequence(if_.orelse)
+ self.visit_sequence(if_.orelse)
elif test_constant == optimize.CONST_TRUE:
self.visit_sequence(if_.body)
else:
@@ -516,16 +503,14 @@
self.use_next_block(cleanup)
self.emit_op(ops.POP_BLOCK)
self.pop_frame_block(F_BLOCK_LOOP, start)
- if fr.orelse:
- self.visit_sequence(fr.orelse)
+ self.visit_sequence(fr.orelse)
self.use_next_block(end)
def visit_While(self, wh):
self.update_position(wh.lineno, True)
test_constant = wh.test.as_constant_truth(self.space)
if test_constant == optimize.CONST_FALSE:
- if wh.orelse:
- self.visit_sequence(wh.orelse)
+ self.visit_sequence(wh.orelse)
else:
end = self.new_block()
anchor = None
@@ -545,8 +530,7 @@
self.use_next_block(anchor)
self.emit_op(ops.POP_BLOCK)
self.pop_frame_block(F_BLOCK_LOOP, loop)
- if wh.orelse:
- self.visit_sequence(wh.orelse)
+ self.visit_sequence(wh.orelse)
self.use_next_block(end)
def visit_TryExcept(self, te):
@@ -582,8 +566,7 @@
self.use_next_block(next_except)
self.emit_op(ops.END_FINALLY)
self.use_next_block(otherwise)
- if te.orelse:
- self.visit_sequence(te.orelse)
+ self.visit_sequence(te.orelse)
self.use_next_block(end)
def visit_TryFinally(self, tf):
@@ -894,27 +877,19 @@
def visit_Tuple(self, tup):
self.update_position(tup.lineno)
- if tup.elts:
- elt_count = len(tup.elts)
- else:
- elt_count = 0
+ elt_count = len(tup.elts) if tup.elts is not None else 0
if tup.ctx == ast.Store:
self.emit_op_arg(ops.UNPACK_SEQUENCE, elt_count)
- if elt_count:
- self.visit_sequence(tup.elts)
+ self.visit_sequence(tup.elts)
if tup.ctx == ast.Load:
self.emit_op_arg(ops.BUILD_TUPLE, elt_count)
def visit_List(self, l):
self.update_position(l.lineno)
- if l.elts:
- elt_count = len(l.elts)
- else:
- elt_count = 0
+ elt_count = len(l.elts) if l.elts is not None else 0
if l.ctx == ast.Store:
self.emit_op_arg(ops.UNPACK_SEQUENCE, elt_count)
- if elt_count:
- self.visit_sequence(l.elts)
+ self.visit_sequence(l.elts)
if l.ctx == ast.Load:
self.emit_op_arg(ops.BUILD_LIST, elt_count)
@@ -942,15 +917,12 @@
def visit_Call(self, call):
self.update_position(call.lineno)
- if self._optimize_builtin_call(call) or \
- self._optimize_method_call(call):
+ if self._optimize_method_call(call):
return
call.func.walkabout(self)
- arg = 0
+ arg = len(call.args) if call.args is not None else 0
call_type = 0
- if call.args:
- arg = len(call.args)
- self.visit_sequence(call.args)
+ self.visit_sequence(call.args)
if call.keywords:
self.visit_sequence(call.keywords)
arg |= len(call.keywords) << 8
@@ -977,28 +949,6 @@
def _call_has_simple_args(self, call):
return self._call_has_no_star_args(call) and not call.keywords
- def _optimize_builtin_call(self, call):
- if not self.space.config.objspace.opcodes.CALL_LIKELY_BUILTIN or \
- not self._call_has_simple_args(call) or \
- not isinstance(call.func, ast.Name):
- return False
- func_name = call.func
- assert isinstance(func_name, ast.Name)
- name_scope = self.scope.lookup(func_name.id)
- if name_scope == symtable.SCOPE_GLOBAL_IMPLICIT or \
- name_scope == symtable.SCOPE_UNKNOWN:
- builtin_index = BUILTIN_TO_INDEX.get(func_name.id, -1)
- if builtin_index != -1:
- if call.args:
- args_count = len(call.args)
- self.visit_sequence(call.args)
- else:
- args_count = 0
- arg = builtin_index << 8 | args_count
- self.emit_op_arg(ops.CALL_LIKELY_BUILTIN, arg)
- return True
- return False
-
def _optimize_method_call(self, call):
if not self.space.config.objspace.opcodes.CALL_METHOD or \
not self._call_has_no_star_args(call) or \
@@ -1008,16 +958,10 @@
assert isinstance(attr_lookup, ast.Attribute)
attr_lookup.value.walkabout(self)
self.emit_op_name(ops.LOOKUP_METHOD, self.names, attr_lookup.attr)
- if call.args:
- self.visit_sequence(call.args)
- arg_count = len(call.args)
- else:
- arg_count = 0
- if call.keywords:
- self.visit_sequence(call.keywords)
- kwarg_count = len(call.keywords)
- else:
- kwarg_count = 0
+ self.visit_sequence(call.args)
+ arg_count = len(call.args) if call.args is not None else 0
+ self.visit_sequence(call.keywords)
+ kwarg_count = len(call.keywords) if call.keywords is not None else 0
self.emit_op_arg(ops.CALL_METHOD, (kwarg_count << 8) | arg_count)
return True
@@ -1275,7 +1219,10 @@
def _compile(self, func):
assert isinstance(func, ast.FunctionDef)
# If there's a docstring, store it as the first constant.
- doc_expr = self.possible_docstring(func.body[0])
+ if func.body:
+ doc_expr = self.possible_docstring(func.body[0])
+ else:
+ doc_expr = None
if doc_expr is not None:
self.add_const(doc_expr.s)
start = 1
@@ -1287,8 +1234,9 @@
if args.args:
self._handle_nested_args(args.args)
self.argcount = len(args.args)
- for i in range(start, len(func.body)):
- func.body[i].walkabout(self)
+ if func.body:
+ for i in range(start, len(func.body)):
+ func.body[i].walkabout(self)
class LambdaCodeGenerator(AbstractFunctionCodeGenerator):
diff --git a/pypy/interpreter/astcompiler/misc.py b/pypy/interpreter/astcompiler/misc.py
--- a/pypy/interpreter/astcompiler/misc.py
+++ b/pypy/interpreter/astcompiler/misc.py
@@ -27,9 +27,10 @@
_emit_syntax_warning(space, w_msg, w_filename, w_lineno, w_offset)
-def parse_future(tree):
+def parse_future(tree, feature_flags):
future_lineno = 0
future_column = 0
+ flags = 0
have_docstring = False
body = None
if isinstance(tree, ast.Module):
@@ -37,7 +38,7 @@
elif isinstance(tree, ast.Interactive):
body = tree.body
if body is None:
- return 0, 0
+ return 0, 0, 0
for stmt in body:
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Str):
if have_docstring:
@@ -48,11 +49,16 @@
if stmt.module == "__future__":
future_lineno = stmt.lineno
future_column = stmt.col_offset
+ for alias in stmt.names:
+ assert isinstance(alias, ast.alias)
+ # If this is an invalid flag, it will be caught later in
+ # codegen.py.
+ flags |= feature_flags.get(alias.name, 0)
else:
break
else:
break
- return future_lineno, future_column
+ return flags, future_lineno, future_column
class ForbiddenNameAssignment(Exception):
diff --git a/pypy/interpreter/astcompiler/symtable.py b/pypy/interpreter/astcompiler/symtable.py
--- a/pypy/interpreter/astcompiler/symtable.py
+++ b/pypy/interpreter/astcompiler/symtable.py
@@ -356,10 +356,8 @@
# Function defaults and decorators happen in the outer scope.
args = func.args
assert isinstance(args, ast.arguments)
- if args.defaults:
- self.visit_sequence(args.defaults)
- if func.decorator_list:
- self.visit_sequence(func.decorator_list)
+ self.visit_sequence(args.defaults)
+ self.visit_sequence(func.decorator_list)
new_scope = FunctionScope(func.name, func.lineno, func.col_offset)
self.push_scope(new_scope, func)
func.args.walkabout(self)
@@ -372,10 +370,8 @@
def visit_ClassDef(self, clsdef):
self.note_symbol(clsdef.name, SYM_ASSIGNED)
- if clsdef.bases:
- self.visit_sequence(clsdef.bases)
- if clsdef.decorator_list:
- self.visit_sequence(clsdef.decorator_list)
+ self.visit_sequence(clsdef.bases)
+ self.visit_sequence(clsdef.decorator_list)
self.push_scope(ClassScope(clsdef), clsdef)
self.visit_sequence(clsdef.body)
self.pop_scope()
@@ -431,8 +427,7 @@
def visit_Lambda(self, lamb):
args = lamb.args
assert isinstance(args, ast.arguments)
- if args.defaults:
- self.visit_sequence(args.defaults)
+ self.visit_sequence(args.defaults)
new_scope = FunctionScope("lambda", lamb.lineno, lamb.col_offset)
self.push_scope(new_scope, lamb)
lamb.args.walkabout(self)
@@ -447,8 +442,7 @@
self.push_scope(new_scope, node)
self.implicit_arg(0)
outer.target.walkabout(self)
- if outer.ifs:
- self.visit_sequence(outer.ifs)
+ self.visit_sequence(outer.ifs)
self.visit_sequence(comps[1:])
for item in list(consider):
item.walkabout(self)
diff --git a/pypy/interpreter/astcompiler/tools/asdl_py.py b/pypy/interpreter/astcompiler/tools/asdl_py.py
--- a/pypy/interpreter/astcompiler/tools/asdl_py.py
+++ b/pypy/interpreter/astcompiler/tools/asdl_py.py
@@ -221,8 +221,9 @@
self.emit("class ASTVisitor(object):")
self.emit("")
self.emit("def visit_sequence(self, seq):", 1)
- self.emit("for node in seq:", 2)
- self.emit("node.walkabout(self)", 3)
+ self.emit("if seq is not None:", 2)
+ self.emit("for node in seq:", 3)
+ self.emit("node.walkabout(self)", 4)
self.emit("")
self.emit("def default_visitor(self, node):", 1)
self.emit("raise NodeVisitorNotImplemented", 2)
@@ -280,15 +281,13 @@
def visitField(self, field):
if field.type.value not in asdl.builtin_types and \
field.type.value not in self.data.simple_types:
- if field.seq or field.opt:
+ level = 2
+ template = "node.%s.walkabout(self)"
+ if field.seq:
+ template = "self.visit_sequence(node.%s)"
+ elif field.opt:
self.emit("if node.%s:" % (field.name,), 2)
level = 3
- else:
- level = 2
- if field.seq:
- template = "self.visit_sequence(node.%s)"
- else:
- template = "node.%s.walkabout(self)"
self.emit(template % (field.name,), level)
return True
return False
@@ -446,6 +445,7 @@
if field.seq:
self.emit("w_self.w_%s = w_new_value" % (field.name,), 1)
else:
+ save_original_object = False
self.emit("try:", 1)
if field.type.value not in asdl.builtin_types:
# These are always other AST nodes.
@@ -454,9 +454,7 @@
(field.type,), 2)
self.emit("w_self.%s = obj.to_simple_int(space)" %
(field.name,), 2)
- self.emit("# need to save the original object too", 2)
- self.emit("w_self.setdictvalue(space, '%s', w_new_value)"
- % (field.name,), 2)
+ save_original_object = True
else:
config = (field.name, field.type, repr(field.opt))
self.emit("w_self.%s = space.interp_w(%s, w_new_value, %s)" %
@@ -480,6 +478,12 @@
self.emit(" w_self.setdictvalue(space, '%s', w_new_value)"
% (field.name,), 1)
self.emit(" return", 1)
+ if save_original_object:
+ self.emit("# need to save the original object too", 1)
+ self.emit("w_self.setdictvalue(space, '%s', w_new_value)"
+ % (field.name,), 1)
+ else:
+ self.emit("w_self.deldictvalue(space, '%s')" %(field.name,), 1)
self.emit("w_self.initialization_state |= %s" % (flag,), 1)
self.emit("")
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -44,11 +44,11 @@
return True
return False
- def deldictvalue(self, space, w_name):
+ def deldictvalue(self, space, attr):
w_dict = self.getdict(space)
if w_dict is not None:
try:
- space.delitem(w_dict, w_name)
+ space.delitem(w_dict, space.wrap(attr))
return True
except OperationError, ex:
if not ex.match(space, space.w_KeyError):
@@ -130,6 +130,9 @@
raise operationerrfmt(space.w_TypeError,
"cannot create weak reference to '%s' object", typename)
+ def delweakref(self):
+ pass
+
def clear_all_weakrefs(self):
"""Call this at the beginning of interp-level __del__() methods
in subclasses. It ensures that weakrefs (if any) are cleared
@@ -143,29 +146,28 @@
# app-level, e.g. a user-defined __del__(), and this code
# tries to use weakrefs again, it won't reuse the broken
# (already-cleared) weakrefs from this lifeline.
- self.setweakref(lifeline.space, None)
+ self.delweakref()
lifeline.clear_all_weakrefs()
- __already_enqueued_for_destruction = False
+ __already_enqueued_for_destruction = ()
- def _enqueue_for_destruction(self, space, call_user_del=True):
+ def enqueue_for_destruction(self, space, callback, descrname):
"""Put the object in the destructor queue of the space.
- At a later, safe point in time, UserDelAction will use
- space.userdel() to call the object's app-level __del__ method.
+ At a later, safe point in time, UserDelAction will call
+ callback(self). If that raises OperationError, prints it
+ to stderr with the descrname string.
+
+ Note that 'callback' will usually need to start with:
+ assert isinstance(self, W_SpecificClass)
"""
# this function always resurect the object, so when
# running on top of CPython we must manually ensure that
# we enqueue it only once
if not we_are_translated():
- if self.__already_enqueued_for_destruction:
+ if callback in self.__already_enqueued_for_destruction:
return
- self.__already_enqueued_for_destruction = True
- self.clear_all_weakrefs()
- if call_user_del:
- space.user_del_action.register_dying_object(self)
-
- def _call_builtin_destructor(self):
- pass # method overridden in typedef.py
+ self.__already_enqueued_for_destruction += (callback,)
+ space.user_del_action.register_callback(self, callback, descrname)
# hooks that the mapdict implementations needs:
def _get_mapdict_map(self):
@@ -237,7 +239,7 @@
class ObjSpace(object):
"""Base class for the interpreter-level implementations of object spaces.
- http://codespeak.net/pypy/dist/pypy/doc/objspace.html"""
+ http://pypy.readthedocs.org/en/latest/objspace.html"""
full_exceptions = True # full support for exceptions (normalization & more)
@@ -311,9 +313,6 @@
mod = self.interpclass_w(w_mod)
if isinstance(mod, Module) and mod.startup_called:
mod.shutdown(self)
- if self.config.objspace.std.withdictmeasurement:
- from pypy.objspace.std.dictmultiobject import report
- report()
if self.config.objspace.logbytecodes:
self.reportbytecodecounts()
if self.config.objspace.std.logspaceoptypes:
@@ -928,6 +927,9 @@
return self.w_True
return self.w_False
+ def issequence_w(self, w_obj):
+ return (self.findattr(w_obj, self.wrap("__getitem__")) is not None)
+
def isinstance_w(self, w_obj, w_type):
return self.is_true(self.isinstance(w_obj, w_type))
@@ -1282,6 +1284,17 @@
self.wrap("expected a 32-bit integer"))
return value
+ def truncatedint(self, w_obj):
+ # Like space.gateway_int_w(), but return the integer truncated
+ # instead of raising OverflowError. For obscure cases only.
+ try:
+ return self.int_w(w_obj)
+ except OperationError, e:
+ if not e.match(self, self.w_OverflowError):
+ raise
+ from pypy.rlib.rarithmetic import intmask
+ return intmask(self.bigint_w(w_obj).uintmask())
+
def c_filedescriptor_w(self, w_fd):
# This is only used sometimes in CPython, e.g. for os.fsync() but
# not os.close(). It's likely designed for 'select'. It's irregular
diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -189,7 +189,7 @@
if space.is_w(w_value, space.w_None):
# raise Type: we assume we have to instantiate Type
w_value = space.call_function(w_type)
- w_type = space.exception_getclass(w_value)
+ w_type = self._exception_getclass(space, w_value)
else:
w_valuetype = space.exception_getclass(w_value)
if space.exception_issubclass_w(w_valuetype, w_type):
@@ -204,18 +204,12 @@
else:
# raise Type, X: assume X is the constructor argument
w_value = space.call_function(w_type, w_value)
- w_type = space.exception_getclass(w_value)
+ w_type = self._exception_getclass(space, w_value)
else:
# the only case left here is (inst, None), from a 'raise inst'.
w_inst = w_type
- w_instclass = space.exception_getclass(w_inst)
- if not space.exception_is_valid_class_w(w_instclass):
- instclassname = w_instclass.getname(space)
- msg = ("exceptions must be old-style classes or derived "
- "from BaseException, not %s")
- raise operationerrfmt(space.w_TypeError, msg, instclassname)
-
+ w_instclass = self._exception_getclass(space, w_inst)
if not space.is_w(w_value, space.w_None):
raise OperationError(space.w_TypeError,
space.wrap("instance exception may not "
@@ -226,6 +220,15 @@
self.w_type = w_type
self._w_value = w_value
+ def _exception_getclass(self, space, w_inst):
+ w_type = space.exception_getclass(w_inst)
+ if not space.exception_is_valid_class_w(w_type):
+ typename = w_type.getname(space)
+ msg = ("exceptions must be old-style classes or derived "
+ "from BaseException, not %s")
+ raise operationerrfmt(space.w_TypeError, msg, typename)
+ return w_type
+
def write_unraisable(self, space, where, w_object=None):
if w_object is None:
objrepr = ''
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -484,44 +484,31 @@
def __init__(self, space):
AsyncAction.__init__(self, space)
- self.dying_objects_w = []
- self.weakrefs_w = []
+ self.dying_objects = []
self.finalizers_lock_count = 0
- def register_dying_object(self, w_obj):
- self.dying_objects_w.append(w_obj)
- self.fire()
-
- def register_weakref_callback(self, w_ref):
- self.weakrefs_w.append(w_ref)
+ def register_callback(self, w_obj, callback, descrname):
+ self.dying_objects.append((w_obj, callback, descrname))
self.fire()
def perform(self, executioncontext, frame):
if self.finalizers_lock_count > 0:
return
- # Each call to perform() first grabs the self.dying_objects_w
+ # Each call to perform() first grabs the self.dying_objects
# and replaces it with an empty list. We do this to try to
# avoid too deep recursions of the kind of __del__ being called
# while in the middle of another __del__ call.
- pending_w = self.dying_objects_w
- self.dying_objects_w = []
+ pending = self.dying_objects
+ self.dying_objects = []
space = self.space
- for i in range(len(pending_w)):
- w_obj = pending_w[i]
- pending_w[i] = None
+ for i in range(len(pending)):
+ w_obj, callback, descrname = pending[i]
+ pending[i] = (None, None, None)
try:
- space.userdel(w_obj)
+ callback(w_obj)
except OperationError, e:
- e.write_unraisable(space, 'method __del__ of ', w_obj)
+ e.write_unraisable(space, descrname, w_obj)
e.clear(space) # break up reference cycles
- # finally, this calls the interp-level destructor for the
- # cases where there is both an app-level and a built-in __del__.
- w_obj._call_builtin_destructor()
- pending_w = self.weakrefs_w
- self.weakrefs_w = []
- for i in range(len(pending_w)):
- w_ref = pending_w[i]
- w_ref.activate_callback()
class FrameTraceAction(AsyncAction):
"""An action that calls the local trace functions (w_f_trace)."""
diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py
--- a/pypy/interpreter/function.py
+++ b/pypy/interpreter/function.py
@@ -16,7 +16,7 @@
funccallunrolling = unrolling_iterable(range(4))
- at jit.purefunction_promote()
+ at jit.elidable_promote()
def _get_immutable_code(func):
assert not func.can_change_code
return func.code
@@ -31,7 +31,8 @@
_immutable_fields_ = ['code?',
'w_func_globals?',
'closure?',
- 'defs_w?[*]']
+ 'defs_w?[*]',
+ 'name?']
def __init__(self, space, code, w_globals=None, defs_w=[], closure=None,
forcename=None):
@@ -63,7 +64,7 @@
if jit.we_are_jitted():
if not self.can_change_code:
return _get_immutable_code(self)
- return jit.hint(self.code, promote=True)
+ return jit.promote(self.code)
return self.code
def funccall(self, *args_w): # speed hack
@@ -465,19 +466,23 @@
space.abstract_isinstance_w(w_firstarg, self.w_class)):
pass # ok
else:
- myname = self.getname(space,"")
- clsdescr = self.w_class.getname(space,"")
+ myname = self.getname(space, "")
+ clsdescr = self.w_class.getname(space, "")
if clsdescr:
- clsdescr+=" "
+ clsdescr += " instance"
+ else:
+ clsdescr = "instance"
if w_firstarg is None:
instdescr = "nothing"
else:
- instname = space.abstract_getclass(w_firstarg).getname(space,"")
+ instname = space.abstract_getclass(w_firstarg).getname(space,
+ "")
if instname:
- instname += " "
- instdescr = "%sinstance" %instname
- msg = ("unbound method %s() must be called with %s"
- "instance as first argument (got %s instead)")
+ instdescr = instname + " instance"
+ else:
+ instdescr = "instance"
+ msg = ("unbound method %s() must be called with %s "
+ "as first argument (got %s instead)")
raise operationerrfmt(space.w_TypeError, msg,
myname, clsdescr, instdescr)
return space.call_args(self.w_function, args)
diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py
--- a/pypy/interpreter/gateway.py
+++ b/pypy/interpreter/gateway.py
@@ -140,6 +140,9 @@
def visit_c_nonnegint(self, el, app_sig):
self.checked_space_method(el, app_sig)
+ def visit_truncatedint(self, el, app_sig):
+ self.checked_space_method(el, app_sig)
+
def visit__Wrappable(self, el, app_sig):
name = el.__name__
argname = self.orig_arg()
@@ -257,6 +260,9 @@
def visit_c_nonnegint(self, typ):
self.run_args.append("space.c_nonnegint_w(%s)" % (self.scopenext(),))
+ def visit_truncatedint(self, typ):
+ self.run_args.append("space.truncatedint(%s)" % (self.scopenext(),))
+
def _make_unwrap_activation_class(self, unwrap_spec, cache={}):
try:
key = tuple(unwrap_spec)
@@ -387,6 +393,9 @@
def visit_c_nonnegint(self, typ):
self.unwrap.append("space.c_nonnegint_w(%s)" % (self.nextarg(),))
+ def visit_truncatedint(self, typ):
+ self.unwrap.append("space.truncatedint(%s)" % (self.nextarg(),))
+
def make_fastfunc(unwrap_spec, func):
unwrap_info = UnwrapSpec_FastFunc_Unwrap()
unwrap_info.apply_over(unwrap_spec)
@@ -396,11 +405,14 @@
fastfunc = func
else:
# try to avoid excessive bloat
- if func.__module__ == 'pypy.interpreter.astcompiler.ast':
+ mod = func.__module__
+ if mod is None:
+ mod = ""
+ if mod == 'pypy.interpreter.astcompiler.ast':
raise FastFuncNotSupported
- if (not func.__module__.startswith('pypy.module.__builtin__') and
- not func.__module__.startswith('pypy.module.sys') and
- not func.__module__.startswith('pypy.module.math')):
+ if (not mod.startswith('pypy.module.__builtin__') and
+ not mod.startswith('pypy.module.sys') and
+ not mod.startswith('pypy.module.math')):
if not func.__name__.startswith('descr'):
raise FastFuncNotSupported
d = {}
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -62,7 +62,7 @@
raise operr
# XXX it's not clear that last_instr should be promoted at all
# but as long as it is necessary for call_assembler, let's do it early
- last_instr = jit.hint(frame.last_instr, promote=True)
+ last_instr = jit.promote(frame.last_instr)
if last_instr == -1:
if w_arg and not space.is_w(w_arg, space.w_None):
msg = "can't send non-None value to a just-started generator"
@@ -114,6 +114,7 @@
def descr_close(self):
"""x.close(arg) -> raise GeneratorExit inside generator."""
+ assert isinstance(self, GeneratorIterator)
space = self.space
try:
w_retval = self.throw(space.w_GeneratorExit, space.w_None,
@@ -141,22 +142,16 @@
code_name = self.pycode.co_name
return space.wrap(code_name)
- def descr__del__(self):
- """
- applevel __del__, which is called at a safe point after the
- interp-level __del__ enqueued the object for destruction
- """
- self.descr_close()
-
def __del__(self):
# Only bother enqueuing self to raise an exception if the frame is
# still not finished and finally or except blocks are present.
- must_call_close = False
+ self.clear_all_weakrefs()
if self.frame is not None:
block = self.frame.lastblock
while block is not None:
if not isinstance(block, LoopBlock):
- must_call_close = True
+ self.enqueue_for_destruction(self.space,
+ GeneratorIterator.descr_close,
+ "interrupting generator of ")
break
block = block.previous
- self._enqueue_for_destruction(self.space, must_call_close)
diff --git a/pypy/interpreter/module.py b/pypy/interpreter/module.py
--- a/pypy/interpreter/module.py
+++ b/pypy/interpreter/module.py
@@ -9,6 +9,8 @@
class Module(Wrappable):
"""A module."""
+ _immutable_fields_ = ["w_dict?"]
+
_frozen = False
def __init__(self, space, w_name, w_dict=None, add_package=True):
diff --git a/pypy/interpreter/pycompiler.py b/pypy/interpreter/pycompiler.py
--- a/pypy/interpreter/pycompiler.py
+++ b/pypy/interpreter/pycompiler.py
@@ -119,7 +119,10 @@
raise OperationError(self.space.w_TypeError, self.space.wrap(
"invalid node type"))
- future_pos = misc.parse_future(node)
+ fut = misc.parse_future(node, self.future_flags.compiler_features)
+ f_flags, f_lineno, f_col = fut
+ future_pos = f_lineno, f_col
+ flags |= f_flags
info = pyparse.CompileInfo(filename, mode, flags, future_pos)
return self._compile_ast(node, info)
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -905,16 +905,15 @@
def SETUP_WITH(self, offsettoend, next_instr):
w_manager = self.peekvalue()
+ w_enter = self.space.lookup(w_manager, "__enter__")
w_descr = self.space.lookup(w_manager, "__exit__")
- if w_descr is None:
- raise OperationError(self.space.w_AttributeError,
- self.space.wrap("__exit__"))
+ if w_enter is None or w_descr is None:
+ typename = self.space.type(w_manager).getname(self.space)
+ raise operationerrfmt(self.space.w_AttributeError,
+ "'%s' object is not a context manager"
+ " (no __enter__/__exit__ method)", typename)
w_exit = self.space.get(w_descr, w_manager)
self.settopvalue(w_exit)
- w_enter = self.space.lookup(w_manager, "__enter__")
- if w_enter is None:
- raise OperationError(self.space.w_AttributeError,
- self.space.wrap("__enter__"))
w_result = self.space.get_and_call_function(w_enter, w_manager)
block = WithBlock(self, next_instr + offsettoend)
self.append_block(block)
@@ -1060,18 +1059,6 @@
def SET_LINENO(self, lineno, next_instr):
pass
- def CALL_LIKELY_BUILTIN(self, oparg, next_instr):
- # overridden by faster version in the standard object space.
- from pypy.module.__builtin__ import OPTIMIZED_BUILTINS
- varname = OPTIMIZED_BUILTINS[oparg >> 8]
- w_function = self._load_global(varname)
- nargs = oparg&0xFF
- try:
- w_result = self.space.call_valuestack(w_function, nargs, self)
- finally:
- self.dropvalues(nargs)
- self.pushvalue(w_result)
-
# overridden by faster version in the standard object space.
LOOKUP_METHOD = LOAD_ATTR
CALL_METHOD = CALL_FUNCTION
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
@@ -106,7 +106,7 @@
if isinstance(seen[0], Method):
found = 'method %s of %s' % (
seen[0].w_function.name,
- seen[0].w_class.getname(space, '?'))
+ seen[0].w_class.getname(space))
else:
assert isinstance(seen[0], Function)
found = 'builtin %s' % seen[0].name
@@ -232,31 +232,6 @@
assert [i[0] for i in events] == ['c_call', 'c_return', 'return', 'c_call']
assert events[0][1] == events[1][1]
- def test_tracing_range_builtinshortcut(self):
- opts = {"objspace.opcodes.CALL_LIKELY_BUILTIN": True}
- space = gettestobjspace(**opts)
- source = """def f(profile):
- import sys
- sys.setprofile(profile)
- range(10)
- sys.setprofile(None)
- """
- w_events = space.appexec([space.wrap(source)], """(source):
- import sys
- l = []
- def profile(frame, event, arg):
- l.append((event, arg))
- d = {}
- exec source in d
- f = d['f']
- f(profile)
- import dis
- print dis.dis(f)
- return l
- """)
- events = space.unwrap(w_events)
- assert [i[0] for i in events] == ['c_call', 'c_return', 'c_call']
-
def test_profile_and_exception(self):
space = self.space
w_res = space.appexec([], """():
@@ -280,9 +255,6 @@
""")
-class TestExecutionContextWithCallLikelyBuiltin(TestExecutionContext):
- keywords = {'objspace.opcodes.CALL_LIKELY_BUILTIN': True}
-
class TestExecutionContextWithCallMethod(TestExecutionContext):
keywords = {'objspace.opcodes.CALL_METHOD': True}
diff --git a/pypy/interpreter/test/test_raise.py b/pypy/interpreter/test/test_raise.py
--- a/pypy/interpreter/test/test_raise.py
+++ b/pypy/interpreter/test/test_raise.py
@@ -274,3 +274,9 @@
pass
except A:
pass
+
+ def test_new_returns_bad_instance(self):
+ class MyException(Exception):
+ def __new__(cls, *args):
+ return object()
+ raises(TypeError, "raise MyException")
diff --git a/pypy/interpreter/test/test_typedef.py b/pypy/interpreter/test/test_typedef.py
--- a/pypy/interpreter/test/test_typedef.py
+++ b/pypy/interpreter/test/test_typedef.py
@@ -1,3 +1,4 @@
+import gc
from pypy.interpreter import typedef
from pypy.tool.udir import udir
from pypy.interpreter.baseobjspace import Wrappable
@@ -16,7 +17,7 @@
def g():
f()
-
+
try:
g()
except:
@@ -180,6 +181,85 @@
assert err.value.message == "'some_type' objects are unhashable"
""")
+ def test_destructor(self):
+ space = self.space
+ class W_Level1(Wrappable):
+ def __init__(self, space1):
+ assert space1 is space
+ def __del__(self):
+ space.call_method(w_seen, 'append', space.wrap(1))
+ class W_Level2(Wrappable):
+ def __init__(self, space1):
+ assert space1 is space
+ def __del__(self):
+ self.enqueue_for_destruction(space, W_Level2.destructormeth,
+ 'FOO ')
+ def destructormeth(self):
+ space.call_method(w_seen, 'append', space.wrap(2))
+ W_Level1.typedef = typedef.TypeDef(
+ 'level1',
+ __new__ = typedef.generic_new_descr(W_Level1))
+ W_Level2.typedef = typedef.TypeDef(
+ 'level2',
+ __new__ = typedef.generic_new_descr(W_Level2))
+ #
+ w_seen = space.newlist([])
+ W_Level1(space)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [1]
+ #
+ w_seen = space.newlist([])
+ W_Level2(space)
+ gc.collect(); gc.collect()
+ assert space.str_w(space.repr(w_seen)) == "[]" # not called yet
+ ec = space.getexecutioncontext()
+ self.space.user_del_action.perform(ec, None)
+ assert space.unwrap(w_seen) == [2]
+ #
+ w_seen = space.newlist([])
+ self.space.appexec([self.space.gettypeobject(W_Level1.typedef)],
+ """(level1):
+ class A3(level1):
+ pass
+ A3()
+ """)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [1]
+ #
+ w_seen = space.newlist([])
+ self.space.appexec([self.space.gettypeobject(W_Level1.typedef),
+ w_seen],
+ """(level1, seen):
+ class A4(level1):
+ def __del__(self):
+ seen.append(4)
+ A4()
+ """)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [4, 1]
+ #
+ w_seen = space.newlist([])
+ self.space.appexec([self.space.gettypeobject(W_Level2.typedef)],
+ """(level2):
+ class A5(level2):
+ pass
+ A5()
+ """)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [2]
+ #
+ w_seen = space.newlist([])
+ self.space.appexec([self.space.gettypeobject(W_Level2.typedef),
+ w_seen],
+ """(level2, seen):
+ class A6(level2):
+ def __del__(self):
+ seen.append(6)
+ A6()
+ """)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [6, 2]
+
class AppTestTypeDef:
@@ -210,19 +290,20 @@
def m(self):
"aaa"
m.x = 3
+ class B(A):
+ pass
- bm = A().m
+ bm = B().m
assert bm.__func__ is bm.im_func
assert bm.__self__ is bm.im_self
- assert bm.im_class is A
- if '__pypy__' in sys.builtin_module_names:
- assert bm.__objclass__ is A
+ assert bm.im_class is B
assert bm.__doc__ == "aaa"
assert bm.x == 3
raises(AttributeError, setattr, bm, 'x', 15)
l = []
assert l.append.__self__ is l
- if '__pypy__' in sys.builtin_module_names:
- assert l.append.__objclass__ is list
assert l.__add__.__self__ is l
- assert l.__add__.__objclass__ is list
+ # note: 'l.__add__.__objclass__' is not defined in pypy
+ # because it's a regular method, and .__objclass__
+ # differs from .im_class in case the method is
+ # defined in some parent class of l's actual class
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -9,7 +9,7 @@
from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.tool.sourcetools import compile2, func_with_new_name
from pypy.rlib.objectmodel import instantiate, compute_identity_hash, specialize
-from pypy.rlib.jit import hint
+from pypy.rlib.jit import promote
class TypeDef:
def __init__(self, __name, __base=None, **rawdict):
@@ -23,7 +23,7 @@
self.hasdict |= __base.hasdict
self.weakrefable |= __base.weakrefable
self.rawdict = {}
- self.acceptable_as_base_class = True
+ self.acceptable_as_base_class = '__new__' in rawdict
self.applevel_subclasses_base = None
# xxx used by faking
self.fakedcpytype = None
@@ -206,7 +206,7 @@
user_overridden_class = True
def getclass(self, space):
- return hint(self.w__class__, promote=True)
+ return promote(self.w__class__)
def setclass(self, space, w_subtype):
# only used by descr_set___class__
@@ -228,21 +228,26 @@
return self._lifeline_
def setweakref(self, space, weakreflifeline):
self._lifeline_ = weakreflifeline
+ def delweakref(self):
+ self._lifeline_ = None
add(Proto)
if "del" in features:
+ parent_destructor = getattr(supercls, '__del__', None)
+ def call_parent_del(self):
+ assert isinstance(self, subcls)
+ parent_destructor(self)
+ def call_applevel_del(self):
+ assert isinstance(self, subcls)
+ self.space.userdel(self)
class Proto(object):
def __del__(self):
- self._enqueue_for_destruction(self.space)
- # if the base class needs its own interp-level __del__,
- # we override the _call_builtin_destructor() method to invoke it
- # after the app-level destructor.
- parent_destructor = getattr(supercls, '__del__', None)
- if parent_destructor is not None:
- def _call_builtin_destructor(self):
- parent_destructor(self)
- Proto._call_builtin_destructor = _call_builtin_destructor
-
+ self.clear_all_weakrefs()
+ self.enqueue_for_destruction(self.space, call_applevel_del,
+ 'method __del__ of ')
+ if parent_destructor is not None:
+ self.enqueue_for_destruction(self.space, call_parent_del,
+ 'internal destructor of ')
add(Proto)
if "slots" in features:
@@ -630,9 +635,12 @@
return self._lifeline_
def setweakref(self, space, weakreflifeline):
self._lifeline_ = weakreflifeline
+ def delweakref(self):
+ self._lifeline_ = None
cls._lifeline_ = None
cls.getweakref = getweakref
cls.setweakref = setweakref
+ cls.delweakref = delweakref
return weakref_descr
@@ -771,7 +779,6 @@
im_self = interp_attrproperty_w('w_instance', cls=Method),
__self__ = interp_attrproperty_w('w_instance', cls=Method),
im_class = interp_attrproperty_w('w_class', cls=Method),
- __objclass__ = interp_attrproperty_w('w_class', cls=Method),
__getattribute__ = interp2app(Method.descr_method_getattribute),
__eq__ = interp2app(Method.descr_method_eq),
__ne__ = descr_generic_ne,
@@ -859,8 +866,6 @@
descrmismatch='close'),
__iter__ = interp2app(GeneratorIterator.descr__iter__,
descrmismatch='__iter__'),
- __del__ = interp2app(GeneratorIterator.descr__del__,
- descrmismatch='__del__'),
gi_running = interp_attrproperty('running', cls=GeneratorIterator),
gi_frame = GetSetProperty(GeneratorIterator.descr_gi_frame),
gi_code = GetSetProperty(GeneratorIterator.descr_gi_code),
diff --git a/pypy/jit/backend/llsupport/gc.py b/pypy/jit/backend/llsupport/gc.py
--- a/pypy/jit/backend/llsupport/gc.py
+++ b/pypy/jit/backend/llsupport/gc.py
@@ -453,21 +453,33 @@
class WriteBarrierDescr(AbstractDescr):
def __init__(self, gc_ll_descr):
+ GCClass = gc_ll_descr.GCClass
self.llop1 = gc_ll_descr.llop1
self.WB_FUNCPTR = gc_ll_descr.WB_FUNCPTR
self.WB_ARRAY_FUNCPTR = gc_ll_descr.WB_ARRAY_FUNCPTR
- self.fielddescr_tid = get_field_descr(gc_ll_descr,
- gc_ll_descr.GCClass.HDR, 'tid')
- self.jit_wb_if_flag = gc_ll_descr.GCClass.JIT_WB_IF_FLAG
- # if convenient for the backend, we also compute the info about
+ self.fielddescr_tid = get_field_descr(gc_ll_descr, GCClass.HDR, 'tid')
+ #
+ self.jit_wb_if_flag = GCClass.JIT_WB_IF_FLAG
+ self.jit_wb_if_flag_byteofs, self.jit_wb_if_flag_singlebyte = (
+ self.extract_flag_byte(self.jit_wb_if_flag))
+ #
+ if hasattr(GCClass, 'JIT_WB_CARDS_SET'):
+ self.jit_wb_cards_set = GCClass.JIT_WB_CARDS_SET
+ self.jit_wb_card_page_shift = GCClass.JIT_WB_CARD_PAGE_SHIFT
+ self.jit_wb_cards_set_byteofs, self.jit_wb_cards_set_singlebyte = (
+ self.extract_flag_byte(self.jit_wb_cards_set))
+ else:
+ self.jit_wb_cards_set = 0
+
+ def extract_flag_byte(self, flag_word):
+ # if convenient for the backend, we compute the info about
# the flag as (byte-offset, single-byte-flag).
import struct
- value = struct.pack("l", self.jit_wb_if_flag)
+ value = struct.pack("l", flag_word)
assert value.count('\x00') == len(value) - 1 # only one byte is != 0
i = 0
while value[i] == '\x00': i += 1
- self.jit_wb_if_flag_byteofs = i
- self.jit_wb_if_flag_singlebyte = struct.unpack('b', value[i])[0]
+ return (i, struct.unpack('b', value[i])[0])
def get_write_barrier_fn(self, cpu):
llop1 = self.llop1
diff --git a/pypy/jit/backend/llvm/llvm_rffi.py b/pypy/jit/backend/llvm/llvm_rffi.py
--- a/pypy/jit/backend/llvm/llvm_rffi.py
+++ b/pypy/jit/backend/llvm/llvm_rffi.py
@@ -3,7 +3,7 @@
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.translator.tool.cbuild import ExternalCompilationInfo, log
-if sys.platform != 'linux2':
+if not sys.platform.startswith('linux'):
py.test.skip("Linux only for now")
# ____________________________________________________________
diff --git a/pypy/jit/backend/test/runner_test.py b/pypy/jit/backend/test/runner_test.py
--- a/pypy/jit/backend/test/runner_test.py
+++ b/pypy/jit/backend/test/runner_test.py
@@ -1707,6 +1707,7 @@
jit_wb_if_flag = 4096
jit_wb_if_flag_byteofs = struct.pack("i", 4096).index('\x10')
jit_wb_if_flag_singlebyte = 0x10
+ jit_wb_cards_set = 0
def get_write_barrier_from_array_fn(self, cpu):
return funcbox.getint()
#
@@ -1728,6 +1729,72 @@
else:
assert record == []
+ def test_cond_call_gc_wb_array_card_marking_fast_path(self):
+ def func_void(a, b, c):
+ record.append((a, b, c))
+ record = []
+ #
+ S = lltype.Struct('S', ('tid', lltype.Signed))
+ S_WITH_CARDS = lltype.Struct('S_WITH_CARDS',
+ ('card0', lltype.Char),
+ ('card1', lltype.Char),
+ ('card2', lltype.Char),
+ ('card3', lltype.Char),
+ ('card4', lltype.Char),
+ ('card5', lltype.Char),
+ ('card6', lltype.Char),
+ ('card7', lltype.Char),
+ ('data', S))
+ FUNC = self.FuncType([lltype.Ptr(S), lltype.Signed, lltype.Ptr(S)],
+ lltype.Void)
+ func_ptr = llhelper(lltype.Ptr(FUNC), func_void)
+ funcbox = self.get_funcbox(self.cpu, func_ptr)
+ class WriteBarrierDescr(AbstractDescr):
+ jit_wb_if_flag = 4096
+ jit_wb_if_flag_byteofs = struct.pack("i", 4096).index('\x10')
+ jit_wb_if_flag_singlebyte = 0x10
+ jit_wb_cards_set = 8192
+ jit_wb_cards_set_byteofs = struct.pack("i", 8192).index('\x20')
+ jit_wb_cards_set_singlebyte = 0x20
+ jit_wb_card_page_shift = 7
+ def get_write_barrier_from_array_fn(self, cpu):
+ return funcbox.getint()
+ #
+ for BoxIndexCls in [BoxInt, ConstInt]:
+ for cond in [False, True]:
+ print
+ print '_'*79
+ print 'BoxIndexCls =', BoxIndexCls
+ print 'JIT_WB_CARDS_SET =', cond
+ print
+ value = random.randrange(-sys.maxint, sys.maxint)
+ value |= 4096
+ if cond:
+ value |= 8192
+ else:
+ value &= ~8192
+ s = lltype.malloc(S_WITH_CARDS, immortal=True, zero=True)
+ s.data.tid = value
+ sgcref = rffi.cast(llmemory.GCREF, s.data)
+ del record[:]
+ box_index = BoxIndexCls((9<<7) + 17)
+ self.execute_operation(rop.COND_CALL_GC_WB_ARRAY,
+ [BoxPtr(sgcref), box_index, BoxPtr(sgcref)],
+ 'void', descr=WriteBarrierDescr())
+ if cond:
+ assert record == []
+ assert s.card6 == '\x02'
+ else:
+ assert record == [(s.data, (9<<7) + 17, s.data)]
+ assert s.card6 == '\x00'
+ assert s.card0 == '\x00'
+ assert s.card1 == '\x00'
+ assert s.card2 == '\x00'
+ assert s.card3 == '\x00'
+ assert s.card4 == '\x00'
+ assert s.card5 == '\x00'
+ assert s.card7 == '\x00'
+
def test_force_operations_returning_void(self):
values = []
def maybe_force(token, flag):
diff --git a/pypy/jit/backend/x86/arch.py b/pypy/jit/backend/x86/arch.py
--- a/pypy/jit/backend/x86/arch.py
+++ b/pypy/jit/backend/x86/arch.py
@@ -27,3 +27,6 @@
# which are used in the malloc itself. They are:
# ecx, ebx, esi, edi [32 and 64 bits]
# r8, r9, r10, r12, r13, r14, r15 [64 bits only]
+#
+# Note that with asmgcc, the locations corresponding to callee-save registers
+# are never used.
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
@@ -181,6 +181,7 @@
# instructions in assembler, with a mark_gc_roots in between.
# With shadowstack, this is not needed, so we produce a single helper.
gcrootmap = self.cpu.gc_ll_descr.gcrootmap
+ shadow_stack = (gcrootmap is not None and gcrootmap.is_shadow_stack)
#
# ---------- first helper for the slow path of malloc ----------
mc = codebuf.MachineCodeBlockWrapper()
@@ -190,10 +191,19 @@
mc.SUB_rr(edx.value, eax.value) # compute the size we want
addr = self.cpu.gc_ll_descr.get_malloc_slowpath_addr()
#
- if gcrootmap is not None and gcrootmap.is_shadow_stack:
+ # The registers to save in the copy area: with shadowstack, most
+ # registers need to be saved. With asmgcc, the callee-saved registers
+ # don't need to.
+ save_in_copy_area = gpr_reg_mgr_cls.REGLOC_TO_COPY_AREA_OFS.items()
+ if not shadow_stack:
+ save_in_copy_area = [(reg, ofs) for (reg, ofs) in save_in_copy_area
+ if reg not in gpr_reg_mgr_cls.REGLOC_TO_GCROOTMAP_REG_INDEX]
+ #
+ for reg, ofs in save_in_copy_area:
+ mc.MOV_br(ofs, reg.value)
+ #
+ if shadow_stack:
# ---- shadowstack ----
- for reg, ofs in gpr_reg_mgr_cls.REGLOC_TO_COPY_AREA_OFS.items():
- mc.MOV_br(ofs, reg.value)
mc.SUB_ri(esp.value, 16 - WORD) # stack alignment of 16 bytes
if IS_X86_32:
mc.MOV_sr(0, edx.value) # push argument
@@ -201,15 +211,13 @@
mc.MOV_rr(edi.value, edx.value)
mc.CALL(imm(addr))
mc.ADD_ri(esp.value, 16 - WORD)
- for reg, ofs in gpr_reg_mgr_cls.REGLOC_TO_COPY_AREA_OFS.items():
- mc.MOV_rb(reg.value, ofs)
else:
# ---- asmgcc ----
if IS_X86_32:
mc.MOV_sr(WORD, edx.value) # save it as the new argument
elif IS_X86_64:
- # rdi can be clobbered: its content was forced to the stack
- # by _fastpath_malloc(), like all other save_around_call_regs.
+ # rdi can be clobbered: its content was saved in the
+ # copy area of the stack
mc.MOV_rr(edi.value, edx.value)
mc.JMP(imm(addr)) # tail call to the real malloc
rawstart = mc.materialize(self.cpu.asmmemmgr, [])
@@ -217,6 +225,10 @@
# ---------- second helper for the slow path of malloc ----------
mc = codebuf.MachineCodeBlockWrapper()
#
+ for reg, ofs in save_in_copy_area:
+ mc.MOV_rb(reg.value, ofs)
+ assert reg is not eax and reg is not edx
+ #
if self.cpu.supports_floats: # restore the XMM registers
for i in range(self.cpu.NUM_REGS):# from where they were saved
mc.MOVSD_xs(i, (WORD*2)+8*i)
@@ -416,10 +428,13 @@
fullsize = self.mc.get_relative_pos()
#
rawstart = self.materialize_loop(looptoken)
- debug_print("Loop #%d (%s) has address %x to %x" % (
+ debug_start("jit-backend-addr")
+ debug_print("Loop %d (%s) has address %x to %x (bootstrap %x)" % (
looptoken.number, loopname,
rawstart + self.looppos,
- rawstart + directbootstrappos))
+ rawstart + directbootstrappos,
+ rawstart))
+ debug_stop("jit-backend-addr")
self._patch_stackadjust(rawstart + stackadjustpos,
frame_depth + param_depth)
self.patch_pending_failure_recoveries(rawstart)
@@ -478,9 +493,10 @@
fullsize = self.mc.get_relative_pos()
#
rawstart = self.materialize_loop(original_loop_token)
-
- debug_print("Bridge out of guard %d has address %x to %x" %
+ debug_start("jit-backend-addr")
+ debug_print("bridge out of Guard %d has address %x to %x" %
(descr_number, rawstart, rawstart + codeendpos))
+ debug_stop("jit-backend-addr")
self._patch_stackadjust(rawstart + stackadjustpos,
frame_depth + param_depth)
self.patch_pending_failure_recoveries(rawstart)
@@ -2242,10 +2258,12 @@
if opnum == rop.COND_CALL_GC_WB:
N = 2
func = descr.get_write_barrier_fn(self.cpu)
+ card_marking = False
elif opnum == rop.COND_CALL_GC_WB_ARRAY:
N = 3
func = descr.get_write_barrier_from_array_fn(self.cpu)
assert func != 0
+ card_marking = descr.jit_wb_cards_set != 0
else:
raise AssertionError(opnum)
#
@@ -2254,6 +2272,18 @@
imm(descr.jit_wb_if_flag_singlebyte))
self.mc.J_il8(rx86.Conditions['Z'], 0) # patched later
jz_location = self.mc.get_relative_pos()
+
+ # for cond_call_gc_wb_array, also add another fast path:
+ # if GCFLAG_CARDS_SET, then we can just set one bit and be done
+ if card_marking:
+ self.mc.TEST8(addr_add_const(loc_base,
+ descr.jit_wb_cards_set_byteofs),
+ imm(descr.jit_wb_cards_set_singlebyte))
+ self.mc.J_il8(rx86.Conditions['NZ'], 0) # patched later
+ jnz_location = self.mc.get_relative_pos()
+ else:
+ jnz_location = 0
+
# the following is supposed to be the slow path, so whenever possible
# we choose the most compact encoding over the most efficient one.
if IS_X86_32:
@@ -2293,6 +2323,43 @@
loc = arglocs[i]
assert isinstance(loc, RegLoc)
self.mc.POP_r(loc.value)
+
+ # if GCFLAG_CARDS_SET, then we can do the whole thing that would
+ # be done in the CALL above with just four instructions, so here
+ # is an inline copy of them
+ if card_marking:
+ self.mc.JMP_l8(0) # jump to the exit, patched later
+ jmp_location = self.mc.get_relative_pos()
+ # patch the JNZ above
+ offset = self.mc.get_relative_pos() - jnz_location
+ assert 0 < offset <= 127
+ self.mc.overwrite(jnz_location-1, chr(offset))
+ #
+ loc_index = arglocs[1]
+ if isinstance(loc_index, RegLoc):
+ # choose a scratch register
+ tmp1 = loc_index
+ self.mc.PUSH_r(tmp1.value)
+ # SHR tmp, card_page_shift
+ self.mc.SHR_ri(tmp1.value, descr.jit_wb_card_page_shift)
+ # XOR tmp, -8
+ self.mc.XOR_ri(tmp1.value, -8)
+ # BTS [loc_base], tmp
+ self.mc.BTS(addr_add_const(loc_base, 0), tmp1)
+ # done
+ self.mc.POP_r(tmp1.value)
+ elif isinstance(loc_index, ImmedLoc):
+ byte_index = loc_index.value >> descr.jit_wb_card_page_shift
+ byte_ofs = ~(byte_index >> 3)
+ byte_val = 1 << (byte_index & 7)
+ self.mc.OR8(addr_add_const(loc_base, byte_ofs), imm(byte_val))
+ else:
+ raise AssertionError("index is neither RegLoc nor ImmedLoc")
+ # patch the JMP above
+ offset = self.mc.get_relative_pos() - jmp_location
+ assert 0 < offset <= 127
+ self.mc.overwrite(jmp_location-1, chr(offset))
+ #
# patch the JZ above
offset = self.mc.get_relative_pos() - jz_location
assert 0 < offset <= 127
@@ -2369,8 +2436,7 @@
# there are two helpers to call only with asmgcc
slowpath_addr1 = self.malloc_slowpath1
self.mc.CALL(imm(slowpath_addr1))
- self.mark_gc_roots(self.write_new_force_index(),
- use_copy_area=shadow_stack)
+ self.mark_gc_roots(self.write_new_force_index(), use_copy_area=True)
slowpath_addr2 = self.malloc_slowpath2
self.mc.CALL(imm(slowpath_addr2))
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
@@ -921,27 +921,13 @@
def _do_fastpath_malloc(self, op, size, tid):
gc_ll_descr = self.assembler.cpu.gc_ll_descr
self.rm.force_allocate_reg(op.result, selected_reg=eax)
-
- if gc_ll_descr.gcrootmap and gc_ll_descr.gcrootmap.is_shadow_stack:
- # ---- shadowstack ----
- # We need edx as a temporary, but otherwise don't save any more
- # register. See comments in _build_malloc_slowpath().
- tmp_box = TempBox()
- self.rm.force_allocate_reg(tmp_box, selected_reg=edx)
- self.rm.possibly_free_var(tmp_box)
- else:
- # ---- asmgcc ----
- # We need to force-allocate each of save_around_call_regs now.
- # The alternative would be to save and restore them around the
- # actual call to malloc(), in the rare case where we need to do
- # it; however, mark_gc_roots() would need to be adapted to know
- # where the variables end up being saved. Messy.
- for reg in self.rm.save_around_call_regs:
- if reg is not eax:
- tmp_box = TempBox()
- self.rm.force_allocate_reg(tmp_box, selected_reg=reg)
- self.rm.possibly_free_var(tmp_box)
-
+ #
+ # We need edx as a temporary, but otherwise don't save any more
+ # register. See comments in _build_malloc_slowpath().
+ tmp_box = TempBox()
+ self.rm.force_allocate_reg(tmp_box, selected_reg=edx)
+ self.rm.possibly_free_var(tmp_box)
+ #
self.assembler.malloc_cond(
gc_ll_descr.get_nursery_free_addr(),
gc_ll_descr.get_nursery_top_addr(),
@@ -1337,14 +1323,26 @@
if reg is eax:
continue # ok to ignore this one
if (isinstance(v, BoxPtr) and self.rm.stays_alive(v)):
- if use_copy_area:
- assert reg in self.rm.REGLOC_TO_COPY_AREA_OFS
- area_offset = self.rm.REGLOC_TO_COPY_AREA_OFS[reg]
- gcrootmap.add_frame_offset(shape, area_offset)
- else:
- assert reg in self.rm.REGLOC_TO_GCROOTMAP_REG_INDEX
- gcrootmap.add_callee_save_reg(
- shape, self.rm.REGLOC_TO_GCROOTMAP_REG_INDEX[reg])
+ #
+ # The register 'reg' is alive across this call.
+ gcrootmap = self.assembler.cpu.gc_ll_descr.gcrootmap
+ if gcrootmap is None or not gcrootmap.is_shadow_stack:
+ #
+ # Asmgcc: if reg is a callee-save register, we can
+ # explicitly mark it as containing a BoxPtr.
+ if reg in self.rm.REGLOC_TO_GCROOTMAP_REG_INDEX:
+ gcrootmap.add_callee_save_reg(
+ shape, self.rm.REGLOC_TO_GCROOTMAP_REG_INDEX[reg])
+ continue
+ #
+ # Else, 'use_copy_area' must be True (otherwise this BoxPtr
+ # should not be in a register). The copy area contains the
+ # real value of the register.
+ assert use_copy_area
+ assert reg in self.rm.REGLOC_TO_COPY_AREA_OFS
+ area_offset = self.rm.REGLOC_TO_COPY_AREA_OFS[reg]
+ gcrootmap.add_frame_offset(shape, area_offset)
+ #
return gcrootmap.compress_callshape(shape,
self.assembler.datablockwrapper)
diff --git a/pypy/jit/backend/x86/regloc.py b/pypy/jit/backend/x86/regloc.py
--- a/pypy/jit/backend/x86/regloc.py
+++ b/pypy/jit/backend/x86/regloc.py
@@ -476,6 +476,7 @@
AND = _binaryop('AND')
OR = _binaryop('OR')
+ OR8 = _binaryop('OR8')
XOR = _binaryop('XOR')
NOT = _unaryop('NOT')
SHL = _binaryop('SHL')
@@ -483,6 +484,7 @@
SAR = _binaryop('SAR')
TEST = _binaryop('TEST')
TEST8 = _binaryop('TEST8')
+ BTS = _binaryop('BTS')
ADD = _binaryop('ADD')
SUB = _binaryop('SUB')
diff --git a/pypy/jit/backend/x86/rx86.py b/pypy/jit/backend/x86/rx86.py
--- a/pypy/jit/backend/x86/rx86.py
+++ b/pypy/jit/backend/x86/rx86.py
@@ -464,7 +464,7 @@
# ------------------------------ MOV ------------------------------
- MOV_ri = insn(rex_w, register(1), '\xB8', immediate(2, 'q'))
+ MOV_ri = insn(register(1), '\xB8', immediate(2))
MOV8_ri = insn(rex_fw, byte_register(1), '\xB0', immediate(2, 'b'))
# ------------------------------ Arithmetic ------------------------------
@@ -496,6 +496,10 @@
AND8_rr = insn(rex_fw, '\x20', byte_register(1), byte_register(2,8), '\xC0')
OR8_rr = insn(rex_fw, '\x08', byte_register(1), byte_register(2,8), '\xC0')
+ OR8_mi = insn(rex_fw, '\x80', orbyte(1<<3), mem_reg_plus_const(1),
+ immediate(2, 'b'))
+ OR8_ji = insn(rex_fw, '\x80', orbyte(1<<3), abs_, immediate(1),
+ immediate(2, 'b'))
NEG_r = insn(rex_w, '\xF7', register(1), '\xD8')
@@ -565,6 +569,9 @@
TEST8_ji = insn(rex_nw, '\xF6', orbyte(0<<3), abs_, immediate(1), immediate(2, 'b'))
TEST_rr = insn(rex_w, '\x85', register(2,8), register(1), '\xC0')
+ BTS_mr = insn(rex_w, '\x0F\xAB', register(2,8), mem_reg_plus_const(1))
+ BTS_jr = insn(rex_w, '\x0F\xAB', register(2,8), abs_, immediate(1))
+
# x87 instructions
FSTP_b = insn('\xDD', orbyte(3<<3), stack_bp(1))
@@ -632,16 +639,20 @@
CQO = insn(rex_w, '\x99')
- # MOV_ri from the parent class is not wrong, but here is a better encoding
- # for the common case where the immediate fits in 32 bits
+ # Three different encodings... following what gcc does. From the
+ # shortest encoding to the longest one.
+ MOV_riu32 = insn(rex_nw, register(1), '\xB8', immediate(2, 'i'))
MOV_ri32 = insn(rex_w, '\xC7', register(1), '\xC0', immediate(2, 'i'))
- MOV_ri64 = AbstractX86CodeBuilder.MOV_ri
+ MOV_ri64 = insn(rex_w, register(1), '\xB8', immediate(2, 'q'))
def MOV_ri(self, reg, immed):
- if fits_in_32bits(immed):
+ if 0 <= immed <= 4294967295:
+ immed = intmask(rffi.cast(rffi.INT, immed))
+ self.MOV_riu32(reg, immed)
+ elif fits_in_32bits(immed): # for negative values that fit in 32 bit
self.MOV_ri32(reg, immed)
else:
- AbstractX86CodeBuilder.MOV_ri(self, reg, immed)
+ self.MOV_ri64(reg, immed)
def define_modrm_modes(insnname_template, before_modrm, after_modrm=[], regtype='GPR'):
def add_insn(code, *modrm):
diff --git a/pypy/jit/backend/x86/test/test_regloc.py b/pypy/jit/backend/x86/test/test_regloc.py
--- a/pypy/jit/backend/x86/test/test_regloc.py
+++ b/pypy/jit/backend/x86/test/test_regloc.py
@@ -24,9 +24,14 @@
assert_encodes_as(cb64, "MOV16", (r8, ebx), '\x66\x41\x89\xD8') # 11 011 000
assert_encodes_as(cb64, "MOV16", (ebx, r8), '\x66\x44\x89\xC3') # 11 000 011
assert_encodes_as(cb64, "MOV16", (ecx, ebx), '\x66\x40\x89\xD9')
- # XXX: What we are testing for here is actually not the most compact
- # encoding.
- assert_encodes_as(cb64, "MOV16", (ecx, ImmedLoc(12345)), '\x66\x40\xC7\xC1\x39\x30')
+ assert_encodes_as(cb64, "MOV16", (ecx, ImmedLoc(12345)), '\x66\xB9\x39\x30')
+ # for the next case we don't pick the most efficient encoding, but well
+ expected = '\x66\x40\xC7\xC1\xC7\xCF' # could be '\x66\xB9\xC7\xCF'
+ assert_encodes_as(cb64, "MOV16", (ecx, ImmedLoc(-12345)), expected)
+ assert_encodes_as(cb64, "MOV16", (r9, ImmedLoc(12345)), '\x66\x41\xB9\x39\x30')
+ # for the next case we don't pick the most efficient encoding, but well
+ expected = '\x66\x41\xC7\xC1\xC7\xCF' # could be '\x66\x41\xB9\xC7\xCF'
+ assert_encodes_as(cb64, "MOV16", (r9, ImmedLoc(-12345)), expected)
assert_encodes_as(cb64, "MOV16", (AddressLoc(r13, ImmedLoc(0), 0, 0), ImmedLoc(12345)), '\x66\x41\xC7\x45\x00\x39\x30')
def test_cmp_16():
@@ -44,7 +49,7 @@
def test_relocation():
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.jit.backend.x86 import codebuf
- for target in [0x01020304, 0x0102030405060708]:
+ for target in [0x01020304, -0x05060708, 0x0102030405060708]:
if target > sys.maxint:
continue
mc = codebuf.MachineCodeBlockWrapper()
@@ -58,10 +63,15 @@
expected = "\xE8" + struct.pack('<i', target - (rawstart + 5))
elif IS_X86_64:
assert mc.relocations == []
- if target <= 0x7fffffff:
+ if 0 <= target <= 0xffffffff:
+ assert length == 9
+ expected = (
+ "\x41\xBB\x04\x03\x02\x01" # MOV %r11, target
+ "\x41\xFF\xD3") # CALL *%r11
+ elif -0x80000000 <= target < 0:
assert length == 10
expected = (
- "\x49\xC7\xC3\x04\x03\x02\x01" # MOV %r11, target
+ "\x49\xC7\xC3\xF8\xF8\xF9\xFA" # MOV %r11, target
"\x41\xFF\xD3") # CALL *%r11
else:
assert length == 13
diff --git a/pypy/jit/backend/x86/test/test_rx86.py b/pypy/jit/backend/x86/test/test_rx86.py
--- a/pypy/jit/backend/x86/test/test_rx86.py
+++ b/pypy/jit/backend/x86/test/test_rx86.py
@@ -198,9 +198,19 @@
def test_mov_ri_64():
s = CodeBuilder64()
s.MOV_ri(ecx, -2)
+ s.MOV_ri(r15, -3)
+ s.MOV_ri(ebx, -0x80000003)
+ s.MOV_ri(r13, -0x80000002)
+ s.MOV_ri(ecx, 42)
s.MOV_ri(r12, 0x80000042)
+ s.MOV_ri(r12, 0x100000007)
assert s.getvalue() == ('\x48\xC7\xC1\xFE\xFF\xFF\xFF' +
- '\x49\xBC\x42\x00\x00\x80\x00\x00\x00\x00')
+ '\x49\xC7\xC7\xFD\xFF\xFF\xFF' +
+ '\x48\xBB\xFD\xFF\xFF\x7F\xFF\xFF\xFF\xFF' +
+ '\x49\xBD\xFE\xFF\xFF\x7F\xFF\xFF\xFF\xFF' +
+ '\xB9\x2A\x00\x00\x00' +
+ '\x41\xBC\x42\x00\x00\x80' +
+ '\x49\xBC\x07\x00\x00\x00\x01\x00\x00\x00')
def test_mov_rm_64():
s = CodeBuilder64()
diff --git a/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py b/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py
--- a/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py
+++ b/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py
@@ -212,6 +212,17 @@
for mode, v in zip(argmodes, args):
ops.append(assembler_operand[mode](v))
ops.reverse()
+ #
+ if (instrname.lower() == 'mov' and suffix == 'q' and
+ ops[0].startswith('$') and 0 <= int(ops[0][1:]) <= 4294967295
+ and ops[1].startswith('%r')):
+ # movq $xxx, %rax => movl $xxx, %eax
+ suffix = 'l'
+ if ops[1][2:].isdigit():
+ ops[1] += 'd'
+ else:
+ ops[1] = '%e' + ops[1][2:]
+ #
op = '\t%s%s %s%s' % (instrname.lower(), suffix,
', '.join(ops), following)
g.write('%s\n' % op)
diff --git a/pypy/jit/backend/x86/test/test_zrpy_gc.py b/pypy/jit/backend/x86/test/test_zrpy_gc.py
--- a/pypy/jit/backend/x86/test/test_zrpy_gc.py
+++ b/pypy/jit/backend/x86/test/test_zrpy_gc.py
@@ -10,7 +10,7 @@
from pypy.rlib import rgc
from pypy.rpython.lltypesystem import lltype, llmemory, rffi
from pypy.rlib.jit import JitDriver, dont_look_inside
-from pypy.rlib.jit import purefunction, unroll_safe
+from pypy.rlib.jit import elidable, unroll_safe
from pypy.jit.backend.llsupport.gc import GcLLDescr_framework
from pypy.tool.udir import udir
from pypy.config.translationoption import DEFL_GC
@@ -524,6 +524,76 @@
def test_compile_framework_8(self):
self.run('compile_framework_8')
+ def define_compile_framework_9(cls):
+ # Like compile_framework_8, but with variable indexes and large
+ # arrays, testing the card_marking case
+ def before(n, x):
+ return n, x, None, None, None, None, None, None, None, None, [X(123)], None
+ def f(n, x, x0, x1, x2, x3, x4, x5, x6, x7, l, s):
+ if n < 1900:
+ check(l[0].x == 123)
+ num = 512 + (n & 7)
+ l = [None] * num
+ l[0] = X(123)
+ l[1] = X(n)
+ l[2] = X(n+10)
+ l[3] = X(n+20)
+ l[4] = X(n+30)
+ l[5] = X(n+40)
+ l[6] = X(n+50)
+ l[7] = X(n+60)
+ l[num-8] = X(n+70)
+ l[num-9] = X(n+80)
+ l[num-10] = X(n+90)
+ l[num-11] = X(n+100)
+ l[-12] = X(n+110)
+ l[-13] = X(n+120)
+ l[-14] = X(n+130)
+ l[-15] = X(n+140)
+ if n < 1800:
+ num = 512 + (n & 7)
+ check(len(l) == num)
+ check(l[0].x == 123)
+ check(l[1].x == n)
+ check(l[2].x == n+10)
+ check(l[3].x == n+20)
+ check(l[4].x == n+30)
+ check(l[5].x == n+40)
+ check(l[6].x == n+50)
+ check(l[7].x == n+60)
+ check(l[num-8].x == n+70)
+ check(l[num-9].x == n+80)
+ check(l[num-10].x == n+90)
+ check(l[num-11].x == n+100)
+ check(l[-12].x == n+110)
+ check(l[-13].x == n+120)
+ check(l[-14].x == n+130)
+ check(l[-15].x == n+140)
+ n -= x.foo
+ return n, x, x0, x1, x2, x3, x4, x5, x6, x7, l, s
+ def after(n, x, x0, x1, x2, x3, x4, x5, x6, x7, l, s):
+ check(len(l) >= 512)
+ check(l[0].x == 123)
+ check(l[1].x == 2)
+ check(l[2].x == 12)
+ check(l[3].x == 22)
+ check(l[4].x == 32)
+ check(l[5].x == 42)
+ check(l[6].x == 52)
+ check(l[7].x == 62)
+ check(l[-8].x == 72)
+ check(l[-9].x == 82)
+ check(l[-10].x == 92)
+ check(l[-11].x == 102)
+ check(l[-12].x == 112)
+ check(l[-13].x == 122)
+ check(l[-14].x == 132)
+ check(l[-15].x == 142)
+ return before, f, after
+
+ def test_compile_framework_9(self):
+ self.run('compile_framework_9')
+
def define_compile_framework_external_exception_handling(cls):
def before(n, x):
x = X(0)
@@ -561,7 +631,7 @@
self.run('compile_framework_external_exception_handling')
def define_compile_framework_bug1(self):
- @purefunction
+ @elidable
def nonmoving():
x = X(1)
for i in range(7):
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
@@ -2,7 +2,7 @@
from pypy.tool.udir import udir
from pypy.rlib.jit import JitDriver, unroll_parameters
from pypy.rlib.jit import PARAMETERS, dont_look_inside
-from pypy.rlib.jit import hint
+from pypy.rlib.jit import promote
from pypy.jit.metainterp.jitprof import Profiler
from pypy.jit.backend.detect_cpu import getcpuclass
from pypy.jit.backend.test.support import CCompiledMixin
@@ -78,8 +78,7 @@
x = float(j)
while i > 0:
jitdriver2.jit_merge_point(i=i, res=res, func=func, x=x)
- jitdriver2.can_enter_jit(i=i, res=res, func=func, x=x)
- func = hint(func, promote=True)
+ promote(func)
argchain = ArgChain()
argchain.arg(x)
res = func.call(argchain, rffi.DOUBLE)
diff --git a/pypy/jit/codewriter/call.py b/pypy/jit/codewriter/call.py
--- a/pypy/jit/codewriter/call.py
+++ b/pypy/jit/codewriter/call.py
@@ -5,10 +5,9 @@
from pypy.jit.codewriter import support
from pypy.jit.codewriter.jitcode import JitCode
-from pypy.jit.codewriter.effectinfo import VirtualizableAnalyzer
-from pypy.jit.codewriter.effectinfo import QuasiImmutAnalyzer
-from pypy.jit.codewriter.effectinfo import effectinfo_from_writeanalyze
-from pypy.jit.codewriter.effectinfo import EffectInfo, CallInfoCollection
+from pypy.jit.codewriter.effectinfo import (VirtualizableAnalyzer,
+ QuasiImmutAnalyzer, CanReleaseGILAnalyzer, effectinfo_from_writeanalyze,
+ EffectInfo, CallInfoCollection)
from pypy.translator.simplify import get_funcobj, get_functype
from pypy.rpython.lltypesystem import lltype, llmemory
from pypy.translator.backendopt.canraise import RaiseAnalyzer
@@ -32,6 +31,7 @@
self.readwrite_analyzer = ReadWriteAnalyzer(translator)
self.virtualizable_analyzer = VirtualizableAnalyzer(translator)
self.quasiimmut_analyzer = QuasiImmutAnalyzer(translator)
+ self.canreleasegil_analyzer = CanReleaseGILAnalyzer(translator)
#
for index, jd in enumerate(jitdrivers_sd):
jd.index = index
@@ -208,26 +208,28 @@
assert NON_VOID_ARGS == [T for T in ARGS if T is not lltype.Void]
assert RESULT == FUNC.RESULT
# ok
- # get the 'pure' and 'loopinvariant' flags from the function object
- pure = False
+ # get the 'elidable' and 'loopinvariant' flags from the function object
+ elidable = False
loopinvariant = False
if op.opname == "direct_call":
func = getattr(get_funcobj(op.args[0].value), '_callable', None)
- pure = getattr(func, "_pure_function_", False)
+ elidable = getattr(func, "_elidable_function_", False)
loopinvariant = getattr(func, "_jit_loop_invariant_", False)
if loopinvariant:
assert not NON_VOID_ARGS, ("arguments not supported for "
"loop-invariant function!")
# build the extraeffect
- can_invalidate = self.quasiimmut_analyzer.analyze(op)
+ can_release_gil = self.canreleasegil_analyzer.analyze(op)
+ # can_release_gil implies can_invalidate
+ can_invalidate = can_release_gil or self.quasiimmut_analyzer.analyze(op)
if extraeffect is None:
if self.virtualizable_analyzer.analyze(op):
extraeffect = EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE
elif loopinvariant:
extraeffect = EffectInfo.EF_LOOPINVARIANT
- elif pure:
+ elif elidable:
# XXX check what to do about exceptions (also MemoryError?)
- extraeffect = EffectInfo.EF_PURE
+ extraeffect = EffectInfo.EF_ELIDABLE
elif self._canraise(op):
extraeffect = EffectInfo.EF_CAN_RAISE
else:
@@ -235,11 +237,11 @@
#
effectinfo = effectinfo_from_writeanalyze(
self.readwrite_analyzer.analyze(op), self.cpu, extraeffect,
- oopspecindex, can_invalidate)
+ oopspecindex, can_invalidate, can_release_gil)
#
if oopspecindex != EffectInfo.OS_NONE:
assert effectinfo is not None
- if pure or loopinvariant:
+ if elidable or loopinvariant:
assert effectinfo is not None
assert extraeffect != EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE
# XXX this should also say assert not can_invalidate, but
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
@@ -9,7 +9,7 @@
_cache = {}
# the 'extraeffect' field is one of the following values:
- EF_PURE = 0 #pure function (and cannot raise)
+ EF_ELIDABLE = 0 #elidable function (and cannot raise)
EF_LOOPINVARIANT = 1 #special: call it only once per loop
EF_CANNOT_RAISE = 2 #a function which cannot raise
EF_CAN_RAISE = 3 #normal function (can raise)
@@ -79,20 +79,22 @@
write_descrs_fields, write_descrs_arrays,
extraeffect=EF_CAN_RAISE,
oopspecindex=OS_NONE,
- can_invalidate=False):
+ can_invalidate=False, can_release_gil=False):
key = (frozenset(readonly_descrs_fields),
frozenset(readonly_descrs_arrays),
frozenset(write_descrs_fields),
frozenset(write_descrs_arrays),
extraeffect,
- oopspecindex)
+ oopspecindex,
+ can_invalidate,
+ can_release_gil)
if key in cls._cache:
return cls._cache[key]
result = object.__new__(cls)
result.readonly_descrs_fields = readonly_descrs_fields
result.readonly_descrs_arrays = readonly_descrs_arrays
if extraeffect == EffectInfo.EF_LOOPINVARIANT or \
- extraeffect == EffectInfo.EF_PURE:
+ extraeffect == EffectInfo.EF_ELIDABLE:
result.write_descrs_fields = []
result.write_descrs_arrays = []
else:
@@ -100,6 +102,7 @@
result.write_descrs_arrays = write_descrs_arrays
result.extraeffect = extraeffect
result.can_invalidate = can_invalidate
+ result.can_release_gil = can_release_gil
result.oopspecindex = oopspecindex
cls._cache[key] = result
return result
@@ -111,12 +114,13 @@
return self.extraeffect >= self.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE
def has_random_effects(self):
- return self.oopspecindex == self.OS_LIBFFI_CALL
+ return self.oopspecindex == self.OS_LIBFFI_CALL or self.can_release_gil
def effectinfo_from_writeanalyze(effects, cpu,
extraeffect=EffectInfo.EF_CAN_RAISE,
oopspecindex=EffectInfo.OS_NONE,
- can_invalidate=False):
+ can_invalidate=False,
+ can_release_gil=False):
from pypy.translator.backendopt.writeanalyze import top_set
if effects is top_set:
return None
@@ -158,7 +162,8 @@
write_descrs_arrays,
extraeffect,
oopspecindex,
- can_invalidate)
+ can_invalidate,
+ can_release_gil)
def consider_struct(TYPE, fieldname):
if fieldType(TYPE, fieldname) is lltype.Void:
@@ -194,6 +199,16 @@
def analyze_simple_operation(self, op, graphinfo):
return op.opname == 'jit_force_quasi_immutable'
+class CanReleaseGILAnalyzer(BoolGraphAnalyzer):
+ def analyze_direct_call(self, graph, seen=None):
+ releases_gil = False
+ if hasattr(graph, "func") and hasattr(graph.func, "_ptr"):
+ releases_gil = graph.func._ptr._obj.releases_gil
+ return releases_gil or super(CanReleaseGILAnalyzer, self).analyze_direct_call(graph, seen)
+
+ def analyze_simple_operation(self, op, graphinfo):
+ return False
+
# ____________________________________________________________
class CallInfoCollection(object):
diff --git a/pypy/jit/codewriter/jitcode.py b/pypy/jit/codewriter/jitcode.py
--- a/pypy/jit/codewriter/jitcode.py
+++ b/pypy/jit/codewriter/jitcode.py
@@ -1,7 +1,6 @@
from pypy.jit.metainterp.history import AbstractDescr
from pypy.jit.codewriter import heaptracker
from pypy.rlib.objectmodel import we_are_translated
-from pypy.rpython.lltypesystem import llmemory
class JitCode(AbstractDescr):
@@ -102,7 +101,7 @@
def _clone_if_mutable(self):
raise NotImplementedError
-
+
class MissingLiveness(Exception):
pass
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
@@ -1,18 +1,16 @@
-import py, sys
-from pypy.rpython.lltypesystem import lltype, llmemory, rstr, rclass
-from pypy.rpython import rlist
-from pypy.jit.metainterp.history import getkind
-from pypy.objspace.flow.model import SpaceOperation, Variable, Constant
-from pypy.objspace.flow.model import Block, Link, c_last_exception
-from pypy.jit.codewriter.flatten import ListOfKind, IndirectCallTargets
+import py
from pypy.jit.codewriter import support, heaptracker, longlong
from pypy.jit.codewriter.effectinfo import EffectInfo
+from pypy.jit.codewriter.flatten import ListOfKind, IndirectCallTargets
from pypy.jit.codewriter.policy import log
+from pypy.jit.metainterp import quasiimmut
+from pypy.jit.metainterp.history import getkind
from pypy.jit.metainterp.typesystem import deref, arrayItem
-from pypy.jit.metainterp import quasiimmut
-from pypy.rpython.rclass import IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY
+from pypy.objspace.flow.model import SpaceOperation, Variable, Constant, c_last_exception
from pypy.rlib import objectmodel
from pypy.rlib.jit import _we_are_jitted
+from pypy.rpython.lltypesystem import lltype, llmemory, rstr, rclass
+from pypy.rpython.rclass import IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY
from pypy.translator.simplify import get_funcobj
from pypy.translator.unsimplify import varoftype
@@ -765,13 +763,64 @@
raise NotImplementedError("cast_ptr_to_int")
def rewrite_op_force_cast(self, op):
- from pypy.rpython.lltypesystem.rffi import size_and_sign, sizeof
- from pypy.rlib.rarithmetic import intmask
assert not self._is_gc(op.args[0])
- size2, unsigned2 = size_and_sign(op.result.concretetype)
- if size2 >= sizeof(lltype.Signed):
+ fromll = longlong.is_longlong(op.args[0].concretetype)
+ toll = longlong.is_longlong(op.result.concretetype)
+ if fromll and toll:
+ return
+ if fromll:
+ args = op.args
+ opname = 'truncate_longlong_to_int'
+ RESULT = lltype.Signed
+ v = varoftype(RESULT)
+ op1 = SpaceOperation(opname, args, v)
+ op2 = self.rewrite_operation(op1)
+ oplist = self.force_cast_without_longlong(op2.result, op.result)
+ if oplist:
+ return [op2] + oplist
+ #
+ # force a renaming to put the correct result in place, even though
+ # it might be slightly mistyped (e.g. Signed versus Unsigned)
+ assert op2.result is v
+ op2.result = op.result
+ return op2
+ elif toll:
+ from pypy.rpython.lltypesystem import rffi
+ size, unsigned = rffi.size_and_sign(op.args[0].concretetype)
+ if unsigned:
+ INTERMEDIATE = lltype.Unsigned
+ else:
+ INTERMEDIATE = lltype.Signed
+ v = varoftype(INTERMEDIATE)
+ oplist = self.force_cast_without_longlong(op.args[0], v)
+ if not oplist:
+ v = op.args[0]
+ oplist = []
+ if unsigned:
+ opname = 'cast_uint_to_longlong'
+ else:
+ opname = 'cast_int_to_longlong'
+ op1 = SpaceOperation(opname, [v], op.result)
+ op2 = self.rewrite_operation(op1)
+ return oplist + [op2]
+ else:
+ return self.force_cast_without_longlong(op.args[0], op.result)
+
+ def force_cast_without_longlong(self, v_arg, v_result):
+ from pypy.rpython.lltypesystem.rffi import size_and_sign, sizeof, FLOAT
+ #
+ if (v_result.concretetype in (FLOAT, lltype.Float) or
+ v_arg.concretetype in (FLOAT, lltype.Float)):
+ assert (v_result.concretetype == lltype.Float and
+ v_arg.concretetype == lltype.Float), "xxx unsupported cast"
+ return
+ #
+ size2, unsigned2 = size_and_sign(v_result.concretetype)
+ assert size2 <= sizeof(lltype.Signed)
+ if size2 == sizeof(lltype.Signed):
return # the target type is LONG or ULONG
- size1, unsigned1 = size_and_sign(op.args[0].concretetype)
+ size1, unsigned1 = size_and_sign(v_arg.concretetype)
+ assert size1 <= sizeof(lltype.Signed)
#
def bounds(size, unsigned):
if unsigned:
@@ -784,22 +833,28 @@
return # the target type includes the source range
#
result = []
- v1 = op.args[0]
if min2:
c_min2 = Constant(min2, lltype.Signed)
- v2 = Variable(); v2.concretetype = lltype.Signed
- result.append(SpaceOperation('int_sub', [v1, c_min2], v2))
+ v2 = varoftype(lltype.Signed)
+ result.append(SpaceOperation('int_sub', [v_arg, c_min2], v2))
else:
- v2 = v1
+ v2 = v_arg
c_mask = Constant(int((1<<(8*size2))-1), lltype.Signed)
- v3 = Variable(); v3.concretetype = lltype.Signed
+ v3 = varoftype(lltype.Signed)
result.append(SpaceOperation('int_and', [v2, c_mask], v3))
if min2:
- result.append(SpaceOperation('int_add', [v3, c_min2], op.result))
+ result.append(SpaceOperation('int_add', [v3, c_min2], v_result))
else:
- result[-1].result = op.result
+ result[-1].result = v_result
return result
+ def rewrite_op_direct_ptradd(self, op):
+ from pypy.rpython.lltypesystem import rffi
+ # xxx otherwise, not implemented:
+ assert op.args[0].concretetype == rffi.CCHARP
+ #
+ return SpaceOperation('int_add', [op.args[0], op.args[1]], op.result)
+
# ----------
# Long longs, for 32-bit only. Supported operations are left unmodified,
# and unsupported ones are turned into a call to a function from
@@ -847,7 +902,7 @@
op1 = self.prepare_builtin_call(op, "llong_%s", args)
op2 = self._handle_oopspec_call(op1, args,
EffectInfo.OS_LLONG_%s,
- EffectInfo.EF_PURE)
+ EffectInfo.EF_ELIDABLE)
if %r == "TO_INT":
assert op2.result.concretetype == lltype.Signed
return op2
@@ -883,30 +938,7 @@
rewrite_op_ullong_is_true = rewrite_op_llong_is_true
def rewrite_op_cast_primitive(self, op):
- fromll = longlong.is_longlong(op.args[0].concretetype)
- toll = longlong.is_longlong(op.result.concretetype)
- if fromll != toll:
- args = op.args
- if fromll:
- opname = 'truncate_longlong_to_int'
- RESULT = lltype.Signed
- else:
- from pypy.rpython.lltypesystem import rffi
- if rffi.cast(op.args[0].concretetype, -1) < 0:
- opname = 'cast_int_to_longlong'
- else:
- opname = 'cast_uint_to_longlong'
- RESULT = lltype.SignedLongLong
- v = varoftype(RESULT)
- op1 = SpaceOperation(opname, args, v)
- op2 = self.rewrite_operation(op1)
- #
- # force a renaming to put the correct result in place, even though
- # it might be slightly mistyped (e.g. Signed versus Unsigned)
- assert op2.result is v
- op2.result = op.result
- #
- return op2
+ return self.rewrite_op_force_cast(op)
# ----------
# Renames, from the _old opname to the _new one.
@@ -1083,6 +1115,9 @@
return meth(op, args, *descrs)
def _get_list_nonneg_canraise_flags(self, op):
+ # XXX as far as I can see, this function will always return True
+ # because functions that are neither nonneg nor fast don't have an
+ # oopspec any more
# xxx break of abstraction:
func = get_funcobj(op.args[0].value)._callable
# base hints on the name of the ll function, which is a bit xxx-ish
@@ -1240,7 +1275,7 @@
calldescr = self.callcontrol.getcalldescr(op, oopspecindex,
extraeffect)
if extraeffect is not None:
- assert (type(calldescr) is str # for tests
+ assert (is_test_calldescr(calldescr) # for tests
or calldescr.get_extra_info().extraeffect == extraeffect)
if isinstance(op.args[0].value, str):
pass # for tests only
@@ -1328,13 +1363,13 @@
otherindex += EffectInfo._OS_offset_uni
self._register_extra_helper(otherindex, othername,
argtypes, resulttype,
- EffectInfo.EF_PURE)
+ EffectInfo.EF_ELIDABLE)
#
return self._handle_oopspec_call(op, args, dict[oopspec_name],
- EffectInfo.EF_PURE)
+ EffectInfo.EF_ELIDABLE)
def _handle_str2unicode_call(self, op, oopspec_name, args):
- # ll_str2unicode is not EF_PURE, because it can raise
+ # ll_str2unicode is not EF_ELIDABLE, because it can raise
# UnicodeDecodeError...
return self._handle_oopspec_call(op, args, EffectInfo.OS_STR2UNICODE)
@@ -1374,13 +1409,13 @@
assert vinfo is not None
self.vable_flags[op.args[0]] = op.args[2].value
return []
-
+
# ---------
# ll_math.sqrt_nonneg()
-
+
def _handle_math_sqrt_call(self, op, oopspec_name, args):
return self._handle_oopspec_call(op, args, EffectInfo.OS_MATH_SQRT,
- EffectInfo.EF_PURE)
+ EffectInfo.EF_ELIDABLE)
def rewrite_op_jit_force_quasi_immutable(self, op):
v_inst, c_fieldname = op.args
@@ -1401,6 +1436,9 @@
return "using virtualizable array in illegal way in %r" % (
self.args[0],)
+def is_test_calldescr(calldescr):
+ return type(calldescr) is str or getattr(calldescr, '_for_tests_only', False)
+
def _with_prefix(prefix):
result = {}
for name in dir(Transformer):
diff --git a/pypy/jit/codewriter/policy.py b/pypy/jit/codewriter/policy.py
--- a/pypy/jit/codewriter/policy.py
+++ b/pypy/jit/codewriter/policy.py
@@ -1,9 +1,7 @@
-from pypy.translator.simplify import get_funcobj
from pypy.jit.metainterp import history
-from pypy.rpython.lltypesystem import lltype, rclass
from pypy.tool.udir import udir
-import py, sys
+import py
from pypy.tool.ansi_print import ansi_log
log = py.log.Producer('jitcodewriter')
py.log.setconsumer('jitcodewriter', ansi_log)
@@ -35,8 +33,8 @@
def _reject_function(self, func):
if hasattr(func, '_jit_look_inside_'):
return not func._jit_look_inside_
- # explicitly pure functions are always opaque
- if getattr(func, '_pure_function_', False):
+ # explicitly elidable functions are always opaque
+ if getattr(func, '_elidable_function_', False):
return True
# pypy.rpython.module.* are opaque helpers
mod = func.__module__ or '?'
diff --git a/pypy/jit/codewriter/support.py b/pypy/jit/codewriter/support.py
--- a/pypy/jit/codewriter/support.py
+++ b/pypy/jit/codewriter/support.py
@@ -185,7 +185,7 @@
return llop.int_floordiv(lltype.Signed, x, y)
def _ll_2_int_floordiv_ovf(x, y):
- if x == -sys.maxint - 1 and y == -1:
+ if x == -sys.maxint - 1 and y == -1:
raise OverflowError
return llop.int_floordiv(lltype.Signed, x, y)
@@ -222,7 +222,7 @@
return -x
else:
return x
-
+
# math support
# ------------
@@ -395,7 +395,7 @@
('int_lshift_ovf', [lltype.Signed, lltype.Signed], lltype.Signed),
('int_abs', [lltype.Signed], lltype.Signed),
('ll_math.ll_math_sqrt', [lltype.Float], lltype.Float),
- ]
+]
class LLtypeHelpers:
@@ -420,10 +420,6 @@
_ll_1_dict_values.need_result_type = True
_ll_1_dict_items .need_result_type = True
- def _ll_1_newdictiter(ITER, d):
- return ll_rdict.ll_dictiter(lltype.Ptr(ITER), d)
- _ll_1_newdictiter.need_result_type = True
-
_dictnext_keys = staticmethod(ll_rdict.ll_dictnext_group['keys'])
_dictnext_values = staticmethod(ll_rdict.ll_dictnext_group['values'])
_dictnext_items = staticmethod(ll_rdict.ll_dictnext_group['items'])
@@ -574,10 +570,6 @@
_ll_1_dict_values.need_result_type = True
_ll_1_dict_items .need_result_type = True
- def _ll_1_newdictiter(ITER, d):
- return oo_rdict.ll_dictiter(ITER, d)
- _ll_1_newdictiter.need_result_type = True
-
_dictnext_keys = staticmethod(oo_rdict.ll_dictnext_group['keys'])
_dictnext_values = staticmethod(oo_rdict.ll_dictnext_group['values'])
_dictnext_items = staticmethod(oo_rdict.ll_dictnext_group['items'])
diff --git a/pypy/jit/codewriter/test/test_call.py b/pypy/jit/codewriter/test/test_call.py
--- a/pypy/jit/codewriter/test/test_call.py
+++ b/pypy/jit/codewriter/test/test_call.py
@@ -1,6 +1,6 @@
import py
from pypy.objspace.flow.model import SpaceOperation, Constant, Variable
-from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.lltypesystem import lltype, rffi
from pypy.translator.unsimplify import varoftype
from pypy.rlib import jit
from pypy.jit.codewriter.call import CallControl
@@ -103,7 +103,7 @@
op = SpaceOperation('direct_call', [Constant(object())],
Variable())
- assert cc.guess_call_kind(op) == 'residual'
+ assert cc.guess_call_kind(op) == 'residual'
class funcptr:
class graph:
@@ -118,7 +118,7 @@
op = SpaceOperation('direct_call', [Constant(funcptr)],
Variable())
res = cc.graphs_from(op)
- assert res == [g]
+ assert res == [g]
assert cc.guess_call_kind(op) == 'regular'
class funcptr:
@@ -126,7 +126,7 @@
op = SpaceOperation('direct_call', [Constant(funcptr)],
Variable())
res = cc.graphs_from(op)
- assert res is None
+ assert res is None
assert cc.guess_call_kind(op) == 'residual'
h = object()
@@ -142,7 +142,7 @@
Variable())
res = cc.graphs_from(op)
assert res is None
- assert cc.guess_call_kind(op) == 'residual'
+ assert cc.guess_call_kind(op) == 'residual'
# ____________________________________________________________
@@ -171,3 +171,24 @@
def test_jit_force_virtualizable_effectinfo():
py.test.skip("XXX add a test for CallControl.getcalldescr() -> EF_xxx")
+
+def test_releases_gil_analyzer():
+ from pypy.jit.backend.llgraph.runner import LLtypeCPU
+
+ T = rffi.CArrayPtr(rffi.TIME_T)
+ external = rffi.llexternal("time", [T], rffi.TIME_T, threadsafe=True)
+
+ @jit.dont_look_inside
+ def f():
+ return external(lltype.nullptr(T.TO))
+
+ rtyper = support.annotate(f, [])
+ jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
+ cc = CallControl(LLtypeCPU(rtyper), jitdrivers_sd=[jitdriver_sd])
+ res = cc.find_all_graphs(FakePolicy())
+
+ [f_graph] = [x for x in res if x.func is f]
+ [block, _] = list(f_graph.iterblocks())
+ [op] = block.operations
+ call_descr = cc.getcalldescr(op)
+ assert call_descr.extrainfo.can_release_gil
\ No newline at end of file
diff --git a/pypy/jit/codewriter/test/test_flatten.py b/pypy/jit/codewriter/test/test_flatten.py
--- a/pypy/jit/codewriter/test/test_flatten.py
+++ b/pypy/jit/codewriter/test/test_flatten.py
@@ -3,6 +3,7 @@
from pypy.jit.codewriter.flatten import flatten_graph, reorder_renaming_list
from pypy.jit.codewriter.flatten import GraphFlattener, ListOfKind, Register
from pypy.jit.codewriter.format import assert_format
+from pypy.jit.codewriter import longlong
from pypy.jit.metainterp.history import AbstractDescr
from pypy.rpython.lltypesystem import lltype, rclass, rstr
from pypy.objspace.flow.model import SpaceOperation, Variable, Constant
@@ -30,6 +31,9 @@
'float': FakeRegAlloc()}
class FakeDescr(AbstractDescr):
+ _for_tests_only = True
+ def __init__(self, oopspecindex=None):
+ self.oopspecindex = oopspecindex
def __repr__(self):
return '<Descr>'
def as_vtable_size_descr(self):
@@ -55,19 +59,24 @@
def arraydescrof(self, ARRAY):
return FakeDescr()
+class FakeCallInfoCollection:
+ def add(self, *args):
+ pass
+
class FakeCallControl:
_descr_cannot_raise = FakeDescr()
+ callinfocollection = FakeCallInfoCollection()
def guess_call_kind(self, op):
return 'residual'
- def getcalldescr(self, op):
+ def getcalldescr(self, op, oopspecindex=None, extraeffect=None):
try:
if 'cannot_raise' in op.args[0].value._obj.graph.name:
return self._descr_cannot_raise
except AttributeError:
pass
- return FakeDescr()
+ return FakeDescr(oopspecindex)
def calldescr_canraise(self, calldescr):
- return calldescr is not self._descr_cannot_raise
+ return calldescr is not self._descr_cannot_raise and calldescr.oopspecindex is None
def get_vinfo(self, VTYPEPTR):
return None
@@ -734,7 +743,9 @@
def test_force_cast(self):
from pypy.rpython.lltypesystem import rffi
-
+ # NB: we don't need to test for INT here, the logic in jtransform is
+ # general enough so that if we have the below cases it should
+ # generalize also to INT
for FROM, TO, expected in [
(rffi.SIGNEDCHAR, rffi.SIGNEDCHAR, ""),
(rffi.SIGNEDCHAR, rffi.UCHAR, "int_and %i0, $255 -> %i1"),
@@ -797,14 +808,44 @@
expected = [s.strip() for s in expected.splitlines()]
check_force_cast(FROM, TO, expected, 42)
check_force_cast(FROM, TO, expected, -42)
- expected.append('int_return %i' + str(len(expected)))
- expected = '\n'.join(expected)
+ returnvar = "%i" + str(len(expected))
+ expected.append('int_return ' + returnvar)
+ expectedstr = '\n'.join(expected)
#
def f(n):
return rffi.cast(TO, n)
- self.encoding_test(f, [rffi.cast(FROM, 42)], expected,
+ self.encoding_test(f, [rffi.cast(FROM, 42)], expectedstr,
transform=True)
+ if not longlong.is_64_bit:
+ if FROM in (rffi.LONG, rffi.ULONG):
+ if FROM == rffi.LONG:
+ FROM = rffi.LONGLONG
+ else:
+ FROM = rffi.ULONGLONG
+ expected.insert(0,
+ "residual_call_irf_i $<* fn llong_to_int>, <Descr>, I[], R[], F[%f0] -> %i0")
+ expectedstr = '\n'.join(expected)
+ self.encoding_test(f, [rffi.cast(FROM, 42)], expectedstr,
+ transform=True)
+ elif TO in (rffi.LONG, rffi.ULONG):
+ if TO == rffi.LONG:
+ TO = rffi.LONGLONG
+ else:
+ TO = rffi.ULONGLONG
+ if rffi.cast(FROM, -1) < 0:
+ fnname = "llong_from_int"
+ else:
+ fnname = "llong_from_uint"
+ expected.pop() # remove int_return
+ expected.append(
+ "residual_call_irf_f $<* fn %s>, <Descr>, I[%s], R[], F[] -> %%f0"
+ % (fnname, returnvar))
+ expected.append("float_return %f0")
+ expectedstr = '\n'.join(expected)
+ self.encoding_test(f, [rffi.cast(FROM, 42)], expectedstr,
+ transform=True)
+
def test_force_cast_pointer(self):
from pypy.rpython.lltypesystem import rffi
def h(p):
@@ -813,6 +854,23 @@
int_return %i0
""", transform=True)
+ def test_force_cast_float(self):
+ from pypy.rpython.lltypesystem import rffi
+ def f(n):
+ return rffi.cast(lltype.Float, n)
+ self.encoding_test(f, [12.456], """
+ float_return %f0
+ """, transform=True)
+
+ def test_direct_ptradd(self):
+ from pypy.rpython.lltypesystem import rffi
+ def f(p, n):
+ return lltype.direct_ptradd(p, n)
+ self.encoding_test(f, [lltype.nullptr(rffi.CCHARP.TO), 123], """
+ int_add %i0, %i1 -> %i2
+ int_return %i2
+ """, transform=True)
+
def check_force_cast(FROM, TO, operations, value):
"""Check that the test is correctly written..."""
diff --git a/pypy/jit/codewriter/test/test_jtransform.py b/pypy/jit/codewriter/test/test_jtransform.py
--- a/pypy/jit/codewriter/test/test_jtransform.py
+++ b/pypy/jit/codewriter/test/test_jtransform.py
@@ -122,7 +122,7 @@
if oopspecindex == EI.OS_STR2UNICODE:
assert extraeffect == None # not pure, can raise!
else:
- assert extraeffect == EI.EF_PURE
+ assert extraeffect == EI.EF_ELIDABLE
return 'calldescr-%d' % oopspecindex
def calldescr_canraise(self, calldescr):
return False
@@ -769,7 +769,7 @@
def get_vinfo(self, v):
return None
def could_be_green_field(self, S1, name1):
- assert S1 is S
+ assert S1 == S
assert name1 == 'x'
return True
S = lltype.GcStruct('S', ('x', lltype.Char),
diff --git a/pypy/jit/codewriter/test/test_longlong.py b/pypy/jit/codewriter/test/test_longlong.py
--- a/pypy/jit/codewriter/test/test_longlong.py
+++ b/pypy/jit/codewriter/test/test_longlong.py
@@ -37,7 +37,7 @@
class TestLongLong:
def setup_class(cls):
- if sys.maxint > 2147483647:
+ if longlong.is_64_bit:
py.test.skip("only for 32-bit platforms")
def do_check(self, opname, oopspecindex, ARGS, RESULT):
@@ -46,6 +46,8 @@
op = SpaceOperation(opname, vlist, v_result)
tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
op1 = tr.rewrite_operation(op)
+ if isinstance(op1, list):
+ [op1] = op1
#
def is_llf(TYPE):
return (TYPE == lltype.SignedLongLong or
@@ -196,6 +198,23 @@
for T2 in [lltype.Signed, lltype.Unsigned]:
self.do_check('cast_primitive', EffectInfo.OS_LLONG_TO_INT,
[T1], T2)
+ self.do_check('force_cast', EffectInfo.OS_LLONG_TO_INT,
+ [T1], T2)
+ if T2 == lltype.Signed:
+ expected = EffectInfo.OS_LLONG_FROM_INT
+ else:
+ expected = EffectInfo.OS_LLONG_FROM_UINT
+ self.do_check('cast_primitive', expected, [T2], T1)
+ self.do_check('force_cast', expected, [T2], T1)
+ #
+ for T1 in [lltype.SignedLongLong, lltype.UnsignedLongLong]:
+ for T2 in [lltype.SignedLongLong, lltype.UnsignedLongLong]:
+ vlist = [varoftype(T1)]
+ v_result = varoftype(T2)
+ op = SpaceOperation('force_cast', vlist, v_result)
+ tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
+ op1 = tr.rewrite_operation(op)
+ assert op1 is None
def test_constants(self):
for TYPE in [lltype.SignedLongLong, lltype.UnsignedLongLong]:
diff --git a/pypy/jit/codewriter/test/test_policy.py b/pypy/jit/codewriter/test/test_policy.py
--- a/pypy/jit/codewriter/test/test_policy.py
+++ b/pypy/jit/codewriter/test/test_policy.py
@@ -45,8 +45,8 @@
policy.set_supports_floats(False)
assert not policy.look_inside_graph(graph)
-def test_purefunction():
- @jit.purefunction
+def test_elidable():
+ @jit.elidable
def g(x):
return x + 2
graph = support.getgraph(g, [5])
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
@@ -765,6 +765,7 @@
"""
short_preamble = None
failed_states = None
+ retraced_count = 0
terminating = False # see TerminatingLoopToken in compile.py
outermost_jitdriver_sd = None
# and more data specified by the backend when the loop is compiled
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
@@ -3,7 +3,7 @@
from pypy.jit.metainterp.optimizeopt.intbounds import OptIntBounds
from pypy.jit.metainterp.optimizeopt.virtualize import OptVirtualize
from pypy.jit.metainterp.optimizeopt.heap import OptHeap
-from pypy.jit.metainterp.optimizeopt.string import OptString
+from pypy.jit.metainterp.optimizeopt.vstring import OptString
from pypy.jit.metainterp.optimizeopt.unroll import optimize_unroll, OptInlineShortPreamble
from pypy.jit.metainterp.optimizeopt.fficall import OptFfiCall
from pypy.jit.metainterp.optimizeopt.simplify import OptSimplify
@@ -21,15 +21,14 @@
unroll_all_opts = unrolling_iterable(ALL_OPTS)
ALL_OPTS_DICT = dict.fromkeys([name for name, _ in ALL_OPTS])
-
+ALL_OPTS_LIST = [name for name, _ in ALL_OPTS]
ALL_OPTS_NAMES = ':'.join([name for name, _ in ALL_OPTS])
-PARAMETERS['enable_opts'] = ALL_OPTS_NAMES
def build_opt_chain(metainterp_sd, enable_opts,
inline_short_preamble=True, retraced=False):
config = metainterp_sd.config
optimizations = []
- unroll = 'unroll' in enable_opts
+ unroll = 'unroll' in enable_opts # 'enable_opts' is normally a dict
for name, opt in unroll_all_opts:
if name in enable_opts:
if opt is not None:
@@ -62,7 +61,6 @@
optimizations, unroll = build_opt_chain(metainterp_sd, enable_opts,
inline_short_preamble, retraced)
-
if unroll:
optimize_unroll(metainterp_sd, loop, optimizations)
else:
diff --git a/pypy/jit/metainterp/optimizeopt/fficall.py b/pypy/jit/metainterp/optimizeopt/fficall.py
--- a/pypy/jit/metainterp/optimizeopt/fficall.py
+++ b/pypy/jit/metainterp/optimizeopt/fficall.py
@@ -1,10 +1,10 @@
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_start, debug_stop, debug_print, have_debug_prints
+from pypy.rlib.debug import debug_start, debug_stop, debug_print
from pypy.jit.codewriter.effectinfo import EffectInfo
from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.jit.metainterp.optimizeopt.util import _findall
+from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
from pypy.jit.backend.llsupport.ffisupport import UnsupportedKind
@@ -48,7 +48,7 @@
inst_argtypes is actually a low-level array, but we can use it
directly since the only thing we do with it is to read its items
"""
-
+
llfunc = funcval.box.getref_base()
if we_are_translated():
func = cast_base_ptr_to_instance(Func, llfunc)
@@ -203,13 +203,7 @@
def propagate_forward(self, op):
if self.logops is not None:
debug_print(self.logops.repr_of_resop(op))
- opnum = op.getopnum()
- for value, func in optimize_ops:
- if opnum == value:
- func(self, op)
- break
- else:
- self.emit_operation(op)
+ dispatch_opt(self, op)
def _get_oopspec(self, op):
effectinfo = op.getdescr().get_extra_info()
@@ -220,4 +214,5 @@
def _get_funcval(self, op):
return self.getvalue(op.getarg(1))
-optimize_ops = _findall(OptFfiCall, 'optimize_')
+dispatch_opt = make_dispatcher_method(OptFfiCall, 'optimize_',
+ default=OptFfiCall.emit_operation)
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
@@ -1,9 +1,10 @@
import os
-from pypy.jit.metainterp.optimizeopt.util import _findall
-from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.rlib.objectmodel import we_are_translated
+
from pypy.jit.metainterp.jitexc import JitException
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
+from pypy.rlib.objectmodel import we_are_translated
class CachedField(object):
@@ -73,7 +74,7 @@
assert self._lazy_setfield is None
self._cached_fields[structvalue] = fieldvalue
- def force_lazy_setfield(self, optheap):
+ def force_lazy_setfield(self, optheap, can_cache=True):
op = self._lazy_setfield
if op is not None:
# This is the way _lazy_setfield is usually reset to None.
@@ -83,12 +84,16 @@
self._cached_fields.clear()
self._lazy_setfield = None
optheap.next_optimization.propagate_forward(op)
+ if not can_cache:
+ return
# Once it is done, we can put at least one piece of information
# back in the cache: the value of this particular structure's
# field.
structvalue = optheap.getvalue(op.getarg(0))
fieldvalue = optheap.getvalue(op.getarglist()[-1])
self.remember_field_value(structvalue, fieldvalue)
+ elif not can_cache:
+ self._cached_fields.clear()
def get_reconstructed(self, optimizer, valuemap):
assert self._lazy_setfield is None
@@ -202,20 +207,9 @@
for arraydescr in effectinfo.readonly_descrs_arrays:
self.force_lazy_setarrayitem(arraydescr)
for fielddescr in effectinfo.write_descrs_fields:
- self.force_lazy_setfield(fielddescr)
- try:
- cf = self.cached_fields[fielddescr]
- cf._cached_fields.clear()
- except KeyError:
- pass
+ self.force_lazy_setfield(fielddescr, can_cache=False)
for arraydescr in effectinfo.write_descrs_arrays:
- self.force_lazy_setarrayitem(arraydescr)
- try:
- submap = self.cached_arrayitems[arraydescr]
- for cf in submap.itervalues():
- cf._cached_fields.clear()
- except KeyError:
- pass
+ self.force_lazy_setarrayitem(arraydescr, can_cache=False)
if effectinfo.check_forces_virtual_or_virtualizable():
vrefinfo = self.optimizer.metainterp_sd.virtualref_info
self.force_lazy_setfield(vrefinfo.descr_forced)
@@ -238,20 +232,20 @@
if value in cf._cached_fields:
cf._cached_fields[newvalue] = cf._cached_fields[value]
- def force_lazy_setfield(self, descr):
+ def force_lazy_setfield(self, descr, can_cache=True):
try:
cf = self.cached_fields[descr]
except KeyError:
return
- cf.force_lazy_setfield(self)
+ cf.force_lazy_setfield(self, can_cache)
- def force_lazy_setarrayitem(self, arraydescr):
+ def force_lazy_setarrayitem(self, arraydescr, can_cache=True):
try:
submap = self.cached_arrayitems[arraydescr]
except KeyError:
return
for cf in submap.values():
- cf.force_lazy_setfield(self)
+ cf.force_lazy_setfield(self, can_cache)
def fixup_guard_situation(self):
# hackish: reverse the order of the last two operations if it makes
@@ -387,7 +381,7 @@
cf.do_setfield(self, op)
else:
# variable index, so make sure the lazy setarrayitems are done
- self.force_lazy_setarrayitem(op.getdescr())
+ self.force_lazy_setarrayitem(op.getdescr(), can_cache=False)
# and then emit the operation
self.emit_operation(op)
@@ -431,13 +425,7 @@
self._seen_guard_not_invalidated = True
self.emit_operation(op)
- def propagate_forward(self, op):
- opnum = op.getopnum()
- for value, func in optimize_ops:
- if opnum == value:
- func(self, op)
- break
- else:
- self.emit_operation(op)
-optimize_ops = _findall(OptHeap, 'optimize_')
+dispatch_opt = make_dispatcher_method(OptHeap, 'optimize_',
+ default=OptHeap.emit_operation)
+OptHeap.propagate_forward = dispatch_opt
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
@@ -1,9 +1,10 @@
+from pypy.jit.metainterp.history import ConstInt
+from pypy.jit.metainterp.optimizeopt.intutils import (IntBound, IntLowerBound,
+ IntUpperBound)
from pypy.jit.metainterp.optimizeopt.optimizer import Optimization, CONST_1, CONST_0
-from pypy.jit.metainterp.optimizeopt.util import _findall
-from pypy.jit.metainterp.optimizeopt.intutils import (IntBound, IntUnbounded,
- IntLowerBound, IntUpperBound)
-from pypy.jit.metainterp.history import Const, ConstInt
-from pypy.jit.metainterp.resoperation import rop, ResOperation
+from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
+from pypy.jit.metainterp.resoperation import rop
+
class OptIntBounds(Optimization):
"""Keeps track of the bounds placed on integers by guards and remove
@@ -34,14 +35,11 @@
op = self.posponedop
self.posponedop = None
- opnum = op.getopnum()
- for value, func in optimize_ops:
- if opnum == value:
- func(self, op)
- break
- else:
- assert not op.is_ovf()
- self.emit_operation(op)
+ dispatch_opt(self, op)
+
+ def opt_default(self, op):
+ assert not op.is_ovf()
+ self.emit_operation(op)
def propagate_bounds_backward(self, box):
@@ -57,11 +55,7 @@
op = self.optimizer.producer[box]
except KeyError:
return
- opnum = op.getopnum()
- for value, func in propagate_bounds_ops:
- if opnum == value:
- func(self, op)
- break
+ dispatch_bounds_ops(self, op)
def optimize_GUARD_TRUE(self, op):
self.emit_operation(op)
@@ -382,6 +376,18 @@
v1.intbound.make_gt(IntBound(0, 0))
self.propagate_bounds_backward(op.getarg(0))
+ def propagate_bounds_INT_IS_ZERO(self, op):
+ r = self.getvalue(op.result)
+ if r.is_constant():
+ if r.box.same_constant(CONST_1):
+ v1 = self.getvalue(op.getarg(0))
+ # Clever hack, we can't use self.make_constant_int yet because
+ # the args aren't in the values dictionary yet so it runs into
+ # an assert, this is a clever way of expressing the same thing.
+ v1.intbound.make_ge(IntBound(0, 0))
+ v1.intbound.make_lt(IntBound(1, 1))
+ self.propagate_bounds_backward(op.getarg(0))
+
def propagate_bounds_INT_ADD(self, op):
v1 = self.getvalue(op.getarg(0))
v2 = self.getvalue(op.getarg(1))
@@ -428,5 +434,6 @@
propagate_bounds_INT_MUL_OVF = propagate_bounds_INT_MUL
-optimize_ops = _findall(OptIntBounds, 'optimize_')
-propagate_bounds_ops = _findall(OptIntBounds, 'propagate_bounds_')
+dispatch_opt = make_dispatcher_method(OptIntBounds, 'optimize_',
+ default=OptIntBounds.opt_default)
+dispatch_bounds_ops = make_dispatcher_method(OptIntBounds, 'propagate_bounds_')
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
@@ -1,17 +1,11 @@
-from pypy.jit.metainterp.history import Box, BoxInt, LoopToken, BoxFloat,\
- ConstFloat
-from pypy.jit.metainterp.history import Const, ConstInt, ConstPtr, ConstObj, REF
+from pypy.jit.metainterp import jitprof, resume, compile
+from pypy.jit.metainterp.executor import execute_nonspec
+from pypy.jit.metainterp.history import BoxInt, BoxFloat, Const, ConstInt, REF
+from pypy.jit.metainterp.optimizeopt.intutils import IntBound, IntUnbounded
+from pypy.jit.metainterp.optimizeopt.util import (make_dispatcher_method,
+ args_dict)
from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.jit.metainterp import jitprof
-from pypy.jit.metainterp.executor import execute_nonspec
-from pypy.jit.metainterp.optimizeopt.util import _findall, sort_descrs
-from pypy.jit.metainterp.optimizeopt.util import descrlist_dict, args_dict
-from pypy.jit.metainterp.optimize import InvalidLoop
-from pypy.jit.metainterp import resume, compile
from pypy.jit.metainterp.typesystem import llhelper, oohelper
-from pypy.rpython.lltypesystem import lltype
-from pypy.jit.metainterp.history import AbstractDescr, make_hashable_int
-from pypy.jit.metainterp.optimizeopt.intutils import IntBound, IntUnbounded
from pypy.tool.pairtype import extendabletype
LEVEL_UNKNOWN = '\x00'
@@ -434,14 +428,7 @@
def propagate_forward(self, op):
self.producer[op.result] = op
- opnum = op.getopnum()
- for value, func in optimize_ops:
- if opnum == value:
- func(self, op)
- break
- else:
- self.optimize_default(op)
- #print '\n'.join([str(o) for o in self.newoperations]) + '\n---\n'
+ dispatch_opt(self, op)
def test_emittable(self, op):
return True
@@ -569,7 +556,8 @@
def optimize_DEBUG_MERGE_POINT(self, op):
self.emit_operation(op)
-optimize_ops = _findall(Optimizer, 'optimize_')
+dispatch_opt = make_dispatcher_method(Optimizer, 'optimize_',
+ default=Optimizer.optimize_default)
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
@@ -1,10 +1,11 @@
+from pypy.jit.codewriter.effectinfo import EffectInfo
+from pypy.jit.metainterp.history import ConstInt, make_hashable_int
+from pypy.jit.metainterp.optimize import InvalidLoop
+from pypy.jit.metainterp.optimizeopt.intutils import IntBound
from pypy.jit.metainterp.optimizeopt.optimizer import *
-from pypy.jit.metainterp.resoperation import opboolinvers, opboolreflex
-from pypy.jit.metainterp.history import ConstInt
-from pypy.jit.metainterp.optimizeopt.util import _findall
-from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.jit.codewriter.effectinfo import EffectInfo
-from pypy.jit.metainterp.optimizeopt.intutils import IntBound
+from pypy.jit.metainterp.optimizeopt.util import _findall, make_dispatcher_method
+from pypy.jit.metainterp.resoperation import (opboolinvers, opboolreflex, rop,
+ ResOperation)
from pypy.rlib.rarithmetic import highest_bit
@@ -21,18 +22,13 @@
if self.find_rewritable_bool(op, args):
return
- opnum = op.getopnum()
- for value, func in optimize_ops:
- if opnum == value:
- func(self, op)
- break
- else:
- self.emit_operation(op)
+ dispatch_opt(self, op)
def test_emittable(self, op):
opnum = op.getopnum()
- for value, func in optimize_guards:
+ for value, cls, func in optimize_guards:
if opnum == value:
+ assert isinstance(op, cls)
try:
func(self, op, dryrun=True)
return self.is_emittable(op)
@@ -204,6 +200,7 @@
))
return
self.emit_operation(op)
+ self.pure(rop.FLOAT_MUL, [arg2, arg1], op.result)
def optimize_FLOAT_NEG(self, op):
v1 = op.getarg(0)
@@ -477,5 +474,6 @@
self.emit_operation(op)
-optimize_ops = _findall(OptRewrite, 'optimize_')
+dispatch_opt = make_dispatcher_method(OptRewrite, 'optimize_',
+ default=OptRewrite.emit_operation)
optimize_guards = _findall(OptRewrite, 'optimize_', 'GUARD')
diff --git a/pypy/jit/metainterp/optimizeopt/simplify.py b/pypy/jit/metainterp/optimizeopt/simplify.py
--- a/pypy/jit/metainterp/optimizeopt/simplify.py
+++ b/pypy/jit/metainterp/optimizeopt/simplify.py
@@ -1,7 +1,7 @@
+from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
+from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
+from pypy.jit.metainterp.resoperation import ResOperation, rop
-from pypy.jit.metainterp.resoperation import ResOperation, rop
-from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
-from pypy.jit.metainterp.optimizeopt.util import _findall
class OptSimplify(Optimization):
def optimize_CALL_PURE(self, op):
@@ -25,13 +25,7 @@
# but it's a bit hard to implement robustly if heap.py is also run
pass
- def propagate_forward(self, op):
- opnum = op.getopnum()
- for value, func in optimize_ops:
- if opnum == value:
- func(self, op)
- break
- else:
- self.emit_operation(op)
-optimize_ops = _findall(OptSimplify, 'optimize_')
+dispatch_opt = make_dispatcher_method(OptSimplify, 'optimize_',
+ default=OptSimplify.emit_operation)
+OptSimplify.propagate_forward = dispatch_opt
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
@@ -439,6 +439,23 @@
"""
self.optimize_loop(ops, expected)
+ def test_int_is_zero_int_is_true(self):
+ ops = """
+ [i0]
+ i1 = int_is_zero(i0)
+ guard_true(i1) []
+ i2 = int_is_true(i0)
+ guard_false(i2) []
+ jump(i0)
+ """
+ expected = """
+ [i0]
+ i1 = int_is_zero(i0)
+ guard_true(i1) []
+ jump(0)
+ """
+ self.optimize_loop(ops, expected)
+
def test_ooisnull_oononnull_2(self):
ops = """
[p0]
@@ -1738,6 +1755,48 @@
"""
self.optimize_loop(ops, expected)
+ def test_duplicate_getarrayitem_after_setarrayitem_bug(self):
+ ops = """
+ [p0, i0, i1]
+ setarrayitem_gc(p0, 0, i0, descr=arraydescr)
+ i6 = int_add(i0, 1)
+ setarrayitem_gc(p0, i1, i6, descr=arraydescr)
+ i10 = getarrayitem_gc(p0, 0, descr=arraydescr)
+ i11 = int_add(i10, i0)
+ jump(p0, i11, i1)
+ """
+ expected = """
+ [p0, i0, i1]
+ i6 = int_add(i0, 1)
+ setarrayitem_gc(p0, 0, i0, descr=arraydescr)
+ setarrayitem_gc(p0, i1, i6, descr=arraydescr)
+ i10 = getarrayitem_gc(p0, 0, descr=arraydescr)
+ i11 = int_add(i10, i0)
+ jump(p0, i11, i1)
+ """
+ self.optimize_loop(ops, expected)
+
+ def test_duplicate_getarrayitem_after_setarrayitem_bug2(self):
+ ops = """
+ [p0, i0, i1]
+ i2 = getarrayitem_gc(p0, 0, descr=arraydescr)
+ i6 = int_add(i0, 1)
+ setarrayitem_gc(p0, i1, i6, descr=arraydescr)
+ i10 = getarrayitem_gc(p0, 0, descr=arraydescr)
+ i11 = int_add(i10, i2)
+ jump(p0, i11, i1)
+ """
+ expected = """
+ [p0, i0, i1]
+ i2 = getarrayitem_gc(p0, 0, descr=arraydescr)
+ i6 = int_add(i0, 1)
+ setarrayitem_gc(p0, i1, i6, descr=arraydescr)
+ i10 = getarrayitem_gc(p0, 0, descr=arraydescr)
+ i11 = int_add(i10, i2)
+ jump(p0, i11, i1)
+ """
+ self.optimize_loop(ops, expected)
+
def test_bug_1(self):
ops = """
[i0, p1]
@@ -3899,11 +3958,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p3 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p3, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p3, 0, i4, i5)
+ copystrcontent(p1, p3, 0, 0, i1)
+ copystrcontent(p2, p3, 0, i1, i2)
jump(p2, p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -3924,9 +3980,7 @@
p3 = newstr(i3)
strsetitem(p3, 0, i0)
strsetitem(p3, 1, i1)
- i4 = strlen(p2)
- i5 = int_add(2, i4) # will be killed by the backend
- copystrcontent(p2, p3, 0, 2, i4)
+ copystrcontent(p2, p3, 0, 2, i2)
jump(i1, i0, p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -3945,10 +3999,9 @@
i2 = strlen(p2)
i3 = int_add(i2, 2)
p3 = newstr(i3)
- i4 = strlen(p2)
- copystrcontent(p2, p3, 0, 0, i4)
- strsetitem(p3, i4, i0)
- i5 = int_add(i4, 1)
+ copystrcontent(p2, p3, 0, 0, i2)
+ strsetitem(p3, i2, i0)
+ i5 = int_add(i2, 1)
strsetitem(p3, i5, i1)
i6 = int_add(i5, 1) # will be killed by the backend
jump(i1, i0, p3)
@@ -3970,14 +4023,9 @@
i3 = strlen(p3)
i123 = int_add(i12, i3)
p5 = newstr(i123)
- i1b = strlen(p1)
- copystrcontent(p1, p5, 0, 0, i1b)
- i2b = strlen(p2)
- i12b = int_add(i1b, i2b)
- copystrcontent(p2, p5, 0, i1b, i2b)
- i3b = strlen(p3)
- i123b = int_add(i12b, i3b) # will be killed by the backend
- copystrcontent(p3, p5, 0, i12b, i3b)
+ copystrcontent(p1, p5, 0, 0, i1)
+ copystrcontent(p2, p5, 0, i1, i2)
+ copystrcontent(p3, p5, 0, i12, i3)
jump(p2, p3, p5)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -3993,10 +4041,8 @@
i2 = strlen(p2)
i3 = int_add(i2, 1)
p3 = newstr(i3)
- i4 = strlen(p2)
- copystrcontent(p2, p3, 0, 0, i4)
- strsetitem(p3, i4, 120) # == ord('x')
- i5 = int_add(i4, 1) # will be killed by the backend
+ copystrcontent(p2, p3, 0, 0, i2)
+ strsetitem(p3, i2, 120) # == ord('x')
jump(p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -4114,16 +4160,13 @@
i5 = int_add(i3, i4)
p4 = newstr(i5)
copystrcontent(p1, p4, i1, 0, i3)
- i4b = strlen(p2)
- i6 = int_add(i3, i4b) # killed by the backend
- copystrcontent(p2, p4, 0, i3, i4b)
+ copystrcontent(p2, p4, 0, i3, i4)
jump(p4, i1, i2, p2)
"""
self.optimize_strunicode_loop(ops, expected)
# ----------
def optimize_strunicode_loop_extradescrs(self, ops, optops):
- from pypy.jit.metainterp.optimizeopt import string
class FakeCallInfoCollection:
def callinfo_for_oopspec(self, oopspecindex):
calldescrtype = type(LLtypeMixin.strequaldescr)
@@ -4162,11 +4205,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p4 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p4, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p4, 0, i4, i5)
+ copystrcontent(p1, p4, 0, 0, i1)
+ copystrcontent(p2, p4, 0, i1, i2)
i0 = call(0, p3, p4, descr=strequaldescr)
escape(i0)
jump(p1, p2, p3)
@@ -4358,11 +4398,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p4 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p4, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p4, 0, i4, i5)
+ copystrcontent(p1, p4, 0, 0, i1)
+ copystrcontent(p2, p4, 0, i1, i2)
i0 = call(0, s"hello world", p4, descr=streq_nonnull_descr)
escape(i0)
jump(p1, p2)
@@ -4495,7 +4532,7 @@
escape(i1)
jump(p0, i0)
"""
- self.optimize_loop(ops, expected)
+ self.optimize_strunicode_loop(ops, expected)
def test_int_is_true_bounds(self):
ops = """
@@ -4514,8 +4551,77 @@
guard_true(i1) []
jump(p0)
"""
+ self.optimize_strunicode_loop(ops, expected)
+
+ def test_strslice_subtraction_folds(self):
+ ops = """
+ [p0, i0]
+ i1 = int_add(i0, 1)
+ p1 = call(0, p0, i0, i1, descr=strslicedescr)
+ escape(p1)
+ jump(p0, i1)
+ """
+ expected = """
+ [p0, i0]
+ i1 = int_add(i0, 1)
+ p1 = newstr(1)
+ i2 = strgetitem(p0, i0)
+ strsetitem(p1, 0, i2)
+ escape(p1)
+ jump(p0, i1)
+ """
+ self.optimize_strunicode_loop(ops, expected)
+
+ def test_float_mul_reversed(self):
+ ops = """
+ [f0, f1]
+ f2 = float_mul(f0, f1)
+ f3 = float_mul(f1, f0)
+ jump(f2, f3)
+ """
+ expected = """
+ [f0, f1]
+ f2 = float_mul(f0, f1)
+ jump(f2, f2)
+ """
self.optimize_loop(ops, expected)
+ def test_null_char_str(self):
+ ops = """
+ [p0]
+ p1 = newstr(4)
+ setfield_gc(p0, p1, descr=valuedescr)
+ jump(p0)
+ """
+ # It used to be the case that this would have a series of
+ # strsetitem(p1, idx, 0), which was silly because memory is 0 filled
+ # when allocated.
+ expected = """
+ [p0]
+ p1 = newstr(4)
+ setfield_gc(p0, p1, descr=valuedescr)
+ jump(p0)
+ """
+ self.optimize_strunicode_loop(ops, expected)
+
+ def test_newstr_strlen(self):
+ ops = """
+ [i0]
+ p0 = newstr(i0)
+ escape(p0)
+ i1 = strlen(p0)
+ i2 = int_add(i1, 1)
+ jump(i2)
+ """
+ expected = """
+ [i0]
+ p0 = newstr(i0)
+ escape(p0)
+ i1 = int_add(i0, 1)
+ jump(i1)
+ """
+ self.optimize_strunicode_loop(ops, expected)
+
class TestLLtype(BaseTestOptimizeBasic, LLtypeMixin):
pass
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -2820,11 +2820,11 @@
def test_residual_call_invalidate_some_arrays(self):
ops = """
[p1, p2, i1]
- p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p3 = getarrayitem_gc(p2, 0, descr=arraydescr2)
p4 = getarrayitem_gc(p2, 1, descr=arraydescr2)
i2 = getarrayitem_gc(p1, 1, descr=arraydescr)
i3 = call(i1, descr=writearraydescr)
- p5 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p5 = getarrayitem_gc(p2, 0, descr=arraydescr2)
p6 = getarrayitem_gc(p2, 1, descr=arraydescr2)
i4 = getarrayitem_gc(p1, 1, descr=arraydescr)
escape(p3)
@@ -2837,7 +2837,7 @@
"""
expected = """
[p1, p2, i1]
- p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p3 = getarrayitem_gc(p2, 0, descr=arraydescr2)
p4 = getarrayitem_gc(p2, 1, descr=arraydescr2)
i2 = getarrayitem_gc(p1, 1, descr=arraydescr)
i3 = call(i1, descr=writearraydescr)
@@ -5082,11 +5082,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p3 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p3, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p3, 0, i4, i5)
+ copystrcontent(p1, p3, 0, 0, i1)
+ copystrcontent(p2, p3, 0, i1, i2)
jump(p2, p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5107,9 +5104,7 @@
p3 = newstr(i3)
strsetitem(p3, 0, i0)
strsetitem(p3, 1, i1)
- i4 = strlen(p2)
- i5 = int_add(2, i4) # will be killed by the backend
- copystrcontent(p2, p3, 0, 2, i4)
+ copystrcontent(p2, p3, 0, 2, i2)
jump(i1, i0, p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5128,10 +5123,9 @@
i2 = strlen(p2)
i3 = int_add(i2, 2)
p3 = newstr(i3)
- i4 = strlen(p2)
- copystrcontent(p2, p3, 0, 0, i4)
- strsetitem(p3, i4, i0)
- i5 = int_add(i4, 1)
+ copystrcontent(p2, p3, 0, 0, i2)
+ strsetitem(p3, i2, i0)
+ i5 = int_add(i2, 1)
strsetitem(p3, i5, i1)
i6 = int_add(i5, 1) # will be killed by the backend
jump(i1, i0, p3)
@@ -5153,14 +5147,9 @@
i3 = strlen(p3)
i123 = int_add(i12, i3)
p5 = newstr(i123)
- i1b = strlen(p1)
- copystrcontent(p1, p5, 0, 0, i1b)
- i2b = strlen(p2)
- i12b = int_add(i1b, i2b)
- copystrcontent(p2, p5, 0, i1b, i2b)
- i3b = strlen(p3)
- i123b = int_add(i12b, i3b) # will be killed by the backend
- copystrcontent(p3, p5, 0, i12b, i3b)
+ copystrcontent(p1, p5, 0, 0, i1)
+ copystrcontent(p2, p5, 0, i1, i2)
+ copystrcontent(p3, p5, 0, i12, i3)
jump(p2, p3, p5)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5176,10 +5165,8 @@
i2 = strlen(p2)
i3 = int_add(i2, 1)
p3 = newstr(i3)
- i4 = strlen(p2)
- copystrcontent(p2, p3, 0, 0, i4)
- strsetitem(p3, i4, 120) # == ord('x')
- i5 = int_add(i4, 1) # will be killed by the backend
+ copystrcontent(p2, p3, 0, 0, i2)
+ strsetitem(p3, i2, 120) # == ord('x')
jump(p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5303,9 +5290,7 @@
i5 = int_add(i3, i4)
p4 = newstr(i5)
copystrcontent(p1, p4, i1, 0, i3)
- i4b = strlen(p2)
- i6 = int_add(i3, i4b) # killed by the backend
- copystrcontent(p2, p4, 0, i3, i4b)
+ copystrcontent(p2, p4, 0, i3, i4)
jump(p4, i1, i2, p2)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5373,7 +5358,6 @@
# ----------
def optimize_strunicode_loop_extradescrs(self, ops, optops, preamble=None):
- from pypy.jit.metainterp.optimizeopt import string
class FakeCallInfoCollection:
def callinfo_for_oopspec(self, oopspecindex):
calldescrtype = type(LLtypeMixin.strequaldescr)
@@ -5412,11 +5396,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p4 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p4, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p4, 0, i4, i5)
+ copystrcontent(p1, p4, 0, 0, i1)
+ copystrcontent(p2, p4, 0, i1, i2)
i0 = call(0, p3, p4, descr=strequaldescr)
escape(i0)
jump(p1, p2, p3)
@@ -5610,11 +5591,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p4 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p4, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p4, 0, i4, i5)
+ copystrcontent(p1, p4, 0, 0, i1)
+ copystrcontent(p2, p4, 0, i1, i2)
i0 = call(0, s"hello world", p4, descr=streq_nonnull_descr)
escape(i0)
jump(p1, p2)
diff --git a/pypy/jit/metainterp/optimizeopt/unroll.py b/pypy/jit/metainterp/optimizeopt/unroll.py
--- a/pypy/jit/metainterp/optimizeopt/unroll.py
+++ b/pypy/jit/metainterp/optimizeopt/unroll.py
@@ -1,14 +1,12 @@
+from pypy.jit.codewriter.effectinfo import EffectInfo
+from pypy.jit.metainterp.compile import ResumeGuardDescr
+from pypy.jit.metainterp.history import TreeLoop, LoopToken
+from pypy.jit.metainterp.jitexc import JitException
+from pypy.jit.metainterp.optimize import InvalidLoop, RetraceLoop
from pypy.jit.metainterp.optimizeopt.optimizer import *
-from pypy.jit.metainterp.optimizeopt.virtualize import AbstractVirtualValue
from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.jit.metainterp.compile import ResumeGuardDescr
from pypy.jit.metainterp.resume import Snapshot
-from pypy.jit.metainterp.history import TreeLoop, LoopToken
-from pypy.rlib.debug import debug_start, debug_stop, debug_print
-from pypy.jit.metainterp.optimize import InvalidLoop, RetraceLoop
-from pypy.jit.metainterp.jitexc import JitException
-from pypy.jit.metainterp.history import make_hashable_int
-from pypy.jit.codewriter.effectinfo import EffectInfo
+from pypy.rlib.debug import debug_print
# Assumptions
# ===========
@@ -22,7 +20,7 @@
# are also recreated to allow virtuals not supported to be forced.
#
# First of all, the optimizations are not allowed to introduce new
-# boxes. It is the unoptimized version of the trace that is inlined to
+# boxes. It is the unoptimized version of the trace that is inlined to
# form the second iteration of the loop. Otherwise the
# state of the virtuals would not be updated correctly. Whenever some
# box from the first iteration is reused in the second iteration, it
@@ -57,7 +55,7 @@
# be absorbed into the virtual p2 and never seen by the heap
# optimizer. At the end of the loop both p2 and p3 are virtuals, but
# the loop needs p2 to be a pointer to be able to call itself. So it
-# is forced producing the operations
+# is forced producing the operations
#
# p2 = new_with_vtable(ConstClass(node_vtable))
# setfield_gc(p2, i2, descr=nextdescr)
@@ -68,7 +66,7 @@
# the trace were optimized under the wrong assumption that the
# setfield_gc was store sinked which could lead to errors. In this
# case what would happen is that it would be inserted once more in
-# front of the guard.
+# front of the guard.
@@ -112,7 +110,7 @@
def inline_descr_inplace(self, descr):
if isinstance(descr, ResumeGuardDescr):
descr.rd_snapshot = self.inline_snapshot(descr.rd_snapshot)
-
+
def inline_arg(self, arg):
if arg is None:
return None
@@ -139,7 +137,7 @@
return False
return True
- def generate_guards(self, other, args, cpu, extra_guards):
+ def generate_guards(self, other, args, cpu, extra_guards):
assert len(self.state) == len(other.state) == len(args)
for i in range(len(self.state)):
self.state[i].generate_guards(other.state[i], args[i],
@@ -153,7 +151,7 @@
def register_virtual_fields(self, keybox, fieldboxes):
self.fieldboxes[keybox] = fieldboxes
-
+
def already_seen_virtual(self, keybox):
return keybox in self.fieldboxes
@@ -233,20 +231,20 @@
if self.level == LEVEL_CONSTANT:
import pdb; pdb.set_trace()
raise NotImplementedError
-
+
class UnrollOptimizer(Optimization):
"""Unroll the loop into two iterations. The first one will
become the preamble or entry bridge (don't think there is a
distinction anymore)"""
-
+
def __init__(self, metainterp_sd, loop, optimizations):
self.optimizer = Optimizer(metainterp_sd, loop, optimizations)
self.cloned_operations = []
for op in self.optimizer.loop.operations:
newop = op.clone()
self.cloned_operations.append(newop)
-
+
def propagate_all_forward(self):
loop = self.optimizer.loop
jumpop = loop.operations[-1]
@@ -284,7 +282,7 @@
assert isinstance(start_resumedescr, ResumeGuardDescr)
snapshot = start_resumedescr.rd_snapshot
while snapshot is not None:
- snapshot_args = snapshot.boxes
+ snapshot_args = snapshot.boxes
new_snapshot_args = []
for a in snapshot_args:
if not isinstance(a, Const):
@@ -313,7 +311,7 @@
short_loop.inputargs = loop.preamble.inputargs[:]
short_loop.operations = short
- # Clone ops and boxes to get private versions and
+ # Clone ops and boxes to get private versions and
newargs = [a.clonebox() for a in short_loop.inputargs]
inliner = Inliner(short_loop.inputargs, newargs)
short_loop.inputargs = newargs
@@ -336,10 +334,10 @@
for op in short_loop.operations:
if op.result:
op.result.forget_value()
-
+
def inline(self, loop_operations, loop_args, jump_args):
self.inliner = inliner = Inliner(loop_args, jump_args)
-
+
for v in self.optimizer.values.values():
v.last_guard_index = -1 # FIXME: Are there any more indexes stored?
@@ -371,12 +369,12 @@
jumpargs = jmp.getarglist()
# FIXME: Should also loop over operations added by forcing things in this loop
- for op in newoperations:
+ for op in newoperations:
boxes_created_this_iteration[op.result] = True
args = op.getarglist()
if op.is_guard():
args = args + op.getfailargs()
-
+
for a in args:
if not isinstance(a, Const) and not a in boxes_created_this_iteration:
if a not in inputargs:
@@ -439,7 +437,7 @@
"at preamble position: ", preamble_i,
"loop position: ", loop_i)
return None
-
+
if self.sameop(newop, loop_ops[loop_i]) \
and loop_i < len(loop_ops):
try:
@@ -460,7 +458,7 @@
"loop position: ", loop_i)
return None
short_preamble.append(op)
-
+
state.update(op)
preamble_i += 1
@@ -470,7 +468,7 @@
"at position", loop_i)
return None
-
+
jumpargs = []
for i in range(len(loop.inputargs)):
try:
@@ -498,7 +496,7 @@
return None
if op.result:
seen[op.result] = True
-
+
return short_preamble
class ExeState(object):
@@ -508,7 +506,7 @@
self.unsafe_getitem = {}
self.unsafe_getarrayitem = {}
self.unsafe_getarrayitem_indexes = {}
-
+
# Make sure it is safe to move the instrucions in short_preamble
# to the top making short_preamble followed by loop equvivalent
# to preamble
@@ -546,14 +544,14 @@
effectinfo = descr.get_extra_info()
if effectinfo is not None:
if effectinfo.extraeffect == EffectInfo.EF_LOOPINVARIANT or \
- effectinfo.extraeffect == EffectInfo.EF_PURE:
+ effectinfo.extraeffect == EffectInfo.EF_ELIDABLE:
return True
return False
-
+
def update(self, op):
if (op.has_no_side_effect() or
op.is_ovf() or
- op.is_guard()):
+ op.is_guard()):
return
opnum = op.getopnum()
descr = op.getdescr()
@@ -566,7 +564,7 @@
if (opnum == rop.SETARRAYITEM_GC or
opnum == rop.SETARRAYITEM_RAW):
index = op.getarg(1)
- if isinstance(index, Const):
+ if isinstance(index, Const):
d = self.unsafe_getarrayitem_indexes.get(descr, None)
if d is None:
d = self.unsafe_getarrayitem_indexes[descr] = {}
@@ -592,7 +590,7 @@
def __init__(self):
self.map = {}
-
+
def link_ops(self, preambleop, loopop):
pargs = preambleop.getarglist()
largs = loopop.getarglist()
@@ -606,7 +604,7 @@
if not loopop.result:
raise ImpossibleLink
self.link_boxes(preambleop.result, loopop.result)
-
+
def link_boxes(self, pbox, lbox):
if lbox in self.map:
@@ -627,11 +625,11 @@
def __init__(self, retraced):
self.retraced = retraced
self.inliner = None
-
-
+
+
def reconstruct_for_next_iteration(self, optimizer, valuemap):
return self
-
+
def propagate_forward(self, op):
if op.getopnum() == rop.JUMP:
descr = op.getdescr()
@@ -657,7 +655,7 @@
sh.virtual_state.generate_guards(virtual_state,
args, cpu,
extra_guards)
-
+
ok = True
except InvalidLoop:
pass
@@ -676,24 +674,28 @@
jumpop = self.optimizer.newoperations.pop()
assert jumpop.getopnum() == rop.JUMP
for guard in extra_guards:
- descr = sh.start_resumedescr.clone_if_mutable()
- self.inliner.inline_descr_inplace(descr)
- guard.setdescr(descr)
+ d = sh.start_resumedescr.clone_if_mutable()
+ self.inliner.inline_descr_inplace(d)
+ guard.setdescr(d)
self.emit_operation(guard)
self.optimizer.newoperations.append(jumpop)
return
- retraced_count = len(short)
- if descr.failed_states:
- retraced_count += len(descr.failed_states)
+ retraced_count = descr.retraced_count
+ descr.retraced_count += 1
limit = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.retrace_limit
if not self.retraced and retraced_count<limit:
if not descr.failed_states:
+ debug_print("Retracing (%d of %d)" % (retraced_count,
+ limit))
raise RetraceLoop
for failed in descr.failed_states:
if failed.generalization_of(virtual_state):
# Retracing once more will most likely fail again
break
else:
+ debug_print("Retracing (%d of %d)" % (retraced_count,
+ limit))
+
raise RetraceLoop
else:
if not descr.failed_states:
@@ -701,21 +703,21 @@
else:
descr.failed_states.append(virtual_state)
self.emit_operation(op)
-
-
-
+
+
+
def inline(self, loop_operations, loop_args, jump_args, dryrun=False):
self.inliner = inliner = Inliner(loop_args, jump_args)
for op in loop_operations:
newop = inliner.inline_op(op)
-
+
if not dryrun:
self.emit_operation(newop)
else:
if not self.is_emittable(newop):
return False
-
+
return True
#def inline_arg(self, arg):
diff --git a/pypy/jit/metainterp/optimizeopt/util.py b/pypy/jit/metainterp/optimizeopt/util.py
--- a/pypy/jit/metainterp/optimizeopt/util.py
+++ b/pypy/jit/metainterp/optimizeopt/util.py
@@ -20,9 +20,25 @@
if op_prefix and not name.startswith(op_prefix):
continue
if hasattr(Class, name_prefix + name):
- result.append((value, getattr(Class, name_prefix + name)))
+ opclass = resoperation.opclasses[getattr(rop, name)]
+ assert name in opclass.__name__
+ result.append((value, opclass, getattr(Class, name_prefix + name)))
return unrolling_iterable(result)
+def make_dispatcher_method(Class, name_prefix, op_prefix=None, default=None):
+ ops = _findall(Class, name_prefix, op_prefix)
+ def dispatch(self, op, *args):
+ opnum = op.getopnum()
+ for value, cls, func in ops:
+ if opnum == value:
+ assert isinstance(op, cls)
+ return func(self, op, *args)
+ if default:
+ return default(self, op, *args)
+ dispatch.func_name = "dispatch_" + name_prefix
+ return dispatch
+
+
def partition(array, left, right):
last_item = array[right]
pivot = last_item.sort_key()
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
@@ -1,11 +1,11 @@
+from pypy.jit.codewriter.heaptracker import vtable2descr
+from pypy.jit.metainterp.executor import execute
from pypy.jit.metainterp.history import Const, ConstInt, BoxInt
+from pypy.jit.metainterp.optimizeopt import optimizer
+from pypy.jit.metainterp.optimizeopt.util import (make_dispatcher_method,
+ descrlist_dict, sort_descrs)
from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.jit.metainterp.optimizeopt.util import _findall, sort_descrs
-from pypy.jit.metainterp.optimizeopt.util import descrlist_dict
from pypy.rlib.objectmodel import we_are_translated
-from pypy.jit.metainterp.optimizeopt import optimizer
-from pypy.jit.metainterp.executor import execute
-from pypy.jit.codewriter.heaptracker import vtable2descr
class AbstractVirtualValue(optimizer.OptValue):
@@ -456,13 +456,8 @@
###self.heap_op_optimizer.optimize_SETARRAYITEM_GC(op, value, fieldvalue)
self.emit_operation(op)
- def propagate_forward(self, op):
- opnum = op.getopnum()
- for value, func in optimize_ops:
- if opnum == value:
- func(self, op)
- break
- else:
- self.emit_operation(op)
-optimize_ops = _findall(OptVirtualize, 'optimize_')
+dispatch_opt = make_dispatcher_method(OptVirtualize, 'optimize_',
+ default=OptVirtualize.emit_operation)
+
+OptVirtualize.propagate_forward = dispatch_opt
diff --git a/pypy/jit/metainterp/optimizeopt/string.py b/pypy/jit/metainterp/optimizeopt/vstring.py
rename from pypy/jit/metainterp/optimizeopt/string.py
rename to pypy/jit/metainterp/optimizeopt/vstring.py
--- a/pypy/jit/metainterp/optimizeopt/string.py
+++ b/pypy/jit/metainterp/optimizeopt/vstring.py
@@ -1,18 +1,14 @@
-from pypy.rpython.lltypesystem import lltype, rstr, llmemory
+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)
+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.util import make_dispatcher_method
+from pypy.jit.metainterp.resoperation import rop, ResOperation
+from pypy.rlib.objectmodel import specialize, we_are_translated
+from pypy.rlib.unroll import unrolling_iterable
from pypy.rpython import annlowlevel
-from pypy.jit.metainterp.history import Box, BoxInt, BoxPtr
-from pypy.jit.metainterp.history import Const, ConstInt, ConstPtr
-from pypy.jit.metainterp.history import get_const_ptr_for_string
-from pypy.jit.metainterp.history import get_const_ptr_for_unicode
-from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.jit.metainterp.optimizeopt import optimizer, virtualize
-from pypy.jit.metainterp.optimizeopt.optimizer import CONST_0, CONST_1
-from pypy.jit.metainterp.optimizeopt.optimizer import llhelper
-from pypy.jit.metainterp.optimizeopt.util import _findall
-from pypy.jit.codewriter.effectinfo import EffectInfo
-from pypy.jit.codewriter import heaptracker
-from pypy.rlib.unroll import unrolling_iterable
-from pypy.rlib.objectmodel import specialize, we_are_translated
+from pypy.rpython.lltypesystem import lltype, rstr
class StrOrUnicode(object):
@@ -61,7 +57,7 @@
self.ensure_nonnull()
box = self.force_box()
lengthbox = BoxInt()
- optimization.emit_operation(ResOperation(mode.STRLEN, [box], lengthbox))
+ optimization.optimize_default(ResOperation(mode.STRLEN, [box], lengthbox))
return lengthbox
@specialize.arg(1)
@@ -72,13 +68,13 @@
else:
return None
- def string_copy_parts(self, optimization, targetbox, offsetbox, mode):
+ def string_copy_parts(self, optimizer, targetbox, offsetbox, mode):
# Copies the pointer-to-string 'self' into the target string
# given by 'targetbox', at the specified offset. Returns the offset
# at the end of the copy.
- lengthbox = self.getstrlen(optimization, mode)
+ lengthbox = self.getstrlen(optimizer, mode)
srcbox = self.force_box()
- return copy_str_content(optimization, srcbox, targetbox,
+ return copy_str_content(optimizer, srcbox, targetbox,
CONST_0, offsetbox, lengthbox, mode)
@@ -147,10 +143,11 @@
def string_copy_parts(self, optimizer, targetbox, offsetbox, mode):
for i in range(len(self._chars)):
charbox = self._chars[i].force_box()
- optimizer.emit_operation(ResOperation(mode.STRSETITEM, [targetbox,
- offsetbox,
- charbox],
- None))
+ if not (isinstance(charbox, Const) and charbox.same_constant(CONST_0)):
+ optimizer.emit_operation(ResOperation(mode.STRSETITEM, [targetbox,
+ offsetbox,
+ charbox],
+ None))
offsetbox = _int_add(optimizer, offsetbox, CONST_1)
return offsetbox
@@ -335,7 +332,7 @@
if optimizer is None:
return None
resbox = BoxInt()
- optimizer.emit_operation(ResOperation(rop.INT_ADD, [box1, box2], resbox))
+ optimizer.optimize_default(ResOperation(rop.INT_ADD, [box1, box2], resbox))
return resbox
def _int_sub(optimizer, box1, box2):
@@ -345,10 +342,10 @@
if isinstance(box1, ConstInt):
return ConstInt(box1.value - box2.value)
resbox = BoxInt()
- optimizer.emit_operation(ResOperation(rop.INT_SUB, [box1, box2], resbox))
+ optimizer.optimize_default(ResOperation(rop.INT_SUB, [box1, box2], resbox))
return resbox
-def _strgetitem(optimization, strbox, indexbox, mode):
+def _strgetitem(optimizer, strbox, indexbox, mode):
if isinstance(strbox, ConstPtr) and isinstance(indexbox, ConstInt):
if mode is mode_string:
s = strbox.getref(lltype.Ptr(rstr.STR))
@@ -357,7 +354,7 @@
s = strbox.getref(lltype.Ptr(rstr.UNICODE))
return ConstInt(ord(s.chars[indexbox.getint()]))
resbox = BoxInt()
- optimization.emit_operation(ResOperation(mode.STRGETITEM, [strbox, indexbox],
+ optimizer.optimize_default(ResOperation(mode.STRGETITEM, [strbox, indexbox],
resbox))
return resbox
@@ -402,6 +399,7 @@
else:
self.getvalue(op.result).ensure_nonnull()
self.emit_operation(op)
+ self.pure(mode.STRLEN, [op.result], op.getarg(0))
def optimize_STRSETITEM(self, op):
value = self.getvalue(op.getarg(0))
@@ -440,7 +438,7 @@
if vindex.is_constant():
return value.getitem(vindex.box.getint())
#
- resbox = _strgetitem(self, value.force_box(), vindex.force_box(), mode)
+ resbox = _strgetitem(self.optimizer, value.force_box(), vindex.force_box(), mode)
return self.getvalue(resbox)
def optimize_STRLEN(self, op):
@@ -450,7 +448,7 @@
def _optimize_STRLEN(self, op, mode):
value = self.getvalue(op.getarg(0))
- lengthbox = value.getstrlen(self, mode)
+ lengthbox = value.getstrlen(self.optimizer, mode)
self.make_equal_to(op.result, self.getvalue(lengthbox))
def optimize_CALL(self, op):
@@ -649,16 +647,11 @@
self.emit_operation(op)
return
- opnum = op.getopnum()
- for value, func in optimize_ops:
- if opnum == value:
- func(self, op)
- break
- else:
- self.emit_operation(op)
+ dispatch_opt(self, op)
-optimize_ops = _findall(OptString, 'optimize_')
+dispatch_opt = make_dispatcher_method(OptString, 'optimize_',
+ default=OptString.emit_operation)
def _findall_call_oopspec():
prefix = 'opt_call_stroruni_'
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
@@ -56,6 +56,8 @@
# for resume.py operation
self.parent_resumedata_snapshot = None
self.parent_resumedata_frame_info_list = None
+ # counter for unrolling inlined loops
+ self.unroll_iterations = 1
@specialize.arg(3)
def copy_constants(self, registers, constants, ConstClass):
@@ -310,26 +312,27 @@
self.opimpl_goto_if_not(condbox, target)
''' % (_opimpl, _opimpl.upper())).compile()
+
+ def _establish_nullity(self, box, orgpc):
+ value = box.nonnull()
+ if value:
+ if box not in self.metainterp.known_class_boxes:
+ self.generate_guard(rop.GUARD_NONNULL, box, resumepc=orgpc)
+ else:
+ if not isinstance(box, Const):
+ self.generate_guard(rop.GUARD_ISNULL, box, resumepc=orgpc)
+ promoted_box = box.constbox()
+ self.metainterp.replace_box(box, promoted_box)
+ return value
+
@arguments("orgpc", "box", "label")
def opimpl_goto_if_not_ptr_nonzero(self, orgpc, box, target):
- value = box.nonnull()
- if value:
- opnum = rop.GUARD_NONNULL
- else:
- opnum = rop.GUARD_ISNULL
- self.generate_guard(opnum, box, resumepc=orgpc)
- if not value:
+ if not self._establish_nullity(box, orgpc):
self.pc = target
@arguments("orgpc", "box", "label")
def opimpl_goto_if_not_ptr_iszero(self, orgpc, box, target):
- value = box.nonnull()
- if value:
- opnum = rop.GUARD_NONNULL
- else:
- opnum = rop.GUARD_ISNULL
- self.generate_guard(opnum, box, resumepc=orgpc)
- if value:
+ if self._establish_nullity(box, orgpc):
self.pc = target
@arguments("box", "box", "box")
@@ -364,7 +367,9 @@
def opimpl_new_with_vtable(self, sizedescr):
cpu = self.metainterp.cpu
cls = heaptracker.descr2vtable(cpu, sizedescr)
- return self.execute(rop.NEW_WITH_VTABLE, ConstInt(cls))
+ resbox = self.execute(rop.NEW_WITH_VTABLE, ConstInt(cls))
+ self.metainterp.known_class_boxes[resbox] = None
+ return resbox
## @FixME #arguments("box")
## def opimpl_runtimenew(self, classbox):
@@ -387,8 +392,21 @@
@arguments("box", "descr", "box")
def _opimpl_getarrayitem_gc_any(self, arraybox, arraydescr, indexbox):
- return self.execute_with_descr(rop.GETARRAYITEM_GC,
- arraydescr, arraybox, indexbox)
+ cache = self.metainterp.heap_array_cache.get(arraydescr, None)
+ if cache and isinstance(indexbox, ConstInt):
+ index = indexbox.getint()
+ frombox, tobox = cache.get(index, (None, None))
+ if frombox is arraybox:
+ return tobox
+ resbox = self.execute_with_descr(rop.GETARRAYITEM_GC,
+ arraydescr, arraybox, indexbox)
+ if isinstance(indexbox, ConstInt):
+ if not cache:
+ cache = self.metainterp.heap_array_cache[arraydescr] = {}
+ index = indexbox.getint()
+ cache[index] = arraybox, resbox
+ return resbox
+
opimpl_getarrayitem_gc_i = _opimpl_getarrayitem_gc_any
opimpl_getarrayitem_gc_r = _opimpl_getarrayitem_gc_any
@@ -416,6 +434,13 @@
indexbox, itembox):
self.execute_with_descr(rop.SETARRAYITEM_GC, arraydescr, arraybox,
indexbox, itembox)
+ if isinstance(indexbox, ConstInt):
+ cache = self.metainterp.heap_array_cache.setdefault(arraydescr, {})
+ cache[indexbox.getint()] = arraybox, itembox
+ else:
+ cache = self.metainterp.heap_array_cache.get(arraydescr, None)
+ if cache:
+ cache.clear()
opimpl_setarrayitem_gc_i = _opimpl_setarrayitem_gc_any
opimpl_setarrayitem_gc_r = _opimpl_setarrayitem_gc_any
@@ -451,21 +476,17 @@
def opimpl_newlist(self, structdescr, lengthdescr, itemsdescr, arraydescr,
sizebox):
sbox = self.metainterp.execute_and_record(rop.NEW, structdescr)
- self.metainterp.execute_and_record(rop.SETFIELD_GC, lengthdescr,
- sbox, sizebox)
+ self._opimpl_setfield_gc_any(sbox, lengthdescr, sizebox)
abox = self.metainterp.execute_and_record(rop.NEW_ARRAY, arraydescr,
sizebox)
- self.metainterp.execute_and_record(rop.SETFIELD_GC, itemsdescr,
- sbox, abox)
+ self._opimpl_setfield_gc_any(sbox, itemsdescr, abox)
return sbox
@arguments("box", "descr", "descr", "box")
def _opimpl_getlistitem_gc_any(self, listbox, itemsdescr, arraydescr,
indexbox):
- arraybox = self.metainterp.execute_and_record(rop.GETFIELD_GC,
- itemsdescr, listbox)
- return self.execute_with_descr(rop.GETARRAYITEM_GC,
- arraydescr, arraybox, indexbox)
+ arraybox = self._opimpl_getfield_gc_any(listbox, itemsdescr)
+ return self._opimpl_getarrayitem_gc_any(arraybox, arraydescr, indexbox)
opimpl_getlistitem_gc_i = _opimpl_getlistitem_gc_any
opimpl_getlistitem_gc_r = _opimpl_getlistitem_gc_any
@@ -474,10 +495,9 @@
@arguments("box", "descr", "descr", "box", "box")
def _opimpl_setlistitem_gc_any(self, listbox, itemsdescr, arraydescr,
indexbox, valuebox):
- arraybox = self.metainterp.execute_and_record(rop.GETFIELD_GC,
- itemsdescr, listbox)
- self.execute_with_descr(rop.SETARRAYITEM_GC, arraydescr, arraybox,
- indexbox, valuebox)
+ arraybox = self._opimpl_getfield_gc_any(listbox, itemsdescr)
+ self._opimpl_setarrayitem_gc_any(arraybox, arraydescr, indexbox,
+ valuebox)
opimpl_setlistitem_gc_i = _opimpl_setlistitem_gc_any
opimpl_setlistitem_gc_r = _opimpl_setlistitem_gc_any
@@ -499,18 +519,29 @@
@arguments("box", "descr")
def _opimpl_getfield_gc_any(self, box, fielddescr):
- return self.execute_with_descr(rop.GETFIELD_GC, fielddescr, box)
+ return self._opimpl_getfield_gc_any_pureornot(
+ rop.GETFIELD_GC, box, fielddescr)
opimpl_getfield_gc_i = _opimpl_getfield_gc_any
opimpl_getfield_gc_r = _opimpl_getfield_gc_any
opimpl_getfield_gc_f = _opimpl_getfield_gc_any
@arguments("box", "descr")
def _opimpl_getfield_gc_pure_any(self, box, fielddescr):
- return self.execute_with_descr(rop.GETFIELD_GC_PURE, fielddescr, box)
+ return self._opimpl_getfield_gc_any_pureornot(
+ rop.GETFIELD_GC_PURE, box, fielddescr)
opimpl_getfield_gc_i_pure = _opimpl_getfield_gc_pure_any
opimpl_getfield_gc_r_pure = _opimpl_getfield_gc_pure_any
opimpl_getfield_gc_f_pure = _opimpl_getfield_gc_pure_any
+ @specialize.arg(1)
+ def _opimpl_getfield_gc_any_pureornot(self, opnum, box, fielddescr):
+ frombox, tobox = self.metainterp.heap_cache.get(fielddescr, (None, None))
+ if frombox is box:
+ return tobox
+ resbox = self.execute_with_descr(opnum, fielddescr, box)
+ self.metainterp.heap_cache[fielddescr] = (box, resbox)
+ return resbox
+
@arguments("orgpc", "box", "descr")
def _opimpl_getfield_gc_greenfield_any(self, pc, box, fielddescr):
ginfo = self.metainterp.jitdriver_sd.greenfield_info
@@ -529,7 +560,11 @@
@arguments("box", "descr", "box")
def _opimpl_setfield_gc_any(self, box, fielddescr, valuebox):
+ frombox, tobox = self.metainterp.heap_cache.get(fielddescr, (None, None))
+ if frombox is box and tobox is valuebox:
+ return
self.execute_with_descr(rop.SETFIELD_GC, fielddescr, box, valuebox)
+ self.metainterp.heap_cache[fielddescr] = (box, valuebox)
opimpl_setfield_gc_i = _opimpl_setfield_gc_any
opimpl_setfield_gc_r = _opimpl_setfield_gc_any
opimpl_setfield_gc_f = _opimpl_setfield_gc_any
@@ -593,12 +628,16 @@
standard_box = self.metainterp.virtualizable_boxes[-1]
if standard_box is box:
return False
+ if box in self.metainterp.nonstandard_virtualizables:
+ return True
eqbox = self.metainterp.execute_and_record(rop.PTR_EQ, None,
box, standard_box)
eqbox = self.implement_guard_value(pc, eqbox)
isstandard = eqbox.getint()
if isstandard:
self.metainterp.replace_box(box, standard_box)
+ else:
+ self.metainterp.nonstandard_virtualizables[box] = None
return not isstandard
def _get_virtualizable_field_index(self, fielddescr):
@@ -610,7 +649,7 @@
@arguments("orgpc", "box", "descr")
def _opimpl_getfield_vable(self, pc, box, fielddescr):
if self._nonstandard_virtualizable(pc, box):
- return self.execute_with_descr(rop.GETFIELD_GC, fielddescr, box)
+ return self._opimpl_getfield_gc_any(box, fielddescr)
self.metainterp.check_synchronized_virtualizable()
index = self._get_virtualizable_field_index(fielddescr)
return self.metainterp.virtualizable_boxes[index]
@@ -622,8 +661,7 @@
@arguments("orgpc", "box", "descr", "box")
def _opimpl_setfield_vable(self, pc, box, fielddescr, valuebox):
if self._nonstandard_virtualizable(pc, box):
- self.execute_with_descr(rop.SETFIELD_GC, fielddescr, box, valuebox)
- return
+ return self._opimpl_setfield_gc_any(box, fielddescr, valuebox)
index = self._get_virtualizable_field_index(fielddescr)
self.metainterp.virtualizable_boxes[index] = valuebox
self.metainterp.synchronize_virtualizable()
@@ -653,10 +691,8 @@
@arguments("orgpc", "box", "descr", "descr", "box")
def _opimpl_getarrayitem_vable(self, pc, box, fdescr, adescr, indexbox):
if self._nonstandard_virtualizable(pc, box):
- arraybox = self.metainterp.execute_and_record(rop.GETFIELD_GC,
- fdescr, box)
- return self.execute_with_descr(rop.GETARRAYITEM_GC, adescr,
- arraybox, indexbox)
+ arraybox = self._opimpl_getfield_gc_any(box, fdescr)
+ return self._opimpl_getarrayitem_gc_any(arraybox, adescr, indexbox)
self.metainterp.check_synchronized_virtualizable()
index = self._get_arrayitem_vable_index(pc, fdescr, indexbox)
return self.metainterp.virtualizable_boxes[index]
@@ -669,10 +705,9 @@
def _opimpl_setarrayitem_vable(self, pc, box, fdescr, adescr, indexbox,
valuebox):
if self._nonstandard_virtualizable(pc, box):
- arraybox = self.metainterp.execute_and_record(rop.GETFIELD_GC,
- fdescr, box)
- self.execute_with_descr(rop.SETARRAYITEM_GC, adescr,
- arraybox, indexbox, valuebox)
+ arraybox = self._opimpl_getfield_gc_any(box, fdescr)
+ self._opimpl_setarrayitem_gc_any(arraybox, adescr,
+ indexbox, valuebox)
return
index = self._get_arrayitem_vable_index(pc, fdescr, indexbox)
self.metainterp.virtualizable_boxes[index] = valuebox
@@ -686,8 +721,7 @@
@arguments("orgpc", "box", "descr", "descr")
def opimpl_arraylen_vable(self, pc, box, fdescr, adescr):
if self._nonstandard_virtualizable(pc, box):
- arraybox = self.metainterp.execute_and_record(rop.GETFIELD_GC,
- fdescr, box)
+ arraybox = self._opimpl_getfield_gc_any(box, fdescr)
return self.execute_with_descr(rop.ARRAYLEN_GC, adescr, arraybox)
vinfo = self.metainterp.jitdriver_sd.virtualizable_info
virtualizable_box = self.metainterp.virtualizable_boxes[-1]
@@ -845,7 +879,9 @@
@arguments("orgpc", "box")
def opimpl_guard_class(self, orgpc, box):
clsbox = self.cls_of_box(box)
- self.generate_guard(rop.GUARD_CLASS, box, [clsbox], resumepc=orgpc)
+ if box not in self.metainterp.known_class_boxes:
+ self.generate_guard(rop.GUARD_CLASS, box, [clsbox], resumepc=orgpc)
+ self.metainterp.known_class_boxes[box] = None
return clsbox
@arguments("int", "orgpc")
@@ -897,6 +933,10 @@
# 'redboxes' back into the registers where it comes from.
put_back_list_of_boxes3(self, jcposition, redboxes)
else:
+ if jitdriver_sd.warmstate.should_unroll_one_iteration(greenboxes):
+ if self.unroll_iterations > 0:
+ self.unroll_iterations -= 1
+ return
# warning! careful here. We have to return from the current
# frame containing the jit_merge_point, and then use
# do_recursive_call() to follow the recursive call. This is
@@ -1128,13 +1168,11 @@
metainterp.jitdriver_sd.greenfield_info is not None):
virtualizable_boxes = metainterp.virtualizable_boxes
saved_pc = self.pc
- try:
- if resumepc >= 0:
- self.pc = resumepc
- resume.capture_resumedata(metainterp.framestack, virtualizable_boxes,
- metainterp.virtualref_boxes, resumedescr)
- finally:
- self.pc = saved_pc
+ if resumepc >= 0:
+ self.pc = resumepc
+ resume.capture_resumedata(metainterp.framestack, virtualizable_boxes,
+ metainterp.virtualref_boxes, resumedescr)
+ self.pc = saved_pc
def implement_guard_value(self, orgpc, box):
"""Promote the given Box into a Const. Note: be careful, it's a
@@ -1233,7 +1271,7 @@
effect = effectinfo.extraeffect
if effect == effectinfo.EF_CANNOT_RAISE:
return self.execute_varargs(rop.CALL, allboxes, descr, False)
- elif effect == effectinfo.EF_PURE:
+ elif effect == effectinfo.EF_ELIDABLE:
return self.metainterp.record_result_of_call_pure(
self.execute_varargs(rop.CALL, allboxes, descr, False))
elif effect == effectinfo.EF_LOOPINVARIANT:
@@ -1449,6 +1487,16 @@
self.last_exc_value_box = None
self.retracing_loop_from = None
self.call_pure_results = args_dict_box()
+ # contains boxes where the class is already known
+ self.known_class_boxes = {}
+ # contains frame boxes that are not virtualizables
+ self.nonstandard_virtualizables = {}
+ # heap cache
+ # maps descrs to (from_box, to_box) tuples
+ self.heap_cache = {}
+ # heap array cache
+ # maps descrs to {index: (from_box, to_box)} dicts
+ self.heap_array_cache = {}
def perform_call(self, jitcode, boxes, greenkey=None):
# causes the metainterp to enter the given subfunction
@@ -1624,10 +1672,27 @@
# record the operation
profiler = self.staticdata.profiler
profiler.count_ops(opnum, RECORDED_OPS)
+ self._invalidate_caches(opnum, descr)
op = self.history.record(opnum, argboxes, resbox, descr)
self.attach_debug_info(op)
return resbox
+ def _invalidate_caches(self, opnum, descr):
+ if opnum == rop.SETFIELD_GC:
+ return
+ if opnum == rop.SETARRAYITEM_GC:
+ return
+ if rop._NOSIDEEFFECT_FIRST <= opnum <= rop._NOSIDEEFFECT_LAST:
+ return
+ if opnum == rop.CALL:
+ effectinfo = descr.get_extra_info()
+ if effectinfo.extraeffect == effectinfo.EF_ELIDABLE:
+ return
+ if self.heap_cache:
+ self.heap_cache.clear()
+ if self.heap_array_cache:
+ self.heap_array_cache.clear()
+
def attach_debug_info(self, op):
if (not we_are_translated() and op is not None
and getattr(self, 'framestack', None)):
@@ -1789,6 +1854,11 @@
duplicates[box] = None
def reached_loop_header(self, greenboxes, redboxes, resumedescr):
+ self.known_class_boxes = {}
+ self.nonstandard_virtualizables = {} # XXX maybe not needed?
+ self.heap_cache = {}
+ self.heap_array_cache = {}
+
duplicates = {}
self.remove_consts_and_duplicates(redboxes, len(redboxes),
duplicates)
@@ -2295,6 +2365,16 @@
for i in range(len(boxes)):
if boxes[i] is oldbox:
boxes[i] = newbox
+ for descr, (frombox, tobox) in self.heap_cache.iteritems():
+ change = False
+ if frombox is oldbox:
+ change = True
+ frombox = newbox
+ if tobox is oldbox:
+ change = True
+ tobox = newbox
+ if change:
+ self.heap_cache[descr] = frombox, tobox
def find_biggest_function(self):
start_stack = []
diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py
--- a/pypy/jit/metainterp/resoperation.py
+++ b/pypy/jit/metainterp/resoperation.py
@@ -281,9 +281,6 @@
assert len(args) == 2
self._arg0, self._arg1 = args
- def getarglist(self):
- return [self._arg0, self._arg1, self._arg2]
-
def numargs(self):
return 2
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
@@ -1,7 +1,7 @@
import py
import sys
from pypy.rlib.jit import JitDriver, we_are_jitted, hint, dont_look_inside
-from pypy.rlib.jit import loop_invariant
+from pypy.rlib.jit import loop_invariant, elidable, promote
from pypy.rlib.jit import jit_debug, assert_green, AssertGreenFailed
from pypy.rlib.jit import unroll_safe, current_trace_length
from pypy.jit.metainterp import pyjitpl, history
@@ -11,7 +11,7 @@
from pypy import conftest
from pypy.rlib.rarithmetic import ovfcheck
from pypy.jit.metainterp.typesystem import LLTypeHelper, OOTypeHelper
-from pypy.rpython.lltypesystem import lltype, llmemory
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
from pypy.rpython.ootypesystem import ootype
from pypy.jit.metainterp.optimizeopt import ALL_OPTS_DICT
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
@@ -304,12 +304,12 @@
assert res == 42
self.check_operations_history(int_add=1, int_mul=0, call=1, guard_no_exception=0)
- def test_residual_call_pure(self):
+ def test_residual_call_elidable(self):
def externfn(x, y):
return x * y
- externfn._pure_function_ = True
+ externfn._elidable_function_ = True
def f(n):
- n = hint(n, promote=True)
+ promote(n)
return externfn(n, n+1)
res = self.interp_operations(f, [6])
assert res == 42
@@ -317,10 +317,10 @@
self.check_operations_history(int_add=0, int_mul=0,
call=0, call_pure=0)
- def test_residual_call_pure_1(self):
+ def test_residual_call_elidable_1(self):
+ @elidable
def externfn(x, y):
return x * y
- externfn._pure_function_ = True
def f(n):
return externfn(n, n+1)
res = self.interp_operations(f, [6])
@@ -329,11 +329,11 @@
self.check_operations_history(int_add=1, int_mul=0,
call=0, call_pure=1)
- def test_residual_call_pure_2(self):
+ def test_residual_call_elidable_2(self):
myjitdriver = JitDriver(greens = [], reds = ['n'])
+ @elidable
def externfn(x):
return x - 1
- externfn._pure_function_ = True
def f(n):
while n > 0:
myjitdriver.can_enter_jit(n=n)
@@ -346,11 +346,11 @@
# by optimizeopt.py
self.check_loops(int_sub=0, call=1, call_pure=0)
- def test_constfold_call_pure(self):
+ def test_constfold_call_elidable(self):
myjitdriver = JitDriver(greens = ['m'], reds = ['n'])
+ @elidable
def externfn(x):
return x - 3
- externfn._pure_function_ = True
def f(n, m):
while n > 0:
myjitdriver.can_enter_jit(n=n, m=m)
@@ -362,11 +362,11 @@
# the CALL_PURE is constant-folded away by optimizeopt.py
self.check_loops(int_sub=1, call=0, call_pure=0)
- def test_constfold_call_pure_2(self):
+ def test_constfold_call_elidable_2(self):
myjitdriver = JitDriver(greens = ['m'], reds = ['n'])
+ @elidable
def externfn(x):
return x - 3
- externfn._pure_function_ = True
class V:
def __init__(self, value):
self.value = value
@@ -382,19 +382,19 @@
# the CALL_PURE is constant-folded away by optimizeopt.py
self.check_loops(int_sub=1, call=0, call_pure=0)
- def test_pure_function_returning_object(self):
+ def test_elidable_function_returning_object(self):
myjitdriver = JitDriver(greens = ['m'], reds = ['n'])
class V:
def __init__(self, x):
self.x = x
v1 = V(1)
v2 = V(2)
+ @elidable
def externfn(x):
if x:
return v1
else:
return v2
- externfn._pure_function_ = True
def f(n, m):
while n > 0:
myjitdriver.can_enter_jit(n=n, m=m)
@@ -508,6 +508,32 @@
assert res == 84 - 61 - 62
self.check_history(call=1) # because the trace starts immediately
+ def test_unroll_one_loop_iteration(self):
+ def unroll(code):
+ return code == 0
+ myjitdriver = JitDriver(greens = ['code'],
+ reds = ['loops', 'inner_loops', 's'],
+ should_unroll_one_iteration=unroll)
+
+ def f(code, loops, inner_loops):
+ s = 0
+ while loops > 0:
+ myjitdriver.jit_merge_point(code=code, loops=loops,
+ inner_loops=inner_loops, s=s)
+ if code == 1:
+ s += f(0, inner_loops, 0)
+ loops -= 1
+ s += 1
+ return s
+
+ res = self.meta_interp(f, [1, 4, 1], enable_opts="", inline=True)
+ assert res == f(1, 4, 1)
+ self.check_history(call_assembler=0)
+
+ res = self.meta_interp(f, [1, 4, 2], enable_opts="", inline=True)
+ assert res == f(1, 4, 2)
+ self.check_history(call_assembler=1)
+
def test_format(self):
def f(n):
return len("<%d>" % n)
@@ -984,11 +1010,14 @@
pass
class B(A):
pass
+ @dont_look_inside
+ def extern(n):
+ if n:
+ return A()
+ else:
+ return B()
def fn(n):
- if n:
- obj = A()
- else:
- obj = B()
+ obj = extern(n)
return isinstance(obj, B)
res = self.interp_operations(fn, [0])
assert res
@@ -1021,6 +1050,7 @@
res = self.meta_interp(main, [])
assert res == 55
+
def test_assert_isinstance(self):
class A:
pass
@@ -1181,7 +1211,7 @@
return tup[1]
res = self.interp_operations(f, [3, 5])
assert res == 5
- self.check_operations_history(setfield_gc=2, getfield_gc_pure=1)
+ self.check_operations_history(setfield_gc=2, getfield_gc_pure=0)
def test_oosend_look_inside_only_one(self):
class A:
@@ -1252,7 +1282,7 @@
myjitdriver.jit_merge_point(x=x, l=l)
a = l[x]
x = a.g(x)
- hint(a, promote=True)
+ promote(a)
return x
res = self.meta_interp(f, [299], listops=True)
assert res == f(299)
@@ -1312,7 +1342,7 @@
x -= 5
else:
x -= 7
- hint(a, promote=True)
+ promote(a)
return x
res = self.meta_interp(f, [299], listops=True)
assert res == f(299)
@@ -1343,7 +1373,7 @@
x -= 5
else:
x -= 7
- hint(a, promote=True)
+ promote(a)
return x
res = self.meta_interp(f, [299], listops=True)
assert res == f(299)
@@ -1377,7 +1407,7 @@
x = a.g(x)
else:
x -= 7
- hint(a, promote=True)
+ promote(a)
return x
res = self.meta_interp(f, [399], listops=True)
assert res == f(399)
@@ -1496,7 +1526,7 @@
glob.a = B()
const = 2
else:
- const = hint(const, promote=True)
+ promote(const)
x -= const
res += a.x
a = None
@@ -1531,7 +1561,7 @@
myjitdriver.can_enter_jit(x=x)
myjitdriver.jit_merge_point(x=x)
a = A()
- hint(a, promote=True)
+ promote(a)
x -= 1
self.meta_interp(f, [50])
self.check_loop_count(1)
@@ -1549,8 +1579,6 @@
assert res == 1
def test_raw_malloc_and_access(self):
- from pypy.rpython.lltypesystem import rffi
-
TP = rffi.CArray(lltype.Signed)
def f(n):
@@ -1564,8 +1592,6 @@
assert res == 10
def test_raw_malloc_and_access_float(self):
- from pypy.rpython.lltypesystem import rffi
-
TP = rffi.CArray(lltype.Float)
def f(n, f):
@@ -1595,9 +1621,9 @@
self.check_loops(jit_debug=2)
def test_assert_green(self):
- def f(x, promote):
- if promote:
- x = hint(x, promote=True)
+ def f(x, promote_flag):
+ if promote_flag:
+ promote(x)
assert_green(x)
return x
res = self.interp_operations(f, [8, 1])
@@ -1676,8 +1702,8 @@
return a1.val + b1.val
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_loop_count(8)
+ self.check_loops(getarrayitem_gc=7, everywhere=True)
py.test.skip("for the following, we need setarrayitem(varindex)")
self.check_loops(getarrayitem_gc=6, everywhere=True)
@@ -1817,7 +1843,7 @@
while y > 0:
myjitdriver.can_enter_jit(y=y, x=x, res=res, const=const)
myjitdriver.jit_merge_point(y=y, x=x, res=res, const=const)
- const = hint(const, promote=True)
+ const = promote(const)
res = res.binop(A(const))
if y<7:
res = x
@@ -1994,7 +2020,7 @@
myjitdriver.jit_merge_point(a=a, b=b, n=n, sa=sa)
if 0 < a <= 5: pass
if 0 < b <= 5: pass
- sa += (((((a << b) << b) << b) >> b) >> b) >> b
+ sa += (((((a << b) << b) << b) >> b) >> b) >> b
n += 1
return sa
@@ -2002,12 +2028,12 @@
n = sa = 0
while n < 10:
myjitdriver.jit_merge_point(a=a, b=b, n=n, sa=sa)
- if 0 < a < hint(sys.maxint/2, promote=True): pass
+ if 0 < a < promote(sys.maxint/2): pass
if 0 < b < 100: pass
- sa += (((((a << b) << b) << b) >> b) >> b) >> b
+ sa += (((((a << b) << b) << b) >> b) >> b) >> b
n += 1
return sa
-
+
assert self.meta_interp(f1, [5, 5]) == 50
self.check_loops(int_rshift=0, everywhere=True)
@@ -2039,7 +2065,7 @@
myjitdriver.jit_merge_point(a=a, b=b, n=n, sa=sa)
if -5 <= a < 0: pass
if 0 < b <= 5: pass
- sa += (((((a << b) << b) << b) >> b) >> b) >> b
+ sa += (((((a << b) << b) << b) >> b) >> b) >> b
n += 1
return sa
@@ -2047,12 +2073,12 @@
n = sa = 0
while n < 10:
myjitdriver.jit_merge_point(a=a, b=b, n=n, sa=sa)
- if -hint(sys.maxint/2, promote=True) < a < 0: pass
+ if -promote(sys.maxint/2) < a < 0: pass
if 0 < b < 100: pass
- sa += (((((a << b) << b) << b) >> b) >> b) >> b
+ sa += (((((a << b) << b) << b) >> b) >> b) >> b
n += 1
return sa
-
+
assert self.meta_interp(f1, [-5, 5]) == -50
self.check_loops(int_rshift=0, everywhere=True)
@@ -2082,7 +2108,7 @@
n = sa = 0
while n < 10:
myjitdriver.jit_merge_point(a=a, b=b, n=n, sa=sa)
- if 0 < a < hint(sys.maxint/2, promote=True): pass
+ if 0 < a < promote(sys.maxint/2): pass
if 0 < b < 100: pass
sa += (a << b) >> b
n += 1
@@ -2123,7 +2149,7 @@
def get_printable_location(i):
return str(i)
-
+
myjitdriver = JitDriver(greens = ['i'], reds = ['j', 'c', 'a'],
get_printable_location=get_printable_location)
bytecode = "0j10jc20a3"
@@ -2139,7 +2165,7 @@
if op == 'j':
j += 1
elif op == 'c':
- c = hint(c, promote=True)
+ promote(c)
c = 1 - c
elif op == '2':
if j < 3:
@@ -2208,7 +2234,8 @@
self.local_names[0] = 1
def retrieve(self):
- variables = hint(self.variables, promote=True)
+ variables = self.variables
+ promote(variables)
result = self.local_names[0]
if result == 0:
return -1
@@ -2231,7 +2258,7 @@
assert self.meta_interp(build, []) == 7
self.check_loops(getfield_gc_pure=0)
self.check_loops(getfield_gc_pure=2, everywhere=True)
-
+
def test_frame_finished_during_retrace(self):
class Base(object):
pass
@@ -2262,7 +2289,7 @@
return sa
res = self.meta_interp(f, [])
assert res == f()
-
+
def test_frame_finished_during_continued_retrace(self):
class Base(object):
pass
@@ -2313,6 +2340,67 @@
assert res == -2
#self.check_loops(getarrayitem_gc=0, setarrayitem_gc=0) -- xxx?
+ 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)
+ pc = sa = i = 0
+ while pc < len(bytecode):
+ myjitdriver.jit_merge_point(pc=pc, n=n, sa=sa, i=i)
+ n = hint(n, promote=True)
+ op = bytecode[pc]
+ if op == '0':
+ i = 0
+ elif op == '+':
+ i += 1
+ elif op == 's':
+ sa += i
+ elif op == 'S':
+ sa += 2
+ elif op == 'I':
+ if i < n:
+ pc -= 2
+ myjitdriver.can_enter_jit(pc=pc, n=n, sa=sa, i=i)
+ continue
+ pc += 1
+ return sa
+
+ def g(n1, n2):
+ for i in range(10):
+ f(n1)
+ for i in range(10):
+ f(n2)
+
+ nn = [10, 3]
+ assert self.meta_interp(g, nn) == g(*nn)
+
+ # The attempts of retracing first loop will end up retracing the
+ # second and thus fail 5 times, saturating the retrace_count. Instead a
+ # bridge back to the preamble of the first loop is produced. A guard in
+ # this bridge is later traced resulting in a retrace of the second loop.
+ # Thus we end up with:
+ # 1 preamble and 1 specialized version of first loop
+ # 1 preamble and 2 specialized version of second loop
+ self.check_tree_loop_count(2 + 3)
+
+ # FIXME: Add a gloabl retrace counter and test that we are not trying more than 5 times.
+
+ def g(n):
+ for i in range(n):
+ for j in range(10):
+ f(n-i)
+
+ res = self.meta_interp(g, [10])
+ assert res == g(10)
+ # 1 preamble and 6 speciealized versions of each loop
+ self.check_tree_loop_count(2*(1 + 6))
+
+
class TestOOtype(BasicTests, OOJitMixin):
def test_oohash(self):
@@ -2490,5 +2578,57 @@
self.meta_interp(f, [], enable_opts='')
self.check_loops(new_with_vtable=1)
+ def test_release_gil_flush_heap_cache(self):
+ T = rffi.CArrayPtr(rffi.TIME_T)
+
+ external = rffi.llexternal("time", [T], rffi.TIME_T, threadsafe=True)
+ # Not a real lock, has all the same properties with respect to GIL
+ # release though, so good for this test.
+ class Lock(object):
+ @dont_look_inside
+ def acquire(self):
+ external(lltype.nullptr(T.TO))
+ @dont_look_inside
+ def release(self):
+ external(lltype.nullptr(T.TO))
+ class X(object):
+ def __init__(self, idx):
+ self.field = idx
+ @dont_look_inside
+ def get_obj(z):
+ return X(z)
+ myjitdriver = JitDriver(greens=[], reds=["n", "l", "z", "lock"])
+ def f(n, z):
+ lock = Lock()
+ l = 0
+ while n > 0:
+ myjitdriver.jit_merge_point(lock=lock, l=l, n=n, z=z)
+ x = get_obj(z)
+ l += x.field
+ lock.acquire()
+ # This must not reuse the previous one.
+ n -= x.field
+ lock.release()
+ return n
+ res = self.meta_interp(f, [10, 1])
+ self.check_loops(getfield_gc=2)
+ assert res == f(10, 1)
+
+ def test_jit_merge_point_with_raw_pointer(self):
+ driver = JitDriver(greens = [], reds = ['n', 'x'])
+
+ TP = lltype.Array(lltype.Signed, hints={'nolength': True})
+
+ def f(n):
+ x = lltype.malloc(TP, 10, flavor='raw')
+ x[0] = 1
+ while n > 0:
+ driver.jit_merge_point(n=n, x=x)
+ n -= x[0]
+ lltype.free(x, flavor='raw')
+ return n
+
+ self.meta_interp(f, [10], repeat=3)
+
class TestLLtype(BaseLLtypeTests, LLJitMixin):
pass
diff --git a/pypy/jit/metainterp/test/test_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,7 +1,7 @@
import py
from pypy.rlib.rarithmetic import r_singlefloat, r_longlong, r_ulonglong
-from pypy.rlib.jit import JitDriver, hint, dont_look_inside
+from pypy.rlib.jit import JitDriver, promote, dont_look_inside
from pypy.rlib.unroll import unrolling_iterable
from pypy.rlib.libffi import ArgChain, longlong2float, float2longlong
from pypy.rlib.libffi import IS_32_BIT
@@ -49,8 +49,7 @@
res = init_result
while n < 10:
driver.jit_merge_point(n=n, res=res, func=func)
- driver.can_enter_jit(n=n, res=res, func=func)
- func = hint(func, promote=True)
+ promote(func)
argchain = ArgChain()
# this loop is unrolled
for method_name, argval in method_and_args:
diff --git a/pypy/jit/metainterp/test/test_immutable.py b/pypy/jit/metainterp/test/test_immutable.py
--- a/pypy/jit/metainterp/test/test_immutable.py
+++ b/pypy/jit/metainterp/test/test_immutable.py
@@ -1,5 +1,9 @@
+from pypy.rlib import jit
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
+ at jit.dont_look_inside
+def escape(x):
+ return x
class ImmutableFieldsTests:
@@ -11,7 +15,7 @@
self.x = x
def f(x):
- y = X(x)
+ y = escape(X(x))
return y.x + 5
res = self.interp_operations(f, [23])
assert res == 28
@@ -33,7 +37,7 @@
def f(x, y):
X(x) # force the field 'x' to be on class 'X'
- z = Y(x, y)
+ z = escape(Y(x, y))
return z.x + z.y + 5
res = self.interp_operations(f, [23, 11])
assert res == 39
@@ -42,7 +46,7 @@
def f(x, y):
# this time, the field 'x' only shows up on subclass 'Y'
- z = Y(x, y)
+ z = escape(Y(x, y))
return z.x + z.y + 5
res = self.interp_operations(f, [23, 11])
assert res == 39
@@ -58,7 +62,7 @@
def f(index):
l = [1, 2, 3, 4]
l[2] = 30
- a = X(l)
+ a = escape(X(l))
return a.y[index]
res = self.interp_operations(f, [2], listops=True)
assert res == 30
@@ -76,7 +80,7 @@
self.y = y
def f(x, index):
- y = X([x], x+1)
+ y = escape(X([x], x+1))
return y.lst[index] + y.y + 5
res = self.interp_operations(f, [23, 0], listops=True)
assert res == 23 + 24 + 5
diff --git a/pypy/jit/metainterp/test/test_jitprof.py b/pypy/jit/metainterp/test/test_jitprof.py
--- a/pypy/jit/metainterp/test/test_jitprof.py
+++ b/pypy/jit/metainterp/test/test_jitprof.py
@@ -1,6 +1,6 @@
from pypy.jit.metainterp.warmspot import ll_meta_interp
-from pypy.rlib.jit import JitDriver, dont_look_inside, purefunction
+from pypy.rlib.jit import JitDriver, dont_look_inside, elidable
from pypy.jit.metainterp.test.support import LLJitMixin
from pypy.jit.metainterp import pyjitpl
from pypy.jit.metainterp.jitprof import *
@@ -89,7 +89,7 @@
assert profiler.calls == 1
def test_blackhole_pure(self):
- @purefunction
+ @elidable
def g(n):
return n+1
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,6 +1,6 @@
import py
from pypy.rlib.jit import JitDriver, we_are_jitted, hint
-from pypy.rlib.jit import unroll_safe, dont_look_inside
+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
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
@@ -926,7 +926,7 @@
myjitdriver.can_enter_jit(codeno=codeno, frame=frame, n=n, x=x)
myjitdriver.jit_merge_point(codeno=codeno, frame=frame, n=n,
x=x)
- frame.s = hint(frame.s, promote=True)
+ frame.s = promote(frame.s)
n -= 1
s = frame.s
assert s >= 0
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
@@ -1,5 +1,5 @@
import py
-from pypy.rlib.jit import JitDriver, hint, purefunction
+from pypy.rlib.jit import JitDriver, promote, elidable
from pypy.jit.codewriter.policy import StopAtXPolicy
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
@@ -604,7 +604,7 @@
def test_constfold_pure_oosend(self):
myjitdriver = JitDriver(greens=[], reds = ['i', 'obj'])
class A:
- @purefunction
+ @elidable
def foo(self):
return 42
def fn(n, i):
@@ -613,7 +613,7 @@
while i > 0:
myjitdriver.can_enter_jit(i=i, obj=obj)
myjitdriver.jit_merge_point(i=i, obj=obj)
- obj = hint(obj, promote=True)
+ promote(obj)
res = obj.foo()
i-=1
return res
diff --git a/pypy/jit/metainterp/test/test_tracingopts.py b/pypy/jit/metainterp/test/test_tracingopts.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/test/test_tracingopts.py
@@ -0,0 +1,407 @@
+import py
+import sys
+from pypy.rlib import jit
+from pypy.jit.metainterp.test.support import LLJitMixin
+
+
+class TestLLtype(LLJitMixin):
+ def test_dont_record_repeated_guard_class(self):
+ class A:
+ pass
+ class B(A):
+ pass
+ @jit.dont_look_inside
+ def extern(n):
+ if n == -7:
+ return None
+ elif n:
+ return A()
+ else:
+ return B()
+ def fn(n):
+ obj = extern(n)
+ return isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B)
+ res = self.interp_operations(fn, [0])
+ assert res == 4
+ self.check_operations_history(guard_class=1, guard_nonnull=1)
+ res = self.interp_operations(fn, [1])
+ assert not res
+
+ def test_dont_record_guard_class_after_new(self):
+ class A:
+ pass
+ class B(A):
+ pass
+ def fn(n):
+ if n == -7:
+ obj = None
+ elif n:
+ obj = A()
+ else:
+ obj = B()
+ return isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B)
+ res = self.interp_operations(fn, [0])
+ assert res == 4
+ self.check_operations_history(guard_class=0, guard_nonnull=0)
+ res = self.interp_operations(fn, [1])
+ assert not res
+
+ def test_guard_isnull_nullifies(self):
+ class A:
+ pass
+ a = A()
+ a.x = None
+ def fn(n):
+ if n == -7:
+ a.x = ""
+ obj = a.x
+ res = 0
+ if not obj:
+ res += 1
+ if obj:
+ res += 1
+ if obj is None:
+ res += 1
+ if obj is not None:
+ res += 1
+ return res
+ res = self.interp_operations(fn, [0])
+ assert res == 2
+ self.check_operations_history(guard_isnull=1)
+
+ def test_heap_caching_while_tracing(self):
+ class A:
+ pass
+ a1 = A()
+ a2 = A()
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a.x = n
+ return a.x
+ res = self.interp_operations(fn, [7])
+ assert res == 7
+ self.check_operations_history(getfield_gc=0)
+ res = self.interp_operations(fn, [-7])
+ assert res == -7
+ self.check_operations_history(getfield_gc=0)
+
+ def fn(n, ca, cb):
+ a1.x = n
+ a2.x = n
+ a = a1
+ if ca:
+ a = a2
+ b = a1
+ if cb:
+ b = a
+ return a.x + b.x
+ res = self.interp_operations(fn, [7, 0, 1])
+ assert res == 7 * 2
+ self.check_operations_history(getfield_gc=1)
+ res = self.interp_operations(fn, [-7, 1, 1])
+ assert res == -7 * 2
+ self.check_operations_history(getfield_gc=1)
+
+ def test_heap_caching_while_tracing_invalidation(self):
+ class A:
+ pass
+ a1 = A()
+ a2 = A()
+ @jit.dont_look_inside
+ def f(a):
+ a.x = 5
+ l = [1]
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a.x = n
+ x1 = a.x
+ f(a)
+ x2 = a.x
+ l[0] = x2
+ return a.x + x1 + x2
+ res = self.interp_operations(fn, [7])
+ assert res == 5 * 2 + 7
+ self.check_operations_history(getfield_gc=1)
+
+ def test_heap_caching_dont_store_same(self):
+ class A:
+ pass
+ a1 = A()
+ a2 = A()
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a.x = n
+ a.x = n
+ return a.x
+ res = self.interp_operations(fn, [7])
+ assert res == 7
+ self.check_operations_history(getfield_gc=0, setfield_gc=1)
+ res = self.interp_operations(fn, [-7])
+ assert res == -7
+ self.check_operations_history(getfield_gc=0)
+
+ def test_array_caching(self):
+ a1 = [0, 0]
+ a2 = [0, 0]
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a[0] = n
+ x1 = a[0]
+ a[n - n] = n + 1
+ return a[0] + x1
+ res = self.interp_operations(fn, [7])
+ assert res == 7 + 7 + 1
+ self.check_operations_history(getarrayitem_gc=1)
+ res = self.interp_operations(fn, [-7])
+ assert res == -7 - 7 + 1
+ self.check_operations_history(getarrayitem_gc=1)
+
+ def fn(n, ca, cb):
+ a1[0] = n
+ a2[0] = n
+ a = a1
+ if ca:
+ a = a2
+ b = a1
+ if cb:
+ b = a
+ return a[0] + b[0]
+ res = self.interp_operations(fn, [7, 0, 1])
+ assert res == 7 * 2
+ self.check_operations_history(getarrayitem_gc=1)
+ res = self.interp_operations(fn, [-7, 1, 1])
+ assert res == -7 * 2
+ self.check_operations_history(getarrayitem_gc=1)
+
+ def test_array_caching_while_tracing_invalidation(self):
+ a1 = [0, 0]
+ a2 = [0, 0]
+ @jit.dont_look_inside
+ def f(a):
+ a[0] = 5
+ class A: pass
+ l = A()
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a[0] = n
+ x1 = a[0]
+ f(a)
+ x2 = a[0]
+ l.x = x2
+ return a[0] + x1 + x2
+ res = self.interp_operations(fn, [7])
+ assert res == 5 * 2 + 7
+ self.check_operations_history(getarrayitem_gc=1)
+
+ def test_array_and_getfield_interaction(self):
+ class A: pass
+ a1 = A()
+ a2 = A()
+ a1.l = a2.l = [0, 0]
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a.l = [0, 0]
+ a.x = 0
+ a.l[a.x] = n
+ a.x += 1
+ a.l[a.x] = n + 1
+ x1 = a.l[a.x]
+ a.x -= 1
+ x2 = a.l[a.x]
+ return x1 + x2
+ res = self.interp_operations(fn, [7])
+ assert res == 7 * 2 + 1
+ self.check_operations_history(setarrayitem_gc=2, setfield_gc=3,
+ getarrayitem_gc=0, getfield_gc=1)
+
+ def test_promote_changes_heap_cache(self):
+ class A: pass
+ a1 = A()
+ a2 = A()
+ a1.l = a2.l = [0, 0]
+ a1.x = a2.x = 0
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ a.l = [0, 0]
+ jit.promote(a.x)
+ a.l[a.x] = n
+ a.x += 1
+ a.l[a.x] = n + 1
+ x1 = a.l[a.x]
+ a.x -= 1
+ x2 = a.l[a.x]
+ return x1 + x2
+ res = self.interp_operations(fn, [7])
+ assert res == 7 * 2 + 1
+ self.check_operations_history(setarrayitem_gc=2, setfield_gc=2,
+ getarrayitem_gc=0, getfield_gc=2)
+
+ def test_list_caching(self):
+ a1 = [0, 0]
+ a2 = [0, 0]
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = a2
+ if n < -1000:
+ a.append(5)
+ a[0] = n
+ x1 = a[0]
+ a[n - n] = n + 1
+ return a[0] + x1
+ res = self.interp_operations(fn, [7])
+ assert res == 7 + 7 + 1
+ self.check_operations_history(getarrayitem_gc=1,
+ getfield_gc=1)
+ res = self.interp_operations(fn, [-7])
+ assert res == -7 - 7 + 1
+ self.check_operations_history(getarrayitem_gc=1,
+ getfield_gc=1)
+
+ def fn(n, ca, cb):
+ a1[0] = n
+ a2[0] = n
+ a = a1
+ if ca:
+ a = a2
+ if n < -100:
+ a.append(5)
+ b = a1
+ if cb:
+ b = a
+ return a[0] + b[0]
+ res = self.interp_operations(fn, [7, 0, 1])
+ assert res == 7 * 2
+ self.check_operations_history(getarrayitem_gc=1,
+ getfield_gc=3)
+ res = self.interp_operations(fn, [-7, 1, 1])
+ assert res == -7 * 2
+ self.check_operations_history(getarrayitem_gc=1,
+ getfield_gc=3)
+
+ def test_list_caching_negative(self):
+ def fn(n):
+ a = [0] * n
+ if n > 1000:
+ a.append(0)
+ a[-1] = n
+ x1 = a[-1]
+ a[n - n - 1] = n + 1
+ return a[-1] + x1
+ res = self.interp_operations(fn, [7])
+ assert res == 7 + 7 + 1
+ self.check_operations_history(setarrayitem_gc=2,
+ setfield_gc=2)
+
+ def test_virtualizable_with_array_heap_cache(self):
+ myjitdriver = jit.JitDriver(greens = [], reds = ['n', 'x', 'i', 'frame'],
+ virtualizables = ['frame'])
+
+ class Frame(object):
+ _virtualizable2_ = ['l[*]', 's']
+
+ def __init__(self, a, s):
+ self = jit.hint(self, access_directly=True, fresh_virtualizable=True)
+ self.l = [0] * (4 + a)
+ self.s = s
+
+ def f(n, a, i):
+ frame = Frame(a, 0)
+ frame.l[0] = a
+ frame.l[1] = a + 1
+ frame.l[2] = a + 2
+ frame.l[3] = a + 3
+ if not i:
+ return frame.l[0] + len(frame.l)
+ x = 0
+ while n > 0:
+ myjitdriver.can_enter_jit(frame=frame, n=n, x=x, i=i)
+ myjitdriver.jit_merge_point(frame=frame, n=n, x=x, i=i)
+ frame.s = jit.promote(frame.s)
+ n -= 1
+ s = frame.s
+ assert s >= 0
+ x += frame.l[s]
+ frame.s += 1
+ s = frame.s
+ assert s >= 0
+ x += frame.l[s]
+ x += len(frame.l)
+ x += f(n, n, 0)
+ frame.s -= 1
+ return x
+
+ res = self.meta_interp(f, [10, 1, 1], listops=True)
+ assert res == f(10, 1, 1)
+ self.check_history(getarrayitem_gc=0, getfield_gc=0)
+
+ def test_heap_caching_pure(self):
+ class A(object):
+ pass
+ p1 = A()
+ p2 = A()
+ def fn(n):
+ if n >= 0:
+ a = (n, n + 1)
+ p = p1
+ else:
+ a = (n + 1, n)
+ p = p2
+ p.x = a
+
+ return p.x[0] + p.x[1]
+ res = self.interp_operations(fn, [7])
+ assert res == 7 + 7 + 1
+ self.check_operations_history(getfield_gc=0, getfield_gc_pure=0)
+ res = self.interp_operations(fn, [-7])
+ assert res == -7 - 7 + 1
+ self.check_operations_history(getfield_gc=0, getfield_gc_pure=0)
+
+ def test_heap_caching_and_elidable_function(self):
+ class A:
+ pass
+ class B: pass
+ a1 = A()
+ a1.y = 6
+ a2 = A()
+ a2.y = 13
+ @jit.elidable
+ def f(b):
+ return b + 1
+ def fn(n):
+ if n > 0:
+ a = a1
+ else:
+ a = A()
+ a.x = n
+ z = f(6)
+ return z + a.x
+ res = self.interp_operations(fn, [7])
+ assert res == 7 + 7
+ self.check_operations_history(getfield_gc=0)
+ res = self.interp_operations(fn, [-7])
+ assert res == -7 + 7
+ self.check_operations_history(getfield_gc=0)
+ return
diff --git a/pypy/jit/metainterp/test/test_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
@@ -1,5 +1,5 @@
import py
-from pypy.rlib.jit import JitDriver, hint
+from pypy.rlib.jit import JitDriver, promote
from pypy.rlib.objectmodel import compute_unique_id
from pypy.jit.codewriter.policy import StopAtXPolicy
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
@@ -300,7 +300,7 @@
while n > 0:
myjitdriver.can_enter_jit(n=n, i=i, stufflist=stufflist)
myjitdriver.jit_merge_point(n=n, i=i, stufflist=stufflist)
- i = hint(i, promote=True)
+ promote(i)
v = Stuff(i)
n -= stufflist.lst[v.x].x
return n
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
@@ -5,7 +5,7 @@
from pypy.rpython.rclass import IR_IMMUTABLE, IR_IMMUTABLE_ARRAY
from pypy.jit.codewriter.policy import StopAtXPolicy
from pypy.jit.codewriter import heaptracker
-from pypy.rlib.jit import JitDriver, hint, dont_look_inside
+from pypy.rlib.jit import JitDriver, hint, dont_look_inside, promote
from pypy.rlib.rarithmetic import intmask
from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
from pypy.rpython.rclass import FieldListAccessor
@@ -377,7 +377,7 @@
expected = f(20)
res = self.meta_interp(f, [20], enable_opts='')
assert res == expected
- self.check_loops(getfield_gc=3, setfield_gc=0,
+ self.check_loops(getfield_gc=1, setfield_gc=0,
arraylen_gc=1, getarrayitem_gc=1, setarrayitem_gc=1)
# ------------------------------
@@ -480,7 +480,7 @@
while n > 0:
myjitdriver.can_enter_jit(frame=frame, n=n, x=x)
myjitdriver.jit_merge_point(frame=frame, n=n, x=x)
- frame.s = hint(frame.s, promote=True)
+ frame.s = promote(frame.s)
n -= 1
s = frame.s
assert s >= 0
@@ -1133,6 +1133,7 @@
res = self.meta_interp(f, [10])
assert res == 55
self.check_loops(new_with_vtable=0, ptr_eq=1, everywhere=True)
+ self.check_history(ptr_eq=2)
def test_virtual_child_frame_with_arrays(self):
myjitdriver = JitDriver(greens = [], reds = ['frame'],
diff --git a/pypy/jit/metainterp/test/test_warmstate.py b/pypy/jit/metainterp/test/test_warmstate.py
--- a/pypy/jit/metainterp/test/test_warmstate.py
+++ b/pypy/jit/metainterp/test/test_warmstate.py
@@ -186,6 +186,7 @@
_get_printable_location_ptr = None
_confirm_enter_jit_ptr = None
_can_never_inline_ptr = None
+ _should_unroll_one_iteration_ptr = None
class FakeCell:
dont_trace_here = False
state = WarmEnterState(FakeWarmRunnerDesc(), FakeJitDriverSD())
@@ -214,6 +215,7 @@
_confirm_enter_jit_ptr = None
_can_never_inline_ptr = None
_get_jitcell_at_ptr = None
+ _should_unroll_one_iteration_ptr = None
state = WarmEnterState(FakeWarmRunnerDesc(), FakeJitDriverSD())
state.make_jitdriver_callbacks()
res = state.get_location_str([ConstInt(5), constfloat(42.5)])
@@ -238,6 +240,7 @@
_confirm_enter_jit_ptr = llhelper(ENTER_JIT, confirm_enter_jit)
_can_never_inline_ptr = None
_get_jitcell_at_ptr = None
+ _should_unroll_one_iteration_ptr = None
state = WarmEnterState(FakeWarmRunnerDesc(), FakeJitDriverSD())
state.make_jitdriver_callbacks()
@@ -262,6 +265,7 @@
_confirm_enter_jit_ptr = None
_can_never_inline_ptr = llhelper(CAN_NEVER_INLINE, can_never_inline)
_get_jitcell_at_ptr = None
+ _should_unroll_one_iteration_ptr = None
state = WarmEnterState(FakeWarmRunnerDesc(), FakeJitDriverSD())
state.make_jitdriver_callbacks()
diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py
--- a/pypy/jit/metainterp/warmspot.py
+++ b/pypy/jit/metainterp/warmspot.py
@@ -10,6 +10,7 @@
from pypy.rlib.objectmodel import we_are_translated
from pypy.rlib.unroll import unrolling_iterable
from pypy.rlib.debug import fatalerror
+from pypy.rlib.rstackovf import StackOverflow
from pypy.translator.simplify import get_functype
from pypy.translator.unsimplify import call_final_function
@@ -408,21 +409,28 @@
jd.warmstate = state
def crash_in_jit(e):
- if not we_are_translated():
- print "~~~ Crash in JIT!"
- print '~~~ %s: %s' % (e.__class__, e)
- if sys.stdout == sys.__stdout__:
- import pdb; pdb.post_mortem(sys.exc_info()[2])
- raise
- fatalerror('~~~ Crash in JIT! %s' % (e,), traceback=True)
+ try:
+ raise e
+ except JitException:
+ raise # go through
+ except MemoryError:
+ raise # go through
+ except StackOverflow:
+ raise # go through
+ except Exception, e:
+ if not we_are_translated():
+ print "~~~ Crash in JIT!"
+ print '~~~ %s: %s' % (e.__class__, e)
+ if sys.stdout == sys.__stdout__:
+ import pdb; pdb.post_mortem(sys.exc_info()[2])
+ raise
+ fatalerror('~~~ Crash in JIT! %s' % (e,), traceback=True)
crash_in_jit._dont_inline_ = True
if self.translator.rtyper.type_system.name == 'lltypesystem':
def maybe_enter_jit(*args):
try:
maybe_compile_and_run(state.increment_threshold, *args)
- except JitException:
- raise # go through
except Exception, e:
crash_in_jit(e)
maybe_enter_jit._always_inline_ = True
@@ -460,6 +468,9 @@
onlygreens=False)
jd._can_never_inline_ptr = self._make_hook_graph(jd,
annhelper, jd.jitdriver.can_never_inline, annmodel.s_Bool)
+ jd._should_unroll_one_iteration_ptr = self._make_hook_graph(jd,
+ annhelper, jd.jitdriver.should_unroll_one_iteration,
+ annmodel.s_Bool)
annhelper.finish()
def _make_hook_graph(self, jitdriver_sd, annhelper, func,
diff --git a/pypy/jit/metainterp/warmstate.py b/pypy/jit/metainterp/warmstate.py
--- a/pypy/jit/metainterp/warmstate.py
+++ b/pypy/jit/metainterp/warmstate.py
@@ -138,7 +138,10 @@
refvalue = cpu.ts.cast_to_ref(value)
cpu.set_future_value_ref(j, refvalue)
elif typecode == 'int':
- intvalue = lltype.cast_primitive(lltype.Signed, value)
+ if isinstance(lltype.typeOf(value), lltype.Ptr):
+ intvalue = llmemory.AddressAsInt(llmemory.cast_ptr_to_adr(value))
+ else:
+ intvalue = lltype.cast_primitive(lltype.Signed, value)
cpu.set_future_value_int(j, intvalue)
elif typecode == 'float':
if lltype.typeOf(value) is lltype.Float:
@@ -237,7 +240,7 @@
d = {}
if NonConstant(False):
value = 'blah' # not a constant ''
- if value is None:
+ if value is None or value == 'all':
value = ALL_OPTS_NAMES
for name in value.split(":"):
if name:
@@ -569,6 +572,19 @@
return can_inline_greenargs(*greenargs)
self.can_inline_greenargs = can_inline_greenargs
self.can_inline_callable = can_inline_callable
+
+ if jd._should_unroll_one_iteration_ptr is None:
+ def should_unroll_one_iteration(greenkey):
+ return False
+ else:
+ rtyper = self.warmrunnerdesc.rtyper
+ inline_ptr = jd._should_unroll_one_iteration_ptr
+ def should_unroll_one_iteration(greenkey):
+ greenargs = unwrap_greenkey(greenkey)
+ fn = support.maybe_on_top_of_llinterp(rtyper, inline_ptr)
+ return fn(*greenargs)
+ self.should_unroll_one_iteration = should_unroll_one_iteration
+
if hasattr(jd.jitdriver, 'on_compile'):
def on_compile(logger, token, operations, type, greenkey):
greenargs = unwrap_greenkey(greenkey)
diff --git a/pypy/jit/tl/spli/interpreter.py b/pypy/jit/tl/spli/interpreter.py
--- a/pypy/jit/tl/spli/interpreter.py
+++ b/pypy/jit/tl/spli/interpreter.py
@@ -2,7 +2,7 @@
from pypy.tool import stdlib_opcode
from pypy.jit.tl.spli import objects, pycode
from pypy.rlib.unroll import unrolling_iterable
-from pypy.rlib.jit import JitDriver, hint, dont_look_inside
+from pypy.rlib.jit import JitDriver, promote, dont_look_inside
from pypy.rlib.objectmodel import we_are_translated
opcode_method_names = stdlib_opcode.host_bytecode_spec.method_names
@@ -78,7 +78,7 @@
while True:
jitdriver.jit_merge_point(code=code, instr_index=instr_index,
frame=self)
- self.stack_depth = hint(self.stack_depth, promote=True)
+ self.stack_depth = promote(self.stack_depth)
op = ord(code[instr_index])
instr_index += 1
if op >= HAVE_ARGUMENT:
diff --git a/pypy/jit/tl/tiny2.py b/pypy/jit/tl/tiny2.py
--- a/pypy/jit/tl/tiny2.py
+++ b/pypy/jit/tl/tiny2.py
@@ -27,7 +27,7 @@
{ #1 #1 1 SUB ->#1 #1 } => when called with 5, gives '5 4 3 2 1'
"""
-from pypy.rlib.jit import hint
+from pypy.rlib.jit import hint, promote
#
# See pypy/doc/jit.txt for a higher-level overview of the JIT techniques
@@ -75,9 +75,9 @@
# ones. The JIT compiler cannot look into indirect calls, but it
# can analyze and inline the code in directly-called functions.
y = stack.pop()
- hint(y.__class__, promote=True)
+ promote(y.__class__)
x = stack.pop()
- hint(x.__class__, promote=True)
+ promote(x.__class__)
try:
z = IntBox(func_int(x.as_int(), y.as_int()))
except ValueError:
@@ -108,7 +108,7 @@
# doesn't have to worry about the 'args' list being unpredictably
# modified.
oldargs = args
- argcount = hint(len(oldargs), promote=True)
+ argcount = promote(len(oldargs))
args = []
n = 0
while n < argcount:
@@ -160,8 +160,7 @@
# read out of the 'loops' list will be a compile-time constant
# because it was pushed as a compile-time constant by the '{'
# case above into 'loops', which is a virtual list, so the
- # promotion below is just a way to make the colors match.
- pos = hint(pos, promote=True)
+ promote(pos)
else:
stack.append(StrBox(opcode))
return stack
diff --git a/pypy/jit/tl/tiny2_hotpath.py b/pypy/jit/tl/tiny2_hotpath.py
--- a/pypy/jit/tl/tiny2_hotpath.py
+++ b/pypy/jit/tl/tiny2_hotpath.py
@@ -27,7 +27,7 @@
{ #1 #1 1 SUB ->#1 #1 } => when called with 5, gives '5 4 3 2 1'
"""
-from pypy.rlib.jit import hint, JitDriver
+from pypy.rlib.jit import hint, promote, JitDriver
#
# See pypy/doc/jit.txt for a higher-level overview of the JIT techniques
@@ -77,9 +77,9 @@
# ones. The JIT compiler cannot look into indirect calls, but it
# can analyze and inline the code in directly-called functions.
stack, y = stack.pop()
- hint(y.__class__, promote=True)
+ promote(y.__class__)
stack, x = stack.pop()
- hint(x.__class__, promote=True)
+ promote(x.__class__)
try:
z = IntBox(func_int(x.as_int(), y.as_int()))
except ValueError:
@@ -120,7 +120,7 @@
# modified.
oldloops = invariants
oldargs = reds.args
- argcount = hint(len(oldargs), promote=True)
+ argcount = promote(len(oldargs))
args = []
n = 0
while n < argcount:
@@ -189,7 +189,7 @@
# because it was pushed as a compile-time constant by the '{'
# case above into 'loops', which is a virtual list, so the
# promotion below is just a way to make the colors match.
- pos = hint(pos, promote=True)
+ pos = promote(pos)
tinyjitdriver.can_enter_jit(args=args, loops=loops, stack=stack,
bytecode=bytecode, pos=pos)
else:
diff --git a/pypy/jit/tl/tiny3_hotpath.py b/pypy/jit/tl/tiny3_hotpath.py
--- a/pypy/jit/tl/tiny3_hotpath.py
+++ b/pypy/jit/tl/tiny3_hotpath.py
@@ -28,7 +28,7 @@
{ #1 #1 1 SUB ->#1 #1 } => when called with 5, gives '5 4 3 2 1'
"""
-from pypy.rlib.jit import hint, JitDriver
+from pypy.rlib.jit import promote, hint, JitDriver
from pypy.rlib.objectmodel import specialize
#
@@ -83,9 +83,9 @@
# ones. The JIT compiler cannot look into indirect calls, but it
# can analyze and inline the code in directly-called functions.
stack, y = stack.pop()
- hint(y.__class__, promote=True)
+ promote(y.__class__)
stack, x = stack.pop()
- hint(x.__class__, promote=True)
+ promote(x.__class__)
if isinstance(x, IntBox) and isinstance(y, IntBox):
z = IntBox(func_int(x.as_int(), y.as_int()))
else:
@@ -125,7 +125,7 @@
# modified.
oldloops = invariants
oldargs = reds.args
- argcount = hint(len(oldargs), promote=True)
+ argcount = promote(len(oldargs))
args = []
n = 0
while n < argcount:
@@ -194,7 +194,7 @@
# because it was pushed as a compile-time constant by the '{'
# case above into 'loops', which is a virtual list, so the
# promotion below is just a way to make the colors match.
- pos = hint(pos, promote=True)
+ pos = promote(pos)
tinyjitdriver.can_enter_jit(args=args, loops=loops, stack=stack,
bytecode=bytecode, pos=pos)
else:
diff --git a/pypy/jit/tl/tl.py b/pypy/jit/tl/tl.py
--- a/pypy/jit/tl/tl.py
+++ b/pypy/jit/tl/tl.py
@@ -2,7 +2,7 @@
import py
from pypy.jit.tl.tlopcode import *
-from pypy.rlib.jit import JitDriver, hint, dont_look_inside
+from pypy.rlib.jit import JitDriver, hint, dont_look_inside, promote
def char2int(c):
t = ord(c)
@@ -81,7 +81,7 @@
myjitdriver.jit_merge_point(pc=pc, code=code,
stack=stack, inputarg=inputarg)
opcode = ord(code[pc])
- stack.stackpos = hint(stack.stackpos, promote=True)
+ stack.stackpos = promote(stack.stackpos)
pc += 1
if opcode == NOP:
diff --git a/pypy/jit/tl/tlc.py b/pypy/jit/tl/tlc.py
--- a/pypy/jit/tl/tlc.py
+++ b/pypy/jit/tl/tlc.py
@@ -5,7 +5,7 @@
from pypy.rlib.objectmodel import specialize, we_are_translated
from pypy.jit.tl.tlopcode import *
from pypy.jit.tl import tlopcode
-from pypy.rlib.jit import JitDriver
+from pypy.rlib.jit import JitDriver, elidable
class Obj(object):
@@ -71,6 +71,7 @@
classes = [] # [(descr, cls), ...]
+ @elidable
def get(key):
for descr, cls in Class.classes:
if key.attributes == descr.attributes and\
@@ -79,7 +80,6 @@
result = Class(key)
Class.classes.append((key, result))
return result
- get._pure_function_ = True
get = staticmethod(get)
def __init__(self, descr):
diff --git a/pypy/jit/tool/oparser.py b/pypy/jit/tool/oparser.py
--- a/pypy/jit/tool/oparser.py
+++ b/pypy/jit/tool/oparser.py
@@ -337,6 +337,11 @@
num += 1
return num, ops, last_offset
+ def postprocess(self, loop):
+ """ A hook that can be overloaded to do some postprocessing
+ """
+ return loop
+
def parse_offset(self, line):
if line.startswith('+'):
# it begins with an offset, like: "+10: i1 = int_add(...)"
diff --git a/pypy/module/__builtin__/__init__.py b/pypy/module/__builtin__/__init__.py
--- a/pypy/module/__builtin__/__init__.py
+++ b/pypy/module/__builtin__/__init__.py
@@ -5,20 +5,6 @@
# put builtins here that should be optimized somehow
-OPTIMIZED_BUILTINS = ["len", "range", "xrange", "min", "max", "enumerate",
- "isinstance", "type", "zip", "file", "format", "open", "abs", "chr",
- "unichr", "ord", "pow", "repr", "hash", "oct", "hex", "round", "cmp",
- "getattr", "setattr", "delattr", "callable", "int", "str", "float"]
-
-assert len(OPTIMIZED_BUILTINS) <= 256
-
-BUILTIN_TO_INDEX = {}
-
-for i, name in enumerate(OPTIMIZED_BUILTINS):
- BUILTIN_TO_INDEX[name] = i
-
-assert len(OPTIMIZED_BUILTINS) == len(BUILTIN_TO_INDEX)
-
class Module(MixedModule):
"""Built-in functions, exceptions, and other objects."""
expose__file__attribute = False
@@ -141,9 +127,6 @@
def setup_after_space_initialization(self):
"""NOT_RPYTHON"""
space = self.space
- self.builtins_by_index = [None] * len(OPTIMIZED_BUILTINS)
- for i, name in enumerate(OPTIMIZED_BUILTINS):
- self.builtins_by_index[i] = space.getattr(self, space.wrap(name))
# install the more general version of isinstance() & co. in the space
from pypy.module.__builtin__ import abstractinst as ab
space.abstract_isinstance_w = ab.abstract_isinstance_w.__get__(space)
diff --git a/pypy/module/__builtin__/abstractinst.py b/pypy/module/__builtin__/abstractinst.py
--- a/pypy/module/__builtin__/abstractinst.py
+++ b/pypy/module/__builtin__/abstractinst.py
@@ -58,7 +58,10 @@
# -- case (anything, type)
try:
- w_result = space.isinstance(w_obj, w_klass_or_tuple, allow_override)
+ if allow_override:
+ w_result = space.isinstance_allow_override(w_obj, w_klass_or_tuple)
+ else:
+ w_result = space.isinstance(w_obj, w_klass_or_tuple)
except OperationError, e: # if w_klass_or_tuple was not a type, ignore it
if not e.match(space, space.w_TypeError):
raise # propagate other errors
@@ -72,8 +75,11 @@
w_pretendtype = space.getattr(w_obj, space.wrap('__class__'))
if space.is_w(w_pretendtype, space.type(w_obj)):
return False # common case: obj.__class__ is type(obj)
- w_result = space.issubtype(w_pretendtype, w_klass_or_tuple,
- allow_override)
+ if allow_override:
+ w_result = space.issubtype_allow_override(w_pretendtype,
+ w_klass_or_tuple)
+ else:
+ w_result = space.issubtype(w_pretendtype, w_klass_or_tuple)
except OperationError, e:
if e.async(space):
raise
@@ -134,7 +140,11 @@
# -- case (type, type)
try:
- w_result = space.issubtype(w_derived, w_klass_or_tuple, allow_override)
+ if allow_override:
+ w_result = space.issubtype_allow_override(w_derived,
+ w_klass_or_tuple)
+ else:
+ w_result = space.issubtype(w_derived, w_klass_or_tuple)
except OperationError, e: # if one of the args was not a type, ignore it
if not e.match(space, space.w_TypeError):
raise # propagate other errors
diff --git a/pypy/module/__builtin__/compiling.py b/pypy/module/__builtin__/compiling.py
--- a/pypy/module/__builtin__/compiling.py
+++ b/pypy/module/__builtin__/compiling.py
@@ -5,7 +5,7 @@
from pypy.interpreter.pycode import PyCode
from pypy.interpreter.error import OperationError
from pypy.interpreter.astcompiler import consts, ast
-from pypy.interpreter.gateway import NoneNotWrapped, unwrap_spec
+from pypy.interpreter.gateway import unwrap_spec
@unwrap_spec(filename=str, mode=str, flags=int, dont_inherit=int)
def compile(space, w_source, filename, mode, flags=0, dont_inherit=0):
diff --git a/pypy/module/__builtin__/descriptor.py b/pypy/module/__builtin__/descriptor.py
--- a/pypy/module/__builtin__/descriptor.py
+++ b/pypy/module/__builtin__/descriptor.py
@@ -1,12 +1,10 @@
-
-from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.error import OperationError
+from pypy.interpreter.function import StaticMethod, ClassMethod
from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.objspace.descroperation import object_getattribute, object_setattr
-from pypy.interpreter.function import StaticMethod, ClassMethod
-from pypy.interpreter.typedef import GetSetProperty, descr_get_dict, \
- descr_set_dict, interp_attrproperty_w, generic_new_descr
+from pypy.interpreter.typedef import (TypeDef, interp_attrproperty_w,
+ generic_new_descr)
+from pypy.objspace.descroperation import object_getattribute
class W_Super(Wrappable):
def __init__(self, space, w_starttype, w_objtype, w_self):
diff --git a/pypy/module/__builtin__/functional.py b/pypy/module/__builtin__/functional.py
--- a/pypy/module/__builtin__/functional.py
+++ b/pypy/module/__builtin__/functional.py
@@ -4,13 +4,12 @@
"""
from pypy.interpreter.error import OperationError
-from pypy.interpreter.gateway import NoneNotWrapped, applevel
+from pypy.interpreter.gateway import NoneNotWrapped
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.baseobjspace import Wrappable
from pypy.rlib.rarithmetic import r_uint, intmask
from pypy.rlib.objectmodel import specialize
-from inspect import getsource, getfile
from pypy.rlib.rbigint import rbigint
@@ -662,7 +661,6 @@
def descr_reduce(self):
from pypy.interpreter.mixedmodule import MixedModule
- from pypy.module._pickle_support import maker # helper fns
space = self.space
w_mod = space.getbuiltinmodule('_pickle_support')
mod = space.interp_w(MixedModule, w_mod)
diff --git a/pypy/module/__builtin__/interp_classobj.py b/pypy/module/__builtin__/interp_classobj.py
--- a/pypy/module/__builtin__/interp_classobj.py
+++ b/pypy/module/__builtin__/interp_classobj.py
@@ -1,18 +1,16 @@
import new
from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.interpreter.gateway import NoneNotWrapped, applevel, interp2app
+from pypy.interpreter.gateway import interp2app
from pypy.interpreter.typedef import TypeDef, make_weakref_descr
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.typedef import GetSetProperty, descr_get_dict
-from pypy.interpreter.typedef import descr_set_dict
-from pypy.rlib.rarithmetic import r_uint, intmask
+from pypy.interpreter.typedef import GetSetProperty, descr_get_dict, descr_set_dict
from pypy.rlib.objectmodel import compute_identity_hash
from pypy.rlib.debug import make_sure_not_resized
from pypy.rlib import jit
def raise_type_err(space, argument, expected, w_obj):
- type_name = space.type(w_obj).getname(space, '?')
+ type_name = space.type(w_obj).getname(space)
raise operationerrfmt(space.w_TypeError,
"argument %s must be %s, not %s",
argument, expected, type_name)
@@ -420,7 +418,7 @@
if w_meth is not None:
space.call_function(w_meth, w_name)
else:
- if not self.deldictvalue(space, w_name):
+ if not self.deldictvalue(space, name):
raise operationerrfmt(
space.w_AttributeError,
"%s instance has no attribute '%s'",
diff --git a/pypy/module/__builtin__/interp_memoryview.py b/pypy/module/__builtin__/interp_memoryview.py
--- a/pypy/module/__builtin__/interp_memoryview.py
+++ b/pypy/module/__builtin__/interp_memoryview.py
@@ -2,7 +2,7 @@
Implementation of the 'buffer' and 'memoryview' types.
"""
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter import gateway, buffer
+from pypy.interpreter import buffer
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.interpreter.error import OperationError
diff --git a/pypy/module/__builtin__/operation.py b/pypy/module/__builtin__/operation.py
--- a/pypy/module/__builtin__/operation.py
+++ b/pypy/module/__builtin__/operation.py
@@ -4,12 +4,10 @@
from pypy.interpreter import gateway
from pypy.interpreter.error import OperationError
-from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.gateway import unwrap_spec
from pypy.rlib.runicode import UNICHR
from pypy.rlib.rfloat import isnan, isinf, round_double
from pypy.rlib import rfloat
-import math
import __builtin__
NoneNotWrapped = gateway.NoneNotWrapped
diff --git a/pypy/module/__builtin__/test/test_builtin.py b/pypy/module/__builtin__/test/test_builtin.py
--- a/pypy/module/__builtin__/test/test_builtin.py
+++ b/pypy/module/__builtin__/test/test_builtin.py
@@ -631,62 +631,6 @@
raises(TypeError, pr, end=3)
raises(TypeError, pr, sep=42)
-class AppTestBuiltinOptimized(object):
- def setup_class(cls):
- from pypy.conftest import gettestobjspace
- cls.space = gettestobjspace(**{"objspace.opcodes.CALL_LIKELY_BUILTIN": True})
-
- # hum, we need to invoke the compiler explicitely
- def test_xrange_len(self):
- s = """def test():
- x = xrange(33)
- assert len(x) == 33
- x = xrange(33.2)
- assert len(x) == 33
- x = xrange(33,0,-1)
- assert len(x) == 33
- x = xrange(33,0)
- assert len(x) == 0
- x = xrange(33,0.2)
- assert len(x) == 0
- x = xrange(0,33)
- assert len(x) == 33
- x = xrange(0,33,-1)
- assert len(x) == 0
- x = xrange(0,33,2)
- assert len(x) == 17
- x = xrange(0,32,2)
- assert len(x) == 16
- """
- ns = {}
- exec s in ns
- ns["test"]()
-
- def test_delete_from_builtins(self):
- s = """ """
- # XXX write this test!
-
- def test_shadow_case_bound_method(self):
- s = """def test(l):
- n = len(l)
- old_len = len
- class A(object):
- x = 5
- def length(self, o):
- return self.x*old_len(o)
- import __builtin__
- __builtin__.len = A().length
- try:
- m = len(l)
- finally:
- __builtin__.len = old_len
- return n+m
- """
- ns = {}
- exec s in ns
- res = ns["test"]([2,3,4])
- assert res == 18
-
def test_round(self):
assert round(11.234) == 11.0
assert round(11.234, -1) == 10.0
diff --git a/pypy/module/__builtin__/test/test_classobj.py b/pypy/module/__builtin__/test/test_classobj.py
--- a/pypy/module/__builtin__/test/test_classobj.py
+++ b/pypy/module/__builtin__/test/test_classobj.py
@@ -987,9 +987,9 @@
if option.runappdirect:
py.test.skip("can only be run on py.py")
def is_strdict(space, w_class):
- from pypy.objspace.std.dictmultiobject import StrDictImplementation
+ from pypy.objspace.std.dictmultiobject import StringDictStrategy
w_d = w_class.getdict(space)
- return space.wrap(isinstance(w_d, StrDictImplementation) and w_d.r_dict_content is None)
+ return space.wrap(isinstance(w_d.strategy, StringDictStrategy))
cls.w_is_strdict = cls.space.wrap(gateway.interp2app(is_strdict))
diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py
--- a/pypy/module/__pypy__/__init__.py
+++ b/pypy/module/__pypy__/__init__.py
@@ -25,6 +25,7 @@
'debug_print_once' : 'interp_debug.debug_print_once',
'builtinify' : 'interp_magic.builtinify',
'lookup_special' : 'interp_magic.lookup_special',
+ 'do_what_I_mean' : 'interp_magic.do_what_I_mean',
}
submodules = {
diff --git a/pypy/module/__pypy__/interp_debug.py b/pypy/module/__pypy__/interp_debug.py
--- a/pypy/module/__pypy__/interp_debug.py
+++ b/pypy/module/__pypy__/interp_debug.py
@@ -1,5 +1,4 @@
-from pypy.interpreter.gateway import interp2app, NoneNotWrapped, unwrap_spec
-from pypy.interpreter.error import OperationError
+from pypy.interpreter.gateway import unwrap_spec
from pypy.rlib import debug, jit
diff --git a/pypy/module/__pypy__/interp_identitydict.py b/pypy/module/__pypy__/interp_identitydict.py
--- a/pypy/module/__pypy__/interp_identitydict.py
+++ b/pypy/module/__pypy__/interp_identitydict.py
@@ -1,6 +1,6 @@
from pypy.interpreter.error import OperationError
from pypy.interpreter.typedef import TypeDef
-from pypy.interpreter.gateway import NoneNotWrapped, interp2app, unwrap_spec
+from pypy.interpreter.gateway import interp2app
from pypy.interpreter.baseobjspace import Wrappable
class W_IdentityDict(Wrappable):
diff --git a/pypy/module/__pypy__/interp_magic.py b/pypy/module/__pypy__/interp_magic.py
--- a/pypy/module/__pypy__/interp_magic.py
+++ b/pypy/module/__pypy__/interp_magic.py
@@ -70,3 +70,6 @@
if w_descr is None:
return space.w_None
return space.get(w_descr, w_obj)
+
+def do_what_I_mean(space):
+ return space.wrap(42)
diff --git a/pypy/module/__pypy__/test/test_special.py b/pypy/module/__pypy__/test/test_special.py
--- a/pypy/module/__pypy__/test/test_special.py
+++ b/pypy/module/__pypy__/test/test_special.py
@@ -49,3 +49,8 @@
class X:
pass
raises(TypeError, lookup_special, X(), "foo")
+
+ def test_do_what_I_mean(self):
+ from __pypy__ import do_what_I_mean
+ x = do_what_I_mean()
+ assert x == 42
diff --git a/pypy/module/_ast/test/test_ast.py b/pypy/module/_ast/test/test_ast.py
--- a/pypy/module/_ast/test/test_ast.py
+++ b/pypy/module/_ast/test/test_ast.py
@@ -186,6 +186,11 @@
mod = self.get_ast("from __future__ import with_statement; import y; " \
"from __future__ import nested_scopes")
raises(SyntaxError, compile, mod, "<test>", "exec")
+ mod = self.get_ast("from __future__ import division\nx = 1/2")
+ co = compile(mod, "<test>", "exec")
+ ns = {}
+ exec co in ns
+ assert ns["x"] == .5
def test_field_attr_writable(self):
import _ast as ast
@@ -245,3 +250,38 @@
assert x.left == n1
assert x.op == addop
assert x.right == n3
+
+ def test_functiondef(self):
+ import _ast as ast
+ fAst = ast.FunctionDef(
+ name="foo",
+ args=ast.arguments(
+ args=[], vararg=None, kwarg=None, defaults=[],
+ kwonlyargs=[], kw_defaults=[]),
+ body=[], decorator_list=[], lineno=5, col_offset=0)
+ exprAst = ast.Interactive(body=[fAst])
+ compiled = compile(exprAst, "<foo>", "single")
+ #
+ d = {}
+ eval(compiled, d, d)
+ assert type(d['foo']) is type(lambda: 42)
+ assert d['foo']() is None
+
+ def test_missing_name(self):
+ import _ast as ast
+ n = ast.FunctionDef(name=None)
+ n.name = "foo"
+ n.name = "foo"
+ n.name = "foo"
+ assert n.name == "foo"
+
+ def test_issue793(self):
+ import _ast as ast
+ body = ast.Module([
+ ast.TryExcept([ast.Pass(lineno=2, col_offset=4)],
+ [ast.ExceptHandler(ast.Name('Exception', ast.Load(),
+ lineno=3, col_offset=0),
+ None, [], lineno=4, col_offset=0)],
+ [], lineno=1, col_offset=0)
+ ])
+ exec compile(body, '<string>', 'exec')
diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py
--- a/pypy/module/_codecs/interp_codecs.py
+++ b/pypy/module/_codecs/interp_codecs.py
@@ -1,6 +1,6 @@
from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.interpreter.gateway import NoneNotWrapped, interp2app, unwrap_spec
-from pypy.rlib.rstring import StringBuilder, UnicodeBuilder
+from pypy.rlib.rstring import UnicodeBuilder
from pypy.rlib.objectmodel import we_are_translated
class CodecState(object):
@@ -186,7 +186,7 @@
text = u'\ufffd' * size
return space.newtuple([space.wrap(text), w_end])
else:
- typename = space.type(w_exc).getname(space, '?')
+ typename = space.type(w_exc).getname(space)
raise operationerrfmt(space.w_TypeError,
"don't know how to handle %s in error callback", typename)
@@ -207,7 +207,7 @@
pos += 1
return space.newtuple([space.wrap(builder.build()), w_end])
else:
- typename = space.type(w_exc).getname(space, '?')
+ typename = space.type(w_exc).getname(space)
raise operationerrfmt(space.w_TypeError,
"don't know how to handle %s in error callback", typename)
@@ -240,7 +240,7 @@
pos += 1
return space.newtuple([space.wrap(builder.build()), w_end])
else:
- typename = space.type(w_exc).getname(space, '?')
+ typename = space.type(w_exc).getname(space)
raise operationerrfmt(space.w_TypeError,
"don't know how to handle %s in error callback", typename)
diff --git a/pypy/module/_ffi/interp_ffi.py b/pypy/module/_ffi/interp_ffi.py
--- a/pypy/module/_ffi/interp_ffi.py
+++ b/pypy/module/_ffi/interp_ffi.py
@@ -1,9 +1,8 @@
-import sys
-from pypy.interpreter.baseobjspace import Wrappable, Arguments
+from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.error import OperationError, wrap_oserror, \
operationerrfmt
-from pypy.interpreter.gateway import interp2app, NoneNotWrapped, unwrap_spec
-from pypy.interpreter.typedef import TypeDef, GetSetProperty
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.typedef import TypeDef
from pypy.module._rawffi.structure import W_StructureInstance, W_Structure
#
from pypy.rpython.lltypesystem import lltype, rffi
@@ -16,7 +15,7 @@
class W_FFIType(Wrappable):
_immutable_fields_ = ['name', 'ffitype', 'w_datashape', 'w_pointer_to']
-
+
def __init__(self, name, ffitype, w_datashape=None, w_pointer_to=None):
self.name = name
self.ffitype = ffitype
@@ -83,7 +82,6 @@
def build_ffi_types():
- from pypy.rlib.clibffi import FFI_TYPE_P
types = [
# note: most of the type name directly come from the C equivalent,
# with the exception of bytes: in C, ubyte and char are equivalent,
@@ -149,13 +147,19 @@
raise OperationError(space.w_TypeError, space.wrap(msg))
return res
+def unwrap_truncate_int(TP, space, w_arg):
+ if space.is_true(space.isinstance(w_arg, space.w_int)):
+ return rffi.cast(TP, space.int_w(w_arg))
+ else:
+ return rffi.cast(TP, space.bigint_w(w_arg).ulonglongmask())
+unwrap_truncate_int._annspecialcase_ = 'specialize:arg(0)'
# ========================================================================
class W_FuncPtr(Wrappable):
_immutable_fields_ = ['func', 'argtypes_w[*]', 'w_restype']
-
+
def __init__(self, func, argtypes_w, w_restype):
self.func = func
self.argtypes_w = argtypes_w
@@ -181,15 +185,14 @@
# note that we must check for longlong first, because either
# is_signed or is_unsigned returns true anyway
assert libffi.IS_32_BIT
- kind = libffi.types.getkind(w_argtype.ffitype) # XXX: remove the kind
- self.arg_longlong(space, argchain, kind, w_arg)
+ self.arg_longlong(space, argchain, w_arg)
elif w_argtype.is_signed():
- argchain.arg(space.int_w(w_arg))
+ argchain.arg(unwrap_truncate_int(rffi.LONG, space, w_arg))
elif w_argtype.is_pointer():
w_arg = self.convert_pointer_arg_maybe(space, w_arg, w_argtype)
argchain.arg(intmask(space.uint_w(w_arg)))
elif w_argtype.is_unsigned():
- argchain.arg(intmask(space.uint_w(w_arg)))
+ argchain.arg(unwrap_truncate_int(rffi.ULONG, space, w_arg))
elif w_argtype.is_char():
w_arg = space.ord(w_arg)
argchain.arg(space.int_w(w_arg))
@@ -202,7 +205,7 @@
argchain.arg_singlefloat(space.float_w(w_arg))
elif w_argtype.is_struct():
# arg_raw directly takes value to put inside ll_args
- w_arg = space.interp_w(W_StructureInstance, w_arg)
+ w_arg = space.interp_w(W_StructureInstance, w_arg)
ptrval = w_arg.ll_buffer
argchain.arg_raw(ptrval)
else:
@@ -220,22 +223,17 @@
return w_arg
@jit.dont_look_inside
- def arg_longlong(self, space, argchain, kind, w_arg):
+ def arg_longlong(self, space, argchain, w_arg):
bigarg = space.bigint_w(w_arg)
- if kind == 'I':
- llval = bigarg.tolonglong()
- elif kind == 'U':
- ullval = bigarg.toulonglong()
- llval = rffi.cast(rffi.LONGLONG, ullval)
- else:
- assert False
+ ullval = bigarg.ulonglongmask()
+ llval = rffi.cast(rffi.LONGLONG, ullval)
# this is a hack: we store the 64 bits of the long long into the
# 64 bits of a float (i.e., a C double)
floatval = libffi.longlong2float(llval)
argchain.arg_longlong(floatval)
def call(self, space, args_w):
- self = jit.hint(self, promote=True)
+ self = jit.promote(self)
argchain = self.build_argchain(space, args_w)
w_restype = self.w_restype
if w_restype.is_longlong():
@@ -404,7 +402,7 @@
except KeyError:
raise operationerrfmt(space.w_AttributeError,
"No symbol %s found in library %s", name, self.name)
-
+
return W_FuncPtr(func, argtypes_w, w_restype)
@unwrap_spec(name=str)
diff --git a/pypy/module/_ffi/test/test__ffi.py b/pypy/module/_ffi/test/test__ffi.py
--- a/pypy/module/_ffi/test/test__ffi.py
+++ b/pypy/module/_ffi/test/test__ffi.py
@@ -111,7 +111,6 @@
types.double)
assert pow(2, 3) == 8
-
def test_int_args(self):
"""
DLLEXPORT int sum_xy(int x, int y)
@@ -119,10 +118,12 @@
return x+y;
}
"""
+ import sys
from _ffi import CDLL, types
libfoo = CDLL(self.libfoo_name)
sum_xy = libfoo.getfunc('sum_xy', [types.sint, types.sint], types.sint)
assert sum_xy(30, 12) == 42
+ assert sum_xy(sys.maxint*2, 0) == -2
def test_void_result(self):
"""
@@ -247,6 +248,9 @@
types.ulong)
assert sum_xy(sys.maxint, 12) == sys.maxint+12
assert sum_xy(sys.maxint+1, 12) == sys.maxint+13
+ #
+ res = sum_xy(sys.maxint*2+3, 0)
+ assert res == 1
def test_unsigned_short_args(self):
"""
@@ -375,6 +379,9 @@
res = sum_xy(x, y)
expected = maxint64 + 3
assert res == expected
+ #
+ res = sum_xy(maxint64*2+3, 0)
+ assert res == 1
def test_byval_argument(self):
"""
diff --git a/pypy/module/_file/interp_file.py b/pypy/module/_file/interp_file.py
--- a/pypy/module/_file/interp_file.py
+++ b/pypy/module/_file/interp_file.py
@@ -43,11 +43,17 @@
# assume that the file and stream objects are only visible in the
# thread that runs __del__, so no race condition should be possible
self.clear_all_weakrefs()
+ if self.stream is not None:
+ self.enqueue_for_destruction(self.space, W_File.destructor,
+ 'close() method of ')
+
+ def destructor(self):
+ assert isinstance(self, W_File)
try:
self.direct_close()
except StreamErrors, e:
operr = wrap_streamerror(self.space, e, self.w_name)
- operr.write_unraisable(self.space, '__del__ of ', self)
+ raise operr
def fdopenstream(self, stream, fd, mode, w_name=None):
self.fd = fd
diff --git a/pypy/module/_file/interp_stream.py b/pypy/module/_file/interp_stream.py
--- a/pypy/module/_file/interp_stream.py
+++ b/pypy/module/_file/interp_stream.py
@@ -3,12 +3,10 @@
from pypy.rlib.streamio import StreamErrors
from pypy.interpreter.error import OperationError, wrap_oserror2
-from pypy.interpreter.gateway import ObjSpace
-from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.baseobjspace import ObjSpace, Wrappable
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.gateway import interp2app
-import os
def wrap_streamerror(space, e, w_filename=None):
if isinstance(e, streamio.StreamError):
diff --git a/pypy/module/_io/interp_bufferedio.py b/pypy/module/_io/interp_bufferedio.py
--- a/pypy/module/_io/interp_bufferedio.py
+++ b/pypy/module/_io/interp_bufferedio.py
@@ -4,7 +4,6 @@
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.interpreter.buffer import RWBuffer
-from pypy.rpython.lltypesystem import lltype, rffi
from pypy.rlib.rstring import StringBuilder
from pypy.rlib.rarithmetic import r_longlong, intmask
from pypy.tool.sourcetools import func_renamer
@@ -175,7 +174,7 @@
return space.call_method(self.w_raw, "isatty")
def repr_w(self, space):
- typename = space.type(self).getname(space, '?')
+ typename = space.type(self).getname(space)
module = space.str_w(space.type(self).get_module())
try:
w_name = space.getattr(self, space.wrap("name"))
diff --git a/pypy/module/_io/interp_fileio.py b/pypy/module/_io/interp_fileio.py
--- a/pypy/module/_io/interp_fileio.py
+++ b/pypy/module/_io/interp_fileio.py
@@ -1,5 +1,4 @@
-from pypy.interpreter.typedef import (
- TypeDef, interp_attrproperty, interp_attrproperty_w, GetSetProperty)
+from pypy.interpreter.typedef import TypeDef, interp_attrproperty, GetSetProperty
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.error import OperationError, wrap_oserror, wrap_oserror2
from pypy.rlib.rarithmetic import r_longlong
diff --git a/pypy/module/_io/interp_io.py b/pypy/module/_io/interp_io.py
--- a/pypy/module/_io/interp_io.py
+++ b/pypy/module/_io/interp_io.py
@@ -6,7 +6,6 @@
TypeDef, interp_attrproperty, generic_new_descr)
from pypy.module.exceptions.interp_exceptions import W_IOError
from pypy.module._io.interp_fileio import W_FileIO
-from pypy.module._io.interp_iobase import W_IOBase
from pypy.module._io.interp_textio import W_TextIOWrapper
from pypy.rpython.module.ll_os_stat import STAT_FIELD_TYPES
@@ -119,7 +118,7 @@
if buffering < 0:
buffering = DEFAULT_BUFFER_SIZE
- if "st_blksize" in STAT_FIELD_TYPES:
+ if space.config.translation.type_system == 'lltype' and 'st_blksize' in STAT_FIELD_TYPES:
fileno = space.int_w(space.call_method(w_raw, "fileno"))
try:
st = os.fstat(fileno)
diff --git a/pypy/module/_io/interp_iobase.py b/pypy/module/_io/interp_iobase.py
--- a/pypy/module/_io/interp_iobase.py
+++ b/pypy/module/_io/interp_iobase.py
@@ -57,6 +57,11 @@
def __del__(self):
self.clear_all_weakrefs()
+ self.enqueue_for_destruction(self.space, W_IOBase.destructor,
+ 'internal __del__ of ')
+
+ def destructor(self):
+ assert isinstance(self, W_IOBase)
space = self.space
w_closed = space.findattr(self, space.wrap('closed'))
try:
@@ -155,7 +160,7 @@
raise operationerrfmt(
space.w_IOError,
"peek() should have returned a bytes object, "
- "not '%s'", space.type(w_readahead).getname(space, '?'))
+ "not '%s'", space.type(w_readahead).getname(space))
length = space.len_w(w_readahead)
if length > 0:
n = 0
@@ -181,7 +186,7 @@
raise operationerrfmt(
space.w_IOError,
"peek() should have returned a bytes object, "
- "not '%s'", space.type(w_read).getname(space, '?'))
+ "not '%s'", space.type(w_read).getname(space))
read = space.str_w(w_read)
if not read:
break
diff --git a/pypy/module/_io/interp_stringio.py b/pypy/module/_io/interp_stringio.py
--- a/pypy/module/_io/interp_stringio.py
+++ b/pypy/module/_io/interp_stringio.py
@@ -129,7 +129,7 @@
if not space.isinstance_w(w_obj, space.w_unicode):
raise operationerrfmt(space.w_TypeError,
"string argument expected, got '%s'",
- space.type(w_obj).getname(space, '?'))
+ space.type(w_obj).getname(space))
self._check_closed(space)
orig_size = space.len_w(w_obj)
diff --git a/pypy/module/_io/interp_textio.py b/pypy/module/_io/interp_textio.py
--- a/pypy/module/_io/interp_textio.py
+++ b/pypy/module/_io/interp_textio.py
@@ -4,7 +4,7 @@
generic_new_descr)
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.error import OperationError
from pypy.rlib.rarithmetic import intmask, r_ulonglong, r_uint
from pypy.rlib.rbigint import rbigint
from pypy.rlib.rstring import UnicodeBuilder
diff --git a/pypy/module/_locale/interp_locale.py b/pypy/module/_locale/interp_locale.py
--- a/pypy/module/_locale/interp_locale.py
+++ b/pypy/module/_locale/interp_locale.py
@@ -1,4 +1,3 @@
-from pypy.rpython.tool import rffi_platform as platform
from pypy.rlib import rposix
from pypy.rlib.rarithmetic import intmask
diff --git a/pypy/module/_lsprof/interp_lsprof.py b/pypy/module/_lsprof/interp_lsprof.py
--- a/pypy/module/_lsprof/interp_lsprof.py
+++ b/pypy/module/_lsprof/interp_lsprof.py
@@ -149,7 +149,7 @@
factor * float(self.ll_it), w_sublist)
return space.wrap(w_se)
- @jit.purefunction
+ @jit.elidable
def _get_or_make_subentry(self, entry, make=True):
try:
return self.calls[entry]
@@ -167,7 +167,7 @@
self.previous = profobj.current_context
entry.recursionLevel += 1
if profobj.subcalls and self.previous:
- caller = jit.hint(self.previous.entry, promote=True)
+ caller = jit.promote(self.previous.entry)
subentry = caller._get_or_make_subentry(entry)
subentry.recursionLevel += 1
self.ll_t0 = profobj.ll_timer()
@@ -179,7 +179,7 @@
self.previous.ll_subt += tt
entry._stop(tt, it)
if profobj.subcalls and self.previous:
- caller = jit.hint(self.previous.entry, promote=True)
+ caller = jit.promote(self.previous.entry)
subentry = caller._get_or_make_subentry(entry, False)
if subentry is not None:
subentry._stop(tt, it)
@@ -212,7 +212,7 @@
module += '.'
return '{%s%s}' % (module, w_arg.name)
else:
- class_name = space.type(w_arg).getname(space, '?')
+ class_name = space.type(w_arg).getname(space)
return "{'%s' object}" % (class_name,)
def lsprof_call(space, w_self, frame, event, w_arg):
@@ -282,7 +282,7 @@
c_setup_profiling()
space.getexecutioncontext().setllprofile(lsprof_call, space.wrap(self))
- @jit.purefunction
+ @jit.elidable
def _get_or_make_entry(self, f_code, make=True):
try:
return self.data[f_code]
@@ -293,7 +293,7 @@
return entry
return None
- @jit.purefunction
+ @jit.elidable
def _get_or_make_builtin_entry(self, key, make=True):
try:
return self.builtin_data[key]
@@ -306,7 +306,7 @@
def _enter_call(self, f_code):
# we have a superb gc, no point in freelist :)
- self = jit.hint(self, promote=True)
+ self = jit.promote(self)
entry = self._get_or_make_entry(f_code)
self.current_context = ProfilerContext(self, entry)
@@ -314,14 +314,14 @@
context = self.current_context
if context is None:
return
- self = jit.hint(self, promote=True)
+ self = jit.promote(self)
entry = self._get_or_make_entry(f_code, False)
if entry is not None:
context._stop(self, entry)
self.current_context = context.previous
def _enter_builtin_call(self, key):
- self = jit.hint(self, promote=True)
+ self = jit.promote(self)
entry = self._get_or_make_builtin_entry(key)
self.current_context = ProfilerContext(self, entry)
@@ -329,7 +329,7 @@
context = self.current_context
if context is None:
return
- self = jit.hint(self, promote=True)
+ self = jit.promote(self)
entry = self._get_or_make_builtin_entry(key, False)
if entry is not None:
context._stop(self, entry)
diff --git a/pypy/module/_lsprof/test/test_cprofile.py b/pypy/module/_lsprof/test/test_cprofile.py
--- a/pypy/module/_lsprof/test/test_cprofile.py
+++ b/pypy/module/_lsprof/test/test_cprofile.py
@@ -181,8 +181,7 @@
class AppTestWithDifferentBytecodes(AppTestCProfile):
- keywords = {'objspace.opcodes.CALL_LIKELY_BUILTIN': True,
- 'objspace.opcodes.CALL_METHOD': True}
+ keywords = {'objspace.opcodes.CALL_METHOD': True}
expected_output = {}
diff --git a/pypy/module/_minimal_curses/fficurses.py b/pypy/module/_minimal_curses/fficurses.py
--- a/pypy/module/_minimal_curses/fficurses.py
+++ b/pypy/module/_minimal_curses/fficurses.py
@@ -2,12 +2,10 @@
""" The ffi for rpython, need to be imported for side effects
"""
-import sys
from pypy.rpython.lltypesystem import rffi
from pypy.rpython.lltypesystem import lltype
from pypy.rpython.tool import rffi_platform
from pypy.rpython.extfunc import register_external
-from pypy.rpython.extregistry import ExtRegistryEntry
from pypy.module._minimal_curses import interp_curses
from pypy.translator.tool.cbuild import ExternalCompilationInfo
@@ -82,7 +80,7 @@
return res
finally:
rffi.free_charp(ll_cap)
-
+
register_external(interp_curses._curses_tigetstr, [str], str,
export_name='_curses.tigetstr', llimpl=tigetstr_llimpl)
diff --git a/pypy/module/_multibytecodec/c_codecs.py b/pypy/module/_multibytecodec/c_codecs.py
--- a/pypy/module/_multibytecodec/c_codecs.py
+++ b/pypy/module/_multibytecodec/c_codecs.py
@@ -1,4 +1,4 @@
-import py, sys
+import py
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.translator.tool.cbuild import ExternalCompilationInfo
from pypy.tool.autopath import pypydir
@@ -55,10 +55,12 @@
"pypy_cjk_dec_init", "pypy_cjk_dec_free", "pypy_cjk_dec_chunk",
"pypy_cjk_dec_outbuf", "pypy_cjk_dec_outlen",
"pypy_cjk_dec_inbuf_remaining", "pypy_cjk_dec_inbuf_consumed",
+ "pypy_cjk_dec_replace_on_error",
"pypy_cjk_enc_init", "pypy_cjk_enc_free", "pypy_cjk_enc_chunk",
"pypy_cjk_enc_reset", "pypy_cjk_enc_outbuf", "pypy_cjk_enc_outlen",
"pypy_cjk_enc_inbuf_remaining", "pypy_cjk_enc_inbuf_consumed",
+ "pypy_cjk_enc_replace_on_error",
] + ["pypy_cjkcodec_%s" % codec for codec in codecs],
)
diff --git a/pypy/module/_multibytecodec/interp_multibytecodec.py b/pypy/module/_multibytecodec/interp_multibytecodec.py
--- a/pypy/module/_multibytecodec/interp_multibytecodec.py
+++ b/pypy/module/_multibytecodec/interp_multibytecodec.py
@@ -1,5 +1,5 @@
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.gateway import ObjSpace, interp2app, unwrap_spec
+from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.error import OperationError
from pypy.module._multibytecodec import c_codecs
diff --git a/pypy/module/_multiprocessing/interp_connection.py b/pypy/module/_multiprocessing/interp_connection.py
--- a/pypy/module/_multiprocessing/interp_connection.py
+++ b/pypy/module/_multiprocessing/interp_connection.py
@@ -4,7 +4,7 @@
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.error import (
OperationError, wrap_oserror, operationerrfmt)
-from pypy.rpython.lltypesystem import rffi, lltype, llmemory
+from pypy.rpython.lltypesystem import rffi, lltype
from pypy.rlib.rarithmetic import intmask
from pypy.rlib import rpoll
import sys
@@ -360,7 +360,7 @@
conn_type = ["read-only", "write-only", "read-write"][self.flags]
return space.wrap("<%s %s, handle %zd>" % (
- conn_type, space.type(self).getname(space, '?'), self.do_fileno()))
+ conn_type, space.type(self).getname(space), self.do_fileno()))
def is_valid(self):
return self.handle != self.INVALID_HANDLE_VALUE
diff --git a/pypy/module/_multiprocessing/interp_semaphore.py b/pypy/module/_multiprocessing/interp_semaphore.py
--- a/pypy/module/_multiprocessing/interp_semaphore.py
+++ b/pypy/module/_multiprocessing/interp_semaphore.py
@@ -15,7 +15,6 @@
if sys.platform == 'win32':
from pypy.rlib import rwin32
- from pypy.interpreter.error import wrap_windowserror
from pypy.module._multiprocessing.interp_win32 import (
handle_w, _GetTickCount)
diff --git a/pypy/module/_pickle_support/maker.py b/pypy/module/_pickle_support/maker.py
--- a/pypy/module/_pickle_support/maker.py
+++ b/pypy/module/_pickle_support/maker.py
@@ -1,4 +1,4 @@
-from pypy.interpreter.error import OperationError
+from pypy.interpreter.error import OperationError
from pypy.interpreter.nestedscope import Cell
from pypy.interpreter.pycode import PyCode
from pypy.interpreter.function import Function, Method
@@ -8,7 +8,6 @@
from pypy.interpreter.generator import GeneratorIterator
from pypy.rlib.objectmodel import instantiate
from pypy.interpreter.gateway import unwrap_spec
-from pypy.objspace.std.dicttype import dictiter_typedef
from pypy.objspace.std.iterobject import W_SeqIterObject, W_ReverseSeqIterObject
@@ -79,7 +78,7 @@
try:
return gateway.BuiltinCode.find(identifier)
except KeyError:
- raise OperationError(space.w_RuntimeError,
+ raise OperationError(space.w_RuntimeError,
space.wrap("cannot unpickle builtin code: "+
identifier))
@@ -89,7 +88,7 @@
try:
return function.Function.find(identifier)
except KeyError:
- raise OperationError(space.w_RuntimeError,
+ raise OperationError(space.w_RuntimeError,
space.wrap("cannot unpickle builtin function: "+
identifier))
diff --git a/pypy/module/_rawffi/callback.py b/pypy/module/_rawffi/callback.py
--- a/pypy/module/_rawffi/callback.py
+++ b/pypy/module/_rawffi/callback.py
@@ -2,10 +2,10 @@
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.rpython.lltypesystem import lltype, rffi
-from pypy.module._rawffi.array import get_elem, push_elem
+from pypy.module._rawffi.array import push_elem
from pypy.module._rawffi.structure import W_Structure
-from pypy.module._rawffi.interp_rawffi import W_DataInstance, letter2tp, \
- wrap_value, unwrap_value, unwrap_truncate_int, unpack_argshapes
+from pypy.module._rawffi.interp_rawffi import (W_DataInstance, letter2tp,
+ unwrap_value, unpack_argshapes)
from pypy.rlib.clibffi import USERDATA_P, CallbackFuncPtr, FUNCFLAG_CDECL
from pypy.rlib.clibffi import ffi_type_void
from pypy.rlib import rweakref
diff --git a/pypy/module/_rawffi/interp_rawffi.py b/pypy/module/_rawffi/interp_rawffi.py
--- a/pypy/module/_rawffi/interp_rawffi.py
+++ b/pypy/module/_rawffi/interp_rawffi.py
@@ -1,7 +1,6 @@
-import sys
from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.error import OperationError, wrap_oserror, operationerrfmt
-from pypy.interpreter.gateway import interp2app, NoneNotWrapped, unwrap_spec
+from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.rlib.clibffi import *
@@ -15,7 +14,7 @@
from pypy.rlib import rwin32
from pypy.tool.sourcetools import func_with_new_name
-from pypy.rlib.rarithmetic import intmask, r_uint, r_singlefloat
+from pypy.rlib.rarithmetic import intmask, r_uint
from pypy.module._rawffi.tracker import tracker
TYPEMAP = {
diff --git a/pypy/module/_socket/interp_func.py b/pypy/module/_socket/interp_func.py
--- a/pypy/module/_socket/interp_func.py
+++ b/pypy/module/_socket/interp_func.py
@@ -1,9 +1,8 @@
-from pypy.interpreter.gateway import NoneNotWrapped, unwrap_spec
+from pypy.interpreter.gateway import unwrap_spec
from pypy.module._socket.interp_socket import converted_error, W_RSocket
from pypy.rlib import rsocket
from pypy.rlib.rsocket import SocketError
-from pypy.rlib.rarithmetic import r_uint
-from pypy.interpreter.error import OperationError, operationerrfmt
+from pypy.interpreter.error import OperationError
def gethostname(space):
"""gethostname() -> string
diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py
--- a/pypy/module/_ssl/interp_ssl.py
+++ b/pypy/module/_ssl/interp_ssl.py
@@ -1,7 +1,7 @@
from __future__ import with_statement
from pypy.rpython.lltypesystem import rffi, lltype
from pypy.interpreter.error import OperationError
-from pypy.interpreter.baseobjspace import W_Root, ObjSpace, Wrappable
+from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.gateway import interp2app, unwrap_spec
@@ -11,7 +11,6 @@
from pypy.module._socket import interp_socket
-import sys
## user defined constants
X509_NAME_MAXLEN = 256
@@ -131,13 +130,13 @@
self._issuer = lltype.malloc(rffi.CCHARP.TO, X509_NAME_MAXLEN, flavor='raw')
self._issuer[0] = '\0'
self.shutdown_seen_zero = False
-
+
def server(self):
return self.space.wrap(rffi.charp2str(self._server))
-
+
def issuer(self):
return self.space.wrap(rffi.charp2str(self._issuer))
-
+
def __del__(self):
if self.peer_cert:
libssl_X509_free(self.peer_cert)
@@ -147,7 +146,7 @@
libssl_SSL_CTX_free(self.ctx)
lltype.free(self._server, flavor='raw')
lltype.free(self._issuer, flavor='raw')
-
+
@unwrap_spec(data='bufferstr')
def write(self, data):
"""write(s) -> len
@@ -230,10 +229,10 @@
raw_buf, gc_buf = rffi.alloc_buffer(num_bytes)
while True:
err = 0
-
+
count = libssl_SSL_read(self.ssl, raw_buf, num_bytes)
err = libssl_SSL_get_error(self.ssl, count)
-
+
if err == SSL_ERROR_WANT_READ:
sockstate = check_socket_and_wait_for_timeout(self.space,
self.w_socket, False)
@@ -245,17 +244,17 @@
return self.space.wrap("")
else:
sockstate = SOCKET_OPERATION_OK
-
+
if sockstate == SOCKET_HAS_TIMED_OUT:
raise ssl_error(self.space, "The read operation timed out")
elif sockstate == SOCKET_IS_NONBLOCKING:
break
-
+
if err == SSL_ERROR_WANT_READ or err == SSL_ERROR_WANT_WRITE:
continue
else:
break
-
+
if count <= 0:
raise _ssl_seterror(self.space, self, count)
@@ -351,7 +350,7 @@
self.shutdown_seen_zero = True
continue
- # Possibly retry shutdown until timeout or failure
+ # Possibly retry shutdown until timeout or failure
ssl_err = libssl_SSL_get_error(self.ssl, ret)
if ssl_err == SSL_ERROR_WANT_READ:
sockstate = check_socket_and_wait_for_timeout(
@@ -397,7 +396,7 @@
else:
w_proto = space.w_None
- bits = libssl_SSL_CIPHER_get_bits(current,
+ bits = libssl_SSL_CIPHER_get_bits(current,
lltype.nullptr(rffi.INTP.TO))
w_bits = space.newint(bits)
@@ -552,7 +551,7 @@
ext = libssl_X509_get_ext(certificate, i)
method = libssl_X509V3_EXT_get(ext)
if not method:
- raise ssl_error(space,
+ raise ssl_error(space,
"No method for internalizing subjectAltName!'")
with lltype.scoped_alloc(rffi.CCHARPP.TO, 1) as p_ptr:
@@ -858,7 +857,7 @@
cert = libssl_BIO_new(libssl_BIO_s_file())
if not cert:
raise ssl_error(space, "Can't malloc memory to read file")
-
+
try:
if libssl_BIO_read_filename(cert, filename) <= 0:
raise ssl_error(space, "Can't open file")
@@ -873,7 +872,7 @@
libssl_X509_free(x)
finally:
libssl_BIO_free(cert)
-
+
# this function is needed to perform locking on shared data
# structures. (Note that OpenSSL uses a number of global data
# structures that will be implicitly shared whenever multiple threads
diff --git a/pypy/module/_stackless/interp_coroutine.py b/pypy/module/_stackless/interp_coroutine.py
--- a/pypy/module/_stackless/interp_coroutine.py
+++ b/pypy/module/_stackless/interp_coroutine.py
@@ -15,13 +15,10 @@
experience to decide where to set the limits.
"""
-from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.argument import Arguments
from pypy.interpreter.typedef import GetSetProperty, TypeDef
-from pypy.interpreter.typedef import interp_attrproperty, interp_attrproperty_w
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.interpreter.function import StaticMethod
from pypy.module._stackless.stackless_flags import StacklessFlags
from pypy.module._stackless.rcoroutine import Coroutine, BaseCoState, AbstractThunk, CoroutineExit
@@ -38,9 +35,9 @@
self.costate = costate
if not space.is_true(space.callable(w_obj)):
raise operationerrfmt(
- space.w_TypeError,
+ space.w_TypeError,
"'%s' object is not callable",
- space.type(w_obj).getname(space, '?'))
+ space.type(w_obj).getname(space))
self.w_func = w_obj
self.args = args
@@ -65,7 +62,7 @@
# Should be moved to interp_stackless.py if it's ever implemented... Currently
# used by pypy/lib/stackless.py.
-W_TaskletExit = _new_exception('TaskletExit', W_SystemExit,
+W_TaskletExit = _new_exception('TaskletExit', W_SystemExit,
"""Tasklet killed manually.""")
class AppCoroutine(Coroutine): # XXX, StacklessFlags):
@@ -90,7 +87,7 @@
def _get_state(space):
return space.fromcache(AppCoState)
_get_state = staticmethod(_get_state)
-
+
def w_bind(self, w_func, __args__):
space = self.space
if self.frame is not None:
@@ -127,7 +124,7 @@
w_excvalue = operror.get_w_value(space)
w_exctraceback = operror.get_traceback()
w_excinfo = space.newtuple([w_exctype, w_excvalue, w_exctraceback])
-
+
if w_exctype is self.costate.w_CoroutineExit:
self.coroutine_exit = True
else:
@@ -146,22 +143,22 @@
def w_kill(self):
self.kill()
-
+
def w_throw(self, w_type, w_value=None, w_traceback=None):
space = self.space
operror = OperationError(w_type, w_value)
operror.normalize_exception(space)
-
+
if not space.is_w(w_traceback, space.w_None):
from pypy.interpreter import pytraceback
tb = space.interpclass_w(w_traceback)
- if tb is None or not space.is_true(space.isinstance(tb,
+ if tb is None or not space.is_true(space.isinstance(tb,
space.gettypeobject(pytraceback.PyTraceback.typedef))):
raise OperationError(space.w_TypeError,
space.wrap("throw: arg 3 must be a traceback or None"))
operror.set_traceback(tb)
-
+
self._kill(operror)
def _userdel(self):
@@ -280,7 +277,7 @@
assert isinstance(w_klass, W_TypeObject)
old_flag = w_klass.flag_heaptype
w_klass.flag_heaptype = True
-
+
space.appexec([w_klass, space.wrap(funcname)], """
(klass, funcname):
func = getattr(klass, funcname)
@@ -332,23 +329,23 @@
# Exporting new exception to space
self.w_CoroutineExit = space.gettypefor(W_CoroutineExit)
space.setitem(
- space.exceptions_module.w_dict,
- space.new_interned_str('CoroutineExit'),
- self.w_CoroutineExit)
- space.setitem(space.builtin.w_dict,
- space.new_interned_str('CoroutineExit'),
+ space.exceptions_module.w_dict,
+ space.new_interned_str('CoroutineExit'),
self.w_CoroutineExit)
-
+ space.setitem(space.builtin.w_dict,
+ space.new_interned_str('CoroutineExit'),
+ self.w_CoroutineExit)
+
# Should be moved to interp_stackless.py if it's ever implemented...
self.w_TaskletExit = space.gettypefor(W_TaskletExit)
space.setitem(
- space.exceptions_module.w_dict,
- space.new_interned_str('TaskletExit'),
- self.w_TaskletExit)
- space.setitem(space.builtin.w_dict,
- space.new_interned_str('TaskletExit'),
- self.w_TaskletExit)
-
+ space.exceptions_module.w_dict,
+ space.new_interned_str('TaskletExit'),
+ self.w_TaskletExit)
+ space.setitem(space.builtin.w_dict,
+ space.new_interned_str('TaskletExit'),
+ self.w_TaskletExit)
+
def post_install(self):
self.current = self.main = AppCoroutine(self.space, state=self)
self.main.subctx.clear_framestack() # wack
@@ -378,7 +375,7 @@
instr = frame.last_instr
opcode = ord(code[instr])
map = pythonopcode.opmap
- call_ops = [map['CALL_FUNCTION'], map['CALL_FUNCTION_KW'], map['CALL_FUNCTION_VAR'],
+ call_ops = [map['CALL_FUNCTION'], map['CALL_FUNCTION_KW'], map['CALL_FUNCTION_VAR'],
map['CALL_FUNCTION_VAR_KW'], map['CALL_METHOD']]
assert opcode in call_ops
instr += 1
diff --git a/pypy/module/_stackless/interp_stackless.py b/pypy/module/_stackless/interp_stackless.py
--- a/pypy/module/_stackless/interp_stackless.py
+++ b/pypy/module/_stackless/interp_stackless.py
@@ -1,9 +1,6 @@
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.typedef import GetSetProperty, TypeDef
-from pypy.interpreter.typedef import interp_attrproperty, interp_attrproperty_w
+from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.gateway import interp2app
-from pypy.interpreter.error import OperationError
-from pypy.rlib.rarithmetic import intmask
import os
diff --git a/pypy/module/_stackless/rclonable.py b/pypy/module/_stackless/rclonable.py
--- a/pypy/module/_stackless/rclonable.py
+++ b/pypy/module/_stackless/rclonable.py
@@ -1,7 +1,6 @@
from pypy.module._stackless.interp_coroutine import AbstractThunk, Coroutine
from pypy.rlib.rgc import gc_swap_pool, gc_clone
from pypy.rlib.objectmodel import we_are_translated
-from pypy.interpreter.error import OperationError
class InterpClonableMixin:
@@ -76,7 +75,7 @@
if not isinstance(current, InterpClonableCoroutine):
raise RuntimeError("fork() in a non-clonable coroutine")
thunk = ForkThunk(current)
- coro_fork = InterpClonableCoroutine()
+ coro_fork = InterpClonableCoroutine()
coro_fork.bind(thunk)
coro_fork.switch()
# we resume here twice. The following would need explanations about
diff --git a/pypy/module/_weakref/interp__weakref.py b/pypy/module/_weakref/interp__weakref.py
--- a/pypy/module/_weakref/interp__weakref.py
+++ b/pypy/module/_weakref/interp__weakref.py
@@ -1,16 +1,15 @@
import py
-from pypy.interpreter.argument import Arguments
from pypy.interpreter.baseobjspace import Wrappable, W_Root
from pypy.interpreter.error import OperationError
from pypy.interpreter.gateway import interp2app, ObjSpace
-from pypy.interpreter.typedef import GetSetProperty, TypeDef
+from pypy.interpreter.typedef import TypeDef
from pypy.rlib import jit
import weakref
class WeakrefLifeline(W_Root):
def __init__(self, space):
- self.space = space # this is here for W_Root.clear_all_weakrefs()
+ self.space = space
self.refs_weak = []
self.cached_weakref_index = -1
self.cached_proxy_index = -1
@@ -23,8 +22,10 @@
"""
for i in range(len(self.refs_weak) - 1, -1, -1):
w_ref = self.refs_weak[i]()
- if w_ref is not None:
- self.space.user_del_action.register_weakref_callback(w_ref)
+ if w_ref is not None and w_ref.w_callable is not None:
+ w_ref.enqueue_for_destruction(self.space,
+ W_WeakrefBase.activate_callback,
+ 'weakref callback of ')
def clear_all_weakrefs(self):
"""Clear all weakrefs. This is called when an app-level object has
@@ -118,18 +119,15 @@
self.w_obj_weak = dead_ref
def activate_callback(w_self):
- if not w_self.w_callable is None:
- try:
- w_self.space.call_function(w_self.w_callable, w_self)
- except OperationError, e:
- e.write_unraisable(w_self.space, 'weakref callback ', w_self.w_callable)
+ assert isinstance(w_self, W_WeakrefBase)
+ w_self.space.call_function(w_self.w_callable, w_self)
def descr__repr__(self, space):
w_obj = self.dereference()
if w_obj is None:
state = '; dead'
else:
- typename = space.type(w_obj).getname(space, '?')
+ typename = space.type(w_obj).getname(space)
objname = w_obj.getname(space, '')
if objname:
state = "; to '%s' (%s)" % (typename, objname)
diff --git a/pypy/module/_winreg/interp_winreg.py b/pypy/module/_winreg/interp_winreg.py
--- a/pypy/module/_winreg/interp_winreg.py
+++ b/pypy/module/_winreg/interp_winreg.py
@@ -1,6 +1,5 @@
from __future__ import with_statement
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.baseobjspace import ObjSpace, W_Root
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.interpreter.error import OperationError, wrap_windowserror
diff --git a/pypy/module/array/interp_array.py b/pypy/module/array/interp_array.py
--- a/pypy/module/array/interp_array.py
+++ b/pypy/module/array/interp_array.py
@@ -1,18 +1,20 @@
from __future__ import with_statement
+from pypy.interpreter.buffer import RWBuffer
from pypy.interpreter.error import OperationError
-from pypy.interpreter.typedef import TypeDef, GetSetProperty, make_weakref_descr
-from pypy.rpython.lltypesystem import lltype, rffi
from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.rlib.unroll import unrolling_iterable
-from pypy.rlib.rarithmetic import ovfcheck
-from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.typedef import GetSetProperty, make_weakref_descr
+from pypy.module._file.interp_file import W_File
+from pypy.objspace.std.model import W_Object
+from pypy.objspace.std.multimethod import FailedToImplement
from pypy.objspace.std.stdtypedef import SMM, StdTypeDef
from pypy.objspace.std.register_all import register_all
-from pypy.objspace.std.model import W_Object
-from pypy.module._file.interp_file import W_File
-from pypy.interpreter.buffer import RWBuffer
-from pypy.objspace.std.multimethod import FailedToImplement
+from pypy.rlib.rarithmetic import ovfcheck
+from pypy.rlib.unroll import unrolling_iterable
+from pypy.rpython.lltypesystem import lltype, rffi
+
+
+memcpy = rffi.llexternal("memcpy", [rffi.VOIDP, rffi.VOIDP, rffi.SIZE_T], lltype.Void)
@unwrap_spec(typecode=str)
def w_array(space, w_cls, typecode, __args__):
@@ -37,7 +39,7 @@
if len(__args__.arguments_w) > 0:
w_initializer = __args__.arguments_w[0]
if space.type(w_initializer) is space.w_str:
- a.fromstring(w_initializer)
+ a.fromstring(space.str_w(w_initializer))
elif space.type(w_initializer) is space.w_unicode:
a.fromsequence(w_initializer)
elif space.type(w_initializer) is space.w_list:
@@ -73,6 +75,7 @@
array_buffer_info = SMM('buffer_info', 1)
array_reduce = SMM('__reduce__', 1)
+array_copy = SMM('__copy__', 1)
array_byteswap = SMM('byteswap', 1)
@@ -96,7 +99,7 @@
itemsize = GetSetProperty(descr_itemsize),
typecode = GetSetProperty(descr_typecode),
__weakref__ = make_weakref_descr(W_ArrayBase),
- )
+)
W_ArrayBase.typedef.registermethods(globals())
@@ -159,8 +162,6 @@
self.data[index] = char
-
-
def make_array(mytype):
class W_Array(W_ArrayBase):
itemsize = mytype.bytes
@@ -268,12 +269,10 @@
raise
self.setlen(oldlen + i)
- def fromstring(self, w_s):
- space = self.space
- s = space.str_w(w_s)
+ def fromstring(self, s):
if len(s) % self.itemsize != 0:
msg = 'string length not a multiple of item size'
- raise OperationError(space.w_ValueError, space.wrap(msg))
+ raise OperationError(self.space.w_ValueError, self.space.wrap(msg))
oldlen = self.len
new = len(s) / mytype.bytes
self.setlen(oldlen + new)
@@ -311,6 +310,14 @@
def charbuf(self):
return rffi.cast(rffi.CCHARP, self.buffer)
+ def w_getitem(self, space, idx):
+ item = self.buffer[idx]
+ if mytype.typecode in 'bBhHil':
+ item = rffi.cast(lltype.Signed, item)
+ elif mytype.typecode == 'f':
+ item = float(item)
+ return space.wrap(item)
+
# Basic get/set/append/extend methods
def len__Array(space, self):
@@ -319,12 +326,7 @@
def getitem__Array_ANY(space, self, w_idx):
idx, stop, step = space.decode_index(w_idx, self.len)
assert step == 0
- item = self.buffer[idx]
- if mytype.typecode in 'bBhHil':
- item = rffi.cast(lltype.Signed, item)
- elif mytype.typecode == 'f':
- item = float(item)
- return self.space.wrap(item)
+ return self.w_getitem(space, idx)
def getitem__Array_Slice(space, self, w_slice):
start, stop, step, size = space.decode_index4(w_slice, self.len)
@@ -387,7 +389,7 @@
def array_count__Array_ANY(space, self, w_val):
cnt = 0
for i in range(self.len):
- w_item = getitem__Array_ANY(space, self, space.wrap(i))
+ w_item = self.w_getitem(space, i)
if space.is_true(space.eq(w_item, w_val)):
cnt += 1
return space.wrap(cnt)
@@ -395,7 +397,7 @@
def array_index__Array_ANY(space, self, w_val):
cnt = 0
for i in range(self.len):
- w_item = getitem__Array_ANY(space, self, space.wrap(i))
+ w_item = self.w_getitem(space, i)
if space.is_true(space.eq(w_item, w_val)):
return space.wrap(i)
msg = 'array.index(x): x not in list'
@@ -413,7 +415,7 @@
if i < 0 or i >= self.len:
msg = 'pop index out of range'
raise OperationError(space.w_IndexError, space.wrap(msg))
- w_val = getitem__Array_ANY(space, self, space.wrap(i))
+ w_val = self.w_getitem(space, i)
while i < self.len - 1:
self.buffer[i] = self.buffer[i + 1]
i += 1
@@ -515,14 +517,14 @@
def array_tolist__Array(space, self):
w_l = space.newlist([])
for i in range(self.len):
- w_l.append(getitem__Array_ANY(space, self, space.wrap(i)))
+ w_l.append(self.w_getitem(space, i))
return w_l
def array_fromlist__Array_List(space, self, w_lst):
self.fromlist(w_lst)
def array_fromstring__Array_ANY(space, self, w_s):
- self.fromstring(w_s)
+ self.fromstring(space.str_w(w_s))
def array_tostring__Array(space, self):
cbuf = self.charbuf()
@@ -569,10 +571,7 @@
self.fromsequence(w_ustr)
def array_tounicode__Array(space, self):
- u = u""
- for i in range(self.len):
- u += self.buffer[i]
- return space.wrap(u)
+ return space.wrap(rffi.wcharpsize2unicode(self.buffer, self.len))
else:
def array_fromunicode__Array_Unicode(space, self, w_ustr):
@@ -615,6 +614,16 @@
dct = space.w_None
return space.newtuple([space.type(self), space.newtuple(args), dct])
+ def array_copy__Array(space, self):
+ w_a = mytype.w_class(self.space)
+ w_a.setlen(self.len)
+ memcpy(
+ rffi.cast(rffi.VOIDP, w_a.buffer),
+ rffi.cast(rffi.VOIDP, self.buffer),
+ self.len * mytype.bytes
+ )
More information about the pypy-commit
mailing list