Author: Philip Jenvey <pjen...@underboss.org> Branch: py3k Changeset: r70135:706e3a4c1ffa Date: 2014-03-20 15:41 -0700 http://bitbucket.org/pypy/pypy/changeset/706e3a4c1ffa/
Log: merge default diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst --- a/pypy/doc/cpython_differences.rst +++ b/pypy/doc/cpython_differences.rst @@ -292,6 +292,10 @@ depending on the compiler settings, the default of 768KB is enough for about 1400 calls. +* since the implementation of dictionary is different, the exact number + which ``__hash__`` and ``__eq__`` are called is different. Since CPython + does not give any specific guarantees either, don't rely on it. + * assignment to ``__class__`` is limited to the cases where it works on CPython 2.5. On CPython 2.6 and 2.7 it works in a bit more cases, which are not supported by PyPy so far. (If needed, diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -114,3 +114,6 @@ app-level. The `Buffer` class is now used by `W_MemoryView` and `W_Buffer`, which is not present in Python 3. Previously `W_Buffer` was an alias to `Buffer`, which was wrappable itself. + +.. branch: improve-consecutive-dict-lookups +Improve the situation when dict lookups of the same key are performed in a chain diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -441,11 +441,10 @@ return name - def getbuiltinmodule(self, name, force_init=False, reuse=True): + def getbuiltinmodule(self, name, force_init=False): w_name = self.wrap(name) w_modules = self.sys.get('modules') if not force_init: - assert reuse is True try: return self.getitem(w_modules, w_name) except OperationError, e: @@ -464,15 +463,7 @@ # Initialize the module from pypy.interpreter.module import Module if isinstance(w_mod, Module): - if not reuse and w_mod.startup_called: - # Create a copy of the module - w_mod.getdict(self) # unlazy w_initialdict - w_new = self.wrap(Module(self, w_name)) - self.call_method(w_new.getdict(self), 'update', - w_mod.w_initialdict) - w_mod = w_new - else: - w_mod.init(self) + w_mod.init(self) # Add the module to sys.modules self.setitem(w_modules, w_name, w_mod) diff --git a/pypy/module/imp/importing.py b/pypy/module/imp/importing.py --- a/pypy/module/imp/importing.py +++ b/pypy/module/imp/importing.py @@ -585,8 +585,7 @@ return space.call_method(find_info.w_loader, "load_module", w_modulename) if find_info.modtype == C_BUILTIN: - return space.getbuiltinmodule(find_info.filename, force_init=True, - reuse=reuse) + return space.getbuiltinmodule(find_info.filename, force_init=True) if find_info.modtype in (PY_SOURCE, PY_COMPILED, C_EXTENSION, PKG_DIRECTORY): w_mod = None diff --git a/pypy/module/imp/test/test_app.py b/pypy/module/imp/test/test_app.py --- a/pypy/module/imp/test/test_app.py +++ b/pypy/module/imp/test/test_app.py @@ -219,6 +219,7 @@ def test_builtin_reimport(self): # from https://bugs.pypy.org/issue1514 + skip("fix me") import sys, marshal old = marshal.loads @@ -238,6 +239,7 @@ # taken from https://bugs.pypy.org/issue1514, with extra cases # that show a difference with CPython: we can get on CPython # several module objects for the same built-in module :-( + skip("several built-in module objects: not supported by pypy") import sys, marshal old = marshal.loads diff --git a/pypy/module/imp/test/test_import.py b/pypy/module/imp/test/test_import.py --- a/pypy/module/imp/test/test_import.py +++ b/pypy/module/imp/test/test_import.py @@ -652,6 +652,7 @@ assert hasattr(time, 'clock') def test_reimport_builtin_simple_case_2(self): + skip("fix me") import sys, time time.foo = "bar" del sys.modules['time'] @@ -660,6 +661,7 @@ def test_reimport_builtin(self): import imp, sys, time + skip("fix me") oldpath = sys.path time.tzset = "<test_reimport_builtin removed this>" diff --git a/rpython/jit/backend/arm/opassembler.py b/rpython/jit/backend/arm/opassembler.py --- a/rpython/jit/backend/arm/opassembler.py +++ b/rpython/jit/backend/arm/opassembler.py @@ -583,6 +583,10 @@ emit_op_getfield_raw_pure = emit_op_getfield_gc emit_op_getfield_gc_pure = emit_op_getfield_gc + def emit_op_increment_debug_counter(self, op, arglocs, regalloc, fcond): + # XXX implement me + return fcond + def emit_op_getinteriorfield_gc(self, op, arglocs, regalloc, fcond): (base_loc, index_loc, res_loc, ofs_loc, ofs, itemsize, fieldsize) = arglocs diff --git a/rpython/jit/backend/arm/regalloc.py b/rpython/jit/backend/arm/regalloc.py --- a/rpython/jit/backend/arm/regalloc.py +++ b/rpython/jit/backend/arm/regalloc.py @@ -849,6 +849,10 @@ prepare_op_getfield_raw_pure = prepare_op_getfield_gc prepare_op_getfield_gc_pure = prepare_op_getfield_gc + def prepare_op_increment_debug_counter(self, op, fcond): + # XXX implement me + return [] + def prepare_op_getinteriorfield_gc(self, op, fcond): t = unpack_interiorfielddescr(op.getdescr()) ofs, itemsize, fieldsize, sign = t diff --git a/rpython/jit/backend/llgraph/runner.py b/rpython/jit/backend/llgraph/runner.py --- a/rpython/jit/backend/llgraph/runner.py +++ b/rpython/jit/backend/llgraph/runner.py @@ -553,6 +553,10 @@ else: return self.bh_raw_load_i(struct, offset, descr) + def bh_increment_debug_counter(self, addr): + p = rffi.cast(rffi.CArrayPtr(lltype.Signed), addr) + p[0] += 1 + def unpack_arraydescr_size(self, arraydescr): from rpython.jit.backend.llsupport.symbolic import get_array_token from rpython.jit.backend.llsupport.descr import get_type_flag, FLAG_SIGNED diff --git a/rpython/jit/backend/llsupport/assembler.py b/rpython/jit/backend/llsupport/assembler.py --- a/rpython/jit/backend/llsupport/assembler.py +++ b/rpython/jit/backend/llsupport/assembler.py @@ -15,7 +15,7 @@ DEBUG_COUNTER = lltype.Struct('DEBUG_COUNTER', # 'b'ridge, 'l'abel or # 'e'ntry point - ('i', lltype.Signed), + ('i', lltype.Signed), # first field, at offset 0 ('type', lltype.Char), ('number', lltype.Signed) ) @@ -64,7 +64,6 @@ self.cpu = cpu self.memcpy_addr = 0 self.rtyper = cpu.rtyper - self.debug_counter_descr = cpu.fielddescrof(DEBUG_COUNTER, 'i') self._debug = False def setup_once(self): @@ -265,14 +264,8 @@ def _append_debugging_code(self, operations, tp, number, token): counter = self._register_counter(tp, number, token) c_adr = ConstInt(rffi.cast(lltype.Signed, counter)) - box = BoxInt() - box2 = BoxInt() - ops = [ResOperation(rop.GETFIELD_RAW, [c_adr], - box, descr=self.debug_counter_descr), - ResOperation(rop.INT_ADD, [box, ConstInt(1)], box2), - ResOperation(rop.SETFIELD_RAW, [c_adr, box2], - None, descr=self.debug_counter_descr)] - operations.extend(ops) + operations.append( + ResOperation(rop.INCREMENT_DEBUG_COUNTER, [c_adr], None)) def _register_counter(self, tp, number, token): # YYY very minor leak -- we need the counters to stay alive diff --git a/rpython/jit/backend/test/runner_test.py b/rpython/jit/backend/test/runner_test.py --- a/rpython/jit/backend/test/runner_test.py +++ b/rpython/jit/backend/test/runner_test.py @@ -3736,7 +3736,7 @@ assert False, 'should not be called' from rpython.jit.codewriter.effectinfo import EffectInfo - effectinfo = EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE, EffectInfo.OS_MATH_SQRT) + effectinfo = EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE, EffectInfo.OS_MATH_SQRT) FPTR = self.Ptr(self.FuncType([lltype.Float], lltype.Float)) func_ptr = llhelper(FPTR, math_sqrt) FUNC = deref(FPTR) @@ -4338,3 +4338,12 @@ assert rffi.cast(lltype.Signed, a[0]) == -7654 assert rffi.cast(lltype.Signed, a[1]) == 777 lltype.free(a, flavor='raw') + + def test_increment_debug_counter(self): + foo = lltype.malloc(rffi.CArray(lltype.Signed), 1, flavor='raw') + foo[0] = 1789200 + self.execute_operation(rop.INCREMENT_DEBUG_COUNTER, + [ConstInt(rffi.cast(lltype.Signed, foo))], + 'void') + assert foo[0] == 1789201 + lltype.free(foo, flavor='raw') diff --git a/rpython/jit/backend/x86/assembler.py b/rpython/jit/backend/x86/assembler.py --- a/rpython/jit/backend/x86/assembler.py +++ b/rpython/jit/backend/x86/assembler.py @@ -287,7 +287,6 @@ cast_instance_to_gcref(self.cpu.propagate_exception_descr)) ofs = self.cpu.get_ofs_of_frame_field('jf_descr') self.mc.MOV(RawEbpLoc(ofs), imm(propagate_exception_descr)) - self.mc.MOV_rr(eax.value, ebp.value) # self._call_footer() rawstart = self.mc.materialize(self.cpu.asmmemmgr, []) @@ -435,8 +434,8 @@ self.wb_slowpath[withcards + 2 * withfloats] = rawstart @rgc.no_release_gil - def assemble_loop(self, logger, loopname, inputargs, operations, looptoken, - log): + def assemble_loop(self, inputargs, operations, looptoken, log, + loopname, logger): '''adds the following attributes to looptoken: _ll_function_addr (address of the generated func, as an int) _ll_loop_code (debug: addr of the start of the ResOps) @@ -515,8 +514,8 @@ size_excluding_failure_stuff - looppos) @rgc.no_release_gil - def assemble_bridge(self, logger, faildescr, inputargs, operations, - original_loop_token, log): + def assemble_bridge(self, faildescr, inputargs, operations, + original_loop_token, log, logger): if not we_are_translated(): # Arguments should be unique assert len(set(inputargs)) == len(inputargs) @@ -761,6 +760,9 @@ # def _call_footer(self): + # the return value is the jitframe + self.mc.MOV_rr(eax.value, ebp.value) + gcrootmap = self.cpu.gc_ll_descr.gcrootmap if gcrootmap and gcrootmap.is_shadow_stack: self._call_footer_shadowstack(gcrootmap) @@ -1467,6 +1469,14 @@ ofs_loc) self.load_from_mem(resloc, src_addr, fieldsize_loc, sign_loc) + def genop_discard_increment_debug_counter(self, op, arglocs): + # The argument should be an immediate address. This should + # generate code equivalent to a GETFIELD_RAW, an ADD(1), and a + # SETFIELD_RAW. Here we use the direct from-memory-to-memory + # increment operation of x86. + base_loc, = arglocs + self.mc.INC(mem(base_loc, 0)) + def genop_discard_setfield_gc(self, op, arglocs): base_loc, ofs_loc, size_loc, value_loc = arglocs assert isinstance(size_loc, ImmedLoc) @@ -1823,12 +1833,8 @@ # did just above. ofs = self.cpu.get_ofs_of_frame_field('jf_descr') ofs2 = self.cpu.get_ofs_of_frame_field('jf_gcmap') - mc.POP(eax) - mc.MOV_br(ofs2, eax.value) - mc.POP(eax) - mc.MOV_br(ofs, eax.value) - # the return value is the jitframe - mc.MOV_rr(eax.value, ebp.value) + mc.POP_b(ofs2) + mc.POP_b(ofs) self._call_footer() rawstart = mc.materialize(self.cpu.asmmemmgr, []) @@ -1861,7 +1867,6 @@ # keep that one and kill all the others ofs = self.cpu.get_ofs_of_frame_field('jf_gcmap') self.mc.MOV_bi(ofs, 0) - self.mc.MOV_rr(eax.value, ebp.value) # exit function self._call_footer() diff --git a/rpython/jit/backend/x86/regalloc.py b/rpython/jit/backend/x86/regalloc.py --- a/rpython/jit/backend/x86/regalloc.py +++ b/rpython/jit/backend/x86/regalloc.py @@ -1003,6 +1003,10 @@ consider_getfield_raw_pure = consider_getfield_gc consider_getfield_gc_pure = consider_getfield_gc + def consider_increment_debug_counter(self, op): + base_loc = self.loc(op.getarg(0)) + self.perform_discard(op, [base_loc]) + def consider_getarrayitem_gc(self, op): itemsize, ofs, sign = unpack_arraydescr(op.getdescr()) args = op.getarglist() diff --git a/rpython/jit/backend/x86/regloc.py b/rpython/jit/backend/x86/regloc.py --- a/rpython/jit/backend/x86/regloc.py +++ b/rpython/jit/backend/x86/regloc.py @@ -488,12 +488,22 @@ for possible_code in unrolling_location_codes: if code == possible_code: val = getattr(loc, "value_" + possible_code)() - if self.WORD == 8 and possible_code == 'i' and not rx86.fits_in_32bits(val): - self._load_scratch(val) + # Faking out of certain operations for x86_64 + fits32 = rx86.fits_in_32bits + if possible_code == 'i' and not fits32(val): + self._load_scratch(val) # for 'PUSH(imm)' _rx86_getattr(self, name + "_r")(X86_64_SCRATCH_REG.value) - else: - methname = name + "_" + possible_code - _rx86_getattr(self, methname)(val) + return + if possible_code == 'j' and not fits32(val): + val = self._addr_as_reg_offset(val) + _rx86_getattr(self, name + "_m")(val) + return + if possible_code == 'm' and not fits32(val[1]): + val = self._fix_static_offset_64_m(val) + if possible_code == 'a' and not fits32(val[3]): + val = self._fix_static_offset_64_a(val) + methname = name + "_" + possible_code + _rx86_getattr(self, methname)(val) return func_with_new_name(INSN, "INSN_" + name) @@ -600,6 +610,7 @@ TEST8 = _binaryop('TEST8') BTS = _binaryop('BTS') + INC = _unaryop('INC') ADD = _binaryop('ADD') SUB = _binaryop('SUB') IMUL = _binaryop('IMUL') diff --git a/rpython/jit/backend/x86/runner.py b/rpython/jit/backend/x86/runner.py --- a/rpython/jit/backend/x86/runner.py +++ b/rpython/jit/backend/x86/runner.py @@ -93,16 +93,15 @@ def compile_loop(self, inputargs, operations, looptoken, log=True, name='', logger=None): - return self.assembler.assemble_loop(logger, name, inputargs, operations, - looptoken, log=log) + return self.assembler.assemble_loop(inputargs, operations, looptoken, log, + name, logger) def compile_bridge(self, faildescr, inputargs, operations, original_loop_token, log=True, logger=None): clt = original_loop_token.compiled_loop_token clt.compiling_a_bridge() - return self.assembler.assemble_bridge(logger, faildescr, inputargs, - operations, - original_loop_token, log=log) + return self.assembler.assemble_bridge(faildescr, inputargs, operations, + original_loop_token, log, logger) def clear_latest_values(self, count): setitem = self.assembler.fail_boxes_ptr.setitem diff --git a/rpython/jit/backend/x86/rx86.py b/rpython/jit/backend/x86/rx86.py --- a/rpython/jit/backend/x86/rx86.py +++ b/rpython/jit/backend/x86/rx86.py @@ -470,6 +470,9 @@ # ------------------------------ Arithmetic ------------------------------ + INC_m = insn(rex_w, '\xFF', orbyte(0), mem_reg_plus_const(1)) + INC_j = insn(rex_w, '\xFF', orbyte(0), abs_(1)) + ADD_ri,ADD_rr,ADD_rb,_,_,ADD_rm,ADD_rj,_,_ = common_modes(0) OR_ri, OR_rr, OR_rb, _,_,OR_rm, OR_rj, _,_ = common_modes(1) AND_ri,AND_rr,AND_rb,_,_,AND_rm,AND_rj,_,_ = common_modes(4) diff --git a/rpython/jit/backend/x86/test/test_assembler.py b/rpython/jit/backend/x86/test/test_assembler.py --- a/rpython/jit/backend/x86/test/test_assembler.py +++ b/rpython/jit/backend/x86/test/test_assembler.py @@ -55,9 +55,7 @@ asm = cpu.assembler asm.setup_once() asm.setup(looptoken) - self.fm = X86FrameManager(0) - self.xrm = X86XMMRegisterManager(None, frame_manager=self.fm, - assembler=asm) + self.xrm = X86XMMRegisterManager(None, assembler=asm) callback(asm) asm.mc.RET() rawstart = asm.materialize_loop(looptoken) @@ -75,29 +73,6 @@ res = self.do_test(callback) assert res == 42 - def test_push_stack(self): - def callback(asm): - loc = self.fm.frame_pos(5, INT) - asm.mc.SUB_ri(esp.value, 64) - asm.mov(imm(42), loc) - asm.regalloc_push(loc) - asm.regalloc_pop(eax) - asm.mc.ADD_ri(esp.value, 64) - res = self.do_test(callback) - assert res == 42 - - def test_pop_stack(self): - def callback(asm): - loc = self.fm.frame_pos(5, INT) - asm.mc.SUB_ri(esp.value, 64) - asm.mov(imm(42), edx) - asm.regalloc_push(edx) - asm.regalloc_pop(loc) - asm.mov(loc, eax) - asm.mc.ADD_ri(esp.value, 64) - res = self.do_test(callback) - assert res == 42 - def test_simple_xmm(self): def callback(asm): c = ConstFloat(longlong.getfloatstorage(-42.5)) @@ -109,32 +84,8 @@ res = self.do_test(callback) assert res == -42 - def test_push_stack_xmm(self): + def test_xmm_pushes_8_bytes(self): def callback(asm): - c = ConstFloat(longlong.getfloatstorage(-42.5)) - loc = self.xrm.convert_to_imm(c) - loc2 = self.fm.frame_pos(4, FLOAT) - asm.mc.SUB_ri(esp.value, 64) - asm.mov(loc, xmm5) - asm.mov(xmm5, loc2) - asm.regalloc_push(loc2) - asm.regalloc_pop(xmm0) - asm.mc.ADD_ri(esp.value, 64) - asm.mc.CVTTSD2SI(eax, xmm0) - res = self.do_test(callback) - assert res == -42 - - def test_pop_stack_xmm(self): - def callback(asm): - c = ConstFloat(longlong.getfloatstorage(-42.5)) - loc = self.xrm.convert_to_imm(c) - loc2 = self.fm.frame_pos(4, FLOAT) - asm.mc.SUB_ri(esp.value, 64) - asm.mov(loc, xmm5) asm.regalloc_push(xmm5) - asm.regalloc_pop(loc2) - asm.mov(loc2, xmm0) - asm.mc.ADD_ri(esp.value, 64) - asm.mc.CVTTSD2SI(eax, xmm0) - res = self.do_test(callback) - assert res == -42 + asm.mc.ADD(esp, imm(8)) + self.do_test(callback) diff --git a/rpython/jit/backend/x86/test/test_regloc.py b/rpython/jit/backend/x86/test/test_regloc.py --- a/rpython/jit/backend/x86/test/test_regloc.py +++ b/rpython/jit/backend/x86/test/test_regloc.py @@ -373,3 +373,56 @@ '\x59' ) assert cb.getvalue() == expected_instructions + + # ------------------------------------------------------------ + + def test_push_immed64(self): + immed = 0x0123456789ABCDEF + cb = LocationCodeBuilder64() + cb.PUSH(imm(immed)) + # + expected_instructions = ( + # mov r11, 0x0123456789ABCDEF + '\x49\xBB\xEF\xCD\xAB\x89\x67\x45\x23\x01' + # push r11 + '\x41\x53' + ) + assert cb.getvalue() == expected_instructions + + def test_inc_64bit_address_1(self): + base_addr = 0x0123456789ABCDEF + cb = LocationCodeBuilder64() + cb.INC(AddressLoc(ImmedLoc(0), ImmedLoc(0), 0, base_addr)) + # this case is a INC_j + # + expected_instructions = ( + # mov r11, 0x0123456789ABCDEF + '\x49\xBB\xEF\xCD\xAB\x89\x67\x45\x23\x01' + # inc [r11] + '\x49\xFF\x03' + ) + assert cb.getvalue() == expected_instructions + + def test_inc_64bit_address_2(self): + py.test.skip("there is no unary instruction INSN_a so far") + base_addr = 0x0123456789ABCDEF + cb = LocationCodeBuilder64() + cb.INC(AddressLoc(ImmedLoc(0), edx, 3, base_addr)) + # this case would be a INC_a + xxx + + def test_inc_64bit_address_3(self): + base_addr = 0x0123456789ABCDEF + cb = LocationCodeBuilder64() + cb.INC(AddressLoc(eax, ImmedLoc(0), 0, base_addr)) + # this case is a INC_m + # + expected_instructions = ( + # mov r11, 0x0123456789ABCDEF + '\x49\xBB\xEF\xCD\xAB\x89\x67\x45\x23\x01' + # lea r11, [rax+r11] + '\x4E\x8D\x1C\x18' + # inc [r11] + '\x49\xFF\x03' + ) + assert cb.getvalue() == expected_instructions diff --git a/rpython/jit/backend/x86/test/test_runner.py b/rpython/jit/backend/x86/test/test_runner.py --- a/rpython/jit/backend/x86/test/test_runner.py +++ b/rpython/jit/backend/x86/test/test_runner.py @@ -427,8 +427,8 @@ debug._log = None # assert ops_offset is looptoken._x86_ops_offset - # 2*(getfield_raw/int_add/setfield_raw) + ops + None - assert len(ops_offset) == 2*3 + len(operations) + 1 + # 2*increment_debug_counter + ops + None + assert len(ops_offset) == 2 + len(operations) + 1 assert (ops_offset[operations[0]] <= ops_offset[operations[1]] <= ops_offset[operations[2]] <= diff --git a/rpython/jit/codewriter/call.py b/rpython/jit/codewriter/call.py --- a/rpython/jit/codewriter/call.py +++ b/rpython/jit/codewriter/call.py @@ -178,7 +178,7 @@ return (fnaddr, calldescr) def getcalldescr(self, op, oopspecindex=EffectInfo.OS_NONE, - extraeffect=None): + extraeffect=None, extradescr=None): """Return the calldescr that describes all calls done by 'op'. This returns a calldescr that we can put in the corresponding call operation in the calling jitcode. It gets an effectinfo @@ -259,6 +259,7 @@ effectinfo = effectinfo_from_writeanalyze( self.readwrite_analyzer.analyze(op, self.seen), self.cpu, extraeffect, oopspecindex, can_invalidate, call_release_gil_target, + extradescr, ) # assert effectinfo is not None diff --git a/rpython/jit/codewriter/effectinfo.py b/rpython/jit/codewriter/effectinfo.py --- a/rpython/jit/codewriter/effectinfo.py +++ b/rpython/jit/codewriter/effectinfo.py @@ -21,6 +21,7 @@ OS_ARRAYCOPY = 1 # "list.ll_arraycopy" OS_STR2UNICODE = 2 # "str.str2unicode" OS_SHRINK_ARRAY = 3 # rgc.ll_shrink_array + OS_DICT_LOOKUP = 4 # ll_dict_lookup # OS_STR_CONCAT = 22 # "stroruni.concat" OS_STR_SLICE = 23 # "stroruni.slice" @@ -88,15 +89,18 @@ # for debugging: _OS_CANRAISE = set([ OS_NONE, OS_STR2UNICODE, OS_LIBFFI_CALL, OS_RAW_MALLOC_VARSIZE_CHAR, - OS_JIT_FORCE_VIRTUAL, OS_SHRINK_ARRAY, + OS_JIT_FORCE_VIRTUAL, OS_SHRINK_ARRAY, OS_DICT_LOOKUP, ]) def __new__(cls, readonly_descrs_fields, readonly_descrs_arrays, + readonly_descrs_interiorfields, write_descrs_fields, write_descrs_arrays, + write_descrs_interiorfields, extraeffect=EF_CAN_RAISE, oopspecindex=OS_NONE, can_invalidate=False, - call_release_gil_target=llmemory.NULL): + call_release_gil_target=llmemory.NULL, + extradescrs=None): key = (frozenset_or_none(readonly_descrs_fields), frozenset_or_none(readonly_descrs_arrays), frozenset_or_none(write_descrs_fields), @@ -121,17 +125,21 @@ result = object.__new__(cls) result.readonly_descrs_fields = readonly_descrs_fields result.readonly_descrs_arrays = readonly_descrs_arrays + result.readonly_descrs_interiorfields = readonly_descrs_interiorfields if extraeffect == EffectInfo.EF_LOOPINVARIANT or \ extraeffect == EffectInfo.EF_ELIDABLE_CANNOT_RAISE or \ extraeffect == EffectInfo.EF_ELIDABLE_CAN_RAISE: result.write_descrs_fields = [] result.write_descrs_arrays = [] + result.write_descrs_interiorfields = [] else: result.write_descrs_fields = write_descrs_fields result.write_descrs_arrays = write_descrs_arrays + result.write_descrs_interiorfields = write_descrs_interiorfields result.extraeffect = extraeffect result.can_invalidate = can_invalidate result.oopspecindex = oopspecindex + result.extradescrs = extradescrs result.call_release_gil_target = call_release_gil_target if result.check_can_raise(): assert oopspecindex in cls._OS_CANRAISE @@ -163,7 +171,7 @@ return None return frozenset(x) -EffectInfo.MOST_GENERAL = EffectInfo(None, None, None, None, +EffectInfo.MOST_GENERAL = EffectInfo(None, None, None, None, None, None, EffectInfo.EF_RANDOM_EFFECTS, can_invalidate=True) @@ -172,19 +180,24 @@ extraeffect=EffectInfo.EF_CAN_RAISE, oopspecindex=EffectInfo.OS_NONE, can_invalidate=False, - call_release_gil_target=llmemory.NULL): + call_release_gil_target=llmemory.NULL, + extradescr=None): from rpython.translator.backendopt.writeanalyze import top_set if effects is top_set or extraeffect == EffectInfo.EF_RANDOM_EFFECTS: readonly_descrs_fields = None readonly_descrs_arrays = None + readonly_descrs_interiorfields = None write_descrs_fields = None write_descrs_arrays = None + write_descrs_interiorfields = None extraeffect = EffectInfo.EF_RANDOM_EFFECTS else: readonly_descrs_fields = [] readonly_descrs_arrays = [] + readonly_descrs_interiorfields = [] write_descrs_fields = [] write_descrs_arrays = [] + write_descrs_interiorfields = [] def add_struct(descrs_fields, (_, T, fieldname)): T = deref(T) @@ -198,6 +211,17 @@ descr = cpu.arraydescrof(ARRAY) descrs_arrays.append(descr) + def add_interiorfield(descrs_interiorfields, (_, T, fieldname)): + T = deref(T) + if not isinstance(T, lltype.Array): + return # let's not consider structs for now + if not consider_array(T): + return + if getattr(T.OF, fieldname) is lltype.Void: + return + descr = cpu.interiorfielddescrof(T, fieldname) + descrs_interiorfields.append(descr) + for tup in effects: if tup[0] == "struct": add_struct(write_descrs_fields, tup) @@ -205,6 +229,12 @@ tupw = ("struct",) + tup[1:] if tupw not in effects: add_struct(readonly_descrs_fields, tup) + elif tup[0] == "interiorfield": + add_interiorfield(write_descrs_interiorfields, tup) + elif tup[0] == "readinteriorfield": + tupw = ('interiorfield',) + tup[1:] + if tupw not in effects: + add_interiorfield(readonly_descrs_interiorfields, tup) elif tup[0] == "array": add_array(write_descrs_arrays, tup) elif tup[0] == "readarray": @@ -216,12 +246,15 @@ # return EffectInfo(readonly_descrs_fields, readonly_descrs_arrays, + readonly_descrs_interiorfields, write_descrs_fields, write_descrs_arrays, + write_descrs_interiorfields, extraeffect, oopspecindex, can_invalidate, - call_release_gil_target) + call_release_gil_target, + extradescr) def consider_struct(TYPE, fieldname): if fieldType(TYPE, fieldname) is lltype.Void: diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py --- a/rpython/jit/codewriter/jtransform.py +++ b/rpython/jit/codewriter/jtransform.py @@ -403,6 +403,9 @@ prepare = self._handle_math_sqrt_call elif oopspec_name.startswith('rgc.'): prepare = self._handle_rgc_call + elif oopspec_name.endswith('dict.lookup'): + # also ordereddict.lookup + prepare = self._handle_dict_lookup_call else: prepare = self.prepare_builtin_call try: @@ -1680,9 +1683,11 @@ # ---------- # Strings and Unicodes. - def _handle_oopspec_call(self, op, args, oopspecindex, extraeffect=None): + def _handle_oopspec_call(self, op, args, oopspecindex, extraeffect=None, + extradescr=None): calldescr = self.callcontrol.getcalldescr(op, oopspecindex, - extraeffect) + extraeffect, + extradescr=extradescr) if extraeffect is not None: assert (is_test_calldescr(calldescr) # for tests or calldescr.get_extra_info().extraeffect == extraeffect) @@ -1846,6 +1851,14 @@ return self._handle_oopspec_call(op, args, EffectInfo.OS_MATH_SQRT, EffectInfo.EF_ELIDABLE_CANNOT_RAISE) + def _handle_dict_lookup_call(self, op, oopspec_name, args): + extradescr1 = self.cpu.fielddescrof(op.args[1].concretetype.TO, + 'entries') + extradescr2 = self.cpu.interiorfielddescrof( + op.args[1].concretetype.TO.entries.TO, 'key') + return self._handle_oopspec_call(op, args, EffectInfo.OS_DICT_LOOKUP, + extradescr=[extradescr1, extradescr2]) + def _handle_rgc_call(self, op, oopspec_name, args): if oopspec_name == 'rgc.ll_shrink_array': return self._handle_oopspec_call(op, args, EffectInfo.OS_SHRINK_ARRAY, EffectInfo.EF_CAN_RAISE) diff --git a/rpython/jit/codewriter/test/test_jtransform.py b/rpython/jit/codewriter/test/test_jtransform.py --- a/rpython/jit/codewriter/test/test_jtransform.py +++ b/rpython/jit/codewriter/test/test_jtransform.py @@ -60,7 +60,8 @@ class FakeResidualCallControl: def guess_call_kind(self, op): return 'residual' - def getcalldescr(self, op, oopspecindex=None, extraeffect=None): + def getcalldescr(self, op, oopspecindex=None, extraeffect=None, + extradescr=None): return 'calldescr' def calldescr_canraise(self, calldescr): return True @@ -117,7 +118,8 @@ self.callinfocollection = FakeCallInfoCollection() def guess_call_kind(self, op): return 'builtin' - def getcalldescr(self, op, oopspecindex=None, extraeffect=None): + def getcalldescr(self, op, oopspecindex=None, extraeffect=None, + extradescr=None): assert oopspecindex is not None # in this test EI = effectinfo.EffectInfo if oopspecindex != EI.OS_ARRAYCOPY: diff --git a/rpython/jit/codewriter/test/test_list.py b/rpython/jit/codewriter/test/test_list.py --- a/rpython/jit/codewriter/test/test_list.py +++ b/rpython/jit/codewriter/test/test_list.py @@ -37,7 +37,8 @@ class FakeCallControl: class getcalldescr(AbstractDescr): - def __init__(self, op, oopspecindex=0, extraeffect=None): + def __init__(self, op, oopspecindex=0, extraeffect=None, + extradescr=None): self.op = op self.oopspecindex = oopspecindex def __repr__(self): diff --git a/rpython/jit/metainterp/executor.py b/rpython/jit/metainterp/executor.py --- a/rpython/jit/metainterp/executor.py +++ b/rpython/jit/metainterp/executor.py @@ -332,6 +332,7 @@ continue if value in (rop.FORCE_TOKEN, rop.CALL_ASSEMBLER, + rop.INCREMENT_DEBUG_COUNTER, rop.COND_CALL_GC_WB, rop.COND_CALL_GC_WB_ARRAY, rop.DEBUG_MERGE_POINT, diff --git a/rpython/jit/metainterp/optimizeopt/heap.py b/rpython/jit/metainterp/optimizeopt/heap.py --- a/rpython/jit/metainterp/optimizeopt/heap.py +++ b/rpython/jit/metainterp/optimizeopt/heap.py @@ -1,8 +1,10 @@ import os +from rpython.jit.codewriter.effectinfo import EffectInfo +from rpython.jit.metainterp.optimizeopt.util import args_dict from rpython.jit.metainterp.history import Const from rpython.jit.metainterp.jitexc import JitException -from rpython.jit.metainterp.optimizeopt.optimizer import Optimization, MODE_ARRAY, LEVEL_KNOWNCLASS +from rpython.jit.metainterp.optimizeopt.optimizer import Optimization, MODE_ARRAY, LEVEL_KNOWNCLASS, REMOVED from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method from rpython.jit.metainterp.resoperation import rop, ResOperation from rpython.rlib.objectmodel import we_are_translated @@ -173,6 +175,10 @@ self.cached_fields = {} # cached array items: {array descr: {index: CachedField}} self.cached_arrayitems = {} + # cached dict items: {dict descr: {(optval, index): box-or-const}} + self.cached_dict_reads = {} + # cache of corresponding array descrs + self.corresponding_array_descrs = {} # self._lazy_setfields_and_arrayitems = [] self._remove_guard_not_invalidated = False @@ -180,9 +186,13 @@ self.postponed_op = None def force_at_end_of_preamble(self): + self.cached_dict_reads.clear() + self.corresponding_array_descrs.clear() self.force_all_lazy_setfields_and_arrayitems() def flush(self): + self.cached_dict_reads.clear() + self.corresponding_array_descrs.clear() self.force_all_lazy_setfields_and_arrayitems() self.emit_postponed_op() @@ -214,6 +224,7 @@ del self._lazy_setfields_and_arrayitems[:] self.cached_fields.clear() self.cached_arrayitems.clear() + self.cached_dict_reads.clear() def field_cache(self, descr): try: @@ -282,6 +293,44 @@ self.force_all_lazy_setfields_and_arrayitems() self.clean_caches() + def optimize_CALL(self, op): + # dispatch based on 'oopspecindex' to a method that handles + # specifically the given oopspec call. For non-oopspec calls, + # oopspecindex is just zero. + effectinfo = op.getdescr().get_extra_info() + oopspecindex = effectinfo.oopspecindex + if oopspecindex == EffectInfo.OS_DICT_LOOKUP: + if self._optimize_CALL_DICT_LOOKUP(op): + return + self.emit_operation(op) + + def _optimize_CALL_DICT_LOOKUP(self, op): + descrs = op.getdescr().get_extra_info().extradescrs + assert descrs # translation hint + descr1 = descrs[0] + try: + d = self.cached_dict_reads[descr1] + except KeyError: + d = self.cached_dict_reads[descr1] = args_dict() + self.corresponding_array_descrs[descrs[1]] = descr1 + args = self.optimizer.make_args_key(op) + try: + res_v = d[args] + except KeyError: + d[args] = self.getvalue(op.result) + return False + else: + self.make_equal_to(op.result, res_v) + self.last_emitted_operation = REMOVED + return True + + def optimize_GUARD_NO_EXCEPTION(self, op): + if self.last_emitted_operation is REMOVED: + return + self.emit_operation(op) + + optimize_GUARD_EXCEPTION = optimize_GUARD_NO_EXCEPTION + def force_from_effectinfo(self, effectinfo): # XXX we can get the wrong complexity here, if the lists # XXX stored on effectinfo are large @@ -290,9 +339,20 @@ for arraydescr in effectinfo.readonly_descrs_arrays: self.force_lazy_setarrayitem(arraydescr) for fielddescr in effectinfo.write_descrs_fields: + try: + del self.cached_dict_reads[fielddescr] + except KeyError: + pass self.force_lazy_setfield(fielddescr, can_cache=False) for arraydescr in effectinfo.write_descrs_arrays: self.force_lazy_setarrayitem(arraydescr, can_cache=False) + for descr in effectinfo.write_descrs_interiorfields: + if descr in self.corresponding_array_descrs: + dictdescr = self.corresponding_array_descrs.pop(descr) + try: + del self.cached_dict_reads[dictdescr] + except KeyError: + pass # someone did it already if effectinfo.check_forces_virtual_or_virtualizable(): vrefinfo = self.optimizer.metainterp_sd.virtualref_info self.force_lazy_setfield(vrefinfo.descr_forced) diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py --- a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py +++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py @@ -5444,6 +5444,21 @@ """ self.optimize_loop(ops, expected) + def test_consecutive_getinteriorfields(self): + py.test.skip("we want this to pass") + ops = """ + [p0, i0] + i1 = getinteriorfield_gc(p0, i0, descr=valuedescr) + i2 = getinteriorfield_gc(p0, i0, descr=valuedescr) + jump(i1, i2) + """ + expected = """ + [p0, i0] + i1 = getinteriorfield_gc(p0, i0, descr=valuedescr) + jump(i1, i1) + """ + self.optimize_loop(ops, expected) + class TestLLtype(BaseTestOptimizeBasic, LLtypeMixin): pass diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py b/rpython/jit/metainterp/optimizeopt/test/test_util.py --- a/rpython/jit/metainterp/optimizeopt/test/test_util.py +++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py @@ -181,28 +181,29 @@ plaincalldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo.MOST_GENERAL) nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [], [], [])) + EffectInfo([], [], [], [], [], [])) writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [], [adescr], [])) + EffectInfo([], [], [], [adescr], [], [])) writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [], [adescr], [arraydescr])) + EffectInfo([], [], [], [adescr], [arraydescr], + [])) readadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([adescr], [], [], [])) + EffectInfo([adescr], [], [], [], [], [])) mayforcevirtdescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([nextdescr], [], [], [], + EffectInfo([nextdescr], [], [], [], [], [], EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE, can_invalidate=True)) arraycopydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [arraydescr], [], [arraydescr], + EffectInfo([], [arraydescr], [], [], [arraydescr], [], EffectInfo.EF_CANNOT_RAISE, oopspecindex=EffectInfo.OS_ARRAYCOPY)) raw_malloc_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [], [], [], + EffectInfo([], [], [], [], [], [], EffectInfo.EF_CAN_RAISE, oopspecindex=EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR)) raw_free_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [], [], [], + EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE, oopspecindex=EffectInfo.OS_RAW_FREE)) @@ -251,17 +252,18 @@ _oopspecindex = getattr(EffectInfo, _os) locals()[_name] = \ cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE, + EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE, oopspecindex=_oopspecindex)) # _oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI')) locals()[_name.replace('str', 'unicode')] = \ cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE, + EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE, oopspecindex=_oopspecindex)) s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, - EffectInfo([], [], [], [], oopspecindex=EffectInfo.OS_STR2UNICODE)) + EffectInfo([], [], [], [], [], [], + oopspecindex=EffectInfo.OS_STR2UNICODE)) # class LoopToken(AbstractDescr): @@ -277,7 +279,7 @@ virtualtokendescr = vrefinfo.descr_virtual_token virtualforceddescr = vrefinfo.descr_forced FUNC = lltype.FuncType([], lltype.Void) - ei = EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE, + ei = EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE, can_invalidate=False, oopspecindex=EffectInfo.OS_JIT_FORCE_VIRTUALIZABLE) clear_vable = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, ei) diff --git a/rpython/jit/metainterp/resoperation.py b/rpython/jit/metainterp/resoperation.py --- a/rpython/jit/metainterp/resoperation.py +++ b/rpython/jit/metainterp/resoperation.py @@ -494,6 +494,7 @@ # must be forced, however we need to execute it anyway '_NOSIDEEFFECT_LAST', # ----- end of no_side_effect operations ----- + 'INCREMENT_DEBUG_COUNTER/1', 'SETARRAYITEM_GC/3d', 'SETARRAYITEM_RAW/3d', 'SETINTERIORFIELD_GC/3d', diff --git a/rpython/jit/metainterp/test/test_dict.py b/rpython/jit/metainterp/test/test_dict.py --- a/rpython/jit/metainterp/test/test_dict.py +++ b/rpython/jit/metainterp/test/test_dict.py @@ -193,6 +193,107 @@ self.check_simple_loop({'int_sub': 1, 'int_gt': 1, 'guard_true': 1, 'jump': 1}) + def test_dict_two_lookups(self): + driver = JitDriver(greens = [], reds = 'auto') + d = {'a': 3, 'b': 4} + indexes = ['a', 'b'] + + def f(n): + s = 0 + while n > 0: + driver.jit_merge_point() + s += d[indexes[n & 1]] + s += d[indexes[n & 1]] + n -= 1 + return s + + self.meta_interp(f, [10]) + # XXX should be one getinteriorfield_gc + self.check_simple_loop(call=1, getinteriorfield_gc=2, + guard_no_exception=1) + + def test_ordered_dict_two_lookups(self): + driver = JitDriver(greens = [], reds = 'auto') + d = OrderedDict() + d['a'] = 3 + d['b'] = 4 + indexes = ['a', 'b'] + + def f(n): + s = 0 + while n > 0: + driver.jit_merge_point() + s += d[indexes[n & 1]] + s += d[indexes[n & 1]] + n -= 1 + return s + + self.meta_interp(f, [10]) + # XXX should be one getinteriorfield_gc + self.check_simple_loop(call=1, getinteriorfield_gc=2, + guard_no_exception=1) + + def test_dict_insert_invalidates_caches(self): + driver = JitDriver(greens = [], reds = 'auto') + indexes = ['aa', 'b', 'cc'] + + def f(n): + d = {'aa': 3, 'b': 4, 'cc': 5} + s = 0 + while n > 0: + driver.jit_merge_point() + index = indexes[n & 1] + s += d[index] + d['aa'] += 1 # this will invalidate the index + s += d[index] + n -= 1 + return s + + res = self.meta_interp(f, [10]) + assert res == f(10) + self.check_simple_loop(call=5) + + def test_dict_array_write_invalidates_caches(self): + driver = JitDriver(greens = [], reds = 'auto') + indexes = ['aa', 'b', 'cc'] + + def f(n): + d = {'aa': 3, 'b': 4, 'cc': 5} + s = 0 + while n > 0: + driver.jit_merge_point() + index = indexes[n & 1] + s += d[index] + del d['cc'] + s += d[index] + d['cc'] = 3 + n -= 1 + return s + + exp = f(10) + res = self.meta_interp(f, [10]) + assert res == exp + self.check_simple_loop(call=7) + + def test_dict_double_lookup_2(self): + driver = JitDriver(greens = [], reds = 'auto') + indexes = ['aa', 'b', 'cc'] + + def f(n): + d = {'aa': 3, 'b': 4, 'cc': 5} + s = 0 + while n > 0: + driver.jit_merge_point() + index = indexes[n & 1] + s += d[index] + d[index] += 1 + n -= 1 + return s + + res = self.meta_interp(f, [10]) + assert res == f(10) + self.check_simple_loop(call=3) + class TestLLtype(DictTests, LLJitMixin): pass diff --git a/rpython/jit/metainterp/virtualizable.py b/rpython/jit/metainterp/virtualizable.py --- a/rpython/jit/metainterp/virtualizable.py +++ b/rpython/jit/metainterp/virtualizable.py @@ -302,7 +302,7 @@ self.clear_vable_ptr = self.warmrunnerdesc.helper_func( FUNCPTR, self.clear_vable_token) FUNC = FUNCPTR.TO - ei = EffectInfo([], [], [], [], EffectInfo.EF_CANNOT_RAISE, + ei = EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE, can_invalidate=False, oopspecindex=EffectInfo.OS_JIT_FORCE_VIRTUALIZABLE) diff --git a/rpython/rlib/rfile.py b/rpython/rlib/rfile.py --- a/rpython/rlib/rfile.py +++ b/rpython/rlib/rfile.py @@ -14,6 +14,11 @@ includes = ['stdio.h', 'sys/types.h'] if os.name == "posix": includes += ['unistd.h'] + ftruncate = 'ftruncate' + fileno = 'fileno' +else: + ftruncate = '_chsize' + fileno = '_fileno' eci = ExternalCompilationInfo(includes=includes) def llexternal(*args, **kwargs): @@ -41,10 +46,10 @@ c_fseek = llexternal('fseek', [lltype.Ptr(FILE), rffi.LONG, rffi.INT], rffi.INT) c_tmpfile = llexternal('tmpfile', [], lltype.Ptr(FILE)) -c_fileno = llexternal('fileno', [lltype.Ptr(FILE)], rffi.INT) +c_fileno = llexternal(fileno, [lltype.Ptr(FILE)], rffi.INT) c_ftell = llexternal('ftell', [lltype.Ptr(FILE)], rffi.LONG) c_fflush = llexternal('fflush', [lltype.Ptr(FILE)], rffi.INT) -c_ftruncate = llexternal('ftruncate', [rffi.INT, OFF_T], rffi.INT, macro=True) +c_ftruncate = llexternal(ftruncate, [rffi.INT, OFF_T], rffi.INT, macro=True) c_fgets = llexternal('fgets', [rffi.CCHARP, rffi.INT, lltype.Ptr(FILE)], rffi.CCHARP) diff --git a/rpython/rtyper/lltypesystem/rdict.py b/rpython/rtyper/lltypesystem/rdict.py --- a/rpython/rtyper/lltypesystem/rdict.py +++ b/rpython/rtyper/lltypesystem/rdict.py @@ -569,6 +569,7 @@ PERTURB_SHIFT = 5 @jit.look_inside_iff(lambda d, key, hash: jit.isvirtual(d) and jit.isconstant(key)) +@jit.oopspec('dict.lookup(d, key, hash)') def ll_dict_lookup(d, key, hash): entries = d.entries ENTRIES = lltype.typeOf(entries).TO diff --git a/rpython/rtyper/lltypesystem/rordereddict.py b/rpython/rtyper/lltypesystem/rordereddict.py --- a/rpython/rtyper/lltypesystem/rordereddict.py +++ b/rpython/rtyper/lltypesystem/rordereddict.py @@ -709,6 +709,7 @@ @jit.look_inside_iff(lambda d, key, hash, store_flag, T: jit.isvirtual(d) and jit.isconstant(key)) +@jit.oopspec('ordereddict.lookup(d, key, hash, store_flag, T)') def ll_dict_lookup(d, key, hash, store_flag, T): INDEXES = _ll_ptr_to_array_of(T) entries = d.entries diff --git a/rpython/translator/backendopt/test/test_writeanalyze.py b/rpython/translator/backendopt/test/test_writeanalyze.py --- a/rpython/translator/backendopt/test/test_writeanalyze.py +++ b/rpython/translator/backendopt/test/test_writeanalyze.py @@ -353,3 +353,23 @@ result = wa.analyze(fgraph.startblock.operations[-1]) assert list(result) == [("struct", lltype.Ptr(S), "x")] + + def test_interiorfield(self): + A = lltype.GcArray(lltype.Struct('x', ('x', lltype.Signed), + ('y', lltype.Signed))) + + def g(x): + a = lltype.malloc(A, 1) + a[0].y = 3 + return f(a, x) + + def f(a, x): + a[0].x = x + return a[0].y + + t, wa = self.translate(g, [int]) + ggraph = graphof(t, g) + result = wa.analyze(ggraph.startblock.operations[-1]) + res = list(result) + assert ('readinteriorfield', lltype.Ptr(A), 'y') in res + assert ('interiorfield', lltype.Ptr(A), 'x') in res diff --git a/rpython/translator/backendopt/writeanalyze.py b/rpython/translator/backendopt/writeanalyze.py --- a/rpython/translator/backendopt/writeanalyze.py +++ b/rpython/translator/backendopt/writeanalyze.py @@ -1,4 +1,4 @@ -from rpython.flowspace.model import Variable +from rpython.flowspace.model import Variable, Constant from rpython.translator.backendopt import graphanalyze top_set = object() @@ -37,6 +37,12 @@ return top_set return result1.union(result2) + def _getinteriorname(self, op): + if (isinstance(op.args[1], Constant) and + isinstance(op.args[1].value, str)): + return op.args[1].value + return op.args[2].value + def analyze_simple_operation(self, op, graphinfo): if op.opname == "setfield": if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]): @@ -45,11 +51,18 @@ elif op.opname == "setarrayitem": if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]): return self._array_result(op.args[0].concretetype) + elif op.opname == "setinteriorfield": + if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]): + name = self._getinteriorname(op) + return self._interiorfield_result(op.args[0].concretetype, name) return empty_set def _array_result(self, TYPE): return frozenset([("array", TYPE)]) + def _interiorfield_result(self, TYPE, fieldname): + return frozenset([("interiorfield", TYPE, fieldname)]) + def compute_graph_info(self, graph): return FreshMallocs(graph) @@ -99,4 +112,8 @@ elif op.opname == "getarrayitem": return frozenset([ ("readarray", op.args[0].concretetype)]) + elif op.opname == "getinteriorfield": + name = self._getinteriorname(op) + return frozenset([("readinteriorfield", op.args[0].concretetype, + name)]) return WriteAnalyzer.analyze_simple_operation(self, op, graphinfo) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit