[pypy-commit] pypy improve-rbigint: Merge in default

stian noreply at buildbot.pypy.org
Sat Jul 21 18:41:55 CEST 2012


Author: stian
Branch: improve-rbigint
Changeset: r56363:c7360edc54db
Date: 2012-07-12 19:39 +0200
http://bitbucket.org/pypy/pypy/changeset/c7360edc54db/

Log:	Merge in default

diff --git a/pypy/annotation/bookkeeper.py b/pypy/annotation/bookkeeper.py
--- a/pypy/annotation/bookkeeper.py
+++ b/pypy/annotation/bookkeeper.py
@@ -201,6 +201,7 @@
                     for op in block.operations:
                         if op.opname in ('simple_call', 'call_args'):
                             yield op
+
                         # some blocks are partially annotated
                         if binding(op.result, None) is None:
                             break   # ignore the unannotated part
diff --git a/pypy/annotation/test/test_annrpython.py b/pypy/annotation/test/test_annrpython.py
--- a/pypy/annotation/test/test_annrpython.py
+++ b/pypy/annotation/test/test_annrpython.py
@@ -3793,7 +3793,37 @@
         assert isinstance(s, annmodel.SomeString)
         assert s.no_nul
 
-
+    def test_base_iter(self):
+        class A(object):
+            def __iter__(self):
+                return self
+        
+        def fn():
+            return iter(A())
+
+        a = self.RPythonAnnotator()
+        s = a.build_types(fn, [])
+        assert isinstance(s, annmodel.SomeInstance)
+        assert s.classdef.name.endswith('.A')
+
+    def test_iter_next(self):
+        class A(object):
+            def __iter__(self):
+                return self
+
+            def next(self):
+                return 1
+        
+        def fn():
+            s = 0
+            for x in A():
+                s += x
+            return s
+
+        a = self.RPythonAnnotator()
+        s = a.build_types(fn, [])
+        assert len(a.translator.graphs) == 3 # fn, __iter__, next
+        assert isinstance(s, annmodel.SomeInteger)
 
 def g(n):
     return [0,1,2,n]
diff --git a/pypy/annotation/unaryop.py b/pypy/annotation/unaryop.py
--- a/pypy/annotation/unaryop.py
+++ b/pypy/annotation/unaryop.py
@@ -609,33 +609,36 @@
 
 class __extend__(SomeInstance):
 
+    def _true_getattr(ins, attr):
+        if attr == '__class__':
+            return ins.classdef.read_attr__class__()
+        attrdef = ins.classdef.find_attribute(attr)
+        position = getbookkeeper().position_key
+        attrdef.read_locations[position] = True
+        s_result = attrdef.getvalue()
+        # hack: if s_result is a set of methods, discard the ones
+        #       that can't possibly apply to an instance of ins.classdef.
+        # XXX do it more nicely
+        if isinstance(s_result, SomePBC):
+            s_result = ins.classdef.lookup_filter(s_result, attr,
+                                                  ins.flags)
+        elif isinstance(s_result, SomeImpossibleValue):
+            ins.classdef.check_missing_attribute_update(attr)
+            # blocking is harmless if the attribute is explicitly listed
+            # in the class or a parent class.
+            for basedef in ins.classdef.getmro():
+                if basedef.classdesc.all_enforced_attrs is not None:
+                    if attr in basedef.classdesc.all_enforced_attrs:
+                        raise HarmlesslyBlocked("get enforced attr")
+        elif isinstance(s_result, SomeList):
+            s_result = ins.classdef.classdesc.maybe_return_immutable_list(
+                attr, s_result)
+        return s_result
+
     def getattr(ins, s_attr):
         if s_attr.is_constant() and isinstance(s_attr.const, str):
             attr = s_attr.const
-            if attr == '__class__':
-                return ins.classdef.read_attr__class__()
-            attrdef = ins.classdef.find_attribute(attr)
-            position = getbookkeeper().position_key
-            attrdef.read_locations[position] = True
-            s_result = attrdef.getvalue()
-            # hack: if s_result is a set of methods, discard the ones
-            #       that can't possibly apply to an instance of ins.classdef.
-            # XXX do it more nicely
-            if isinstance(s_result, SomePBC):
-                s_result = ins.classdef.lookup_filter(s_result, attr,
-                                                      ins.flags)
-            elif isinstance(s_result, SomeImpossibleValue):
-                ins.classdef.check_missing_attribute_update(attr)
-                # blocking is harmless if the attribute is explicitly listed
-                # in the class or a parent class.
-                for basedef in ins.classdef.getmro():
-                    if basedef.classdesc.all_enforced_attrs is not None:
-                        if attr in basedef.classdesc.all_enforced_attrs:
-                            raise HarmlesslyBlocked("get enforced attr")
-            elif isinstance(s_result, SomeList):
-                s_result = ins.classdef.classdesc.maybe_return_immutable_list(
-                    attr, s_result)
-            return s_result
+            return ins._true_getattr(attr)
         return SomeObject()
     getattr.can_only_throw = []
 
@@ -657,6 +660,19 @@
         if not ins.can_be_None:
             s.const = True
 
+    def iter(ins):
+        s_iterable = ins._true_getattr('__iter__')
+        bk = getbookkeeper()
+        # record for calltables
+        bk.emulate_pbc_call(bk.position_key, s_iterable, [])
+        return s_iterable.call(bk.build_args("simple_call", []))
+
+    def next(ins):
+        s_next = ins._true_getattr('next')
+        bk = getbookkeeper()
+        # record for calltables
+        bk.emulate_pbc_call(bk.position_key, s_next, [])
+        return s_next.call(bk.build_args("simple_call", []))
 
 class __extend__(SomeBuiltin):
     def _can_only_throw(bltn, *args):
diff --git a/pypy/doc/coding-guide.rst b/pypy/doc/coding-guide.rst
--- a/pypy/doc/coding-guide.rst
+++ b/pypy/doc/coding-guide.rst
@@ -341,8 +341,8 @@
 
 **objects**
 
-  Normal rules apply. Special methods are not honoured, except ``__init__`` and
-  ``__del__``.
+  Normal rules apply. Special methods are not honoured, except ``__init__``,
+  ``__del__`` and ``__iter__``.
 
 This layout makes the number of types to take care about quite limited.
 
diff --git a/pypy/jit/backend/llgraph/runner.py b/pypy/jit/backend/llgraph/runner.py
--- a/pypy/jit/backend/llgraph/runner.py
+++ b/pypy/jit/backend/llgraph/runner.py
@@ -4,6 +4,7 @@
 
 from pypy.rlib.unroll import unrolling_iterable
 from pypy.rlib.objectmodel import we_are_translated
+from pypy.rlib.jit_hooks import LOOP_RUN_CONTAINER
 from pypy.rpython.lltypesystem import lltype, llmemory, rclass
 from pypy.rpython.ootypesystem import ootype
 from pypy.rpython.llinterp import LLInterpreter
@@ -33,6 +34,10 @@
         self.arg_types = arg_types
         self.count_fields_if_immut = count_fields_if_immut
         self.ffi_flags = ffi_flags
+        self._debug = False
+
+    def set_debug(self, v):
+        self._debug = True
 
     def get_arg_types(self):
         return self.arg_types
@@ -583,6 +588,9 @@
             for x in args_f:
                 llimpl.do_call_pushfloat(x)
 
+    def get_all_loop_runs(self):
+        return lltype.malloc(LOOP_RUN_CONTAINER, 0)
+
     def force(self, force_token):
         token = llmemory.cast_int_to_adr(force_token)
         frame = llimpl.get_forced_token_frame(token)
diff --git a/pypy/jit/backend/model.py b/pypy/jit/backend/model.py
--- a/pypy/jit/backend/model.py
+++ b/pypy/jit/backend/model.py
@@ -55,6 +55,21 @@
         """Called once by the front-end when the program stops."""
         pass
 
+    def get_all_loop_runs(self):
+        """ Function that will return number of times all the loops were run.
+        Requires earlier setting of set_debug(True), otherwise you won't
+        get the information.
+
+        Returns an instance of LOOP_RUN_CONTAINER from rlib.jit_hooks
+        """
+        raise NotImplementedError
+
+    def set_debug(self, value):
+        """ Enable or disable debugging info. Does nothing by default. Returns
+        the previous setting.
+        """
+        return False
+
     def compile_loop(self, inputargs, operations, looptoken, log=True, name=''):
         """Assemble the given loop.
         Should create and attach a fresh CompiledLoopToken to
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
@@ -101,7 +101,9 @@
                                       llmemory.cast_ptr_to_adr(ptrs))
 
     def set_debug(self, v):
+        r = self._debug
         self._debug = v
+        return r
 
     def setup_once(self):
         # the address of the function called by 'new'
@@ -750,7 +752,6 @@
     @specialize.argtype(1)
     def _inject_debugging_code(self, looptoken, operations, tp, number):
         if self._debug:
-            # before doing anything, let's increase a counter
             s = 0
             for op in operations:
                 s += op.getopnum()
diff --git a/pypy/jit/backend/x86/runner.py b/pypy/jit/backend/x86/runner.py
--- a/pypy/jit/backend/x86/runner.py
+++ b/pypy/jit/backend/x86/runner.py
@@ -3,6 +3,7 @@
 from pypy.rpython.lltypesystem.lloperation import llop
 from pypy.rpython.llinterp import LLInterpreter
 from pypy.rlib.objectmodel import we_are_translated
+from pypy.rlib.jit_hooks import LOOP_RUN_CONTAINER
 from pypy.jit.codewriter import longlong
 from pypy.jit.metainterp import history, compile
 from pypy.jit.backend.x86.assembler import Assembler386
@@ -44,6 +45,9 @@
 
         self.profile_agent = profile_agent
 
+    def set_debug(self, flag):
+        return self.assembler.set_debug(flag)
+
     def setup(self):
         if self.opts is not None:
             failargs_limit = self.opts.failargs_limit
@@ -181,6 +185,14 @@
         # positions invalidated
         looptoken.compiled_loop_token.invalidate_positions = []
 
+    def get_all_loop_runs(self):
+        l = lltype.malloc(LOOP_RUN_CONTAINER,
+                          len(self.assembler.loop_run_counters))
+        for i, ll_s in enumerate(self.assembler.loop_run_counters):
+            l[i].type = ll_s.type
+            l[i].number = ll_s.number
+            l[i].counter = ll_s.i
+        return l
 
 class CPU386(AbstractX86CPU):
     backend_name = 'x86'
diff --git a/pypy/jit/backend/x86/test/test_ztranslation.py b/pypy/jit/backend/x86/test/test_ztranslation.py
--- a/pypy/jit/backend/x86/test/test_ztranslation.py
+++ b/pypy/jit/backend/x86/test/test_ztranslation.py
@@ -3,6 +3,7 @@
 from pypy.rlib.jit import JitDriver, unroll_parameters, set_param
 from pypy.rlib.jit import PARAMETERS, dont_look_inside
 from pypy.rlib.jit import promote
+from pypy.rlib import jit_hooks
 from pypy.jit.metainterp.jitprof import Profiler
 from pypy.jit.backend.detect_cpu import getcpuclass
 from pypy.jit.backend.test.support import CCompiledMixin
@@ -170,6 +171,22 @@
         assert 1024 <= bound <= 131072
         assert bound & (bound-1) == 0       # a power of two
 
+    def test_jit_get_stats(self):
+        driver = JitDriver(greens = [], reds = ['i'])
+        
+        def f():
+            i = 0
+            while i < 100000:
+                driver.jit_merge_point(i=i)
+                i += 1
+
+        def main():
+            f()
+            ll_times = jit_hooks.stats_get_loop_run_times(None)
+            return len(ll_times)
+
+        res = self.meta_interp(main, [])
+        assert res == 1
 
 class TestTranslationRemoveTypePtrX86(CCompiledMixin):
     CPUClass = getcpuclass()
diff --git a/pypy/jit/metainterp/compile.py b/pypy/jit/metainterp/compile.py
--- a/pypy/jit/metainterp/compile.py
+++ b/pypy/jit/metainterp/compile.py
@@ -5,7 +5,7 @@
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.rlib.debug import debug_start, debug_stop, debug_print
 from pypy.rlib import rstack
-from pypy.rlib.jit import JitDebugInfo
+from pypy.rlib.jit import JitDebugInfo, Counters
 from pypy.conftest import option
 from pypy.tool.sourcetools import func_with_new_name
 
@@ -22,8 +22,7 @@
 
 def giveup():
     from pypy.jit.metainterp.pyjitpl import SwitchToBlackhole
-    from pypy.jit.metainterp.jitprof import ABORT_BRIDGE
-    raise SwitchToBlackhole(ABORT_BRIDGE)
+    raise SwitchToBlackhole(Counters.ABORT_BRIDGE)
 
 def show_procedures(metainterp_sd, procedure=None, error=None):
     # debugging
diff --git a/pypy/jit/metainterp/jitprof.py b/pypy/jit/metainterp/jitprof.py
--- a/pypy/jit/metainterp/jitprof.py
+++ b/pypy/jit/metainterp/jitprof.py
@@ -6,42 +6,11 @@
 from pypy.rlib.debug import debug_print, debug_start, debug_stop
 from pypy.rlib.debug import have_debug_prints
 from pypy.jit.metainterp.jitexc import JitException
+from pypy.rlib.jit import Counters
 
-counters="""
-TRACING
-BACKEND
-OPS
-RECORDED_OPS
-GUARDS
-OPT_OPS
-OPT_GUARDS
-OPT_FORCINGS
-ABORT_TOO_LONG
-ABORT_BRIDGE
-ABORT_BAD_LOOP
-ABORT_ESCAPE
-ABORT_FORCE_QUASIIMMUT
-NVIRTUALS
-NVHOLES
-NVREUSED
-TOTAL_COMPILED_LOOPS
-TOTAL_COMPILED_BRIDGES
-TOTAL_FREED_LOOPS
-TOTAL_FREED_BRIDGES
-"""
 
-counter_names = []
-
-def _setup():
-    names = counters.split()
-    for i, name in enumerate(names):
-        globals()[name] = i
-        counter_names.append(name)
-    global ncounters
-    ncounters = len(names)
-_setup()
-
-JITPROF_LINES = ncounters + 1 + 1 # one for TOTAL, 1 for calls, update if needed
+JITPROF_LINES = Counters.ncounters + 1 + 1
+# one for TOTAL, 1 for calls, update if needed
 _CPU_LINES = 4       # the last 4 lines are stored on the cpu
 
 class BaseProfiler(object):
@@ -71,9 +40,12 @@
     def count(self, kind, inc=1):
         pass
 
-    def count_ops(self, opnum, kind=OPS):
+    def count_ops(self, opnum, kind=Counters.OPS):
         pass
 
+    def get_counter(self, num):
+        return -1.0
+
 class Profiler(BaseProfiler):
     initialized = False
     timer = time.time
@@ -89,7 +61,7 @@
         self.starttime = self.timer()
         self.t1 = self.starttime
         self.times = [0, 0]
-        self.counters = [0] * (ncounters - _CPU_LINES)
+        self.counters = [0] * (Counters.ncounters - _CPU_LINES)
         self.calls = 0
         self.current = []
 
@@ -117,19 +89,30 @@
             return
         self.times[ev1] += self.t1 - t0
 
-    def start_tracing(self):   self._start(TRACING)
-    def end_tracing(self):     self._end  (TRACING)
+    def start_tracing(self):   self._start(Counters.TRACING)
+    def end_tracing(self):     self._end  (Counters.TRACING)
 
-    def start_backend(self):   self._start(BACKEND)
-    def end_backend(self):     self._end  (BACKEND)
+    def start_backend(self):   self._start(Counters.BACKEND)
+    def end_backend(self):     self._end  (Counters.BACKEND)
 
     def count(self, kind, inc=1):
         self.counters[kind] += inc        
-    
-    def count_ops(self, opnum, kind=OPS):
+
+    def get_counter(self, num):
+        if num == Counters.TOTAL_COMPILED_LOOPS:
+            return self.cpu.total_compiled_loops
+        elif num == Counters.TOTAL_COMPILED_BRIDGES:
+            return self.cpu.total_compiled_bridges
+        elif num == Counters.TOTAL_FREED_LOOPS:
+            return self.cpu.total_freed_loops
+        elif num == Counters.TOTAL_FREED_BRIDGES:
+            return self.cpu.total_freed_bridges
+        return self.counters[num]
+
+    def count_ops(self, opnum, kind=Counters.OPS):
         from pypy.jit.metainterp.resoperation import rop
         self.counters[kind] += 1
-        if opnum == rop.CALL and kind == RECORDED_OPS:# or opnum == rop.OOSEND:
+        if opnum == rop.CALL and kind == Counters.RECORDED_OPS:# or opnum == rop.OOSEND:
             self.calls += 1
 
     def print_stats(self):
@@ -142,26 +125,29 @@
         cnt = self.counters
         tim = self.times
         calls = self.calls
-        self._print_line_time("Tracing", cnt[TRACING],   tim[TRACING])
-        self._print_line_time("Backend", cnt[BACKEND],   tim[BACKEND])
+        self._print_line_time("Tracing", cnt[Counters.TRACING],
+                              tim[Counters.TRACING])
+        self._print_line_time("Backend", cnt[Counters.BACKEND],
+                              tim[Counters.BACKEND])
         line = "TOTAL:      \t\t%f" % (self.tk - self.starttime, )
         debug_print(line)
-        self._print_intline("ops", cnt[OPS])
-        self._print_intline("recorded ops", cnt[RECORDED_OPS])
+        self._print_intline("ops", cnt[Counters.OPS])
+        self._print_intline("recorded ops", cnt[Counters.RECORDED_OPS])
         self._print_intline("  calls", calls)
-        self._print_intline("guards", cnt[GUARDS])
-        self._print_intline("opt ops", cnt[OPT_OPS])
-        self._print_intline("opt guards", cnt[OPT_GUARDS])
-        self._print_intline("forcings", cnt[OPT_FORCINGS])
-        self._print_intline("abort: trace too long", cnt[ABORT_TOO_LONG])
-        self._print_intline("abort: compiling", cnt[ABORT_BRIDGE])
-        self._print_intline("abort: vable escape", cnt[ABORT_ESCAPE])
-        self._print_intline("abort: bad loop", cnt[ABORT_BAD_LOOP])
+        self._print_intline("guards", cnt[Counters.GUARDS])
+        self._print_intline("opt ops", cnt[Counters.OPT_OPS])
+        self._print_intline("opt guards", cnt[Counters.OPT_GUARDS])
+        self._print_intline("forcings", cnt[Counters.OPT_FORCINGS])
+        self._print_intline("abort: trace too long",
+                            cnt[Counters.ABORT_TOO_LONG])
+        self._print_intline("abort: compiling", cnt[Counters.ABORT_BRIDGE])
+        self._print_intline("abort: vable escape", cnt[Counters.ABORT_ESCAPE])
+        self._print_intline("abort: bad loop", cnt[Counters.ABORT_BAD_LOOP])
         self._print_intline("abort: force quasi-immut",
-                                               cnt[ABORT_FORCE_QUASIIMMUT])
-        self._print_intline("nvirtuals", cnt[NVIRTUALS])
-        self._print_intline("nvholes", cnt[NVHOLES])
-        self._print_intline("nvreused", cnt[NVREUSED])
+                            cnt[Counters.ABORT_FORCE_QUASIIMMUT])
+        self._print_intline("nvirtuals", cnt[Counters.NVIRTUALS])
+        self._print_intline("nvholes", cnt[Counters.NVHOLES])
+        self._print_intline("nvreused", cnt[Counters.NVREUSED])
         cpu = self.cpu
         if cpu is not None:   # for some tests
             self._print_intline("Total # of loops",
diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py b/pypy/jit/metainterp/optimizeopt/optimizer.py
--- a/pypy/jit/metainterp/optimizeopt/optimizer.py
+++ b/pypy/jit/metainterp/optimizeopt/optimizer.py
@@ -401,7 +401,7 @@
             o.turned_constant(value)
 
     def forget_numberings(self, virtualbox):
-        self.metainterp_sd.profiler.count(jitprof.OPT_FORCINGS)
+        self.metainterp_sd.profiler.count(jitprof.Counters.OPT_FORCINGS)
         self.resumedata_memo.forget_numberings(virtualbox)
 
     def getinterned(self, box):
@@ -535,9 +535,9 @@
             else:
                 self.ensure_imported(value)
                 op.setarg(i, value.force_box(self))
-        self.metainterp_sd.profiler.count(jitprof.OPT_OPS)
+        self.metainterp_sd.profiler.count(jitprof.Counters.OPT_OPS)
         if op.is_guard():
-            self.metainterp_sd.profiler.count(jitprof.OPT_GUARDS)
+            self.metainterp_sd.profiler.count(jitprof.Counters.OPT_GUARDS)
             if self.replaces_guard and op in self.replaces_guard:
                 self.replace_op(self.replaces_guard[op], op)
                 del self.replaces_guard[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
@@ -13,9 +13,7 @@
 from pypy.jit.metainterp import executor
 from pypy.jit.metainterp.logger import Logger
 from pypy.jit.metainterp.jitprof import EmptyProfiler
-from pypy.jit.metainterp.jitprof import GUARDS, RECORDED_OPS, ABORT_ESCAPE
-from pypy.jit.metainterp.jitprof import ABORT_TOO_LONG, ABORT_BRIDGE, \
-                                        ABORT_FORCE_QUASIIMMUT, ABORT_BAD_LOOP
+from pypy.rlib.jit import Counters
 from pypy.jit.metainterp.jitexc import JitException, get_llexception
 from pypy.jit.metainterp.heapcache import HeapCache
 from pypy.rlib.objectmodel import specialize
@@ -675,7 +673,7 @@
             from pypy.jit.metainterp.quasiimmut import do_force_quasi_immutable
             do_force_quasi_immutable(self.metainterp.cpu, box.getref_base(),
                                      mutatefielddescr)
-            raise SwitchToBlackhole(ABORT_FORCE_QUASIIMMUT)
+            raise SwitchToBlackhole(Counters.ABORT_FORCE_QUASIIMMUT)
         self.generate_guard(rop.GUARD_ISNULL, mutatebox, resumepc=orgpc)
 
     def _nonstandard_virtualizable(self, pc, box):
@@ -1255,7 +1253,7 @@
         guard_op = metainterp.history.record(opnum, moreargs, None,
                                              descr=resumedescr)
         self.capture_resumedata(resumedescr, resumepc)
-        self.metainterp.staticdata.profiler.count_ops(opnum, GUARDS)
+        self.metainterp.staticdata.profiler.count_ops(opnum, Counters.GUARDS)
         # count
         metainterp.attach_debug_info(guard_op)
         return guard_op
@@ -1776,7 +1774,7 @@
             return resbox.constbox()
         # record the operation
         profiler = self.staticdata.profiler
-        profiler.count_ops(opnum, RECORDED_OPS)
+        profiler.count_ops(opnum, Counters.RECORDED_OPS)
         self.heapcache.invalidate_caches(opnum, descr, argboxes)
         op = self.history.record(opnum, argboxes, resbox, descr)
         self.attach_debug_info(op)
@@ -1837,7 +1835,7 @@
             if greenkey_of_huge_function is not None:
                 warmrunnerstate.disable_noninlinable_function(
                     greenkey_of_huge_function)
-            raise SwitchToBlackhole(ABORT_TOO_LONG)
+            raise SwitchToBlackhole(Counters.ABORT_TOO_LONG)
 
     def _interpret(self):
         # Execute the frames forward until we raise a DoneWithThisFrame,
@@ -1921,7 +1919,7 @@
         try:
             self.prepare_resume_from_failure(key.guard_opnum, dont_change_position)
             if self.resumekey_original_loop_token is None:   # very rare case
-                raise SwitchToBlackhole(ABORT_BRIDGE)
+                raise SwitchToBlackhole(Counters.ABORT_BRIDGE)
             self.interpret()
         except SwitchToBlackhole, stb:
             self.run_blackhole_interp_to_cancel_tracing(stb)
@@ -1996,7 +1994,7 @@
                 # raises in case it works -- which is the common case
                 if self.partial_trace:
                     if  start != self.retracing_from:
-                        raise SwitchToBlackhole(ABORT_BAD_LOOP) # For now
+                        raise SwitchToBlackhole(Counters.ABORT_BAD_LOOP) # For now
                 self.compile_loop(original_boxes, live_arg_boxes, start, resumedescr)
                 # creation of the loop was cancelled!
                 self.cancel_count += 1
@@ -2005,7 +2003,7 @@
                     if memmgr:
                         if self.cancel_count > memmgr.max_unroll_loops:
                             self.staticdata.log('cancelled too many times!')
-                            raise SwitchToBlackhole(ABORT_BAD_LOOP)
+                            raise SwitchToBlackhole(Counters.ABORT_BAD_LOOP)
                 self.staticdata.log('cancelled, tracing more...')
 
         # Otherwise, no loop found so far, so continue tracing.
@@ -2299,7 +2297,8 @@
             if vinfo.tracing_after_residual_call(virtualizable):
                 # the virtualizable escaped during CALL_MAY_FORCE.
                 self.load_fields_from_virtualizable()
-                raise SwitchToBlackhole(ABORT_ESCAPE, raising_exception=True)
+                raise SwitchToBlackhole(Counters.ABORT_ESCAPE,
+                                        raising_exception=True)
                 # ^^^ we set 'raising_exception' to True because we must still
                 # have the eventual exception raised (this is normally done
                 # after the call to vable_after_residual_call()).
diff --git a/pypy/jit/metainterp/resume.py b/pypy/jit/metainterp/resume.py
--- a/pypy/jit/metainterp/resume.py
+++ b/pypy/jit/metainterp/resume.py
@@ -254,9 +254,9 @@
         self.cached_virtuals.clear()
 
     def update_counters(self, profiler):
-        profiler.count(jitprof.NVIRTUALS, self.nvirtuals)
-        profiler.count(jitprof.NVHOLES, self.nvholes)
-        profiler.count(jitprof.NVREUSED, self.nvreused)
+        profiler.count(jitprof.Counters.NVIRTUALS, self.nvirtuals)
+        profiler.count(jitprof.Counters.NVHOLES, self.nvholes)
+        profiler.count(jitprof.Counters.NVREUSED, self.nvreused)
 
 _frame_info_placeholder = (None, 0, 0)
 
diff --git a/pypy/jit/metainterp/test/test_jitiface.py b/pypy/jit/metainterp/test/test_jitiface.py
--- a/pypy/jit/metainterp/test/test_jitiface.py
+++ b/pypy/jit/metainterp/test/test_jitiface.py
@@ -1,13 +1,15 @@
 
-from pypy.rlib.jit import JitDriver, JitHookInterface
+from pypy.rlib.jit import JitDriver, JitHookInterface, Counters
 from pypy.rlib import jit_hooks
 from pypy.jit.metainterp.test.support import LLJitMixin
 from pypy.jit.codewriter.policy import JitPolicy
-from pypy.jit.metainterp.jitprof import ABORT_FORCE_QUASIIMMUT
 from pypy.jit.metainterp.resoperation import rop
 from pypy.rpython.annlowlevel import hlstr
+from pypy.jit.metainterp.jitprof import Profiler
 
-class TestJitHookInterface(LLJitMixin):
+class JitHookInterfaceTests(object):
+    # !!!note!!! - don't subclass this from the backend. Subclass the LL
+    # class later instead
     def test_abort_quasi_immut(self):
         reasons = []
         
@@ -41,7 +43,7 @@
         assert f(100, 7) == 721
         res = self.meta_interp(f, [100, 7], policy=JitPolicy(iface))
         assert res == 721
-        assert reasons == [ABORT_FORCE_QUASIIMMUT] * 2
+        assert reasons == [Counters.ABORT_FORCE_QUASIIMMUT] * 2
 
     def test_on_compile(self):
         called = []
@@ -146,3 +148,74 @@
             assert jit_hooks.resop_getresult(op) == box5
 
         self.meta_interp(main, [])
+
+    def test_get_stats(self):
+        driver = JitDriver(greens = [], reds = ['i', 's'])
+
+        def loop(i):
+            s = 0
+            while i > 0:
+                driver.jit_merge_point(i=i, s=s)
+                if i % 2:
+                    s += 1
+                i -= 1
+                s+= 2
+            return s
+
+        def main():
+            loop(30)
+            assert jit_hooks.stats_get_counter_value(None,
+                                           Counters.TOTAL_COMPILED_LOOPS) == 1
+            assert jit_hooks.stats_get_counter_value(None,
+                                           Counters.TOTAL_COMPILED_BRIDGES) == 1
+            assert jit_hooks.stats_get_counter_value(None,
+                                                     Counters.TRACING) == 2
+            assert jit_hooks.stats_get_times_value(None, Counters.TRACING) >= 0
+
+        self.meta_interp(main, [], ProfilerClass=Profiler)
+
+class LLJitHookInterfaceTests(JitHookInterfaceTests):
+    # use this for any backend, instead of the super class
+    
+    def test_ll_get_stats(self):
+        driver = JitDriver(greens = [], reds = ['i', 's'])
+
+        def loop(i):
+            s = 0
+            while i > 0:
+                driver.jit_merge_point(i=i, s=s)
+                if i % 2:
+                    s += 1
+                i -= 1
+                s+= 2
+            return s
+
+        def main(b):
+            jit_hooks.stats_set_debug(None, b)
+            loop(30)
+            l = jit_hooks.stats_get_loop_run_times(None)
+            if b:
+                assert len(l) == 4
+                # completely specific test that would fail each time
+                # we change anything major. for now it's 4
+                # (loop, bridge, 2 entry points)
+                assert l[0].type == 'e'
+                assert l[0].number == 0
+                assert l[0].counter == 4
+                assert l[1].type == 'l'
+                assert l[1].counter == 4
+                assert l[2].type == 'l'
+                assert l[2].counter == 23
+                assert l[3].type == 'b'
+                assert l[3].number == 4
+                assert l[3].counter == 11
+            else:
+                assert len(l) == 0
+        self.meta_interp(main, [True], ProfilerClass=Profiler)
+        # this so far does not work because of the way setup_once is done,
+        # but fine, it's only about untranslated version anyway
+        #self.meta_interp(main, [False], ProfilerClass=Profiler)
+        
+
+class TestJitHookInterface(JitHookInterfaceTests, LLJitMixin):
+    pass
diff --git a/pypy/jit/metainterp/test/test_jitprof.py b/pypy/jit/metainterp/test/test_jitprof.py
--- a/pypy/jit/metainterp/test/test_jitprof.py
+++ b/pypy/jit/metainterp/test/test_jitprof.py
@@ -1,9 +1,9 @@
 
 from pypy.jit.metainterp.warmspot import ll_meta_interp
-from pypy.rlib.jit import JitDriver, dont_look_inside, elidable
+from pypy.rlib.jit import JitDriver, dont_look_inside, elidable, Counters
 from pypy.jit.metainterp.test.support import LLJitMixin
 from pypy.jit.metainterp import pyjitpl
-from pypy.jit.metainterp.jitprof import *
+from pypy.jit.metainterp.jitprof import Profiler
 
 class FakeProfiler(Profiler):
     def start(self):
@@ -46,10 +46,10 @@
         assert res == 84
         profiler = pyjitpl._warmrunnerdesc.metainterp_sd.profiler
         expected = [
-            TRACING,
-            BACKEND,
-            ~ BACKEND,
-            ~ TRACING,
+            Counters.TRACING,
+            Counters.BACKEND,
+            ~ Counters.BACKEND,
+            ~ Counters.TRACING,
             ]
         assert profiler.events == expected
         assert profiler.times == [2, 1]
diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py
--- a/pypy/jit/metainterp/warmspot.py
+++ b/pypy/jit/metainterp/warmspot.py
@@ -6,6 +6,7 @@
 from pypy.annotation import model as annmodel
 from pypy.rpython.llinterp import LLException
 from pypy.rpython.test.test_llinterp import get_interpreter, clear_tcache
+from pypy.rpython.annlowlevel import cast_instance_to_base_ptr
 from pypy.objspace.flow.model import SpaceOperation, Variable, Constant
 from pypy.objspace.flow.model import checkgraph, Link, copygraph
 from pypy.rlib.objectmodel import we_are_translated
@@ -221,7 +222,7 @@
         self.rewrite_access_helpers()
         self.codewriter.make_jitcodes(verbose=verbose)
         self.rewrite_can_enter_jits()
-        self.rewrite_set_param()
+        self.rewrite_set_param_and_get_stats()
         self.rewrite_force_virtual(vrefinfo)
         self.rewrite_force_quasi_immutable()
         self.add_finish()
@@ -632,14 +633,22 @@
             self.rewrite_access_helper(op)
 
     def rewrite_access_helper(self, op):
-        ARGS = [arg.concretetype for arg in op.args[2:]]
-        RESULT = op.result.concretetype
-        FUNCPTR = lltype.Ptr(lltype.FuncType(ARGS, RESULT))
         # make sure we make a copy of function so it no longer belongs
         # to extregistry
         func = op.args[1].value
-        func = func_with_new_name(func, func.func_name + '_compiled')
-        ptr = self.helper_func(FUNCPTR, func)
+        if func.func_name.startswith('stats_'):
+            # get special treatment since we rewrite it to a call that accepts
+            # jit driver
+            func = func_with_new_name(func, func.func_name + '_compiled')
+            def new_func(ignored, *args):
+                return func(self, *args)
+            ARGS = [lltype.Void] + [arg.concretetype for arg in op.args[3:]]
+        else:
+            ARGS = [arg.concretetype for arg in op.args[2:]]
+            new_func = func_with_new_name(func, func.func_name + '_compiled')
+        RESULT = op.result.concretetype
+        FUNCPTR = lltype.Ptr(lltype.FuncType(ARGS, RESULT))
+        ptr = self.helper_func(FUNCPTR, new_func)
         op.opname = 'direct_call'
         op.args = [Constant(ptr, FUNCPTR)] + op.args[2:]
 
@@ -859,7 +868,7 @@
             call_final_function(self.translator, finish,
                                 annhelper = self.annhelper)
 
-    def rewrite_set_param(self):
+    def rewrite_set_param_and_get_stats(self):
         from pypy.rpython.lltypesystem.rstr import STR
 
         closures = {}
diff --git a/pypy/module/cpyext/intobject.py b/pypy/module/cpyext/intobject.py
--- a/pypy/module/cpyext/intobject.py
+++ b/pypy/module/cpyext/intobject.py
@@ -6,7 +6,7 @@
     PyObject, PyObjectFields, CONST_STRING, CANNOT_FAIL, Py_ssize_t)
 from pypy.module.cpyext.pyobject import (
     make_typedescr, track_reference, RefcountState, from_ref)
-from pypy.rlib.rarithmetic import r_uint, intmask, LONG_TEST
+from pypy.rlib.rarithmetic import r_uint, intmask, LONG_TEST, r_ulonglong
 from pypy.objspace.std.intobject import W_IntObject
 import sys
 
@@ -83,6 +83,20 @@
         num = space.bigint_w(w_int)
         return num.uintmask()
 
+ at cpython_api([PyObject], rffi.ULONGLONG, error=-1)
+def PyInt_AsUnsignedLongLongMask(space, w_obj):
+    """Will first attempt to cast the object to a PyIntObject or
+    PyLongObject, if it is not already one, and then return its value as
+    unsigned long long, without checking for overflow.
+    """
+    w_int = space.int(w_obj)
+    if space.is_true(space.isinstance(w_int, space.w_int)):
+        num = space.int_w(w_int)
+        return r_ulonglong(num)
+    else:
+        num = space.bigint_w(w_int)
+        return num.ulonglongmask()
+
 @cpython_api([PyObject], lltype.Signed, error=CANNOT_FAIL)
 def PyInt_AS_LONG(space, w_int):
     """Return the value of the object w_int. No error checking is performed."""
diff --git a/pypy/module/cpyext/test/test_intobject.py b/pypy/module/cpyext/test/test_intobject.py
--- a/pypy/module/cpyext/test/test_intobject.py
+++ b/pypy/module/cpyext/test/test_intobject.py
@@ -34,6 +34,11 @@
         assert (api.PyInt_AsUnsignedLongMask(space.wrap(10**30))
                 == 10**30 % ((sys.maxint + 1) * 2))
 
+        assert (api.PyInt_AsUnsignedLongLongMask(space.wrap(sys.maxint))
+                == sys.maxint)
+        assert (api.PyInt_AsUnsignedLongLongMask(space.wrap(10**30))
+                == 10**30 % (2**64))
+
     def test_coerce(self, space, api):
         class Coerce(object):
             def __int__(self):
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
@@ -166,4 +166,5 @@
         'eye': 'app_numpy.eye',
         'max': 'app_numpy.max',
         'arange': 'app_numpy.arange',
+        'count_nonzero': 'app_numpy.count_nonzero',
     }
diff --git a/pypy/module/micronumpy/app_numpy.py b/pypy/module/micronumpy/app_numpy.py
--- a/pypy/module/micronumpy/app_numpy.py
+++ b/pypy/module/micronumpy/app_numpy.py
@@ -2,6 +2,10 @@
 
 import _numpypy
 
+def count_nonzero(a):
+    if not hasattr(a, 'count_nonzero'):
+        a = _numpypy.array(a)
+    return a.count_nonzero()
 
 def average(a):
     # This implements a weighted average, for now we don't implement the
diff --git a/pypy/module/micronumpy/compile.py b/pypy/module/micronumpy/compile.py
--- a/pypy/module/micronumpy/compile.py
+++ b/pypy/module/micronumpy/compile.py
@@ -35,7 +35,7 @@
     pass
 
 SINGLE_ARG_FUNCTIONS = ["sum", "prod", "max", "min", "all", "any",
-                        "unegative", "flat", "tostring"]
+                        "unegative", "flat", "tostring","count_nonzero"]
 TWO_ARG_FUNCTIONS = ["dot", 'take']
 THREE_ARG_FUNCTIONS = ['where']
 
@@ -445,6 +445,8 @@
             elif self.name == "tostring":
                 arr.descr_tostring(interp.space)
                 w_res = None
+            elif self.name == "count_nonzero":
+                w_res = arr.descr_count_nonzero(interp.space)
             else:
                 assert False # unreachable code
         elif self.name in TWO_ARG_FUNCTIONS:
@@ -478,6 +480,8 @@
             return w_res
         if isinstance(w_res, FloatObject):
             dtype = get_dtype_cache(interp.space).w_float64dtype
+        elif isinstance(w_res, IntObject):
+            dtype = get_dtype_cache(interp.space).w_int64dtype
         elif isinstance(w_res, BoolObject):
             dtype = get_dtype_cache(interp.space).w_booldtype
         elif isinstance(w_res, interp_boxes.W_GenericBox):
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
@@ -402,6 +402,11 @@
                 i += 1
         return Chunks(result)
 
+    def descr_count_nonzero(self, space):
+        concr = self.get_concrete()
+        res = concr.count_all_true()
+        return space.wrap(res)
+
     def count_all_true(self):
         sig = self.find_sig()
         frame = sig.create_frame(self)
@@ -1486,6 +1491,7 @@
     take = interp2app(BaseArray.descr_take),
     compress = interp2app(BaseArray.descr_compress),
     repeat = interp2app(BaseArray.descr_repeat),
+    count_nonzero = interp2app(BaseArray.descr_count_nonzero),
 )
 
 
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
@@ -2042,6 +2042,12 @@
         raises(ValueError, "array(5).item(1)")
         assert array([1]).item() == 1
 
+    def test_count_nonzero(self):
+        from _numpypy import array
+        a = array([1,0,5,0,10])
+        assert a.count_nonzero() == 3
+ 
+
 class AppTestSupport(BaseNumpyAppTest):
     def setup_class(cls):
         import struct
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
@@ -640,6 +640,13 @@
         raises(ValueError, count_reduce_items, a, -4)
         raises(ValueError, count_reduce_items, a, (0, 2, -4))
 
+    def test_count_nonzero(self):
+        from _numpypy import where, count_nonzero, arange
+        a = arange(10)
+        assert count_nonzero(a) == 9
+        a[9] = 0
+        assert count_nonzero(a) == 8
+
     def test_true_divide(self):
         from _numpypy import arange, array, true_divide
         assert (true_divide(arange(3), array([2, 2, 2])) == array([0, 0.5, 1])).all()
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
@@ -479,3 +479,22 @@
                                 'int_sub': 3,
                                 'jump': 1,
                                 'setinteriorfield_raw': 1})
+    
+    def define_count_nonzero():
+        return """
+        a = [[0, 2, 3, 4], [5, 6, 0, 8], [9, 10, 11, 0]]
+        count_nonzero(a) 
+        """
+
+    def test_count_nonzero(self):
+        result = self.run("count_nonzero")
+        assert result == 9
+        self.check_simple_loop({'setfield_gc': 3, 
+                                'getinteriorfield_raw': 1, 
+                                'guard_false': 1, 
+                                'jump': 1, 
+                                'int_ge': 1, 
+                                'new_with_vtable': 1, 
+                                'int_add': 2, 
+                                'float_ne': 1})
+
diff --git a/pypy/module/pypyjit/__init__.py b/pypy/module/pypyjit/__init__.py
--- a/pypy/module/pypyjit/__init__.py
+++ b/pypy/module/pypyjit/__init__.py
@@ -10,8 +10,12 @@
         'set_compile_hook': 'interp_resop.set_compile_hook',
         'set_optimize_hook': 'interp_resop.set_optimize_hook',
         'set_abort_hook': 'interp_resop.set_abort_hook',
+        'get_stats_snapshot': 'interp_resop.get_stats_snapshot',
+        'enable_debug': 'interp_resop.enable_debug',
+        'disable_debug': 'interp_resop.disable_debug',
         'ResOperation': 'interp_resop.WrappedOp',
         'DebugMergePoint': 'interp_resop.DebugMergePoint',
+        'JitLoopInfo': 'interp_resop.W_JitLoopInfo',
         'Box': 'interp_resop.WrappedBox',
         'PARAMETER_DOCS': 'space.wrap(pypy.rlib.jit.PARAMETER_DOCS)',
     }
diff --git a/pypy/module/pypyjit/interp_resop.py b/pypy/module/pypyjit/interp_resop.py
--- a/pypy/module/pypyjit/interp_resop.py
+++ b/pypy/module/pypyjit/interp_resop.py
@@ -11,16 +11,23 @@
 from pypy.jit.metainterp.resoperation import rop, AbstractResOp
 from pypy.rlib.nonconst import NonConstant
 from pypy.rlib import jit_hooks
+from pypy.rlib.jit import Counters
+from pypy.rlib.rarithmetic import r_uint
 from pypy.module.pypyjit.interp_jit import pypyjitdriver
 
 class Cache(object):
     in_recursion = False
+    no = 0
 
     def __init__(self, space):
         self.w_compile_hook = space.w_None
         self.w_abort_hook = space.w_None
         self.w_optimize_hook = space.w_None
 
+    def getno(self):
+        self.no += 1
+        return self.no - 1
+
 def wrap_greenkey(space, jitdriver, greenkey, greenkey_repr):
     if greenkey is None:
         return space.w_None
@@ -40,23 +47,9 @@
     """ set_compile_hook(hook)
 
     Set a compiling hook that will be called each time a loop is compiled.
