[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