Commits

Alex Gaynor  committed 15c56a2 Merge

Merged upstream.

  • Participants
  • Parent commits 6f9e0f4, 4f17c98

Comments (0)

Files changed (33)

File pypy/annotation/bookkeeper.py

         desc = self.getdesc(cls)
         return desc.getuniqueclassdef()
 
-    def getlistdef(self, **flags):
+    def getlistdef(self, **flags_if_new):
         """Get the ListDef associated with the current position."""
         try:
             listdef = self.listdefs[self.position_key]
         except KeyError:
             listdef = self.listdefs[self.position_key] = ListDef(self)
-            listdef.listitem.__dict__.update(flags)
+            listdef.listitem.__dict__.update(flags_if_new)
         return listdef
 
     def newlist(self, *s_values, **flags):
         listdef = self.getlistdef(**flags)
         for s_value in s_values:
             listdef.generalize(s_value)
+        if flags:
+            assert flags.keys() == ['range_step']
+            listdef.generalize_range_step(flags['range_step'])
         return SomeList(listdef)
 
     def getdictdef(self, is_r_dict=False):

File pypy/annotation/listdef.py

     def generalize(self, s_value):
         self.listitem.generalize(s_value)
 
+    def generalize_range_step(self, range_step):
+        newlistitem = ListItem(self.listitem.bookkeeper, s_ImpossibleValue)
+        newlistitem.range_step = range_step
+        self.listitem.merge(newlistitem)
+
     def __repr__(self):
         return '<[%r]%s%s%s%s>' % (self.listitem.s_value,
                                self.listitem.mutated and 'm' or '',

File pypy/annotation/test/test_annrpython.py

         a = self.RPythonAnnotator()
         raises(Exception, a.build_types, f, [int])
 
+    def test_range_variable_step(self):
+        def g(n):
+            return range(0, 10, n)
+        def f(n):
+            r = g(1)    # constant step, at first
+            s = g(n)    # but it becomes a variable step
+            return r
+        a = self.RPythonAnnotator()
+        s = a.build_types(f, [int])
+        assert s.listdef.listitem.range_step == 0
+
 
 def g(n):
     return [0,1,2,n]

File pypy/doc/project-ideas.rst

 
 * alternatively, look at Software Transactional Memory
 
+Introduce new benchmarks
+------------------------
+
+We're usually happy to introduce new benchmarks. Please consult us
+before, but in general something that's real-world python code
+and is not already represented is welcome. We need at least a standalone
+script that can run without parameters. Example ideas (benchmarks need
+to be got from them!):
+
+* `hg`
+
+* `sympy`
+
 Experiment (again) with LLVM backend for RPython compilation
 ------------------------------------------------------------
 

File pypy/jit/metainterp/logger.py

     def repr_of_resop(self, op, ops_offset=None):
         if op.getopnum() == rop.DEBUG_MERGE_POINT:
             jd_sd = self.metainterp_sd.jitdrivers_sd[op.getarg(0).getint()]
-            s = jd_sd.warmstate.get_location_str(op.getarglist()[1:])
-            return "debug_merge_point('%s')" % (s,)
+            s = jd_sd.warmstate.get_location_str(op.getarglist()[2:])
+            return "debug_merge_point(%d, '%s')" % (op.getarg(1).getint(), s)
         if ops_offset is None:
             offset = -1
         else:

File pypy/jit/metainterp/pyjitpl.py

 
     def debug_merge_point(self, jd_index, in_recursion, greenkey):
         # debugging: produce a DEBUG_MERGE_POINT operation
-        self.metainterp.history.record(rop.DEBUG_MERGE_POINT,
-                                       [ConstInt(jd_index)] + greenkey, None)
+        args = [ConstInt(jd_index), ConstInt(in_recursion)] + greenkey
+        self.metainterp.history.record(rop.DEBUG_MERGE_POINT, args, None)
 
     @arguments("box", "label")
     def opimpl_goto_if_exception_mismatch(self, vtablebox, next_exc_target):

File pypy/jit/metainterp/test/test_logger.py

     def test_debug_merge_point(self):
         inp = '''
         []
-        debug_merge_point(0, "dupa")
+        debug_merge_point(0, 0, "dupa")
         '''
         _, loop, oloop = self.reparse(inp, check_equal=False)
-        assert loop.operations[0].getarg(1)._get_str() == "dupa"
-        assert oloop.operations[0].getarg(0)._get_str() == "dupa"
+        assert loop.operations[0].getarg(2)._get_str() == "dupa"
+        assert oloop.operations[0].getarg(1)._get_str() == "dupa"
         
     def test_floats(self):
         inp = '''

File pypy/jit/metainterp/test/test_warmspot.py

         self.meta_interp(f, [123, 10])
         assert len(get_stats().locations) >= 4
         for loc in get_stats().locations:
-            assert loc == (123,)
+            assert loc == (0, 123)
 
     def test_set_param_enable_opts(self):
         from pypy.rpython.annlowlevel import llstr, hlstr

File pypy/jit/tool/oparser.py

         descr = None
         if argspec.strip():
             if opname == 'debug_merge_point':
-                allargs = argspec.split(', ', 1)
+                allargs = argspec.split(',', 2)
             else:
                 allargs = [arg for arg in argspec.split(",")
                            if arg != '']

File pypy/jit/tool/pypytrace-mode.el

 (defun set-truncate-lines ()
   (setq truncate-lines t))
 
+;; to generate the list of keywords:
+;; from pypy.jit.metainterp import resoperation
+;; print ' '.join(sorted('"%s"' % op.lower() for op in resoperation.opname.values() if not op.startswith('GUARD')))
+
+
+
 (define-generic-mode 
   'pypytrace-mode                   ;; name of the mode to create
   nil
-  '("jump" "finish" "int_add" "int_sub" "int_mul" "int_floordiv" "uint_floordiv" "int_mod" "int_and" "int_or" "int_xor" "int_rshift" "int_lshift" "uint_rshift" "float_add" "float_sub" "float_mul" "float_truediv" "float_neg" "float_abs" "cast_float_to_int" "cast_int_to_float" "int_lt" "int_le" "int_eq" "int_ne" "int_gt" "int_ge" "uint_lt" "uint_le" "uint_gt" "uint_ge" "float_lt" "float_le" "float_eq" "float_ne" "float_gt" "float_ge" "int_is_zero" "int_is_true" "int_neg" "int_invert" "same_as" "ptr_eq" "ptr_ne" "arraylen_gc" "strlen" "strgetitem" "getfield_gc_pure" "getfield_raw_pure" "getarrayitem_gc_pure" "unicodelen" "unicodegetitem" "getarrayitem_gc" "getarrayitem_raw" "getfield_gc" "getfield_raw" "new" "new_with_vtable" "new_array" "force_token" "virtual_ref" "setarrayitem_gc" "setarrayitem_raw" "setfield_gc" "setfield_raw" "arraycopy" "newstr" "strsetitem" "unicodesetitem" "newunicode" "cond_call_gc_wb" "virtual_ref_finish" "call" "call_assembler" "call_may_force" "call_loopinvariant" "call_pure" "int_add_ovf" "int_sub_ovf" "int_mul_ovf") ;; keywords
+  '("arraylen_gc" "call" "call_assembler" "call_loopinvariant" "call_may_force" "call_pure" "call_release_gil" "cast_float_to_int" "cast_int_to_float" "cond_call_gc_wb" "copystrcontent" "copyunicodecontent" "debug_merge_point" "finish" "float_abs" "float_add" "float_eq" "float_ge" "float_gt" "float_le" "float_lt" "float_mul" "float_ne" "float_neg" "float_sub" "float_truediv" "force_token" "getarrayitem_gc" "getarrayitem_gc_pure" "getarrayitem_raw" "getfield_gc" "getfield_gc_pure" "getfield_raw" "getfield_raw_pure" "int_add" "int_add_ovf" "int_and" "int_eq" "int_floordiv" "int_ge" "int_gt" "int_invert" "int_is_true" "int_is_zero" "int_le" "int_lshift" "int_lt" "int_mod" "int_mul" "int_mul_ovf" "int_ne" "int_neg" "int_or" "int_rshift" "int_sub" "int_sub_ovf" "int_xor" "jit_debug" "jump" "new" "new_array" "new_with_vtable" "newstr" "newunicode" "ptr_eq" "ptr_ne" "quasiimmut_field" "read_timestamp" "same_as" "setarrayitem_gc" "setarrayitem_raw" "setfield_gc" "setfield_raw" "strgetitem" "strlen" "strsetitem" "uint_floordiv" "uint_ge" "uint_gt" "uint_le" "uint_lt" "uint_rshift" "unicodegetitem" "unicodelen" "unicodesetitem" "virtual_ref" "virtual_ref_finish") ;; keywords
   '( ;; additional regexps
     ("^# Loop.*" . 'hi-blue)
     ("\\[.*\\]" . 'font-lock-comment-face) ;; comment out argument lists

File pypy/jit/tool/test/test_oparser.py

     x = '''
     []
     debug_merge_point(0, "info")
-    debug_merge_point(1, 'info')
+    debug_merge_point(0, 'info')
     debug_merge_point(1, '<some ('other,')> info')
-    debug_merge_point(1, '(stuff) #1')
+    debug_merge_point(0, '(stuff) #1')
     '''
     loop = parse(x)
     assert loop.operations[0].getarg(1)._get_str() == 'info'

File pypy/module/__builtin__/__init__.py

 
         'apply'         : 'app_functional.apply',
         'sorted'        : 'app_functional.sorted',
+        'any'           : 'app_functional.any',
+        'all'           : 'app_functional.all',
         'vars'          : 'app_inspect.vars',
         'dir'           : 'app_inspect.dir',
 
         'range'         : 'functional.range_int',
         'xrange'        : 'functional.W_XRange',
         'enumerate'     : 'functional.W_Enumerate',
-        'all'           : 'functional.all',
-        'any'           : 'functional.any',
         'min'           : 'functional.min',
         'max'           : 'functional.max',
         'sum'           : 'functional.sum',

File pypy/module/__builtin__/app_functional.py

     sorted_lst = list(lst)
     sorted_lst.sort(cmp, key, reverse)
     return sorted_lst
+
+def any(seq):
+    """any(iterable) -> bool
+
+Return True if bool(x) is True for any x in the iterable."""
+    for x in seq:
+        if x:
+            return True
+    return False
+
+def all(seq):
+    """all(iterable) -> bool
+
+Return True if bool(x) is True for all values x in the iterable."""
+    for x in seq:
+        if not x:
+            return False
+    return True

File pypy/module/__builtin__/functional.py

     w_empty = space.call_function(w_str_type)
     return space.call_method(w_empty, "join", space.newlist(result_w))
 
-def all(space, w_S):
-    """all(iterable) -> bool
-
-Return True if bool(x) is True for all values x in the iterable."""
-    w_iter = space.iter(w_S)
-    while True:
-        try:
-            w_next = space.next(w_iter)
-        except OperationError, e:
-            if not e.match(space, space.w_StopIteration):
-                raise       # re-raise other app-level exceptions
-            break
-        if not space.is_true(w_next):
-            return space.w_False
-    return space.w_True
-
-
-def any(space, w_S):
-    """any(iterable) -> bool
-
-Return True if bool(x) is True for any x in the iterable."""
-    w_iter = space.iter(w_S)
-    while True:
-        try:
-            w_next = space.next(w_iter)
-        except OperationError, e:
-            if not e.match(space, space.w_StopIteration):
-                raise       # re-raise other app-level exceptions
-            break
-        if space.is_true(w_next):
-            return space.w_True
-    return space.w_False
-
-
 class W_Enumerate(Wrappable):
 
     def __init__(self, w_iter, w_start):

File pypy/module/pypyjit/interp_jit.py

 from pypy.interpreter.baseobjspace import ObjSpace, W_Root
 from opcode import opmap
 from pypy.rlib.objectmodel import we_are_translated
+from pypy.rlib.nonconst import NonConstant
 
 PyFrame._virtualizable2_ = ['last_instr', 'pycode',
                             'valuestackdepth', 'valuestack_w[*]',
         
         space = self.space
         cache = space.fromcache(Cache)
+        if cache.in_recursion:
+            return
         if space.is_true(cache.w_compile_hook):
             logops = logger._make_log_operations()
             list_w = [space.wrap(logops.repr_of_resop(op))
                       for op in operations]
             pycode = cast_base_ptr_to_instance(PyCode, ll_pycode)
+            cache.in_recursion = True
             try:
                 space.call_function(cache.w_compile_hook,
                                     space.wrap('main'),
                                     space.newlist(list_w))
             except OperationError, e:
                 e.write_unraisable(space, "jit hook ", cache.w_compile_hook)
+            cache.in_recursion = False
 
     def on_compile_bridge(self, logger, orig_looptoken, operations, n):
         space = self.space
         cache = space.fromcache(Cache)
+        if cache.in_recursion:
+            return
         if space.is_true(cache.w_compile_hook):
             logops = logger._make_log_operations()
             list_w = [space.wrap(logops.repr_of_resop(op))
                       for op in operations]
+            cache.in_recursion = True
             try:
                 space.call_function(cache.w_compile_hook,
                                     space.wrap('main'),
                                     space.newlist(list_w))
             except OperationError, e:
                 e.write_unraisable(space, "jit hook ", cache.w_compile_hook)
+            cache.in_recursion = False
 
 pypyjitdriver = PyPyJitDriver(get_printable_location = get_printable_location,
                               get_jitcell_at = get_jitcell_at,
     return space.call_args(w_callable, __args__)
 
 class Cache(object):
+    in_recursion = False
+    
     def __init__(self, space):
         self.w_compile_hook = space.w_None
 
     for jit merge point. in case it's `main` it'll be a tuple
     (code, offset, is_being_profiled)
 
+    Note that jit hook is not reentrant. It means that if the code
+    inside the jit hook is itself jitted, it will get compiled, but the
+    jit hook won't be called for that.
+
     XXX write down what else
     """
     cache = space.fromcache(Cache)
     cache.w_compile_hook = w_hook
+    cache.in_recursion = NonConstant(False)
     return space.w_None

File pypy/module/pypyjit/test/test_jit_hook.py

             sys.stderr = prev
         assert 'jit hook' in s.getvalue()
         assert 'ZeroDivisionError' in s.getvalue()
+
+    def test_non_reentrant(self):
+        import pypyjit
+        l = []
+        
+        def hook(*args):
+            l.append(None)
+            self.on_compile()
+            self.on_compile_bridge()
+        
+        pypyjit.set_compile_hook(hook)
+        self.on_compile()
+        assert len(l) == 1 # and did not crash
+        self.on_compile_bridge()
+        assert len(l) == 2 # and did not crash
+        

File pypy/module/pypyjit/test_pypy_c/test_00_model.py

+import sys
+import types
+import subprocess
+import py
+from lib_pypy import disassembler
+from pypy.tool.udir import udir
+from pypy.tool import logparser
+from pypy.jit.tool.jitoutput import parse_prof
+from pypy.module.pypyjit.test_pypy_c.model import Log, find_ids_range, find_ids, \
+    LoopWithIds, OpMatcher
+
+class BaseTestPyPyC(object):
+    def setup_class(cls):
+        if '__pypy__' not in sys.builtin_module_names:
+            py.test.skip("must run this test with pypy")
+        if not sys.pypy_translation_info['translation.jit']:
+            py.test.skip("must give a pypy-c with the jit enabled")
+        cls.tmpdir = udir.join('test-pypy-jit')
+        cls.tmpdir.ensure(dir=True)
+
+    def setup_method(self, meth):
+        self.filepath = self.tmpdir.join(meth.im_func.func_name + '.py')
+
+    def run(self, func_or_src, args=[], import_site=False, **jitopts):
+        jitopts.setdefault('threshold', 200)
+        src = py.code.Source(func_or_src)
+        if isinstance(func_or_src, types.FunctionType):
+            funcname = func_or_src.func_name
+        else:
+            funcname = 'main'
+        # write the snippet
+        arglist = ', '.join(map(repr, args))
+        with self.filepath.open("w") as f:
+            # we don't want to see the small bridges created
+            # by the checkinterval reaching the limit
+            f.write("import sys\n")
+            f.write("sys.setcheckinterval(10000000)\n")
+            f.write(str(src) + "\n")
+            f.write("print %s(%s)\n" % (funcname, arglist))
+        #
+        # run a child pypy-c with logging enabled
+        logfile = self.filepath.new(ext='.log')
+        #
+        cmdline = [sys.executable]
+        if not import_site:
+            cmdline.append('-S')
+        for key, value in jitopts.iteritems():
+            cmdline += ['--jit', '%s=%s' % (key, value)]
+        cmdline.append(str(self.filepath))
+        #
+        print cmdline, logfile
+        env={'PYPYLOG': 'jit-log-opt,jit-summary:' + str(logfile)}
+        #env={'PYPYLOG': ':' + str(logfile)}
+        pipe = subprocess.Popen(cmdline,
+                                env=env,
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE)
+        stdout, stderr = pipe.communicate()
+        if stderr.startswith('SKIP:'):
+            py.test.skip(stderr)
+        assert not stderr
+        #
+        # parse the JIT log
+        rawlog = logparser.parse_log_file(str(logfile))
+        rawtraces = logparser.extract_category(rawlog, 'jit-log-opt-')
+        log = Log(rawtraces)
+        log.result = eval(stdout)
+        #
+        summaries  = logparser.extract_category(rawlog, 'jit-summary')
+        if len(summaries) > 0:
+            log.jit_summary = parse_prof(summaries[-1])
+        else:
+            log.jit_summary = None
+        #
+        return log
+
+    def run_and_check(self, src, args=[], **jitopts):
+        log1 = self.run(src, args, threshold=-1)  # without the JIT
+        log2 = self.run(src, args, **jitopts)     # with the JIT
+        assert log1.result == log2.result
+        # check that the JIT actually ran
+        assert len(log2.loops_by_filename(self.filepath)) > 0
+
+
+class TestLog(object):
+
+    def test_find_ids_range(self):
+        def f():
+            a = 0 # ID: myline
+            return a
+        #
+        start_lineno = f.func_code.co_firstlineno
+        code = disassembler.dis(f)
+        ids = find_ids_range(code)
+        assert len(ids) == 1
+        myline_range = ids['myline']
+        assert list(myline_range) == range(start_lineno+1, start_lineno+2)
+
+    def test_find_ids(self):
+        def f():
+            i = 0
+            x = 0
+            z = x + 3 # ID: myline
+            return z
+        #
+        code = disassembler.dis(f)
+        ids = find_ids(code)
+        assert len(ids) == 1
+        myline = ids['myline']
+        opcodes_names = [opcode.__class__.__name__ for opcode in myline]
+        assert opcodes_names == ['LOAD_FAST', 'LOAD_CONST', 'BINARY_ADD', 'STORE_FAST']
+
+
+class TestOpMatcher(object):
+
+    def match(self, src1, src2, **kwds):
+        from pypy.tool.jitlogparser.parser import SimpleParser
+        loop = SimpleParser.parse_from_input(src1)
+        matcher = OpMatcher(loop.operations, src=src1)
+        return matcher.match(src2, **kwds)
+
+    def test_match_var(self):
+        match_var = OpMatcher([]).match_var
+        assert match_var('v0', 'V0')
+        assert not match_var('v0', 'V1')
+        assert match_var('v0', 'V0')
+        #
+        # for ConstPtr, we allow the same alpha-renaming as for variables
+        assert match_var('ConstPtr(ptr0)', 'PTR0')
+        assert not match_var('ConstPtr(ptr0)', 'PTR1')
+        assert match_var('ConstPtr(ptr0)', 'PTR0')
+        #
+        # for ConstClass, we want the exact matching
+        assert match_var('ConstClass(foo)', 'ConstClass(foo)')
+        assert not match_var('ConstClass(bar)', 'v1')
+        assert not match_var('v2', 'ConstClass(baz)')
+        #
+        # the var '_' matches everything (but only on the right, of course)
+        assert match_var('v0', '_')
+        assert match_var('v0', 'V0')
+        assert match_var('ConstPtr(ptr0)', '_')
+        py.test.raises(AssertionError, "match_var('_', 'v0')")
+
+    def test_parse_op(self):
+        res = OpMatcher.parse_op("  a =   int_add(  b,  3 ) # foo")
+        assert res == ("int_add", "a", ["b", "3"], None)
+        res = OpMatcher.parse_op("guard_true(a)")
+        assert res == ("guard_true", None, ["a"], None)
+        res = OpMatcher.parse_op("setfield_gc(p0, i0, descr=<foobar>)")
+        assert res == ("setfield_gc", None, ["p0", "i0"], "<foobar>")
+        res = OpMatcher.parse_op("i1 = getfield_gc(p0, descr=<foobar>)")
+        assert res == ("getfield_gc", "i1", ["p0"], "<foobar>")
+        res = OpMatcher.parse_op("p0 = force_token()")
+        assert res == ("force_token", "p0", [], None)
+
+    def test_exact_match(self):
+        loop = """
+            [i0]
+            i2 = int_add(i0, 1)
+            jump(i2)
+        """
+        expected = """
+            i5 = int_add(i2, 1)
+            jump(i5, descr=...)
+        """
+        assert self.match(loop, expected)
+        #
+        expected = """
+            i5 = int_sub(i2, 1)
+            jump(i5, descr=...)
+        """
+        assert not self.match(loop, expected)
+        #
+        expected = """
+            i5 = int_add(i2, 1)
+            jump(i5, descr=...)
+            extra_stuff(i5)
+        """
+        assert not self.match(loop, expected)
+        #
+        expected = """
+            i5 = int_add(i2, 1)
+            # missing op at the end
+        """
+        assert not self.match(loop, expected)
+
+    def test_match_descr(self):
+        loop = """
+            [p0]
+            setfield_gc(p0, 1, descr=<foobar>)
+        """
+        assert self.match(loop, "setfield_gc(p0, 1, descr=<foobar>)")
+        assert self.match(loop, "setfield_gc(p0, 1, descr=...)")
+        assert self.match(loop, "setfield_gc(p0, 1, descr=<.*bar>)")
+        assert not self.match(loop, "setfield_gc(p0, 1)")
+        assert not self.match(loop, "setfield_gc(p0, 1, descr=<zzz>)")
+
+
+    def test_partial_match(self):
+        loop = """
+            [i0]
+            i1 = int_add(i0, 1)
+            i2 = int_sub(i1, 10)
+            i3 = int_floordiv(i2, 100)
+            i4 = int_mul(i1, 1000)
+            jump(i4)
+        """
+        expected = """
+            i1 = int_add(0, 1)
+            ...
+            i4 = int_mul(i1, 1000)
+            jump(i4, descr=...)
+        """
+        assert self.match(loop, expected)
+
+    def test_partial_match_is_non_greedy(self):
+        loop = """
+            [i0]
+            i1 = int_add(i0, 1)
+            i2 = int_sub(i1, 10)
+            i3 = int_mul(i2, 1000)
+            i4 = int_mul(i1, 1000)
+            jump(i4, descr=...)
+        """
+        expected = """
+            i1 = int_add(0, 1)
+            ...
+            _ = int_mul(_, 1000)
+            jump(i4, descr=...)
+        """
+        # this does not match, because the ... stops at the first int_mul, and
+        # then the second one does not match
+        assert not self.match(loop, expected)
+
+    def test_partial_match_at_the_end(self):
+        loop = """
+            [i0]
+            i1 = int_add(i0, 1)
+            i2 = int_sub(i1, 10)
+            i3 = int_floordiv(i2, 100)
+            i4 = int_mul(i1, 1000)
+            jump(i4)
+        """
+        expected = """
+            i1 = int_add(0, 1)
+            ...
+        """
+        assert self.match(loop, expected)
+
+    def test_ignore_opcodes(self):
+        loop = """
+            [i0]
+            i1 = int_add(i0, 1)
+            i4 = force_token()
+            i2 = int_sub(i1, 10)
+            jump(i4)
+        """
+        expected = """
+            i1 = int_add(i0, 1)
+            i2 = int_sub(i1, 10)
+            jump(i4, descr=...)
+        """
+        assert self.match(loop, expected, ignore_ops=['force_token'])
+
+    def test_match_dots_in_arguments(self):
+        loop = """
+            [i0]
+            i1 = int_add(0, 1)
+            jump(i4, descr=...)
+        """
+        expected = """
+            i1 = int_add(...)
+            jump(i4, descr=...)
+        """
+        assert self.match(loop, expected)
+
+
+class TestRunPyPyC(BaseTestPyPyC):
+
+    def test_run_function(self):
+        def f(a, b):
+            return a+b
+        log = self.run(f, [30, 12])
+        assert log.result == 42
+
+    def test_run_src(self):
+        src = """
+            def f(a, b):
+                return a+b
+            def main(a, b):
+                return f(a, b)
+        """
+        log = self.run(src, [30, 12])
+        assert log.result == 42
+
+    def test_skip(self):
+        import pytest
+        def f():
+            import sys
+            print >> sys.stderr, 'SKIP: foobar'
+        #
+        raises(pytest.skip.Exception, "self.run(f, [])")
+
+    def test_parse_jitlog(self):
+        def f():
+            i = 0
+            while i < 1003:
+                i += 1
+            return i
+        #
+        log = self.run(f)
+        assert log.result == 1003
+        loops = log.loops_by_filename(self.filepath)
+        assert len(loops) == 1
+        assert loops[0].filename == self.filepath
+        assert not loops[0].is_entry_bridge
+        #
+        loops = log.loops_by_filename(self.filepath, is_entry_bridge=True)
+        assert len(loops) == 1
+        assert loops[0].is_entry_bridge
+        #
+        loops = log.loops_by_filename(self.filepath, is_entry_bridge='*')
+        assert len(loops) == 2
+
+    def test_loops_by_id(self):
+        def f():
+            i = 0
+            while i < 1003:
+                i += 1 # ID: increment
+            return i
+        #
+        log = self.run(f)
+        loop, = log.loops_by_id('increment')
+        assert loop.filename == self.filepath
+        assert loop.code.co.co_name == 'f'
+        #
+        ops = loop.allops()
+        assert log.opnames(ops) == [
+            # this is the actual loop
+            'int_lt', 'guard_true', 'int_add',
+            # this is the signal checking stuff
+            'getfield_raw', 'int_sub', 'setfield_raw', 'int_lt', 'guard_false',
+            'jump'
+            ]
+
+    def test_ops_by_id(self):
+        def f():
+            i = 0
+            while i < 1003: # ID: cond
+                i += 1 # ID: increment
+                a = 0  # to make sure that JUMP_ABSOLUTE is not part of the ID
+            return i
+        #
+        log = self.run(f)
+        loop, = log.loops_by_id('increment')
+        #
+        ops = loop.ops_by_id('increment')
+        assert log.opnames(ops) == ['int_add']
+        #
+        ops = loop.ops_by_id('cond')
+        # the 'jump' at the end is because the last opcode in the loop
+        # coincides with the first, and so it thinks that 'jump' belongs to
+        # the id
+        assert log.opnames(ops) == ['int_lt', 'guard_true', 'jump']
+
+    def test_ops_by_id_and_opcode(self):
+        def f():
+            i = 0
+            j = 0
+            while i < 1003:
+                i += 1; j -= 1 # ID: foo
+                a = 0  # to make sure that JUMP_ABSOLUTE is not part of the ID
+            return i
+        #
+        log = self.run(f)
+        loop, = log.loops_by_id('foo')
+        #
+        ops = loop.ops_by_id('foo', opcode='INPLACE_ADD')
+        assert log.opnames(ops) == ['int_add']
+        #
+        ops = loop.ops_by_id('foo', opcode='INPLACE_SUBTRACT')
+        assert log.opnames(ops) == ['int_sub_ovf', 'guard_no_overflow']
+        
+
+    def test_inlined_function(self):
+        def f():
+            def g(x):
+                return x+1 # ID: add
+            i = 0
+            while i < 1003:
+                i = g(i) # ID: call
+                a = 0    # to make sure that JUMP_ABSOLUTE is not part of the ID
+            return i
+        #
+        log = self.run(f)
+        loop, = log.loops_by_filename(self.filepath)
+        call_ops = log.opnames(loop.ops_by_id('call'))
+        assert call_ops == ['force_token'] # it does not follow inlining
+        #
+        add_ops = log.opnames(loop.ops_by_id('add'))
+        assert add_ops == ['int_add']
+        #
+        ops = log.opnames(loop.allops())
+        assert ops == [
+            # this is the actual loop
+            'int_lt', 'guard_true', 'force_token', 'int_add',
+            # this is the signal checking stuff
+            'getfield_raw', 'int_sub', 'setfield_raw', 'int_lt', 'guard_false',
+            'jump'
+            ]
+
+    def test_loop_match(self):
+        def f():
+            i = 0
+            while i < 1003:
+                i += 1 # ID: increment
+            return i
+        #
+        log = self.run(f)
+        loop, = log.loops_by_id('increment')
+        assert loop.match("""
+            i6 = int_lt(i4, 1003)
+            guard_true(i6, descr=...)
+            i8 = int_add(i4, 1)
+            # signal checking stuff
+            i10 = getfield_raw(37212896, descr=<.* pypysig_long_struct.c_value .*>)
+            i12 = int_sub(i10, 1)
+            setfield_raw(37212896, i12, descr=<.* pypysig_long_struct.c_value .*>)
+            i14 = int_lt(i12, 0)
+            guard_false(i14, descr=...)
+            jump(p0, p1, p2, p3, i8, descr=...)
+        """)
+        #
+        assert loop.match("""
+            i6 = int_lt(i4, 1003)
+            guard_true(i6, descr=...)
+            i8 = int_add(i4, 1)
+            --TICK--
+            jump(p0, p1, p2, p3, i8, descr=...)
+        """)
+        #
+        assert not loop.match("""
+            i6 = int_lt(i4, 1003)
+            guard_true(i6)
+            i8 = int_add(i5, 1) # variable mismatch
+            --TICK--
+            jump(p0, p1, p2, p3, i8, descr=...)
+        """)
+
+    def test_match_by_id(self):
+        def f():
+            i = 0
+            j = 2000
+            while i < 1003:
+                i += 1 # ID: increment
+                j -= 1 # ID: product
+                a = 0  # to make sure that JUMP_ABSOLUTE is not part of the ID
+            return i
+        #
+        log = self.run(f)
+        loop, = log.loops_by_id('increment')
+        assert loop.match_by_id('increment', """
+            i1 = int_add(i0, 1)
+        """)
+        assert loop.match_by_id('product', """
+            i4 = int_sub_ovf(i3, 1)
+            guard_no_overflow(descr=...)
+        """)
+
+    def test_match_constants(self):
+        def f():
+            from socket import ntohs
+            i = 0
+            while i < 1003:
+                i += 1
+                j = ntohs(1) # ID: ntohs
+                a = 0
+            return i
+        log = self.run(f, import_site=True)
+        loop, = log.loops_by_id('ntohs')
+        assert loop.match_by_id('ntohs', """
+            guard_not_invalidated(descr=...)
+            p12 = call(ConstClass(ntohs), 1, descr=...)
+            guard_no_exception(descr=...)
+        """)
+        #
+        assert not loop.match_by_id('ntohs', """
+            guard_not_invalidated(descr=...)
+            p12 = call(ConstClass(foobar), 1, descr=...)
+            guard_no_exception(descr=...)
+        """)
+        

File pypy/module/pypyjit/test_pypy_c/test__ffi.py

+import py
+import sys
+from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC
+
+class Test__ffi(BaseTestPyPyC):
+
+    def test__ffi_call(self):
+        from pypy.rlib.test.test_libffi import get_libm_name
+        def main(libm_name):
+            try:
+                from _ffi import CDLL, types
+            except ImportError:
+                sys.stderr.write('SKIP: cannot import _ffi\n')
+                return 0
+
+            libm = CDLL(libm_name)
+            pow = libm.getfunc('pow', [types.double, types.double],
+                               types.double)
+            i = 0
+            res = 0
+            while i < 300:
+                tmp = pow(2, 3)   # ID: fficall
+                res += tmp
+                i += 1
+            return pow.getaddr(), res
+        #
+        libm_name = get_libm_name(sys.platform)
+        log = self.run(main, [libm_name])
+        pow_addr, res = log.result
+        assert res == 8.0 * 300
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match_by_id('fficall', """
+            p16 = getfield_gc(ConstPtr(ptr15), descr=<.* .*Function.inst_name .*>)
+            guard_not_invalidated(descr=...)
+            i17 = force_token()
+            setfield_gc(p0, i17, descr=<.* .*PyFrame.vable_token .*>)
+            f21 = call_release_gil(%d, 2.000000, 3.000000, descr=<FloatCallDescr>)
+            guard_not_forced(descr=...)
+            guard_no_exception(descr=...)
+        """ % pow_addr)
+
+
+    def test__ffi_call_frame_does_not_escape(self):
+        from pypy.rlib.test.test_libffi import get_libm_name
+        def main(libm_name):
+            try:
+                from _ffi import CDLL, types
+            except ImportError:
+                sys.stderr.write('SKIP: cannot import _ffi\n')
+                return 0
+
+            libm = CDLL(libm_name)
+            pow = libm.getfunc('pow', [types.double, types.double],
+                               types.double)
+
+            def mypow(a, b):
+                return pow(a, b)
+
+            i = 0
+            res = 0
+            while i < 300:
+                tmp = mypow(2, 3)
+                res += tmp
+                i += 1
+            return pow.getaddr(), res
+        #
+        libm_name = get_libm_name(sys.platform)
+        log = self.run(main, [libm_name])
+        pow_addr, res = log.result
+        assert res == 8.0 * 300
+        loop, = log.loops_by_filename(self.filepath)
+        opnames = log.opnames(loop.allops())
+        # we only force the virtualref, not its content
+        assert opnames.count('new_with_vtable') == 1
+
+    def test__ffi_call_releases_gil(self):
+        from pypy.rlib.test.test_libffi import get_libc_name
+        def main(libc_name, n):
+            import time
+            from threading import Thread
+            from _ffi import CDLL, types
+            #
+            libc = CDLL(libc_name)
+            sleep = libc.getfunc('sleep', [types.uint], types.uint)
+            delays = [0]*n + [1]
+            #
+            def loop_of_sleeps(i, delays):
+                for delay in delays:
+                    sleep(delay)    # ID: sleep
+            #
+            threads = [Thread(target=loop_of_sleeps, args=[i, delays]) for i in range(5)]
+            start = time.time()
+            for i, thread in enumerate(threads):
+                thread.start()
+            for thread in threads:
+                thread.join()
+            end = time.time()
+            return end - start
+        #
+        log = self.run(main, [get_libc_name(), 200], threshold=150)
+        assert 1 <= log.result <= 1.5 # at most 0.5 seconds of overhead
+        loops = log.loops_by_id('sleep')
+        assert len(loops) == 1 # make sure that we actually JITted the loop
+
+
+    def test_ctypes_call(self):
+        from pypy.rlib.test.test_libffi import get_libm_name
+        def main(libm_name):
+            import ctypes
+            libm = ctypes.CDLL(libm_name)
+            fabs = libm.fabs
+            fabs.argtypes = [ctypes.c_double]
+            fabs.restype = ctypes.c_double
+            x = -4
+            i = 0
+            while i < 300:
+                x = fabs(x)
+                x = x - 100
+                i += 1
+            return fabs._ptr.getaddr(), x
+
+        libm_name = get_libm_name(sys.platform)
+        log = self.run(main, [libm_name])
+        fabs_addr, res = log.result
+        assert res == -4.0
+        loop, = log.loops_by_filename(self.filepath)
+        ops = loop.allops()
+        opnames = log.opnames(ops)
+        assert opnames.count('new_with_vtable') == 1 # only the virtualref
+        assert opnames.count('call_release_gil') == 1
+        idx = opnames.index('call_release_gil')
+        call = ops[idx]
+        assert int(call.args[0]) == fabs_addr

File pypy/module/pypyjit/test_pypy_c/test_array.py

+import py
+from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC
+
+class TestArray(BaseTestPyPyC):
+
+    def test_arraycopy_disappears(self):
+        def main(n):
+            i = 0
+            while i < n:
+                t = (1, 2, 3, i + 1)
+                t2 = t[:]
+                del t
+                i = t2[3]
+                del t2
+            return i
+        #
+        log = self.run(main, [500])
+        assert log.result == 500
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match("""
+            i7 = int_lt(i5, i6)
+            guard_true(i7, descr=<Guard3>)
+            i9 = int_add(i5, 1)
+            --TICK--
+            jump(p0, p1, p2, p3, p4, i9, i6, descr=<Loop0>)
+        """)
+
+    def test_array_sum(self):
+        def main():
+            from array import array
+            img = array("i", range(128) * 5) * 480
+            l, i = 0, 0
+            while i < len(img):
+                l += img[i]
+                i += 1
+            return l
+        #
+        log = self.run(main, [])
+        assert log.result == 19507200
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match("""
+            i13 = int_lt(i7, i9)
+            guard_true(i13, descr=<Guard3>)
+            i15 = getarrayitem_raw(i10, i7, descr=<.*ArrayNoLengthDescr>)
+            i16 = int_add_ovf(i8, i15)
+            guard_no_overflow(descr=<Guard4>)
+            i18 = int_add(i7, 1)
+            --TICK--
+            jump(p0, p1, p2, p3, p4, p5, p6, i18, i16, i9, i10, descr=<Loop0>)
+        """)
+
+    def test_array_intimg(self):
+        def main():
+            from array import array
+            img = array('i', range(3)) * (350 * 480)
+            intimg = array('i', (0,)) * (640 * 480)
+            l, i = 0, 640
+            while i < 640 * 480:
+                assert len(img) == 3*350*480
+                assert len(intimg) == 640*480
+                l = l + img[i]
+                intimg[i] = (intimg[i-640] + l)
+                i += 1
+            return intimg[i - 1]
+        #
+        log = self.run(main, [])
+        assert log.result == 73574560
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match("""
+            i13 = int_lt(i8, 307200)
+            guard_true(i13, descr=<Guard3>)
+        # 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>)
+            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>)
+        # on 64bit, there is a guard checking that i19 actually fits into 32bit
+            ...
+            setarrayitem_raw(i11, i8, _, descr=<.*ArrayNoLengthDescr>)
+            i28 = int_add(i8, 1)
+            --TICK--
+            jump(p0, p1, p2, p3, p4, p5, p6, p7, i28, i15, i10, i11, descr=<Loop0>)
+        """)
+
+
+    def test_zeropadded(self):
+        def main():
+            from array import array
+            class ZeroPadded(array):
+                def __new__(cls, l):
+                    self = array.__new__(cls, 'd', range(l))
+                    return self
+
+                def __getitem__(self, i):
+                    if i < 0 or i >= len(self):
+                        return 0
+                    return array.__getitem__(self, i) # ID: get
+            #
+            buf = ZeroPadded(2000)
+            i = 10
+            sa = 0
+            while i < 2000 - 10:
+                sa += buf[i-2] + buf[i-1] + buf[i] + buf[i+1] + buf[i+2]
+                i += 1
+            return sa
+
+        log = self.run(main, [])
+        assert log.result == 9895050.0
+        loop, = log.loops_by_filename(self.filepath)
+        #
+        # check that the overloaded __getitem__ does not introduce double
+        # array bound checks.
+        #
+        # The force_token()s are still there, but will be eliminated by the
+        # backend regalloc, so they are harmless
+        assert loop.match(ignore_ops=['force_token'],
+                          expected_src="""
+            ...
+            i20 = int_ge(i18, i8)
+            guard_false(i20, descr=...)
+            f21 = getarrayitem_raw(i13, i18, descr=...)
+            f23 = getarrayitem_raw(i13, i14, descr=...)
+            f24 = float_add(f21, f23)
+            f26 = getarrayitem_raw(i13, i6, descr=...)
+            f27 = float_add(f24, f26)
+            i29 = int_add(i6, 1)
+            i31 = int_ge(i29, i8)
+            guard_false(i31, descr=...)
+            f33 = getarrayitem_raw(i13, i29, descr=...)
+            f34 = float_add(f27, f33)
+            i36 = int_add(i6, 2)
+            i38 = int_ge(i36, i8)
+            guard_false(i38, descr=...)
+            f39 = getarrayitem_raw(i13, i36, descr=...)
+            ...
+        """)
+
+    def test_circular(self):
+        def main():
+            from array import array
+            class Circular(array):
+                def __new__(cls):
+                    self = array.__new__(cls, 'd', range(256))
+                    return self
+                def __getitem__(self, i):
+                    assert len(self) == 256
+                    return array.__getitem__(self, i & 255)
+            #
+            buf = Circular()
+            i = 10
+            sa = 0
+            while i < 2000 - 10:
+                sa += buf[i-2] + buf[i-1] + buf[i] + buf[i+1] + buf[i+2]
+                i += 1
+            return sa
+        #
+        log = self.run(main, [])
+        assert log.result == 1239690.0
+        loop, = log.loops_by_filename(self.filepath)
+        #
+        # check that the array bound checks are removed
+        #
+        # The force_token()s are still there, but will be eliminated by the
+        # backend regalloc, so they are harmless
+        assert loop.match(ignore_ops=['force_token'],
+                          expected_src="""
+            ...
+            i17 = int_and(i14, 255)
+            f18 = getarrayitem_raw(i8, i17, descr=...)
+            f20 = getarrayitem_raw(i8, i9, descr=...)
+            f21 = float_add(f18, f20)
+            f23 = getarrayitem_raw(i8, i10, descr=...)
+            f24 = float_add(f21, f23)
+            i26 = int_add(i6, 1)
+            i29 = int_and(i26, 255)
+            f30 = getarrayitem_raw(i8, i29, descr=...)
+            f31 = float_add(f24, f30)
+            i33 = int_add(i6, 2)
+            i36 = int_and(i33, 255)
+            f37 = getarrayitem_raw(i8, i36, descr=...)
+            ...
+        """)

File pypy/module/pypyjit/test_pypy_c/test_boolrewrite.py

+import py
+from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC
+
+class TestBoolRewrite(BaseTestPyPyC):
+
+    def test_boolrewrite_inverse(self):
+        """
+        Test for this case::
+            guard(i < x)
+            ...
+            guard(i >= y)
+
+        where x and y can be either constants or variables. There are cases in
+        which the second guard is proven to be always true.
+        """
+
+        for a, b, res, opt_expected in (('2000', '2000', 20001000, True),
+                                        ( '500',  '500', 15001500, True),
+                                        ( '300',  '600', 16001700, False),
+                                        (   'a',    'b', 16001700, False),
+                                        (   'a',    'a', 13001700, True)):
+            src = """
+                def main():
+                    sa = 0
+                    a = 300
+                    b = 600
+                    for i in range(1000):
+                        if i < %s:         # ID: lt
+                            sa += 1
+                        else:
+                            sa += 2
+                        #
+                        if i >= %s:        # ID: ge
+                            sa += 10000
+                        else:
+                            sa += 20000
+                    return sa
+            """ % (a, b)
+            #
+            log = self.run(src, [], threshold=400)
+            assert log.result == res
+            loop, = log.loops_by_filename(self.filepath)
+            le_ops = log.opnames(loop.ops_by_id('lt'))
+            ge_ops = log.opnames(loop.ops_by_id('ge'))
+            assert le_ops.count('int_lt') == 1
+            #
+            if opt_expected:
+                assert ge_ops.count('int_ge') == 0
+            else:
+                # if this assert fails it means that the optimization was
+                # applied even if we don't expect to. Check whether the
+                # optimization is valid, and either fix the code or fix the
+                # test :-)
+                assert ge_ops.count('int_ge') == 1
+
+    def test_boolrewrite_reflex(self):
+        """
+        Test for this case::
+            guard(i < x)
+            ...
+            guard(y > i)
+
+        where x and y can be either constants or variables. There are cases in
+        which the second guard is proven to be always true.
+        """
+        for a, b, res, opt_expected in (('2000', '2000', 10001000, True),
+                                        ( '500',  '500', 15001500, True),
+                                        ( '300',  '600', 14001700, False),
+                                        (   'a',    'b', 14001700, False),
+                                        (   'a',    'a', 17001700, True)):
+
+            src = """
+                def main():
+                    sa = 0
+                    a = 300
+                    b = 600
+                    for i in range(1000):
+                        if i < %s:        # ID: lt
+                            sa += 1
+                        else:
+                            sa += 2
+                        if %s > i:        # ID: gt
+                            sa += 10000
+                        else:
+                            sa += 20000
+                    return sa
+            """ % (a, b)
+            log = self.run(src, [], threshold=400)
+            assert log.result == res
+            loop, = log.loops_by_filename(self.filepath)
+            le_ops = log.opnames(loop.ops_by_id('lt'))
+            gt_ops = log.opnames(loop.ops_by_id('gt'))
+            assert le_ops.count('int_lt') == 1
+            #
+            if opt_expected:
+                assert gt_ops.count('int_gt') == 0
+            else:
+                # if this assert fails it means that the optimization was
+                # applied even if we don't expect to. Check whether the
+                # optimization is valid, and either fix the code or fix the
+                # test :-)
+                assert gt_ops.count('int_gt') == 1
+
+
+    def test_boolrewrite_allcases_inverse(self):
+        """
+        Test for this case::
+            guard(i < x)
+            ...
+            guard(i > y)
+
+        with all possible combination of binary comparison operators.  This
+        test only checks that we get the expected result, not that any
+        optimization has been applied.
+        """
+        ops = ('<', '>', '<=', '>=', '==', '!=')
+        for op1 in ops:
+            for op2 in ops:
+                for a,b in ((500, 500), (300, 600)):
+                    src = """
+                        def main():
+                            sa = 0
+                            for i in range(300):
+                                if i %s %d:
+                                    sa += 1
+                                else:
+                                    sa += 2
+                                if i %s %d:
+                                    sa += 10000
+                                else:
+                                    sa += 20000
+                            return sa
+                    """ % (op1, a, op2, b)
+                    yield self.run_and_check, src
+
+                    src = """
+                        def main():
+                            sa = 0
+                            i = 0.0
+                            while i < 250.0:
+                                if i %s %f:
+                                    sa += 1
+                                else:
+                                    sa += 2
+                                if i %s %f:
+                                    sa += 10000
+                                else:
+                                    sa += 20000
+                                i += 0.25
+                            return sa
+                    """ % (op1, float(a)/4.0, op2, float(b)/4.0)
+                    yield self.run_and_check, src
+
+
+    def test_boolrewrite_allcases_reflex(self):
+        """
+        Test for this case::
+            guard(i < x)
+            ...
+            guard(x > i)
+
+        with all possible combination of binary comparison operators.  This
+        test only checks that we get the expected result, not that any
+        optimization has been applied.
+        """
+        ops = ('<', '>', '<=', '>=', '==', '!=')
+        for op1 in ops:
+            for op2 in ops:
+                for a,b in ((500, 500), (300, 600)):
+                    src = """
+                        def main():
+                            sa = 0
+                            for i in range(300):
+                                if i %s %d:
+                                    sa += 1
+                                else:
+                                    sa += 2
+                                if %d %s i:
+                                    sa += 10000
+                                else:
+                                    sa += 20000
+                            return sa
+                    """ % (op1, a, b, op2)
+                    yield self.run_and_check, src
+
+                    src = """
+                        def main():
+                            sa = 0
+                            i = 0.0
+                            while i < 250.0:
+                                if i %s %f:
+                                    sa += 1
+                                else:
+                                    sa += 2
+                                if %f %s i:
+                                    sa += 10000
+                                else:
+                                    sa += 20000
+                                i += 0.25
+                            return sa
+                    """ % (op1, float(a)/4.0, float(b)/4.0, op2)
+                    yield self.run_and_check, src
+
+    def test_boolrewrite_ptr(self):
+        """
+        This test only checks that we get the expected result, not that any
+        optimization has been applied.
+        """
+        compares = ('a == b', 'b == a', 'a != b', 'b != a', 'a == c', 'c != b')
+        for e1 in compares:
+            for e2 in compares:
+                src = """
+                    class tst(object):
+                        pass
+                    def main():
+                        a = tst()
+                        b = tst()
+                        c = tst()
+                        sa = 0
+                        for i in range(300):
+                            if %s:
+                                sa += 1
+                            else:
+                                sa += 2
+                            if %s:
+                                sa += 10000
+                            else:
+                                sa += 20000
+                            if i > 750:
+                                a = b
+                        return sa
+                """ % (e1, e2)
+                yield self.run_and_check, src

File pypy/module/pypyjit/test_pypy_c/test_call.py

+import py
+from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC
+
+class TestCall(BaseTestPyPyC):
+
+    def test_recursive_call(self):
+        def fn():
+            def rec(n):
+                if n == 0:
+                    return 0
+                return 1 + rec(n-1)
+            #
+            # this loop is traced and then aborted, because the trace is too
+            # long. But then "rec" is marked as "don't inline"
+            i = 0
+            j = 0
+            while i < 20:
+                i += 1
+                j += rec(100)
+            #
+            # next time we try to trace "rec", instead of inlining we compile
+            # it separately and generate a call_assembler
+            i = 0
+            j = 0
+            while i < 20:
+                i += 1
+                j += rec(100) # ID: call_rec
+                a = 0
+            return j
+        #
+        log = self.run(fn, [], threshold=18)
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match_by_id('call_rec', """
+            ...
+            p53 = call_assembler(..., descr=...)
+            guard_not_forced(descr=...)
+            guard_no_exception(descr=...)
+            ...
+        """)
+
+    def test_simple_call(self):
+        src = """
+            OFFSET = 0
+            def f(i):
+                return i + 1 + OFFSET # ID: add
+            def main(n):
+                i = 0
+                while i < n+OFFSET:   # ID: cond
+                    i = f(f(i))       # ID: call
+                    a = 0
+                return i
+        """
+        log = self.run(src, [1000])
+        assert log.result == 1000
+        # first, we test what is inside the entry bridge
+        # -----------------------------------------------
+        entry_bridge, = log.loops_by_id('call', is_entry_bridge=True)
+        # LOAD_GLOBAL of OFFSET
+        ops = entry_bridge.ops_by_id('cond', opcode='LOAD_GLOBAL')
+        assert log.opnames(ops) == ["guard_value",
+                                    "getfield_gc", "guard_value",
+                                    "getfield_gc", "guard_isnull",
+                                    "getfield_gc", "guard_nonnull_class"]
+        # LOAD_GLOBAL of OFFSET but in different function partially folded
+        # away
+        # XXX could be improved
+        ops = entry_bridge.ops_by_id('add', opcode='LOAD_GLOBAL')
+        assert log.opnames(ops) == ["guard_value", "getfield_gc", "guard_isnull"]
+        #
+        # two LOAD_GLOBAL of f, the second is folded away
+        ops = entry_bridge.ops_by_id('call', opcode='LOAD_GLOBAL')
+        assert log.opnames(ops) == ["getfield_gc", "guard_nonnull_class"]
+        #
+        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>)
+            p33 = getfield_gc(p29, descr=<GcPtrFieldDescr pypy.interpreter.function.Function.inst_code .*>)
+            guard_value(p33, ConstPtr(ptr34), descr=<Guard19>)
+            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>)
+            i42 = getfield_gc(p38, descr=<NonGcPtrFieldDescr pypy.interpreter.executioncontext.ExecutionContext.inst_profilefunc .*>)
+            i43 = int_is_zero(i42)
+            guard_true(i43, descr=<Guard21>)
+            i50 = force_token()
+        """)
+        #
+        # then, we test the actual loop
+        # -----------------------------
+        loop, = log.loops_by_id('call')
+        assert loop.match("""
+            i12 = int_lt(i5, i6)
+            guard_true(i12, descr=<Guard3>)
+            i13 = force_token()
+            i15 = int_add(i5, 1)
+            i16 = int_add_ovf(i15, i7)
+            guard_no_overflow(descr=<Guard4>)
+            i18 = force_token()
+            i20 = int_add_ovf(i16, 1)
+            guard_no_overflow(descr=<Guard5>)
+            i21 = int_add_ovf(i20, i7)
+            guard_no_overflow(descr=<Guard6>)
+            --TICK--
+            jump(p0, p1, p2, p3, p4, i21, i6, i7, p8, p9, p10, p11, descr=<Loop0>)
+        """)
+
+    def test_method_call(self):
+        def fn(n):
+            class A(object):
+                def __init__(self, a):
+                    self.a = a
+                def f(self, i):
+                    return self.a + i
+            i = 0
+            a = A(1)
+            while i < n:
+                x = a.f(i)    # ID: meth1
+                i = a.f(x)    # ID: meth2
+            return i
+        #
+        log = self.run(fn, [1000])
+        assert log.result == 1000
+        #
+        # first, we test the entry bridge
+        # -------------------------------
+        entry_bridge, = log.loops_by_filename(self.filepath, is_entry_bridge=True)
+        ops = entry_bridge.ops_by_id('meth1', opcode='LOOKUP_METHOD')
+        assert log.opnames(ops) == ['guard_value', 'getfield_gc', 'guard_value',
+                                    'guard_not_invalidated']
+        # the second LOOKUP_METHOD is folded away
+        assert list(entry_bridge.ops_by_id('meth2', opcode='LOOKUP_METHOD')) == []
+        #
+        # then, the actual loop
+        # ----------------------
+        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>)
+            i16 = force_token()
+            i17 = int_add_ovf(i10, i6)
+            guard_no_overflow(descr=<Guard5>)
+            i18 = force_token()
+            i19 = int_add_ovf(i10, i17)
+            guard_no_overflow(descr=<Guard6>)
+            --TICK--
+            jump(p0, p1, p2, p3, p4, p5, i19, p7, i17, i9, i10, p11, p12, p13, descr=<Loop0>)
+        """)
+
+    def test_static_classmethod_call(self):
+        def fn(n):
+            class A(object):
+                @classmethod
+                def f(cls, i):
+                    return i + (cls is A) + 1
+                @staticmethod
+                def g(i):
+                    return i - 1
+            #
+            i = 0
+            a = A()
+            while i < n:
+                x = a.f(i)
+                i = a.g(x)
+            return i
+        #
+        log = self.run(fn, [1000])
+        assert log.result == 1000
+        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>)
+            i15 = force_token()
+            i17 = int_add_ovf(i8, 1)
+            guard_no_overflow(descr=<Guard5>)
+            i18 = force_token()
+            --TICK--
+            jump(p0, p1, p2, p3, p4, p5, i8, p7, i17, i9, p10, p11, p12, descr=<Loop0>)
+        """)
+
+    def test_default_and_kw(self):
+        def main(n):
+            def f(i, j=1):
+                return i + j
+            #
+            i = 0
+            while i < n:
+                i = f(f(i), j=1) # ID: call
+                a = 0
+            return i
+        #
+        log = self.run(main, [1000])
+        assert log.result == 1000
+        loop, = log.loops_by_id('call')
+        assert loop.match_by_id('call', """
+            i14 = force_token()
+            i16 = force_token()
+        """)
+
+    def test_kwargs(self):
+        # this is not a very precise test, could be improved
+        def main(x):
+            def g(**args):
+                return len(args)
+            #
+            s = 0
+            d = {}
+            for i in range(x):
+                s += g(**d)       # ID: call
+                d[str(i)] = i
+                if i % 100 == 99:
+                    d = {}
+            return s
+        #
+        log = self.run(main, [1000])
+        assert log.result == 49500
+        loop, = log.loops_by_id('call')
+        ops = log.opnames(loop.ops_by_id('call'))
+        guards = [ops for ops in ops if ops.startswith('guard')]
+        assert len(guards) <= 5
+
+    def test_stararg_virtual(self):
+        def main(x):
+            def g(*args):
+                return len(args)
+            def h(a, b, c):
+                return c
+            #
+            s = 0
+            for i in range(x):
+                l = [i, x, 2]
+                s += g(*l)       # ID: g1
+                s += h(*l)       # ID: h1
+                s += g(i, x, 2)  # ID: g2
+                a = 0
+            for i in range(x):
+                l = [x, 2]
+                s += g(i, *l)    # ID: g3
+                s += h(i, *l)    # ID: h2
+                a = 0
+            return s
+        #
+        log = self.run(main, [1000])
+        assert log.result == 13000
+        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>)
+        """)
+        assert loop0.match_by_id('h1', """
+            i20 = force_token()
+            i22 = int_add_ovf(i8, 2)
+            guard_no_overflow(descr=<Guard5>)
+        """)
+        assert loop0.match_by_id('g2', """
+            i27 = force_token()
+            i29 = int_add_ovf(i26, 3)
+            guard_no_overflow(descr=<Guard6>)
+        """)
+        #
+        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=<Guard37>)
+        """)
+        assert loop1.match_by_id('h2', """
+            i25 = force_token()
+            i27 = int_add_ovf(i23, 2)
+            guard_no_overflow(descr=<Guard38>)
+        """)
+
+    def test_stararg(self):
+        def main(x):
+            def g(*args):
+                return args[-1]
+            def h(*args):
+                return len(args)
+            #
+            s = 0
+            l = []
+            i = 0
+            while i < x:
+                l.append(1)
+                s += g(*l)     # ID: g
+                i = h(*l)      # ID: h
+                a = 0
+            return s
+        #
+        log = self.run(main, [1000])
+        assert log.result == 1000
+        loop, = log.loops_by_id('g')
+        ops_g = log.opnames(loop.ops_by_id('g'))
+        ops_h = log.opnames(loop.ops_by_id('h'))
+        ops = ops_g + ops_h
+        assert 'new_with_vtable' not in ops
+        assert 'call_may_force' not in ops
+
+    def test_call_builtin_function(self):
+        def main(n):
+            i = 2
+            l = []
+            while i < n:
+                i += 1
+                l.append(i)    # ID: append
+                a = 0
+            return i, len(l)
+        #
+        log = self.run(main, [1000])
+        assert log.result == (1000, 998)
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match_by_id('append', """
+            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>)
+            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 .*>)
+            setarrayitem_gc(p17, i13, p19, descr=<GcPtrArrayDescr>)
+        """)
+
+    def test_blockstack_virtualizable(self):
+        def main(n):
+            from pypyjit import residual_call
+            i = 0
+            while i < n:
+                try:
+                    residual_call(len, [])   # ID: call
+                except:
+                    pass
+                i += 1
+            return i
+        #
+        log = self.run(main, [500])
+        assert log.result == 500
+        loop, = log.loops_by_id('call')
+        assert loop.match_by_id('call', opcode='CALL_FUNCTION', expected_src="""
+            # make sure that the "block" is not allocated
+            ...
+            i20 = force_token()
+            setfield_gc(p0, i20, descr=<SignedFieldDescr .*PyFrame.vable_token .*>)
+            p22 = new_with_vtable(19511408)
+            p24 = new_array(1, descr=<GcPtrArrayDescr>)
+            p26 = new_with_vtable(ConstClass(W_ListObject))
+            p27 = new(descr=<SizeDescr .*>)
+            p29 = new_array(0, descr=<GcPtrArrayDescr>)
+            setfield_gc(p27, p29, descr=<GcPtrFieldDescr list.items .*>)
+            setfield_gc(p26, p27, descr=<.* .*W_ListObject.inst_wrappeditems .*>)
+            setarrayitem_gc(p24, 0, p26, descr=<GcPtrArrayDescr>)
+            setfield_gc(p22, p24, descr=<GcPtrFieldDescr .*Arguments.inst_arguments_w .*>)
+            p32 = call_may_force(11376960, p18, p22, descr=<GcPtrCallDescr>)
+            ...
+        """)
+
+    def test_func_defaults(self):
+        def main(n):
+            i = 1
+            while i < n:
+                i += len(xrange(i+1)) - i
+            return i
+
+        log = self.run(main, [10000])
+        assert log.result == 10000
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match("""
+            i10 = int_lt(i5, i6)
+            guard_true(i10, descr=<Guard3>)
+            i120 = int_add(i5, 1)
+            guard_not_invalidated(descr=<Guard4>)
+            --TICK--
+            jump(..., descr=<Loop0>)
+        """)

File pypy/module/pypyjit/test_pypy_c/test_exception.py

+import py
+from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC
+
+class TestException(BaseTestPyPyC):
+
+    def test_cmp_exc(self):
+        def f1(n):
+            # So we don't get a LOAD_GLOBAL op
+            KE = KeyError
+            i = 0
+            while i < n:
+                try:
+                    raise KE
+                except KE: # ID: except
+                    i += 1
+            return i
+
+        log = self.run(f1, [10000])
+        assert log.result == 10000
+        loop, = log.loops_by_id("except")
+        ops = list(loop.ops_by_id("except", opcode="COMPARE_OP"))
+        assert ops == []
+
+    def test_exception_inside_loop_1(self):
+        def main(n):
+            while n:
+                try:
+                    raise ValueError
+                except ValueError:
+                    pass
+                n -= 1
+            return n
+        #
+        log = self.run(main, [1000])
+        assert log.result == 0
+        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>)
+        --EXC-TICK--
+        i12 = int_sub_ovf(i3, 1)
+        guard_no_overflow(descr=<Guard6>)
+        --TICK--
+        jump(..., descr=<Loop0>)
+        """)
+
+    def test_exception_inside_loop_2(self):
+        def main(n):
+            def g(n):
+                raise ValueError(n)  # ID: raise
+            def f(n):
+                g(n)
+            #
+            while n:
+                try: