aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--extra_tests/ctypes_tests/test_structures.py6
-rw-r--r--lib_pypy/tools/build_cffi_imports.py2
-rw-r--r--pypy/doc/index.rst2
-rw-r--r--pypy/doc/whatsnew-head.rst10
-rw-r--r--pypy/interpreter/baseobjspace.py29
-rw-r--r--pypy/interpreter/executioncontext.py2
-rw-r--r--pypy/interpreter/generator.py3
-rw-r--r--pypy/interpreter/pycode.py3
-rw-r--r--pypy/interpreter/pyframe.py17
-rw-r--r--pypy/interpreter/pyopcode.py11
-rw-r--r--pypy/module/__builtin__/functional.py19
-rw-r--r--pypy/module/array/interp_array.py8
-rw-r--r--pypy/module/cpyext/test/test_abstract.py27
-rw-r--r--pypy/module/cpyext/test/test_unicodeobject.py26
-rw-r--r--pypy/module/cpyext/typeobject.py10
-rw-r--r--pypy/module/itertools/interp_itertools.py15
-rw-r--r--pypy/module/pypyjit/test_pypy_c/test_generators.py10
-rw-r--r--pypy/module/pypyjit/test_pypy_c/test_micronumpy.py25
-rw-r--r--pypy/module/time/interp_time.py10
-rw-r--r--pypy/objspace/descroperation.py13
-rw-r--r--pypy/objspace/std/bytearrayobject.py16
-rw-r--r--pypy/objspace/std/listobject.py26
-rw-r--r--pypy/objspace/std/setobject.py4
-rw-r--r--pypy/objspace/std/tupleobject.py29
-rw-r--r--pypy/objspace/std/typeobject.py3
-rw-r--r--rpython/jit/backend/zarch/test/test_regalloc.py3
-rw-r--r--rpython/jit/metainterp/heapcache.py49
-rw-r--r--rpython/jit/metainterp/jitprof.py1
-rw-r--r--rpython/jit/metainterp/optimizeopt/heap.py16
-rw-r--r--rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py26
-rw-r--r--rpython/jit/metainterp/pyjitpl.py32
-rw-r--r--rpython/jit/metainterp/test/test_heapcache.py47
-rw-r--r--rpython/jit/metainterp/test/test_jitprof.py29
-rw-r--r--rpython/jit/metainterp/test/test_quasiimmut.py26
-rw-r--r--rpython/jit/metainterp/test/test_threadlocal.py10
-rw-r--r--rpython/jit/metainterp/test/test_tracingopts.py91
-rw-r--r--rpython/jit/metainterp/warmspot.py4
-rw-r--r--rpython/jit/tool/jitoutput.py1
-rw-r--r--rpython/jit/tool/test/test_jitoutput.py2
-rw-r--r--rpython/rlib/_rsocket_rffi.py2
-rw-r--r--rpython/rlib/jit.py1
-rw-r--r--rpython/rlib/rvmprof/src/shared/vmprof_common.h2
-rw-r--r--rpython/rtyper/rclass.py3
-rw-r--r--rpython/rtyper/test/test_rclass.py27
-rw-r--r--rpython/translator/c/src/support.c2
45 files changed, 583 insertions, 117 deletions
diff --git a/extra_tests/ctypes_tests/test_structures.py b/extra_tests/ctypes_tests/test_structures.py
index b6bd075942..fa6ed900ae 100644
--- a/extra_tests/ctypes_tests/test_structures.py
+++ b/extra_tests/ctypes_tests/test_structures.py
@@ -1,6 +1,7 @@
from ctypes import *
import pytest
+import sys
def test_subclass_initializer():
@@ -207,6 +208,9 @@ def test_memoryview():
)
mv = memoryview(c_array)
- assert mv.format == 'T{<h:a:<h:b:}'
+ if sys.byteorder == 'little':
+ assert mv.format == 'T{<h:a:<h:b:}'
+ else:
+ assert mv.format == 'T{>h:a:>h:b:}'
assert mv.shape == (2, 3)
assert mv.itemsize == 4
diff --git a/lib_pypy/tools/build_cffi_imports.py b/lib_pypy/tools/build_cffi_imports.py
index 489dfb5ea8..52563b2ed5 100644
--- a/lib_pypy/tools/build_cffi_imports.py
+++ b/lib_pypy/tools/build_cffi_imports.py
@@ -196,6 +196,8 @@ def create_cffi_import_libraries(pypy_c, options, basedir, only=None,
except:
import traceback;traceback.print_exc()
failures.append((key, module))
+ if os.path.exists(deps_destdir):
+ shutil.rmtree(deps_destdir, ignore_errors=True)
return failures
if __name__ == '__main__':
diff --git a/pypy/doc/index.rst b/pypy/doc/index.rst
index 88f036d3ab..ebb2a26e00 100644
--- a/pypy/doc/index.rst
+++ b/pypy/doc/index.rst
@@ -106,7 +106,7 @@ Meeting PyPy developers
.. _here: https://quodlibet.duckdns.org/irc/pypy/latest.log.html#irc-end
.. _Development mailing list: http://mail.python.org/mailman/listinfo/pypy-dev
.. _Commit mailing list: http://mail.python.org/mailman/listinfo/pypy-commit
-.. _Development bug/feature tracker: https://bitbucket.org/pypy/pypy/issues
+.. _Development bug/feature tracker: https://foss.heptapod.net/pypy/pypy/issues
Indices and tables
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
index 31c3155cbb..473147a271 100644
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -21,6 +21,16 @@ Fixes incorrect error text for ``int('1', base=1000)``
adapt contributing documentation to heptapod
+.. branch: pypy-jitdriver-greenkeys
+
+Improve code generation for generators (and generator expressions in
+particular) when passing them to builtin functions that consume iterators, such
+as ``sum``, ``map``, ``max``, etc.
+
+.. branch: warmup-improvements-various
+
+Improves warmup time by up to 20%.
+
.. branch: StringIO-perf
Improve performance of io.StringIO(). It should now be faster than CPython in
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
index 98b6144ae0..6a7f3a5339 100644
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -22,12 +22,12 @@ from pypy.interpreter.miscutils import ThreadLocals, make_weak_value_dictionary
__all__ = ['ObjSpace', 'OperationError', 'W_Root']
-def get_printable_location(tp):
- return "unpackiterable: %s" % (tp, )
+def get_printable_location(greenkey):
+ return "unpackiterable [%s]" % (greenkey.iterator_greenkey_printable(), )
unpackiterable_driver = jit.JitDriver(name='unpackiterable',
- greens=['tp'],
- reds=['items', 'w_iterator'],
+ greens=['greenkey'],
+ reds='auto',
get_printable_location=get_printable_location)
@@ -373,6 +373,15 @@ class W_Root(object):
return lst[:]
return None
+ def iterator_greenkey(self, space):
+ """ Return something that can be used as a green key in jit drivers
+ that iterate over self. by default, it's just the type of self, but
+ custom iterators should override it. """
+ return space.type(self)
+
+ def iterator_greenkey_printable(self):
+ return "?"
+
class InternalSpaceCache(Cache):
"""A generic cache for an object space. Arbitrary information can
@@ -966,11 +975,9 @@ class ObjSpace(object):
except MemoryError:
items = [] # it might have lied
- tp = self.type(w_iterator)
+ greenkey = self.iterator_greenkey(w_iterator)
while True:
- unpackiterable_driver.jit_merge_point(tp=tp,
- w_iterator=w_iterator,
- items=items)
+ unpackiterable_driver.jit_merge_point(greenkey=greenkey)
try:
w_item = self.next(w_iterator)
except OperationError as e:
@@ -2025,6 +2032,12 @@ class ObjSpace(object):
finally:
self.sys.track_resources = flag
+ def iterator_greenkey(self, w_iterable):
+ """ Return something that can be used as a green key in jit drivers
+ that iterate over self. by default, it's just the type of self, but
+ custom iterators should override it. """
+ return w_iterable.iterator_greenkey(self)
+
class AppExecCache(SpaceCache):
@not_rpython
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
index c1c9e8aab8..ddb47ad51a 100644
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -23,8 +23,6 @@ class ExecutionContext(object):
# XXX [fijal] but they're not. is_being_profiled is guarded a bit all
# over the place as well as w_tracefunc
- _immutable_fields_ = ['profilefunc?', 'w_tracefunc?']
-
def __init__(self, space):
self.space = space
self.topframeref = jit.vref_None
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
index c7f73fe9a6..62fc71cd8e 100644
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -231,6 +231,9 @@ return next yielded value or raise StopIteration."""
self.frame = None
rgc.may_ignore_finalizer(self)
+ def iterator_greenkey(self, space):
+ return self.pycode
+
def get_printable_location_genentry(bytecode):
return '%s <generator>' % (bytecode.get_repr(),)
diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py
index 3d6438fa5f..6cbc67b595 100644
--- a/pypy/interpreter/pycode.py
+++ b/pypy/interpreter/pycode.py
@@ -425,6 +425,9 @@ class PyCode(eval.Code):
return "<code object %s, file '%s', line %d>" % (
self.co_name, self.co_filename, self.co_firstlineno)
+ def iterator_greenkey_printable(self):
+ return self.get_repr()
+
def __repr__(self):
return self.get_repr()
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
index 2944f230ab..365a4a3b86 100644
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -310,9 +310,12 @@ class PyFrame(W_Root):
assert self.locals_cells_stack_w[depth] is None
self.valuestackdepth = depth + 1
+ def assert_stack_index(self, index):
+ if we_are_translated():
+ return
+ assert self._check_stack_index(index)
+
def _check_stack_index(self, index):
- # will be completely removed by the optimizer if only used in an assert
- # and if asserts are disabled
code = self.pycode
ncellvars = len(code.co_cellvars)
nfreevars = len(code.co_freevars)
@@ -324,7 +327,7 @@ class PyFrame(W_Root):
def popvalue_maybe_none(self):
depth = self.valuestackdepth - 1
- assert self._check_stack_index(depth)
+ self.assert_stack_index(depth)
assert depth >= 0
w_object = self.locals_cells_stack_w[depth]
self.locals_cells_stack_w[depth] = None
@@ -353,7 +356,7 @@ class PyFrame(W_Root):
def peekvalues(self, n):
values_w = [None] * n
base = self.valuestackdepth - n
- assert self._check_stack_index(base)
+ self.assert_stack_index(base)
assert base >= 0
while True:
n -= 1
@@ -366,7 +369,7 @@ class PyFrame(W_Root):
def dropvalues(self, n):
n = hint(n, promote=True)
finaldepth = self.valuestackdepth - n
- assert self._check_stack_index(finaldepth)
+ self.assert_stack_index(finaldepth)
assert finaldepth >= 0
while True:
n -= 1
@@ -402,14 +405,14 @@ class PyFrame(W_Root):
def peekvalue_maybe_none(self, index_from_top=0):
index_from_top = hint(index_from_top, promote=True)
index = self.valuestackdepth + ~index_from_top
- assert self._check_stack_index(index)
+ self.assert_stack_index(index)
assert index >= 0
return self.locals_cells_stack_w[index]
def settopvalue(self, w_object, index_from_top=0):
index_from_top = hint(index_from_top, promote=True)
index = self.valuestackdepth + ~index_from_top
- assert self._check_stack_index(index)
+ self.assert_stack_index(index)
assert index >= 0
self.locals_cells_stack_w[index] = ll_assert_not_none(w_object)
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
index 1e1f172afc..dfbd2a5dd2 100644
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -147,10 +147,12 @@ class __extend__(pyframe.PyFrame):
while True:
self.last_instr = intmask(next_instr)
if jit.we_are_jitted():
- ec.bytecode_only_trace(self)
+ if self.debugdata:
+ ec.bytecode_only_trace(self)
+ next_instr = r_uint(self.last_instr)
else:
ec.bytecode_trace(self)
- next_instr = r_uint(self.last_instr)
+ next_instr = r_uint(self.last_instr)
opcode = ord(co_code[next_instr])
next_instr += 1
@@ -176,10 +178,13 @@ class __extend__(pyframe.PyFrame):
oparg = (oparg * 65536) | (hi * 256) | lo
if opcode == opcodedesc.RETURN_VALUE.index:
+ if not self.blockstack_non_empty():
+ self.frame_finished_execution = True # for generators
+ raise Return
w_returnvalue = self.popvalue()
block = self.unrollstack(SReturnValue.kind)
if block is None:
- self.pushvalue(w_returnvalue) # XXX ping pong
+ self.pushvalue(w_returnvalue)
raise Return
else:
unroller = SReturnValue(w_returnvalue)
diff --git a/pypy/module/__builtin__/functional.py b/pypy/module/__builtin__/functional.py
index 9106ee410c..cf6fc81c06 100644
--- a/pypy/module/__builtin__/functional.py
+++ b/pypy/module/__builtin__/functional.py
@@ -131,10 +131,21 @@ def range_with_longs(space, w_start, w_stop, w_step):
v = v.add(step)
return space.newlist(res_w)
+def get_printable_location(has_key, has_item, greenkey):
+ return "min [has_key=%s, has_item=%s, %s]" % (
+ has_key, has_item, greenkey.iterator_greenkey_printable())
+
min_jitdriver = jit.JitDriver(name='min',
- greens=['has_key', 'has_item', 'w_type'], reds='auto')
+ greens=['has_key', 'has_item', 'greenkey'], reds='auto',
+ get_printable_location=get_printable_location)
+
+def get_printable_location(has_key, has_item, greenkey):
+ return "min [has_key=%s, has_item=%s, %s]" % (
+ has_key, has_item, greenkey.iterator_greenkey_printable())
+
max_jitdriver = jit.JitDriver(name='max',
- greens=['has_key', 'has_item', 'w_type'], reds='auto')
+ greens=['has_key', 'has_item', 'greenkey'], reds='auto',
+ get_printable_location=get_printable_location)
@specialize.arg(3)
def min_max_sequence(space, w_sequence, w_key, implementation_of):
@@ -145,14 +156,14 @@ def min_max_sequence(space, w_sequence, w_key, implementation_of):
compare = space.lt
jitdriver = min_jitdriver
w_iter = space.iter(w_sequence)
- w_type = space.type(w_iter)
+ greenkey = space.iterator_greenkey(w_iter)
has_key = w_key is not None
has_item = False
w_max_item = None
w_max_val = None
while True:
jitdriver.jit_merge_point(has_key=has_key, has_item=has_item,
- w_type=w_type)
+ greenkey=greenkey)
try:
w_item = space.next(w_iter)
except OperationError as e:
diff --git a/pypy/module/array/interp_array.py b/pypy/module/array/interp_array.py
index 19ad6ab823..58927af9b8 100644
--- a/pypy/module/array/interp_array.py
+++ b/pypy/module/array/interp_array.py
@@ -72,7 +72,8 @@ def compare_arrays(space, arr1, arr2, comp_op):
if comp_op == NE and arr1.len != arr2.len:
return space.w_True
lgt = min(arr1.len, arr2.len)
- for i in range(lgt):
+ i = 0
+ while i < lgt:
arr_eq_driver.jit_merge_point(comp_func=comp_op)
w_elem1 = arr1.w_getitem(space, i, integer_instead_of_char=True)
w_elem2 = arr2.w_getitem(space, i, integer_instead_of_char=True)
@@ -102,6 +103,7 @@ def compare_arrays(space, arr1, arr2, comp_op):
return space.w_False
elif not space.is_true(space.eq(w_elem1, w_elem2)):
return space.w_True
+ i += 1
# we have some leftovers
if comp_op == EQ:
return space.w_True
@@ -128,7 +130,8 @@ def index_count_array(arr, w_val, count=False):
tp_item = space.type(w_val)
arrclass = arr.__class__
cnt = 0
- for i in range(arr.len):
+ i = 0
+ while i < arr.len:
index_count_jd.jit_merge_point(
tp_item=tp_item, count=count,
arrclass=arrclass)
@@ -138,6 +141,7 @@ def index_count_array(arr, w_val, count=False):
cnt += 1
else:
return i
+ i += 1
if count:
return cnt
return -1
diff --git a/pypy/module/cpyext/test/test_abstract.py b/pypy/module/cpyext/test/test_abstract.py
index 8649f880bd..ee7b3e927d 100644
--- a/pypy/module/cpyext/test/test_abstract.py
+++ b/pypy/module/cpyext/test/test_abstract.py
@@ -127,4 +127,29 @@ class AppTestBufferProtocol(AppTestCpythonExtensionBase):
assert raises(TypeError, buffer_support.writebuffer_as_string, buf)
assert s == buffer_support.charbuffer_as_string(buf)
-
+ def test_user_bufferable(self):
+ try:
+ import __pypy__
+ except ImportError:
+ skip('PyPy only test')
+
+ class MyBuf(__pypy__.bufferable.bufferable):
+ def __init__(self, mview):
+ if not isinstance(mview, memoryview):
+ raise ValueError('mview must be a memoryview')
+ self.mview = mview
+
+ def __buffer__(self, flags):
+ if flags & 1 and self.buffer.readonly:
+ raise TypeError('cannot return a writable buffer')
+ return __pypy__.newmemoryview(self.mview, self.mview.itemsize,
+ self.mview.format, self.mview.shape)
+
+ s = b'abc'
+ m = memoryview(s)
+ buf = MyBuf(m)
+ buffer_support = self.get_buffer_support()
+ assert buffer_support.check_readbuffer(buf)
+ assert s == buffer_support.readbuffer_as_string(buf)
+ assert raises(TypeError, buffer_support.writebuffer_as_string, buf)
+ assert s == buffer_support.charbuffer_as_string(buf)
diff --git a/pypy/module/cpyext/test/test_unicodeobject.py b/pypy/module/cpyext/test/test_unicodeobject.py
index b65582a79b..5300214b05 100644
--- a/pypy/module/cpyext/test/test_unicodeobject.py
+++ b/pypy/module/cpyext/test/test_unicodeobject.py
@@ -156,6 +156,32 @@ class AppTestUnicodeObject(AppTestCpythonExtensionBase):
s = module.asutf32(u)
assert s == u.encode('utf-32')
+ def test_lower_cython(self):
+ # mimic exactly what cython does, without the extra checks
+ import time
+ module = self.import_extension('foo', [
+ ("lower", "METH_O",
+ """
+ PyObject *p, *res, *tup;
+ p = PyObject_GetAttrString(args, "lower");
+ if (p == NULL) {
+ return NULL;
+ }
+ tup = PyTuple_New(0);
+ Py_INCREF(tup);
+ res = PyObject_Call(p, tup, NULL);
+ Py_DECREF(tup);
+ return res;
+ """)])
+ assert module.lower('ABC') == 'abc'
+ try:
+ time.tzset()
+ except AttributeError:
+ # only on posix
+ pass
+ tz1 = time.tzname[1]
+ assert module.lower(tz1) == tz1.lower()
+
class TestUnicode(BaseApiTest):
def test_unicodeobject(self, space):
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
index 388c689096..d0b5a3b6c4 100644
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -595,8 +595,9 @@ def bf_getreadbuffer(space, w_buf, segment, ref):
raise oefmt(space.w_SystemError,
"accessing non-existent segment")
buf = space.readbuf_w(w_buf)
- if isinstance(buf, StringBuffer):
- return str_getreadbuffer(space, w_buf, segment, ref)
+ # if isinstance(buf, StringBuffer):
+ # # Link the data pointer of buf to ref[0]
+ # return _str_getreadbuffer(space, w_buf, segment, ref)
address = buf.get_raw_address()
ref[0] = address
return len(buf)
@@ -616,6 +617,9 @@ def bf_getwritebuffer(space, w_buf, segment, ref):
@slot_function([PyObject, Py_ssize_t, rffi.VOIDPP], lltype.Signed, error=-1)
def str_getreadbuffer(space, w_str, segment, ref):
+ return _str_getreadbuffer(space, w_str, segment, ref)
+
+def _str_getreadbuffer(space, w_str, segment, ref):
from pypy.module.cpyext.bytesobject import PyString_AsString
if segment != 0:
raise oefmt(space.w_SystemError,
@@ -641,7 +645,7 @@ def unicode_getreadbuffer(space, w_str, segment, ref):
@slot_function([PyObject, Py_ssize_t, rffi.CCHARPP], lltype.Signed, error=-1)
def str_getcharbuffer(space, w_buf, segment, ref):
- return str_getreadbuffer(space, w_buf, segment, rffi.cast(rffi.VOIDPP, ref))
+ return _str_getreadbuffer(space, w_buf, segment, rffi.cast(rffi.VOIDPP, ref))
@slot_function([PyObject, Py_ssize_t, rffi.VOIDPP], lltype.Signed, error=-1)
def buf_getreadbuffer(space, pyref, segment, ref):
diff --git a/pypy/module/itertools/interp_itertools.py b/pypy/module/itertools/interp_itertools.py
index ac667c983b..116fa5371c 100644
--- a/pypy/module/itertools/interp_itertools.py
+++ b/pypy/module/itertools/interp_itertools.py
@@ -322,10 +322,12 @@ W_IFilterFalse.typedef = TypeDef(
""")
+def get_printable_location(greenkey):
+ return "islice_ignore_items [%s]" % (greenkey.iterator_greenkey_printable(), )
islice_ignore_items_driver = jit.JitDriver(name='islice_ignore_items',
- greens=['tp'],
- reds=['num', 'w_islice',
- 'w_iterator'])
+ greens=['greenkey'],
+ reds='auto',
+ get_printable_location=get_printable_location)
class W_ISlice(W_Root):
def __init__(self, space, w_iterable, w_startstop, args_w):
@@ -418,12 +420,9 @@ class W_ISlice(W_Root):
if w_iterator is None:
raise OperationError(self.space.w_StopIteration, self.space.w_None)
- tp = self.space.type(w_iterator)
+ greenkey = self.space.iterator_greenkey(w_iterator)
while True:
- islice_ignore_items_driver.jit_merge_point(tp=tp,
- num=num,
- w_islice=self,
- w_iterator=w_iterator)
+ islice_ignore_items_driver.jit_merge_point(greenkey=greenkey)
try:
self.space.next(w_iterator)
except OperationError as e:
diff --git a/pypy/module/pypyjit/test_pypy_c/test_generators.py b/pypy/module/pypyjit/test_pypy_c/test_generators.py
index d1f11eec52..313c438461 100644
--- a/pypy/module/pypyjit/test_pypy_c/test_generators.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_generators.py
@@ -61,3 +61,13 @@ class TestGenerators(BaseTestPyPyC):
i2 = int_sub_ovf(i1, 42)
guard_no_overflow(descr=...)
""")
+
+ def test_nonstd_jitdriver_distinguishes_generators(self):
+ def main():
+ # test the "contains" jitdriver, but the others are the same
+ res = (9999 in (i for i in range(20000)))
+ res += (9999 in (i for i in range(20000)))
+ return res
+ log = self.run(main, [])
+ assert len(log.loops) == 2 # as opposed to one loop, one bridge
+
diff --git a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py
index 12820c75da..e31287f77f 100644
--- a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py
@@ -153,6 +153,7 @@ class TestMicroNumPy(BaseTestPyPyC):
guard_class(p1, #, descr=...)
p4 = getfield_gc_r(p1, descr=<FieldP pypy.module.micronumpy.iterators.ArrayIter.inst_array \d+ pure>)
i5 = getfield_gc_i(p0, descr=<FieldS pypy.module.micronumpy.iterators.IterState.inst_offset \d+>)
+ guard_not_invalidated(descr=...)
p6 = getfield_gc_r(p4, descr=<FieldP pypy.module.micronumpy.concrete.BaseConcreteArray.inst_dtype \d+ pure>)
p7 = getfield_gc_r(p6, descr=<FieldP pypy.module.micronumpy.descriptor.W_Dtype.inst_itemtype \d+ pure>)
guard_class(p7, ConstClass(Float64), descr=...)
@@ -164,7 +165,6 @@ class TestMicroNumPy(BaseTestPyPyC):
%(align_check)s
f16 = raw_load_f(i9, i5, descr=<ArrayF \d+>)
guard_true(i15, descr=...)
- guard_not_invalidated(descr=...)
i18 = float_ne(f16, 0.000000)
guard_true(i18, descr=...)
guard_nonnull_class(p2, ConstClass(W_BoolBox), descr=...)
@@ -207,8 +207,8 @@ class TestMicroNumPy(BaseTestPyPyC):
loop = log._filter(log.loops[0])
loop.match("""
%(align_check)s
- f31 = raw_load_f(i9, i29, descr=<ArrayF 8>)
guard_not_invalidated(descr=...)
+ f31 = raw_load_f(i9, i29, descr=<ArrayF 8>)
i32 = float_ne(f31, 0.000000)
guard_true(i32, descr=...)
i36 = int_add(i24, 1)
@@ -255,6 +255,7 @@ class TestMicroNumPy(BaseTestPyPyC):
assert loop.match("""
i76 = int_lt(i71, 300)
guard_true(i76, descr=...)
+ guard_not_invalidated(descr=...)
i77 = int_ge(i71, i59)
guard_false(i77, descr=...)
i78 = int_mul(i71, i61)
@@ -294,13 +295,13 @@ class TestMicroNumPy(BaseTestPyPyC):
assert loop.match("""
i81 = int_lt(i76, 300)
guard_true(i81, descr=...)
+ guard_not_invalidated(descr=...)
i82 = int_ge(i76, i62)
guard_false(i82, descr=...)
i83 = int_mul(i76, i64)
i84 = int_add(i58, i83)
""" + alignment_check + """
f85 = raw_load_f(i70, i84, descr=<ArrayF 8>)
- guard_not_invalidated(descr=...)
f86 = float_add(f74, f85)
i87 = int_add(i76, 1)
--TICK--
@@ -323,9 +324,9 @@ class TestMicroNumPy(BaseTestPyPyC):
assert log.result == 42.0
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
+ guard_not_invalidated(descr=...)
i86 = int_lt(i79, i45)
guard_true(i86, descr=...)
- guard_not_invalidated(descr=...)
i88 = int_ge(i87, i59)
guard_false(i88, descr=...)
%(align_check)s
@@ -348,27 +349,19 @@ class TestMicroNumPy(BaseTestPyPyC):
ai = arr.flat
i = 0
while i < arr.size:
- a = ai[i]
+ a = ai[i] # ID: getitem
i += 1
return a
log = self.run(main, [])
assert log.result == 42.0
loop, = log.loops_by_filename(self.filepath)
- assert loop.match("""
- i125 = int_lt(i117, i44)
- guard_true(i125, descr=...)
+ assert loop.match_by_id("getitem", """
i126 = int_lt(i117, i50)
guard_true(i126, descr=...)
i128 = int_mul(i117, i59)
i129 = int_add(i55, i128)
%(align_check)s
f149 = raw_load_f(i100, i129, descr=<ArrayF 8>)
- i151 = int_add(i117, 1)
- setfield_gc(p156, i55, descr=<FieldS pypy.module.micronumpy.iterators.IterState.inst_offset .+>)
- setarrayitem_gc(p150, 1, 0, descr=<ArrayS .+>)
- setarrayitem_gc(p150, 0, 0, descr=<ArrayS .+>)
- --TICK--
- jump(..., descr=...)
""" % {'align_check': align_check('i129')})
def test_array_flatiter_setitem_single(self):
@@ -385,13 +378,13 @@ class TestMicroNumPy(BaseTestPyPyC):
assert log.result == 42.0
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
+ guard_not_invalidated(descr=...)
i128 = int_lt(i120, i42)
guard_true(i128, descr=...)
i129 = int_lt(i120, i48)
guard_true(i129, descr=...)
i131 = int_mul(i120, i57)
i132 = int_add(i53, i131)
- guard_not_invalidated(descr=...)
%(align_check)s
raw_store(i103, i132, 42.000000, descr=<ArrayF 8>)
i153 = int_add(i120, 1)
@@ -422,13 +415,13 @@ class TestMicroNumPy(BaseTestPyPyC):
guard_false(i92, descr=...)
i93 = int_add(i91, 1)
setfield_gc(p23, i93, descr=<FieldS pypy.objspace.std.iterobject.W_AbstractSeqIterObject.inst_index 8>)
+ guard_not_invalidated(descr=...)
i94 = int_ge(i91, i56)
guard_false(i94, descr=...)
i96 = int_mul(i91, i58)
i97 = int_add(i51, i96)
%(align_check)s
f98 = raw_load_f(i63, i97, descr=<ArrayF 8>)
- guard_not_invalidated(descr=...)
f100 = float_mul(f98, 0.500000)
i101 = int_add(i79, 1)
i102 = arraylen_gc(p85, descr=<ArrayP .>)
diff --git a/pypy/module/time/interp_time.py b/pypy/module/time/interp_time.py
index f4c6b4f023..e6e9934ec6 100644
--- a/pypy/module/time/interp_time.py
+++ b/pypy/module/time/interp_time.py
@@ -236,11 +236,11 @@ def _init_timezone(space):
timezone = c_get_timezone()
altzone = timezone - 3600
daylight = c_get_daylight()
- with rffi.scoped_alloc_buffer(100) as buf:
- s = c_get_tzname(100, 0, buf.raw)
- tzname[0] = buf.str(s)
- s = c_get_tzname(100, 1, buf.raw)
- tzname[1] = buf.str(s)
+ for i in [0, 1]:
+ blen = c_get_tzname(0, i, None)
+ with rffi.scoped_alloc_buffer(blen) as buf:
+ s = c_get_tzname(blen, i, buf.raw)
+ tzname[i] = buf.str(s - 1)
if _POSIX:
if _CYGWIN:
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
index 1cfe23136b..32ec45addd 100644
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -124,8 +124,14 @@ class Object(object):
def descr__init__(space, w_obj, __args__):
pass
+def get_printable_location(itergreenkey, w_itemtype):
+ return "DescrOperation.contains [%s, %s]" % (
+ itergreenkey.iterator_greenkey_printable(),
+ w_itemtype.getname(w_itemtype.space))
+
contains_jitdriver = jit.JitDriver(name='contains',
- greens=['w_type'], reds='auto')
+ greens=['itergreenkey', 'w_itemtype'], reds='auto',
+ get_printable_location=get_printable_location)
class DescrOperation(object):
# This is meant to be a *mixin*.
@@ -406,9 +412,10 @@ class DescrOperation(object):
def _contains(space, w_container, w_item):
w_iter = space.iter(w_container)
- w_type = space.type(w_iter)
+ itergreenkey = space.iterator_greenkey(w_iter)
+ w_itemtype = space.type(w_item)
while 1:
- contains_jitdriver.jit_merge_point(w_type=w_type)
+ contains_jitdriver.jit_merge_point(itergreenkey=itergreenkey, w_itemtype=w_itemtype)
try:
w_next = space.next(w_iter)
except OperationError as e:
diff --git a/pypy/objspace/std/bytearrayobject.py b/pypy/objspace/std/bytearrayobject.py
index fca124e120..9cecb9a22f 100644
--- a/pypy/objspace/std/bytearrayobject.py
+++ b/pypy/objspace/std/bytearrayobject.py
@@ -561,14 +561,14 @@ def makebytearraydata_w(space, w_source):
return list(buf.as_str())
return _from_byte_sequence(space, w_source)
-def _get_printable_location(w_type):
- return ('bytearray_from_byte_sequence [w_type=%s]' %
- w_type.getname(w_type.space))
+def _get_printable_location(greenkey):
+ return ('bytearray_from_byte_sequence [%s]' %
+ greenkey.iterator_greenkey_printable())
_byteseq_jitdriver = jit.JitDriver(
name='bytearray_from_byte_sequence',
- greens=['w_type'],
- reds=['w_iter', 'data'],
+ greens=['greenkey'],
+ reds='auto',
get_printable_location=_get_printable_location)
def _from_byte_sequence(space, w_source):
@@ -586,11 +586,9 @@ def _from_byte_sequence(space, w_source):
return data
def _from_byte_sequence_loop(space, w_iter, data):
- w_type = space.type(w_iter)
+ greenkey = space.iterator_greenkey(w_iter)
while True:
- _byteseq_jitdriver.jit_merge_point(w_type=w_type,
- w_iter=w_iter,
- data=data)
+ _byteseq_jitdriver.jit_merge_point(greenkey=greenkey)
try:
w_item = space.next(w_iter)
except OperationError as e:
diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py
index 1afe562307..2dc9ae8d97 100644
--- a/pypy/objspace/std/listobject.py
+++ b/pypy/objspace/std/listobject.py
@@ -131,26 +131,26 @@ def get_strategy_from_list_objects(space, list_w, sizehint):
return space.fromcache(ObjectListStrategy)
-def _get_printable_location(w_type):
- return ('list__do_extend_from_iterable [w_type=%s]' %
- w_type.getname(w_type.space))
+def _get_printable_location(strategy_type, greenkey):
+ return 'list__do_extend_from_iterable [%s, %s]' % (
+ strategy_type,
+ greenkey.iterator_greenkey_printable())
_do_extend_jitdriver = jit.JitDriver(
name='list__do_extend_from_iterable',
- greens=['w_type'],
- reds=['i', 'w_iterator', 'w_list'],
+ greens=['strategy_type', 'greenkey'],
+ reds='auto',
get_printable_location=_get_printable_location)
def _do_extend_from_iterable(space, w_list, w_iterable):
w_iterator = space.iter(w_iterable)
- w_type = space.type(w_iterator)
+ greenkey = space.iterator_greenkey(w_iterator)
i = 0
while True:
- _do_extend_jitdriver.jit_merge_point(w_type=w_type,
- i=i,
- w_iterator=w_iterator,
- w_list=w_list)
+ _do_extend_jitdriver.jit_merge_point(
+ greenkey=greenkey,
+ strategy_type=type(w_list.strategy))
try:
w_list.append(space.next(w_iterator))
except OperationError as e:
@@ -749,7 +749,9 @@ class W_ListObject(W_Root):
if mucked:
raise oefmt(space.w_ValueError, "list modified during sort")
-find_jmp = jit.JitDriver(greens = ['tp'], reds = 'auto', name = 'list.find')
+def get_printable_location(strategy_type, tp):
+ return "list.find [%s, %s]" % (strategy_type, tp.getname(tp.space), )
+find_jmp = jit.JitDriver(greens=['strategy_type', 'tp'], reds='auto', name='list.find', get_printable_location=get_printable_location)
class ListStrategy(object):
@@ -777,7 +779,7 @@ class ListStrategy(object):
# needs to be safe against eq_w mutating stuff
tp = space.type(w_item)
while i < stop and i < w_list.length():
- find_jmp.jit_merge_point(tp=tp)
+ find_jmp.jit_merge_point(tp=tp, strategy_type=type(self))
if space.eq_w(w_item, w_list.getitem(i)):
return i
i += 1
diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py
index 235cab2a82..605627dc95 100644
--- a/pypy/objspace/std/setobject.py
+++ b/pypy/objspace/std/setobject.py
@@ -1681,7 +1681,7 @@ def _pick_correct_strategy_unroll(space, w_set, w_iterable):
def get_printable_location(tp, strategy):
- return "create_set: %s %s" % (tp, strategy)
+ return "create_set: %s %s" % (tp.iterator_greenkey_printable(), strategy)
create_set_driver = jit.JitDriver(name='create_set',
greens=['tp', 'strategy'],
@@ -1692,7 +1692,7 @@ def _create_from_iterable(space, w_set, w_iterable):
w_set.strategy = strategy = space.fromcache(EmptySetStrategy)
w_set.sstorage = strategy.get_empty_storage()
- tp = space.type(w_iterable)
+ tp = space.iterator_greenkey(w_iterable)
w_iter = space.iter(w_iterable)
while True:
diff --git a/pypy/objspace/std/tupleobject.py b/pypy/objspace/std/tupleobject.py
index cbdae7efe6..d45521ebec 100644
--- a/pypy/objspace/std/tupleobject.py
+++ b/pypy/objspace/std/tupleobject.py
@@ -27,13 +27,22 @@ def _unroll_condition_cmp(self, space, other):
jit.loop_unrolling_heuristic(other, other.length(), UNROLL_CUTOFF))
-contains_jmp = jit.JitDriver(greens = ['tp'], reds = 'auto',
- name = 'tuple.contains')
+def get_printable_location(tp):
+ return "tuple.contains [%s]" % (tp.getname(tp.space), )
+
+contains_driver = jit.JitDriver(greens = ['tp'], reds = 'auto',
+ name = 'tuple.contains',
+ get_printable_location=get_printable_location)
+
+def get_printable_location(w_type):
+ return "tuple.hash [%s]" % (w_type.getname(w_type.space), )
hash_driver = jit.JitDriver(
name='tuple.hash',
greens=['w_type'],
- reds='auto')
+ reds='auto',
+ get_printable_location=get_printable_location
+ )
class W_AbstractTupleObject(W_Root):
__slots__ = ()
@@ -159,10 +168,14 @@ class W_AbstractTupleObject(W_Root):
def _descr_contains_jmp(self, space, w_obj):
tp = space.type(w_obj)
- for w_item in self.tolist():
- contains_jmp.jit_merge_point(tp=tp)
+ list_w = self.tolist()
+ i = 0
+ while i < len(list_w):
+ w_item = list_w[i]
+ contains_driver.jit_merge_point(tp=tp)
if space.eq_w(w_obj, w_item):
return space.w_True
+ i += 1
return space.w_False
def descr_add(self, space, w_other):
@@ -307,12 +320,16 @@ class W_TupleObject(W_AbstractTupleObject):
x = 0x345678
z = len(self.wrappeditems)
w_type = space.type(self.wrappeditems[0])
- for w_item in self.wrappeditems:
+ wrappeditems = self.wrappeditems
+ i = 0
+ while i < len(wrappeditems):
hash_driver.jit_merge_point(w_type=w_type)
+ w_item = wrappeditems[i]
y = space.hash_w(w_item)
x = (x ^ y) * mult
z -= 1
mult += 82520 + z + z
+ i += 1
x += 97531
return space.newint(intmask(x))
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
index 039b3d5881..3d042356a4 100644
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -711,6 +711,9 @@ class W_TypeObject(W_Root):
else:
return space.newtext("<%s '%s'>" % (kind, self.name))
+ def iterator_greenkey_printable(self):
+ return self.name
+
def descr_getattribute(self, space, w_name):
name = space.text_w(w_name)
w_descr = space.lookup(self, name)
diff --git a/rpython/jit/backend/zarch/test/test_regalloc.py b/rpython/jit/backend/zarch/test/test_regalloc.py
index 9f8655cf8b..e8ff7fc686 100644
--- a/rpython/jit/backend/zarch/test/test_regalloc.py
+++ b/rpython/jit/backend/zarch/test/test_regalloc.py
@@ -13,6 +13,9 @@ CPU = getcpuclass()
class FakeAssembler(object):
def __init__(self):
self.move_count = 0
+ self.num_spills = 0
+ self.num_spills_to_existing = 0
+
def regalloc_mov(self, f, t):
self.move_count += 1
diff --git a/rpython/jit/metainterp/heapcache.py b/rpython/jit/metainterp/heapcache.py
index d643907182..0d46b4e120 100644
--- a/rpython/jit/metainterp/heapcache.py
+++ b/rpython/jit/metainterp/heapcache.py
@@ -1,5 +1,6 @@
from rpython.jit.metainterp.history import Const, ConstInt
from rpython.jit.metainterp.history import FrontendOp, RefFrontendOp
+from rpython.jit.metainterp.history import new_ref_dict
from rpython.jit.metainterp.resoperation import rop, OpHelpers
from rpython.jit.metainterp.executor import constant_from_op
from rpython.rlib.rarithmetic import r_uint32, r_uint
@@ -61,12 +62,17 @@ class CacheEntry(object):
# on writes to the field.
self.quasiimmut_seen = None
+ # set of refs for *constants* that we've seen a quasi-immut field on.
+ self.quasiimmut_seen_refs = None
+
def _clear_cache_on_write(self, seen_allocation_of_target):
if not seen_allocation_of_target:
self.cache_seen_allocation.clear()
self.cache_anything.clear()
if self.quasiimmut_seen is not None:
self.quasiimmut_seen.clear()
+ if self.quasiimmut_seen_refs is not None:
+ self.quasiimmut_seen_refs.clear()
def _seen_alloc(self, ref_box):
if not isinstance(ref_box, RefFrontendOp):
@@ -100,6 +106,8 @@ class CacheEntry(object):
self._invalidate_unescaped(self.cache_seen_allocation)
if self.quasiimmut_seen is not None:
self.quasiimmut_seen.clear()
+ if self.quasiimmut_seen_refs is not None:
+ self.quasiimmut_seen_refs.clear()
def _invalidate_unescaped(self, d):
for ref_box in d.keys():
@@ -162,6 +170,12 @@ class HeapCache(object):
# heap array cache
# maps descrs to {index: CacheEntry} dicts
self.heap_array_cache = {}
+ self.need_guard_not_invalidated = True
+
+ # result of one loop invariant call
+ self.loop_invariant_result = None
+ self.loop_invariant_descr = None
+ self.loop_invariant_arg0int = -1
def reset_keep_likely_virtuals(self):
# Update only 'head_version', but 'likely_virtual_version' remains
@@ -272,6 +286,7 @@ class HeapCache(object):
rop._NOSIDEEFFECT_FIRST <= opnum <= rop._NOSIDEEFFECT_LAST or
rop._GUARD_FIRST <= opnum <= rop._GUARD_LAST):
return
+ self.need_guard_not_invalidated = True # can do better, but good start
if (OpHelpers.is_plain_call(opnum) or
OpHelpers.is_call_loopinvariant(opnum) or
OpHelpers.is_cond_call_value(opnum) or
@@ -387,7 +402,7 @@ class HeapCache(object):
self._set_flag(box, HF_KNOWN_NULLITY)
def is_nonstandard_virtualizable(self, box):
- return self._check_flag(box, HF_NONSTD_VABLE)
+ return self._check_flag(box, HF_NONSTD_VABLE) or self._check_flag(box, HF_SEEN_ALLOCATION)
def nonstandard_virtualizables_now_known(self, box):
self._set_flag(box, HF_NONSTD_VABLE)
@@ -500,15 +515,37 @@ class HeapCache(object):
def is_quasi_immut_known(self, fielddescr, box):
cache = self.heap_cache.get(fielddescr, None)
- if cache is not None and cache.quasiimmut_seen is not None:
- return box in cache.quasiimmut_seen
+ if cache is not None:
+ if isinstance(box, Const):
+ if cache.quasiimmut_seen_refs is not None:
+ return box.getref_base() in cache.quasiimmut_seen_refs
+ else:
+ if cache.quasiimmut_seen is not None:
+ return box in cache.quasiimmut_seen
return False
def quasi_immut_now_known(self, fielddescr, box):
cache = self.heap_cache.get(fielddescr, None)
if cache is None:
cache = self.heap_cache[fielddescr] = CacheEntry(self)
- if cache.quasiimmut_seen is not None:
- cache.quasiimmut_seen[box] = None
+ if isinstance(box, Const):
+ if cache.quasiimmut_seen_refs is None:
+ cache.quasiimmut_seen_refs = new_ref_dict()
+ cache.quasiimmut_seen_refs[box.getref_base()] = None
else:
- cache.quasiimmut_seen = {box: None}
+ if cache.quasiimmut_seen is not None:
+ cache.quasiimmut_seen[box] = None
+ else:
+ cache.quasiimmut_seen = {box: None}
+
+ def call_loopinvariant_known_result(self, allboxes, descr):
+ if self.loop_invariant_descr is not descr:
+ return None
+ if self.loop_invariant_arg0int != allboxes[0].getint():
+ return None
+ return self.loop_invariant_result
+
+ def call_loopinvariant_now_known(self, allboxes, descr, res):
+ self.loop_invariant_descr = descr
+ self.loop_invariant_arg0int = allboxes[0].getint()
+ self.loop_invariant_result = res
diff --git a/rpython/jit/metainterp/jitprof.py b/rpython/jit/metainterp/jitprof.py
index c3d3d70454..4ba150a97f 100644
--- a/rpython/jit/metainterp/jitprof.py
+++ b/rpython/jit/metainterp/jitprof.py
@@ -138,6 +138,7 @@ class Profiler(BaseProfiler):
line = "TOTAL: \t\t%f" % (self.tk - self.starttime, )
debug_print(line)
self._print_intline("ops", cnt[Counters.OPS])
+ self._print_intline("heapcached ops", cnt[Counters.HEAPCACHED_OPS])
self._print_intline("recorded ops", cnt[Counters.RECORDED_OPS])
self._print_intline(" calls", calls)
self._print_intline("guards", cnt[Counters.GUARDS])
diff --git a/rpython/jit/metainterp/optimizeopt/heap.py b/rpython/jit/metainterp/optimizeopt/heap.py
index bfaa58e1bc..53c8b3a883 100644
--- a/rpython/jit/metainterp/optimizeopt/heap.py
+++ b/rpython/jit/metainterp/optimizeopt/heap.py
@@ -229,7 +229,6 @@ class OptHeap(Optimization):
# cache of corresponding {array descrs: dict 'entries' field descr}
self.corresponding_array_descrs = {}
#
- self._remove_guard_not_invalidated = False
self._seen_guard_not_invalidated = False
def setup(self):
@@ -655,7 +654,6 @@ class OptHeap(Optimization):
# registered.
structvalue = self.ensure_ptr_info_arg0(op)
if not structvalue.is_constant():
- self._remove_guard_not_invalidated = True
return # not a constant at all; ignore QUASIIMMUT_FIELD
#
from rpython.jit.metainterp.quasiimmut import QuasiImmutDescr
@@ -670,11 +668,19 @@ class OptHeap(Optimization):
if self.optimizer.quasi_immutable_deps is None:
self.optimizer.quasi_immutable_deps = {}
self.optimizer.quasi_immutable_deps[qmutdescr.qmut] = None
- self._remove_guard_not_invalidated = False
def optimize_GUARD_NOT_INVALIDATED(self, op):
- if self._remove_guard_not_invalidated:
- return
+ # logic: we need one guard_not_invalidated after every call that can
+ # invalidate something. This is independent to whether the
+ # quasiimmut_field op is removed or not! The tracer will only trace one
+ # guard_not_invalidated after each call, so even if the first
+ # quasiimmut_field is removed, the second one might not be and could
+ # rely on the presence of an earlier guard_not_invalidated. This might
+ # under rare circumstances leave a extra guard_not_invalidated in the
+ # trace! But guard_not_invalidated is cheap, it emits no instructions
+ # and its only cost is the size of the resume data. Therfore that is
+ # still a better tradeoff than capturing resume data for every
+ # quasiimmut_field in the front end *all the time*
if self._seen_guard_not_invalidated:
return
self._seen_guard_not_invalidated = True
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
index c2816d7421..4de28a02ba 100644
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -7217,6 +7217,7 @@ class TestOptimizeOpt(BaseTestWithUnroll):
"""
expected = """
[p0, p1, i0]
+ guard_not_invalidated() []
i1 = getfield_gc_i(p0, descr=quasifielddescr)
escape_n(i1)
jump(p1, p0, i1)
@@ -7240,6 +7241,29 @@ class TestOptimizeOpt(BaseTestWithUnroll):
"""
self.optimize_loop(ops, expected, expected)
+ def test_always_leave_one_guard_not_invalidated(self):
+ ops = """
+ [p0, p1, i0]
+ quasiimmut_field(p0, descr=quasiimmutdescr)
+ guard_not_invalidated() [] # needs to stay
+ i1 = getfield_gc_i(p0, descr=quasifielddescr)
+ quasiimmut_field(ConstPtr(quasiptr), descr=quasiimmutdescr)
+ guard_not_invalidated() []
+ i2 = getfield_gc_i(ConstPtr(quasiptr), descr=quasifielddescr)
+ escape_n(i1)
+ escape_n(i2)
+ jump(p1, p0, i1)
+ """
+ expected = """
+ [p0, p1, i0]
+ guard_not_invalidated() []
+ i1 = getfield_gc_i(p0, descr=quasifielddescr)
+ escape_n(i1)
+ escape_n(-4247)
+ jump(p1, p0, i1)
+ """
+ self.optimize_loop(ops, expected)
+
def test_remove_extra_guards_not_invalidated(self):
ops = """
[i0]
@@ -7318,7 +7342,9 @@ class TestOptimizeOpt(BaseTestWithUnroll):
"""
expected = """
[i0a, i0b]
+ guard_not_invalidated() []
call_may_force_n(i0b, descr=mayforcevirtdescr)
+ guard_not_invalidated() []
i3 = escape_i(421)
i4 = escape_i(421)
jump(i3, i4)
diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py
index 4c9ee96686..3b092633ad 100644
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -281,6 +281,7 @@ class MIFrame(object):
@arguments("box")
def opimpl_assert_not_none(self, box):
if self.metainterp.heapcache.is_nullity_known(box):
+ self.metainterp.staticdata.profiler.count_ops(rop.ASSERT_NOT_NONE, Counters.HEAPCACHED_OPS)
return
self.execute(rop.ASSERT_NOT_NONE, box)
self.metainterp.heapcache.nullity_now_known(box)
@@ -289,6 +290,7 @@ class MIFrame(object):
def opimpl_record_exact_class(self, box, clsbox):
from rpython.rtyper.lltypesystem import llmemory
if self.metainterp.heapcache.is_class_known(box):
+ self.metainterp.staticdata.profiler.count_ops(rop.RECORD_EXACT_CLASS, Counters.HEAPCACHED_OPS)
return
if isinstance(clsbox, Const):
self.execute(rop.RECORD_EXACT_CLASS, box, clsbox)
@@ -395,6 +397,7 @@ class MIFrame(object):
heapcache = self.metainterp.heapcache
value = box.nonnull()
if heapcache.is_nullity_known(box):
+ self.metainterp.staticdata.profiler.count_ops(rop.GUARD_NONNULL, Counters.HEAPCACHED_OPS)
return value
if value:
if not self.metainterp.heapcache.is_class_known(box):
@@ -478,6 +481,7 @@ class MIFrame(object):
if tobox:
# sanity check: see whether the current array value
# corresponds to what the cache thinks the value is
+ self.metainterp.staticdata.profiler.count_ops(rop.GETARRAYITEM_GC_I, Counters.HEAPCACHED_OPS)
resvalue = executor.execute(self.metainterp.cpu, self.metainterp,
op, arraydescr, arraybox, indexbox)
if op == 'i':
@@ -579,6 +583,8 @@ class MIFrame(object):
lengthbox = self.execute_with_descr(
rop.ARRAYLEN_GC, arraydescr, arraybox)
self.metainterp.heapcache.arraylen_now_known(arraybox, lengthbox)
+ else:
+ self.metainterp.staticdata.profiler.count_ops(rop.ARRAYLEN_GC, Counters.HEAPCACHED_OPS)
return lengthbox
@arguments("box", "box", "descr", "orgpc")
@@ -734,6 +740,7 @@ class MIFrame(object):
# see ConstFloat.same_constant
assert ConstFloat(resvalue).same_constant(
upd.currfieldbox.constbox())
+ self.metainterp.staticdata.profiler.count_ops(rop.GETFIELD_GC_I, Counters.HEAPCACHED_OPS)
return upd.currfieldbox
resbox = self.execute_with_descr(opnum, fielddescr, box)
upd.getfield_now_known(resbox)
@@ -764,6 +771,7 @@ class MIFrame(object):
def _opimpl_setfield_gc_any(self, box, valuebox, fielddescr):
upd = self.metainterp.heapcache.get_field_updater(box, fielddescr)
if upd.currfieldbox is valuebox:
+ self.metainterp.staticdata.profiler.count_ops(rop.SETFIELD_GC, Counters.HEAPCACHED_OPS)
return
self.metainterp.execute_and_record(rop.SETFIELD_GC, fielddescr, box, valuebox)
upd.setfield(valuebox)
@@ -865,14 +873,19 @@ class MIFrame(object):
from rpython.jit.metainterp.quasiimmut import QuasiImmutDescr
cpu = self.metainterp.cpu
if self.metainterp.heapcache.is_quasi_immut_known(fielddescr, box):
+ self.metainterp.staticdata.profiler.count_ops(rop.QUASIIMMUT_FIELD, Counters.HEAPCACHED_OPS)
return
descr = QuasiImmutDescr(cpu, box.getref_base(), fielddescr,
mutatefielddescr)
self.metainterp.heapcache.quasi_immut_now_known(fielddescr, box)
self.metainterp.history.record(rop.QUASIIMMUT_FIELD, [box],
None, descr=descr)
- self.metainterp.generate_guard(rop.GUARD_NOT_INVALIDATED,
- resumepc=orgpc)
+ if self.metainterp.heapcache.need_guard_not_invalidated:
+ self.metainterp.generate_guard(rop.GUARD_NOT_INVALIDATED,
+ resumepc=orgpc)
+ self.metainterp.heapcache.need_guard_not_invalidated = False
+
+
@arguments("box", "descr", "orgpc")
def opimpl_jit_force_quasi_immutable(self, box, mutatefielddescr, orgpc):
@@ -905,6 +918,7 @@ class MIFrame(object):
# returns True if 'box' is actually not the "standard" virtualizable
# that is stored in metainterp.virtualizable_boxes[-1]
if self.metainterp.heapcache.is_nonstandard_virtualizable(box):
+ self.metainterp.staticdata.profiler.count_ops(rop.PTR_EQ, Counters.HEAPCACHED_OPS)
return True
if box is self.metainterp.forced_virtualizable:
self.metainterp.forced_virtualizable = None
@@ -1743,24 +1757,29 @@ class MIFrame(object):
effect = effectinfo.extraeffect
tp = descr.get_normalized_result_type()
if effect == effectinfo.EF_LOOPINVARIANT:
+ res = self.metainterp.heapcache.call_loopinvariant_known_result(allboxes, descr)
+ if res is not None:
+ return res
if tp == 'i':
- return self.execute_varargs(rop.CALL_LOOPINVARIANT_I,
+ res = self.execute_varargs(rop.CALL_LOOPINVARIANT_I,
allboxes,
descr, False, False)
elif tp == 'r':
- return self.execute_varargs(rop.CALL_LOOPINVARIANT_R,
+ res = self.execute_varargs(rop.CALL_LOOPINVARIANT_R,
allboxes,
descr, False, False)
elif tp == 'f':
- return self.execute_varargs(rop.CALL_LOOPINVARIANT_F,
+ res = self.execute_varargs(rop.CALL_LOOPINVARIANT_F,
allboxes,
descr, False, False)
elif tp == 'v':
- return self.execute_varargs(rop.CALL_LOOPINVARIANT_N,
+ res = self.execute_varargs(rop.CALL_LOOPINVARIANT_N,
allboxes,
descr, False, False)
else:
assert False
+ self.metainterp.heapcache.call_loopinvariant_now_known(allboxes, descr, res)
+ return res
exc = effectinfo.check_can_raise()
pure = effectinfo.check_is_elidable()
if tp == 'i':
@@ -1815,6 +1834,7 @@ class MIFrame(object):
if standard_box is vref_box:
return vref_box
if self.metainterp.heapcache.is_nonstandard_virtualizable(vref_box):
+ self.metainterp.staticdata.profiler.count_ops(rop.PTR_EQ, Counters.HEAPCACHED_OPS)
return None
eqbox = self.metainterp.execute_and_record(rop.PTR_EQ, None, vref_box, standard_box)
eqbox = self.implement_guard_value(eqbox, pc)
diff --git a/rpython/jit/metainterp/test/test_heapcache.py b/rpython/jit/metainterp/test/test_heapcache.py
index 7d7a8eee78..72649a81bd 100644
--- a/rpython/jit/metainterp/test/test_heapcache.py
+++ b/rpython/jit/metainterp/test/test_heapcache.py
@@ -3,6 +3,7 @@ from rpython.jit.metainterp.heapcache import HeapCache
from rpython.jit.metainterp.resoperation import rop, InputArgInt
from rpython.jit.metainterp.history import ConstInt, ConstPtr, BasicFailDescr
from rpython.jit.metainterp.history import IntFrontendOp, RefFrontendOp
+from rpython.rtyper.lltypesystem import llmemory, rffi
descr1 = object()
descr2 = object()
@@ -111,6 +112,16 @@ class TestHeapCache(object):
assert not h.is_nonstandard_virtualizable(box1)
assert not h.is_nonstandard_virtualizable(box2)
+ def test_nonstandard_virtualizable_allocation(self):
+ h = HeapCache()
+ box1 = RefFrontendOp(1)
+ h.new(box1)
+ # we've seen the allocation, so it's not the virtualizable
+ assert h.is_nonstandard_virtualizable(box1)
+
+ h.reset()
+ assert not h.is_nonstandard_virtualizable(box1)
+
def test_heapcache_fields(self):
h = HeapCache()
box1 = RefFrontendOp(1)
@@ -853,3 +864,39 @@ class TestHeapCache(object):
rop.CALL_N, FakeCallDescr(FakeEffectinfo.EF_CAN_RAISE), [])
assert not h.is_quasi_immut_known(descr2, box3)
assert not h.is_quasi_immut_known(descr2, box4)
+
+
+ def test_quasiimmut_seen_consts(self):
+ h = HeapCache()
+ box1 = ConstPtr(rffi.cast(llmemory.GCREF, 1))
+ box2 = ConstPtr(rffi.cast(llmemory.GCREF, 1))
+ box3 = ConstPtr(rffi.cast(llmemory.GCREF, 1))
+ box4 = ConstPtr(rffi.cast(llmemory.GCREF, 1))
+ assert not h.is_quasi_immut_known(descr1, box1)
+ assert not h.is_quasi_immut_known(descr1, box2)
+ assert not h.is_quasi_immut_known(descr2, box3)
+ assert not h.is_quasi_immut_known(descr2, box4)
+ h.quasi_immut_now_known(descr1, box1)
+ assert h.is_quasi_immut_known(descr1, box1)
+ assert h.is_quasi_immut_known(descr1, box2)
+ assert not h.is_quasi_immut_known(descr2, box3)
+ assert not h.is_quasi_immut_known(descr2, box4)
+ h.quasi_immut_now_known(descr2, box3)
+ assert h.is_quasi_immut_known(descr1, box1)
+ assert h.is_quasi_immut_known(descr1, box2)
+ assert h.is_quasi_immut_known(descr2, box3)
+ assert h.is_quasi_immut_known(descr2, box4)
+
+ # invalidate the descr1 cache
+
+ vbox1 = RefFrontendOp(1)
+ vbox2 = RefFrontendOp(2)
+ h.setfield(vbox1, vbox2, descr1)
+ assert not h.is_quasi_immut_known(descr1, box1)
+ assert not h.is_quasi_immut_known(descr1, box2)
+
+ # a call invalidates everything
+ h.invalidate_caches(
+ rop.CALL_N, FakeCallDescr(FakeEffectinfo.EF_CAN_RAISE), [])
+ assert not h.is_quasi_immut_known(descr2, box3)
+ assert not h.is_quasi_immut_known(descr2, box4)
diff --git a/rpython/jit/metainterp/test/test_jitprof.py b/rpython/jit/metainterp/test/test_jitprof.py
index a083124a78..41173a52a2 100644
--- a/rpython/jit/metainterp/test/test_jitprof.py
+++ b/rpython/jit/metainterp/test/test_jitprof.py
@@ -97,3 +97,32 @@ class TestProfile(ProfilerMixin):
assert res == f(6, 7, 2)
profiler = pyjitpl._warmrunnerdesc.metainterp_sd.profiler
assert profiler.calls == 1
+
+ def test_heapcache_stats(self):
+ class A:
+ pass
+ class B(A):
+ pass
+ @dont_look_inside
+ def extern(n):
+ if n == -7:
+ return None
+ elif n:
+ return A()
+ else:
+ return B()
+ myjitdriver = JitDriver(greens = [], reds='auto')
+ def f(x, y):
+ res = 0
+ while y > 0:
+ myjitdriver.jit_merge_point()
+ obj = extern(y)
+ res += x + isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B) + isinstance(obj, B)
+ res += x
+ y -= 1
+ return res * 2
+ res = self.meta_interp(f, [6, 7])
+ assert res == f(6, 7)
+ profiler = pyjitpl._warmrunnerdesc.metainterp_sd.profiler
+ assert profiler.counters[Counters.HEAPCACHED_OPS] == 3
+
diff --git a/rpython/jit/metainterp/test/test_quasiimmut.py b/rpython/jit/metainterp/test/test_quasiimmut.py
index 0e34c25057..380dc80de1 100644
--- a/rpython/jit/metainterp/test/test_quasiimmut.py
+++ b/rpython/jit/metainterp/test/test_quasiimmut.py
@@ -121,7 +121,7 @@ class QuasiImmutTests(object):
assert f(100, 7) == 721
res = self.meta_interp(f, [100, 7])
assert res == 721
- self.check_resops(guard_not_invalidated=0, getfield_gc_r=1, getfield_gc_i=2)
+ self.check_resops(guard_not_invalidated=2, getfield_gc_r=1, getfield_gc_i=2)
#
from rpython.jit.metainterp.warmspot import get_stats
loops = get_stats().loops
@@ -575,5 +575,29 @@ class QuasiImmutTests(object):
res = self.meta_interp(main, [10])
assert res == main(10)
+ def test_dont_emit_too_many_guard_not_invalidated(self):
+ myjitdriver = JitDriver(greens=['foo'], reds=['x', 'total'])
+ class Foo:
+ _immutable_fields_ = ['a?', 'b?', 'c?']
+ def __init__(self, a):
+ self.a = a
+ self.b = a - 1
+ self.c = a - 3
+ def f(a, x):
+ foo = Foo(a)
+ total = 0
+ while x > 0:
+ myjitdriver.jit_merge_point(foo=foo, x=x, total=total)
+ # read a few quasi-immutable fields out of a Constant
+ total += foo.a + foo.b + foo.c
+ x -= 1
+ return total
+ #
+ res = self.meta_interp(f, [100, 7], enable_opts="")
+ assert res == f(100, 7)
+ # there should be no getfields, even though optimizations are turned off
+ self.check_resops(guard_not_invalidated=1)
+
+
class TestLLtypeGreenFieldsTests(QuasiImmutTests, LLJitMixin):
pass
diff --git a/rpython/jit/metainterp/test/test_threadlocal.py b/rpython/jit/metainterp/test/test_threadlocal.py
index 322e41a405..3207f33910 100644
--- a/rpython/jit/metainterp/test/test_threadlocal.py
+++ b/rpython/jit/metainterp/test/test_threadlocal.py
@@ -27,6 +27,16 @@ class ThreadLocalTest(object):
res = self.interp_operations(f, [])
assert res == 0x92
+ def test_threadlocalref_get_loopinvariant(self):
+ tlfield = rthread.ThreadLocalField(lltype.Signed, 'foobar_test_', True)
+
+ def f():
+ tlfield.setraw(0x544c)
+ return tlfield.getraw() + tlfield.getraw()
+
+ res = self.interp_operations(f, [])
+ assert res == 0x544c * 2
+ self.check_operations_history(call_loopinvariant_i=1)
class TestLLtype(ThreadLocalTest, LLJitMixin):
pass
diff --git a/rpython/jit/metainterp/test/test_tracingopts.py b/rpython/jit/metainterp/test/test_tracingopts.py
index 6d37e0fbe8..0f3a22f895 100644
--- a/rpython/jit/metainterp/test/test_tracingopts.py
+++ b/rpython/jit/metainterp/test/test_tracingopts.py
@@ -431,6 +431,43 @@ class TestLLtype(LLJitMixin):
self.check_history(getarrayitem_gc_i=0, getfield_gc_i=0,
getfield_gc_r=0)
+
+ def test_nonstandard_virtualizable(self):
+ myjitdriver = jit.JitDriver(greens = [], reds = ['n', 'x', 'i', 'frame'],
+ virtualizables = ['frame'])
+
+ class Frame(object):
+ _virtualizable_ = ['s']
+
+ def __init__(self, s):
+ self.s = s
+ self.next = None
+
+ def f(n, a, i):
+ frame = Frame(5)
+ 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)
+ n -= 1
+ s = frame.s
+ assert s >= 0
+ frame.s += 1
+ # make a new frame
+ f = Frame(7)
+ frame.next = f
+ x += f.s
+ frame.s -= 1
+ frame.next = None
+ return x
+
+ res = self.meta_interp(f, [10, 1, 1], listops=True)
+ assert res == f(10, 1, 1)
+ # we now that f is not the standard virtualizable, since we've seen its
+ # allocation
+ self.check_history(ptr_eq=0)
+
+
def test_heap_caching_array_pure(self):
class A(object):
pass
@@ -476,7 +513,6 @@ class TestLLtype(LLJitMixin):
res = self.interp_operations(fn, [-7])
assert res == -7 + 7
self.check_operations_history(getfield_gc_i=0)
- return
def test_heap_caching_multiple_objects(self):
class Gbl(object):
@@ -537,6 +573,31 @@ class TestLLtype(LLJitMixin):
assert res == 10
self.check_operations_history(quasiimmut_field=1)
+ def test_heap_caching_quasi_immutable_2(self):
+ class A:
+ _immutable_fields_ = ['x?']
+ a1 = A()
+ a1.x = 5
+ a2 = A()
+ a2.x = 7
+
+ @jit.elidable
+ def get(n):
+ if n > 0:
+ return a1
+ return a2
+
+ def g(a):
+ return a.x
+
+ def fn(n):
+ jit.promote(n)
+ return get(n).x + get(n).x
+ res = self.interp_operations(fn, [7])
+ assert res == 10
+ self.check_operations_history(quasiimmut_field=1)
+
+
def test_heap_caching_multiple_tuples(self):
class Gbl(object):
@@ -770,3 +831,31 @@ class TestLLtype(LLJitMixin):
assert res == 2 * 14
self.check_operations_history(getfield_gc_i=1)
+ def test_loop_invariant1(self):
+ class A(object):
+ pass
+ a = A()
+ a.current_a = A()
+ a.current_a.x = 1
+ @jit.loop_invariant
+ def f():
+ return a.current_a
+
+ @jit.loop_invariant
+ def f1():
+ return a.current_a
+
+ def g(x):
+ res = 0
+ res += f().x
+ res += f().x
+ res += f().x
+ res += f1().x # not reused!
+ res += f1().x
+ if x > 1000:
+ a.current_a = A()
+ a.current_a.x = 2
+ return res
+ res = self.interp_operations(g, [21])
+ assert res == g(21)
+ self.check_operations_history(call_loopinvariant_r=2)
diff --git a/rpython/jit/metainterp/warmspot.py b/rpython/jit/metainterp/warmspot.py
index 6953be1437..00f7f915ef 100644
--- a/rpython/jit/metainterp/warmspot.py
+++ b/rpython/jit/metainterp/warmspot.py
@@ -425,9 +425,9 @@ class WarmRunnerDesc(object):
graph = copygraph(graph)
[jmpp] = find_jit_merge_points([graph])
graph.startblock = support.split_before_jit_merge_point(*jmpp)
- # XXX this is incredibly obscure, but this is sometiems necessary
+ # XXX this is incredibly obscure, but this is sometimes necessary
# so we don't explode in checkgraph. for reasons unknown this
- # is not contanied within simplify_graph
+ # is not contained within simplify_graph
removenoops.remove_same_as(graph)
# a crash in the following checkgraph() means that you forgot
# to list some variable in greens=[] or reds=[] in JitDriver,
diff --git a/rpython/jit/tool/jitoutput.py b/rpython/jit/tool/jitoutput.py
index 32b7ff5dc9..df57438545 100644
--- a/rpython/jit/tool/jitoutput.py
+++ b/rpython/jit/tool/jitoutput.py
@@ -12,6 +12,7 @@ REGEXES = [
(('backend_no', 'backend_time'), '^Backend:\s+([\d.]+)\s+([\d.]+)$'),
(None, '^TOTAL.*$'),
(('ops.total',), '^ops:\s+(\d+)$'),
+ (('heapcached_ops', ), '^heapcached ops:\s+(\d+)$'),
(('recorded_ops.total',), '^recorded ops:\s+(\d+)$'),
(('recorded_ops.calls',), '^\s+calls:\s+(\d+)$'),
(('guards',), '^guards:\s+(\d+)$'),
diff --git a/rpython/jit/tool/test/test_jitoutput.py b/rpython/jit/tool/test/test_jitoutput.py
index 1fe9b8303b..abfef9241c 100644
--- a/rpython/jit/tool/test/test_jitoutput.py
+++ b/rpython/jit/tool/test/test_jitoutput.py
@@ -47,6 +47,7 @@ DATA = '''Tracing: 1 0.006992
Backend: 1 0.000525
TOTAL: 0.025532
ops: 2
+heapcached ops: 111
recorded ops: 6
calls: 3
guards: 1
@@ -77,6 +78,7 @@ def test_parse():
assert info.backend_no == 1
assert info.backend_time == 0.000525
assert info.ops.total == 2
+ assert info.heapcached_ops == 111
assert info.recorded_ops.total == 6
assert info.recorded_ops.calls == 3
assert info.guards == 1
diff --git a/rpython/rlib/_rsocket_rffi.py b/rpython/rlib/_rsocket_rffi.py
index 9956b51888..81ba133d6d 100644
--- a/rpython/rlib/_rsocket_rffi.py
+++ b/rpython/rlib/_rsocket_rffi.py
@@ -217,7 +217,7 @@ for name in constant_names:
if _WIN32:
# some SDKs define these values with an enum, #ifdef won't work
for name in ('RCVALL_ON', 'RCVALL_OFF', 'RCVALL_SOCKETLEVELONLY', 'TCP_FASTOPEN'):
- setattr(CConfig, name, platform.ConstantInteger(name))
+ setattr(CConfig, name, platform.DefinedConstantInteger(name))
constant_names.append(name)
constants["BDADDR_ANY"] = "00:00:00:00:00:00"
diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py
index d316b4dc3e..e61e372f87 100644
--- a/rpython/rlib/jit.py
+++ b/rpython/rlib/jit.py
@@ -1315,6 +1315,7 @@ class Counters(object):
TRACING
BACKEND
OPS
+ HEAPCACHED_OPS
RECORDED_OPS
GUARDS
OPT_OPS
diff --git a/rpython/rlib/rvmprof/src/shared/vmprof_common.h b/rpython/rlib/rvmprof/src/shared/vmprof_common.h
index b52ee5f7cd..1b2278b1d1 100644
--- a/rpython/rlib/rvmprof/src/shared/vmprof_common.h
+++ b/rpython/rlib/rvmprof/src/shared/vmprof_common.h
@@ -89,7 +89,7 @@ int opened_profile(const char *interp_name, int memory, int proflines, int nativ
result is NULL. */
#if PY_MAJOR_VERSION >= 3 && !defined(_Py_atomic_load_relaxed)
/* this was abruptly un-defined in 3.5.1 */
-void *volatile _PyThreadState_Current;
+extern void *volatile _PyThreadState_Current;
/* XXX simple volatile access is assumed atomic */
# define _Py_atomic_load_relaxed(pp) (*(pp))
#endif
diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py
index 245362edf2..0d09123617 100644
--- a/rpython/rtyper/rclass.py
+++ b/rpython/rtyper/rclass.py
@@ -416,6 +416,9 @@ class ClassRepr(Repr):
v_cls1, v_cls2 = hop.inputargs(class_repr, class_repr)
return hop.gendirectcall(ll_issubclass, v_cls1, v_cls2)
+ def ll_str(self, cls):
+ return cls.name
+
class RootClassRepr(ClassRepr):
"""ClassRepr for the root of the class hierarchy"""
diff --git a/rpython/rtyper/test/test_rclass.py b/rpython/rtyper/test/test_rclass.py
index 6ba42cc1c6..cb97129392 100644
--- a/rpython/rtyper/test/test_rclass.py
+++ b/rpython/rtyper/test/test_rclass.py
@@ -13,6 +13,8 @@ from rpython.rtyper.rclass import (IR_IMMUTABLE, IR_IMMUTABLE_ARRAY,
from rpython.rtyper.test.tool import BaseRtypingTest
from rpython.translator.translator import TranslationContext, graphof
+from rpython.rtyper.annlowlevel import llstr, hlstr
+
class EmptyBase(object):
pass
@@ -1339,3 +1341,28 @@ class TestRclass(BaseRtypingTest):
def f():
return a.next.next.next.next is not None
assert self.interpret(f, []) == True
+
+ def test_str_of_type(self):
+ class A(object):
+ pass
+
+ class B(A):
+ pass
+
+ def f(i):
+ if i:
+ a = A()
+ else:
+ a = B()
+ return str(type(a))
+ assert "A" in hlstr(self.interpret(f, [1]))
+ assert "B" in hlstr(self.interpret(f, [0]))
+
+ def g(i):
+ if i:
+ a = A()
+ else:
+ a = B()
+ return str(a.__class__)
+ assert "A" in hlstr(self.interpret(g, [1]))
+ assert "B" in hlstr(self.interpret(g, [0]))
diff --git a/rpython/translator/c/src/support.c b/rpython/translator/c/src/support.c
index 7bac3aaf52..3b05c5aa4f 100644
--- a/rpython/translator/c/src/support.c
+++ b/rpython/translator/c/src/support.c
@@ -14,7 +14,7 @@
#define NAN_WORD1 0
#define PY_UINT32_T unsigned int
-#ifndef __BIG_ENDIAN__
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define IEEE_8087
#endif