[pypy-commit] pypy default: merge better-jit-hooks. This branch introduces few hooks on applevel that
fijal
noreply at buildbot.pypy.org
Wed Jan 11 21:37:45 CET 2012
Author: Maciej Fijalkowski <fijall at gmail.com>
Branch:
Changeset: r51248:db33420263f5
Date: 2012-01-11 22:36 +0200
http://bitbucket.org/pypy/pypy/changeset/db33420263f5/
Log: merge better-jit-hooks. This branch introduces few hooks on applevel
that let you introspect and modify the list of resops as it gets
compiled. Consult docstrings in
pypyjit.set_compile_hook/set_abort_hook/set_optimize_hook. for
details.
It also exposes interface how to get to JIT structures from
annotated parts of RPython, the details are in pypy.rlib.jit_hooks
diff --git a/lib_pypy/_ctypes/structure.py b/lib_pypy/_ctypes/structure.py
--- a/lib_pypy/_ctypes/structure.py
+++ b/lib_pypy/_ctypes/structure.py
@@ -73,8 +73,12 @@
class Field(object):
def __init__(self, name, offset, size, ctype, num, is_bitfield):
- for k in ('name', 'offset', 'size', 'ctype', 'num', 'is_bitfield'):
- self.__dict__[k] = locals()[k]
+ self.__dict__['name'] = name
+ self.__dict__['offset'] = offset
+ self.__dict__['size'] = size
+ self.__dict__['ctype'] = ctype
+ self.__dict__['num'] = num
+ self.__dict__['is_bitfield'] = is_bitfield
def __setattr__(self, name, value):
raise AttributeError(name)
diff --git a/pypy/interpreter/eval.py b/pypy/interpreter/eval.py
--- a/pypy/interpreter/eval.py
+++ b/pypy/interpreter/eval.py
@@ -2,7 +2,6 @@
This module defines the abstract base classes that support execution:
Code and Frame.
"""
-from pypy.rlib import jit
from pypy.interpreter.error import OperationError
from pypy.interpreter.baseobjspace import Wrappable
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -162,7 +162,8 @@
# generate 2 versions of the function and 2 jit drivers.
def _create_unpack_into():
jitdriver = jit.JitDriver(greens=['pycode'],
- reds=['self', 'frame', 'results'])
+ reds=['self', 'frame', 'results'],
+ name='unpack_into')
def unpack_into(self, results):
"""This is a hack for performance: runs the generator and collects
all produced items in a list."""
@@ -196,4 +197,4 @@
self.frame = None
return unpack_into
unpack_into = _create_unpack_into()
- unpack_into_w = _create_unpack_into()
\ No newline at end of file
+ unpack_into_w = _create_unpack_into()
diff --git a/pypy/jit/backend/llsupport/test/test_runner.py b/pypy/jit/backend/llsupport/test/test_runner.py
--- a/pypy/jit/backend/llsupport/test/test_runner.py
+++ b/pypy/jit/backend/llsupport/test/test_runner.py
@@ -8,6 +8,12 @@
class MyLLCPU(AbstractLLCPU):
supports_floats = True
+
+ class assembler(object):
+ @staticmethod
+ def set_debug(flag):
+ pass
+
def compile_loop(self, inputargs, operations, looptoken):
py.test.skip("llsupport test: cannot compile operations")
diff --git a/pypy/jit/backend/test/runner_test.py b/pypy/jit/backend/test/runner_test.py
--- a/pypy/jit/backend/test/runner_test.py
+++ b/pypy/jit/backend/test/runner_test.py
@@ -17,6 +17,7 @@
from pypy.rpython.llinterp import LLException
from pypy.jit.codewriter import heaptracker, longlong
from pypy.rlib.rarithmetic import intmask
+from pypy.jit.backend.detect_cpu import autodetect_main_model_and_size
def boxfloat(x):
return BoxFloat(longlong.getfloatstorage(x))
@@ -27,6 +28,9 @@
class Runner(object):
+ add_loop_instruction = ['overload for a specific cpu']
+ bridge_loop_instruction = ['overload for a specific cpu']
+
def execute_operation(self, opname, valueboxes, result_type, descr=None):
inputargs, operations = self._get_single_operation_list(opname,
result_type,
@@ -2974,6 +2978,56 @@
res = self.cpu.get_latest_value_int(0)
assert res == -10
+ def test_compile_asmlen(self):
+ from pypy.jit.backend.llsupport.llmodel import AbstractLLCPU
+ if not isinstance(self.cpu, AbstractLLCPU):
+ py.test.skip("pointless test on non-asm")
+ from pypy.jit.backend.x86.tool.viewcode import machine_code_dump
+ import ctypes
+ ops = """
+ [i2]
+ i0 = same_as(i2) # but forced to be in a register
+ label(i0, descr=1)
+ i1 = int_add(i0, i0)
+ guard_true(i1, descr=faildesr) [i1]
+ jump(i1, descr=1)
+ """
+ faildescr = BasicFailDescr(2)
+ loop = parse(ops, self.cpu, namespace=locals())
+ faildescr = loop.operations[-2].getdescr()
+ jumpdescr = loop.operations[-1].getdescr()
+ bridge_ops = """
+ [i0]
+ jump(i0, descr=jumpdescr)
+ """
+ bridge = parse(bridge_ops, self.cpu, namespace=locals())
+ looptoken = JitCellToken()
+ self.cpu.assembler.set_debug(False)
+ info = self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+ bridge_info = self.cpu.compile_bridge(faildescr, bridge.inputargs,
+ bridge.operations,
+ looptoken)
+ self.cpu.assembler.set_debug(True) # always on untranslated
+ assert info.asmlen != 0
+ cpuname = autodetect_main_model_and_size()
+ # XXX we have to check the precise assembler, otherwise
+ # we don't quite know if borders are correct
+
+ def checkops(mc, ops):
+ assert len(mc) == len(ops)
+ for i in range(len(mc)):
+ assert mc[i].split("\t")[-1].startswith(ops[i])
+
+ data = ctypes.string_at(info.asmaddr, info.asmlen)
+ mc = list(machine_code_dump(data, info.asmaddr, cpuname))
+ lines = [line for line in mc if line.count('\t') == 2]
+ checkops(lines, self.add_loop_instructions)
+ data = ctypes.string_at(bridge_info.asmaddr, bridge_info.asmlen)
+ mc = list(machine_code_dump(data, bridge_info.asmaddr, cpuname))
+ lines = [line for line in mc if line.count('\t') == 2]
+ checkops(lines, self.bridge_loop_instructions)
+
+
def test_compile_bridge_with_target(self):
# This test creates a loopy piece of code in a bridge, and builds another
# unrelated loop that ends in a jump directly to this loopy bit of code.
diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py
--- a/pypy/jit/backend/x86/assembler.py
+++ b/pypy/jit/backend/x86/assembler.py
@@ -7,6 +7,7 @@
from pypy.rpython.lltypesystem import lltype, rffi, rstr, llmemory
from pypy.rpython.lltypesystem.lloperation import llop
from pypy.rpython.annlowlevel import llhelper
+from pypy.rlib.jit import AsmInfo
from pypy.jit.backend.model import CompiledLoopToken
from pypy.jit.backend.x86.regalloc import (RegAlloc, get_ebp_ofs, _get_scale,
gpr_reg_mgr_cls, _valid_addressing_size)
@@ -411,6 +412,7 @@
'''adds the following attributes to looptoken:
_x86_function_addr (address of the generated func, as an int)
_x86_loop_code (debug: addr of the start of the ResOps)
+ _x86_fullsize (debug: full size including failure)
_x86_debug_checksum
'''
# XXX this function is too longish and contains some code
@@ -476,7 +478,8 @@
name = "Loop # %s: %s" % (looptoken.number, loopname)
self.cpu.profile_agent.native_code_written(name,
rawstart, full_size)
- return ops_offset
+ return AsmInfo(ops_offset, rawstart + looppos,
+ size_excluding_failure_stuff - looppos)
def assemble_bridge(self, faildescr, inputargs, operations,
original_loop_token, log):
@@ -485,12 +488,7 @@
assert len(set(inputargs)) == len(inputargs)
descr_number = self.cpu.get_fail_descr_number(faildescr)
- try:
- failure_recovery = self._find_failure_recovery_bytecode(faildescr)
- except ValueError:
- debug_print("Bridge out of guard", descr_number,
- "was already compiled!")
- return
+ failure_recovery = self._find_failure_recovery_bytecode(faildescr)
self.setup(original_loop_token)
if log:
@@ -503,6 +501,7 @@
[loc.assembler() for loc in faildescr._x86_debug_faillocs])
regalloc = RegAlloc(self, self.cpu.translate_support_code)
fail_depths = faildescr._x86_current_depths
+ startpos = self.mc.get_relative_pos()
operations = regalloc.prepare_bridge(fail_depths, inputargs, arglocs,
operations,
self.current_clt.allgcrefs)
@@ -537,7 +536,7 @@
name = "Bridge # %s" % (descr_number,)
self.cpu.profile_agent.native_code_written(name,
rawstart, fullsize)
- return ops_offset
+ return AsmInfo(ops_offset, startpos + rawstart, codeendpos - startpos)
def write_pending_failure_recoveries(self):
# for each pending guard, generate the code of the recovery stub
@@ -621,7 +620,10 @@
def _find_failure_recovery_bytecode(self, faildescr):
adr_jump_offset = faildescr._x86_adr_jump_offset
if adr_jump_offset == 0:
- raise ValueError
+ # This case should be prevented by the logic in compile.py:
+ # look for CNT_BUSY_FLAG, which disables tracing from a guard
+ # when another tracing from the same guard is already in progress.
+ raise BridgeAlreadyCompiled
# follow the JMP/Jcond
p = rffi.cast(rffi.INTP, adr_jump_offset)
adr_target = adr_jump_offset + 4 + rffi.cast(lltype.Signed, p[0])
@@ -810,7 +812,10 @@
target = newlooptoken._x86_function_addr
mc = codebuf.MachineCodeBlockWrapper()
mc.JMP(imm(target))
- assert mc.get_relative_pos() <= 13 # keep in sync with prepare_loop()
+ if WORD == 4: # keep in sync with prepare_loop()
+ assert mc.get_relative_pos() == 5
+ else:
+ assert mc.get_relative_pos() <= 13
mc.copy_to_raw_memory(oldadr)
def dump(self, text):
@@ -2550,3 +2555,6 @@
def not_implemented(msg):
os.write(2, '[x86/asm] %s\n' % msg)
raise NotImplementedError(msg)
+
+class BridgeAlreadyCompiled(Exception):
+ pass
diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py
--- a/pypy/jit/backend/x86/regalloc.py
+++ b/pypy/jit/backend/x86/regalloc.py
@@ -188,7 +188,10 @@
# note: we need to make a copy of inputargs because possibly_free_vars
# is also used on op args, which is a non-resizable list
self.possibly_free_vars(list(inputargs))
- self.min_bytes_before_label = 13
+ if WORD == 4: # see redirect_call_assembler()
+ self.min_bytes_before_label = 5
+ else:
+ self.min_bytes_before_label = 13
return operations
def prepare_bridge(self, prev_depths, inputargs, arglocs, operations,
diff --git a/pypy/jit/backend/x86/test/test_runner.py b/pypy/jit/backend/x86/test/test_runner.py
--- a/pypy/jit/backend/x86/test/test_runner.py
+++ b/pypy/jit/backend/x86/test/test_runner.py
@@ -33,6 +33,13 @@
# for the individual tests see
# ====> ../../test/runner_test.py
+ add_loop_instructions = ['mov', 'add', 'test', 'je', 'jmp']
+ if WORD == 4:
+ bridge_loop_instructions = ['lea', 'jmp']
+ else:
+ # the 'mov' is part of the 'jmp' so far
+ bridge_loop_instructions = ['lea', 'mov', 'jmp']
+
def setup_method(self, meth):
self.cpu = CPU(rtyper=None, stats=FakeStats())
self.cpu.setup_once()
@@ -416,7 +423,8 @@
]
inputargs = [i0]
debug._log = dlog = debug.DebugLog()
- ops_offset = self.cpu.compile_loop(inputargs, operations, looptoken)
+ info = self.cpu.compile_loop(inputargs, operations, looptoken)
+ ops_offset = info.ops_offset
debug._log = None
#
assert ops_offset is looptoken._x86_ops_offset
diff --git a/pypy/jit/backend/x86/tool/viewcode.py b/pypy/jit/backend/x86/tool/viewcode.py
--- a/pypy/jit/backend/x86/tool/viewcode.py
+++ b/pypy/jit/backend/x86/tool/viewcode.py
@@ -39,6 +39,7 @@
def machine_code_dump(data, originaddr, backend_name, label_list=None):
objdump_backend_option = {
'x86': 'i386',
+ 'x86_32': 'i386',
'x86_64': 'x86-64',
'i386': 'i386',
}
diff --git a/pypy/jit/codewriter/policy.py b/pypy/jit/codewriter/policy.py
--- a/pypy/jit/codewriter/policy.py
+++ b/pypy/jit/codewriter/policy.py
@@ -8,11 +8,15 @@
class JitPolicy(object):
- def __init__(self):
+ def __init__(self, jithookiface=None):
self.unsafe_loopy_graphs = set()
self.supports_floats = False
self.supports_longlong = False
self.supports_singlefloats = False
+ if jithookiface is None:
+ from pypy.rlib.jit import JitHookInterface
+ jithookiface = JitHookInterface()
+ self.jithookiface = jithookiface
def set_supports_floats(self, flag):
self.supports_floats = flag
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,6 +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.conftest import option
from pypy.tool.sourcetools import func_with_new_name
@@ -75,7 +76,7 @@
if descr is not original_jitcell_token:
original_jitcell_token.record_jump_to(descr)
descr.exported_state = None
- op._descr = None # clear reference, mostly for tests
+ op.cleardescr() # clear reference, mostly for tests
elif isinstance(descr, TargetToken):
# for a JUMP: record it as a potential jump.
# (the following test is not enough to prevent more complicated
@@ -90,8 +91,8 @@
assert descr.exported_state is None
if not we_are_translated():
op._descr_wref = weakref.ref(op._descr)
- op._descr = None # clear reference to prevent the history.Stats
- # from keeping the loop alive during tests
+ op.cleardescr() # clear reference to prevent the history.Stats
+ # from keeping the loop alive during tests
# record this looptoken on the QuasiImmut used in the code
if loop.quasi_immutable_deps is not None:
for qmut in loop.quasi_immutable_deps:
@@ -296,8 +297,6 @@
patch_new_loop_to_load_virtualizable_fields(loop, jitdriver_sd)
original_jitcell_token = loop.original_jitcell_token
- jitdriver_sd.on_compile(metainterp_sd.logger_ops, original_jitcell_token,
- loop.operations, type, greenkey)
loopname = jitdriver_sd.warmstate.get_location_str(greenkey)
globaldata = metainterp_sd.globaldata
original_jitcell_token.number = n = globaldata.loopnumbering
@@ -307,21 +306,38 @@
show_procedures(metainterp_sd, loop)
loop.check_consistency()
+ if metainterp_sd.warmrunnerdesc is not None:
+ hooks = metainterp_sd.warmrunnerdesc.hooks
+ debug_info = JitDebugInfo(jitdriver_sd, metainterp_sd.logger_ops,
+ original_jitcell_token, loop.operations,
+ type, greenkey)
+ hooks.before_compile(debug_info)
+ else:
+ debug_info = None
+ hooks = None
operations = get_deep_immutable_oplist(loop.operations)
metainterp_sd.profiler.start_backend()
debug_start("jit-backend")
try:
- ops_offset = metainterp_sd.cpu.compile_loop(loop.inputargs, operations,
- original_jitcell_token, name=loopname)
+ asminfo = metainterp_sd.cpu.compile_loop(loop.inputargs, operations,
+ original_jitcell_token,
+ name=loopname)
finally:
debug_stop("jit-backend")
metainterp_sd.profiler.end_backend()
+ if hooks is not None:
+ debug_info.asminfo = asminfo
+ hooks.after_compile(debug_info)
metainterp_sd.stats.add_new_loop(loop)
if not we_are_translated():
metainterp_sd.stats.compiled()
metainterp_sd.log("compiled new " + type)
#
loopname = jitdriver_sd.warmstate.get_location_str(greenkey)
+ if asminfo is not None:
+ ops_offset = asminfo.ops_offset
+ else:
+ ops_offset = None
metainterp_sd.logger_ops.log_loop(loop.inputargs, loop.operations, n,
type, ops_offset,
name=loopname)
@@ -332,25 +348,40 @@
def send_bridge_to_backend(jitdriver_sd, metainterp_sd, faildescr, inputargs,
operations, original_loop_token):
n = metainterp_sd.cpu.get_fail_descr_number(faildescr)
- jitdriver_sd.on_compile_bridge(metainterp_sd.logger_ops,
- original_loop_token, operations, n)
if not we_are_translated():
show_procedures(metainterp_sd)
seen = dict.fromkeys(inputargs)
TreeLoop.check_consistency_of_branch(operations, seen)
+ if metainterp_sd.warmrunnerdesc is not None:
+ hooks = metainterp_sd.warmrunnerdesc.hooks
+ debug_info = JitDebugInfo(jitdriver_sd, metainterp_sd.logger_ops,
+ original_loop_token, operations, 'bridge',
+ fail_descr_no=n)
+ hooks.before_compile_bridge(debug_info)
+ else:
+ hooks = None
+ debug_info = None
+ operations = get_deep_immutable_oplist(operations)
metainterp_sd.profiler.start_backend()
- operations = get_deep_immutable_oplist(operations)
debug_start("jit-backend")
try:
- ops_offset = metainterp_sd.cpu.compile_bridge(faildescr, inputargs, operations,
- original_loop_token)
+ asminfo = metainterp_sd.cpu.compile_bridge(faildescr, inputargs,
+ operations,
+ original_loop_token)
finally:
debug_stop("jit-backend")
metainterp_sd.profiler.end_backend()
+ if hooks is not None:
+ debug_info.asminfo = asminfo
+ hooks.after_compile_bridge(debug_info)
if not we_are_translated():
metainterp_sd.stats.compiled()
metainterp_sd.log("compiled new bridge")
#
+ if asminfo is not None:
+ ops_offset = asminfo.ops_offset
+ else:
+ ops_offset = None
metainterp_sd.logger_ops.log_bridge(inputargs, operations, n, ops_offset)
#
#if metainterp_sd.warmrunnerdesc is not None: # for tests
diff --git a/pypy/jit/metainterp/jitdriver.py b/pypy/jit/metainterp/jitdriver.py
--- a/pypy/jit/metainterp/jitdriver.py
+++ b/pypy/jit/metainterp/jitdriver.py
@@ -21,7 +21,6 @@
# self.portal_finishtoken... pypy.jit.metainterp.pyjitpl
# self.index ... pypy.jit.codewriter.call
# self.mainjitcode ... pypy.jit.codewriter.call
- # self.on_compile ... pypy.jit.metainterp.warmstate
# These attributes are read by the backend in CALL_ASSEMBLER:
# self.assembler_helper_adr
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
@@ -18,8 +18,8 @@
OPT_FORCINGS
ABORT_TOO_LONG
ABORT_BRIDGE
+ABORT_BAD_LOOP
ABORT_ESCAPE
-ABORT_BAD_LOOP
ABORT_FORCE_QUASIIMMUT
NVIRTUALS
NVHOLES
@@ -30,10 +30,13 @@
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()
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -117,7 +117,7 @@
def optimize_loop(self, ops, optops, call_pure_results=None):
loop = self.parse(ops)
- token = JitCellToken()
+ token = JitCellToken()
loop.operations = [ResOperation(rop.LABEL, loop.inputargs, None, descr=TargetToken(token))] + \
loop.operations
if loop.operations[-1].getopnum() == rop.JUMP:
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
@@ -1793,6 +1793,15 @@
def aborted_tracing(self, reason):
self.staticdata.profiler.count(reason)
debug_print('~~~ ABORTING TRACING')
+ jd_sd = self.jitdriver_sd
+ if not self.current_merge_points:
+ greenkey = None # we're in the bridge
+ else:
+ greenkey = self.current_merge_points[0][0][:jd_sd.num_green_args]
+ self.staticdata.warmrunnerdesc.hooks.on_abort(reason,
+ jd_sd.jitdriver,
+ greenkey,
+ jd_sd.warmstate.get_location_str(greenkey))
self.staticdata.stats.aborted()
def blackhole_if_trace_too_long(self):
@@ -1967,8 +1976,6 @@
self.compile_loop(original_boxes, live_arg_boxes, start, resumedescr)
# creation of the loop was cancelled!
self.staticdata.log('cancelled, tracing more...')
- #self.staticdata.log('cancelled, stopping tracing')
- #raise SwitchToBlackhole(ABORT_BAD_LOOP)
# Otherwise, no loop found so far, so continue tracing.
start = len(self.history.operations)
diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py
--- a/pypy/jit/metainterp/resoperation.py
+++ b/pypy/jit/metainterp/resoperation.py
@@ -18,6 +18,8 @@
pc = 0
opnum = 0
+ _attrs_ = ('result',)
+
def __init__(self, result):
self.result = result
@@ -62,6 +64,9 @@
def setdescr(self, descr):
raise NotImplementedError
+ def cleardescr(self):
+ pass
+
# common methods
# --------------
@@ -194,6 +199,9 @@
self._check_descr(descr)
self._descr = descr
+ def cleardescr(self):
+ self._descr = None
+
def _check_descr(self, descr):
if not we_are_translated() and getattr(descr, 'I_am_a_descr', False):
return # needed for the mock case in oparser_model
diff --git a/pypy/jit/metainterp/test/support.py b/pypy/jit/metainterp/test/support.py
--- a/pypy/jit/metainterp/test/support.py
+++ b/pypy/jit/metainterp/test/support.py
@@ -56,8 +56,6 @@
greenfield_info = None
result_type = result_kind
portal_runner_ptr = "???"
- on_compile = lambda *args: None
- on_compile_bridge = lambda *args: None
stats = history.Stats()
cpu = CPUClass(rtyper, stats, None, False)
diff --git a/pypy/jit/metainterp/test/test_compile.py b/pypy/jit/metainterp/test/test_compile.py
--- a/pypy/jit/metainterp/test/test_compile.py
+++ b/pypy/jit/metainterp/test/test_compile.py
@@ -53,8 +53,6 @@
call_pure_results = {}
class jitdriver_sd:
warmstate = FakeState()
- on_compile = staticmethod(lambda *args: None)
- on_compile_bridge = staticmethod(lambda *args: None)
virtualizable_info = None
def test_compile_loop():
diff --git a/pypy/jit/metainterp/test/test_jitdriver.py b/pypy/jit/metainterp/test/test_jitdriver.py
--- a/pypy/jit/metainterp/test/test_jitdriver.py
+++ b/pypy/jit/metainterp/test/test_jitdriver.py
@@ -10,57 +10,6 @@
def getloc2(g):
return "in jitdriver2, with g=%d" % g
-class JitDriverTests(object):
- def test_on_compile(self):
- called = {}
-
- class MyJitDriver(JitDriver):
- def on_compile(self, logger, looptoken, operations, type, n, m):
- called[(m, n, type)] = looptoken
-
- driver = MyJitDriver(greens = ['n', 'm'], reds = ['i'])
-
- def loop(n, m):
- i = 0
- while i < n + m:
- driver.can_enter_jit(n=n, m=m, i=i)
- driver.jit_merge_point(n=n, m=m, i=i)
- i += 1
-
- self.meta_interp(loop, [1, 4])
- assert sorted(called.keys()) == [(4, 1, "loop")]
- self.meta_interp(loop, [2, 4])
- assert sorted(called.keys()) == [(4, 1, "loop"),
- (4, 2, "loop")]
-
- def test_on_compile_bridge(self):
- called = {}
-
- class MyJitDriver(JitDriver):
- def on_compile(self, logger, looptoken, operations, type, n, m):
- called[(m, n, type)] = loop
- def on_compile_bridge(self, logger, orig_token, operations, n):
- assert 'bridge' not in called
- called['bridge'] = orig_token
-
- driver = MyJitDriver(greens = ['n', 'm'], reds = ['i'])
-
- def loop(n, m):
- i = 0
- while i < n + m:
- driver.can_enter_jit(n=n, m=m, i=i)
- driver.jit_merge_point(n=n, m=m, i=i)
- if i >= 4:
- i += 2
- i += 1
-
- self.meta_interp(loop, [1, 10])
- assert sorted(called.keys()) == ['bridge', (10, 1, "loop")]
-
-
-class TestLLtypeSingle(JitDriverTests, LLJitMixin):
- pass
-
class MultipleJitDriversTests(object):
def test_simple(self):
diff --git a/pypy/jit/metainterp/test/test_jitiface.py b/pypy/jit/metainterp/test/test_jitiface.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/test/test_jitiface.py
@@ -0,0 +1,148 @@
+
+from pypy.rlib.jit import JitDriver, JitHookInterface
+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
+
+class TestJitHookInterface(LLJitMixin):
+ def test_abort_quasi_immut(self):
+ reasons = []
+
+ class MyJitIface(JitHookInterface):
+ def on_abort(self, reason, jitdriver, greenkey, greenkey_repr):
+ assert jitdriver is myjitdriver
+ assert len(greenkey) == 1
+ reasons.append(reason)
+ assert greenkey_repr == 'blah'
+
+ iface = MyJitIface()
+
+ myjitdriver = JitDriver(greens=['foo'], reds=['x', 'total'],
+ get_printable_location=lambda *args: 'blah')
+
+ class Foo:
+ _immutable_fields_ = ['a?']
+ def __init__(self, a):
+ self.a = a
+ def f(a, x):
+ foo = Foo(a)
+ total = 0
+ while x > 0:
+ myjitdriver.jit_merge_point(foo=foo, x=x, total=total)
+ # read a quasi-immutable field out of a Constant
+ total += foo.a
+ foo.a += 1
+ x -= 1
+ return total
+ #
+ assert f(100, 7) == 721
+ res = self.meta_interp(f, [100, 7], policy=JitPolicy(iface))
+ assert res == 721
+ assert reasons == [ABORT_FORCE_QUASIIMMUT] * 2
+
+ def test_on_compile(self):
+ called = []
+
+ class MyJitIface(JitHookInterface):
+ def after_compile(self, di):
+ called.append(("compile", di.greenkey[1].getint(),
+ di.greenkey[0].getint(), di.type))
+
+ def before_compile(self, di):
+ called.append(("optimize", di.greenkey[1].getint(),
+ di.greenkey[0].getint(), di.type))
+
+ #def before_optimize(self, jitdriver, logger, looptoken, oeprations,
+ # type, greenkey):
+ # called.append(("trace", greenkey[1].getint(),
+ # greenkey[0].getint(), type))
+
+ iface = MyJitIface()
+
+ driver = JitDriver(greens = ['n', 'm'], reds = ['i'])
+
+ def loop(n, m):
+ i = 0
+ while i < n + m:
+ driver.can_enter_jit(n=n, m=m, i=i)
+ driver.jit_merge_point(n=n, m=m, i=i)
+ i += 1
+
+ self.meta_interp(loop, [1, 4], policy=JitPolicy(iface))
+ assert called == [#("trace", 4, 1, "loop"),
+ ("optimize", 4, 1, "loop"),
+ ("compile", 4, 1, "loop")]
+ self.meta_interp(loop, [2, 4], policy=JitPolicy(iface))
+ assert called == [#("trace", 4, 1, "loop"),
+ ("optimize", 4, 1, "loop"),
+ ("compile", 4, 1, "loop"),
+ #("trace", 4, 2, "loop"),
+ ("optimize", 4, 2, "loop"),
+ ("compile", 4, 2, "loop")]
+
+ def test_on_compile_bridge(self):
+ called = []
+
+ class MyJitIface(JitHookInterface):
+ def after_compile(self, di):
+ called.append("compile")
+
+ def after_compile_bridge(self, di):
+ called.append("compile_bridge")
+
+ def before_compile_bridge(self, di):
+ called.append("before_compile_bridge")
+
+ driver = JitDriver(greens = ['n', 'm'], reds = ['i'])
+
+ def loop(n, m):
+ i = 0
+ while i < n + m:
+ driver.can_enter_jit(n=n, m=m, i=i)
+ driver.jit_merge_point(n=n, m=m, i=i)
+ if i >= 4:
+ i += 2
+ i += 1
+
+ self.meta_interp(loop, [1, 10], policy=JitPolicy(MyJitIface()))
+ assert called == ["compile", "before_compile_bridge", "compile_bridge"]
+
+ def test_resop_interface(self):
+ driver = JitDriver(greens = [], reds = ['i'])
+
+ def loop(i):
+ while i > 0:
+ driver.jit_merge_point(i=i)
+ i -= 1
+
+ def main():
+ loop(1)
+ op = jit_hooks.resop_new(rop.INT_ADD,
+ [jit_hooks.boxint_new(3),
+ jit_hooks.boxint_new(4)],
+ jit_hooks.boxint_new(1))
+ assert hlstr(jit_hooks.resop_getopname(op)) == 'int_add'
+ assert jit_hooks.resop_getopnum(op) == rop.INT_ADD
+ box = jit_hooks.resop_getarg(op, 0)
+ assert jit_hooks.box_getint(box) == 3
+ box2 = jit_hooks.box_clone(box)
+ assert box2 != box
+ assert jit_hooks.box_getint(box2) == 3
+ assert not jit_hooks.box_isconst(box2)
+ box3 = jit_hooks.box_constbox(box)
+ assert jit_hooks.box_getint(box) == 3
+ assert jit_hooks.box_isconst(box3)
+ box4 = jit_hooks.box_nonconstbox(box)
+ assert not jit_hooks.box_isconst(box4)
+ box5 = jit_hooks.boxint_new(18)
+ jit_hooks.resop_setarg(op, 0, box5)
+ assert jit_hooks.resop_getarg(op, 0) == box5
+ box6 = jit_hooks.resop_getresult(op)
+ assert jit_hooks.box_getint(box6) == 1
+ jit_hooks.resop_setresult(op, box5)
+ assert jit_hooks.resop_getresult(op) == box5
+
+ self.meta_interp(main, [])
diff --git a/pypy/jit/metainterp/test/test_ztranslation.py b/pypy/jit/metainterp/test/test_ztranslation.py
--- a/pypy/jit/metainterp/test/test_ztranslation.py
+++ b/pypy/jit/metainterp/test/test_ztranslation.py
@@ -3,7 +3,9 @@
from pypy.jit.backend.llgraph import runner
from pypy.rlib.jit import JitDriver, unroll_parameters, set_param
from pypy.rlib.jit import PARAMETERS, dont_look_inside, hint
+from pypy.rlib.jit_hooks import boxint_new, resop_new, resop_opnum
from pypy.jit.metainterp.jitprof import Profiler
+from pypy.jit.metainterp.resoperation import rop
from pypy.rpython.lltypesystem import lltype, llmemory
class TranslationTest:
@@ -22,6 +24,7 @@
# - jitdriver hooks
# - two JITs
# - string concatenation, slicing and comparison
+ # - jit hooks interface
class Frame(object):
_virtualizable2_ = ['l[*]']
@@ -91,7 +94,9 @@
return f.i
#
def main(i, j):
- return f(i) - f2(i+j, i, j)
+ op = resop_new(rop.INT_ADD, [boxint_new(3), boxint_new(5)],
+ boxint_new(8))
+ return f(i) - f2(i+j, i, j) + resop_opnum(op)
res = ll_meta_interp(main, [40, 5], CPUClass=self.CPUClass,
type_system=self.type_system,
listops=True)
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
@@ -1,4 +1,5 @@
import sys, py
+from pypy.tool.sourcetools import func_with_new_name
from pypy.rpython.lltypesystem import lltype, llmemory
from pypy.rpython.annlowlevel import llhelper, MixLevelHelperAnnotator,\
cast_base_ptr_to_instance, hlstr
@@ -112,7 +113,7 @@
return ll_meta_interp(function, args, backendopt=backendopt,
translate_support_code=True, **kwds)
-def _find_jit_marker(graphs, marker_name):
+def _find_jit_marker(graphs, marker_name, check_driver=True):
results = []
for graph in graphs:
for block in graph.iterblocks():
@@ -120,8 +121,8 @@
op = block.operations[i]
if (op.opname == 'jit_marker' and
op.args[0].value == marker_name and
- (op.args[1].value is None or
- op.args[1].value.active)): # the jitdriver
+ (not check_driver or op.args[1].value is None or
+ op.args[1].value.active)): # the jitdriver
results.append((graph, block, i))
return results
@@ -140,6 +141,9 @@
"found several jit_merge_points in the same graph")
return results
+def find_access_helpers(graphs):
+ return _find_jit_marker(graphs, 'access_helper', False)
+
def locate_jit_merge_point(graph):
[(graph, block, pos)] = find_jit_merge_points([graph])
return block, pos, block.operations[pos]
@@ -206,6 +210,7 @@
vrefinfo = VirtualRefInfo(self)
self.codewriter.setup_vrefinfo(vrefinfo)
#
+ self.hooks = policy.jithookiface
self.make_virtualizable_infos()
self.make_exception_classes()
self.make_driverhook_graphs()
@@ -213,6 +218,7 @@
self.rewrite_jit_merge_points(policy)
verbose = False # not self.cpu.translate_support_code
+ self.rewrite_access_helpers()
self.codewriter.make_jitcodes(verbose=verbose)
self.rewrite_can_enter_jits()
self.rewrite_set_param()
@@ -619,6 +625,24 @@
graph = self.annhelper.getgraph(func, args_s, s_result)
return self.annhelper.graph2delayed(graph, FUNC)
+ def rewrite_access_helpers(self):
+ ah = find_access_helpers(self.translator.graphs)
+ for graph, block, index in ah:
+ op = block.operations[index]
+ 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)
+ op.opname = 'direct_call'
+ op.args = [Constant(ptr, FUNCPTR)] + op.args[2:]
+
def rewrite_jit_merge_points(self, policy):
for jd in self.jitdrivers_sd:
self.rewrite_jit_merge_point(jd, policy)
diff --git a/pypy/jit/metainterp/warmstate.py b/pypy/jit/metainterp/warmstate.py
--- a/pypy/jit/metainterp/warmstate.py
+++ b/pypy/jit/metainterp/warmstate.py
@@ -596,20 +596,6 @@
return fn(*greenargs)
self.should_unroll_one_iteration = should_unroll_one_iteration
- if hasattr(jd.jitdriver, 'on_compile'):
- def on_compile(logger, token, operations, type, greenkey):
- greenargs = unwrap_greenkey(greenkey)
- return jd.jitdriver.on_compile(logger, token, operations, type,
- *greenargs)
- def on_compile_bridge(logger, orig_token, operations, n):
- return jd.jitdriver.on_compile_bridge(logger, orig_token,
- operations, n)
- jd.on_compile = on_compile
- jd.on_compile_bridge = on_compile_bridge
- else:
- jd.on_compile = lambda *args: None
- jd.on_compile_bridge = lambda *args: None
-
redargtypes = ''.join([kind[0] for kind in jd.red_args_types])
def get_assembler_token(greenkey):
diff --git a/pypy/jit/tool/oparser.py b/pypy/jit/tool/oparser.py
--- a/pypy/jit/tool/oparser.py
+++ b/pypy/jit/tool/oparser.py
@@ -89,11 +89,18 @@
assert typ == 'class'
return self.model.ConstObj(ootype.cast_to_object(obj))
- def get_descr(self, poss_descr):
+ def get_descr(self, poss_descr, allow_invent):
if poss_descr.startswith('<'):
return None
- else:
+ try:
return self._consts[poss_descr]
+ except KeyError:
+ if allow_invent:
+ int(poss_descr)
+ token = self.model.JitCellToken()
+ tt = self.model.TargetToken(token)
+ self._consts[poss_descr] = tt
+ return tt
def box_for_var(self, elem):
try:
@@ -186,7 +193,8 @@
poss_descr = allargs[-1].strip()
if poss_descr.startswith('descr='):
- descr = self.get_descr(poss_descr[len('descr='):])
+ descr = self.get_descr(poss_descr[len('descr='):],
+ opname == 'label')
allargs = allargs[:-1]
for arg in allargs:
arg = arg.strip()
diff --git a/pypy/jit/tool/oparser_model.py b/pypy/jit/tool/oparser_model.py
--- a/pypy/jit/tool/oparser_model.py
+++ b/pypy/jit/tool/oparser_model.py
@@ -6,7 +6,7 @@
from pypy.jit.metainterp.history import TreeLoop, JitCellToken
from pypy.jit.metainterp.history import Box, BoxInt, BoxFloat
from pypy.jit.metainterp.history import ConstInt, ConstObj, ConstPtr, ConstFloat
- from pypy.jit.metainterp.history import BasicFailDescr
+ from pypy.jit.metainterp.history import BasicFailDescr, TargetToken
from pypy.jit.metainterp.typesystem import llhelper
from pypy.jit.metainterp.history import get_const_ptr_for_string
@@ -42,6 +42,10 @@
class JitCellToken(object):
I_am_a_descr = True
+ class TargetToken(object):
+ def __init__(self, jct):
+ pass
+
class BasicFailDescr(object):
I_am_a_descr = True
diff --git a/pypy/jit/tool/test/test_oparser.py b/pypy/jit/tool/test/test_oparser.py
--- a/pypy/jit/tool/test/test_oparser.py
+++ b/pypy/jit/tool/test/test_oparser.py
@@ -4,7 +4,8 @@
from pypy.jit.tool.oparser import parse, OpParser
from pypy.jit.metainterp.resoperation import rop
-from pypy.jit.metainterp.history import AbstractDescr, BoxInt, JitCellToken
+from pypy.jit.metainterp.history import AbstractDescr, BoxInt, JitCellToken,\
+ TargetToken
class BaseTestOparser(object):
@@ -243,6 +244,16 @@
b = loop.getboxes()
assert isinstance(b.sum0, BoxInt)
+ def test_label(self):
+ x = """
+ [i0]
+ label(i0, descr=1)
+ jump(i0, descr=1)
+ """
+ loop = self.parse(x)
+ assert loop.operations[0].getdescr() is loop.operations[1].getdescr()
+ assert isinstance(loop.operations[0].getdescr(), TargetToken)
+
class ForbiddenModule(object):
def __init__(self, name, old_mod):
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
@@ -16,24 +16,28 @@
virtualizables=['frame'],
reds=['result_size', 'frame', 'ri', 'self', 'result'],
get_printable_location=signature.new_printable_location('numpy'),
+ name='numpy',
)
all_driver = jit.JitDriver(
greens=['shapelen', 'sig'],
virtualizables=['frame'],
reds=['frame', 'self', 'dtype'],
get_printable_location=signature.new_printable_location('all'),
+ name='numpy_all',
)
any_driver = jit.JitDriver(
greens=['shapelen', 'sig'],
virtualizables=['frame'],
reds=['frame', 'self', 'dtype'],
get_printable_location=signature.new_printable_location('any'),
+ name='numpy_any',
)
slice_driver = jit.JitDriver(
greens=['shapelen', 'sig'],
virtualizables=['frame'],
reds=['self', 'frame', 'source', 'res_iter'],
get_printable_location=signature.new_printable_location('slice'),
+ name='numpy_slice',
)
def _find_shape_and_elems(space, w_iterable):
@@ -297,6 +301,7 @@
greens=['shapelen', 'sig'],
reds=['result', 'idx', 'frame', 'self', 'cur_best', 'dtype'],
get_printable_location=signature.new_printable_location(op_name),
+ name='numpy_' + op_name,
)
def loop(self):
sig = self.find_sig()
diff --git a/pypy/module/micronumpy/interp_ufuncs.py b/pypy/module/micronumpy/interp_ufuncs.py
--- a/pypy/module/micronumpy/interp_ufuncs.py
+++ b/pypy/module/micronumpy/interp_ufuncs.py
@@ -14,6 +14,7 @@
virtualizables = ["frame"],
reds = ["frame", "self", "dtype", "value", "obj"],
get_printable_location=new_printable_location('reduce'),
+ name='numpy_reduce',
)
class W_Ufunc(Wrappable):
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
@@ -7,16 +7,21 @@
interpleveldefs = {
'set_param': 'interp_jit.set_param',
'residual_call': 'interp_jit.residual_call',
- 'set_compile_hook': 'interp_jit.set_compile_hook',
- 'DebugMergePoint': 'interp_resop.W_DebugMergePoint',
+ 'set_compile_hook': 'interp_resop.set_compile_hook',
+ 'set_optimize_hook': 'interp_resop.set_optimize_hook',
+ 'set_abort_hook': 'interp_resop.set_abort_hook',
+ 'ResOperation': 'interp_resop.WrappedOp',
+ 'Box': 'interp_resop.WrappedBox',
}
def setup_after_space_initialization(self):
# force the __extend__ hacks to occur early
from pypy.module.pypyjit.interp_jit import pypyjitdriver
+ from pypy.module.pypyjit.policy import pypy_hooks
# add the 'defaults' attribute
from pypy.rlib.jit import PARAMETERS
space = self.space
pypyjitdriver.space = space
w_obj = space.wrap(PARAMETERS)
space.setattr(space.wrap(self), space.wrap('defaults'), w_obj)
+ pypy_hooks.space = space
diff --git a/pypy/module/pypyjit/interp_jit.py b/pypy/module/pypyjit/interp_jit.py
--- a/pypy/module/pypyjit/interp_jit.py
+++ b/pypy/module/pypyjit/interp_jit.py
@@ -13,11 +13,7 @@
from pypy.interpreter.pycode import PyCode, CO_GENERATOR
from pypy.interpreter.pyframe import PyFrame
from pypy.interpreter.pyopcode import ExitFrame
-from pypy.interpreter.gateway import unwrap_spec
from opcode import opmap
-from pypy.rlib.nonconst import NonConstant
-from pypy.jit.metainterp.resoperation import rop
-from pypy.module.pypyjit.interp_resop import debug_merge_point_from_boxes
PyFrame._virtualizable2_ = ['last_instr', 'pycode',
'valuestackdepth', 'locals_stack_w[*]',
@@ -51,72 +47,19 @@
def should_unroll_one_iteration(next_instr, is_being_profiled, bytecode):
return (bytecode.co_flags & CO_GENERATOR) != 0
-def wrap_oplist(space, logops, operations):
- list_w = []
- for op in operations:
- if op.getopnum() == rop.DEBUG_MERGE_POINT:
- list_w.append(space.wrap(debug_merge_point_from_boxes(
- op.getarglist())))
- else:
- list_w.append(space.wrap(logops.repr_of_resop(op)))
- return list_w
-
class PyPyJitDriver(JitDriver):
reds = ['frame', 'ec']
greens = ['next_instr', 'is_being_profiled', 'pycode']
virtualizables = ['frame']
- def on_compile(self, logger, looptoken, operations, type, next_instr,
- is_being_profiled, ll_pycode):
- from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
-
- space = self.space
- cache = space.fromcache(Cache)
- if cache.in_recursion:
- return
- if space.is_true(cache.w_compile_hook):
- logops = logger._make_log_operations()
- list_w = wrap_oplist(space, logops, operations)
- pycode = cast_base_ptr_to_instance(PyCode, ll_pycode)
- cache.in_recursion = True
- try:
- space.call_function(cache.w_compile_hook,
- space.wrap('main'),
- space.wrap(type),
- space.newtuple([pycode,
- space.wrap(next_instr),
- space.wrap(is_being_profiled)]),
- space.newlist(list_w))
- except OperationError, e:
- e.write_unraisable(space, "jit hook ", cache.w_compile_hook)
- cache.in_recursion = False
-
- def on_compile_bridge(self, logger, orig_looptoken, operations, n):
- space = self.space
- cache = space.fromcache(Cache)
- if cache.in_recursion:
- return
- if space.is_true(cache.w_compile_hook):
- logops = logger._make_log_operations()
- list_w = wrap_oplist(space, logops, operations)
- cache.in_recursion = True
- try:
- space.call_function(cache.w_compile_hook,
- space.wrap('main'),
- space.wrap('bridge'),
- space.wrap(n),
- space.newlist(list_w))
- except OperationError, e:
- e.write_unraisable(space, "jit hook ", cache.w_compile_hook)
- cache.in_recursion = False
-
pypyjitdriver = PyPyJitDriver(get_printable_location = get_printable_location,
get_jitcell_at = get_jitcell_at,
set_jitcell_at = set_jitcell_at,
confirm_enter_jit = confirm_enter_jit,
can_never_inline = can_never_inline,
should_unroll_one_iteration =
- should_unroll_one_iteration)
+ should_unroll_one_iteration,
+ name='pypyjit')
class __extend__(PyFrame):
@@ -223,34 +166,3 @@
'''For testing. Invokes callable(...), but without letting
the JIT follow the call.'''
return space.call_args(w_callable, __args__)
-
-class Cache(object):
- in_recursion = False
-
- def __init__(self, space):
- self.w_compile_hook = space.w_None
-
-def set_compile_hook(space, w_hook):
- """ 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(merge_point_type, loop_type, greenkey or guard_number, operations)
-
- for now merge point type is always `main`
-
- loop_type can be either `loop` `entry_bridge` or `bridge`
- in case loop is not `bridge`, greenkey will be a set of constants
- for jit merge point. in case it's `main` it'll be a tuple
- (code, offset, is_being_profiled)
-
- Note that jit hook is not reentrant. It means that if the code
- inside the jit hook is itself jitted, it will get compiled, but the
- jit hook won't be called for that.
-
- XXX write down what else
- """
- cache = space.fromcache(Cache)
- cache.w_compile_hook = w_hook
- cache.in_recursion = NonConstant(False)
- return space.w_None
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
@@ -1,41 +1,197 @@
-from pypy.interpreter.typedef import TypeDef, interp_attrproperty
+from pypy.interpreter.typedef import TypeDef, GetSetProperty
from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.gateway import unwrap_spec, interp2app
+from pypy.interpreter.gateway import unwrap_spec, interp2app, NoneNotWrapped
from pypy.interpreter.pycode import PyCode
-from pypy.rpython.lltypesystem import lltype
-from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
+from pypy.interpreter.error import OperationError
+from pypy.rpython.lltypesystem import lltype, llmemory
+from pypy.rpython.annlowlevel import cast_base_ptr_to_instance, hlstr
from pypy.rpython.lltypesystem.rclass import OBJECT
+from pypy.jit.metainterp.resoperation import rop, AbstractResOp
+from pypy.rlib.nonconst import NonConstant
+from pypy.rlib import jit_hooks
-class W_DebugMergePoint(Wrappable):
- """ A class representing debug_merge_point JIT operation
+class Cache(object):
+ in_recursion = False
+
+ 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 wrap_greenkey(space, jitdriver, greenkey, greenkey_repr):
+ if greenkey is None:
+ return space.w_None
+ jitdriver_name = jitdriver.name
+ if jitdriver_name == 'pypyjit':
+ next_instr = greenkey[0].getint()
+ is_being_profiled = greenkey[1].getint()
+ ll_code = lltype.cast_opaque_ptr(lltype.Ptr(OBJECT),
+ greenkey[2].getref_base())
+ pycode = cast_base_ptr_to_instance(PyCode, ll_code)
+ return space.newtuple([space.wrap(pycode), space.wrap(next_instr),
+ space.newbool(bool(is_being_profiled))])
+ else:
+ return space.wrap(greenkey_repr)
+
+def set_compile_hook(space, w_hook):
+ """ 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
+
+ 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.
"""
+ cache = space.fromcache(Cache)
+ cache.w_compile_hook = w_hook
+ cache.in_recursion = NonConstant(False)
- def __init__(self, mp_no, offset, pycode):
- self.mp_no = mp_no
+def set_optimize_hook(space, w_hook):
+ """ set_optimize_hook(hook)
+
+ Set a compiling hook that will be called each time a loop is optimized,
+ 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.
+
+ Result value will be the resulting list of operations, or None
+ """
+ cache = space.fromcache(Cache)
+ cache.w_optimize_hook = w_hook
+ cache.in_recursion = NonConstant(False)
+
+def set_abort_hook(space, w_hook):
+ """ set_abort_hook(hook)
+
+ Set a hook (callable) that will be called each time there is tracing
+ aborted due to some reason.
+
+ The hook will be called as in: hook(jitdriver_name, greenkey, reason)
+
+ Where reason is the reason for abort, see documentation for set_compile_hook
+ for descriptions of other arguments.
+ """
+ cache = space.fromcache(Cache)
+ cache.w_abort_hook = w_hook
+ cache.in_recursion = NonConstant(False)
+
+def wrap_oplist(space, logops, operations, ops_offset=None):
+ l_w = []
+ for op in operations:
+ if ops_offset is None:
+ ofs = -1
+ else:
+ ofs = ops_offset.get(op, 0)
+ l_w.append(WrappedOp(jit_hooks._cast_to_gcref(op), ofs,
+ logops.repr_of_resop(op)))
+ return l_w
+
+class WrappedBox(Wrappable):
+ """ A class representing a single box
+ """
+ def __init__(self, llbox):
+ self.llbox = llbox
+
+ def descr_getint(self, space):
+ return space.wrap(jit_hooks.box_getint(self.llbox))
+
+ at unwrap_spec(no=int)
+def descr_new_box(space, w_tp, no):
+ return WrappedBox(jit_hooks.boxint_new(no))
+
+WrappedBox.typedef = TypeDef(
+ 'Box',
+ __new__ = interp2app(descr_new_box),
+ getint = interp2app(WrappedBox.descr_getint),
+)
+
+ at unwrap_spec(num=int, offset=int, repr=str, res=WrappedBox)
+def descr_new_resop(space, w_tp, num, w_args, res, offset=-1,
+ repr=''):
+ args = [space.interp_w(WrappedBox, w_arg).llbox for w_arg in
+ space.listview(w_args)]
+ if res is None:
+ llres = jit_hooks.emptyval()
+ else:
+ llres = res.llbox
+ return WrappedOp(jit_hooks.resop_new(num, args, llres), offset, repr)
+
+class WrappedOp(Wrappable):
+ """ A class representing a single ResOperation, wrapped nicely
+ """
+ def __init__(self, op, offset, repr_of_resop):
+ self.op = op
self.offset = offset
- self.pycode = pycode
+ self.repr_of_resop = repr_of_resop
def descr_repr(self, space):
- return space.wrap('DebugMergePoint()')
+ return space.wrap(self.repr_of_resop)
- at unwrap_spec(mp_no=int, offset=int, pycode=PyCode)
-def new_debug_merge_point(space, w_tp, mp_no, offset, pycode):
- return W_DebugMergePoint(mp_no, offset, pycode)
+ def descr_num(self, space):
+ return space.wrap(jit_hooks.resop_getopnum(self.op))
-def debug_merge_point_from_boxes(boxes):
- mp_no = boxes[0].getint()
- offset = boxes[2].getint()
- llcode = lltype.cast_opaque_ptr(lltype.Ptr(OBJECT),
- boxes[4].getref_base())
- pycode = cast_base_ptr_to_instance(PyCode, llcode)
- assert pycode is not None
- return W_DebugMergePoint(mp_no, offset, pycode)
+ def descr_name(self, space):
+ return space.wrap(hlstr(jit_hooks.resop_getopname(self.op)))
-W_DebugMergePoint.typedef = TypeDef(
- 'DebugMergePoint',
- __new__ = interp2app(new_debug_merge_point),
- __doc__ = W_DebugMergePoint.__doc__,
- __repr__ = interp2app(W_DebugMergePoint.descr_repr),
- code = interp_attrproperty('pycode', W_DebugMergePoint),
+ @unwrap_spec(no=int)
+ def descr_getarg(self, space, no):
+ return WrappedBox(jit_hooks.resop_getarg(self.op, no))
+
+ @unwrap_spec(no=int, box=WrappedBox)
+ def descr_setarg(self, space, no, box):
+ jit_hooks.resop_setarg(self.op, no, box.llbox)
+
+ def descr_getresult(self, space):
+ return WrappedBox(jit_hooks.resop_getresult(self.op))
+
+ def descr_setresult(self, space, w_box):
+ box = space.interp_w(WrappedBox, w_box)
+ jit_hooks.resop_setresult(self.op, box.llbox)
+
+WrappedOp.typedef = TypeDef(
+ 'ResOperation',
+ __doc__ = WrappedOp.__doc__,
+ __new__ = interp2app(descr_new_resop),
+ __repr__ = interp2app(WrappedOp.descr_repr),
+ num = GetSetProperty(WrappedOp.descr_num),
+ name = GetSetProperty(WrappedOp.descr_name),
+ getarg = interp2app(WrappedOp.descr_getarg),
+ setarg = interp2app(WrappedOp.descr_setarg),
+ result = GetSetProperty(WrappedOp.descr_getresult,
+ WrappedOp.descr_setresult)
)
+WrappedOp.acceptable_as_base_class = 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,4 +1,112 @@
from pypy.jit.codewriter.policy import JitPolicy
+from pypy.rlib.jit import JitHookInterface
+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
+
+class PyPyJitIface(JitHookInterface):
+ def on_abort(self, reason, jitdriver, greenkey, greenkey_repr):
+ space = self.space
+ cache = space.fromcache(Cache)
+ if cache.in_recursion:
+ return
+ if space.is_true(cache.w_abort_hook):
+ cache.in_recursion = True
+ try:
+ try:
+ space.call_function(cache.w_abort_hook,
+ space.wrap(jitdriver.name),
+ wrap_greenkey(space, jitdriver,
+ greenkey, greenkey_repr),
+ space.wrap(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)
+
+ def after_compile_bridge(self, debug_info):
+ self._compile_hook(debug_info,
+ self.space.wrap(debug_info.fail_descr_no))
+
+ 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)
+
+ def before_compile_bridge(self, debug_info):
+ self._optimize_hook(debug_info,
+ self.space.wrap(debug_info.fail_descr_no))
+
+ def _compile_hook(self, debug_info, w_arg):
+ 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)
+ 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))
+ 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):
+ 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)
+ 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))
+ if space.is_w(w_res, space.w_None):
+ return
+ l = []
+ for w_item in space.listview(w_res):
+ item = space.interp_w(WrappedOp, w_item)
+ l.append(jit_hooks._cast_to_resop(item.op))
+ del debug_info.operations[:] # modifying operations above is
+ # probably not a great idea since types may not work
+ # and we'll end up with half-working list and
+ # a segfault/fatal RPython error
+ for elem in l:
+ debug_info.operations.append(elem)
+ except OperationError, e:
+ e.write_unraisable(space, "jit hook ", cache.w_compile_hook)
+ finally:
+ cache.in_recursion = False
+
+pypy_hooks = PyPyJitIface()
class PyPyJitPolicy(JitPolicy):
@@ -12,12 +120,16 @@
modname == 'thread.os_thread'):
return True
if '.' in modname:
- modname, _ = modname.split('.', 1)
+ modname, rest = modname.split('.', 1)
+ else:
+ rest = ''
if modname in ['pypyjit', 'signal', 'micronumpy', 'math', 'exceptions',
'imp', 'sys', 'array', '_ffi', 'itertools', 'operator',
'posix', '_socket', '_sre', '_lsprof', '_weakref',
'__pypy__', 'cStringIO', '_collections', 'struct',
'mmap', 'marshal']:
+ if modname == 'pypyjit' and 'interp_resop' in rest:
+ return False
return True
return False
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
@@ -1,22 +1,40 @@
import py
from pypy.conftest import gettestobjspace, option
+from pypy.interpreter.gateway import interp2app
from pypy.interpreter.pycode import PyCode
-from pypy.interpreter.gateway import interp2app
-from pypy.jit.metainterp.history import JitCellToken
-from pypy.jit.metainterp.resoperation import ResOperation, rop
+from pypy.jit.metainterp.history import JitCellToken, ConstInt, ConstPtr
+from pypy.jit.metainterp.resoperation import rop
from pypy.jit.metainterp.logger import Logger
from pypy.rpython.annlowlevel import (cast_instance_to_base_ptr,
cast_base_ptr_to_instance)
from pypy.rpython.lltypesystem import lltype, llmemory
+from pypy.rpython.lltypesystem.rclass import OBJECT
from pypy.module.pypyjit.interp_jit import pypyjitdriver
+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
+
+class MockJitDriverSD(object):
+ class warmstate(object):
+ @staticmethod
+ def get_location_str(boxes):
+ ll_code = lltype.cast_opaque_ptr(lltype.Ptr(OBJECT),
+ boxes[2].getref_base())
+ pycode = cast_base_ptr_to_instance(PyCode, ll_code)
+ return pycode.co_name
+
+ jitdriver = pypyjitdriver
+
class MockSD(object):
class cpu(object):
ts = llhelper
+ jitdrivers_sd = [MockJitDriverSD]
+
class AppTestJitHook(object):
def setup_class(cls):
if option.runappdirect:
@@ -24,9 +42,9 @@
space = gettestobjspace(usemodules=('pypyjit',))
cls.space = space
w_f = space.appexec([], """():
- def f():
+ def function():
pass
- return f
+ return function
""")
cls.w_f = w_f
ll_code = cast_instance_to_base_ptr(w_f.code)
@@ -34,41 +52,73 @@
logger = Logger(MockSD())
oplist = parse("""
- [i1, i2]
+ [i1, i2, p2]
i3 = int_add(i1, i2)
debug_merge_point(0, 0, 0, 0, ConstPtr(ptr0))
+ guard_nonnull(p2) []
guard_true(i3) []
""", namespace={'ptr0': code_gcref}).operations
+ greenkey = [ConstInt(0), ConstInt(0), ConstPtr(code_gcref)]
+ offset = {}
+ for i, op in enumerate(oplist):
+ if i != 1:
+ offset[op] = i
+
+ di_loop = JitDebugInfo(MockJitDriverSD, logger, JitCellToken(),
+ oplist, 'loop', greenkey)
+ di_loop_optimize = JitDebugInfo(MockJitDriverSD, logger, JitCellToken(),
+ oplist, 'loop', greenkey)
+ di_loop.asminfo = AsmInfo(offset, 0, 0)
+ di_bridge = JitDebugInfo(MockJitDriverSD, logger, JitCellToken(),
+ oplist, 'bridge', fail_descr_no=0)
+ di_bridge.asminfo = AsmInfo(offset, 0, 0)
def interp_on_compile():
- pypyjitdriver.on_compile(logger, JitCellToken(), oplist, 'loop',
- 0, False, ll_code)
+ di_loop.oplist = cls.oplist
+ pypy_hooks.after_compile(di_loop)
def interp_on_compile_bridge():
- pypyjitdriver.on_compile_bridge(logger, JitCellToken(), oplist, 0)
+ pypy_hooks.after_compile_bridge(di_bridge)
+
+ def interp_on_optimize():
+ di_loop_optimize.oplist = cls.oplist
+ pypy_hooks.before_compile(di_loop_optimize)
+
+ def interp_on_abort():
+ pypy_hooks.on_abort(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))
+ cls.w_on_abort = space.wrap(interp2app(interp_on_abort))
+ cls.w_int_add_num = space.wrap(rop.INT_ADD)
+ cls.w_on_optimize = space.wrap(interp2app(interp_on_optimize))
+ cls.orig_oplist = oplist
+
+ def setup_method(self, meth):
+ self.__class__.oplist = self.orig_oplist[:]
def test_on_compile(self):
import pypyjit
all = []
- def hook(*args):
- assert args[0] == 'main'
- assert args[1] in ['loop', 'bridge']
- all.append(args[2:])
+ def hook(name, looptype, tuple_or_guard_no, ops, asmstart, asmlen):
+ all.append((name, looptype, tuple_or_guard_no, ops))
self.on_compile()
pypyjit.set_compile_hook(hook)
assert not all
self.on_compile()
assert len(all) == 1
- assert all[0][0][0].co_name == 'f'
- assert all[0][0][1] == 0
- assert all[0][0][2] == False
- assert len(all[0][1]) == 3
- assert 'int_add' in all[0][1][0]
+ 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]
+ #assert int_add.name == 'int_add'
+ assert int_add.num == self.int_add_num
self.on_compile_bridge()
assert len(all) == 2
pypyjit.set_compile_hook(None)
@@ -116,11 +166,48 @@
pypyjit.set_compile_hook(hook)
self.on_compile()
- dmp = l[0][3][1]
- assert isinstance(dmp, pypyjit.DebugMergePoint)
- assert dmp.code is self.f.func_code
+ op = l[0][3][1]
+ assert isinstance(op, pypyjit.ResOperation)
+ assert 'function' in repr(op)
+
+ def test_on_abort(self):
+ import pypyjit
+ l = []
+
+ def hook(jitdriver_name, greenkey, reason):
+ l.append((jitdriver_name, reason))
+
+ pypyjit.set_abort_hook(hook)
+ self.on_abort()
+ assert l == [('pypyjit', 'ABORT_TOO_LONG')]
+
+ def test_on_optimize(self):
+ import pypyjit
+ l = []
+
+ def hook(name, looptype, tuple_or_guard_no, ops, *args):
+ l.append(ops)
+
+ def optimize_hook(name, looptype, tuple_or_guard_no, ops):
+ return []
+
+ pypyjit.set_compile_hook(hook)
+ pypyjit.set_optimize_hook(optimize_hook)
+ self.on_optimize()
+ self.on_compile()
+ assert l == [[]]
def test_creation(self):
- import pypyjit
- dmp = pypyjit.DebugMergePoint(0, 0, self.f.func_code)
- assert dmp.code is self.f.func_code
+ from pypyjit import Box, ResOperation
+
+ op = ResOperation(self.int_add_num, [Box(1), Box(3)], Box(4))
+ assert op.num == self.int_add_num
+ assert op.name == 'int_add'
+ box = op.getarg(0)
+ assert box.getint() == 1
+ box2 = op.result
+ assert box2.getint() == 4
+ op.setarg(0, box2)
+ assert op.getarg(0).getint() == 4
+ op.result = box
+ assert op.result.getint() == 1
diff --git a/pypy/module/pypyjit/test/test_policy.py b/pypy/module/pypyjit/test/test_policy.py
--- a/pypy/module/pypyjit/test/test_policy.py
+++ b/pypy/module/pypyjit/test/test_policy.py
@@ -52,6 +52,7 @@
for modname in 'pypyjit', 'signal', 'micronumpy', 'math', 'imp':
assert pypypolicy.look_inside_pypy_module(modname)
assert pypypolicy.look_inside_pypy_module(modname + '.foo')
+ assert not pypypolicy.look_inside_pypy_module('pypyjit.interp_resop')
def test_see_jit_module():
assert pypypolicy.look_inside_pypy_module('pypyjit.interp_jit')
diff --git a/pypy/module/pypyjit/test/test_ztranslation.py b/pypy/module/pypyjit/test/test_ztranslation.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/pypyjit/test/test_ztranslation.py
@@ -0,0 +1,5 @@
+
+from pypy.objspace.fake.checkmodule import checkmodule
+
+def test_pypyjit_translates():
+ checkmodule('pypyjit')
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -6,7 +6,6 @@
from pypy.rlib.objectmodel import CDefinedIntSymbolic, keepalive_until_here, specialize
from pypy.rlib.unroll import unrolling_iterable
from pypy.rpython.extregistry import ExtRegistryEntry
-from pypy.tool.sourcetools import func_with_new_name
DEBUG_ELIDABLE_FUNCTIONS = False
@@ -422,13 +421,16 @@
active = True # if set to False, this JitDriver is ignored
virtualizables = []
+ name = 'jitdriver'
def __init__(self, greens=None, reds=None, virtualizables=None,
get_jitcell_at=None, set_jitcell_at=None,
get_printable_location=None, confirm_enter_jit=None,
- can_never_inline=None, should_unroll_one_iteration=None):
+ can_never_inline=None, should_unroll_one_iteration=None,
+ name='jitdriver'):
if greens is not None:
self.greens = greens
+ self.name = name
if reds is not None:
self.reds = reds
if not hasattr(self, 'greens') or not hasattr(self, 'reds'):
@@ -462,23 +464,6 @@
# special-cased by ExtRegistryEntry
pass
- def on_compile(self, logger, looptoken, operations, type, *greenargs):
- """ A hook called when loop is compiled. Overwrite
- for your own jitdriver if you want to do something special, like
- call applevel code
- """
-
- def on_compile_bridge(self, logger, orig_looptoken, operations, n):
- """ A hook called when a bridge is compiled. Overwrite
- for your own jitdriver if you want to do something special
- """
-
- # note: if you overwrite this functions with the above signature it'll
- # work, but the *greenargs is different for each jitdriver, so we
- # can't share the same methods
- del on_compile
- del on_compile_bridge
-
def _make_extregistryentries(self):
# workaround: we cannot declare ExtRegistryEntries for functions
# used as methods of a frozen object, but we can attach the
@@ -640,7 +625,6 @@
def specialize_call(self, hop, **kwds_i):
# XXX to be complete, this could also check that the concretetype
# of the variables are the same for each of the calls.
- from pypy.rpython.error import TyperError
from pypy.rpython.lltypesystem import lltype
driver = self.instance.im_self
greens_v = []
@@ -753,6 +737,105 @@
return hop.genop('jit_marker', vlist,
resulttype=lltype.Void)
+class AsmInfo(object):
+ """ An addition to JitDebugInfo concerning assembler. Attributes:
+
+ ops_offset - dict of offsets of operations or None
+ asmaddr - (int) raw address of assembler block
+ asmlen - assembler block length
+ """
+ def __init__(self, ops_offset, asmaddr, asmlen):
+ self.ops_offset = ops_offset
+ self.asmaddr = asmaddr
+ self.asmlen = asmlen
+
+class JitDebugInfo(object):
+ """ An object representing debug info. Attributes meanings:
+
+ greenkey - a list of green boxes or None for bridge
+ logger - an instance of jit.metainterp.logger.LogOperations
+ type - either 'loop', 'entry bridge' or 'bridge'
+ looptoken - description of a loop
+ fail_descr_no - number of failing descr for bridges, -1 otherwise
+ asminfo - extra assembler information
+ """
+
+ asminfo = None
+ def __init__(self, jitdriver_sd, logger, looptoken, operations, type,
+ greenkey=None, fail_descr_no=-1):
+ self.jitdriver_sd = jitdriver_sd
+ self.logger = logger
+ self.looptoken = looptoken
+ self.operations = operations
+ self.type = type
+ if type == 'bridge':
+ assert fail_descr_no != -1
+ else:
+ assert greenkey is not None
+ self.greenkey = greenkey
+ self.fail_descr_no = fail_descr_no
+
+ def get_jitdriver(self):
+ """ Return where the jitdriver on which the jitting started
+ """
+ return self.jitdriver_sd.jitdriver
+
+ def get_greenkey_repr(self):
+ """ Return the string repr of a greenkey
+ """
+ return self.jitdriver_sd.warmstate.get_location_str(self.greenkey)
+
+class JitHookInterface(object):
+ """ This is the main connector between the JIT and the interpreter.
+ Several methods on this class will be invoked at various stages
+ of JIT running like JIT loops compiled, aborts etc.
+ An instance of this class will be available as policy.jithookiface.
+ """
+ def on_abort(self, reason, jitdriver, greenkey, greenkey_repr):
+ """ A hook called each time a loop is aborted with jitdriver and
+ greenkey where it started, reason is a string why it got aborted
+ """
+
+ #def before_optimize(self, debug_info):
+ # """ A hook called before optimizer is run, called with instance of
+ # JitDebugInfo. Overwrite for custom behavior
+ # """
+ # DISABLED
+
+ def before_compile(self, debug_info):
+ """ A hook called after a loop is optimized, before compiling assembler,
+ called with JitDebugInfo instance. Overwrite for custom behavior
+ """
+
+ def after_compile(self, debug_info):
+ """ A hook called after a loop has compiled assembler,
+ called with JitDebugInfo instance. Overwrite for custom behavior
+ """
+
+ #def before_optimize_bridge(self, debug_info):
+ # operations, fail_descr_no):
+ # """ A hook called before a bridge is optimized.
+ # Called with JitDebugInfo instance, overwrite for
+ # custom behavior
+ # """
+ # DISABLED
+
+ def before_compile_bridge(self, debug_info):
+ """ A hook called before a bridge is compiled, but after optimizations
+ are performed. Called with instance of debug_info, overwrite for
+ custom behavior
+ """
+
+ def after_compile_bridge(self, debug_info):
+ """ A hook called after a bridge is compiled, called with JitDebugInfo
+ 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
@@ -760,7 +843,6 @@
"""
assert isinstance(value, cls)
-
class Entry(ExtRegistryEntry):
_about_ = record_known_class
@@ -771,7 +853,8 @@
assert isinstance(s_inst, annmodel.SomeInstance)
def specialize_call(self, hop):
- from pypy.rpython.lltypesystem import lltype, rclass
+ from pypy.rpython.lltypesystem import rclass, lltype
+
classrepr = rclass.get_type_repr(hop.rtyper)
hop.exception_cannot_occur()
diff --git a/pypy/rlib/jit_hooks.py b/pypy/rlib/jit_hooks.py
new file mode 100644
--- /dev/null
+++ b/pypy/rlib/jit_hooks.py
@@ -0,0 +1,106 @@
+
+from pypy.rpython.extregistry import ExtRegistryEntry
+from pypy.annotation import model as annmodel
+from pypy.rpython.lltypesystem import llmemory, lltype
+from pypy.rpython.lltypesystem import rclass
+from pypy.rpython.annlowlevel import cast_instance_to_base_ptr,\
+ cast_base_ptr_to_instance, llstr, hlstr
+from pypy.rlib.objectmodel import specialize
+
+def register_helper(s_result):
+ def wrapper(helper):
+ class Entry(ExtRegistryEntry):
+ _about_ = helper
+
+ def compute_result_annotation(self, *args):
+ return s_result
+
+ def specialize_call(self, hop):
+ from pypy.rpython.lltypesystem import lltype
+
+ c_func = hop.inputconst(lltype.Void, helper)
+ c_name = hop.inputconst(lltype.Void, 'access_helper')
+ args_v = [hop.inputarg(arg, arg=i)
+ for i, arg in enumerate(hop.args_r)]
+ return hop.genop('jit_marker', [c_name, c_func] + args_v,
+ resulttype=hop.r_result)
+ return helper
+ return wrapper
+
+def _cast_to_box(llref):
+ from pypy.jit.metainterp.history import AbstractValue
+
+ ptr = lltype.cast_opaque_ptr(rclass.OBJECTPTR, llref)
+ return cast_base_ptr_to_instance(AbstractValue, ptr)
+
+def _cast_to_resop(llref):
+ from pypy.jit.metainterp.resoperation import AbstractResOp
+
+ ptr = lltype.cast_opaque_ptr(rclass.OBJECTPTR, llref)
+ return cast_base_ptr_to_instance(AbstractResOp, ptr)
+
+ at specialize.argtype(0)
+def _cast_to_gcref(obj):
+ return lltype.cast_opaque_ptr(llmemory.GCREF,
+ cast_instance_to_base_ptr(obj))
+
+def emptyval():
+ return lltype.nullptr(llmemory.GCREF.TO)
+
+ at register_helper(annmodel.SomePtr(llmemory.GCREF))
+def resop_new(no, llargs, llres):
+ from pypy.jit.metainterp.history import ResOperation
+
+ args = [_cast_to_box(llargs[i]) for i in range(len(llargs))]
+ res = _cast_to_box(llres)
+ return _cast_to_gcref(ResOperation(no, args, res))
+
+ at register_helper(annmodel.SomePtr(llmemory.GCREF))
+def boxint_new(no):
+ from pypy.jit.metainterp.history import BoxInt
+ return _cast_to_gcref(BoxInt(no))
+
+ at register_helper(annmodel.SomeInteger())
+def resop_getopnum(llop):
+ return _cast_to_resop(llop).getopnum()
+
+ at register_helper(annmodel.SomeString(can_be_None=True))
+def resop_getopname(llop):
+ return llstr(_cast_to_resop(llop).getopname())
+
+ at register_helper(annmodel.SomePtr(llmemory.GCREF))
+def resop_getarg(llop, no):
+ return _cast_to_gcref(_cast_to_resop(llop).getarg(no))
+
+ at register_helper(annmodel.s_None)
+def resop_setarg(llop, no, llbox):
+ _cast_to_resop(llop).setarg(no, _cast_to_box(llbox))
+
+ at register_helper(annmodel.SomePtr(llmemory.GCREF))
+def resop_getresult(llop):
+ return _cast_to_gcref(_cast_to_resop(llop).result)
+
+ at register_helper(annmodel.s_None)
+def resop_setresult(llop, llbox):
+ _cast_to_resop(llop).result = _cast_to_box(llbox)
+
+ at register_helper(annmodel.SomeInteger())
+def box_getint(llbox):
+ return _cast_to_box(llbox).getint()
+
+ at register_helper(annmodel.SomePtr(llmemory.GCREF))
+def box_clone(llbox):
+ return _cast_to_gcref(_cast_to_box(llbox).clonebox())
+
+ at register_helper(annmodel.SomePtr(llmemory.GCREF))
+def box_constbox(llbox):
+ return _cast_to_gcref(_cast_to_box(llbox).constbox())
+
+ at register_helper(annmodel.SomePtr(llmemory.GCREF))
+def box_nonconstbox(llbox):
+ return _cast_to_gcref(_cast_to_box(llbox).nonconstbox())
+
+ at register_helper(annmodel.SomeBool())
+def box_isconst(llbox):
+ from pypy.jit.metainterp.history import Const
+ return isinstance(_cast_to_box(llbox), Const)
diff --git a/pypy/rlib/rsre/rsre_jit.py b/pypy/rlib/rsre/rsre_jit.py
--- a/pypy/rlib/rsre/rsre_jit.py
+++ b/pypy/rlib/rsre/rsre_jit.py
@@ -5,7 +5,7 @@
active = True
def __init__(self, name, debugprint, **kwds):
- JitDriver.__init__(self, **kwds)
+ JitDriver.__init__(self, name='rsre_' + name, **kwds)
#
def get_printable_location(*args):
# we print based on indices in 'args'. We first print
diff --git a/pypy/translator/goal/targetpypystandalone.py b/pypy/translator/goal/targetpypystandalone.py
--- a/pypy/translator/goal/targetpypystandalone.py
+++ b/pypy/translator/goal/targetpypystandalone.py
@@ -226,8 +226,8 @@
return self.get_entry_point(config)
def jitpolicy(self, driver):
- from pypy.module.pypyjit.policy import PyPyJitPolicy
- return PyPyJitPolicy()
+ from pypy.module.pypyjit.policy import PyPyJitPolicy, pypy_hooks
+ return PyPyJitPolicy(pypy_hooks)
def get_entry_point(self, config):
from pypy.tool.lib_pypy import import_from_lib_pypy
More information about the pypy-commit
mailing list