[pypy-commit] pypy tealet: hg merge default
arigo
noreply at buildbot.pypy.org
Fri Jul 22 13:42:24 CEST 2011
Author: Armin Rigo <arigo at tunes.org>
Branch: tealet
Changeset: r45875:e63fcce8c423
Date: 2011-07-15 09:09 +0200
http://bitbucket.org/pypy/pypy/changeset/e63fcce8c423/
Log: hg merge default
diff --git a/lib-python/modified-2.7/pickle.py b/lib-python/modified-2.7/pickle.py
--- a/lib-python/modified-2.7/pickle.py
+++ b/lib-python/modified-2.7/pickle.py
@@ -168,7 +168,7 @@
# Pickling machinery
-class Pickler:
+class Pickler(object):
def __init__(self, file, protocol=None):
"""This takes a file-like object for writing a pickle data stream.
diff --git a/lib-python/2.7/test/test_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_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py
--- a/lib_pypy/_ctypes/basics.py
+++ b/lib_pypy/_ctypes/basics.py
@@ -48,7 +48,8 @@
return self.from_param(as_parameter)
def get_ffi_param(self, value):
- return self.from_param(value)._to_ffi_param()
+ cdata = self.from_param(value)
+ return cdata, cdata._to_ffi_param()
def get_ffi_argtype(self):
if self._ffiargtype:
diff --git a/lib_pypy/_ctypes/function.py b/lib_pypy/_ctypes/function.py
--- a/lib_pypy/_ctypes/function.py
+++ b/lib_pypy/_ctypes/function.py
@@ -328,12 +328,14 @@
"native COM method call without 'this' parameter"
)
thisarg = cast(args[0], POINTER(POINTER(c_void_p)))
- newargs, argtypes, outargs = self._convert_args(argtypes, args[1:], kwargs)
+ keepalives, newargs, argtypes, outargs = self._convert_args(argtypes,
+ args[1:], kwargs)
newargs.insert(0, args[0].value)
argtypes.insert(0, c_void_p)
else:
thisarg = None
- newargs, argtypes, outargs = self._convert_args(argtypes, args, kwargs)
+ keepalives, newargs, argtypes, outargs = self._convert_args(argtypes,
+ args, kwargs)
funcptr = self._getfuncptr(argtypes, self._restype_, thisarg)
result = self._call_funcptr(funcptr, *newargs)
@@ -437,16 +439,15 @@
@classmethod
def _conv_param(cls, argtype, arg):
if isinstance(argtype, _CDataMeta):
- #arg = argtype.from_param(arg)
- arg = argtype.get_ffi_param(arg)
- return arg, argtype
+ cobj, ffiparam = argtype.get_ffi_param(arg)
+ return cobj, ffiparam, argtype
if argtype is not None:
arg = argtype.from_param(arg)
if hasattr(arg, '_as_parameter_'):
arg = arg._as_parameter_
if isinstance(arg, _CData):
- return arg._to_ffi_param(), type(arg)
+ return arg, arg._to_ffi_param(), type(arg)
#
# non-usual case: we do the import here to save a lot of code in the
# jit trace of the normal case
@@ -463,11 +464,12 @@
else:
raise TypeError("Don't know how to handle %s" % (arg,))
- return cobj._to_ffi_param(), type(cobj)
+ return cobj, cobj._to_ffi_param(), type(cobj)
def _convert_args(self, argtypes, args, kwargs, marker=object()):
newargs = []
outargs = []
+ keepalives = []
newargtypes = []
total = len(args)
paramflags = self._paramflags
@@ -495,7 +497,8 @@
val = defval
if val is marker:
val = 0
- newarg, newargtype = self._conv_param(argtype, val)
+ keepalive, newarg, newargtype = self._conv_param(argtype, val)
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
elif flag in (0, PARAMFLAG_FIN):
@@ -511,28 +514,32 @@
raise TypeError("required argument '%s' missing" % name)
else:
raise TypeError("not enough arguments")
- newarg, newargtype = self._conv_param(argtype, val)
+ keepalive, newarg, newargtype = self._conv_param(argtype, val)
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
elif flag == PARAMFLAG_FOUT:
if defval is not marker:
outargs.append(defval)
- newarg, newargtype = self._conv_param(argtype, defval)
+ keepalive, newarg, newargtype = self._conv_param(argtype, defval)
else:
import ctypes
val = argtype._type_()
outargs.append(val)
+ keepalive = None
newarg = ctypes.byref(val)
newargtype = type(newarg)
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
else:
raise ValueError("paramflag %d not yet implemented" % flag)
else:
try:
- newarg, newargtype = self._conv_param(argtype, args[i])
+ keepalive, newarg, newargtype = self._conv_param(argtype, args[i])
except (UnicodeError, TypeError, ValueError), e:
raise ArgumentError(str(e))
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
inargs_idx += 1
@@ -541,12 +548,13 @@
extra = args[len(newargs):]
for i, arg in enumerate(extra):
try:
- newarg, newargtype = self._conv_param(None, arg)
+ keepalive, newarg, newargtype = self._conv_param(None, arg)
except (UnicodeError, TypeError, ValueError), e:
raise ArgumentError(str(e))
+ keepalives.append(keepalive)
newargs.append(newarg)
newargtypes.append(newargtype)
- return newargs, newargtypes, outargs
+ return keepalives, newargs, newargtypes, outargs
def _wrap_result(self, restype, result):
diff --git a/lib_pypy/cPickle.py b/lib_pypy/cPickle.py
--- a/lib_pypy/cPickle.py
+++ b/lib_pypy/cPickle.py
@@ -29,7 +29,7 @@
PythonPickler.__init__(self, *args, **kw)
def memoize(self, obj):
- self.memo[None] = None # cPickle starts counting at one
+ self.memo[id(None)] = None # cPickle starts counting at one
return PythonPickler.memoize(self, obj)
def getvalue(self):
diff --git a/pypy/doc/interpreter-optimizations.rst b/pypy/doc/interpreter-optimizations.rst
--- a/pypy/doc/interpreter-optimizations.rst
+++ b/pypy/doc/interpreter-optimizations.rst
@@ -263,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/interpreter/astcompiler/misc.py b/pypy/interpreter/astcompiler/misc.py
--- a/pypy/interpreter/astcompiler/misc.py
+++ b/pypy/interpreter/astcompiler/misc.py
@@ -27,9 +27,10 @@
_emit_syntax_warning(space, w_msg, w_filename, w_lineno, w_offset)
-def parse_future(tree):
+def parse_future(tree, feature_flags):
future_lineno = 0
future_column = 0
+ flags = 0
have_docstring = False
body = None
if isinstance(tree, ast.Module):
@@ -37,7 +38,7 @@
elif isinstance(tree, ast.Interactive):
body = tree.body
if body is None:
- return 0, 0
+ return 0, 0, 0
for stmt in body:
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Str):
if have_docstring:
@@ -48,11 +49,16 @@
if stmt.module == "__future__":
future_lineno = stmt.lineno
future_column = stmt.col_offset
+ for alias in stmt.names:
+ assert isinstance(alias, ast.alias)
+ # If this is an invalid flag, it will be caught later in
+ # codegen.py.
+ flags |= feature_flags.get(alias.name, 0)
else:
break
else:
break
- return future_lineno, future_column
+ return flags, future_lineno, future_column
class ForbiddenNameAssignment(Exception):
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -130,6 +130,9 @@
raise operationerrfmt(space.w_TypeError,
"cannot create weak reference to '%s' object", typename)
+ def delweakref(self):
+ pass
+
def clear_all_weakrefs(self):
"""Call this at the beginning of interp-level __del__() methods
in subclasses. It ensures that weakrefs (if any) are cleared
@@ -143,29 +146,28 @@
# app-level, e.g. a user-defined __del__(), and this code
# tries to use weakrefs again, it won't reuse the broken
# (already-cleared) weakrefs from this lifeline.
- self.setweakref(lifeline.space, None)
+ self.delweakref()
lifeline.clear_all_weakrefs()
- __already_enqueued_for_destruction = False
+ __already_enqueued_for_destruction = ()
- def _enqueue_for_destruction(self, space, call_user_del=True):
+ def enqueue_for_destruction(self, space, callback, descrname):
"""Put the object in the destructor queue of the space.
- At a later, safe point in time, UserDelAction will use
- space.userdel() to call the object's app-level __del__ method.
+ At a later, safe point in time, UserDelAction will call
+ callback(self). If that raises OperationError, prints it
+ to stderr with the descrname string.
+
+ Note that 'callback' will usually need to start with:
+ assert isinstance(self, W_SpecificClass)
"""
# this function always resurect the object, so when
# running on top of CPython we must manually ensure that
# we enqueue it only once
if not we_are_translated():
- if self.__already_enqueued_for_destruction:
+ if callback in self.__already_enqueued_for_destruction:
return
- self.__already_enqueued_for_destruction = True
- self.clear_all_weakrefs()
- if call_user_del:
- space.user_del_action.register_dying_object(self)
-
- def _call_builtin_destructor(self):
- pass # method overridden in typedef.py
+ self.__already_enqueued_for_destruction += (callback,)
+ space.user_del_action.register_callback(self, callback, descrname)
# hooks that the mapdict implementations needs:
def _get_mapdict_map(self):
@@ -925,6 +927,9 @@
return self.w_True
return self.w_False
+ def issequence_w(self, w_obj):
+ return (self.findattr(w_obj, self.wrap("__getitem__")) is not None)
+
def isinstance_w(self, w_obj, w_type):
return self.is_true(self.isinstance(w_obj, w_type))
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/gateway.py b/pypy/interpreter/gateway.py
--- a/pypy/interpreter/gateway.py
+++ b/pypy/interpreter/gateway.py
@@ -396,11 +396,14 @@
fastfunc = func
else:
# try to avoid excessive bloat
- if func.__module__ == 'pypy.interpreter.astcompiler.ast':
+ mod = func.__module__
+ if mod is None:
+ mod = ""
+ if mod == 'pypy.interpreter.astcompiler.ast':
raise FastFuncNotSupported
- if (not func.__module__.startswith('pypy.module.__builtin__') and
- not func.__module__.startswith('pypy.module.sys') and
- not func.__module__.startswith('pypy.module.math')):
+ if (not mod.startswith('pypy.module.__builtin__') and
+ not mod.startswith('pypy.module.sys') and
+ not mod.startswith('pypy.module.math')):
if not func.__name__.startswith('descr'):
raise FastFuncNotSupported
d = {}
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -114,6 +114,7 @@
def descr_close(self):
"""x.close(arg) -> raise GeneratorExit inside generator."""
+ assert isinstance(self, GeneratorIterator)
space = self.space
try:
w_retval = self.throw(space.w_GeneratorExit, space.w_None,
@@ -141,22 +142,16 @@
code_name = self.pycode.co_name
return space.wrap(code_name)
- def descr__del__(self):
- """
- applevel __del__, which is called at a safe point after the
- interp-level __del__ enqueued the object for destruction
- """
- self.descr_close()
-
def __del__(self):
# Only bother enqueuing self to raise an exception if the frame is
# still not finished and finally or except blocks are present.
- must_call_close = False
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.clear_all_weakrefs()
+ self.enqueue_for_destruction(self.space,
+ GeneratorIterator.descr_close,
+ "interrupting generator of ")
break
block = block.previous
- self._enqueue_for_destruction(self.space, must_call_close)
diff --git a/pypy/interpreter/module.py b/pypy/interpreter/module.py
--- a/pypy/interpreter/module.py
+++ b/pypy/interpreter/module.py
@@ -9,6 +9,8 @@
class Module(Wrappable):
"""A module."""
+ _immutable_fields_ = ["w_dict?"]
+
_frozen = False
def __init__(self, space, w_name, w_dict=None, add_package=True):
diff --git a/pypy/interpreter/pycompiler.py b/pypy/interpreter/pycompiler.py
--- a/pypy/interpreter/pycompiler.py
+++ b/pypy/interpreter/pycompiler.py
@@ -119,7 +119,10 @@
raise OperationError(self.space.w_TypeError, self.space.wrap(
"invalid node type"))
- future_pos = misc.parse_future(node)
+ fut = misc.parse_future(node, self.future_flags.compiler_features)
+ f_flags, f_lineno, f_col = fut
+ future_pos = f_lineno, f_col
+ flags |= f_flags
info = pyparse.CompileInfo(filename, mode, flags, future_pos)
return self._compile_ast(node, info)
diff --git a/pypy/interpreter/test/test_typedef.py b/pypy/interpreter/test/test_typedef.py
--- a/pypy/interpreter/test/test_typedef.py
+++ b/pypy/interpreter/test/test_typedef.py
@@ -1,3 +1,4 @@
+import gc
from pypy.interpreter import typedef
from pypy.tool.udir import udir
from pypy.interpreter.baseobjspace import Wrappable
@@ -180,6 +181,85 @@
assert err.value.message == "'some_type' objects are unhashable"
""")
+ def test_destructor(self):
+ space = self.space
+ class W_Level1(Wrappable):
+ def __init__(self, space1):
+ assert space1 is space
+ def __del__(self):
+ space.call_method(w_seen, 'append', space.wrap(1))
+ class W_Level2(Wrappable):
+ def __init__(self, space1):
+ assert space1 is space
+ def __del__(self):
+ self.enqueue_for_destruction(space, W_Level2.destructormeth,
+ 'FOO ')
+ def destructormeth(self):
+ space.call_method(w_seen, 'append', space.wrap(2))
+ W_Level1.typedef = typedef.TypeDef(
+ 'level1',
+ __new__ = typedef.generic_new_descr(W_Level1))
+ W_Level2.typedef = typedef.TypeDef(
+ 'level2',
+ __new__ = typedef.generic_new_descr(W_Level2))
+ #
+ w_seen = space.newlist([])
+ W_Level1(space)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [1]
+ #
+ w_seen = space.newlist([])
+ W_Level2(space)
+ gc.collect(); gc.collect()
+ assert space.str_w(space.repr(w_seen)) == "[]" # not called yet
+ ec = space.getexecutioncontext()
+ self.space.user_del_action.perform(ec, None)
+ assert space.unwrap(w_seen) == [2]
+ #
+ w_seen = space.newlist([])
+ self.space.appexec([self.space.gettypeobject(W_Level1.typedef)],
+ """(level1):
+ class A3(level1):
+ pass
+ A3()
+ """)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [1]
+ #
+ w_seen = space.newlist([])
+ self.space.appexec([self.space.gettypeobject(W_Level1.typedef),
+ w_seen],
+ """(level1, seen):
+ class A4(level1):
+ def __del__(self):
+ seen.append(4)
+ A4()
+ """)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [4, 1]
+ #
+ w_seen = space.newlist([])
+ self.space.appexec([self.space.gettypeobject(W_Level2.typedef)],
+ """(level2):
+ class A5(level2):
+ pass
+ A5()
+ """)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [2]
+ #
+ w_seen = space.newlist([])
+ self.space.appexec([self.space.gettypeobject(W_Level2.typedef),
+ w_seen],
+ """(level2, seen):
+ class A6(level2):
+ def __del__(self):
+ seen.append(6)
+ A6()
+ """)
+ gc.collect(); gc.collect()
+ assert space.unwrap(w_seen) == [6, 2]
+
class AppTestTypeDef:
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -228,21 +228,26 @@
return self._lifeline_
def setweakref(self, space, weakreflifeline):
self._lifeline_ = weakreflifeline
+ def delweakref(self):
+ self._lifeline_ = None
add(Proto)
if "del" in features:
+ parent_destructor = getattr(supercls, '__del__', None)
+ def call_parent_del(self):
+ assert isinstance(self, subcls)
+ parent_destructor(self)
+ def call_applevel_del(self):
+ assert isinstance(self, subcls)
+ self.space.userdel(self)
class Proto(object):
def __del__(self):
- self._enqueue_for_destruction(self.space)
- # if the base class needs its own interp-level __del__,
- # we override the _call_builtin_destructor() method to invoke it
- # after the app-level destructor.
- parent_destructor = getattr(supercls, '__del__', None)
+ self.clear_all_weakrefs()
+ self.enqueue_for_destruction(self.space, call_applevel_del,
+ 'method __del__ of ')
if parent_destructor is not None:
- def _call_builtin_destructor(self):
- parent_destructor(self)
- Proto._call_builtin_destructor = _call_builtin_destructor
-
+ self.enqueue_for_destruction(self.space, call_parent_del,
+ 'internal destructor of ')
add(Proto)
if "slots" in features:
@@ -630,9 +635,12 @@
return self._lifeline_
def setweakref(self, space, weakreflifeline):
self._lifeline_ = weakreflifeline
+ def delweakref(self):
+ self._lifeline_ = None
cls._lifeline_ = None
cls.getweakref = getweakref
cls.setweakref = setweakref
+ cls.delweakref = delweakref
return weakref_descr
@@ -858,8 +866,6 @@
descrmismatch='close'),
__iter__ = interp2app(GeneratorIterator.descr__iter__,
descrmismatch='__iter__'),
- __del__ = interp2app(GeneratorIterator.descr__del__,
- descrmismatch='__del__'),
gi_running = interp_attrproperty('running', cls=GeneratorIterator),
gi_frame = GetSetProperty(GeneratorIterator.descr_gi_frame),
gi_code = GetSetProperty(GeneratorIterator.descr_gi_code),
diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py
--- a/pypy/jit/backend/x86/assembler.py
+++ b/pypy/jit/backend/x86/assembler.py
@@ -416,10 +416,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 +481,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)
diff --git a/pypy/jit/backend/x86/rx86.py b/pypy/jit/backend/x86/rx86.py
--- a/pypy/jit/backend/x86/rx86.py
+++ b/pypy/jit/backend/x86/rx86.py
@@ -464,7 +464,7 @@
# ------------------------------ MOV ------------------------------
- MOV_ri = insn(rex_w, register(1), '\xB8', immediate(2, 'q'))
+ MOV_ri = insn(register(1), '\xB8', immediate(2))
MOV8_ri = insn(rex_fw, byte_register(1), '\xB0', immediate(2, 'b'))
# ------------------------------ Arithmetic ------------------------------
@@ -632,16 +632,20 @@
CQO = insn(rex_w, '\x99')
- # MOV_ri from the parent class is not wrong, but here is a better encoding
- # for the common case where the immediate fits in 32 bits
+ # Three different encodings... following what gcc does. From the
+ # shortest encoding to the longest one.
+ MOV_riu32 = insn(rex_nw, register(1), '\xB8', immediate(2, 'i'))
MOV_ri32 = insn(rex_w, '\xC7', register(1), '\xC0', immediate(2, 'i'))
- MOV_ri64 = AbstractX86CodeBuilder.MOV_ri
+ MOV_ri64 = insn(rex_w, register(1), '\xB8', immediate(2, 'q'))
def MOV_ri(self, reg, immed):
- if fits_in_32bits(immed):
+ if 0 <= immed <= 4294967295:
+ immed = intmask(rffi.cast(rffi.INT, immed))
+ self.MOV_riu32(reg, immed)
+ elif fits_in_32bits(immed): # for negative values that fit in 32 bit
self.MOV_ri32(reg, immed)
else:
- AbstractX86CodeBuilder.MOV_ri(self, reg, immed)
+ self.MOV_ri64(reg, immed)
def define_modrm_modes(insnname_template, before_modrm, after_modrm=[], regtype='GPR'):
def add_insn(code, *modrm):
diff --git a/pypy/jit/backend/x86/test/test_regloc.py b/pypy/jit/backend/x86/test/test_regloc.py
--- a/pypy/jit/backend/x86/test/test_regloc.py
+++ b/pypy/jit/backend/x86/test/test_regloc.py
@@ -24,9 +24,14 @@
assert_encodes_as(cb64, "MOV16", (r8, ebx), '\x66\x41\x89\xD8') # 11 011 000
assert_encodes_as(cb64, "MOV16", (ebx, r8), '\x66\x44\x89\xC3') # 11 000 011
assert_encodes_as(cb64, "MOV16", (ecx, ebx), '\x66\x40\x89\xD9')
- # XXX: What we are testing for here is actually not the most compact
- # encoding.
- assert_encodes_as(cb64, "MOV16", (ecx, ImmedLoc(12345)), '\x66\x40\xC7\xC1\x39\x30')
+ assert_encodes_as(cb64, "MOV16", (ecx, ImmedLoc(12345)), '\x66\xB9\x39\x30')
+ # for the next case we don't pick the most efficient encoding, but well
+ expected = '\x66\x40\xC7\xC1\xC7\xCF' # could be '\x66\xB9\xC7\xCF'
+ assert_encodes_as(cb64, "MOV16", (ecx, ImmedLoc(-12345)), expected)
+ assert_encodes_as(cb64, "MOV16", (r9, ImmedLoc(12345)), '\x66\x41\xB9\x39\x30')
+ # for the next case we don't pick the most efficient encoding, but well
+ expected = '\x66\x41\xC7\xC1\xC7\xCF' # could be '\x66\x41\xB9\xC7\xCF'
+ assert_encodes_as(cb64, "MOV16", (r9, ImmedLoc(-12345)), expected)
assert_encodes_as(cb64, "MOV16", (AddressLoc(r13, ImmedLoc(0), 0, 0), ImmedLoc(12345)), '\x66\x41\xC7\x45\x00\x39\x30')
def test_cmp_16():
@@ -44,7 +49,7 @@
def test_relocation():
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.jit.backend.x86 import codebuf
- for target in [0x01020304, 0x0102030405060708]:
+ for target in [0x01020304, -0x05060708, 0x0102030405060708]:
if target > sys.maxint:
continue
mc = codebuf.MachineCodeBlockWrapper()
@@ -58,10 +63,15 @@
expected = "\xE8" + struct.pack('<i', target - (rawstart + 5))
elif IS_X86_64:
assert mc.relocations == []
- if target <= 0x7fffffff:
+ if 0 <= target <= 0xffffffff:
+ assert length == 9
+ expected = (
+ "\x41\xBB\x04\x03\x02\x01" # MOV %r11, target
+ "\x41\xFF\xD3") # CALL *%r11
+ elif -0x80000000 <= target < 0:
assert length == 10
expected = (
- "\x49\xC7\xC3\x04\x03\x02\x01" # MOV %r11, target
+ "\x49\xC7\xC3\xF8\xF8\xF9\xFA" # MOV %r11, target
"\x41\xFF\xD3") # CALL *%r11
else:
assert length == 13
diff --git a/pypy/jit/backend/x86/test/test_rx86.py b/pypy/jit/backend/x86/test/test_rx86.py
--- a/pypy/jit/backend/x86/test/test_rx86.py
+++ b/pypy/jit/backend/x86/test/test_rx86.py
@@ -198,9 +198,19 @@
def test_mov_ri_64():
s = CodeBuilder64()
s.MOV_ri(ecx, -2)
+ s.MOV_ri(r15, -3)
+ s.MOV_ri(ebx, -0x80000003)
+ s.MOV_ri(r13, -0x80000002)
+ s.MOV_ri(ecx, 42)
s.MOV_ri(r12, 0x80000042)
+ s.MOV_ri(r12, 0x100000007)
assert s.getvalue() == ('\x48\xC7\xC1\xFE\xFF\xFF\xFF' +
- '\x49\xBC\x42\x00\x00\x80\x00\x00\x00\x00')
+ '\x49\xC7\xC7\xFD\xFF\xFF\xFF' +
+ '\x48\xBB\xFD\xFF\xFF\x7F\xFF\xFF\xFF\xFF' +
+ '\x49\xBD\xFE\xFF\xFF\x7F\xFF\xFF\xFF\xFF' +
+ '\xB9\x2A\x00\x00\x00' +
+ '\x41\xBC\x42\x00\x00\x80' +
+ '\x49\xBC\x07\x00\x00\x00\x01\x00\x00\x00')
def test_mov_rm_64():
s = CodeBuilder64()
diff --git a/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py b/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py
--- a/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py
+++ b/pypy/jit/backend/x86/test/test_rx86_32_auto_encoding.py
@@ -212,6 +212,17 @@
for mode, v in zip(argmodes, args):
ops.append(assembler_operand[mode](v))
ops.reverse()
+ #
+ if (instrname.lower() == 'mov' and suffix == 'q' and
+ ops[0].startswith('$') and 0 <= int(ops[0][1:]) <= 4294967295
+ and ops[1].startswith('%r')):
+ # movq $xxx, %rax => movl $xxx, %eax
+ suffix = 'l'
+ if ops[1][2:].isdigit():
+ ops[1] += 'd'
+ else:
+ ops[1] = '%e' + ops[1][2:]
+ #
op = '\t%s%s %s%s' % (instrname.lower(), suffix,
', '.join(ops), following)
g.write('%s\n' % op)
diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -765,13 +765,65 @@
raise NotImplementedError("cast_ptr_to_int")
def rewrite_op_force_cast(self, op):
- from pypy.rpython.lltypesystem.rffi import size_and_sign, sizeof
+ assert not self._is_gc(op.args[0])
+ fromll = longlong.is_longlong(op.args[0].concretetype)
+ toll = longlong.is_longlong(op.result.concretetype)
+ if fromll and toll:
+ return
+ if fromll:
+ args = op.args
+ opname = 'truncate_longlong_to_int'
+ RESULT = lltype.Signed
+ v = varoftype(RESULT)
+ op1 = SpaceOperation(opname, args, v)
+ op2 = self.rewrite_operation(op1)
+ oplist = self.force_cast_without_longlong(op2.result, op.result)
+ if oplist:
+ return [op2] + oplist
+ #
+ # force a renaming to put the correct result in place, even though
+ # it might be slightly mistyped (e.g. Signed versus Unsigned)
+ assert op2.result is v
+ op2.result = op.result
+ return op2
+ elif toll:
+ from pypy.rpython.lltypesystem import rffi
+ size, unsigned = rffi.size_and_sign(op.args[0].concretetype)
+ if unsigned:
+ INTERMEDIATE = lltype.Unsigned
+ else:
+ INTERMEDIATE = lltype.Signed
+ v = varoftype(INTERMEDIATE)
+ oplist = self.force_cast_without_longlong(op.args[0], v)
+ if not oplist:
+ v = op.args[0]
+ oplist = []
+ if unsigned:
+ opname = 'cast_uint_to_longlong'
+ else:
+ opname = 'cast_int_to_longlong'
+ op1 = SpaceOperation(opname, [v], op.result)
+ op2 = self.rewrite_operation(op1)
+ return oplist + [op2]
+ else:
+ return self.force_cast_without_longlong(op.args[0], op.result)
+
+ def force_cast_without_longlong(self, v_arg, v_result):
+ from pypy.rpython.lltypesystem.rffi import size_and_sign, sizeof, FLOAT
from pypy.rlib.rarithmetic import intmask
- assert not self._is_gc(op.args[0])
- size2, unsigned2 = size_and_sign(op.result.concretetype)
- if size2 >= sizeof(lltype.Signed):
+ #
+ if (v_result.concretetype in (FLOAT, lltype.Float) or
+ v_arg.concretetype in (FLOAT, lltype.Float)):
+ assert (v_result.concretetype == lltype.Float and
+ v_arg.concretetype == lltype.Float), "xxx unsupported cast"
+ return
+ #
+ size2, unsigned2 = size_and_sign(v_result.concretetype)
+ assert size2 <= sizeof(lltype.Signed)
+ if size2 == sizeof(lltype.Signed):
return # the target type is LONG or ULONG
- size1, unsigned1 = size_and_sign(op.args[0].concretetype)
+ size1, unsigned1 = size_and_sign(v_arg.concretetype)
+ assert size1 <= sizeof(lltype.Signed)
#
def bounds(size, unsigned):
if unsigned:
@@ -784,22 +836,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
@@ -883,30 +941,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.
@@ -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
@@ -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/regalloc.py b/pypy/jit/codewriter/regalloc.py
--- a/pypy/jit/codewriter/regalloc.py
+++ b/pypy/jit/codewriter/regalloc.py
@@ -96,6 +96,7 @@
def _try_coalesce(self, v, w):
if isinstance(v, Variable) and getkind(v.concretetype) == self.kind:
+ assert getkind(w.concretetype) == self.kind
dg = self._depgraph
uf = self._unionfind
v0 = uf.find_rep(v)
diff --git a/pypy/jit/codewriter/test/test_flatten.py b/pypy/jit/codewriter/test/test_flatten.py
--- a/pypy/jit/codewriter/test/test_flatten.py
+++ b/pypy/jit/codewriter/test/test_flatten.py
@@ -3,6 +3,7 @@
from pypy.jit.codewriter.flatten import flatten_graph, reorder_renaming_list
from pypy.jit.codewriter.flatten import GraphFlattener, ListOfKind, Register
from pypy.jit.codewriter.format import assert_format
+from pypy.jit.codewriter import longlong
from pypy.jit.metainterp.history import AbstractDescr
from pypy.rpython.lltypesystem import lltype, rclass, rstr
from pypy.objspace.flow.model import SpaceOperation, Variable, Constant
@@ -30,6 +31,9 @@
'float': FakeRegAlloc()}
class FakeDescr(AbstractDescr):
+ _for_tests_only = True
+ def __init__(self, oopspecindex=None):
+ self.oopspecindex = oopspecindex
def __repr__(self):
return '<Descr>'
def as_vtable_size_descr(self):
@@ -55,19 +59,24 @@
def arraydescrof(self, ARRAY):
return FakeDescr()
+class FakeCallInfoCollection:
+ def add(self, *args):
+ pass
+
class FakeCallControl:
_descr_cannot_raise = FakeDescr()
+ callinfocollection = FakeCallInfoCollection()
def guess_call_kind(self, op):
return 'residual'
- def getcalldescr(self, op):
+ def getcalldescr(self, op, oopspecindex=None, extraeffect=None):
try:
if 'cannot_raise' in op.args[0].value._obj.graph.name:
return self._descr_cannot_raise
except AttributeError:
pass
- return FakeDescr()
+ return FakeDescr(oopspecindex)
def calldescr_canraise(self, calldescr):
- return calldescr is not self._descr_cannot_raise
+ return calldescr is not self._descr_cannot_raise and calldescr.oopspecindex is None
def get_vinfo(self, VTYPEPTR):
return None
@@ -734,7 +743,9 @@
def test_force_cast(self):
from pypy.rpython.lltypesystem import rffi
-
+ # NB: we don't need to test for INT here, the logic in jtransform is
+ # general enough so that if we have the below cases it should
+ # generalize also to INT
for FROM, TO, expected in [
(rffi.SIGNEDCHAR, rffi.SIGNEDCHAR, ""),
(rffi.SIGNEDCHAR, rffi.UCHAR, "int_and %i0, $255 -> %i1"),
@@ -797,12 +808,42 @@
expected = [s.strip() for s in expected.splitlines()]
check_force_cast(FROM, TO, expected, 42)
check_force_cast(FROM, TO, expected, -42)
- expected.append('int_return %i' + str(len(expected)))
- expected = '\n'.join(expected)
+ returnvar = "%i" + str(len(expected))
+ expected.append('int_return ' + returnvar)
+ expectedstr = '\n'.join(expected)
#
def f(n):
return rffi.cast(TO, n)
- self.encoding_test(f, [rffi.cast(FROM, 42)], expected,
+ self.encoding_test(f, [rffi.cast(FROM, 42)], expectedstr,
+ transform=True)
+
+ if not longlong.is_64_bit:
+ if FROM in (rffi.LONG, rffi.ULONG):
+ if FROM == rffi.LONG:
+ FROM = rffi.LONGLONG
+ else:
+ FROM = rffi.ULONGLONG
+ expected.insert(0,
+ "residual_call_irf_i $<* fn llong_to_int>, <Descr>, I[], R[], F[%f0] -> %i0")
+ expectedstr = '\n'.join(expected)
+ self.encoding_test(f, [rffi.cast(FROM, 42)], expectedstr,
+ transform=True)
+ elif TO in (rffi.LONG, rffi.ULONG):
+ if TO == rffi.LONG:
+ TO = rffi.LONGLONG
+ else:
+ TO = rffi.ULONGLONG
+ if rffi.cast(FROM, -1) < 0:
+ fnname = "llong_from_int"
+ else:
+ fnname = "llong_from_uint"
+ expected.pop() # remove int_return
+ expected.append(
+ "residual_call_irf_f $<* fn %s>, <Descr>, I[%s], R[], F[] -> %%f0"
+ % (fnname, returnvar))
+ expected.append("float_return %f0")
+ expectedstr = '\n'.join(expected)
+ self.encoding_test(f, [rffi.cast(FROM, 42)], expectedstr,
transform=True)
def test_force_cast_pointer(self):
@@ -813,6 +854,23 @@
int_return %i0
""", transform=True)
+ def test_force_cast_float(self):
+ from pypy.rpython.lltypesystem import rffi
+ def f(n):
+ return rffi.cast(lltype.Float, n)
+ self.encoding_test(f, [12.456], """
+ float_return %f0
+ """, transform=True)
+
+ def test_direct_ptradd(self):
+ from pypy.rpython.lltypesystem import rffi
+ def f(p, n):
+ return lltype.direct_ptradd(p, n)
+ self.encoding_test(f, [lltype.nullptr(rffi.CCHARP.TO), 123], """
+ int_add %i0, %i1 -> %i2
+ int_return %i2
+ """, transform=True)
+
def check_force_cast(FROM, TO, operations, value):
"""Check that the test is correctly written..."""
diff --git a/pypy/jit/codewriter/test/test_longlong.py b/pypy/jit/codewriter/test/test_longlong.py
--- a/pypy/jit/codewriter/test/test_longlong.py
+++ b/pypy/jit/codewriter/test/test_longlong.py
@@ -37,7 +37,7 @@
class TestLongLong:
def setup_class(cls):
- if sys.maxint > 2147483647:
+ if longlong.is_64_bit:
py.test.skip("only for 32-bit platforms")
def do_check(self, opname, oopspecindex, ARGS, RESULT):
@@ -46,6 +46,8 @@
op = SpaceOperation(opname, vlist, v_result)
tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
op1 = tr.rewrite_operation(op)
+ if isinstance(op1, list):
+ [op1] = op1
#
def is_llf(TYPE):
return (TYPE == lltype.SignedLongLong or
@@ -196,6 +198,23 @@
for T2 in [lltype.Signed, lltype.Unsigned]:
self.do_check('cast_primitive', EffectInfo.OS_LLONG_TO_INT,
[T1], T2)
+ self.do_check('force_cast', EffectInfo.OS_LLONG_TO_INT,
+ [T1], T2)
+ if T2 == lltype.Signed:
+ expected = EffectInfo.OS_LLONG_FROM_INT
+ else:
+ expected = EffectInfo.OS_LLONG_FROM_UINT
+ self.do_check('cast_primitive', expected, [T2], T1)
+ self.do_check('force_cast', expected, [T2], T1)
+ #
+ for T1 in [lltype.SignedLongLong, lltype.UnsignedLongLong]:
+ for T2 in [lltype.SignedLongLong, lltype.UnsignedLongLong]:
+ vlist = [varoftype(T1)]
+ v_result = varoftype(T2)
+ op = SpaceOperation('force_cast', vlist, v_result)
+ tr = Transformer(FakeCPU(), FakeBuiltinCallControl())
+ op1 = tr.rewrite_operation(op)
+ assert op1 is None
def test_constants(self):
for TYPE in [lltype.SignedLongLong, lltype.UnsignedLongLong]:
diff --git a/pypy/jit/metainterp/optimizeopt/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
@@ -3916,11 +3916,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p3 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p3, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p3, 0, i4, i5)
+ copystrcontent(p1, p3, 0, 0, i1)
+ copystrcontent(p2, p3, 0, i1, i2)
jump(p2, p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -3941,9 +3938,7 @@
p3 = newstr(i3)
strsetitem(p3, 0, i0)
strsetitem(p3, 1, i1)
- i4 = strlen(p2)
- i5 = int_add(2, i4) # will be killed by the backend
- copystrcontent(p2, p3, 0, 2, i4)
+ copystrcontent(p2, p3, 0, 2, i2)
jump(i1, i0, p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -3962,10 +3957,9 @@
i2 = strlen(p2)
i3 = int_add(i2, 2)
p3 = newstr(i3)
- i4 = strlen(p2)
- copystrcontent(p2, p3, 0, 0, i4)
- strsetitem(p3, i4, i0)
- i5 = int_add(i4, 1)
+ copystrcontent(p2, p3, 0, 0, i2)
+ strsetitem(p3, i2, i0)
+ i5 = int_add(i2, 1)
strsetitem(p3, i5, i1)
i6 = int_add(i5, 1) # will be killed by the backend
jump(i1, i0, p3)
@@ -3987,14 +3981,9 @@
i3 = strlen(p3)
i123 = int_add(i12, i3)
p5 = newstr(i123)
- i1b = strlen(p1)
- copystrcontent(p1, p5, 0, 0, i1b)
- i2b = strlen(p2)
- i12b = int_add(i1b, i2b)
- copystrcontent(p2, p5, 0, i1b, i2b)
- i3b = strlen(p3)
- i123b = int_add(i12b, i3b) # will be killed by the backend
- copystrcontent(p3, p5, 0, i12b, i3b)
+ copystrcontent(p1, p5, 0, 0, i1)
+ copystrcontent(p2, p5, 0, i1, i2)
+ copystrcontent(p3, p5, 0, i12, i3)
jump(p2, p3, p5)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -4010,10 +3999,8 @@
i2 = strlen(p2)
i3 = int_add(i2, 1)
p3 = newstr(i3)
- i4 = strlen(p2)
- copystrcontent(p2, p3, 0, 0, i4)
- strsetitem(p3, i4, 120) # == ord('x')
- i5 = int_add(i4, 1) # will be killed by the backend
+ copystrcontent(p2, p3, 0, 0, i2)
+ strsetitem(p3, i2, 120) # == ord('x')
jump(p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -4131,9 +4118,7 @@
i5 = int_add(i3, i4)
p4 = newstr(i5)
copystrcontent(p1, p4, i1, 0, i3)
- i4b = strlen(p2)
- i6 = int_add(i3, i4b) # killed by the backend
- copystrcontent(p2, p4, 0, i3, i4b)
+ copystrcontent(p2, p4, 0, i3, i4)
jump(p4, i1, i2, p2)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -4178,11 +4163,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p4 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p4, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p4, 0, i4, i5)
+ copystrcontent(p1, p4, 0, 0, i1)
+ copystrcontent(p2, p4, 0, i1, i2)
i0 = call(0, p3, p4, descr=strequaldescr)
escape(i0)
jump(p1, p2, p3)
@@ -4374,11 +4356,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p4 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p4, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p4, 0, i4, i5)
+ copystrcontent(p1, p4, 0, 0, i1)
+ copystrcontent(p2, p4, 0, i1, i2)
i0 = call(0, s"hello world", p4, descr=streq_nonnull_descr)
escape(i0)
jump(p1, p2)
@@ -4532,6 +4511,25 @@
"""
self.optimize_loop(ops, expected)
+ def test_strslice_with_other_stuff(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)
+
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
@@ -5082,11 +5082,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p3 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p3, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p3, 0, i4, i5)
+ copystrcontent(p1, p3, 0, 0, i1)
+ copystrcontent(p2, p3, 0, i1, i2)
jump(p2, p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5107,9 +5104,7 @@
p3 = newstr(i3)
strsetitem(p3, 0, i0)
strsetitem(p3, 1, i1)
- i4 = strlen(p2)
- i5 = int_add(2, i4) # will be killed by the backend
- copystrcontent(p2, p3, 0, 2, i4)
+ copystrcontent(p2, p3, 0, 2, i2)
jump(i1, i0, p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5128,10 +5123,9 @@
i2 = strlen(p2)
i3 = int_add(i2, 2)
p3 = newstr(i3)
- i4 = strlen(p2)
- copystrcontent(p2, p3, 0, 0, i4)
- strsetitem(p3, i4, i0)
- i5 = int_add(i4, 1)
+ copystrcontent(p2, p3, 0, 0, i2)
+ strsetitem(p3, i2, i0)
+ i5 = int_add(i2, 1)
strsetitem(p3, i5, i1)
i6 = int_add(i5, 1) # will be killed by the backend
jump(i1, i0, p3)
@@ -5153,14 +5147,9 @@
i3 = strlen(p3)
i123 = int_add(i12, i3)
p5 = newstr(i123)
- i1b = strlen(p1)
- copystrcontent(p1, p5, 0, 0, i1b)
- i2b = strlen(p2)
- i12b = int_add(i1b, i2b)
- copystrcontent(p2, p5, 0, i1b, i2b)
- i3b = strlen(p3)
- i123b = int_add(i12b, i3b) # will be killed by the backend
- copystrcontent(p3, p5, 0, i12b, i3b)
+ copystrcontent(p1, p5, 0, 0, i1)
+ copystrcontent(p2, p5, 0, i1, i2)
+ copystrcontent(p3, p5, 0, i12, i3)
jump(p2, p3, p5)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5176,10 +5165,8 @@
i2 = strlen(p2)
i3 = int_add(i2, 1)
p3 = newstr(i3)
- i4 = strlen(p2)
- copystrcontent(p2, p3, 0, 0, i4)
- strsetitem(p3, i4, 120) # == ord('x')
- i5 = int_add(i4, 1) # will be killed by the backend
+ copystrcontent(p2, p3, 0, 0, i2)
+ strsetitem(p3, i2, 120) # == ord('x')
jump(p3)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5303,9 +5290,7 @@
i5 = int_add(i3, i4)
p4 = newstr(i5)
copystrcontent(p1, p4, i1, 0, i3)
- i4b = strlen(p2)
- i6 = int_add(i3, i4b) # killed by the backend
- copystrcontent(p2, p4, 0, i3, i4b)
+ copystrcontent(p2, p4, 0, i3, i4)
jump(p4, i1, i2, p2)
"""
self.optimize_strunicode_loop(ops, expected)
@@ -5411,11 +5396,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p4 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p4, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p4, 0, i4, i5)
+ copystrcontent(p1, p4, 0, 0, i1)
+ copystrcontent(p2, p4, 0, i1, i2)
i0 = call(0, p3, p4, descr=strequaldescr)
escape(i0)
jump(p1, p2, p3)
@@ -5609,11 +5591,8 @@
i2 = strlen(p2)
i3 = int_add(i1, i2)
p4 = newstr(i3)
- i4 = strlen(p1)
- copystrcontent(p1, p4, 0, 0, i4)
- i5 = strlen(p2)
- i6 = int_add(i4, i5) # will be killed by the backend
- copystrcontent(p2, p4, 0, i4, i5)
+ copystrcontent(p1, p4, 0, 0, i1)
+ copystrcontent(p2, p4, 0, i1, i2)
i0 = call(0, s"hello world", p4, descr=streq_nonnull_descr)
escape(i0)
jump(p1, p2)
diff --git a/pypy/jit/metainterp/optimizeopt/util.py b/pypy/jit/metainterp/optimizeopt/util.py
--- a/pypy/jit/metainterp/optimizeopt/util.py
+++ b/pypy/jit/metainterp/optimizeopt/util.py
@@ -21,7 +21,7 @@
continue
if hasattr(Class, name_prefix + name):
opclass = resoperation.opclasses[getattr(rop, name)]
- print value, name, opclass
+ assert name in opclass.__name__
result.append((value, opclass, getattr(Class, name_prefix + name)))
return unrolling_iterable(result)
diff --git a/pypy/jit/metainterp/optimizeopt/vstring.py b/pypy/jit/metainterp/optimizeopt/vstring.py
--- a/pypy/jit/metainterp/optimizeopt/vstring.py
+++ b/pypy/jit/metainterp/optimizeopt/vstring.py
@@ -61,7 +61,7 @@
self.ensure_nonnull()
box = self.force_box()
lengthbox = BoxInt()
- optimization.emit_operation(ResOperation(mode.STRLEN, [box], lengthbox))
+ optimization.optimize_default(ResOperation(mode.STRLEN, [box], lengthbox))
return lengthbox
@specialize.arg(1)
@@ -72,13 +72,13 @@
else:
return None
- def string_copy_parts(self, optimization, targetbox, offsetbox, mode):
+ def string_copy_parts(self, optimizer, targetbox, offsetbox, mode):
# Copies the pointer-to-string 'self' into the target string
# given by 'targetbox', at the specified offset. Returns the offset
# at the end of the copy.
- lengthbox = self.getstrlen(optimization, mode)
+ lengthbox = self.getstrlen(optimizer, mode)
srcbox = self.force_box()
- return copy_str_content(optimization, srcbox, targetbox,
+ return copy_str_content(optimizer, srcbox, targetbox,
CONST_0, offsetbox, lengthbox, mode)
@@ -335,7 +335,7 @@
if optimizer is None:
return None
resbox = BoxInt()
- optimizer.emit_operation(ResOperation(rop.INT_ADD, [box1, box2], resbox))
+ optimizer.optimize_default(ResOperation(rop.INT_ADD, [box1, box2], resbox))
return resbox
def _int_sub(optimizer, box1, box2):
@@ -345,10 +345,10 @@
if isinstance(box1, ConstInt):
return ConstInt(box1.value - box2.value)
resbox = BoxInt()
- optimizer.emit_operation(ResOperation(rop.INT_SUB, [box1, box2], resbox))
+ optimizer.optimize_default(ResOperation(rop.INT_SUB, [box1, box2], resbox))
return resbox
-def _strgetitem(optimization, strbox, indexbox, mode):
+def _strgetitem(optimizer, strbox, indexbox, mode):
if isinstance(strbox, ConstPtr) and isinstance(indexbox, ConstInt):
if mode is mode_string:
s = strbox.getref(lltype.Ptr(rstr.STR))
@@ -357,7 +357,7 @@
s = strbox.getref(lltype.Ptr(rstr.UNICODE))
return ConstInt(ord(s.chars[indexbox.getint()]))
resbox = BoxInt()
- optimization.emit_operation(ResOperation(mode.STRGETITEM, [strbox, indexbox],
+ optimizer.optimize_default(ResOperation(mode.STRGETITEM, [strbox, indexbox],
resbox))
return resbox
@@ -440,7 +440,7 @@
if vindex.is_constant():
return value.getitem(vindex.box.getint())
#
- resbox = _strgetitem(self, value.force_box(), vindex.force_box(), mode)
+ resbox = _strgetitem(self.optimizer, value.force_box(), vindex.force_box(), mode)
return self.getvalue(resbox)
def optimize_STRLEN(self, op):
@@ -450,7 +450,7 @@
def _optimize_STRLEN(self, op, mode):
value = self.getvalue(op.getarg(0))
- lengthbox = value.getstrlen(self, mode)
+ lengthbox = value.getstrlen(self.optimizer, mode)
self.make_equal_to(op.result, self.getvalue(lengthbox))
def optimize_CALL(self, op):
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -310,26 +310,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 +365,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):
@@ -845,7 +848,9 @@
@arguments("orgpc", "box")
def opimpl_guard_class(self, orgpc, box):
clsbox = self.cls_of_box(box)
+ 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")
@@ -1449,6 +1454,8 @@
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 = {}
def perform_call(self, jitcode, boxes, greenkey=None):
# causes the metainterp to enter the given subfunction
@@ -1789,6 +1796,8 @@
duplicates[box] = None
def reached_loop_header(self, greenboxes, redboxes, resumedescr):
+ self.known_class_boxes = {}
+
duplicates = {}
self.remove_consts_and_duplicates(redboxes, len(redboxes),
duplicates)
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
@@ -984,11 +984,14 @@
pass
class B(A):
pass
+ @dont_look_inside
+ def extern(n):
+ if n:
+ return A()
+ else:
+ return B()
def fn(n):
- if n:
- obj = A()
- else:
- obj = B()
+ obj = extern(n)
return isinstance(obj, B)
res = self.interp_operations(fn, [0])
assert res
@@ -1021,6 +1024,70 @@
res = self.meta_interp(main, [])
assert res == 55
+ def test_dont_record_repeated_guard_class(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()
+ 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_assert_isinstance(self):
class A:
pass
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/_ast/test/test_ast.py b/pypy/module/_ast/test/test_ast.py
--- a/pypy/module/_ast/test/test_ast.py
+++ b/pypy/module/_ast/test/test_ast.py
@@ -186,6 +186,11 @@
mod = self.get_ast("from __future__ import with_statement; import y; " \
"from __future__ import nested_scopes")
raises(SyntaxError, compile, mod, "<test>", "exec")
+ mod = self.get_ast("from __future__ import division\nx = 1/2")
+ co = compile(mod, "<test>", "exec")
+ ns = {}
+ exec co in ns
+ assert ns["x"] == .5
def test_field_attr_writable(self):
import _ast as ast
diff --git a/pypy/module/_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
@@ -149,6 +149,12 @@
raise OperationError(space.w_TypeError, space.wrap(msg))
return res
+def unwrap_truncate_int(TP, space, w_arg):
+ if space.is_true(space.isinstance(w_arg, space.w_int)):
+ return rffi.cast(TP, space.int_w(w_arg))
+ else:
+ return rffi.cast(TP, space.bigint_w(w_arg).ulonglongmask())
+unwrap_truncate_int._annspecialcase_ = 'specialize:arg(0)'
# ========================================================================
@@ -181,15 +187,14 @@
# note that we must check for longlong first, because either
# is_signed or is_unsigned returns true anyway
assert libffi.IS_32_BIT
- kind = libffi.types.getkind(w_argtype.ffitype) # XXX: remove the kind
- self.arg_longlong(space, argchain, kind, w_arg)
+ self.arg_longlong(space, argchain, w_arg)
elif w_argtype.is_signed():
- argchain.arg(space.int_w(w_arg))
+ argchain.arg(unwrap_truncate_int(rffi.LONG, space, w_arg))
elif w_argtype.is_pointer():
w_arg = self.convert_pointer_arg_maybe(space, w_arg, w_argtype)
argchain.arg(intmask(space.uint_w(w_arg)))
elif w_argtype.is_unsigned():
- argchain.arg(intmask(space.uint_w(w_arg)))
+ argchain.arg(unwrap_truncate_int(rffi.ULONG, space, w_arg))
elif w_argtype.is_char():
w_arg = space.ord(w_arg)
argchain.arg(space.int_w(w_arg))
@@ -220,15 +225,10 @@
return w_arg
@jit.dont_look_inside
- def arg_longlong(self, space, argchain, kind, w_arg):
+ def arg_longlong(self, space, argchain, w_arg):
bigarg = space.bigint_w(w_arg)
- if kind == 'I':
- llval = bigarg.tolonglong()
- elif kind == 'U':
- ullval = bigarg.toulonglong()
+ ullval = bigarg.ulonglongmask()
llval = rffi.cast(rffi.LONGLONG, ullval)
- else:
- assert False
# this is a hack: we store the 64 bits of the long long into the
# 64 bits of a float (i.e., a C double)
floatval = libffi.longlong2float(llval)
diff --git a/pypy/module/_ffi/test/test__ffi.py b/pypy/module/_ffi/test/test__ffi.py
--- a/pypy/module/_ffi/test/test__ffi.py
+++ b/pypy/module/_ffi/test/test__ffi.py
@@ -111,7 +111,6 @@
types.double)
assert pow(2, 3) == 8
-
def test_int_args(self):
"""
DLLEXPORT int sum_xy(int x, int y)
@@ -119,10 +118,12 @@
return x+y;
}
"""
+ import sys
from _ffi import CDLL, types
libfoo = CDLL(self.libfoo_name)
sum_xy = libfoo.getfunc('sum_xy', [types.sint, types.sint], types.sint)
assert sum_xy(30, 12) == 42
+ assert sum_xy(sys.maxint*2, 0) == -2
def test_void_result(self):
"""
@@ -247,6 +248,9 @@
types.ulong)
assert sum_xy(sys.maxint, 12) == sys.maxint+12
assert sum_xy(sys.maxint+1, 12) == sys.maxint+13
+ #
+ res = sum_xy(sys.maxint*2+3, 0)
+ assert res == 1
def test_unsigned_short_args(self):
"""
@@ -375,6 +379,9 @@
res = sum_xy(x, y)
expected = maxint64 + 3
assert res == expected
+ #
+ res = sum_xy(maxint64*2+3, 0)
+ assert res == 1
def test_byval_argument(self):
"""
diff --git a/pypy/module/_file/interp_file.py b/pypy/module/_file/interp_file.py
--- a/pypy/module/_file/interp_file.py
+++ b/pypy/module/_file/interp_file.py
@@ -43,11 +43,17 @@
# assume that the file and stream objects are only visible in the
# thread that runs __del__, so no race condition should be possible
self.clear_all_weakrefs()
+ if self.stream is not None:
+ self.enqueue_for_destruction(self.space, W_File.destructor,
+ 'close() method of ')
+
+ def destructor(self):
+ assert isinstance(self, W_File)
try:
self.direct_close()
except StreamErrors, e:
operr = wrap_streamerror(self.space, e, self.w_name)
- operr.write_unraisable(self.space, '__del__ of ', self)
+ raise operr
def fdopenstream(self, stream, fd, mode, w_name=None):
self.fd = fd
diff --git a/pypy/module/_io/interp_iobase.py b/pypy/module/_io/interp_iobase.py
--- a/pypy/module/_io/interp_iobase.py
+++ b/pypy/module/_io/interp_iobase.py
@@ -57,6 +57,11 @@
def __del__(self):
self.clear_all_weakrefs()
+ self.enqueue_for_destruction(self.space, W_IOBase.destructor,
+ 'internal __del__ of ')
+
+ def destructor(self):
+ assert isinstance(self, W_IOBase)
space = self.space
w_closed = space.findattr(self, space.wrap('closed'))
try:
diff --git a/pypy/module/_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
@@ -10,7 +10,7 @@
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 +23,10 @@
"""
for i in range(len(self.refs_weak) - 1, -1, -1):
w_ref = self.refs_weak[i]()
- if w_ref is not None:
- self.space.user_del_action.register_weakref_callback(w_ref)
+ if w_ref is not None and w_ref.w_callable is not None:
+ w_ref.enqueue_for_destruction(self.space,
+ W_WeakrefBase.activate_callback,
+ 'weakref callback of ')
def clear_all_weakrefs(self):
"""Clear all weakrefs. This is called when an app-level object has
@@ -118,11 +120,8 @@
self.w_obj_weak = dead_ref
def activate_callback(w_self):
- if not w_self.w_callable is None:
- try:
+ assert isinstance(w_self, W_WeakrefBase)
w_self.space.call_function(w_self.w_callable, w_self)
- except OperationError, e:
- e.write_unraisable(w_self.space, 'weakref callback ', w_self.w_callable)
def descr__repr__(self, space):
w_obj = self.dereference()
diff --git a/pypy/module/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
@@ -572,10 +572,7 @@
self.fromsequence(w_ustr)
def array_tounicode__Array(space, self):
- u = u""
- for i in range(self.len):
- u += self.buffer[i]
- return space.wrap(u)
+ return space.wrap(rffi.wcharpsize2unicode(self.buffer, self.len))
else:
def array_fromunicode__Array_Unicode(space, self, w_ustr):
diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py
--- a/pypy/module/cpyext/sequence.py
+++ b/pypy/module/cpyext/sequence.py
@@ -22,7 +22,7 @@
def PySequence_Check(space, w_obj):
"""Return 1 if the object provides sequence protocol, and 0 otherwise.
This function always succeeds."""
- return int(space.findattr(w_obj, space.wrap("__getitem__")) is not None)
+ return int(space.issequence_w(w_obj))
@cpython_api([PyObject], Py_ssize_t, error=-1)
def PySequence_Size(space, w_obj):
diff --git a/pypy/module/micronumpy/__init__.py b/pypy/module/micronumpy/__init__.py
--- a/pypy/module/micronumpy/__init__.py
+++ b/pypy/module/micronumpy/__init__.py
@@ -10,6 +10,7 @@
'zeros': 'interp_numarray.zeros',
'empty': 'interp_numarray.zeros',
'ones': 'interp_numarray.ones',
+ 'fromstring': 'interp_support.fromstring',
# ufuncs
'abs': 'interp_ufuncs.absolute',
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -1,5 +1,5 @@
from pypy.interpreter.baseobjspace import ObjSpace, W_Root, Wrappable
-from pypy.interpreter.error import operationerrfmt
+from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.rlib import jit
@@ -7,7 +7,6 @@
from pypy.tool.sourcetools import func_with_new_name
import math
-
def dummy1(v):
assert isinstance(v, float)
return v
@@ -20,6 +19,8 @@
numpy_driver = jit.JitDriver(greens = ['signature'],
reds = ['result_size', 'i', 'self', 'result'])
+all_driver = jit.JitDriver(greens=['signature'], reds=['i', 'size', 'self'])
+any_driver = jit.JitDriver(greens=['signature'], reds=['i', 'size', 'self'])
class Signature(object):
def __init__(self):
@@ -45,10 +46,14 @@
return v1 * v2
def div(v1, v2):
return v1 / v2
-def pow(v1, v2):
+def power(v1, v2):
return math.pow(v1, v2)
def mod(v1, v2):
return math.fmod(v1, v2)
+def maximum(v1, v2):
+ return max(v1, v2)
+def minimum(v1, v2):
+ return min(v1, v2)
class BaseArray(Wrappable):
def __init__(self):
@@ -82,8 +87,8 @@
def _binop_impl(function):
signature = Signature()
def impl(self, space, w_other):
+ w_other = convert_to_array(space, w_other)
new_sig = self.signature.transition(signature)
- if isinstance(w_other, BaseArray):
res = Call2(
function,
self,
@@ -91,14 +96,6 @@
new_sig.transition(w_other.signature)
)
w_other.invalidates.append(res)
- else:
- w_other = FloatWrapper(space.float_w(w_other))
- res = Call2(
- function,
- self,
- w_other,
- new_sig.transition(w_other.signature)
- )
self.invalidates.append(res)
return space.wrap(res)
return func_with_new_name(impl, "binop_%s_impl" % function.__name__)
@@ -107,9 +104,137 @@
descr_sub = _binop_impl(sub)
descr_mul = _binop_impl(mul)
descr_div = _binop_impl(div)
- descr_pow = _binop_impl(pow)
+ descr_pow = _binop_impl(power)
descr_mod = _binop_impl(mod)
+ def _binop_right_impl(function):
+ signature = Signature()
+ def impl(self, space, w_other):
+ new_sig = self.signature.transition(signature)
+ w_other = FloatWrapper(space.float_w(w_other))
+ res = Call2(
+ function,
+ w_other,
+ self,
+ new_sig.transition(w_other.signature)
+ )
+ self.invalidates.append(res)
+ return space.wrap(res)
+ return func_with_new_name(impl,
+ "binop_right_%s_impl" % function.__name__)
+
+ descr_radd = _binop_right_impl(add)
+ descr_rsub = _binop_right_impl(sub)
+ descr_rmul = _binop_right_impl(mul)
+ descr_rdiv = _binop_right_impl(div)
+ descr_rpow = _binop_right_impl(power)
+ descr_rmod = _binop_right_impl(mod)
+
+ def _reduce_sum_prod_impl(function, init):
+ reduce_driver = jit.JitDriver(greens=['signature'],
+ reds = ['i', 'size', 'self', 'result'])
+
+ def loop(self, result, size):
+ i = 0
+ while i < size:
+ reduce_driver.jit_merge_point(signature=self.signature,
+ self=self, size=size, i=i,
+ result=result)
+ result = function(result, self.eval(i))
+ i += 1
+ return result
+
+ def impl(self, space):
+ return space.wrap(loop(self, init, self.find_size()))
+ return func_with_new_name(impl, "reduce_%s_impl" % function.__name__)
+
+ def _reduce_max_min_impl(function):
+ reduce_driver = jit.JitDriver(greens=['signature'],
+ reds = ['i', 'size', 'self', 'result'])
+ def loop(self, result, size):
+ i = 1
+ while i < size:
+ reduce_driver.jit_merge_point(signature=self.signature,
+ self=self, size=size, i=i,
+ result=result)
+ result = function(result, self.eval(i))
+ i += 1
+ return result
+
+ def impl(self, space):
+ size = self.find_size()
+ if size == 0:
+ raise OperationError(space.w_ValueError,
+ space.wrap("Can't call %s on zero-size arrays" \
+ % function.__name__))
+ return space.wrap(loop(self, self.eval(0), size))
+ return func_with_new_name(impl, "reduce_%s_impl" % function.__name__)
+
+ def _reduce_argmax_argmin_impl(function):
+ reduce_driver = jit.JitDriver(greens=['signature'],
+ reds = ['i', 'size', 'result', 'self', 'cur_best'])
+ def loop(self, size):
+ result = 0
+ cur_best = self.eval(0)
+ i = 1
+ while i < size:
+ reduce_driver.jit_merge_point(signature=self.signature,
+ self=self, size=size, i=i,
+ result=result, cur_best=cur_best)
+ new_best = function(cur_best, self.eval(i))
+ if new_best != cur_best:
+ result = i
+ cur_best = new_best
+ i += 1
+ return result
+ def impl(self, space):
+ size = self.find_size()
+ if size == 0:
+ raise OperationError(space.w_ValueError,
+ space.wrap("Can't call %s on zero-size arrays" \
+ % function.__name__))
+ return space.wrap(loop(self, size))
+ return func_with_new_name(impl, "reduce_arg%s_impl" % function.__name__)
+
+ def _all(self):
+ size = self.find_size()
+ i = 0
+ while i < size:
+ all_driver.jit_merge_point(signature=self.signature, self=self, size=size, i=i)
+ if not self.eval(i):
+ return False
+ i += 1
+ return True
+ def descr_all(self, space):
+ return space.wrap(self._all())
+
+ def _any(self):
+ size = self.find_size()
+ i = 0
+ while i < size:
+ any_driver.jit_merge_point(signature=self.signature, self=self, size=size, i=i)
+ if self.eval(i):
+ return True
+ i += 1
+ return False
+ def descr_any(self, space):
+ return space.wrap(self._any())
+
+ descr_sum = _reduce_sum_prod_impl(add, 0.0)
+ descr_prod = _reduce_sum_prod_impl(mul, 1.0)
+ descr_max = _reduce_max_min_impl(maximum)
+ descr_min = _reduce_max_min_impl(minimum)
+ descr_argmax = _reduce_argmax_argmin_impl(maximum)
+ descr_argmin = _reduce_argmax_argmin_impl(minimum)
+
+ def descr_dot(self, space, w_other):
+ if isinstance(w_other, BaseArray):
+ w_res = self.descr_mul(space, w_other)
+ assert isinstance(w_res, BaseArray)
+ return w_res.descr_sum(space)
+ else:
+ return self.descr_mul(space, w_other)
+
def get_concrete(self):
raise NotImplementedError
@@ -136,13 +261,17 @@
return self.get_concrete().descr_setitem(space, item, value)
def descr_mean(self, space):
- s = 0
- concrete = self.get_concrete()
- size = concrete.find_size()
- for i in xrange(size):
- s += concrete.getitem(i)
- return space.wrap(s / size)
+ return space.wrap(space.float_w(self.descr_sum(space))/self.find_size())
+def convert_to_array (space, w_obj):
+ if isinstance(w_obj, BaseArray):
+ return w_obj
+ elif space.issequence_w(w_obj):
+ # Convert to array.
+ return new_numarray(space, w_obj)
+ else:
+ # If it's a scalar
+ return FloatWrapper(space.float_w(w_obj))
class FloatWrapper(BaseArray):
"""
@@ -350,14 +479,17 @@
def __del__(self):
lltype.free(self.storage, flavor='raw')
-def descr_new_numarray(space, w_type, w_size_or_iterable):
+def new_numarray(space, w_size_or_iterable):
l = space.listview(w_size_or_iterable)
arr = SingleDimArray(len(l))
i = 0
for w_elem in l:
arr.storage[i] = space.float_w(space.float(w_elem))
i += 1
- return space.wrap(arr)
+ return arr
+
+def descr_new_numarray(space, w_type, w_size_or_iterable):
+ return space.wrap(new_numarray(space, w_size_or_iterable))
@unwrap_spec(size=int)
def zeros(space, size):
@@ -389,6 +521,21 @@
__div__ = interp2app(BaseArray.descr_div),
__pow__ = interp2app(BaseArray.descr_pow),
__mod__ = interp2app(BaseArray.descr_mod),
+ __radd__ = interp2app(BaseArray.descr_radd),
+ __rsub__ = interp2app(BaseArray.descr_rsub),
+ __rmul__ = interp2app(BaseArray.descr_rmul),
+ __rdiv__ = interp2app(BaseArray.descr_rdiv),
+ __rpow__ = interp2app(BaseArray.descr_rpow),
+ __rmod__ = interp2app(BaseArray.descr_rmod),
mean = interp2app(BaseArray.descr_mean),
+ sum = interp2app(BaseArray.descr_sum),
+ prod = interp2app(BaseArray.descr_prod),
+ max = interp2app(BaseArray.descr_max),
+ min = interp2app(BaseArray.descr_min),
+ argmax = interp2app(BaseArray.descr_argmax),
+ argmin = interp2app(BaseArray.descr_argmin),
+ all = interp2app(BaseArray.descr_all),
+ any = interp2app(BaseArray.descr_any),
+ dot = interp2app(BaseArray.descr_dot),
)
diff --git a/pypy/module/micronumpy/interp_support.py b/pypy/module/micronumpy/interp_support.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/interp_support.py
@@ -0,0 +1,32 @@
+
+from pypy.rlib.rstruct.runpack import runpack
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.interpreter.gateway import unwrap_spec
+from pypy.interpreter.error import OperationError
+from pypy.module.micronumpy.interp_numarray import SingleDimArray
+
+FLOAT_SIZE = rffi.sizeof(lltype.Float)
+
+ at unwrap_spec(s=str)
+def fromstring(space, s):
+ length = len(s)
+
+ if length % FLOAT_SIZE == 0:
+ number = length/FLOAT_SIZE
+ else:
+ raise OperationError(space.w_ValueError, space.wrap(
+ "string length %d not divisable by %d" % (length, FLOAT_SIZE)))
+
+ a = SingleDimArray(number)
+
+ start = 0
+ end = FLOAT_SIZE
+ i = 0
+ while i < number:
+ part = s[start:end]
+ a.storage[i] = runpack('d', part)
+ i += 1
+ start += FLOAT_SIZE
+ end += FLOAT_SIZE
+
+ return space.wrap(a)
diff --git a/pypy/module/micronumpy/interp_ufuncs.py b/pypy/module/micronumpy/interp_ufuncs.py
--- a/pypy/module/micronumpy/interp_ufuncs.py
+++ b/pypy/module/micronumpy/interp_ufuncs.py
@@ -1,30 +1,34 @@
import math
from pypy.interpreter.gateway import unwrap_spec
-from pypy.module.micronumpy.interp_numarray import BaseArray, Call1, Call2, Signature
+from pypy.module.micronumpy.interp_numarray import BaseArray, Call1, Call2, Signature, convert_to_array
from pypy.rlib import rfloat
from pypy.tool.sourcetools import func_with_new_name
-
def ufunc(func):
signature = Signature()
def impl(space, w_obj):
- if isinstance(w_obj, BaseArray):
- w_res = Call1(func, w_obj, w_obj.signature.transition(signature))
- w_obj.invalidates.append(w_res)
+ if space.issequence_w(w_obj):
+ w_obj_arr = convert_to_array(space, w_obj)
+ w_res = Call1(func, w_obj_arr, w_obj_arr.signature.transition(signature))
+ w_obj_arr.invalidates.append(w_res)
return w_res
+ else:
return space.wrap(func(space.float_w(w_obj)))
return func_with_new_name(impl, "%s_dispatcher" % func.__name__)
def ufunc2(func):
signature = Signature()
def impl(space, w_lhs, w_rhs):
- if isinstance(w_lhs, BaseArray) and isinstance(w_rhs, BaseArray):
- new_sig = w_lhs.signature.transition(signature).transition(w_rhs.signature)
- w_res = Call2(func, w_lhs, w_rhs, new_sig)
- w_lhs.invalidates.append(w_res)
- w_rhs.invalidates.append(w_res)
+ if space.issequence_w(w_lhs) or space.issequence_w(w_rhs):
+ w_lhs_arr = convert_to_array(space, w_lhs)
+ w_rhs_arr = convert_to_array(space, w_rhs)
+ new_sig = w_lhs_arr.signature.transition(signature).transition(w_rhs_arr.signature)
+ w_res = Call2(func, w_lhs_arr, w_rhs_arr, new_sig)
+ w_lhs_arr.invalidates.append(w_res)
+ w_rhs_arr.invalidates.append(w_res)
return w_res
+ else:
return space.wrap(func(space.float_w(w_lhs), space.float_w(w_rhs)))
return func_with_new_name(impl, "%s_dispatcher" % func.__name__)
diff --git a/pypy/module/micronumpy/test/test_base.py b/pypy/module/micronumpy/test/test_base.py
--- a/pypy/module/micronumpy/test/test_base.py
+++ b/pypy/module/micronumpy/test/test_base.py
@@ -1,12 +1,10 @@
from pypy.conftest import gettestobjspace
from pypy.module.micronumpy.interp_numarray import SingleDimArray, FloatWrapper
-
class BaseNumpyAppTest(object):
def setup_class(cls):
cls.space = gettestobjspace(usemodules=('micronumpy',))
-
class TestSignature(object):
def test_binop_signature(self, space):
ar = SingleDimArray(10)
@@ -26,4 +24,4 @@
v3 = ar.descr_add(space, v1)
v4 = ar.descr_add(space, v2)
- assert v3.signature is v4.signature
\ No newline at end of file
+ assert v3.signature is v4.signature
diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py
--- a/pypy/module/micronumpy/test/test_numarray.py
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -1,6 +1,7 @@
import py
from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
+from pypy.conftest import gettestobjspace
class AppTestNumArray(BaseNumpyAppTest):
@@ -96,6 +97,15 @@
for i in range(5):
assert b[i] == i + 5
+ def test_add_list(self):
+ from numpy import array
+ a = array(range(5))
+ b = list(reversed(range(5)))
+ c = a + b
+ assert isinstance(c, array)
+ for i in range(5):
+ assert c[i] == 4
+
def test_subtract(self):
from numpy import array
a = array(range(5))
@@ -276,7 +286,97 @@
assert d[1] == 12
def test_mean(self):
- from numpy import array, mean
+ from numpy import array
a = array(range(5))
assert a.mean() == 2.0
assert a[:4].mean() == 1.5
+
+ def test_sum(self):
+ from numpy import array
+ a = array(range(5))
+ assert a.sum() == 10.0
+ assert a[:4].sum() == 6.0
+
+ def test_prod(self):
+ from numpy import array
+ a = array(range(1,6))
+ assert a.prod() == 120.0
+ assert a[:4].prod() == 24.0
+
+ def test_max(self):
+ from numpy import array
+ a = array([-1.2, 3.4, 5.7, -3.0, 2.7])
+ assert a.max() == 5.7
+ b = array([])
+ raises(ValueError, "b.max()")
+
+ def test_max_add(self):
+ from numpy import array
+ a = array([-1.2, 3.4, 5.7, -3.0, 2.7])
+ assert (a+a).max() == 11.4
+
+ def test_min(self):
+ from numpy import array
+ a = array([-1.2, 3.4, 5.7, -3.0, 2.7])
+ assert a.min() == -3.0
+ b = array([])
+ raises(ValueError, "b.min()")
+
+ def test_argmax(self):
+ from numpy import array
+ a = array([-1.2, 3.4, 5.7, -3.0, 2.7])
+ assert a.argmax() == 2
+ b = array([])
+ raises(ValueError, "b.argmax()")
+
+ def test_argmin(self):
+ from numpy import array
+ a = array([-1.2, 3.4, 5.7, -3.0, 2.7])
+ assert a.argmin() == 3
+ b = array([])
+ raises(ValueError, "b.argmin()")
+
+ def test_all(self):
+ from numpy import array
+ a = array(range(5))
+ assert a.all() == False
+ a[0] = 3.0
+ assert a.all() == True
+ b = array([])
+ assert b.all() == True
+
+ def test_any(self):
+ from numpy import array, zeros
+ a = array(range(5))
+ assert a.any() == True
+ b = zeros(5)
+ assert b.any() == False
+ c = array([])
+ assert c.any() == False
+
+ def test_dot(self):
+ from numpy import array
+ a = array(range(5))
+ assert a.dot(a) == 30.0
+
+ def test_dot_constant(self):
+ from numpy import array
+ a = array(range(5))
+ b = a.dot(2.5)
+ for i in xrange(5):
+ assert b[i] == 2.5*a[i]
+
+
+class AppTestSupport(object):
+ def setup_class(cls):
+ import struct
+ cls.space = gettestobjspace(usemodules=('micronumpy',))
+ cls.w_data = cls.space.wrap(struct.pack('dddd', 1, 2, 3, 4))
+
+ def test_fromstring(self):
+ from numpy import fromstring
+ a = fromstring(self.data)
+ for i in range(4):
+ assert a[i] == i + 1
+ raises(ValueError, fromstring, "abc")
+
diff --git a/pypy/module/micronumpy/test/test_ufuncs.py b/pypy/module/micronumpy/test/test_ufuncs.py
--- a/pypy/module/micronumpy/test/test_ufuncs.py
+++ b/pypy/module/micronumpy/test/test_ufuncs.py
@@ -10,6 +10,40 @@
assert sign(-0.0) == 0.0
assert minimum(2.0, 3.0) == 2.0
+ def test_sequence(self):
+ from numpy import array, negative, minimum
+ a = array(range(3))
+ b = [2.0, 1.0, 0.0]
+ c = 1.0
+ b_neg = negative(b)
+ assert isinstance(b_neg, array)
+ for i in range(3):
+ assert b_neg[i] == -b[i]
+ min_a_b = minimum(a, b)
+ assert isinstance(min_a_b, array)
+ for i in range(3):
+ assert min_a_b[i] == min(a[i], b[i])
+ min_b_a = minimum(b, a)
+ assert isinstance(min_b_a, array)
+ for i in range(3):
+ assert min_b_a[i] == min(a[i], b[i])
+ min_a_c = minimum(a, c)
+ assert isinstance(min_a_c, array)
+ for i in range(3):
+ assert min_a_c[i] == min(a[i], c)
+ min_c_a = minimum(c, a)
+ assert isinstance(min_c_a, array)
+ for i in range(3):
+ assert min_c_a[i] == min(a[i], c)
+ min_b_c = minimum(b, c)
+ assert isinstance(min_b_c, array)
+ for i in range(3):
+ assert min_b_c[i] == min(b[i], c)
+ min_c_b = minimum(c, b)
+ assert isinstance(min_c_b, array)
+ for i in range(3):
+ assert min_c_b[i] == min(b[i], c)
+
def test_negative(self):
from numpy import array, negative
diff --git a/pypy/module/micronumpy/test/test_zjit.py b/pypy/module/micronumpy/test/test_zjit.py
--- a/pypy/module/micronumpy/test/test_zjit.py
+++ b/pypy/module/micronumpy/test/test_zjit.py
@@ -4,9 +4,20 @@
FloatWrapper, Call2, SingleDimSlice, add, mul, neg, Call1)
from pypy.module.micronumpy.interp_ufuncs import negative
from pypy.module.micronumpy.compile import numpy_compile
+from pypy.rlib.objectmodel import specialize
class FakeSpace(object):
- pass
+ w_ValueError = None
+
+ def issequence_w(self, w_obj):
+ return True
+
+ @specialize.argtype(1)
+ def wrap(self, w_obj):
+ return w_obj
+
+ def float_w(self, w_obj):
+ return float(w_obj)
class TestNumpyJIt(LLJitMixin):
def setup_class(cls):
@@ -51,6 +62,110 @@
assert result == f(5)
+ def test_sum(self):
+ space = self.space
+
+ def f(i):
+ ar = SingleDimArray(i)
+ return ar.descr_add(space, ar).descr_sum(space)
+
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 2,
+ "int_add": 1,
+ "int_lt": 1, "guard_true": 1, "jump": 1})
+ assert result == f(5)
+
+ def test_prod(self):
+ space = self.space
+
+ def f(i):
+ ar = SingleDimArray(i)
+ return ar.descr_add(space, ar).descr_prod(space)
+
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 1,
+ "float_mul": 1, "int_add": 1,
+ "int_lt": 1, "guard_true": 1, "jump": 1})
+ assert result == f(5)
+
+ def test_max(self):
+ space = self.space
+
+ def f(i):
+ ar = SingleDimArray(i)
+ j = 0
+ while j < i:
+ ar.get_concrete().storage[j] = float(j)
+ j += 1
+ return ar.descr_add(space, ar).descr_max(space)
+
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 1,
+ "float_gt": 1, "int_add": 1,
+ "int_lt": 1, "guard_true": 1,
+ "guard_false": 1, "jump": 1})
+
+ def test_min(self):
+ space = self.space
+
+ def f(i):
+ ar = SingleDimArray(i)
+ j = 0
+ while j < i:
+ ar.get_concrete().storage[j] = float(j)
+ j += 1
+ return ar.descr_add(space, ar).descr_min(space)
+
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 1,
+ "float_lt": 1, "int_add": 1,
+ "int_lt": 1, "guard_true": 2,
+ "jump": 1})
+
+ def test_argmin(self):
+ space = self.space
+
+ def f(i):
+ ar = SingleDimArray(i)
+ j = 0
+ while j < i:
+ ar.get_concrete().storage[j] = float(j)
+ j += 1
+ return ar.descr_add(space, ar).descr_argmin(space)
+
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 1,
+ "float_lt": 1, "int_add": 1,
+ "int_lt": 1, "guard_true": 2,
+ "jump": 1})
+
+ def test_all(self):
+ space = self.space
+
+ def f(i):
+ ar = SingleDimArray(i)
+ j = 0
+ while j < i:
+ ar.get_concrete().storage[j] = 1.0
+ j += 1
+ return ar.descr_add(space, ar).descr_all(space)
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 1,
+ "int_add": 1, "float_ne": 1,
+ "int_lt": 1, "guard_true": 2, "jump": 1})
+
+ def test_any(self):
+ space = self.space
+
+ def f(i):
+ ar = SingleDimArray(i)
+ return ar.descr_add(space, ar).descr_any(space)
+
+ result = self.meta_interp(f, [5], listops=True, backendopt=True)
+ self.check_loops({"getarrayitem_raw": 2, "float_add": 1,
+ "int_add": 1, "float_ne": 1, "guard_false": 1,
+ "int_lt": 1, "guard_true": 1, "jump": 1})
+
def test_already_forecd(self):
def f(i):
ar = SingleDimArray(i)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_array.py b/pypy/module/pypyjit/test_pypy_c/test_array.py
--- a/pypy/module/pypyjit/test_pypy_c/test_array.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_array.py
@@ -19,7 +19,7 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i7 = int_lt(i5, i6)
- guard_true(i7, descr=<Guard3>)
+ guard_true(i7, descr=...)
i9 = int_add(i5, 1)
--TICK--
jump(p0, p1, p2, p3, p4, i9, i6, descr=<Loop0>)
@@ -39,11 +39,12 @@
assert log.result == 19507200
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
+ guard_not_invalidated(descr=...)
i13 = int_lt(i7, i9)
- guard_true(i13, descr=<Guard3>)
+ guard_true(i13, descr=...)
i15 = getarrayitem_raw(i10, i7, descr=<.*ArrayNoLengthDescr>)
i16 = int_add_ovf(i8, i15)
- guard_no_overflow(descr=<Guard4>)
+ guard_no_overflow(descr=...)
i18 = int_add(i7, 1)
--TICK--
jump(p0, p1, p2, p3, p4, p5, i18, i16, p8, i9, i10, descr=<Loop0>)
@@ -68,16 +69,17 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i13 = int_lt(i8, 307200)
- guard_true(i13, descr=<Guard3>)
+ guard_true(i13, descr=...)
+ guard_not_invalidated(descr=...)
# the bound check guard on img has been killed (thanks to the asserts)
i14 = getarrayitem_raw(i10, i8, descr=<.*ArrayNoLengthDescr>)
i15 = int_add_ovf(i9, i14)
- guard_no_overflow(descr=<Guard4>)
+ guard_no_overflow(descr=...)
i17 = int_sub(i8, 640)
# the bound check guard on intimg has been killed (thanks to the asserts)
i18 = getarrayitem_raw(i11, i17, descr=<.*ArrayNoLengthDescr>)
i19 = int_add_ovf(i18, i15)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
# on 64bit, there is a guard checking that i19 actually fits into 32bit
...
setarrayitem_raw(i11, i8, _, descr=<.*ArrayNoLengthDescr>)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_call.py b/pypy/module/pypyjit/test_pypy_c/test_call.py
--- a/pypy/module/pypyjit/test_pypy_c/test_call.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_call.py
@@ -80,19 +80,19 @@
#
assert entry_bridge.match_by_id('call', """
p29 = getfield_gc(ConstPtr(ptr28), descr=<GcPtrFieldDescr pypy.objspace.std.celldict.ModuleCell.inst_w_value .*>)
- guard_nonnull_class(p29, ConstClass(Function), descr=<Guard18>)
+ guard_nonnull_class(p29, ConstClass(Function), descr=...)
p33 = getfield_gc(p29, descr=<GcPtrFieldDescr pypy.interpreter.function.Function.inst_code .*>)
- guard_value(p33, ConstPtr(ptr34), descr=<Guard19>)
+ guard_value(p33, ConstPtr(ptr34), descr=...)
p35 = getfield_gc(p29, descr=<GcPtrFieldDescr pypy.interpreter.function.Function.inst_w_func_globals .*>)
p36 = getfield_gc(p29, descr=<GcPtrFieldDescr pypy.interpreter.function.Function.inst_closure .*>)
p38 = call(ConstClass(getexecutioncontext), descr=<GcPtrCallDescr>)
p39 = getfield_gc(p38, descr=<GcPtrFieldDescr pypy.interpreter.executioncontext.ExecutionContext.inst_topframeref .*>)
i40 = force_token()
p41 = getfield_gc(p38, descr=<GcPtrFieldDescr pypy.interpreter.executioncontext.ExecutionContext.inst_w_tracefunc .*>)
- guard_isnull(p41, descr=<Guard20>)
+ guard_isnull(p41, descr=...)
i42 = getfield_gc(p38, descr=<NonGcPtrFieldDescr pypy.interpreter.executioncontext.ExecutionContext.inst_profilefunc .*>)
i43 = int_is_zero(i42)
- guard_true(i43, descr=<Guard21>)
+ guard_true(i43, descr=...)
i50 = force_token()
""")
#
@@ -101,16 +101,16 @@
loop, = log.loops_by_id('call')
assert loop.match("""
i12 = int_lt(i5, i6)
- guard_true(i12, descr=<Guard3>)
+ guard_true(i12, descr=...)
i13 = force_token()
i15 = int_add(i5, 1)
i16 = int_add_ovf(i15, i7)
- guard_no_overflow(descr=<Guard4>)
+ guard_no_overflow(descr=...)
i18 = force_token()
i20 = int_add_ovf(i16, 1)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
i21 = int_add_ovf(i20, i7)
- guard_no_overflow(descr=<Guard6>)
+ guard_no_overflow(descr=...)
--TICK--
jump(p0, p1, p2, p3, p4, i21, i6, i7, p8, p9, p10, p11, descr=<Loop0>)
""")
@@ -146,14 +146,14 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i15 = int_lt(i6, i9)
- guard_true(i15, descr=<Guard3>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_true(i15, descr=...)
+ guard_not_invalidated(descr=...)
i16 = force_token()
i17 = int_add_ovf(i10, i6)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
i18 = force_token()
i19 = int_add_ovf(i10, i17)
- guard_no_overflow(descr=<Guard6>)
+ guard_no_overflow(descr=...)
--TICK--
jump(p0, p1, p2, p3, p4, p5, i19, p7, i17, i9, i10, p11, p12, p13, descr=<Loop0>)
""")
@@ -180,11 +180,11 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i14 = int_lt(i6, i9)
- guard_true(i14, descr=<Guard3>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_true(i14, descr=...)
+ guard_not_invalidated(descr=...)
i15 = force_token()
i17 = int_add_ovf(i8, 1)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
i18 = force_token()
--TICK--
jump(p0, p1, p2, p3, p4, i8, p7, i17, p8, i9, p10, p11, p12, descr=<Loop0>)
@@ -281,25 +281,23 @@
loop0, = log.loops_by_id('g1')
assert loop0.match_by_id('g1', """
i20 = force_token()
- setfield_gc(p4, i19, descr=<.*W_AbstractSeqIterObject.inst_index .*>)
i22 = int_add_ovf(i8, 3)
- guard_no_overflow(descr=<Guard4>)
+ guard_no_overflow(descr=...)
""")
assert loop0.match_by_id('h1', """
i20 = force_token()
i22 = int_add_ovf(i8, 2)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
""")
assert loop0.match_by_id('g2', """
i27 = force_token()
i29 = int_add_ovf(i26, 3)
- guard_no_overflow(descr=<Guard6>)
+ guard_no_overflow(descr=...)
""")
#
loop1, = log.loops_by_id('g3')
assert loop1.match_by_id('g3', """
i21 = force_token()
- setfield_gc(p4, i20, descr=<.* .*W_AbstractSeqIterObject.inst_index .*>)
i23 = int_add_ovf(i9, 3)
guard_no_overflow(descr=...)
""")
@@ -352,7 +350,7 @@
i13 = getfield_gc(p8, descr=<SignedFieldDescr list.length .*>)
i15 = int_add(i13, 1)
call(ConstClass(_ll_list_resize_ge__listPtr_Signed), p8, i15, descr=<VoidCallDescr>)
- guard_no_exception(descr=<Guard4>)
+ guard_no_exception(descr=...)
p17 = getfield_gc(p8, descr=<GcPtrFieldDescr list.items .*>)
p19 = new_with_vtable(ConstClass(W_IntObject))
setfield_gc(p19, i12, descr=<SignedFieldDescr .*W_IntObject.inst_intval .*>)
@@ -404,9 +402,9 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i10 = int_lt(i5, i6)
- guard_true(i10, descr=<Guard3>)
+ guard_true(i10, descr=...)
+ guard_not_invalidated(descr=...)
i120 = int_add(i5, 1)
- guard_not_invalidated(descr=<Guard4>)
--TICK--
jump(..., descr=<Loop0>)
""")
diff --git a/pypy/module/pypyjit/test_pypy_c/test_exception.py b/pypy/module/pypyjit/test_pypy_c/test_exception.py
--- a/pypy/module/pypyjit/test_pypy_c/test_exception.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_exception.py
@@ -36,11 +36,11 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i5 = int_is_true(i3)
- guard_true(i5, descr=<Guard3>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_true(i5, descr=...)
+ guard_not_invalidated(descr=...)
--EXC-TICK--
i12 = int_sub_ovf(i3, 1)
- guard_no_overflow(descr=<Guard6>)
+ guard_no_overflow(descr=...)
--TICK--
jump(..., descr=<Loop0>)
""")
@@ -84,8 +84,8 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i7 = int_lt(i4, i5)
- guard_true(i7, descr=<Guard3>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_true(i7, descr=...)
+ guard_not_invalidated(descr=...)
--EXC-TICK--
i14 = int_add(i4, 1)
--TICK--
diff --git a/pypy/module/pypyjit/test_pypy_c/test_globals.py b/pypy/module/pypyjit/test_pypy_c/test_globals.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/pypyjit/test_pypy_c/test_globals.py
@@ -0,0 +1,30 @@
+from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC
+
+
+class TestGlobals(BaseTestPyPyC):
+ def test_load_builtin(self):
+ def main(n):
+ import pypyjit
+
+ i = 0
+ while i < n:
+ l = len # ID: loadglobal
+ i += pypyjit.residual_call(l, "a")
+ return i
+ #
+ log = self.run(main, [500])
+ assert log.result == 500
+ loop, = log.loops_by_filename(self.filepath)
+ assert loop.match_by_id("loadglobal", """
+ p10 = getfield_gc(p0, descr=<GcPtrFieldDescr .*Frame.inst_w_globals .*>)
+ guard_value(p10, ConstPtr(ptr11), descr=...)
+ p12 = getfield_gc(p10, descr=<GcPtrFieldDescr .*W_DictMultiObject.inst_strategy .*>)
+ guard_value(p12, ConstPtr(ptr13), descr=...)
+ p15 = getfield_gc(ConstPtr(ptr14), descr=<GcPtrFieldDescr .*ModuleCell.inst_w_value .*>)
+ guard_isnull(p15, descr=...)
+ guard_not_invalidated(descr=...)
+ p19 = getfield_gc(ConstPtr(p17), descr=<GcPtrFieldDescr .*W_DictMultiObject.inst_strategy .*>)
+ guard_value(p19, ConstPtr(ptr20), descr=...)
+ p22 = getfield_gc(ConstPtr(ptr21), descr=<GcPtrFieldDescr .*ModuleCell.inst_w_value .*>)
+ guard_nonnull(p22, descr=...)
+ """)
\ No newline at end of file
diff --git a/pypy/module/pypyjit/test_pypy_c/test_import.py b/pypy/module/pypyjit/test_pypy_c/test_import.py
--- a/pypy/module/pypyjit/test_pypy_c/test_import.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_import.py
@@ -15,13 +15,13 @@
assert log.result == 500
loop, = log.loops_by_id('import')
assert loop.match_by_id('import', """
+ guard_not_invalidated(descr=...)
p11 = getfield_gc(ConstPtr(ptr10), descr=<GcPtrFieldDescr pypy.objspace.std.celldict.ModuleCell.inst_w_value 8>)
- guard_value(p11, ConstPtr(ptr12), descr=<Guard4>)
- guard_not_invalidated(descr=<Guard5>)
+ guard_value(p11, ConstPtr(ptr12), descr=...)
p14 = getfield_gc(ConstPtr(ptr13), descr=<GcPtrFieldDescr pypy.objspace.std.celldict.ModuleCell.inst_w_value 8>)
p16 = getfield_gc(ConstPtr(ptr15), descr=<GcPtrFieldDescr pypy.objspace.std.celldict.ModuleCell.inst_w_value 8>)
- guard_value(p14, ConstPtr(ptr17), descr=<Guard6>)
- guard_isnull(p16, descr=<Guard7>)
+ guard_value(p14, ConstPtr(ptr17), descr=...)
+ guard_isnull(p16, descr=...)
""")
def test_import_fast_path(self, tmpdir):
diff --git a/pypy/module/pypyjit/test_pypy_c/test_instance.py b/pypy/module/pypyjit/test_pypy_c/test_instance.py
--- a/pypy/module/pypyjit/test_pypy_c/test_instance.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_instance.py
@@ -22,10 +22,10 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i7 = int_lt(i5, i6)
- guard_true(i7, descr=<Guard3>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_true(i7, descr=...)
+ guard_not_invalidated(descr=...)
i9 = int_add_ovf(i5, 2)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
--TICK--
jump(p0, p1, p2, p3, p4, i9, i6, descr=<Loop0>)
""")
@@ -47,10 +47,10 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i9 = int_lt(i5, i6)
- guard_true(i9, descr=<Guard3>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_true(i9, descr=...)
+ guard_not_invalidated(descr=...)
i10 = int_add_ovf(i5, i7)
- guard_no_overflow(descr=<Guard5>)
+ guard_no_overflow(descr=...)
--TICK--
jump(p0, p1, p2, p3, p4, i10, i6, p7, i7, p8, descr=<Loop0>)
""")
diff --git a/pypy/module/pypyjit/test_pypy_c/test_min_max.py b/pypy/module/pypyjit/test_pypy_c/test_min_max.py
--- a/pypy/module/pypyjit/test_pypy_c/test_min_max.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_min_max.py
@@ -17,6 +17,7 @@
assert loop.match("""
i7 = int_lt(i4, 300)
guard_true(i7, descr=...)
+ guard_not_invalidated(descr=...)
i9 = int_add_ovf(i5, 3000)
guard_no_overflow(descr=...)
i11 = int_add(i4, 1)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_misc.py b/pypy/module/pypyjit/test_pypy_c/test_misc.py
--- a/pypy/module/pypyjit/test_pypy_c/test_misc.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_misc.py
@@ -84,7 +84,7 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i9 = float_lt(f5, f7)
- guard_true(i9, descr=<Guard3>)
+ guard_true(i9, descr=...)
f10 = float_add(f8, f5)
--TICK--
jump(p0, p1, p2, p3, p4, f10, p6, f7, f8, descr=<Loop0>)
@@ -107,19 +107,19 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i16 = int_ge(i11, i12)
- guard_false(i16, descr=<Guard3>)
+ guard_false(i16, descr=...)
i17 = int_mul(i11, i14)
i18 = int_add(i15, i17)
i20 = int_add(i11, 1)
i21 = force_token()
setfield_gc(p4, i20, descr=<.* .*W_AbstractSeqIterObject.inst_index .*>)
- guard_not_invalidated(descr=<Guard4>)
+ guard_not_invalidated(descr=...)
i23 = int_lt(i18, 0)
- guard_false(i23, descr=<Guard5>)
+ guard_false(i23, descr=...)
i25 = int_ge(i18, i9)
- guard_false(i25, descr=<Guard6>)
+ guard_false(i25, descr=...)
i27 = int_add_ovf(i7, i18)
- guard_no_overflow(descr=<Guard7>)
+ guard_no_overflow(descr=...)
--TICK--
jump(..., descr=<Loop0>)
""")
@@ -164,20 +164,20 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i16 = int_ge(i12, i13)
- guard_false(i16, descr=<Guard3>)
+ guard_false(i16, descr=...)
p17 = getarrayitem_gc(p15, i12, descr=<GcPtrArrayDescr>)
i19 = int_add(i12, 1)
setfield_gc(p9, i19, descr=<SignedFieldDescr .*W_AbstractSeqIterObject.inst_index .*>)
- guard_nonnull_class(p17, 146982464, descr=<Guard4>)
+ guard_nonnull_class(p17, 146982464, descr=...)
i21 = getfield_gc(p17, descr=<SignedFieldDescr .*W_ArrayTypei.inst_len .*>)
i23 = int_lt(0, i21)
- guard_true(i23, descr=<Guard5>)
+ guard_true(i23, descr=...)
i24 = getfield_gc(p17, descr=<NonGcPtrFieldDescr .*W_ArrayTypei.inst_buffer .*>)
i25 = getarrayitem_raw(i24, 0, descr=<.*>)
i27 = int_lt(1, i21)
- guard_false(i27, descr=<Guard6>)
+ guard_false(i27, descr=...)
i28 = int_add_ovf(i10, i25)
- guard_no_overflow(descr=<Guard7>)
+ guard_no_overflow(descr=...)
--TICK--
jump(p0, p1, p2, p3, p4, p5, p6, i28, i25, p9, p10, p11, i19, i13, p14, p15, descr=<Loop0>)
""")
@@ -201,9 +201,9 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i11 = int_lt(i7, 300)
- guard_true(i11, descr=<Guard3>)
+ guard_true(i11, descr=...)
i12 = int_add_ovf(i8, i9)
- guard_no_overflow(descr=<Guard4>)
+ guard_no_overflow(descr=...)
i14 = int_add(i7, 1)
--TICK--
jump(..., descr=...)
diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py
--- a/pypy/module/pypyjit/test_pypy_c/test_string.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_string.py
@@ -16,27 +16,92 @@
loop, = log.loops_by_filename(self.filepath)
assert loop.match("""
i14 = int_lt(i6, i9)
- guard_true(i14, descr=<Guard42>)
+ guard_true(i14, descr=...)
+ guard_not_invalidated(descr=...)
i15 = int_mod(i6, i10)
i17 = int_rshift(i15, 63)
i18 = int_and(i10, i17)
i19 = int_add(i15, i18)
i21 = int_lt(i19, 0)
- guard_false(i21, descr=<Guard43>)
+ guard_false(i21, descr=...)
i22 = int_ge(i19, i10)
- guard_false(i22, descr=<Guard44>)
+ guard_false(i22, descr=...)
i23 = strgetitem(p11, i19)
i24 = int_ge(i19, i12)
- guard_false(i24, descr=<Guard45>)
+ guard_false(i24, descr=...)
i25 = unicodegetitem(p13, i19)
- guard_not_invalidated(descr=<Guard46>)
p27 = newstr(1)
strsetitem(p27, 0, i23)
p30 = call(ConstClass(ll_str2unicode__rpy_stringPtr), p27, descr=<GcPtrCallDescr>)
- guard_no_exception(descr=<Guard47>)
+ guard_no_exception(descr=...)
i32 = call(ConstClass(_ll_2_str_eq_checknull_char__rpy_unicodePtr_UniChar), p30, i25, descr=<SignedCallDescr>)
- guard_true(i32, descr=<Guard48>)
+ guard_true(i32, descr=...)
i34 = int_add(i6, 1)
--TICK--
jump(p0, p1, p2, p3, p4, p5, i34, p7, p8, i9, i10, p11, i12, p13, descr=<Loop4>)
+ """)
+
+ def test_long(self):
+ def main(n):
+ import string
+ i = 1
+ while i < n:
+ i += int(long(string.digits[i % len(string.digits)], 16))
+ return i
+
+ log = self.run(main, [1000])
+ assert log.result == main(1000)
+ loop, = log.loops_by_filename(self.filepath)
+ assert loop.match("""
+ i11 = int_lt(i6, i7)
+ guard_true(i11, descr=...)
+ guard_not_invalidated(descr=...)
+ i13 = int_eq(i6, -9223372036854775808)
+ guard_false(i13, descr=...)
+ i15 = int_mod(i6, i8)
+ i17 = int_rshift(i15, 63)
+ i18 = int_and(i8, i17)
+ i19 = int_add(i15, i18)
+ i21 = int_lt(i19, 0)
+ guard_false(i21, descr=...)
+ i22 = int_ge(i19, i8)
+ guard_false(i22, descr=...)
+ i23 = strgetitem(p10, i19)
+ p25 = newstr(1)
+ strsetitem(p25, 0, i23)
+ p28 = call(ConstClass(strip_spaces), p25, descr=<GcPtrCallDescr>)
+ guard_no_exception(descr=...)
+ i29 = strlen(p28)
+ i30 = int_is_true(i29)
+ guard_true(i30, descr=...)
+ i32 = int_sub(i29, 1)
+ i33 = strgetitem(p28, i32)
+ i35 = int_eq(i33, 108)
+ guard_false(i35, descr=...)
+ i37 = int_eq(i33, 76)
+ guard_false(i37, descr=...)
+ i39 = strgetitem(p28, 0)
+ i41 = int_eq(i39, 45)
+ guard_false(i41, descr=...)
+ i43 = int_eq(i39, 43)
+ guard_false(i43, descr=...)
+ i43 = call(ConstClass(ll_startswith__rpy_stringPtr_rpy_stringPtr), p28, ConstPtr(ptr42), descr=<BoolCallDescr>)
+ guard_false(i43, descr=...)
+ i46 = call(ConstClass(ll_startswith__rpy_stringPtr_rpy_stringPtr), p28, ConstPtr(ptr45), descr=<BoolCallDescr>)
+ guard_false(i46, descr=...)
+ p51 = new_with_vtable(21136408)
+ setfield_gc(p51, p28, descr=<GcPtrFieldDescr .*NumberStringParser.inst_literal .*>)
+ setfield_gc(p51, ConstPtr(ptr51), descr=<GcPtrFieldDescr pypy.objspace.std.strutil.NumberStringParser.inst_fname .*>)
+ setfield_gc(p51, i29, descr=<SignedFieldDescr .*NumberStringParser.inst_n .*>)
+ setfield_gc(p51, 1, descr=<SignedFieldDescr .*NumberStringParser.inst_sign .*>)
+ setfield_gc(p51, 16, descr=<SignedFieldDescr .*NumberStringParser.inst_base .*>)
+ setfield_gc(p51, p28, descr=<GcPtrFieldDescr .*NumberStringParser.inst_s .*>)
+ p55 = call(ConstClass(parse_digit_string), p51, descr=<GcPtrCallDescr>)
+ guard_no_exception(descr=...)
+ i57 = call(ConstClass(rbigint.toint), p55, descr=<SignedCallDescr>)
+ guard_no_exception(descr=...)
+ i58 = int_add_ovf(i6, i57)
+ guard_no_overflow(descr=...)
+ --TICK--
+ jump(p0, p1, p2, p3, p4, p5, i58, i7, i8, p9, p10, descr=<Loop4>)
""")
\ No newline at end of file
diff --git a/pypy/module/sys/version.py b/pypy/module/sys/version.py
--- a/pypy/module/sys/version.py
+++ b/pypy/module/sys/version.py
@@ -10,7 +10,7 @@
CPYTHON_VERSION = (2, 7, 1, "final", 42) #XXX # sync patchlevel.h
CPYTHON_API_VERSION = 1013 #XXX # sync with include/modsupport.h
-PYPY_VERSION = (1, 5, 0, "alpha", 0) #XXX # sync patchlevel.h
+PYPY_VERSION = (1, 6, 0, "dev", 1) #XXX # sync patchlevel.h
if platform.name == 'msvc':
COMPILER_INFO = 'MSC v.%d 32 bit' % (platform.version * 10 + 600)
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py b/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py
--- a/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py
+++ b/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py
@@ -132,6 +132,16 @@
# You cannot assing character format codes as restype any longer
raises(TypeError, setattr, f, "restype", "i")
+
+ def test_truncate_python_longs(self):
+ f = dll._testfunc_i_bhilfd
+ f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double]
+ f.restype = c_int
+ x = sys.maxint * 2
+ result = f(x, x, x, x, 0, 0)
+ assert result == -8
+
+
def test_floatresult(self):
f = dll._testfunc_f_bhilfd
f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double]
@@ -411,6 +421,23 @@
result = f("abcd", ord("b"))
assert result == "bcd"
+ def test_keepalive_buffers(self, monkeypatch):
+ import gc
+ f = dll.my_strchr
+ f.argtypes = [c_char_p]
+ f.restype = c_char_p
+ #
+ orig__call_funcptr = f._call_funcptr
+ def _call_funcptr(funcptr, *newargs):
+ gc.collect()
+ gc.collect()
+ gc.collect()
+ return orig__call_funcptr(funcptr, *newargs)
+ monkeypatch.setattr(f, '_call_funcptr', _call_funcptr)
+ #
+ result = f("abcd", ord("b"))
+ assert result == "bcd"
+
def test_caching_bug_1(self):
# the same test as test_call_some_args, with two extra lines
# in the middle that trigger caching in f._ptr, which then
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_guess_argtypes.py b/pypy/module/test_lib_pypy/ctypes_tests/test_guess_argtypes.py
--- a/pypy/module/test_lib_pypy/ctypes_tests/test_guess_argtypes.py
+++ b/pypy/module/test_lib_pypy/ctypes_tests/test_guess_argtypes.py
@@ -12,7 +12,7 @@
from _ctypes.function import CFuncPtr
def guess(value):
- cobj, ctype = CFuncPtr._conv_param(None, value)
+ _, cobj, ctype = CFuncPtr._conv_param(None, value)
return ctype
## cobj = CFuncPtr._conv_param(None, value)
## return type(cobj)
diff --git a/pypy/module/thread/ll_thread.py b/pypy/module/thread/ll_thread.py
--- a/pypy/module/thread/ll_thread.py
+++ b/pypy/module/thread/ll_thread.py
@@ -21,6 +21,7 @@
'RPyThreadAcquireLock', 'RPyThreadReleaseLock',
'RPyThreadYield',
'RPyThreadGetStackSize', 'RPyThreadSetStackSize',
+ 'RPyOpaqueDealloc_ThreadLock',
'RPyThreadAfterFork']
)
@@ -52,6 +53,9 @@
c_thread_lock_init = llexternal('RPyThreadLockInit', [TLOCKP], rffi.INT,
threadsafe=False) # may add in a global list
+c_thread_lock_dealloc = llexternal('RPyOpaqueDealloc_ThreadLock', [TLOCKP],
+ lltype.Void,
+ threadsafe=True)
c_thread_acquirelock = llexternal('RPyThreadAcquireLock', [TLOCKP, rffi.INT],
rffi.INT,
threadsafe=True) # release the GIL
@@ -156,6 +160,9 @@
return ll_lock
def free_ll_lock(ll_lock):
+ c_thread_acquirelock(ll_lock, 0)
+ c_thread_releaselock(ll_lock)
+ c_thread_lock_dealloc(ll_lock)
lltype.free(ll_lock, flavor='raw', track_allocation=False)
def acquire_NOAUTO(ll_lock, flag):
diff --git a/pypy/module/thread/test/test_import_lock.py b/pypy/module/thread/test/test_import_lock.py
--- a/pypy/module/thread/test/test_import_lock.py
+++ b/pypy/module/thread/test/test_import_lock.py
@@ -66,6 +66,9 @@
def test_lock(self, space, monkeypatch):
from pypy.module.imp.importing import getimportlock, importhook
+ # Force importing the module _file now
+ space.builtin.get('file')
+
# Monkeypatch the import lock and add a counter
importlock = getimportlock(space)
original_acquire = importlock.acquire_lock
diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py
--- a/pypy/objspace/std/dictmultiobject.py
+++ b/pypy/objspace/std/dictmultiobject.py
@@ -736,6 +736,8 @@
class W_DictMultiIterObject(W_Object):
from pypy.objspace.std.dicttype import dictiter_typedef as typedef
+ _immutable_fields_ = ["iteratorimplementation", "itertype"]
+
def __init__(w_self, space, iteratorimplementation, itertype):
w_self.space = space
w_self.iteratorimplementation = iteratorimplementation
diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -431,12 +431,17 @@
return None
assert isinstance(lifeline, WeakrefLifeline)
return lifeline
+ getweakref._cannot_really_call_random_things_ = True
def setweakref(self, space, weakreflifeline):
from pypy.module._weakref.interp__weakref import WeakrefLifeline
- assert (isinstance(weakreflifeline, WeakrefLifeline) or
- weakreflifeline is None)
+ assert isinstance(weakreflifeline, WeakrefLifeline)
self._get_mapdict_map().write(self, ("weakref", SPECIAL), weakreflifeline)
+ setweakref._cannot_really_call_random_things_ = True
+
+ def delweakref(self):
+ self._get_mapdict_map().write(self, ("weakref", SPECIAL), None)
+ delweakref._cannot_really_call_random_things_ = True
class ObjectMixin(object):
_mixin_ = True
diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py
--- a/pypy/objspace/std/setobject.py
+++ b/pypy/objspace/std/setobject.py
@@ -36,6 +36,8 @@
return self._lifeline_
def setweakref(self, space, weakreflifeline):
self._lifeline_ = weakreflifeline
+ def delweakref(self):
+ self._lifeline_ = None
class W_SetObject(W_BaseSetObject):
from pypy.objspace.std.settype import set_typedef as typedef
diff --git a/pypy/objspace/std/test/test_mapdict.py b/pypy/objspace/std/test/test_mapdict.py
--- a/pypy/objspace/std/test/test_mapdict.py
+++ b/pypy/objspace/std/test/test_mapdict.py
@@ -171,7 +171,7 @@
obj = c.instantiate()
assert obj.getweakref() is None
obj.setweakref(space, lifeline1)
- obj.setweakref(space, None)
+ obj.delweakref()
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -532,6 +532,8 @@
return self._lifeline_
def setweakref(self, space, weakreflifeline):
self._lifeline_ = weakreflifeline
+ def delweakref(self):
+ self._lifeline_ = None
# ____________________________________________________________
# Initialization of type objects
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -482,6 +482,13 @@
key[2:])
cache[key] = s_value
+ # add the attribute _dont_reach_me_in_del_ (see pypy.rpython.rclass)
+ try:
+ graph = self.bookkeeper.position_key[0]
+ graph.func._dont_reach_me_in_del_ = True
+ except (TypeError, AttributeError):
+ pass
+
return annmodel.s_None
def annotate_hooks(self, **kwds_s):
diff --git a/pypy/rlib/test/test_jit.py b/pypy/rlib/test/test_jit.py
--- a/pypy/rlib/test/test_jit.py
+++ b/pypy/rlib/test/test_jit.py
@@ -83,6 +83,9 @@
t, rtyper, fngraph = self.gengraph(fn, [int])
+ # added by compute_result_annotation()
+ assert fn._dont_reach_me_in_del_ == True
+
def getargs(func):
for graph in t.graphs:
if getattr(graph, 'func', None) is func:
diff --git a/pypy/rpython/lltypesystem/ll2ctypes.py b/pypy/rpython/lltypesystem/ll2ctypes.py
--- a/pypy/rpython/lltypesystem/ll2ctypes.py
+++ b/pypy/rpython/lltypesystem/ll2ctypes.py
@@ -172,17 +172,6 @@
assert max_n >= 0
ITEM = A.OF
ctypes_item = get_ctypes_type(ITEM, delayed_builders)
- # Python 2.5 ctypes can raise OverflowError on 64-bit builds
- for n in [sys.maxint, 2**31]:
- MAX_SIZE = n/64
- try:
- PtrType = ctypes.POINTER(MAX_SIZE * ctypes_item)
- except OverflowError, e:
- pass
- else:
- break
- else:
- raise e
class CArray(ctypes.Structure):
if not A._hints.get('nolength'):
@@ -191,6 +180,7 @@
else:
_fields_ = [('items', max_n * ctypes_item)]
+ @classmethod
def _malloc(cls, n=None):
if not isinstance(n, int):
raise TypeError, "array length must be an int"
@@ -199,10 +189,29 @@
if hasattr(bigarray, 'length'):
bigarray.length = n
return bigarray
- _malloc = classmethod(_malloc)
+
+ _ptrtype = None
+
+ @classmethod
+ def _get_ptrtype(cls):
+ if cls._ptrtype:
+ return cls._ptrtype
+ # ctypes can raise OverflowError on 64-bit builds
+ for n in [sys.maxint, 2**31]:
+ cls.MAX_SIZE = n/64
+ try:
+ cls._ptrtype = ctypes.POINTER(cls.MAX_SIZE * ctypes_item)
+ except OverflowError, e:
+ pass
+ else:
+ break
+ else:
+ raise e
+ return cls._ptrtype
def _indexable(self, index):
- assert index + 1 < MAX_SIZE
+ PtrType = self._get_ptrtype()
+ assert index + 1 < self.MAX_SIZE
p = ctypes.cast(ctypes.pointer(self.items), PtrType)
return p.contents
diff --git a/pypy/rpython/lltypesystem/rclass.py b/pypy/rpython/lltypesystem/rclass.py
--- a/pypy/rpython/lltypesystem/rclass.py
+++ b/pypy/rpython/lltypesystem/rclass.py
@@ -400,6 +400,7 @@
assert len(s_func.descriptions) == 1
funcdesc, = s_func.descriptions
graph = funcdesc.getuniquegraph()
+ self.check_graph_of_del_does_not_call_too_much(graph)
FUNCTYPE = FuncType([Ptr(source_repr.object_type)], Void)
destrptr = functionptr(FUNCTYPE, graph.name,
graph=graph,
diff --git a/pypy/rpython/lltypesystem/rstr.py b/pypy/rpython/lltypesystem/rstr.py
--- a/pypy/rpython/lltypesystem/rstr.py
+++ b/pypy/rpython/lltypesystem/rstr.py
@@ -486,6 +486,11 @@
return True
+ def ll_startswith_char(s, ch):
+ if not len(s.chars):
+ return False
+ return s.chars[0] == ch
+
@elidable
def ll_endswith(s1, s2):
len1 = len(s1.chars)
@@ -503,6 +508,11 @@
return True
+ def ll_endswith_char(s, ch):
+ if not len(s.chars):
+ return False
+ return s.chars[len(s.chars) - 1] == ch
+
@elidable
def ll_find_char(s, ch, start, end):
i = start
diff --git a/pypy/rpython/lltypesystem/test/test_ll2ctypes.py b/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
--- a/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
+++ b/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
@@ -671,7 +671,7 @@
assert not ALLOCATED # detects memory leaks in the test
def test_arrayofstruct(self):
- S1 = lltype.Struct('S1', ('x', lltype.Signed))
+ S1 = lltype.Struct('S2', ('x', lltype.Signed))
A = lltype.Array(S1, hints={'nolength': True})
a = lltype.malloc(A, 5, flavor='raw')
a[0].x = 100
diff --git a/pypy/rpython/memory/gc/minimark.py b/pypy/rpython/memory/gc/minimark.py
--- a/pypy/rpython/memory/gc/minimark.py
+++ b/pypy/rpython/memory/gc/minimark.py
@@ -256,10 +256,6 @@
# (may) contain a pointer to a young object. Populated by
# the write barrier: when we clear GCFLAG_TRACK_YOUNG_PTRS, we
# add it to this list.
- class Cls(self.AddressStack):
- def append(self2, addr):
- assert addr not in self2.tolist()
- self.AddressStack.append(self2, addr)
self.objects_pointing_to_young = self.AddressStack()
#
# Similar to 'objects_pointing_to_young', but lists objects
diff --git a/pypy/rpython/memory/gc/test/test_direct.py b/pypy/rpython/memory/gc/test/test_direct.py
--- a/pypy/rpython/memory/gc/test/test_direct.py
+++ b/pypy/rpython/memory/gc/test/test_direct.py
@@ -525,6 +525,7 @@
def test_writebarrier_before_copy(self):
from pypy.rpython.memory.gc import minimark
largeobj_size = self.gc.nonlarge_max + 1
+ self.gc.next_major_collection_threshold = 99999.0
p_src = self.malloc(VAR, largeobj_size)
p_dst = self.malloc(VAR, largeobj_size)
# make them old
@@ -564,6 +565,7 @@
from pypy.rpython.memory.gc import minimark
tid = self.get_type_id(VAR)
largeobj_size = self.gc.nonlarge_max + 1
+ self.gc.next_major_collection_threshold = 99999.0
addr_src = self.gc.external_malloc(tid, largeobj_size)
addr_dst = self.gc.external_malloc(tid, largeobj_size)
hdr_src = self.gc.header(addr_src)
diff --git a/pypy/rpython/ootypesystem/ootype.py b/pypy/rpython/ootypesystem/ootype.py
--- a/pypy/rpython/ootypesystem/ootype.py
+++ b/pypy/rpython/ootypesystem/ootype.py
@@ -433,7 +433,9 @@
"ll_streq": Meth([self.SELFTYPE_T], Bool),
"ll_strcmp": Meth([self.SELFTYPE_T], Signed),
"ll_startswith": Meth([self.SELFTYPE_T], Bool),
+ "ll_startswith_char": Meth([self.CHAR], Bool),
"ll_endswith": Meth([self.SELFTYPE_T], Bool),
+ "ll_endswith_char": Meth([self.CHAR], Bool),
"ll_find": Meth([self.SELFTYPE_T, Signed, Signed], Signed),
"ll_rfind": Meth([self.SELFTYPE_T, Signed, Signed], Signed),
"ll_count": Meth([self.SELFTYPE_T, Signed, Signed], Signed),
@@ -1429,10 +1431,18 @@
# NOT_RPYTHON
return self._str.startswith(s._str)
+ def ll_startswith_char(self, s):
+ # NOT_RPYTHON
+ return self._str.startswith(s)
+
def ll_endswith(self, s):
# NOT_RPYTHON
return self._str.endswith(s._str)
+ def ll_endswith_char(self, s):
+ # NOT_RPYTHON
+ return self._str.endswith(s)
+
def ll_find(self, s, start, end):
# NOT_RPYTHON
if start > len(self._str): # workaround to cope with corner case
diff --git a/pypy/rpython/rclass.py b/pypy/rpython/rclass.py
--- a/pypy/rpython/rclass.py
+++ b/pypy/rpython/rclass.py
@@ -374,6 +374,43 @@
def can_ll_be_null(self, s_value):
return s_value.can_be_none()
+ def check_graph_of_del_does_not_call_too_much(self, graph):
+ # RPython-level __del__() methods should not do "too much".
+ # In the PyPy Python interpreter, they usually do simple things
+ # like file.__del__() closing the file descriptor; or if they
+ # want to do more like call an app-level __del__() method, they
+ # enqueue the object instead, and the actual call is done later.
+ #
+ # Here, as a quick way to check "not doing too much", we check
+ # that from no RPython-level __del__() method we can reach a
+ # JitDriver.
+ #
+ # XXX wrong complexity, but good enough because the set of
+ # reachable graphs should be small
+ callgraph = self.rtyper.annotator.translator.callgraph.values()
+ seen = {graph: None}
+ while True:
+ oldlength = len(seen)
+ for caller, callee in callgraph:
+ if caller in seen and callee not in seen:
+ func = getattr(callee, 'func', None)
+ if getattr(func, '_dont_reach_me_in_del_', False):
+ lst = [str(callee)]
+ g = caller
+ while g:
+ lst.append(str(g))
+ g = seen.get(g)
+ lst.append('')
+ raise TyperError("the RPython-level __del__() method "
+ "in %r calls:%s" % (
+ graph, '\n\t'.join(lst[::-1])))
+ if getattr(func, '_cannot_really_call_random_things_',
+ False):
+ continue
+ seen[callee] = caller
+ if len(seen) == oldlength:
+ break
+
# ____________________________________________________________
def rtype_new_instance(rtyper, classdef, llops, classcallhop=None):
diff --git a/pypy/rpython/rlist.py b/pypy/rpython/rlist.py
--- a/pypy/rpython/rlist.py
+++ b/pypy/rpython/rlist.py
@@ -667,7 +667,6 @@
res = l.ll_getitem_fast(index)
ll_delitem_nonneg(dum_nocheck, l, index)
return res
-ll_pop.oopspec = 'list.pop(l, index)'
def ll_reverse(l):
length = l.ll_length()
diff --git a/pypy/rpython/rstr.py b/pypy/rpython/rstr.py
--- a/pypy/rpython/rstr.py
+++ b/pypy/rpython/rstr.py
@@ -81,16 +81,30 @@
return super(AbstractStringRepr, self).rtype_is_true(hop)
def rtype_method_startswith(self, hop):
- str1_repr, str2_repr = self._str_reprs(hop)
- v_str, v_value = hop.inputargs(str1_repr, str2_repr)
+ str1_repr = hop.args_r[0].repr
+ str2_repr = hop.args_r[1]
+ v_str = hop.inputarg(str1_repr, arg=0)
+ if str2_repr == str2_repr.char_repr:
+ v_value = hop.inputarg(str2_repr.char_repr, arg=1)
+ fn = self.ll.ll_startswith_char
+ else:
+ v_value = hop.inputarg(str2_repr, arg=1)
+ fn = self.ll.ll_startswith
hop.exception_cannot_occur()
- return hop.gendirectcall(self.ll.ll_startswith, v_str, v_value)
+ return hop.gendirectcall(fn, v_str, v_value)
def rtype_method_endswith(self, hop):
- str1_repr, str2_repr = self._str_reprs(hop)
- v_str, v_value = hop.inputargs(str1_repr, str2_repr)
+ str1_repr = hop.args_r[0].repr
+ str2_repr = hop.args_r[1]
+ v_str = hop.inputarg(str1_repr, arg=0)
+ if str2_repr == str2_repr.char_repr:
+ v_value = hop.inputarg(str2_repr.char_repr, arg=1)
+ fn = self.ll.ll_endswith_char
+ else:
+ v_value = hop.inputarg(str2_repr, arg=1)
+ fn = self.ll.ll_endswith
hop.exception_cannot_occur()
- return hop.gendirectcall(self.ll.ll_endswith, v_str, v_value)
+ return hop.gendirectcall(fn, v_str, v_value)
def rtype_method_find(self, hop, reverse=False):
# XXX binaryop
diff --git a/pypy/rpython/test/test_rclass.py b/pypy/rpython/test/test_rclass.py
--- a/pypy/rpython/test/test_rclass.py
+++ b/pypy/rpython/test/test_rclass.py
@@ -7,6 +7,7 @@
from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
from pypy.rpython.rclass import IR_IMMUTABLE, IR_IMMUTABLE_ARRAY
from pypy.rpython.rclass import IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY
+from pypy.rpython.error import TyperError
from pypy.objspace.flow.model import summary
class EmptyBase(object):
@@ -1022,6 +1023,24 @@
assert destrptra is not None
assert destrptrb is not None
+ def test_del_forbidden(self):
+ class A(object):
+ def __del__(self):
+ self.foo()
+ def foo(self):
+ self.bar()
+ def bar(self):
+ pass
+ bar._dont_reach_me_in_del_ = True
+ def f():
+ a = A()
+ a.foo()
+ a.bar()
+ t = TranslationContext()
+ t.buildannotator().build_types(f, [])
+ e = py.test.raises(TyperError, t.buildrtyper().specialize)
+ print e.value
+
def test_instance_repr(self):
from pypy.rlib.objectmodel import current_object_addr_as_int
class FooBar(object):
diff --git a/pypy/rpython/test/test_rstr.py b/pypy/rpython/test/test_rstr.py
--- a/pypy/rpython/test/test_rstr.py
+++ b/pypy/rpython/test/test_rstr.py
@@ -227,6 +227,15 @@
res = self.interpret(fn, [i,j])
assert res is fn(i, j)
+ def test_startswith_char(self):
+ const = self.const
+ def fn(i):
+ s = [const(''), const('one'), const('two'), const('o'), const('on'), const('ne'), const('e'), const('twos'), const('foobar'), const('fortytwo')]
+ return s[i].startswith(const('o'))
+ for i in range(10):
+ res = self.interpret(fn, [i])
+ assert res == fn(i)
+
def test_endswith(self):
const = self.const
def fn(i, j):
@@ -238,6 +247,15 @@
res = self.interpret(fn, [i,j])
assert res is fn(i, j)
+ def test_endswith_char(self):
+ const = self.const
+ def fn(i):
+ s = [const(''), const('one'), const('two'), const('o'), const('on'), const('ne'), const('e'), const('twos'), const('foobar'), const('fortytwo')]
+ return s[i].endswith(const('e'))
+ for i in range(10):
+ res = self.interpret(fn, [i])
+ assert res == fn(i)
+
def test_find(self):
const = self.const
def fn(i, j):
diff --git a/pypy/tool/jitlogparser/parser.py b/pypy/tool/jitlogparser/parser.py
--- a/pypy/tool/jitlogparser/parser.py
+++ b/pypy/tool/jitlogparser/parser.py
@@ -1,10 +1,13 @@
import re, sys
-from pypy.jit.metainterp.resoperation import rop, opname
+from pypy.jit.metainterp.resoperation import opname
from pypy.jit.tool.oparser import OpParser
+from pypy.tool.logparser import parse_log_file, extract_category
class Op(object):
bridge = None
+ offset = None
+ asm = None
def __init__(self, name, args, res, descr):
self.name = name
@@ -54,10 +57,53 @@
Op = Op
use_mock_model = True
+ def postprocess(self, loop, backend_dump=None, backend_tp=None,
+ dump_start=0):
+ if backend_dump is not None:
+ raw_asm = self._asm_disassemble(backend_dump.decode('hex'),
+ backend_tp, dump_start)
+ asm = []
+ start = 0
+ for elem in raw_asm:
+ if len(elem.split("\t")) != 3:
+ continue
+ adr, _, v = elem.split("\t")
+ if not start:
+ start = int(adr.strip(":"), 16)
+ ofs = int(adr.strip(":"), 16) - start
+ if ofs >= 0:
+ asm.append((ofs, v.strip("\n")))
+ asm_index = 0
+ for i, op in enumerate(loop.operations):
+ end = 0
+ j = i + 1
+ while end == 0:
+ if j == len(loop.operations):
+ end = loop.last_offset
+ break
+ if loop.operations[j].offset is None:
+ j += 1
+ else:
+ end = loop.operations[j].offset
+ if op.offset is not None:
+ while asm[asm_index][0] < op.offset:
+ asm_index += 1
+ end_index = asm_index
+ while asm[end_index][0] < end:
+ end_index += 1
+ op.asm = '\n'.join([asm[i][1] for i in range(asm_index, end_index)])
+ return loop
+
+ def _asm_disassemble(self, d, origin_addr, tp):
+ from pypy.jit.backend.x86.tool.viewcode import machine_code_dump
+ return list(machine_code_dump(d, tp, origin_addr))
+
@classmethod
- def parse_from_input(cls, input):
- return cls(input, None, {}, 'lltype', None,
- nonstrict=True).parse()
+ def parse_from_input(cls, input, **kwds):
+ parser = cls(input, None, {}, 'lltype', None,
+ nonstrict=True)
+ loop = parser.parse()
+ return parser.postprocess(loop, **kwds)
def parse_args(self, opname, argspec):
if not argspec.strip():
@@ -284,3 +330,46 @@
res.append(op)
i += 1
return res
+
+
+def import_log(logname, ParserCls=SimpleParser):
+ log = parse_log_file(logname)
+ addrs = {}
+ for entry in extract_category(log, 'jit-backend-addr'):
+ m = re.search('bootstrap ([\da-f]+)', entry)
+ if not m:
+ # a bridge
+ m = re.search('has address ([\da-f]+)', entry)
+ addr = int(m.group(1), 16)
+ entry = entry.lower()
+ m = re.search('guard \d+', entry)
+ addrs[addr] = m.group(0)
+ else:
+ name = entry[:entry.find('(') - 1].lower()
+ addrs[int(m.group(1), 16)] = name
+ dumps = {}
+ for entry in extract_category(log, 'jit-backend-dump'):
+ backend, _, dump, _ = entry.split("\n")
+ _, addr, _, data = re.split(" +", dump)
+ backend_name = backend.split(" ")[1]
+ addr = int(addr[1:], 16)
+ if addr in addrs:
+ dumps[addrs[addr]] = (backend_name, addr, data)
+ loops = []
+ for entry in extract_category(log, 'jit-log-opt'):
+ parser = ParserCls(entry, None, {}, 'lltype', None,
+ nonstrict=True)
+ loop = parser.parse()
+ comm = loop.comment
+ comm = comm.lower()
+ if comm.startswith('# bridge'):
+ m = re.search('guard \d+', comm)
+ name = m.group(0)
+ else:
+ name = comm[2:comm.find(':')-1]
+ if name in dumps:
+ bname, start_ofs, dump = dumps[name]
+ parser.postprocess(loop, backend_tp=bname, backend_dump=dump,
+ dump_start=start_ofs)
+ loops.append(loop)
+ return log, loops
diff --git a/pypy/tool/jitlogparser/test/logtest.log b/pypy/tool/jitlogparser/test/logtest.log
new file mode 100644
--- /dev/null
+++ b/pypy/tool/jitlogparser/test/logtest.log
@@ -0,0 +1,38 @@
+[11f210b47027] {jit-backend
+[11f210b900f7] {jit-backend-dump
+BACKEND x86_64
+SYS_EXECUTABLE python
+CODE_DUMP @7f3b0b2e63d5 +0 554889E5534154415541564157488DA500000000488B042590C5540148C7042590C554010000000048898570FFFFFF488B042598C5540148C7042598C554010000000048898568FFFFFF488B0425A0C5540148C70425A0C554010000000048898560FFFFFF488B0425A8C5540148C70425A8C554010000000048898558FFFFFF4C8B3C2550525B0149BB3050920D3B7F00004D8B334983C60149BB3050920D3B7F00004D89334981FF102700000F8D000000004983C7014C8B342580F76A024983EE014C89342580F76A024983FE000F8C00000000E9AEFFFFFF488B042588F76A024829E0483B042580EC3C01760D49BB05632E0B3B7F000041FFD3554889E5534154415541564157488DA550FFFFFF4889BD70FFFFFF4889B568FFFFFF48899560FFFFFF48898D58FFFFFF4D89C7E954FFFFFF49BB00602E0B3B7F000041FFD34440484C3D030300000049BB00602E0B3B7F000041FFD34440484C3D070304000000
+[11f210b949b3] jit-backend-dump}
+[11f210b949b4] {jit-backend-addr
+Loop 0 (<code object f, file 'x.py', line 2> #9 LOAD_FAST) has address 7f3b0b2e645d to 7f3b0b2e64af (bootstrap 7f3b0b2e63d5)
+[11f210bab188] jit-backend-addr}
+[11f210bab189] jit-backend}
+[11f210bacbb7] {jit-log-opt-loop
+# Loop 0 : loop with 19 ops
+[p0, p1, p2, p3, i4]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #9 LOAD_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #12 LOAD_CONST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #15 COMPARE_OP')
++166: i6 = int_lt(i4, 10000)
+guard_true(i6, descr=<Guard3>) [p1, p0, p2, p3, i4]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #18 POP_JUMP_IF_FALSE')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #21 LOAD_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #24 LOAD_CONST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #27 INPLACE_ADD')
++179: i8 = int_add(i4, 1)
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #28 STORE_FAST')
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #31 JUMP_ABSOLUTE')
++183: i10 = getfield_raw(40564608, descr=<SignedFieldDescr pypysig_long_struct.c_value 0>)
++191: i12 = int_sub(i10, 1)
++195: setfield_raw(40564608, i12, descr=<SignedFieldDescr pypysig_long_struct.c_value 0>)
++203: i14 = int_lt(i12, 0)
+guard_false(i14, descr=<Guard4>) [p1, p0, p2, p3, i8, None]
+debug_merge_point(0, '<code object f. file 'x.py'. line 2> #9 LOAD_FAST')
++213: jump(p0, p1, p2, p3, i8, descr=<Loop0>)
++218: --end of the loop--
+[11f210c17981] jit-log-opt-loop}
+[11f210fb1d21] {jit-backend-counts
+0:8965
+1:2
+[11f210fb381b] jit-backend-counts}
diff --git a/pypy/tool/jitlogparser/test/test_parser.py b/pypy/tool/jitlogparser/test/test_parser.py
--- a/pypy/tool/jitlogparser/test/test_parser.py
+++ b/pypy/tool/jitlogparser/test/test_parser.py
@@ -1,12 +1,11 @@
-from pypy.jit.metainterp.resoperation import ResOperation, rop
-from pypy.jit.metainterp.history import ConstInt, Const
-from pypy.tool.jitlogparser.parser import SimpleParser, TraceForOpcode, Function,\
- adjust_bridges
+from pypy.tool.jitlogparser.parser import (SimpleParser, TraceForOpcode,
+ Function, adjust_bridges,
+ import_log)
from pypy.tool.jitlogparser.storage import LoopStorage
-import py
+import py, sys
-def parse(input):
- return SimpleParser.parse_from_input(input)
+def parse(input, **kwds):
+ return SimpleParser.parse_from_input(input, **kwds)
def test_parse():
@@ -111,6 +110,8 @@
assert res.chunks[1].lineno == 3
def test_linerange():
+ if sys.version_info > (2, 6):
+ py.test.skip("unportable test")
fname = str(py.path.local(__file__).join('..', 'x.py'))
ops = parse('''
[i0, i1]
@@ -125,6 +126,8 @@
assert res.lineset == set([7, 8, 9])
def test_linerange_notstarts():
+ if sys.version_info > (2, 6):
+ py.test.skip("unportable test")
fname = str(py.path.local(__file__).join('..', 'x.py'))
ops = parse("""
[p6, p1]
@@ -179,3 +182,42 @@
ops = Function.from_operations(loop.operations, LoopStorage())
chunk = ops.chunks[0]
assert chunk.bytecode_name == 'StrLiteralSearch'
+
+def test_parsing_assembler():
+ backend_dump = "554889E5534154415541564157488DA500000000488B042590C5540148C7042590C554010000000048898570FFFFFF488B042598C5540148C7042598C554010000000048898568FFFFFF488B0425A0C5540148C70425A0C554010000000048898560FFFFFF488B0425A8C5540148C70425A8C554010000000048898558FFFFFF4C8B3C2550525B0149BB30E06C96FC7F00004D8B334983C60149BB30E06C96FC7F00004D89334981FF102700000F8D000000004983C7014C8B342580F76A024983EE014C89342580F76A024983FE000F8C00000000E9AEFFFFFF488B042588F76A024829E0483B042580EC3C01760D49BB05F30894FC7F000041FFD3554889E5534154415541564157488DA550FFFFFF4889BD70FFFFFF4889B568FFFFFF48899560FFFFFF48898D58FFFFFF4D89C7E954FFFFFF49BB00F00894FC7F000041FFD34440484C3D030300000049BB00F00894FC7F000041FFD34440484C3D070304000000"
+ dump_start = 0x7f3b0b2e63d5
+ loop = parse("""
+ # Loop 0 : loop with 19 ops
+ [p0, p1, p2, p3, i4]
+ debug_merge_point(0, '<code object f. file 'x.py'. line 2> #15 COMPARE_OP')
+ +166: i6 = int_lt(i4, 10000)
+ guard_true(i6, descr=<Guard3>) [p1, p0, p2, p3, i4]
+ debug_merge_point(0, '<code object f. file 'x.py'. line 2> #27 INPLACE_ADD')
+ +179: i8 = int_add(i4, 1)
+ debug_merge_point(0, '<code object f. file 'x.py'. line 2> #31 JUMP_ABSOLUTE')
+ +183: i10 = getfield_raw(40564608, descr=<SignedFieldDescr pypysig_long_struct.c_value 0>)
+ +191: i12 = int_sub(i10, 1)
+ +195: setfield_raw(40564608, i12, descr=<SignedFieldDescr pypysig_long_struct.c_value 0>)
+ +203: i14 = int_lt(i12, 0)
+ guard_false(i14, descr=<Guard4>) [p1, p0, p2, p3, i8, None]
+ debug_merge_point(0, '<code object f. file 'x.py'. line 2> #9 LOAD_FAST')
+ +213: jump(p0, p1, p2, p3, i8, descr=<Loop0>)
+ +218: --end of the loop--""", backend_dump=backend_dump,
+ dump_start=dump_start,
+ backend_tp='x86_64')
+ cmp = loop.operations[1]
+ assert 'jge' in cmp.asm
+ assert '0x2710' in cmp.asm
+ assert 'jmp' in loop.operations[-1].asm
+
+def test_import_log():
+ _, loops = import_log(str(py.path.local(__file__).join('..',
+ 'logtest.log')))
+ assert 'jge' in loops[0].operations[3].asm
+
+def test_import_log_2():
+ _, loops = import_log(str(py.path.local(__file__).join('..',
+ 'logtest2.log')))
+ assert 'cmp' in loops[1].operations[1].asm
+ # bridge
+ assert 'cmp' in loops[3].operations[1].asm
diff --git a/pypy/translator/c/genc.py b/pypy/translator/c/genc.py
--- a/pypy/translator/c/genc.py
+++ b/pypy/translator/c/genc.py
@@ -688,11 +688,37 @@
def getothernodes(self):
return self.othernodes[:]
+ def getbasecfilefornode(self, node, basecname):
+ # For FuncNode instances, use the python source filename (relative to
+ # the top directory):
+ if hasattr(node.obj, 'graph'):
+ g = node.obj.graph
+ # Lookup the filename from the function.
+ # However, not all FunctionGraph objs actually have a "func":
+ if hasattr(g, 'func'):
+ if g.filename.endswith('.py'):
+ localpath = py.path.local(g.filename)
+ pypkgpath = localpath.pypkgpath()
+ if pypkgpath:
+ relpypath = localpath.relto(pypkgpath)
+ return relpypath.replace('.py', '.c')
+ return basecname
+
def splitnodesimpl(self, basecname, nodes, nextra, nbetween,
split_criteria=SPLIT_CRITERIA):
+ # Gather nodes by some criteria:
+ nodes_by_base_cfile = {}
+ for node in nodes:
+ c_filename = self.getbasecfilefornode(node, basecname)
+ if c_filename in nodes_by_base_cfile:
+ nodes_by_base_cfile[c_filename].append(node)
+ else:
+ nodes_by_base_cfile[c_filename] = [node]
+
# produce a sequence of nodes, grouped into files
# which have no more than SPLIT_CRITERIA lines
- iternodes = iter(nodes)
+ for basecname in nodes_by_base_cfile:
+ iternodes = iter(nodes_by_base_cfile[basecname])
done = [False]
def subiter():
used = nextra
diff --git a/pypy/translator/c/test/test_standalone.py b/pypy/translator/c/test/test_standalone.py
--- a/pypy/translator/c/test/test_standalone.py
+++ b/pypy/translator/c/test/test_standalone.py
@@ -55,6 +55,13 @@
data = cbuilder.cmdexec('hi there')
assert data.startswith('''hello world\nargument count: 2\n 'hi'\n 'there'\n''')
+ # Verify that the generated C files have sane names:
+ gen_c_files = [str(f) for f in cbuilder.extrafiles]
+ for expfile in ('rlib_rposix.c',
+ 'rpython_lltypesystem_rstr.c',
+ 'translator_c_test_test_standalone.c'):
+ assert cbuilder.targetdir.join(expfile) in gen_c_files
+
def test_print(self):
def entry_point(argv):
print "hello simpler world"
diff --git a/pypy/translator/cli/src/pypylib.cs b/pypy/translator/cli/src/pypylib.cs
--- a/pypy/translator/cli/src/pypylib.cs
+++ b/pypy/translator/cli/src/pypylib.cs
@@ -615,11 +615,29 @@
return s1.StartsWith(s2);
}
+ public static bool ll_startswith_char(string s, char c)
+ {
+ if (s.Length == 0)
+ {
+ return false;
+ }
+ return s[0] == c;
+ }
+
public static bool ll_endswith(string s1, string s2)
{
return s1.EndsWith(s2);
}
+ public static bool ll_endswith_char(string s, char c)
+ {
+ if (s.Length == 0)
+ {
+ return false;
+ }
+ return s[s.Length - 1] == c;
+ }
+
public static int ll_find(string s1, string s2, int start, int stop)
{
if (stop > s1.Length)
diff --git a/pypy/translator/jvm/src/pypy/PyPy.java b/pypy/translator/jvm/src/pypy/PyPy.java
--- a/pypy/translator/jvm/src/pypy/PyPy.java
+++ b/pypy/translator/jvm/src/pypy/PyPy.java
@@ -791,6 +791,20 @@
return str.substring(start,start+cnt);
}
+ public static boolean ll_startswith_char(String str, char c) {
+ if (str.length() == 0) {
+ return false;
+ }
+ return str.charAt(0) == c;
+ }
+
+ public static boolean ll_endswith_char(String str, char c) {
+ if (str.length() == 0) {
+ return false;
+ }
+ return str.charAt(str.length() - 1) == c;
+ }
+
// ----------------------------------------------------------------------
// StringBuffer
More information about the pypy-commit
mailing list