-    The hook will be called with the following signature:
-    hook(jitdriver_name, loop_type, greenkey or guard_number, operations,
-         assembler_addr, assembler_length)
 
-    jitdriver_name is the name of this particular jitdriver, 'pypyjit' is
-    the main interpreter loop
-
-    loop_type can be either `loop` `entry_bridge` or `bridge`
-    in case loop is not `bridge`, greenkey will be a tuple of constants
-    or a string describing it.
-
-    for the interpreter loop` it'll be a tuple
-    (code, offset, is_being_profiled)
-
-    assembler_addr is an integer describing where assembler starts,
-    can be accessed via ctypes, assembler_lenght is the lenght of compiled
-    asm
+    The hook will be called with the pypyjit.JitLoopInfo object. Refer to it's
+    docstring for details.
 
     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
@@ -73,22 +66,8 @@
     but before assembler compilation. This allows to add additional
     optimizations on Python level.
 
-    The hook will be called with the following signature:
-    hook(jitdriver_name, loop_type, greenkey or guard_number, operations)
-
-    jitdriver_name is the name of this particular jitdriver, 'pypyjit' is
-    the main interpreter loop
-
-    loop_type can be either `loop` `entry_bridge` or `bridge`
-    in case loop is not `bridge`, greenkey will be a tuple of constants
-    or a string describing it.
-
-    for the interpreter loop` 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.
+    The hook will be called with the pypyjit.JitLoopInfo object. Refer to it's
+    docstring for details.
 
     Result value will be the resulting list of operations, or None
     """
@@ -209,6 +188,10 @@
         jit_hooks.resop_setresult(self.op, box.llbox)
 
 class DebugMergePoint(WrappedOp):
+    """ A class representing Debug Merge Point - the entry point
+    to a jitted loop.
+    """
+    
     def __init__(self, space, op, repr_of_resop, jd_name, call_depth, call_id,
         w_greenkey):
 
@@ -248,13 +231,149 @@
 DebugMergePoint.typedef = TypeDef(
     'DebugMergePoint', WrappedOp.typedef,
     __new__ = interp2app(descr_new_dmp),
-    greenkey = interp_attrproperty_w("w_greenkey", cls=DebugMergePoint),
+    __doc__ = DebugMergePoint.__doc__,
+    greenkey = interp_attrproperty_w("w_greenkey", cls=DebugMergePoint,
+               doc="Representation of place where the loop was compiled. "
+                    "In the case of the main interpreter loop, it's a triplet "
+                    "(code, ofs, is_profiled)"),
     pycode = GetSetProperty(DebugMergePoint.get_pycode),
-    bytecode_no = GetSetProperty(DebugMergePoint.get_bytecode_no),
-    call_depth = interp_attrproperty("call_depth", cls=DebugMergePoint),
-    call_id = interp_attrproperty("call_id", cls=DebugMergePoint),
-    jitdriver_name = GetSetProperty(DebugMergePoint.get_jitdriver_name),
+    bytecode_no = GetSetProperty(DebugMergePoint.get_bytecode_no,
+                                 doc="offset in the bytecode"),
+    call_depth = interp_attrproperty("call_depth", cls=DebugMergePoint,
+                                     doc="Depth of calls within this loop"),
+    call_id = interp_attrproperty("call_id", cls=DebugMergePoint,
+                     doc="Number of applevel function traced in this loop"),
+    jitdriver_name = GetSetProperty(DebugMergePoint.get_jitdriver_name,
+                     doc="Name of the jitdriver 'pypyjit' in the case "
+                                    "of the main interpreter loop"),
 )
 DebugMergePoint.acceptable_as_base_class = False
 
+class W_JitLoopInfo(Wrappable):
+    """ Loop debug information
+    """
+    
+    w_green_key = None
+    bridge_no   = 0
+    asmaddr     = 0
+    asmlen      = 0
+    
+    def __init__(self, space, debug_info, is_bridge=False):
+        logops = debug_info.logger._make_log_operations()
+        if debug_info.asminfo is not None:
+            ofs = debug_info.asminfo.ops_offset
+        else:
+            ofs = {}
+        self.w_ops = space.newlist(
+            wrap_oplist(space, logops, debug_info.operations, ofs))
+        
+        self.jd_name = debug_info.get_jitdriver().name
+        self.type = debug_info.type
+        if is_bridge:
+            self.bridge_no = debug_info.fail_descr_no
+            self.w_green_key = space.w_None
+        else:
+            self.w_green_key = wrap_greenkey(space,
+                                             debug_info.get_jitdriver(),
+                                             debug_info.greenkey,
+                                             debug_info.get_greenkey_repr())
+        self.loop_no = debug_info.looptoken.number
+        asminfo = debug_info.asminfo
+        if asminfo is not None:
+            self.asmaddr = asminfo.asmaddr
+            self.asmlen = asminfo.asmlen
 
+    def descr_repr(self, space):
+        lgt = space.int_w(space.len(self.w_ops))
+        if self.type == "bridge":
+            code_repr = 'bridge no %d' % self.bridge_no
+        else:
+            code_repr = space.str_w(space.repr(self.w_green_key))
+        return space.wrap('<JitLoopInfo %s, %d operations, starting at <%s>>' %
+                          (self.jd_name, lgt, code_repr))
+
+ at unwrap_spec(loopno=int, asmaddr=int, asmlen=int, loop_no=int,
+             type=str, jd_name=str, bridge_no=int)
+def descr_new_jit_loop_info(space, w_subtype, w_greenkey, w_ops, loopno,
+                            asmaddr, asmlen, loop_no, type, jd_name, bridge_no):
+    w_info = space.allocate_instance(W_JitLoopInfo, w_subtype)
+    w_info.w_green_key = w_greenkey
+    w_info.w_ops = w_ops
+    w_info.asmaddr = asmaddr
+    w_info.asmlen = asmlen
+    w_info.loop_no = loop_no
+    w_info.type = type
+    w_info.jd_name = jd_name
+    w_info.bridge_no = bridge_no
+    return w_info
+
+W_JitLoopInfo.typedef = TypeDef(
+    'JitLoopInfo',
+    __doc__ = W_JitLoopInfo.__doc__,
+    __new__ = interp2app(descr_new_jit_loop_info),
+    jitdriver_name = interp_attrproperty('jd_name', cls=W_JitLoopInfo,
+                       doc="Name of the JitDriver, pypyjit for the main one"),
+    greenkey = interp_attrproperty_w('w_green_key', cls=W_JitLoopInfo,
+               doc="Representation of place where the loop was compiled. "
+                    "In the case of the main interpreter loop, it's a triplet "
+                    "(code, ofs, is_profiled)"),
+    operations = interp_attrproperty_w('w_ops', cls=W_JitLoopInfo, doc=
+                                       "List of operations in this loop."),
+    loop_no = interp_attrproperty('loop_no', cls=W_JitLoopInfo, doc=
+                                  "Loop cardinal number"),
+    __repr__ = interp2app(W_JitLoopInfo.descr_repr),
+)
+W_JitLoopInfo.acceptable_as_base_class = False
+
+class W_JitInfoSnapshot(Wrappable):
+    def __init__(self, space, w_times, w_counters, w_counter_times):
+        self.w_loop_run_times = w_times
+        self.w_counters = w_counters
+        self.w_counter_times = w_counter_times
+
+W_JitInfoSnapshot.typedef = TypeDef(
+    "JitInfoSnapshot",
+    w_loop_run_times = interp_attrproperty_w("w_loop_run_times",
+                                             cls=W_JitInfoSnapshot),
+    w_counters = interp_attrproperty_w("w_counters",
+                                       cls=W_JitInfoSnapshot,
+                                       doc="various JIT counters"),
+    w_counter_times = interp_attrproperty_w("w_counter_times",
+                                            cls=W_JitInfoSnapshot,
+                                            doc="various JIT timers")
+)
+W_JitInfoSnapshot.acceptable_as_base_class = False
+
+def get_stats_snapshot(space):
+    """ Get the jit status in the specific moment in time. Note that this
+    is eager - the attribute access is not lazy, if you need new stats
+    you need to call this function again.
+    """
+    ll_times = jit_hooks.stats_get_loop_run_times(None)
+    w_times = space.newdict()
+    for i in range(len(ll_times)):
+        space.setitem(w_times, space.wrap(ll_times[i].number),
+                      space.wrap(ll_times[i].counter))
+    w_counters = space.newdict()
+    for i, counter_name in enumerate(Counters.counter_names):
+        v = jit_hooks.stats_get_counter_value(None, i)
+        space.setitem_str(w_counters, counter_name, space.wrap(v))
+    w_counter_times = space.newdict()
+    tr_time = jit_hooks.stats_get_times_value(None, Counters.TRACING)
+    space.setitem_str(w_counter_times, 'TRACING', space.wrap(tr_time))
+    b_time = jit_hooks.stats_get_times_value(None, Counters.BACKEND)
+    space.setitem_str(w_counter_times, 'BACKEND', space.wrap(b_time))
+    return space.wrap(W_JitInfoSnapshot(space, w_times, w_counters,
+                                        w_counter_times))
+
+def enable_debug(space):
+    """ Set the jit debugging - completely necessary for some stats to work,
+    most notably assembler counters.
+    """
+    jit_hooks.stats_set_debug(None, True)
+
+def disable_debug(space):
+    """ Disable the jit debugging. This means some very small loops will be
+    marginally faster and the counters will stop working.
+    """
+    jit_hooks.stats_set_debug(None, False)
diff --git a/pypy/module/pypyjit/policy.py b/pypy/module/pypyjit/policy.py
--- a/pypy/module/pypyjit/policy.py
+++ b/pypy/module/pypyjit/policy.py
@@ -1,10 +1,9 @@
 from pypy.jit.codewriter.policy import JitPolicy
-from pypy.rlib.jit import JitHookInterface
+from pypy.rlib.jit import JitHookInterface, Counters
 from pypy.rlib import jit_hooks
 from pypy.interpreter.error import OperationError
-from pypy.jit.metainterp.jitprof import counter_names
-from pypy.module.pypyjit.interp_resop import wrap_oplist, Cache, wrap_greenkey,\
-     WrappedOp
+from pypy.module.pypyjit.interp_resop import Cache, wrap_greenkey,\
+     WrappedOp, W_JitLoopInfo
 
 class PyPyJitIface(JitHookInterface):
     def on_abort(self, reason, jitdriver, greenkey, greenkey_repr):
@@ -20,75 +19,54 @@
                                         space.wrap(jitdriver.name),
                                         wrap_greenkey(space, jitdriver,
                                                       greenkey, greenkey_repr),
-                                        space.wrap(counter_names[reason]))
+                                        space.wrap(
+                                            Counters.counter_names[reason]))
                 except OperationError, e:
                     e.write_unraisable(space, "jit hook ", cache.w_abort_hook)
             finally:
                 cache.in_recursion = False
 
     def after_compile(self, debug_info):
-        w_greenkey = wrap_greenkey(self.space, debug_info.get_jitdriver(),
-                                   debug_info.greenkey,
-                                   debug_info.get_greenkey_repr())
-        self._compile_hook(debug_info, w_greenkey)
+        self._compile_hook(debug_info, is_bridge=False)
 
     def after_compile_bridge(self, debug_info):
-        self._compile_hook(debug_info,
-                           self.space.wrap(debug_info.fail_descr_no))
+        self._compile_hook(debug_info, is_bridge=True)
 
     def before_compile(self, debug_info):
-        w_greenkey = wrap_greenkey(self.space, debug_info.get_jitdriver(),
-                                   debug_info.greenkey,
-                                   debug_info.get_greenkey_repr())
-        self._optimize_hook(debug_info, w_greenkey)
+        self._optimize_hook(debug_info, is_bridge=False)
 
     def before_compile_bridge(self, debug_info):
-        self._optimize_hook(debug_info,
-                            self.space.wrap(debug_info.fail_descr_no))
+        self._optimize_hook(debug_info, is_bridge=True)
 
-    def _compile_hook(self, debug_info, w_arg):
+    def _compile_hook(self, debug_info, is_bridge):
         space = self.space
         cache = space.fromcache(Cache)
         if cache.in_recursion:
             return
         if space.is_true(cache.w_compile_hook):
-            logops = debug_info.logger._make_log_operations()
-            list_w = wrap_oplist(space, logops, debug_info.operations,
-                                 debug_info.asminfo.ops_offset)
+            w_debug_info = W_JitLoopInfo(space, debug_info, is_bridge)
             cache.in_recursion = True
             try:
                 try:
-                    jd_name = debug_info.get_jitdriver().name
-                    asminfo = debug_info.asminfo
                     space.call_function(cache.w_compile_hook,
-                                        space.wrap(jd_name),
-                                        space.wrap(debug_info.type),
-                                        w_arg,
-                                        space.newlist(list_w),
-                                        space.wrap(asminfo.asmaddr),
-                                        space.wrap(asminfo.asmlen))
+                                        space.wrap(w_debug_info))
                 except OperationError, e:
                     e.write_unraisable(space, "jit hook ", cache.w_compile_hook)
             finally:
                 cache.in_recursion = False
 
-    def _optimize_hook(self, debug_info, w_arg):
+    def _optimize_hook(self, debug_info, is_bridge=False):
         space = self.space
         cache = space.fromcache(Cache)
         if cache.in_recursion:
             return
         if space.is_true(cache.w_optimize_hook):
-            logops = debug_info.logger._make_log_operations()
-            list_w = wrap_oplist(space, logops, debug_info.operations)
+            w_debug_info = W_JitLoopInfo(space, debug_info, is_bridge)
             cache.in_recursion = True
             try:
                 try:
-                    jd_name = debug_info.get_jitdriver().name
                     w_res = space.call_function(cache.w_optimize_hook,
-                                                space.wrap(jd_name),
-                                                space.wrap(debug_info.type),
-                                                w_arg,
-                                                space.newlist(list_w))
+                                                space.wrap(w_debug_info))
                     if space.is_w(w_res, space.w_None):
                         return
                     l = []
diff --git a/pypy/module/pypyjit/test/test_jit_hook.py b/pypy/module/pypyjit/test/test_jit_hook.py
--- a/pypy/module/pypyjit/test/test_jit_hook.py
+++ b/pypy/module/pypyjit/test/test_jit_hook.py
@@ -14,8 +14,7 @@
 from pypy.module.pypyjit.policy import pypy_hooks
 from pypy.jit.tool.oparser import parse
 from pypy.jit.metainterp.typesystem import llhelper
-from pypy.jit.metainterp.jitprof import ABORT_TOO_LONG
-from pypy.rlib.jit import JitDebugInfo, AsmInfo
+from pypy.rlib.jit import JitDebugInfo, AsmInfo, Counters
 
 class MockJitDriverSD(object):
     class warmstate(object):
@@ -64,8 +63,10 @@
             if i != 1:
                offset[op] = i
 
-        di_loop = JitDebugInfo(MockJitDriverSD, logger, JitCellToken(),
-                               oplist, 'loop', greenkey)
+        token = JitCellToken()
+        token.number = 0
+        di_loop = JitDebugInfo(MockJitDriverSD, logger, token, oplist, 'loop',
+                   greenkey)
         di_loop_optimize = JitDebugInfo(MockJitDriverSD, logger, JitCellToken(),
                                         oplist, 'loop', greenkey)
         di_loop.asminfo = AsmInfo(offset, 0, 0)
@@ -85,8 +86,8 @@
             pypy_hooks.before_compile(di_loop_optimize)
 
         def interp_on_abort():
-            pypy_hooks.on_abort(ABORT_TOO_LONG, pypyjitdriver, greenkey,
-                                'blah')
+            pypy_hooks.on_abort(Counters.ABORT_TOO_LONG, pypyjitdriver,
+                                greenkey, 'blah')
 
         cls.w_on_compile = space.wrap(interp2app(interp_on_compile))
         cls.w_on_compile_bridge = space.wrap(interp2app(interp_on_compile_bridge))
@@ -95,6 +96,7 @@
         cls.w_dmp_num = space.wrap(rop.DEBUG_MERGE_POINT)
         cls.w_on_optimize = space.wrap(interp2app(interp_on_optimize))
         cls.orig_oplist = oplist
+        cls.w_sorted_keys = space.wrap(sorted(Counters.counter_names))
 
     def setup_method(self, meth):
         self.__class__.oplist = self.orig_oplist[:]
@@ -103,22 +105,23 @@
         import pypyjit
         all = []
 
-        def hook(name, looptype, tuple_or_guard_no, ops, asmstart, asmlen):
-            all.append((name, looptype, tuple_or_guard_no, ops))
+        def hook(info):
+            all.append(info)
 
         self.on_compile()
         pypyjit.set_compile_hook(hook)
         assert not all
         self.on_compile()
         assert len(all) == 1
-        elem = all[0]
-        assert elem[0] == 'pypyjit'
-        assert elem[2][0].co_name == 'function'
-        assert elem[2][1] == 0
-        assert elem[2][2] == False
-        assert len(elem[3]) == 4
-        int_add = elem[3][0]
-        dmp = elem[3][1]
+        info = all[0]
+        assert info.jitdriver_name == 'pypyjit'
+        assert info.greenkey[0].co_name == 'function'
+        assert info.greenkey[1] == 0
+        assert info.greenkey[2] == False
+        assert info.loop_no == 0
+        assert len(info.operations) == 4
+        int_add = info.operations[0]
+        dmp = info.operations[1]
         assert isinstance(dmp, pypyjit.DebugMergePoint)
         assert dmp.pycode is self.f.func_code
         assert dmp.greenkey == (self.f.func_code, 0, False)
@@ -127,6 +130,8 @@
         assert int_add.name == 'int_add'
         assert int_add.num == self.int_add_num
         self.on_compile_bridge()
+        code_repr = "(<code object function, file '?', line 2>, 0, False)"
+        assert repr(all[0]) == '<JitLoopInfo pypyjit, 4 operations, starting at <%s>>' % code_repr
         assert len(all) == 2
         pypyjit.set_compile_hook(None)
         self.on_compile()
@@ -168,12 +173,12 @@
         import pypyjit
         l = []
 
-        def hook(*args):
-            l.append(args)
+        def hook(info):
+            l.append(info)
 
         pypyjit.set_compile_hook(hook)
         self.on_compile()
-        op = l[0][3][1]
+        op = l[0].operations[1]
         assert isinstance(op, pypyjit.ResOperation)
         assert 'function' in repr(op)
 
@@ -192,17 +197,17 @@
         import pypyjit
         l = []
 
-        def hook(name, looptype, tuple_or_guard_no, ops, *args):
-            l.append(ops)
+        def hook(info):
+            l.append(info.jitdriver_name)
 
-        def optimize_hook(name, looptype, tuple_or_guard_no, ops):
+        def optimize_hook(info):
             return []
 
         pypyjit.set_compile_hook(hook)
         pypyjit.set_optimize_hook(optimize_hook)
         self.on_optimize()
         self.on_compile()
-        assert l == [[]]
+        assert l == ['pypyjit']
 
     def test_creation(self):
         from pypyjit import Box, ResOperation
@@ -236,3 +241,13 @@
         op = DebugMergePoint([Box(0)], 'repr', 'notmain', 5, 4, ('str',))
         raises(AttributeError, 'op.pycode')
         assert op.call_depth == 5
+
+    def test_get_stats_snapshot(self):
+        skip("a bit no idea how to test it")
+        from pypyjit import get_stats_snapshot
+
+        stats = get_stats_snapshot() # we can't do much here, unfortunately
+        assert stats.w_loop_run_times == []
+        assert isinstance(stats.w_counters, dict)
+        assert sorted(stats.w_counters.keys()) == self.sorted_keys
+
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -600,7 +600,6 @@
                 raise ValueError
 set_user_param._annspecialcase_ = 'specialize:arg(0)'
 
-
 # ____________________________________________________________
 #
 # Annotation and rtyping of some of the JitDriver methods
@@ -901,11 +900,6 @@
         instance, overwrite for custom behavior
         """
 
