diff options
author | Hakan Ardo <hakan@debian.org> | 2010-11-17 19:41:25 +0000 |
---|---|---|
committer | Hakan Ardo <hakan@debian.org> | 2010-11-17 19:41:25 +0000 |
commit | baf678c574081d15f0e409eb8d004c76e87b95c2 (patch) | |
tree | 1b7f8e03b86f24a6c8a44ece82fcdb6e5602fce4 /pypy/jit | |
parent | Better name (diff) | |
download | pypy-baf678c574081d15f0e409eb8d004c76e87b95c2.tar.gz pypy-baf678c574081d15f0e409eb8d004c76e87b95c2.tar.bz2 pypy-baf678c574081d15f0e409eb8d004c76e87b95c2.zip |
svn merge -r78744:HEAD svn+ssh://hakanardo@codespeak.net/svn/pypy/trunk
Diffstat (limited to 'pypy/jit')
43 files changed, 713 insertions, 301 deletions
diff --git a/pypy/jit/backend/detect_cpu.py b/pypy/jit/backend/detect_cpu.py index ada7c3428f..9ea8e843bb 100644 --- a/pypy/jit/backend/detect_cpu.py +++ b/pypy/jit/backend/detect_cpu.py @@ -34,7 +34,17 @@ def autodetect_main_model(): 'x86_64': 'x86', }[mach] except KeyError: - raise ProcessorAutodetectError, "unsupported processor '%s'" % mach + return mach + +def autodetect_main_model_and_size(): + model = autodetect_main_model() + if sys.maxint == 2**31-1: + model += '_32' + elif sys.maxint == 2**63-1: + model += '_64' + else: + raise AssertionError, "bad value for sys.maxint" + return model def autodetect(): model = autodetect_main_model() diff --git a/pypy/jit/backend/llgraph/llimpl.py b/pypy/jit/backend/llgraph/llimpl.py index deefa7f57d..40e2aa2560 100644 --- a/pypy/jit/backend/llgraph/llimpl.py +++ b/pypy/jit/backend/llgraph/llimpl.py @@ -152,7 +152,7 @@ TYPES = { 'unicodegetitem' : (('ref', 'int'), 'int'), 'unicodesetitem' : (('ref', 'int', 'int'), 'int'), 'cast_ptr_to_int' : (('ref',), 'int'), - 'debug_merge_point': (('ref',), None), + 'debug_merge_point': (('ref', 'int'), None), 'force_token' : ((), 'int'), 'call_may_force' : (('int', 'varargs'), 'intorptr'), 'guard_not_forced': ((), None), @@ -568,7 +568,7 @@ class Frame(object): # return _op_default_implementation - def op_debug_merge_point(self, _, value): + def op_debug_merge_point(self, _, value, recdepth): from pypy.jit.metainterp.warmspot import get_stats loc = ConstPtr(value)._get_str() get_stats().add_merge_point_location(loc) diff --git a/pypy/jit/backend/llsupport/descr.py b/pypy/jit/backend/llsupport/descr.py index b5fdc24dff..be7d563e06 100644 --- a/pypy/jit/backend/llsupport/descr.py +++ b/pypy/jit/backend/llsupport/descr.py @@ -130,6 +130,7 @@ def get_field_descr(gccache, STRUCT, fieldname): # ArrayDescrs _A = lltype.GcArray(lltype.Signed) # a random gcarray +_AF = lltype.GcArray(lltype.Float) # an array of C doubles class BaseArrayDescr(AbstractDescr): @@ -171,16 +172,21 @@ class GcPtrArrayDescr(NonGcPtrArrayDescr): _clsname = 'GcPtrArrayDescr' _is_array_of_pointers = True -_CA = rffi.CArray(lltype.Signed) +class FloatArrayDescr(BaseArrayDescr): + _clsname = 'FloatArrayDescr' + _is_array_of_floats = True + def get_base_size(self, translate_support_code): + basesize, _, _ = symbolic.get_array_token(_AF, translate_support_code) + return basesize + def get_item_size(self, translate_support_code): + return symbolic.get_size(lltype.Float, translate_support_code) class BaseArrayNoLengthDescr(BaseArrayDescr): def get_base_size(self, translate_support_code): - basesize, _, _ = symbolic.get_array_token(_CA, translate_support_code) - return basesize + return 0 def get_ofs_length(self, translate_support_code): - _, _, ofslength = symbolic.get_array_token(_CA, translate_support_code) - return ofslength + return -1 class NonGcPtrArrayNoLengthDescr(BaseArrayNoLengthDescr): _clsname = 'NonGcPtrArrayNoLengthDescr' @@ -192,6 +198,8 @@ class GcPtrArrayNoLengthDescr(NonGcPtrArrayNoLengthDescr): _is_array_of_pointers = True def getArrayDescrClass(ARRAY): + if ARRAY.OF is lltype.Float: + return FloatArrayDescr return getDescrClass(ARRAY.OF, BaseArrayDescr, GcPtrArrayDescr, NonGcPtrArrayDescr, 'Array', 'get_item_size', '_is_array_of_floats', '_is_item_signed') @@ -219,7 +227,8 @@ def get_array_descr(gccache, ARRAY): basesize, itemsize, ofslength = symbolic.get_array_token(ARRAY, False) assert basesize == arraydescr.get_base_size(False) assert itemsize == arraydescr.get_item_size(False) - assert ofslength == arraydescr.get_ofs_length(False) + if not ARRAY._hints.get('nolength', False): + assert ofslength == arraydescr.get_ofs_length(False) if isinstance(ARRAY, lltype.GcArray): gccache.init_array_descr(ARRAY, arraydescr) cache[ARRAY] = arraydescr diff --git a/pypy/jit/backend/llsupport/gc.py b/pypy/jit/backend/llsupport/gc.py index 7a6e4b1274..ebce30a874 100644 --- a/pypy/jit/backend/llsupport/gc.py +++ b/pypy/jit/backend/llsupport/gc.py @@ -19,6 +19,7 @@ from pypy.jit.backend.llsupport.descr import get_call_descr # ____________________________________________________________ class GcLLDescription(GcCache): + minimal_size_in_nursery = 0 def __init__(self, gcdescr, translator=None, rtyper=None): GcCache.__init__(self, translator is not None, rtyper) self.gcdescr = gcdescr @@ -386,6 +387,7 @@ class GcLLDescr_framework(GcLLDescription): (self.array_basesize, _, self.array_length_ofs) = \ symbolic.get_array_token(lltype.GcArray(lltype.Signed), True) self.max_size_of_young_obj = self.GCClass.JIT_max_size_of_young_obj() + self.minimal_size_in_nursery=self.GCClass.JIT_minimal_size_in_nursery() # make a malloc function, with three arguments def malloc_basic(size, tid): @@ -468,6 +470,7 @@ class GcLLDescr_framework(GcLLDescription): def malloc_fixedsize_slowpath(size): if self.DEBUG: random_usage_of_xmm_registers() + assert size >= self.minimal_size_in_nursery try: gcref = llop1.do_malloc_fixedsize_clear(llmemory.GCREF, 0, size, True, False, False) @@ -570,6 +573,9 @@ class GcLLDescr_framework(GcLLDescription): # GETFIELD_RAW from the array 'gcrefs.list'. # newops = [] + # we can only remember one malloc since the next malloc can possibly + # collect + last_malloc = None for op in operations: if op.getopnum() == rop.DEBUG_MERGE_POINT: continue @@ -587,22 +593,32 @@ class GcLLDescr_framework(GcLLDescription): [ConstInt(addr)], box, self.single_gcref_descr)) op.setarg(i, box) + if op.is_malloc(): + last_malloc = op.result + elif op.can_malloc(): + last_malloc = None # ---------- write barrier for SETFIELD_GC ---------- if op.getopnum() == rop.SETFIELD_GC: - v = op.getarg(1) - if isinstance(v, BoxPtr) or (isinstance(v, ConstPtr) and - bool(v.value)): # store a non-NULL - self._gen_write_barrier(newops, op.getarg(0), v) - op = op.copy_and_change(rop.SETFIELD_RAW) + val = op.getarg(0) + # no need for a write barrier in the case of previous malloc + if val is not last_malloc: + v = op.getarg(1) + if isinstance(v, BoxPtr) or (isinstance(v, ConstPtr) and + bool(v.value)): # store a non-NULL + self._gen_write_barrier(newops, op.getarg(0), v) + op = op.copy_and_change(rop.SETFIELD_RAW) # ---------- write barrier for SETARRAYITEM_GC ---------- if op.getopnum() == rop.SETARRAYITEM_GC: - v = op.getarg(2) - if isinstance(v, BoxPtr) or (isinstance(v, ConstPtr) and - bool(v.value)): # store a non-NULL - # XXX detect when we should produce a - # write_barrier_from_array - self._gen_write_barrier(newops, op.getarg(0), v) - op = op.copy_and_change(rop.SETARRAYITEM_RAW) + val = op.getarg(0) + # no need for a write barrier in the case of previous malloc + if val is not last_malloc: + v = op.getarg(2) + if isinstance(v, BoxPtr) or (isinstance(v, ConstPtr) and + bool(v.value)): # store a non-NULL + # XXX detect when we should produce a + # write_barrier_from_array + self._gen_write_barrier(newops, op.getarg(0), v) + op = op.copy_and_change(rop.SETARRAYITEM_RAW) # ---------- newops.append(op) del operations[:] diff --git a/pypy/jit/backend/llsupport/test/test_descr.py b/pypy/jit/backend/llsupport/test/test_descr.py index 23a1991a5a..32d75c0637 100644 --- a/pypy/jit/backend/llsupport/test/test_descr.py +++ b/pypy/jit/backend/llsupport/test/test_descr.py @@ -5,6 +5,7 @@ from pypy.rlib.objectmodel import Symbolic from pypy.rpython.annlowlevel import llhelper from pypy.jit.metainterp.history import BoxInt, BoxFloat, BoxPtr from pypy.jit.metainterp import history +import struct def test_get_size_descr(): c0 = GcCache(False) @@ -130,11 +131,13 @@ def test_get_array_descr(): assert not descr3.is_array_of_floats() assert descr4.is_array_of_floats() # - WORD = rffi.sizeof(lltype.Signed) - assert descr1.get_base_size(False) == WORD - assert descr2.get_base_size(False) == WORD - assert descr3.get_base_size(False) == WORD - assert descr4.get_base_size(False) == WORD + def get_alignment(code): + # Retrieve default alignment for the compiler/platform + return struct.calcsize('l' + code) - struct.calcsize(code) + assert descr1.get_base_size(False) == get_alignment('c') + assert descr2.get_base_size(False) == get_alignment('p') + assert descr3.get_base_size(False) == get_alignment('p') + assert descr4.get_base_size(False) == get_alignment('d') assert descr1.get_ofs_length(False) == 0 assert descr2.get_ofs_length(False) == 0 assert descr3.get_ofs_length(False) == 0 diff --git a/pypy/jit/backend/llsupport/test/test_gc.py b/pypy/jit/backend/llsupport/test/test_gc.py index 8fc2dbb399..afd0e67275 100644 --- a/pypy/jit/backend/llsupport/test/test_gc.py +++ b/pypy/jit/backend/llsupport/test/test_gc.py @@ -6,7 +6,9 @@ from pypy.jit.backend.llsupport.descr import * from pypy.jit.backend.llsupport.gc import * from pypy.jit.backend.llsupport import symbolic from pypy.jit.metainterp.gc import get_description - +from pypy.jit.tool.oparser import parse +from pypy.rpython.lltypesystem.rclass import OBJECT, OBJECT_VTABLE +from pypy.jit.metainterp.test.test_optimizeopt import equaloplists def test_boehm(): gc_ll_descr = GcLLDescr_boehm(None, None, None) @@ -114,7 +116,7 @@ def test_GcRootMap_asmgcc(): assert gcrootmap._gcmap[i*2+1] == expected_shapeaddr[i] -class FakeLLOp: +class FakeLLOp(object): def __init__(self): self.record = [] @@ -148,19 +150,19 @@ class FakeLLOp: return llhelper(FPTRTYPE, self._write_barrier_failing_case) -class TestFramework: +class TestFramework(object): gc = 'hybrid' def setup_method(self, meth): - class config_: - class translation: + class config_(object): + class translation(object): gc = self.gc gcrootfinder = 'asmgcc' gctransformer = 'framework' gcremovetypeptr = False - class FakeTranslator: + class FakeTranslator(object): config = config_ - class FakeCPU: + class FakeCPU(object): def cast_adr_to_int(self, adr): ptr = llmemory.cast_adr_to_ptr(adr, gc_ll_descr.WB_FUNCPTR) assert ptr._obj._callable == llop1._write_barrier_failing_case @@ -270,7 +272,7 @@ class TestFramework: def test_get_rid_of_debug_merge_point(self): operations = [ - ResOperation(rop.DEBUG_MERGE_POINT, ['dummy'], None), + ResOperation(rop.DEBUG_MERGE_POINT, ['dummy', 2], None), ] gc_ll_descr = self.gc_ll_descr gc_ll_descr.rewrite_assembler(None, operations) @@ -278,11 +280,11 @@ class TestFramework: def test_rewrite_assembler_1(self): # check rewriting of ConstPtrs - class MyFakeCPU: + class MyFakeCPU(object): def cast_adr_to_int(self, adr): assert adr == "some fake address" return 43 - class MyFakeGCRefList: + class MyFakeGCRefList(object): def get_address_of_gcref(self, s_gcref1): assert s_gcref1 == s_gcref return "some fake address" @@ -311,10 +313,10 @@ class TestFramework: def test_rewrite_assembler_1_cannot_move(self): # check rewriting of ConstPtrs - class MyFakeCPU: + class MyFakeCPU(object): def cast_adr_to_int(self, adr): xxx # should not be called - class MyFakeGCRefList: + class MyFakeGCRefList(object): def get_address_of_gcref(self, s_gcref1): seen.append(s_gcref1) assert s_gcref1 == s_gcref @@ -394,6 +396,68 @@ class TestFramework: assert operations[1].getarg(2) == v_value assert operations[1].getdescr() == array_descr + def test_rewrite_assembler_initialization_store(self): + S = lltype.GcStruct('S', ('parent', OBJECT), + ('x', lltype.Signed)) + s_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) + xdescr = get_field_descr(self.gc_ll_descr, S, 'x') + ops = parse(""" + [p1] + p0 = new_with_vtable(ConstClass(s_vtable)) + setfield_gc(p0, p1, descr=xdescr) + jump() + """, namespace=locals()) + expected = parse(""" + [p1] + p0 = new_with_vtable(ConstClass(s_vtable)) + # no write barrier + setfield_gc(p0, p1, descr=xdescr) + jump() + """, namespace=locals()) + self.gc_ll_descr.rewrite_assembler(self.fake_cpu, ops.operations) + equaloplists(ops.operations, expected.operations) + + def test_rewrite_assembler_initialization_store_2(self): + S = lltype.GcStruct('S', ('parent', OBJECT), + ('x', lltype.Signed)) + s_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True) + wbdescr = self.gc_ll_descr.write_barrier_descr + xdescr = get_field_descr(self.gc_ll_descr, S, 'x') + ops = parse(""" + [p1] + p0 = new_with_vtable(ConstClass(s_vtable)) + p3 = new_with_vtable(ConstClass(s_vtable)) + setfield_gc(p0, p1, descr=xdescr) + jump() + """, namespace=locals()) + expected = parse(""" + [p1] + p0 = new_with_vtable(ConstClass(s_vtable)) + p3 = new_with_vtable(ConstClass(s_vtable)) + cond_call_gc_wb(p0, p1, descr=wbdescr) + setfield_raw(p0, p1, descr=xdescr) + jump() + """, namespace=locals()) + self.gc_ll_descr.rewrite_assembler(self.fake_cpu, ops.operations) + equaloplists(ops.operations, expected.operations) + + def test_rewrite_assembler_initialization_store_3(self): + A = lltype.GcArray(lltype.Ptr(lltype.GcStruct('S'))) + arraydescr = get_array_descr(self.gc_ll_descr, A) + ops = parse(""" + [p1] + p0 = new_array(3, descr=arraydescr) + setarrayitem_gc(p0, 0, p1, descr=arraydescr) + jump() + """, namespace=locals()) + expected = parse(""" + [p1] + p0 = new_array(3, descr=arraydescr) + setarrayitem_gc(p0, 0, p1, descr=arraydescr) + jump() + """, namespace=locals()) + self.gc_ll_descr.rewrite_assembler(self.fake_cpu, ops.operations) + equaloplists(ops.operations, expected.operations) class TestFrameworkMiniMark(TestFramework): gc = 'minimark' diff --git a/pypy/jit/backend/test/runner_test.py b/pypy/jit/backend/test/runner_test.py index 95d7e3bf5a..9c437fbf00 100644 --- a/pypy/jit/backend/test/runner_test.py +++ b/pypy/jit/backend/test/runner_test.py @@ -2168,12 +2168,14 @@ class LLtypeBackendTest(BaseBackendTest): # Tested with a function that intentionally does not cast the # result to RESTYPE, but makes sure that we return the whole # value in eax or rax. - eci = ExternalCompilationInfo(separate_module_sources=[""" + eci = ExternalCompilationInfo( + separate_module_sources=[""" long fn_test_result_of_call(long x) { return x + 1; } - """]) + """], + export_symbols=['fn_test_result_of_call']) f = rffi.llexternal('fn_test_result_of_call', [lltype.Signed], RESTYPE, compilation_info=eci, _nowrapper=True) value = intmask(0xFFEEDDCCBBAA9988) @@ -2199,12 +2201,14 @@ class LLtypeBackendTest(BaseBackendTest): # Tested with a function that intentionally does not cast the # result to RESTYPE, but makes sure that we return the whole # value in eax or rax. - eci = ExternalCompilationInfo(separate_module_sources=[""" + eci = ExternalCompilationInfo( + separate_module_sources=[""" long fn_test_result_of_call(long x) { return x + 1; } - """]) + """], + export_symbols=['fn_test_result_of_call']) f = rffi.llexternal('fn_test_result_of_call', [lltype.Signed], RESTYPE, compilation_info=eci, _nowrapper=True) value = intmask(0xFFEEDDCCBBAA9988) diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py index 46caebe614..46e430ef72 100644 --- a/pypy/jit/backend/x86/assembler.py +++ b/pypy/jit/backend/x86/assembler.py @@ -303,6 +303,7 @@ class Assembler386(object): _x86_frame_depth _x86_param_depth _x86_arglocs + _x86_debug_checksum """ if not we_are_translated(): # Arguments should be unique @@ -312,7 +313,7 @@ class Assembler386(object): funcname = self._find_debug_merge_point(operations) if log: self._register_counter() - operations = self._inject_debugging_code(operations) + operations = self._inject_debugging_code(looptoken, operations) regalloc = RegAlloc(self, self.cpu.translate_support_code) arglocs = regalloc.prepare_loop(inputargs, operations, looptoken) @@ -336,8 +337,9 @@ class Assembler386(object): self._assemble_bootstrap_direct_call(arglocs, curadr, frame_depth+param_depth) # - debug_print("Loop #", looptoken.number, "has address", - looptoken._x86_loop_code, "to", self.mc.tell()) + debug_print("Loop #%d has address %x to %x" % (looptoken.number, + looptoken._x86_loop_code, + self.mc.tell())) self.mc.end_function() self.write_pending_failure_recoveries() @@ -350,7 +352,7 @@ class Assembler386(object): funcname = self._find_debug_merge_point(operations) if log: self._register_counter() - operations = self._inject_debugging_code(operations) + operations = self._inject_debugging_code(faildescr, operations) arglocs = self.rebuild_faillocs_from_descr( faildescr._x86_failure_recovery_bytecode) @@ -358,7 +360,6 @@ class Assembler386(object): assert ([loc.assembler() for loc in arglocs] == [loc.assembler() for loc in faildescr._x86_debug_faillocs]) regalloc = RegAlloc(self, self.cpu.translate_support_code) - operations = self._inject_debugging_code(operations) fail_depths = faildescr._x86_current_depths regalloc.prepare_bridge(fail_depths, inputargs, arglocs, operations) @@ -378,9 +379,8 @@ class Assembler386(object): faildescr._x86_bridge_param_depth = param_depth # patch the jump from original guard self.patch_jump_for_descr(faildescr, adr_bridge) - debug_print("Bridge out of guard", - descr_number, - "has address", adr_bridge, "to", self.mc.tell()) + debug_print("Bridge out of guard %d has address %x to %x" % + (descr_number, adr_bridge, self.mc.tell())) self.mc.end_function() self.write_pending_failure_recoveries() @@ -433,9 +433,14 @@ class Assembler386(object): mc.done() - def _inject_debugging_code(self, operations): + @specialize.argtype(1) + def _inject_debugging_code(self, looptoken, operations): if self._debug: # before doing anything, let's increase a counter + s = 0 + for op in operations: + s += op.getopnum() + looptoken._x86_debug_checksum = s c_adr = ConstInt(rffi.cast(lltype.Signed, self.loop_run_counters[-1][1])) box = BoxInt() @@ -1768,13 +1773,18 @@ class Assembler386(object): self.implement_guard(guard_token, 'L') def genop_discard_cond_call_gc_wb(self, op, arglocs): - # use 'mc._mc' directly instead of 'mc', to avoid - # bad surprizes if the code buffer is mostly full + # Write code equivalent to write_barrier() in the GC: it checks + # a flag in the object at arglocs[0], and if set, it calls the + # function remember_young_pointer() from the GC. The two arguments + # to the call are in arglocs[:2]. The rest, arglocs[2:], contains + # registers that need to be saved and restored across the call. descr = op.getdescr() if we_are_translated(): cls = self.cpu.gc_ll_descr.has_write_barrier_class() assert cls is not None and isinstance(descr, cls) loc_base = arglocs[0] + # ensure that enough bytes are available to write the whole + # following piece of code atomically (for the JZ) self.mc.ensure_bytes_available(256) self.mc.TEST8_mi((loc_base.value, descr.jit_wb_if_flag_byteofs), descr.jit_wb_if_flag_singlebyte) @@ -1782,36 +1792,39 @@ class Assembler386(object): jz_location = self.mc.get_relative_pos() # the following is supposed to be the slow path, so whenever possible # we choose the most compact encoding over the most efficient one. - # XXX improve a bit, particularly for IS_X86_64. - for i in range(len(arglocs)-1, -1, -1): + if IS_X86_32: + limit = -1 # push all arglocs on the stack + elif IS_X86_64: + limit = 1 # push only arglocs[2:] on the stack + for i in range(len(arglocs)-1, limit, -1): loc = arglocs[i] if isinstance(loc, RegLoc): self.mc.PUSH_r(loc.value) else: - if IS_X86_64: - self.mc.MOV_ri(X86_64_SCRATCH_REG.value, loc.getint()) - self.mc.PUSH_r(X86_64_SCRATCH_REG.value) - else: - self.mc.PUSH_i32(loc.getint()) - + assert not IS_X86_64 # there should only be regs in arglocs[2:] + self.mc.PUSH_i32(loc.getint()) if IS_X86_64: # We clobber these registers to pass the arguments, but that's # okay, because consider_cond_call_gc_wb makes sure that any - # caller-save registers with values in them are present in arglocs, - # so they are saved on the stack above and restored below - self.mc.MOV_rs(edi.value, 0) - self.mc.MOV_rs(esi.value, 8) + # caller-save registers with values in them are present in + # arglocs[2:] too, so they are saved on the stack above and + # restored below. + remap_frame_layout(self, arglocs[:2], [edi, esi], + X86_64_SCRATCH_REG) # misaligned stack in the call, but it's ok because the write barrier # is not going to call anything more. Also, this assumes that the - # write barrier does not touch the xmm registers. + # write barrier does not touch the xmm registers. (Slightly delicate + # assumption, given that the write barrier can end up calling the + # platform's malloc() from AddressStack.append(). XXX may need to + # be done properly) self.mc.CALL(imm(descr.get_write_barrier_fn(self.cpu))) - for i in range(len(arglocs)): + if IS_X86_32: + self.mc.ADD_ri(esp.value, 2*WORD) + for i in range(2, len(arglocs)): loc = arglocs[i] - if isinstance(loc, RegLoc): - self.mc.POP_r(loc.value) - else: - self.mc.ADD_ri(esp.value, WORD) # ignore the pushed constant + assert isinstance(loc, RegLoc) + self.mc.POP_r(loc.value) # patch the JZ above offset = self.mc.get_relative_pos() - jz_location assert 0 < offset <= 127 @@ -1848,6 +1861,7 @@ class Assembler386(object): def malloc_cond_fixedsize(self, nursery_free_adr, nursery_top_adr, size, tid): + size = max(size, self.cpu.gc_ll_descr.minimal_size_in_nursery) self.mc.ensure_bytes_available(256) self.mc.MOV(eax, heap(nursery_free_adr)) self.mc.LEA_rm(edx.value, (eax.value, size)) diff --git a/pypy/jit/backend/x86/test/test_runner.py b/pypy/jit/backend/x86/test/test_runner.py index c307e515bd..05e21d5056 100644 --- a/pypy/jit/backend/x86/test/test_runner.py +++ b/pypy/jit/backend/x86/test/test_runner.py @@ -346,7 +346,7 @@ class TestX86(LLtypeBackendTest): return self.val operations = [ - ResOperation(rop.DEBUG_MERGE_POINT, [FakeString("hello")], None), + ResOperation(rop.DEBUG_MERGE_POINT, [FakeString("hello"), 0], None), ResOperation(rop.INT_ADD, [i0, ConstInt(1)], i1), ResOperation(rop.INT_LE, [i1, ConstInt(9)], i2), ResOperation(rop.GUARD_TRUE, [i2], None, descr=faildescr1), @@ -365,7 +365,7 @@ class TestX86(LLtypeBackendTest): bridge = [ ResOperation(rop.INT_LE, [i1b, ConstInt(19)], i3), ResOperation(rop.GUARD_TRUE, [i3], None, descr=faildescr2), - ResOperation(rop.DEBUG_MERGE_POINT, [FakeString("bye")], None), + ResOperation(rop.DEBUG_MERGE_POINT, [FakeString("bye"), 0], None), ResOperation(rop.JUMP, [i1b], None, descr=looptoken), ] bridge[1].setfailargs([i1b]) @@ -497,7 +497,7 @@ class TestDebuggingAssembler(object): def test_debugger_on(self): loop = """ [i0] - debug_merge_point('xyz') + debug_merge_point('xyz', 0) i1 = int_add(i0, 1) i2 = int_ge(i1, 10) guard_false(i2) [] @@ -515,3 +515,20 @@ class TestDebuggingAssembler(object): self.cpu.finish_once() lines = py.path.local(self.logfile + ".count").readlines() assert lines[0] == '0:10\n' # '10 xyz\n' + + def test_debugger_checksum(self): + loop = """ + [i0] + debug_merge_point('xyz', 0) + i1 = int_add(i0, 1) + i2 = int_ge(i1, 10) + guard_false(i2) [] + jump(i1) + """ + ops = parse(loop) + self.cpu.assembler.set_debug(True) + self.cpu.compile_loop(ops.inputargs, ops.operations, ops.token) + self.cpu.set_future_value_int(0, 0) + self.cpu.execute_token(ops.token) + assert ops.token._x86_debug_checksum == sum([op.getopnum() + for op in ops.operations]) diff --git a/pypy/jit/backend/x86/test/test_zrpy_gc.py b/pypy/jit/backend/x86/test/test_zrpy_gc.py index 5fe1cfdb98..0fd525b09b 100644 --- a/pypy/jit/backend/x86/test/test_zrpy_gc.py +++ b/pypy/jit/backend/x86/test/test_zrpy_gc.py @@ -550,3 +550,29 @@ class TestCompileFramework(object): def test_compile_framework_float(self): self.run('compile_framework_float') + + def define_compile_framework_minimal_size_in_nursery(self): + S = lltype.GcStruct('S') # no fields! + T = lltype.GcStruct('T', ('i', lltype.Signed)) + @unroll_safe + def f42(n, x, x0, x1, x2, x3, x4, x5, x6, x7, l, s): + lst1 = [] + lst2 = [] + i = 0 + while i < 42: + s1 = lltype.malloc(S) + t1 = lltype.malloc(T) + t1.i = 10000 + i + n + lst1.append(s1) + lst2.append(t1) + i += 1 + i = 0 + while i < 42: + check(lst2[i].i == 10000 + i + n) + i += 1 + n -= 1 + return n, x, x0, x1, x2, x3, x4, x5, x6, x7, l, s + return None, f42, None + + def test_compile_framework_minimal_size_in_nursery(self): + self.run('compile_framework_minimal_size_in_nursery') diff --git a/pypy/jit/backend/x86/test/test_ztranslation.py b/pypy/jit/backend/x86/test/test_ztranslation.py index f3510639fd..1e3f81b553 100644 --- a/pypy/jit/backend/x86/test/test_ztranslation.py +++ b/pypy/jit/backend/x86/test/test_ztranslation.py @@ -2,6 +2,7 @@ import py, os, sys from pypy.tool.udir import udir from pypy.rlib.jit import JitDriver, OPTIMIZER_FULL, unroll_parameters from pypy.rlib.jit import PARAMETERS, dont_look_inside +from pypy.rlib.jit import hint from pypy.jit.metainterp.jitprof import Profiler from pypy.jit.backend.detect_cpu import getcpuclass from pypy.jit.backend.test.support import CCompiledMixin @@ -9,6 +10,7 @@ from pypy.jit.codewriter.policy import StopAtXPolicy from pypy.translator.translator import TranslationContext from pypy.jit.backend.x86.arch import IS_X86_32, IS_X86_64 from pypy.config.translationoption import DEFL_GC +from pypy.rlib import rgc class TestTranslationX86(CCompiledMixin): CPUClass = getcpuclass() @@ -82,12 +84,12 @@ class TestTranslationX86(CCompiledMixin): argchain.arg(x) res = func.call(argchain, rffi.DOUBLE) i -= 1 - return res + return int(res) # def main(i, j): return f(i, j) + libffi_stuff(i, j) - expected = f(40, -49) - res = self.meta_interp(f, [40, -49]) + expected = main(40, -49) + res = self.meta_interp(main, [40, -49]) assert res == expected def test_direct_assembler_call_translates(self): diff --git a/pypy/jit/backend/x86/tool/viewcode.py b/pypy/jit/backend/x86/tool/viewcode.py index 33a1895121..dd0ff1606f 100755 --- a/pypy/jit/backend/x86/tool/viewcode.py +++ b/pypy/jit/backend/x86/tool/viewcode.py @@ -37,7 +37,7 @@ def machine_code_dump(data, originaddr, backend_name): 'x86_64': 'x86-64', 'i386': 'i386', } - objdump = ('objdump -M intel,%(backend)s -b binary -m i386 ' + objdump = ('objdump -M %(backend)s -b binary -m i386 ' '--adjust-vma=%(origin)d -D %(file)s') # f = open(tmpfile, 'wb') diff --git a/pypy/jit/codewriter/assembler.py b/pypy/jit/codewriter/assembler.py index 0fb4f6051a..1fdd0d6ea2 100644 --- a/pypy/jit/codewriter/assembler.py +++ b/pypy/jit/codewriter/assembler.py @@ -233,10 +233,9 @@ class Assembler(object): addr = llmemory.cast_ptr_to_adr(value) self.list_of_addr2name.append((addr, name)) - def finished(self): + def finished(self, callinfocollection): # Helper called at the end of assembling. Registers the extra # functions shown in _callinfo_for_oopspec. - from pypy.jit.codewriter.effectinfo import _callinfo_for_oopspec - for _, func in _callinfo_for_oopspec.values(): + for func in callinfocollection.all_function_addresses_as_int(): func = heaptracker.int2adr(func) self.see_raw_object(func.ptr) diff --git a/pypy/jit/codewriter/call.py b/pypy/jit/codewriter/call.py index 9dc12a7cb7..2f82e49a08 100644 --- a/pypy/jit/codewriter/call.py +++ b/pypy/jit/codewriter/call.py @@ -7,7 +7,7 @@ from pypy.jit.codewriter import support from pypy.jit.codewriter.jitcode import JitCode from pypy.jit.codewriter.effectinfo import VirtualizableAnalyzer from pypy.jit.codewriter.effectinfo import effectinfo_from_writeanalyze -from pypy.jit.codewriter.effectinfo import EffectInfo +from pypy.jit.codewriter.effectinfo import EffectInfo, CallInfoCollection from pypy.translator.simplify import get_funcobj, get_functype from pypy.rpython.lltypesystem import lltype, llmemory from pypy.translator.backendopt.canraise import RaiseAnalyzer @@ -23,6 +23,7 @@ class CallControl(object): self.jitdrivers_sd = jitdrivers_sd self.jitcodes = {} # map {graph: jitcode} self.unfinished_graphs = [] # list of graphs with pending jitcodes + self.callinfocollection = CallInfoCollection() if hasattr(cpu, 'rtyper'): # for tests self.rtyper = cpu.rtyper translator = self.rtyper.annotator.translator diff --git a/pypy/jit/codewriter/codewriter.py b/pypy/jit/codewriter/codewriter.py index 9013ed9615..ff2c860c17 100644 --- a/pypy/jit/codewriter/codewriter.py +++ b/pypy/jit/codewriter/codewriter.py @@ -73,7 +73,7 @@ class CodeWriter(object): count += 1 if not count % 500: log.info("Produced %d jitcodes" % count) - self.assembler.finished() + self.assembler.finished(self.callcontrol.callinfocollection) heaptracker.finish_registering(self.cpu) log.info("there are %d JitCode instances." % count) diff --git a/pypy/jit/codewriter/effectinfo.py b/pypy/jit/codewriter/effectinfo.py index d8c18af704..84c7edcab2 100644 --- a/pypy/jit/codewriter/effectinfo.py +++ b/pypy/jit/codewriter/effectinfo.py @@ -144,30 +144,44 @@ class VirtualizableAnalyzer(BoolGraphAnalyzer): # ____________________________________________________________ -_callinfo_for_oopspec = {} # {oopspecindex: (calldescr, func_as_int)} - -def callinfo_for_oopspec(oopspecindex): - """A function that returns the calldescr and the function - address (as an int) of one of the OS_XYZ functions defined above. - Don't use this if there might be several implementations of the same - OS_XYZ specialized by type, e.g. OS_ARRAYCOPY.""" - try: - return _callinfo_for_oopspec[oopspecindex] - except KeyError: - return (None, 0) - - -def _funcptr_for_oopspec_memo(oopspecindex): - from pypy.jit.codewriter import heaptracker - _, func_as_int = callinfo_for_oopspec(oopspecindex) - funcadr = heaptracker.int2adr(func_as_int) - return funcadr.ptr -_funcptr_for_oopspec_memo._annspecialcase_ = 'specialize:memo' - -def funcptr_for_oopspec(oopspecindex): - """A memo function that returns a pointer to the function described - by OS_XYZ (as a real low-level function pointer).""" - funcptr = _funcptr_for_oopspec_memo(oopspecindex) - assert funcptr - return funcptr -funcptr_for_oopspec._annspecialcase_ = 'specialize:arg(0)' +class CallInfoCollection(object): + def __init__(self): + # {oopspecindex: (calldescr, func_as_int)} + self._callinfo_for_oopspec = {} + + def _freeze_(self): + return True + + def add(self, oopspecindex, calldescr, func_as_int): + self._callinfo_for_oopspec[oopspecindex] = calldescr, func_as_int + + def has_oopspec(self, oopspecindex): + return oopspecindex in self._callinfo_for_oopspec + + def all_function_addresses_as_int(self): + return [func for (_, func) in self._callinfo_for_oopspec.values()] + + def callinfo_for_oopspec(self, oopspecindex): + """A function that returns the calldescr and the function + address (as an int) of one of the OS_XYZ functions defined above. + Don't use this if there might be several implementations of the same + OS_XYZ specialized by type, e.g. OS_ARRAYCOPY.""" + try: + return self._callinfo_for_oopspec[oopspecindex] + except KeyError: + return (None, 0) + + def _funcptr_for_oopspec_memo(self, oopspecindex): + from pypy.jit.codewriter import heaptracker + _, func_as_int = self.callinfo_for_oopspec(oopspecindex) + funcadr = heaptracker.int2adr(func_as_int) + return funcadr.ptr + _funcptr_for_oopspec_memo._annspecialcase_ = 'specialize:memo' + + def funcptr_for_oopspec(self, oopspecindex): + """A memo function that returns a pointer to the function described + by OS_XYZ (as a real low-level function pointer).""" + funcptr = self._funcptr_for_oopspec_memo(oopspecindex) + assert funcptr + return funcptr + funcptr_for_oopspec._annspecialcase_ = 'specialize:arg(1)' diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py index 326e1fe16f..68e7127e02 100644 --- a/pypy/jit/codewriter/jtransform.py +++ b/pypy/jit/codewriter/jtransform.py @@ -6,7 +6,7 @@ from pypy.objspace.flow.model import SpaceOperation, Variable, Constant from pypy.objspace.flow.model import Block, Link, c_last_exception from pypy.jit.codewriter.flatten import ListOfKind, IndirectCallTargets from pypy.jit.codewriter import support, heaptracker -from pypy.jit.codewriter.effectinfo import EffectInfo, _callinfo_for_oopspec +from pypy.jit.codewriter.effectinfo import EffectInfo from pypy.jit.codewriter.policy import log from pypy.jit.metainterp.typesystem import deref, arrayItem from pypy.rlib import objectmodel @@ -873,6 +873,8 @@ class Transformer(object): elif oopspec_name == 'jit.assert_green': kind = getkind(args[0].concretetype) return SpaceOperation('%s_assert_green' % kind, args, None) + elif oopspec_name == 'jit.current_trace_length': + return SpaceOperation('current_trace_length', [], op.result) else: raise AssertionError("missing support for %r" % oopspec_name) @@ -1082,7 +1084,8 @@ class Transformer(object): else: func = heaptracker.adr2int( llmemory.cast_ptr_to_adr(op.args[0].value)) - _callinfo_for_oopspec[oopspecindex] = calldescr, func + self.callcontrol.callinfocollection.add(oopspecindex, + calldescr, func) op1 = self.rewrite_call(op, 'residual_call', [op.args[0], calldescr], args=args) @@ -1093,7 +1096,7 @@ class Transformer(object): def _register_extra_helper(self, oopspecindex, oopspec_name, argtypes, resulttype): # a bit hackish - if oopspecindex in _callinfo_for_oopspec: + if self.callcontrol.callinfocollection.has_oopspec(oopspecindex): return c_func, TP = support.builtin_func_for_spec(self.cpu.rtyper, oopspec_name, argtypes, @@ -1107,7 +1110,7 @@ class Transformer(object): else: func = heaptracker.adr2int( llmemory.cast_ptr_to_adr(c_func.value)) - _callinfo_for_oopspec[oopspecindex] = calldescr, func + self.callcontrol.callinfocollection.add(oopspecindex, calldescr, func) def _handle_stroruni_call(self, op, oopspec_name, args): SoU = args[0].concretetype # Ptr(STR) or Ptr(UNICODE) diff --git a/pypy/jit/codewriter/test/test_jtransform.py b/pypy/jit/codewriter/test/test_jtransform.py index 760feea625..01dca13392 100644 --- a/pypy/jit/codewriter/test/test_jtransform.py +++ b/pypy/jit/codewriter/test/test_jtransform.py @@ -74,7 +74,20 @@ class FakeRegularIndirectCallControl: def calldescr_canraise(self, calldescr): return False +class FakeCallInfoCollection: + def __init__(self): + self.seen = [] + def add(self, oopspecindex, calldescr, func): + self.seen.append((oopspecindex, calldescr, func)) + def has_oopspec(self, oopspecindex): + for i, c, f in self.seen: + if i == oopspecindex: + return True + return False + class FakeBuiltinCallControl: + def __init__(self): + self.callinfocollection = FakeCallInfoCollection() def guess_call_kind(self, op): return 'builtin' def getcalldescr(self, op, oopspecindex=None): @@ -810,7 +823,8 @@ def test_unicode_concat(): v2 = varoftype(PSTR) v3 = varoftype(PSTR) op = SpaceOperation('direct_call', [const(func), v1, v2], v3) - tr = Transformer(FakeCPU(), FakeBuiltinCallControl()) + cc = FakeBuiltinCallControl() + tr = Transformer(FakeCPU(), cc) op1 = tr.rewrite_operation(op) assert op1.opname == 'residual_call_r_r' assert op1.args[0].value == func @@ -819,9 +833,10 @@ def test_unicode_concat(): assert op1.result == v3 # # check the callinfo_for_oopspec - got = effectinfo.callinfo_for_oopspec(effectinfo.EffectInfo.OS_UNI_CONCAT) - assert got[0] == op1.args[1] # the calldescr - assert heaptracker.int2adr(got[1]) == llmemory.cast_ptr_to_adr(func) + got = cc.callinfocollection.seen[0] + assert got[0] == effectinfo.EffectInfo.OS_UNI_CONCAT + assert got[1] == op1.args[1] # the calldescr + assert heaptracker.int2adr(got[2]) == llmemory.cast_ptr_to_adr(func) def test_str_slice(): # test that the oopspec is present and correctly transformed @@ -893,7 +908,8 @@ def test_unicode_eq_checknull_char(): v2 = varoftype(PUNICODE) v3 = varoftype(lltype.Bool) op = SpaceOperation('direct_call', [const(func), v1, v2], v3) - tr = Transformer(FakeCPU(), FakeBuiltinCallControl()) + cc = FakeBuiltinCallControl() + tr = Transformer(FakeCPU(), cc) op1 = tr.rewrite_operation(op) assert op1.opname == 'residual_call_r_i' assert op1.args[0].value == func @@ -901,9 +917,9 @@ def test_unicode_eq_checknull_char(): assert op1.args[2] == ListOfKind('ref', [v1, v2]) assert op1.result == v3 # test that the OS_UNIEQ_* functions are registered - cifo = effectinfo._callinfo_for_oopspec - assert effectinfo.EffectInfo.OS_UNIEQ_SLICE_NONNULL in cifo - assert effectinfo.EffectInfo.OS_UNIEQ_CHECKNULL_CHAR in cifo + cic = cc.callinfocollection + assert cic.has_oopspec(effectinfo.EffectInfo.OS_UNIEQ_SLICE_NONNULL) + assert cic.has_oopspec(effectinfo.EffectInfo.OS_UNIEQ_CHECKNULL_CHAR) def test_list_ll_arraycopy(): from pypy.rlib.rgc import ll_arraycopy diff --git a/pypy/jit/metainterp/blackhole.py b/pypy/jit/metainterp/blackhole.py index 700280971b..c67dc8b2fa 100644 --- a/pypy/jit/metainterp/blackhole.py +++ b/pypy/jit/metainterp/blackhole.py @@ -774,6 +774,10 @@ class BlackholeInterpreter(object): def bhimpl_float_assert_green(x): pass + @arguments(returns="i") + def bhimpl_current_trace_length(): + return -1 + # ---------- # the main hints and recursive calls diff --git a/pypy/jit/metainterp/compile.py b/pypy/jit/metainterp/compile.py index 31df49b3ff..342898daab 100644 --- a/pypy/jit/metainterp/compile.py +++ b/pypy/jit/metainterp/compile.py @@ -1,4 +1,5 @@ +from pypy.rpython.lltypesystem import lltype from pypy.rpython.ootypesystem import ootype from pypy.objspace.flow.model import Constant, Variable from pypy.rlib.objectmodel import we_are_translated @@ -13,6 +14,7 @@ from pypy.jit.metainterp.history import BoxPtr, BoxObj, BoxFloat, Const from pypy.jit.metainterp import history from pypy.jit.metainterp.typesystem import llhelper, oohelper from pypy.jit.metainterp.optimizeutil import InvalidLoop +from pypy.jit.metainterp.resume import NUMBERING from pypy.jit.codewriter import heaptracker def giveup(): @@ -52,7 +54,6 @@ def compile_new_loop(metainterp, old_loop_tokens, greenkey, start): """ history = metainterp.history loop = create_empty_loop(metainterp) - loop.greenkey = greenkey loop.inputargs = history.inputargs for box in loop.inputargs: assert isinstance(box, Box) @@ -66,7 +67,6 @@ def compile_new_loop(metainterp, old_loop_tokens, greenkey, start): loop.operations[-1].setdescr(loop_token) # patch the target of the JUMP loop.preamble = create_empty_loop(metainterp, 'Preamble ') - loop.preamble.greenkey = greenkey loop.preamble.inputargs = loop.inputargs loop.preamble.token = make_loop_token(len(loop.inputargs), jitdriver_sd) @@ -214,8 +214,7 @@ def make_done_loop_tokens(): } class ResumeDescr(AbstractFailDescr): - def __init__(self, original_greenkey): - self.original_greenkey = original_greenkey + pass class ResumeGuardDescr(ResumeDescr): _counter = 0 # if < 0, there is one counter per value; @@ -224,7 +223,7 @@ class ResumeGuardDescr(ResumeDescr): # this class also gets the following attributes stored by resume.py code rd_snapshot = None rd_frame_info_list = None - rd_numb = None + rd_numb = lltype.nullptr(NUMBERING) rd_consts = None rd_virtuals = None rd_pendingfields = None @@ -234,8 +233,7 @@ class ResumeGuardDescr(ResumeDescr): CNT_FLOAT = -0x60000000 CNT_MASK = 0x1FFFFFFF - def __init__(self, metainterp_sd, original_greenkey): - ResumeDescr.__init__(self, original_greenkey) + def __init__(self, metainterp_sd): self.metainterp_sd = metainterp_sd def store_final_boxes(self, guard_op, boxes): @@ -339,14 +337,14 @@ class ResumeGuardDescr(ResumeDescr): res.rd_pendingfields = self.rd_pendingfields def _clone_if_mutable(self): - res = ResumeGuardDescr(self.metainterp_sd, self.original_greenkey) + res = ResumeGuardDescr(self.metainterp_sd) self.copy_all_attrbutes_into(res) return res class ResumeGuardForcedDescr(ResumeGuardDescr): - def __init__(self, metainterp_sd, original_greenkey, jitdriver_sd): - ResumeGuardDescr.__init__(self, metainterp_sd, original_greenkey) + def __init__(self, metainterp_sd, jitdriver_sd): + ResumeGuardDescr.__init__(self, metainterp_sd) self.jitdriver_sd = jitdriver_sd def handle_fail(self, metainterp_sd, jitdriver_sd): @@ -413,7 +411,6 @@ class ResumeGuardForcedDescr(ResumeGuardDescr): def _clone_if_mutable(self): res = ResumeGuardForcedDescr(self.metainterp_sd, - self.original_greenkey, self.jitdriver_sd) self.copy_all_attrbutes_into(res) return res @@ -480,9 +477,8 @@ class ResumeGuardCountersFloat(AbstractResumeGuardCounters): class ResumeFromInterpDescr(ResumeDescr): - def __init__(self, original_greenkey, redkey): - ResumeDescr.__init__(self, original_greenkey) - self.redkey = redkey + def __init__(self, original_greenkey): + self.original_greenkey = original_greenkey def compile_and_attach(self, metainterp, new_loop): # We managed to create a bridge going from the interpreter @@ -491,10 +487,8 @@ class ResumeFromInterpDescr(ResumeDescr): # with completely unoptimized arguments, as in the interpreter. metainterp_sd = metainterp.staticdata jitdriver_sd = metainterp.jitdriver_sd - metainterp.history.inputargs = self.redkey - new_loop_token = make_loop_token(len(self.redkey), jitdriver_sd) - new_loop.greenkey = self.original_greenkey - new_loop.inputargs = self.redkey + redargs = new_loop.inputargs + new_loop_token = make_loop_token(len(redargs), jitdriver_sd) new_loop.token = new_loop_token send_loop_to_backend(metainterp_sd, new_loop, "entry bridge") # send the new_loop to warmspot.py, to be called directly the next time diff --git a/pypy/jit/metainterp/logger.py b/pypy/jit/metainterp/logger.py index 189462f3b5..898e1b4ff8 100644 --- a/pypy/jit/metainterp/logger.py +++ b/pypy/jit/metainterp/logger.py @@ -81,7 +81,8 @@ class Logger(object): op = operations[i] if op.getopnum() == rop.DEBUG_MERGE_POINT: loc = op.getarg(0)._get_str() - debug_print("debug_merge_point('%s')" % (loc,)) + reclev = op.getarg(1).getint() + debug_print("debug_merge_point('%s', %s)" % (loc, reclev)) continue args = ", ".join([self.repr_of_arg(memo, op.getarg(i)) for i in range(op.numargs())]) if op.result is not None: diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py b/pypy/jit/metainterp/optimizeopt/optimizer.py index 2bf3551380..cbceaf4b42 100644 --- a/pypy/jit/metainterp/optimizeopt/optimizer.py +++ b/pypy/jit/metainterp/optimizeopt/optimizer.py @@ -86,10 +86,10 @@ class OptValue(object): assert isinstance(constbox, Const) self.box = constbox self.level = LEVEL_CONSTANT - try: - val = self.box.getint() + if isinstance(constbox, ConstInt): + val = constbox.getint() self.intbound = IntBound(val, val) - except NotImplementedError: + else: self.intbound = IntUnbounded() def get_constant_class(self, cpu): diff --git a/pypy/jit/metainterp/optimizeopt/string.py b/pypy/jit/metainterp/optimizeopt/string.py index 949d0b63b9..6680885490 100644 --- a/pypy/jit/metainterp/optimizeopt/string.py +++ b/pypy/jit/metainterp/optimizeopt/string.py @@ -9,7 +9,7 @@ from pypy.jit.metainterp.optimizeopt import optimizer, virtualize from pypy.jit.metainterp.optimizeopt.optimizer import CONST_0, CONST_1 from pypy.jit.metainterp.optimizeopt.optimizer import llhelper from pypy.jit.metainterp.optimizeutil import _findall -from pypy.jit.codewriter.effectinfo import EffectInfo, callinfo_for_oopspec +from pypy.jit.codewriter.effectinfo import EffectInfo from pypy.jit.codewriter import heaptracker from pypy.rlib.unroll import unrolling_iterable from pypy.rlib.objectmodel import specialize, we_are_translated @@ -642,7 +642,8 @@ class OptString(optimizer.Optimization): def generate_modified_call(self, oopspecindex, args, result, mode): oopspecindex += mode.OS_offset - calldescr, func = callinfo_for_oopspec(oopspecindex) + cic = self.optimizer.metainterp_sd.callinfocollection + calldescr, func = cic.callinfo_for_oopspec(oopspecindex) op = ResOperation(rop.CALL, [ConstInt(func)] + args, result, descr=calldescr) self.optimizer.newoperations.append(op) diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py index 35b8fc0360..abb8fc84d3 100644 --- a/pypy/jit/metainterp/pyjitpl.py +++ b/pypy/jit/metainterp/pyjitpl.py @@ -14,7 +14,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 +from pypy.jit.metainterp.jitprof import ABORT_TOO_LONG from pypy.jit.metainterp.jitexc import JitException, get_llexception from pypy.rlib.rarithmetic import intmask from pypy.rlib.objectmodel import specialize @@ -820,7 +820,8 @@ class MIFrame(object): jitdriver_sd = self.metainterp.staticdata.jitdrivers_sd[jdindex] self.verify_green_args(jitdriver_sd, greenboxes) # xxx we may disable the following line in some context later - self.debug_merge_point(jitdriver_sd, greenboxes) + self.debug_merge_point(jitdriver_sd, self.metainterp.in_recursion, + greenboxes) if self.metainterp.seen_loop_header_for_jdindex < 0: if not jitdriver_sd.no_loop_header or not any_operation: return @@ -860,13 +861,13 @@ class MIFrame(object): assembler_call=True) raise ChangeFrame - def debug_merge_point(self, jitdriver_sd, greenkey): + def debug_merge_point(self, jitdriver_sd, in_recursion, greenkey): # debugging: produce a DEBUG_MERGE_POINT operation loc = jitdriver_sd.warmstate.get_location_str(greenkey) debug_print(loc) constloc = self.metainterp.cpu.ts.conststr(loc) self.metainterp.history.record(rop.DEBUG_MERGE_POINT, - [constloc], None) + [constloc, ConstInt(in_recursion)], None) @arguments("box", "label") def opimpl_goto_if_exception_mismatch(self, vtablebox, next_exc_target): @@ -948,6 +949,11 @@ class MIFrame(object): opimpl_ref_assert_green = _opimpl_assert_green opimpl_float_assert_green = _opimpl_assert_green + @arguments() + def opimpl_current_trace_length(self): + trace_length = len(self.metainterp.history.operations) + return ConstInt(trace_length) + @arguments("box") def opimpl_virtual_ref(self, box): # Details on the content of metainterp.virtualref_boxes: @@ -1042,14 +1048,11 @@ class MIFrame(object): else: moreargs = list(extraargs) metainterp_sd = metainterp.staticdata - original_greenkey = metainterp.resumekey.original_greenkey if opnum == rop.GUARD_NOT_FORCED: resumedescr = compile.ResumeGuardForcedDescr(metainterp_sd, - original_greenkey, metainterp.jitdriver_sd) else: - resumedescr = compile.ResumeGuardDescr(metainterp_sd, - original_greenkey) + resumedescr = compile.ResumeGuardDescr(metainterp_sd) guard_op = metainterp.history.record(opnum, moreargs, None, descr=resumedescr) virtualizable_boxes = None @@ -1255,6 +1258,7 @@ class MetaInterpStaticData(object): # self.jitdrivers_sd = codewriter.callcontrol.jitdrivers_sd self.virtualref_info = codewriter.callcontrol.virtualref_info + self.callinfocollection = codewriter.callcontrol.callinfocollection self.setup_jitdrivers_sd(optimizer) # # store this information for fastpath of call_assembler @@ -1618,7 +1622,7 @@ class MetaInterp(object): assert jitdriver_sd is self.jitdriver_sd self.create_empty_history() try: - original_boxes = self.initialize_original_boxes(jitdriver_sd,*args) + original_boxes = self.initialize_original_boxes(jitdriver_sd, *args) return self._compile_and_run_once(original_boxes) finally: self.staticdata.profiler.end_tracing() @@ -1629,9 +1633,8 @@ class MetaInterp(object): self.current_merge_points = [(original_boxes, 0)] num_green_args = self.jitdriver_sd.num_green_args original_greenkey = original_boxes[:num_green_args] - redkey = original_boxes[num_green_args:] - self.resumekey = compile.ResumeFromInterpDescr(original_greenkey, - redkey) + self.resumekey = compile.ResumeFromInterpDescr(original_greenkey) + self.history.inputargs = original_boxes[num_green_args:] self.seen_loop_header_for_jdindex = -1 try: self.interpret() @@ -1653,11 +1656,7 @@ class MetaInterp(object): debug_stop('jit-tracing') def _handle_guard_failure(self, key): - original_greenkey = key.original_greenkey - # notice that here we just put the greenkey - # use -1 to mark that we will have to give up - # because we cannot reconstruct the beginning of the proper loop - self.current_merge_points = [(original_greenkey, -1)] + self.current_merge_points = [] self.resumekey = key self.seen_loop_header_for_jdindex = -1 try: @@ -1721,7 +1720,7 @@ class MetaInterp(object): num_green_args = self.jitdriver_sd.num_green_args for j in range(len(self.current_merge_points)-1, -1, -1): original_boxes, start = self.current_merge_points[j] - assert len(original_boxes) == len(live_arg_boxes) or start < 0 + assert len(original_boxes) == len(live_arg_boxes) for i in range(num_green_args): box1 = original_boxes[i] box2 = live_arg_boxes[i] @@ -1730,10 +1729,6 @@ class MetaInterp(object): break else: # Found! Compile it as a loop. - if start < 0: - # we cannot reconstruct the beginning of the proper loop - raise SwitchToBlackhole(ABORT_BRIDGE) - # raises in case it works -- which is the common case self.compile(original_boxes, live_arg_boxes, start) # creation of the loop was cancelled! diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py index eea9079a9a..08e2e7126c 100644 --- a/pypy/jit/metainterp/resoperation.py +++ b/pypy/jit/metainterp/resoperation.py @@ -143,6 +143,16 @@ class AbstractResOp(object): def can_raise(self): return rop._CANRAISE_FIRST <= self.getopnum() <= rop._CANRAISE_LAST + def is_malloc(self): + # a slightly different meaning from can_malloc + return rop._MALLOC_FIRST <= self.getopnum() <= rop._MALLOC_LAST + + def can_malloc(self): + return self.is_call() or self.is_malloc() + + def is_call(self): + return rop._CALL_FIRST <= self.getopnum() <= rop._CALL_LAST + def is_ovf(self): return rop._OVF_FIRST <= self.getopnum() <= rop._OVF_LAST @@ -441,9 +451,13 @@ _oplist = [ 'GETARRAYITEM_RAW/2d', 'GETFIELD_GC/1d', 'GETFIELD_RAW/1d', + '_MALLOC_FIRST', 'NEW/0d', 'NEW_WITH_VTABLE/1', 'NEW_ARRAY/1d', + 'NEWSTR/1', + 'NEWUNICODE/1', + '_MALLOC_LAST', 'FORCE_TOKEN/0', 'VIRTUAL_REF/2', # removed before it's passed to the backend '_NOSIDEEFFECT_LAST', # ----- end of no_side_effect operations ----- @@ -452,19 +466,18 @@ _oplist = [ 'SETARRAYITEM_RAW/3d', 'SETFIELD_GC/2d', 'SETFIELD_RAW/2d', - 'NEWSTR/1', 'STRSETITEM/3', 'UNICODESETITEM/3', - 'NEWUNICODE/1', #'RUNTIMENEW/1', # ootype operation 'COND_CALL_GC_WB/2d', # [objptr, newvalue] (for the write barrier) - 'DEBUG_MERGE_POINT/1', # debugging only + 'DEBUG_MERGE_POINT/2', # debugging only 'JIT_DEBUG/*', # debugging only 'VIRTUAL_REF_FINISH/2', # removed before it's passed to the backend 'COPYSTRCONTENT/5', # src, dst, srcstart, dststart, length 'COPYUNICODECONTENT/5', '_CANRAISE_FIRST', # ----- start of can_raise operations ----- + '_CALL_FIRST', 'CALL/*d', 'CALL_ASSEMBLER/*d', # call already compiled assembler 'CALL_MAY_FORCE/*d', @@ -473,6 +486,7 @@ _oplist = [ #'OOSEND_PURE', # ootype operation 'CALL_PURE/*d', # removed before it's passed to the backend # CALL_PURE(result, func, arg_1,..,arg_n) + '_CALL_LAST', '_CANRAISE_LAST', # ----- end of can_raise operations ----- '_OVF_FIRST', # ----- start of is_ovf operations ----- diff --git a/pypy/jit/metainterp/resume.py b/pypy/jit/metainterp/resume.py index 338bba1065..89a5d28354 100644 --- a/pypy/jit/metainterp/resume.py +++ b/pypy/jit/metainterp/resume.py @@ -4,8 +4,7 @@ from pypy.jit.metainterp.history import BoxInt, BoxPtr, BoxFloat from pypy.jit.metainterp.history import INT, REF, FLOAT, HOLE from pypy.jit.metainterp.resoperation import rop from pypy.jit.metainterp import jitprof -from pypy.jit.codewriter.effectinfo import EffectInfo, callinfo_for_oopspec -from pypy.jit.codewriter.effectinfo import funcptr_for_oopspec +from pypy.jit.codewriter.effectinfo import EffectInfo from pypy.rpython.lltypesystem import lltype, llmemory, rffi, rstr from pypy.rlib import rarithmetic from pypy.rlib.objectmodel import we_are_translated, specialize @@ -66,12 +65,21 @@ def capture_resumedata(framestack, virtualizable_boxes, virtualref_boxes, snapshot = Snapshot(snapshot, boxes) storage.rd_snapshot = snapshot -class Numbering(object): - __slots__ = ('prev', 'nums') - - def __init__(self, prev, nums): - self.prev = prev - self.nums = nums +# +# The following is equivalent to the RPython-level declaration: +# +# class Numbering: __slots__ = ['prev', 'nums'] +# +# except that it is more compact in translated programs, because the +# array 'nums' is inlined in the single NUMBERING object. This is +# important because this is often the biggest single consumer of memory +# in a pypy-c-jit. +# +NUMBERINGP = lltype.Ptr(lltype.GcForwardReference()) +NUMBERING = lltype.GcStruct('Numbering', + ('prev', NUMBERINGP), + ('nums', lltype.Array(rffi.SHORT))) +NUMBERINGP.TO.become(NUMBERING) TAGMASK = 3 @@ -163,7 +171,7 @@ class ResumeDataLoopMemo(object): def number(self, values, snapshot): if snapshot is None: - return None, {}, 0 + return lltype.nullptr(NUMBERING), {}, 0 if snapshot in self.numberings: numb, liveboxes, v = self.numberings[snapshot] return numb, liveboxes.copy(), v @@ -172,7 +180,7 @@ class ResumeDataLoopMemo(object): n = len(liveboxes)-v boxes = snapshot.boxes length = len(boxes) - nums = [UNASSIGNED] * length + numb = lltype.malloc(NUMBERING, length) for i in range(length): box = boxes[i] value = values.get(box, None) @@ -191,9 +199,9 @@ class ResumeDataLoopMemo(object): tagged = tag(n, TAGBOX) n += 1 liveboxes[box] = tagged - nums[i] = tagged + numb.nums[i] = tagged # - numb = Numbering(numb1, nums) + numb.prev = numb1 self.numberings[snapshot] = numb, liveboxes, v return numb, liveboxes.copy(), v @@ -298,7 +306,7 @@ class ResumeDataVirtualAdder(object): # compute the numbering storage = self.storage # make sure that nobody attached resume data to this guard yet - assert storage.rd_numb is None + assert not storage.rd_numb snapshot = storage.rd_snapshot assert snapshot is not None # is that true? numb, liveboxes_from_env, v = self.memo.number(values, snapshot) @@ -724,34 +732,36 @@ class ResumeDataBoxReader(AbstractResumeDataReader): self.boxes_f = boxes_f self._prepare_next_section(info) - def consume_virtualizable_boxes(self, vinfo, nums): + def consume_virtualizable_boxes(self, vinfo, numb): # we have to ignore the initial part of 'nums' (containing vrefs), # find the virtualizable from nums[-1], and use it to know how many # boxes of which type we have to return. This does not write # anything into the virtualizable. - virtualizablebox = self.decode_ref(nums[-1]) + index = len(numb.nums) - 1 + virtualizablebox = self.decode_ref(numb.nums[index]) virtualizable = vinfo.unwrap_virtualizable_box(virtualizablebox) - return vinfo.load_list_of_boxes(virtualizable, self, nums) + return vinfo.load_list_of_boxes(virtualizable, self, numb) - def consume_virtualref_boxes(self, nums, end): + def consume_virtualref_boxes(self, numb, end): # Returns a list of boxes, assumed to be all BoxPtrs. # We leave up to the caller to call vrefinfo.continue_tracing(). assert (end & 1) == 0 - return [self.decode_ref(nums[i]) for i in range(end)] + return [self.decode_ref(numb.nums[i]) for i in range(end)] def consume_vref_and_vable_boxes(self, vinfo, ginfo): - nums = self.cur_numb.nums - self.cur_numb = self.cur_numb.prev + numb = self.cur_numb + self.cur_numb = numb.prev if vinfo is not None: - virtualizable_boxes = self.consume_virtualizable_boxes(vinfo, nums) - end = len(nums) - len(virtualizable_boxes) + virtualizable_boxes = self.consume_virtualizable_boxes(vinfo, numb) + end = len(numb.nums) - len(virtualizable_boxes) elif ginfo is not None: - virtualizable_boxes = [self.decode_ref(nums[-1])] - end = len(nums) - 1 + index = len(numb.nums) - 1 + virtualizable_boxes = [self.decode_ref(numb.nums[index])] + end = len(numb.nums) - 1 else: virtualizable_boxes = None - end = len(nums) - virtualref_boxes = self.consume_virtualref_boxes(nums, end) + end = len(numb.nums) + virtualref_boxes = self.consume_virtualref_boxes(numb, end) return virtualizable_boxes, virtualref_boxes def allocate_with_vtable(self, known_class): @@ -775,14 +785,16 @@ class ResumeDataBoxReader(AbstractResumeDataReader): strbox, ConstInt(index), charbox) def concat_strings(self, str1num, str2num): - calldescr, func = callinfo_for_oopspec(EffectInfo.OS_STR_CONCAT) + cic = self.metainterp.staticdata.callinfocollection + calldescr, func = cic.callinfo_for_oopspec(EffectInfo.OS_STR_CONCAT) str1box = self.decode_box(str1num, REF) str2box = self.decode_box(str2num, REF) return self.metainterp.execute_and_record_varargs( rop.CALL, [ConstInt(func), str1box, str2box], calldescr) def slice_string(self, strnum, startnum, lengthnum): - calldescr, func = callinfo_for_oopspec(EffectInfo.OS_STR_SLICE) + cic = self.metainterp.staticdata.callinfocollection + calldescr, func = cic.callinfo_for_oopspec(EffectInfo.OS_STR_SLICE) strbox = self.decode_box(strnum, REF) startbox = self.decode_box(startnum, INT) lengthbox = self.decode_box(lengthnum, INT) @@ -801,14 +813,16 @@ class ResumeDataBoxReader(AbstractResumeDataReader): strbox, ConstInt(index), charbox) def concat_unicodes(self, str1num, str2num): - calldescr, func = callinfo_for_oopspec(EffectInfo.OS_UNI_CONCAT) + cic = self.metainterp.staticdata.callinfocollection + calldescr, func = cic.callinfo_for_oopspec(EffectInfo.OS_UNI_CONCAT) str1box = self.decode_box(str1num, REF) str2box = self.decode_box(str2num, REF) return self.metainterp.execute_and_record_varargs( rop.CALL, [ConstInt(func), str1box, str2box], calldescr) def slice_unicode(self, strnum, startnum, lengthnum): - calldescr, func = callinfo_for_oopspec(EffectInfo.OS_UNI_SLICE) + cic = self.metainterp.staticdata.callinfocollection + calldescr, func = cic.callinfo_for_oopspec(EffectInfo.OS_UNI_SLICE) strbox = self.decode_box(strnum, REF) startbox = self.decode_box(startnum, INT) lengthbox = self.decode_box(lengthnum, INT) @@ -904,8 +918,8 @@ class ResumeDataBoxReader(AbstractResumeDataReader): def blackhole_from_resumedata(blackholeinterpbuilder, jitdriver_sd, storage, all_virtuals=None): - resumereader = ResumeDataDirectReader(blackholeinterpbuilder.cpu, storage, - all_virtuals) + resumereader = ResumeDataDirectReader(blackholeinterpbuilder.metainterp_sd, + storage, all_virtuals) vinfo = jitdriver_sd.virtualizable_info ginfo = jitdriver_sd.greenfield_info vrefinfo = blackholeinterpbuilder.metainterp_sd.virtualref_info @@ -940,7 +954,7 @@ def blackhole_from_resumedata(blackholeinterpbuilder, jitdriver_sd, storage, return firstbh def force_from_resumedata(metainterp_sd, storage, vinfo, ginfo): - resumereader = ResumeDataDirectReader(metainterp_sd.cpu, storage) + resumereader = ResumeDataDirectReader(metainterp_sd, storage) resumereader.handling_async_forcing() vrefinfo = metainterp_sd.virtualref_info resumereader.consume_vref_and_vable(vrefinfo, vinfo, ginfo) @@ -954,8 +968,9 @@ class ResumeDataDirectReader(AbstractResumeDataReader): # 1: in handle_async_forcing # 2: resuming from the GUARD_NOT_FORCED - def __init__(self, cpu, storage, all_virtuals=None): - self._init(cpu, storage) + def __init__(self, metainterp_sd, storage, all_virtuals=None): + self._init(metainterp_sd.cpu, storage) + self.callinfocollection = metainterp_sd.callinfocollection if all_virtuals is None: # common case self._prepare(storage) else: @@ -974,23 +989,24 @@ class ResumeDataDirectReader(AbstractResumeDataReader): info = blackholeinterp.get_current_position_info() self._prepare_next_section(info) - def consume_virtualref_info(self, vrefinfo, nums, end): + def consume_virtualref_info(self, vrefinfo, numb, end): # we have to decode a list of references containing pairs # [..., virtual, vref, ...] stopping at 'end' assert (end & 1) == 0 for i in range(0, end, 2): - virtual = self.decode_ref(nums[i]) - vref = self.decode_ref(nums[i+1]) + virtual = self.decode_ref(numb.nums[i]) + vref = self.decode_ref(numb.nums[i+1]) # For each pair, we store the virtual inside the vref. vrefinfo.continue_tracing(vref, virtual) - def consume_vable_info(self, vinfo, nums): + def consume_vable_info(self, vinfo, numb): # we have to ignore the initial part of 'nums' (containing vrefs), # find the virtualizable from nums[-1], load all other values # from the CPU stack, and copy them into the virtualizable if vinfo is None: - return len(nums) - virtualizable = self.decode_ref(nums[-1]) + return len(numb.nums) + index = len(numb.nums) - 1 + virtualizable = self.decode_ref(numb.nums[index]) virtualizable = vinfo.cast_gcref_to_vtype(virtualizable) if self.resume_after_guard_not_forced == 1: # in the middle of handle_async_forcing() @@ -1002,7 +1018,7 @@ class ResumeDataDirectReader(AbstractResumeDataReader): # is and stays 0. Note the call to reset_vable_token() in # warmstate.py. assert not virtualizable.vable_token - return vinfo.write_from_resume_data_partial(virtualizable, self, nums) + return vinfo.write_from_resume_data_partial(virtualizable, self, numb) def load_value_of_type(self, TYPE, tagged): from pypy.jit.metainterp.warmstate import specialize_value @@ -1019,12 +1035,12 @@ class ResumeDataDirectReader(AbstractResumeDataReader): load_value_of_type._annspecialcase_ = 'specialize:arg(1)' def consume_vref_and_vable(self, vrefinfo, vinfo, ginfo): - nums = self.cur_numb.nums - self.cur_numb = self.cur_numb.prev + numb = self.cur_numb + self.cur_numb = numb.prev if self.resume_after_guard_not_forced != 2: - end_vref = self.consume_vable_info(vinfo, nums) + end_vref = self.consume_vable_info(vinfo, numb) if ginfo is not None: end_vref -= 1 - self.consume_virtualref_info(vrefinfo, nums, end_vref) + self.consume_virtualref_info(vrefinfo, numb, end_vref) def allocate_with_vtable(self, known_class): from pypy.jit.metainterp.executor import exec_new_with_vtable @@ -1048,7 +1064,8 @@ class ResumeDataDirectReader(AbstractResumeDataReader): str2 = self.decode_ref(str2num) str1 = lltype.cast_opaque_ptr(lltype.Ptr(rstr.STR), str1) str2 = lltype.cast_opaque_ptr(lltype.Ptr(rstr.STR), str2) - funcptr = funcptr_for_oopspec(EffectInfo.OS_STR_CONCAT) + cic = self.callinfocollection + funcptr = cic.funcptr_for_oopspec(EffectInfo.OS_STR_CONCAT) result = funcptr(str1, str2) return lltype.cast_opaque_ptr(llmemory.GCREF, result) @@ -1057,7 +1074,8 @@ class ResumeDataDirectReader(AbstractResumeDataReader): start = self.decode_int(startnum) length = self.decode_int(lengthnum) str = lltype.cast_opaque_ptr(lltype.Ptr(rstr.STR), str) - funcptr = funcptr_for_oopspec(EffectInfo.OS_STR_SLICE) + cic = self.callinfocollection + funcptr = cic.funcptr_for_oopspec(EffectInfo.OS_STR_SLICE) result = funcptr(str, start, start + length) return lltype.cast_opaque_ptr(llmemory.GCREF, result) @@ -1073,7 +1091,8 @@ class ResumeDataDirectReader(AbstractResumeDataReader): str2 = self.decode_ref(str2num) str1 = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), str1) str2 = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), str2) - funcptr = funcptr_for_oopspec(EffectInfo.OS_UNI_CONCAT) + cic = self.callinfocollection + funcptr = cic.funcptr_for_oopspec(EffectInfo.OS_UNI_CONCAT) result = funcptr(str1, str2) return lltype.cast_opaque_ptr(llmemory.GCREF, result) @@ -1082,7 +1101,8 @@ class ResumeDataDirectReader(AbstractResumeDataReader): start = self.decode_int(startnum) length = self.decode_int(lengthnum) str = lltype.cast_opaque_ptr(lltype.Ptr(rstr.UNICODE), str) - funcptr = funcptr_for_oopspec(EffectInfo.OS_UNI_SLICE) + cic = self.callinfocollection + funcptr = cic.funcptr_for_oopspec(EffectInfo.OS_UNI_SLICE) result = funcptr(str, start, start + length) return lltype.cast_opaque_ptr(llmemory.GCREF, result) @@ -1173,8 +1193,9 @@ def dump_storage(storage, liveboxes): 'at', compute_unique_id(frameinfo)) frameinfo = frameinfo.prev numb = storage.rd_numb - while numb is not None: - debug_print('\tnumb', str([untag(i) for i in numb.nums]), + while numb: + debug_print('\tnumb', str([untag(numb.nums[i]) + for i in range(len(numb.nums))]), 'at', compute_unique_id(numb)) numb = numb.prev for const in storage.rd_consts: diff --git a/pypy/jit/metainterp/test/test_basic.py b/pypy/jit/metainterp/test/test_basic.py index 6b994f5d75..a96440a7bf 100644 --- a/pypy/jit/metainterp/test/test_basic.py +++ b/pypy/jit/metainterp/test/test_basic.py @@ -3,6 +3,7 @@ import sys from pypy.rlib.jit import JitDriver, we_are_jitted, hint, dont_look_inside from pypy.rlib.jit import OPTIMIZER_FULL, OPTIMIZER_SIMPLE, loop_invariant from pypy.rlib.jit import jit_debug, assert_green, AssertGreenFailed +from pypy.rlib.jit import unroll_safe, current_trace_length from pypy.jit.metainterp.warmspot import ll_meta_interp, get_stats from pypy.jit.backend.llgraph import runner from pypy.jit.metainterp import pyjitpl, history @@ -1837,6 +1838,31 @@ class BasicTests: 'int_add': 1, 'int_mul': 1, 'int_sub': 2, 'int_gt': 2, 'jump': 2}) + def test_current_trace_length(self): + myjitdriver = JitDriver(greens = ['g'], reds = ['x']) + @dont_look_inside + def residual(): + print "hi there" + @unroll_safe + def loop(g): + y = 0 + while y < g: + residual() + y += 1 + def f(x, g): + n = 0 + while x > 0: + myjitdriver.can_enter_jit(x=x, g=g) + myjitdriver.jit_merge_point(x=x, g=g) + loop(g) + x -= 1 + n = current_trace_length() + return n + res = self.meta_interp(f, [5, 8]) + assert 14 < res < 42 + res = self.meta_interp(f, [5, 2]) + assert 4 < res < 14 + class TestOOtype(BasicTests, OOJitMixin): diff --git a/pypy/jit/metainterp/test/test_compile.py b/pypy/jit/metainterp/test/test_compile.py index 98edd3f7e8..cddc61080a 100644 --- a/pypy/jit/metainterp/test/test_compile.py +++ b/pypy/jit/metainterp/test/test_compile.py @@ -85,7 +85,7 @@ def test_compile_new_loop(): metainterp.history.inputargs = loop.inputargs[:] # loop_tokens = [] - loop_token = compile_new_loop(metainterp, loop_tokens, [], 0) + loop_token = compile_new_loop(metainterp, loop_tokens, 0) assert loop_tokens == [loop_token] assert loop_token.number == 1 assert staticdata.globaldata.loopnumbering == 2 @@ -101,7 +101,7 @@ def test_compile_new_loop(): metainterp.history.operations = loop.operations[:] metainterp.history.inputargs = loop.inputargs[:] # - loop_token_2 = compile_new_loop(metainterp, loop_tokens, [], 0) + loop_token_2 = compile_new_loop(metainterp, loop_tokens, 0) assert loop_token_2 is loop_token assert loop_tokens == [loop_token] assert len(cpu.seen) == 0 diff --git a/pypy/jit/metainterp/test/test_del.py b/pypy/jit/metainterp/test/test_del.py index 8ce32f6535..5ea0dc817f 100644 --- a/pypy/jit/metainterp/test/test_del.py +++ b/pypy/jit/metainterp/test/test_del.py @@ -85,6 +85,7 @@ class TestLLtype(DelTests, LLJitMixin): def test_signal_action(self): from pypy.module.signal.interp_signal import SignalActionFlag action = SignalActionFlag() + action.has_bytecode_counter = True # myjitdriver = JitDriver(greens = [], reds = ['n', 'x']) class X: @@ -92,17 +93,17 @@ class TestLLtype(DelTests, LLJitMixin): # def f(n): x = X() - while n > 0: + action.reset_ticker(n) + while True: myjitdriver.can_enter_jit(n=n, x=x) myjitdriver.jit_merge_point(n=n, x=x) x.foo = n n -= 1 - if action.get() != 0: + if action.decrement_ticker(1) < 0: break - action.set(0) return 42 self.meta_interp(f, [20]) - self.check_loops(getfield_raw=1, call=0, call_pure=0) + self.check_loops(getfield_raw=1, setfield_raw=1, call=0, call_pure=0) class TestOOtype(DelTests, OOJitMixin): def setup_class(cls): diff --git a/pypy/jit/metainterp/test/test_logger.py b/pypy/jit/metainterp/test/test_logger.py index 24618f6a09..8a2eeaa51e 100644 --- a/pypy/jit/metainterp/test/test_logger.py +++ b/pypy/jit/metainterp/test/test_logger.py @@ -97,7 +97,7 @@ class TestLogger(object): def test_debug_merge_point(self): inp = ''' [] - debug_merge_point("info") + debug_merge_point("info", 0) ''' loop, oloop = self.reparse(inp, check_equal=False) assert loop.operations[0].getarg(0)._get_str() == 'info' diff --git a/pypy/jit/metainterp/test/test_optimizeopt.py b/pypy/jit/metainterp/test/test_optimizeopt.py index baf8e65777..74393e540d 100644 --- a/pypy/jit/metainterp/test/test_optimizeopt.py +++ b/pypy/jit/metainterp/test/test_optimizeopt.py @@ -26,6 +26,100 @@ class FakeMetaInterpStaticData(object): self.options = Fake() self.globaldata = Fake() +def test_store_final_boxes_in_guard(): + from pypy.jit.metainterp.compile import ResumeGuardDescr + from pypy.jit.metainterp.resume import tag, TAGBOX + b0 = BoxInt() + b1 = BoxInt() + opt = optimizeopt.Optimizer(FakeMetaInterpStaticData(LLtypeMixin.cpu), + None) + fdescr = ResumeGuardDescr(None) + op = ResOperation(rop.GUARD_TRUE, ['dummy'], None, descr=fdescr) + # setup rd data + fi0 = resume.FrameInfo(None, "code0", 11) + fdescr.rd_frame_info_list = resume.FrameInfo(fi0, "code1", 33) + snapshot0 = resume.Snapshot(None, [b0]) + fdescr.rd_snapshot = resume.Snapshot(snapshot0, [b1]) + # + opt.store_final_boxes_in_guard(op) + if op.getfailargs() == [b0, b1]: + assert list(fdescr.rd_numb.nums) == [tag(1, TAGBOX)] + assert list(fdescr.rd_numb.prev.nums) == [tag(0, TAGBOX)] + else: + assert op.getfailargs() == [b1, b0] + assert list(fdescr.rd_numb.nums) == [tag(0, TAGBOX)] + assert list(fdescr.rd_numb.prev.nums) == [tag(1, TAGBOX)] + assert fdescr.rd_virtuals is None + assert fdescr.rd_consts == [] + +def test_sharing_field_lists_of_virtual(): + class FakeOptimizer(object): + class cpu(object): + pass + opt = FakeOptimizer() + virt1 = virtualize.AbstractVirtualStructValue(opt, None) + lst1 = virt1._get_field_descr_list() + assert lst1 == [] + lst2 = virt1._get_field_descr_list() + assert lst1 is lst2 + virt1.setfield(LLtypeMixin.valuedescr, optimizeopt.OptValue(None)) + lst3 = virt1._get_field_descr_list() + assert lst3 == [LLtypeMixin.valuedescr] + lst4 = virt1._get_field_descr_list() + assert lst3 is lst4 + + virt2 = virtualize.AbstractVirtualStructValue(opt, None) + lst5 = virt2._get_field_descr_list() + assert lst5 is lst1 + virt2.setfield(LLtypeMixin.valuedescr, optimizeopt.OptValue(None)) + lst6 = virt1._get_field_descr_list() + assert lst6 is lst3 + +def test_reuse_vinfo(): + class FakeVInfo(object): + def set_content(self, fieldnums): + self.fieldnums = fieldnums + def equals(self, fieldnums): + return self.fieldnums == fieldnums + class FakeVirtualValue(virtualize.AbstractVirtualValue): + def _make_virtual(self, *args): + return FakeVInfo() + v1 = FakeVirtualValue(None, None, None) + vinfo1 = v1.make_virtual_info(None, [1, 2, 4]) + vinfo2 = v1.make_virtual_info(None, [1, 2, 4]) + assert vinfo1 is vinfo2 + vinfo3 = v1.make_virtual_info(None, [1, 2, 6]) + assert vinfo3 is not vinfo2 + vinfo4 = v1.make_virtual_info(None, [1, 2, 6]) + assert vinfo3 is vinfo4 + +def test_descrlist_dict(): + from pypy.jit.metainterp import optimizeutil + h1 = optimizeutil.descrlist_hash([]) + h2 = optimizeutil.descrlist_hash([LLtypeMixin.valuedescr]) + h3 = optimizeutil.descrlist_hash( + [LLtypeMixin.valuedescr, LLtypeMixin.nextdescr]) + assert h1 != h2 + assert h2 != h3 + assert optimizeutil.descrlist_eq([], []) + assert not optimizeutil.descrlist_eq([], [LLtypeMixin.valuedescr]) + assert optimizeutil.descrlist_eq([LLtypeMixin.valuedescr], + [LLtypeMixin.valuedescr]) + assert not optimizeutil.descrlist_eq([LLtypeMixin.valuedescr], + [LLtypeMixin.nextdescr]) + assert optimizeutil.descrlist_eq([LLtypeMixin.valuedescr, LLtypeMixin.nextdescr], + [LLtypeMixin.valuedescr, LLtypeMixin.nextdescr]) + assert not optimizeutil.descrlist_eq([LLtypeMixin.nextdescr, LLtypeMixin.valuedescr], + [LLtypeMixin.valuedescr, LLtypeMixin.nextdescr]) + + # descrlist_eq should compare by identity of the descrs, not by the result + # of sort_key + class FakeDescr(object): + def sort_key(self): + return 1 + + assert not optimizeutil.descrlist_eq([FakeDescr()], [FakeDescr()]) + # ____________________________________________________________ class Storage(compile.ResumeGuardDescr): "for tests." @@ -76,6 +170,8 @@ class BaseTestOptimizeOpt(BaseTest): metainterp_sd = FakeMetaInterpStaticData(self.cpu) if hasattr(self, 'vrefinfo'): metainterp_sd.virtualref_info = self.vrefinfo + if hasattr(self, 'callinfocollection'): + metainterp_sd.callinfocollection = self.callinfocollection optimize_loop_1(metainterp_sd, loop) # @@ -1430,7 +1526,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): ops = """ [p1] i1 = getfield_gc(p1, descr=valuedescr) - debug_merge_point(15) + debug_merge_point(15, 0) i2 = getfield_gc(p1, descr=valuedescr) escape(i1) escape(i2) @@ -1439,7 +1535,7 @@ class OptimizeOptTest(BaseTestOptimizeOpt): expected = """ [p1] i1 = getfield_gc(p1, descr=valuedescr) - debug_merge_point(15) + debug_merge_point(15, 0) escape(i1) escape(i1) jump(p1) @@ -4288,22 +4384,20 @@ class TestLLtype(OptimizeOptTest, LLtypeMixin): # ---------- def optimize_strunicode_loop_extradescrs(self, ops, optops, preamble=None): from pypy.jit.metainterp.optimizeopt import string - def my_callinfo_for_oopspec(oopspecindex): - calldescrtype = type(LLtypeMixin.strequaldescr) - for value in LLtypeMixin.__dict__.values(): - if isinstance(value, calldescrtype): - if (value.get_extra_info() and - value.get_extra_info().oopspecindex == oopspecindex): - # returns 0 for 'func' in this test - return value, 0 - raise AssertionError("not found: oopspecindex=%d" % oopspecindex) + class FakeCallInfoCollection: + def callinfo_for_oopspec(self, oopspecindex): + calldescrtype = type(LLtypeMixin.strequaldescr) + for value in LLtypeMixin.__dict__.values(): + if isinstance(value, calldescrtype): + extra = value.get_extra_info() + if extra and extra.oopspecindex == oopspecindex: + # returns 0 for 'func' in this test + return value, 0 + raise AssertionError("not found: oopspecindex=%d" % + oopspecindex) # - saved = string.callinfo_for_oopspec - try: - string.callinfo_for_oopspec = my_callinfo_for_oopspec - self.optimize_strunicode_loop(ops, optops, preamble) - finally: - string.callinfo_for_oopspec = saved + self.callinfocollection = FakeCallInfoCollection() + self.optimize_strunicode_loop(ops, optops, preamble) def test_str_equal_noop1(self): ops = """ diff --git a/pypy/jit/metainterp/test/test_resoperation.py b/pypy/jit/metainterp/test/test_resoperation.py index b8cebb8b7a..b64390a784 100644 --- a/pypy/jit/metainterp/test/test_resoperation.py +++ b/pypy/jit/metainterp/test/test_resoperation.py @@ -61,3 +61,10 @@ def test_instantiate(): assert op.getarglist() == ['a', 'b'] assert op.result == 'c' assert op.getdescr() is mydescr + +def test_can_malloc(): + mydescr = AbstractDescr() + assert rop.ResOperation(rop.rop.NEW, [], 'b').can_malloc() + call = rop.ResOperation(rop.rop.CALL, ['a', 'b'], 'c', descr=mydescr) + assert call.can_malloc() + assert not rop.ResOperation(rop.rop.INT_ADD, ['a', 'b'], 'c').can_malloc() diff --git a/pypy/jit/metainterp/test/test_resume.py b/pypy/jit/metainterp/test/test_resume.py index 3e0a8a8fcb..bd891d9ea6 100644 --- a/pypy/jit/metainterp/test/test_resume.py +++ b/pypy/jit/metainterp/test/test_resume.py @@ -52,6 +52,7 @@ def test_vinfo(): class MyMetaInterp: _already_allocated_resume_virtuals = None + callinfocollection = None def __init__(self, cpu=None): if cpu is None: @@ -142,6 +143,13 @@ def _next_section(reader, *expected): assert bh.written_f == expected_f +def Numbering(prev, nums): + numb = lltype.malloc(NUMBERING, len(nums)) + numb.prev = prev or lltype.nullptr(NUMBERING) + for i in range(len(nums)): + numb.nums[i] = nums[i] + return numb + def test_simple_read(): #b1, b2, b3 = [BoxInt(), BoxPtr(), BoxInt()] c1, c2, c3 = [ConstInt(111), ConstInt(222), ConstInt(333)] @@ -157,12 +165,12 @@ def test_simple_read(): storage.rd_numb = numb # cpu = MyCPU([42, gcref1, -66]) - reader = ResumeDataDirectReader(cpu, storage) + metainterp = MyMetaInterp(cpu) + reader = ResumeDataDirectReader(metainterp, storage) _next_section(reader, 42, 111, gcrefnull, 42, gcref1) _next_section(reader, 222, 333) _next_section(reader, 42, gcref1, -66) # - metainterp = MyMetaInterp(cpu) reader = ResumeDataBoxReader(storage, metainterp) bi, br, bf = [None]*3, [None]*2, [None]*0 info = MyBlackholeInterp([lltype.Signed, lltype.Signed, @@ -194,7 +202,7 @@ def test_simple_read_tagged_ints(): storage.rd_numb = numb # cpu = MyCPU([]) - reader = ResumeDataDirectReader(cpu, storage) + reader = ResumeDataDirectReader(MyMetaInterp(cpu), storage) _next_section(reader, 100) @@ -212,7 +220,7 @@ def test_prepare_virtuals(): class FakeMetainterp(object): _already_allocated_resume_virtuals = None cpu = None - reader = ResumeDataDirectReader(None, FakeStorage()) + reader = ResumeDataDirectReader(MyMetaInterp(None), FakeStorage()) assert reader.force_all_virtuals() == ["allocated", reader.virtual_default] # ____________________________________________________________ @@ -391,15 +399,15 @@ def test_FrameInfo_create(): assert fi1.pc == 3 def test_Numbering_create(): - l = [1, 2] + l = [rffi.r_short(1), rffi.r_short(2)] numb = Numbering(None, l) - assert numb.prev is None - assert numb.nums is l + assert not numb.prev + assert list(numb.nums) == l - l1 = ['b3'] + l1 = [rffi.r_short(3)] numb1 = Numbering(numb, l1) - assert numb1.prev is numb - assert numb1.nums is l1 + assert numb1.prev == numb + assert list(numb1.nums) == l1 def test_capture_resumedata(): b1, b2, b3 = [BoxInt(), BoxPtr(), BoxInt()] @@ -765,11 +773,12 @@ def test_ResumeDataLoopMemo_number(): assert liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX), b3: tag(2, TAGBOX)} - assert numb.nums == [tag(3, TAGINT), tag(2, TAGBOX), tag(0, TAGBOX), - tag(1, TAGINT)] - assert numb.prev.nums == [tag(0, TAGBOX), tag(1, TAGINT), tag(1, TAGBOX), - tag(0, TAGBOX), tag(2, TAGINT)] - assert numb.prev.prev is None + assert list(numb.nums) == [tag(3, TAGINT), tag(2, TAGBOX), tag(0, TAGBOX), + tag(1, TAGINT)] + assert list(numb.prev.nums) == [tag(0, TAGBOX), tag(1, TAGINT), + tag(1, TAGBOX), + tag(0, TAGBOX), tag(2, TAGINT)] + assert not numb.prev.prev numb2, liveboxes2, v = memo.number({}, snap2) assert v == 0 @@ -777,9 +786,9 @@ def test_ResumeDataLoopMemo_number(): assert liveboxes2 == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX), b3: tag(2, TAGBOX)} assert liveboxes2 is not liveboxes - assert numb2.nums == [tag(3, TAGINT), tag(2, TAGBOX), tag(0, TAGBOX), - tag(3, TAGINT)] - assert numb2.prev is numb.prev + assert list(numb2.nums) == [tag(3, TAGINT), tag(2, TAGBOX), tag(0, TAGBOX), + tag(3, TAGINT)] + assert numb2.prev == numb.prev env3 = [c3, b3, b1, c3] snap3 = Snapshot(snap, env3) @@ -800,9 +809,9 @@ def test_ResumeDataLoopMemo_number(): assert v == 0 assert liveboxes3 == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX)} - assert numb3.nums == [tag(3, TAGINT), tag(4, TAGINT), tag(0, TAGBOX), - tag(3, TAGINT)] - assert numb3.prev is numb.prev + assert list(numb3.nums) == [tag(3, TAGINT), tag(4, TAGINT), tag(0, TAGBOX), + tag(3, TAGINT)] + assert numb3.prev == numb.prev # virtual env4 = [c3, b4, b1, c3] @@ -813,9 +822,9 @@ def test_ResumeDataLoopMemo_number(): assert liveboxes4 == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX), b4: tag(0, TAGVIRTUAL)} - assert numb4.nums == [tag(3, TAGINT), tag(0, TAGVIRTUAL), tag(0, TAGBOX), - tag(3, TAGINT)] - assert numb4.prev is numb.prev + assert list(numb4.nums) == [tag(3, TAGINT), tag(0, TAGVIRTUAL), + tag(0, TAGBOX), tag(3, TAGINT)] + assert numb4.prev == numb.prev env5 = [b1, b4, b5] snap5 = Snapshot(snap4, env5) @@ -826,9 +835,9 @@ def test_ResumeDataLoopMemo_number(): assert liveboxes5 == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX), b4: tag(0, TAGVIRTUAL), b5: tag(1, TAGVIRTUAL)} - assert numb5.nums == [tag(0, TAGBOX), tag(0, TAGVIRTUAL), - tag(1, TAGVIRTUAL)] - assert numb5.prev is numb4 + assert list(numb5.nums) == [tag(0, TAGBOX), tag(0, TAGVIRTUAL), + tag(1, TAGVIRTUAL)] + assert numb5.prev == numb4 def test_ResumeDataLoopMemo_number_boxes(): memo = ResumeDataLoopMemo(FakeMetaInterpStaticData()) @@ -926,7 +935,7 @@ def test_virtual_adder_int_constants(): liveboxes = modifier.finish({}) assert storage.rd_snapshot is None cpu = MyCPU([]) - reader = ResumeDataDirectReader(cpu, storage) + reader = ResumeDataDirectReader(MyMetaInterp(cpu), storage) _next_section(reader, sys.maxint, 2**16, -65) _next_section(reader, 2, 3) _next_section(reader, sys.maxint, 1, sys.maxint, 2**16) diff --git a/pypy/jit/metainterp/test/test_virtualref.py b/pypy/jit/metainterp/test/test_virtualref.py index 097f9f99c9..e0e738207f 100644 --- a/pypy/jit/metainterp/test/test_virtualref.py +++ b/pypy/jit/metainterp/test/test_virtualref.py @@ -88,7 +88,11 @@ class VRefTests: cpu.get_latest_value_int = lambda i:guard_op.getfailargs()[i].getint() cpu.get_latest_value_ref = lambda i:guard_op.getfailargs()[i].getref_base() cpu.clear_latest_values = lambda count: None - resumereader = ResumeDataDirectReader(cpu, guard_op.getdescr()) + class FakeMetaInterpSd: + callinfocollection = None + FakeMetaInterpSd.cpu = cpu + resumereader = ResumeDataDirectReader(FakeMetaInterpSd(), + guard_op.getdescr()) vrefinfo = self.metainterp.staticdata.virtualref_info lst = [] vrefinfo.continue_tracing = lambda vref, virtual: \ diff --git a/pypy/jit/metainterp/test/test_ztranslation.py b/pypy/jit/metainterp/test/test_ztranslation.py index 3987544b19..026e4d5dee 100644 --- a/pypy/jit/metainterp/test/test_ztranslation.py +++ b/pypy/jit/metainterp/test/test_ztranslation.py @@ -79,7 +79,7 @@ class TranslationTest: res = ll_meta_interp(main, [40, 5], CPUClass=self.CPUClass, type_system=self.type_system) assert res == main(40, 5) - res = rpython_ll_meta_interp(main, [40, 5], loops=2, + res = rpython_ll_meta_interp(main, [40, 5], CPUClass=self.CPUClass, type_system=self.type_system, optimizer=OPTIMIZER_FULL, @@ -120,7 +120,7 @@ class TranslationTest: res = ll_meta_interp(main, [40], CPUClass=self.CPUClass, type_system=self.type_system) assert res == main(40) - res = rpython_ll_meta_interp(main, [40], loops=2, CPUClass=self.CPUClass, + res = rpython_ll_meta_interp(main, [40], CPUClass=self.CPUClass, type_system=self.type_system, optimizer=OPTIMIZER_FULL, ProfilerClass=Profiler) diff --git a/pypy/jit/metainterp/virtualizable.py b/pypy/jit/metainterp/virtualizable.py index c6071dab9f..baa54fdb3d 100644 --- a/pypy/jit/metainterp/virtualizable.py +++ b/pypy/jit/metainterp/virtualizable.py @@ -100,48 +100,48 @@ class VirtualizableInfo: i = i + 1 assert len(boxes) == i + 1 # - def write_from_resume_data_partial(virtualizable, reader, nums): + def write_from_resume_data_partial(virtualizable, reader, numb): # Load values from the reader (see resume.py) described by # the list of numbers 'nums', and write them in their proper # place in the 'virtualizable'. This works from the end of # the list and returns the index in 'nums' of the start of # the virtualizable data found, allowing the caller to do # further processing with the start of the list. - i = len(nums) - 1 + i = len(numb.nums) - 1 assert i >= 0 for ARRAYITEMTYPE, fieldname in unroll_array_fields_rev: lst = getattr(virtualizable, fieldname) for j in range(getlength(lst)-1, -1, -1): i -= 1 assert i >= 0 - x = reader.load_value_of_type(ARRAYITEMTYPE, nums[i]) + x = reader.load_value_of_type(ARRAYITEMTYPE, numb.nums[i]) setarrayitem(lst, j, x) for FIELDTYPE, fieldname in unroll_static_fields_rev: i -= 1 assert i >= 0 - x = reader.load_value_of_type(FIELDTYPE, nums[i]) + x = reader.load_value_of_type(FIELDTYPE, numb.nums[i]) setattr(virtualizable, fieldname, x) return i # - def load_list_of_boxes(virtualizable, reader, nums): + def load_list_of_boxes(virtualizable, reader, numb): # Uses 'virtualizable' only to know the length of the arrays; # does not write anything into it. The returned list is in # the format expected of virtualizable_boxes, so it ends in # the virtualizable itself. - i = len(nums) - 1 + i = len(numb.nums) - 1 assert i >= 0 - boxes = [reader.decode_box_of_type(self.VTYPEPTR, nums[i])] + boxes = [reader.decode_box_of_type(self.VTYPEPTR, numb.nums[i])] for ARRAYITEMTYPE, fieldname in unroll_array_fields_rev: lst = getattr(virtualizable, fieldname) for j in range(getlength(lst)-1, -1, -1): i -= 1 assert i >= 0 - box = reader.decode_box_of_type(ARRAYITEMTYPE, nums[i]) + box = reader.decode_box_of_type(ARRAYITEMTYPE,numb.nums[i]) boxes.append(box) for FIELDTYPE, fieldname in unroll_static_fields_rev: i -= 1 assert i >= 0 - box = reader.decode_box_of_type(FIELDTYPE, nums[i]) + box = reader.decode_box_of_type(FIELDTYPE, numb.nums[i]) boxes.append(box) boxes.reverse() return boxes diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py index 819fa79957..a5d36ee7b8 100644 --- a/pypy/jit/metainterp/warmspot.py +++ b/pypy/jit/metainterp/warmspot.py @@ -98,8 +98,7 @@ def jittify_and_run(interp, graph, args, repeat=1, repeat -= 1 return res -def rpython_ll_meta_interp(function, args, backendopt=True, - loops='not used right now', **kwds): +def rpython_ll_meta_interp(function, args, backendopt=True, **kwds): return ll_meta_interp(function, args, backendopt=backendopt, translate_support_code=True, **kwds) diff --git a/pypy/jit/tool/loopcounter.py b/pypy/jit/tool/loopcounter.py new file mode 100644 index 0000000000..978056f797 --- /dev/null +++ b/pypy/jit/tool/loopcounter.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +""" +Parse and display the traces produced by pypy-c-jit when PYPYLOG is set. +""" + +import autopath +import py +import sys +import optparse + +def get_timestamp(line): + import re + match = re.match(r'\[([0-9a-f]*)\] .*', line) + return int(match.group(1), 16) + +def main(logfile, options): + log = open(logfile) + loops = 0 + bridges = 0 + time0 = None + print 'timestamp,total,loops,bridges' + for line in log: + if time0 is None and line.startswith('['): + time0 = get_timestamp(line) + if '{jit-log-opt-' in line: + time_now = get_timestamp(line) + if '{jit-log-opt-loop' in line: + loops += 1 + elif '{jit-log-opt-bridge' in line: + bridges += 1 + else: + assert False, 'unknown category %s' % line + total = loops+bridges + timestamp = time_now - time0 + print '%d,%d,%d,%d' % (timestamp, total, loops, bridges) + +if __name__ == '__main__': + parser = optparse.OptionParser(usage="%prog loopfile [options]") + options, args = parser.parse_args() + if len(args) != 1: + parser.print_help() + sys.exit(2) + + main(args[0], options) diff --git a/pypy/jit/tool/oparser.py b/pypy/jit/tool/oparser.py index 6d568bb195..9ac74a1617 100644 --- a/pypy/jit/tool/oparser.py +++ b/pypy/jit/tool/oparser.py @@ -192,7 +192,7 @@ class OpParser(object): descr = None if argspec.strip(): if opname == 'debug_merge_point': - allargs = [argspec] + allargs = argspec.rsplit(', ', 1) else: allargs = [arg for arg in argspec.split(",") if arg != ''] diff --git a/pypy/jit/tool/pypytrace-mode.el b/pypy/jit/tool/pypytrace-mode.el index 77207b6d2c..6136a8331d 100644 --- a/pypy/jit/tool/pypytrace-mode.el +++ b/pypy/jit/tool/pypytrace-mode.el @@ -26,7 +26,7 @@ ("<.*FieldDescr \\([^ ]*\\)" (1 'font-lock-variable-name-face)) ;; comment out debug_merge_point, but then highlight specific part of it ("^debug_merge_point.*" . font-lock-comment-face) - ("^\\(debug_merge_point\\).*code object\\(.*\\), file \\('.*'\\), \\(line .*\\)> \\(.*\\)')" + ("^\\(debug_merge_point\\).*code object\\(.*\\), file \\('.*'\\), \\(line .*\\)> \\(.*\\)" (1 'compilation-warning t) (2 'escape-glyph t) (3 'font-lock-string-face t) diff --git a/pypy/jit/tool/pypytrace.vim b/pypy/jit/tool/pypytrace.vim index be299495e9..42f9cd3994 100644 --- a/pypy/jit/tool/pypytrace.vim +++ b/pypy/jit/tool/pypytrace.vim @@ -21,10 +21,10 @@ syn match pypyDebugMergePoint '^debug_merge_point(.\+)' hi def link pypyLoopStart Structure "hi def link pypyLoopArgs PreProc -hi def link pypyFailArgs String +hi def link pypyFailArgs Special "hi def link pypyOpName Statement -hi def link pypyDebugMergePoint Comment +hi def link pypyDebugMergePoint String hi def link pypyConstPtr Constant hi def link pypyNumber Number -hi def link pypyDescr String +hi def link pypyDescr PreProc hi def link pypyDescrField Label diff --git a/pypy/jit/metainterp/test/test_oparser.py b/pypy/jit/tool/test/test_oparser.py index ef06d4a439..a5a03314e3 100644 --- a/pypy/jit/metainterp/test/test_oparser.py +++ b/pypy/jit/tool/test/test_oparser.py @@ -141,10 +141,10 @@ def test_floats(): def test_debug_merge_point(): x = ''' [] - debug_merge_point("info") - debug_merge_point('info') - debug_merge_point('<some ('other,')> info') - debug_merge_point('(stuff) #1') + debug_merge_point("info", 0) + debug_merge_point('info', 1) + debug_merge_point('<some ('other,')> info', 1) + debug_merge_point('(stuff) #1', 1) ''' loop = parse(x) assert loop.operations[0].getarg(0)._get_str() == 'info' @@ -168,7 +168,7 @@ i4 = int_add(i0, 2) i6 = int_sub(i1, 1) i8 = int_gt(i6, 3) guard_true(i8, descr=<Guard15>) [i4, i6] -debug_merge_point('(no jitdriver.get_printable_location!)') +debug_merge_point('(no jitdriver.get_printable_location!)', 0) jump(i6, i4, descr=<Loop0>) ''' diff --git a/pypy/jit/tool/test/test_traceviewer.py b/pypy/jit/tool/test/test_traceviewer.py index b3f3a3cde1..c505c8a576 100644 --- a/pypy/jit/tool/test/test_traceviewer.py +++ b/pypy/jit/tool/test/test_traceviewer.py @@ -19,7 +19,7 @@ class TestSplitLoops(object): def test_no_of_loops(self): data = [preparse(""" # Loop 0 : loop with 39 ops - debug_merge_point('') + debug_merge_point('', 0) guard_class(p4, 141310752, descr=<Guard5>) [p0, p1] p60 = getfield_gc(p4, descr=<GcPtrFieldDescr 16>) guard_nonnull(p60, descr=<Guard6>) [p0, p1] @@ -51,7 +51,7 @@ class TestSplitLoops(object): assert loop.right.content == 'extra' def test_postparse(self): - real_loops = [FinalBlock("debug_merge_point('<code object _runCallbacks, file '/tmp/x/twisted-trunk/twisted/internet/defer.py', line 357> #40 POP_TOP')", None)] + real_loops = [FinalBlock("debug_merge_point('<code object _runCallbacks, file '/tmp/x/twisted-trunk/twisted/internet/defer.py', line 357> #40 POP_TOP', 0)", None)] postprocess(real_loops, real_loops[:], {}) assert real_loops[0].header.startswith("_runCallbacks, file '/tmp/x/twisted-trunk/twisted/internet/defer.py', line 357") |