-    def get_stats(self):
-        """ Returns various statistics
-        """
-        raise NotImplementedError
-
 def record_known_class(value, cls):
     """
     Assure the JIT that value is an instance of cls. This is not a precise
@@ -932,3 +926,39 @@
         v_cls = hop.inputarg(classrepr, arg=1)
         return hop.genop('jit_record_known_class', [v_inst, v_cls],
                          resulttype=lltype.Void)
+
+class Counters(object):
+    counters="""
+    TRACING
+    BACKEND
+    OPS
+    RECORDED_OPS
+    GUARDS
+    OPT_OPS
+    OPT_GUARDS
+    OPT_FORCINGS
+    ABORT_TOO_LONG
+    ABORT_BRIDGE
+    ABORT_BAD_LOOP
+    ABORT_ESCAPE
+    ABORT_FORCE_QUASIIMMUT
+    NVIRTUALS
+    NVHOLES
+    NVREUSED
+    TOTAL_COMPILED_LOOPS
+    TOTAL_COMPILED_BRIDGES
+    TOTAL_FREED_LOOPS
+    TOTAL_FREED_BRIDGES
+    """
+
+    counter_names = []
+
+    @staticmethod
+    def _setup():
+        names = Counters.counters.split()
+        for i, name in enumerate(names):
+            setattr(Counters, name, i)
+            Counters.counter_names.append(name)
+        Counters.ncounters = len(names)
+
+Counters._setup()
diff --git a/pypy/rlib/jit_hooks.py b/pypy/rlib/jit_hooks.py
--- a/pypy/rlib/jit_hooks.py
+++ b/pypy/rlib/jit_hooks.py
@@ -13,7 +13,10 @@
             _about_ = helper
 
             def compute_result_annotation(self, *args):
-                return s_result
+                if (isinstance(s_result, annmodel.SomeObject) or
+                    s_result is None):
+                    return s_result
+                return annmodel.lltype_to_annotation(s_result)
 
             def specialize_call(self, hop):
                 from pypy.rpython.lltypesystem import lltype
@@ -108,3 +111,26 @@
 def box_isconst(llbox):
     from pypy.jit.metainterp.history import Const
     return isinstance(_cast_to_box(llbox), Const)
+
+# ------------------------- stats interface ---------------------------
+
+ at register_helper(annmodel.SomeBool())
+def stats_set_debug(warmrunnerdesc, flag):
+    return warmrunnerdesc.metainterp_sd.cpu.set_debug(flag)
+
+ at register_helper(annmodel.SomeInteger())
+def stats_get_counter_value(warmrunnerdesc, no):
+    return warmrunnerdesc.metainterp_sd.profiler.get_counter(no)
+
+ at register_helper(annmodel.SomeFloat())
+def stats_get_times_value(warmrunnerdesc, no):
+    return warmrunnerdesc.metainterp_sd.profiler.times[no]
+
+LOOP_RUN_CONTAINER = lltype.GcArray(lltype.Struct('elem',
+                                                  ('type', lltype.Char),
+                                                  ('number', lltype.Signed),
+                                                  ('counter', lltype.Signed)))
+
+ at register_helper(lltype.Ptr(LOOP_RUN_CONTAINER))
+def stats_get_loop_run_times(warmrunnerdesc):
+    return warmrunnerdesc.metainterp_sd.cpu.get_all_loop_runs()
diff --git a/pypy/rpython/annlowlevel.py b/pypy/rpython/annlowlevel.py
--- a/pypy/rpython/annlowlevel.py
+++ b/pypy/rpython/annlowlevel.py
@@ -12,6 +12,7 @@
 from pypy.rpython import extregistry
 from pypy.objspace.flow.model import Constant
 from pypy.translator.simplify import get_functype
+from pypy.rpython.rmodel import warning
 
 class KeyComp(object):
     def __init__(self, val):
@@ -483,6 +484,8 @@
     """NOT_RPYTHON: hack. The object may be disguised as a PTR now.
     Limited to casting a given object to a single type.
     """
+    if hasattr(object, '_freeze_'):
+        warning("Trying to cast a frozen object to pointer")
     if isinstance(PTR, lltype.Ptr):
         TO = PTR.TO
     else:
diff --git a/pypy/rpython/rclass.py b/pypy/rpython/rclass.py
--- a/pypy/rpython/rclass.py
+++ b/pypy/rpython/rclass.py
@@ -378,6 +378,30 @@
     def rtype_is_true(self, hop):
         raise NotImplementedError
 
+    def _emulate_call(self, hop, meth_name):
+        vinst, = hop.inputargs(self)
+        clsdef = hop.args_s[0].classdef
+        s_unbound_attr = clsdef.find_attribute(meth_name).getvalue()
+        s_attr = clsdef.lookup_filter(s_unbound_attr, meth_name,
+                                      hop.args_s[0].flags)
+        if s_attr.is_constant():
+            xxx # does that even happen?
+        if '__iter__' in self.allinstancefields:
+            raise Exception("__iter__ on instance disallowed")
+        r_method = self.rtyper.makerepr(s_attr)
+        r_method.get_method_from_instance(self, vinst, hop.llops)
+        hop2 = hop.copy()
+        hop2.spaceop.opname = 'simple_call'
+        hop2.args_r = [r_method]
+        hop2.args_s = [s_attr]
+        return hop2.dispatch()
+
+    def rtype_iter(self, hop):
+        return self._emulate_call(hop, '__iter__')
+
+    def rtype_next(self, hop):
+        return self._emulate_call(hop, 'next')
+
     def ll_str(self, i):
         raise NotImplementedError
 
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
@@ -1143,6 +1143,62 @@
                                       'cast_pointer': 1,
                                       'setfield': 1}
 
+    def test_iter(self):
+        class Iterable(object):
+            def __init__(self):
+                self.counter = 0
+            
+            def __iter__(self):
+                return self
+
+            def next(self):
+                if self.counter == 5:
+                    raise StopIteration
+                self.counter += 1
+                return self.counter - 1
+
+        def f():
+            i = Iterable()
+            s = 0
+            for elem in i:
+                s += elem
+            return s
+
+        assert self.interpret(f, []) == f()
+
+    def test_iter_2_kinds(self):
+        class BaseIterable(object):
+            def __init__(self):
+                self.counter = 0
+            
+            def __iter__(self):
+                return self
+
+            def next(self):
+                if self.counter >= 5:
+                    raise StopIteration
+                self.counter += self.step
+                return self.counter - 1
+        
+        class Iterable(BaseIterable):
+            step = 1
+
+        class OtherIter(BaseIterable):
+            step = 2
+
+        def f(k):
+            if k:
+                i = Iterable()
+            else:
+                i = OtherIter()
+            s = 0
+            for elem in i:
+                s += elem
+            return s
+
+        assert self.interpret(f, [True]) == f(True)
+        assert self.interpret(f, [False]) == f(False)
+
 
 class TestOOtype(BaseTestRclass, OORtypeMixin):
 
diff --git a/pypy/translator/goal/richards.py b/pypy/translator/goal/richards.py
--- a/pypy/translator/goal/richards.py
+++ b/pypy/translator/goal/richards.py
@@ -343,8 +343,6 @@
 
 import time
 
-
-
 def schedule():
     t = taskWorkArea.taskList
     while t is not None:


More information about the pypy-commit mailing list