Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r83394:060cbab9d669 Date: 2016-03-27 17:15 +0200 http://bitbucket.org/pypy/pypy/changeset/060cbab9d669/
Log: merge heads diff too long, truncating to 2000 out of 8524 lines diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py --- a/pypy/config/pypyoption.py +++ b/pypy/config/pypyoption.py @@ -46,7 +46,6 @@ except detect_cpu.ProcessorAutodetectError: pass - translation_modules = default_modules.copy() translation_modules.update([ "fcntl", "time", "select", "signal", "_rawffi", "zlib", "struct", "_md5", 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 @@ -27,3 +27,8 @@ .. branch: fix_transpose_for_list_v3 Allow arguments to transpose to be sequences + +.. branch: jit-leaner-frontend + +Improve the tracing speed in the frontend as well as heapcache by using a more compact representation +of traces \ No newline at end of file diff --git a/pypy/objspace/std/objectobject.py b/pypy/objspace/std/objectobject.py --- a/pypy/objspace/std/objectobject.py +++ b/pypy/objspace/std/objectobject.py @@ -110,7 +110,7 @@ def descr__init__(space, w_obj, __args__): # don't allow arguments unless __new__ is overridden w_type = space.type(w_obj) - w_parent_new, _ = w_type.lookup_where('__new__') + w_parent_new, _ = space.lookup_in_type_where(w_type, '__new__') if w_parent_new is space.w_object: try: __args__.fixedunpack(0) diff --git a/pypy/tool/gdb_pypy.py b/pypy/tool/gdb_pypy.py --- a/pypy/tool/gdb_pypy.py +++ b/pypy/tool/gdb_pypy.py @@ -288,9 +288,11 @@ RPyListPrinter.recursive = True try: itemlist = [] - for i in range(length): + for i in range(min(length, MAX_DISPLAY_LENGTH)): item = items[i] itemlist.append(str(item)) # may recurse here + if length > MAX_DISPLAY_LENGTH: + itemlist.append("...") str_items = ', '.join(itemlist) finally: RPyListPrinter.recursive = False diff --git a/rpython/config/translationoption.py b/rpython/config/translationoption.py --- a/rpython/config/translationoption.py +++ b/rpython/config/translationoption.py @@ -126,6 +126,9 @@ ChoiceOption("jit_profiler", "integrate profiler support into the JIT", ["off", "oprofile"], default="off"), + ChoiceOption("jit_opencoder_model", "the model limits the maximal length" + " of traces. Use big if you want to go bigger than " + "the default", ["big", "normal"], default="normal"), BoolOption("check_str_without_nul", "Forbid NUL chars in strings in some external function calls", default=False, cmdline=None), diff --git a/rpython/jit/backend/arm/assembler.py b/rpython/jit/backend/arm/assembler.py --- a/rpython/jit/backend/arm/assembler.py +++ b/rpython/jit/backend/arm/assembler.py @@ -939,9 +939,9 @@ op = operations[i] self.mc.mark_op(op) opnum = op.getopnum() - if op.has_no_side_effect() and op not in regalloc.longevity: + if rop.has_no_side_effect(opnum) and op not in regalloc.longevity: regalloc.possibly_free_vars_for_op(op) - elif not we_are_translated() and op.getopnum() == -127: + elif not we_are_translated() and op.getopnum() == rop.FORCE_SPILL: regalloc.prepare_force_spill(op, fcond) else: arglocs = regalloc_operations[opnum](regalloc, op, fcond) @@ -949,7 +949,7 @@ fcond = asm_operations[opnum](self, op, arglocs, regalloc, fcond) assert fcond is not None - if op.is_guard(): + if rop.is_guard(opnum): regalloc.possibly_free_vars(op.getfailargs()) if op.type != 'v': regalloc.possibly_free_var(op) diff --git a/rpython/jit/backend/arm/detect.py b/rpython/jit/backend/arm/detect.py --- a/rpython/jit/backend/arm/detect.py +++ b/rpython/jit/backend/arm/detect.py @@ -1,6 +1,7 @@ import os from rpython.translator.tool.cbuild import ExternalCompilationInfo +from rpython.rtyper.lltypesystem import lltype, rffi from rpython.rtyper.tool import rffi_platform from rpython.rlib.clibffi import FFI_DEFAULT_ABI, FFI_SYSV, FFI_VFP from rpython.translator.platform import CompilationError @@ -15,6 +16,7 @@ asm volatile("VMOV s0, s1"); } """]) +getauxval = rffi.llexternal("getauxval", [lltype.Unsigned], lltype.Unsigned) def detect_hardfloat(): return FFI_DEFAULT_ABI == FFI_VFP @@ -63,3 +65,10 @@ "falling back to", "ARMv%d" % n) debug_stop("jit-backend-arch") return n + + +def detect_neon(): + AT_HWCAP = 16 + HWCAP_NEON = 1 << 12 + hwcap = getauxval(AT_HWCAP) + return bool(hwcap & HWCAP_NEON) 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 @@ -1092,8 +1092,8 @@ self.mc.VCVT_int_to_float(res.value, r.svfp_ip.value) return fcond - # the following five instructions are only ARMv7; - # regalloc.py won't call them at all on ARMv6 + # the following five instructions are only ARMv7 with NEON; + # regalloc.py won't call them at all, in other cases emit_opx_llong_add = gen_emit_float_op('llong_add', 'VADD_i64') emit_opx_llong_sub = gen_emit_float_op('llong_sub', 'VSUB_i64') emit_opx_llong_and = gen_emit_float_op('llong_and', 'VAND_i64') 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 @@ -530,7 +530,7 @@ EffectInfo.OS_LLONG_AND, EffectInfo.OS_LLONG_OR, EffectInfo.OS_LLONG_XOR): - if self.cpu.cpuinfo.arch_version >= 7: + if self.cpu.cpuinfo.neon: args = self._prepare_llong_binop_xx(op, fcond) self.perform_extra(op, args, fcond) return diff --git a/rpython/jit/backend/arm/runner.py b/rpython/jit/backend/arm/runner.py --- a/rpython/jit/backend/arm/runner.py +++ b/rpython/jit/backend/arm/runner.py @@ -7,13 +7,14 @@ from rpython.rlib.jit_hooks import LOOP_RUN_CONTAINER from rpython.rtyper.lltypesystem import lltype, llmemory from rpython.jit.backend.arm.detect import detect_hardfloat -from rpython.jit.backend.arm.detect import detect_arch_version +from rpython.jit.backend.arm.detect import detect_arch_version, detect_neon jitframe.STATICSIZE = JITFRAME_FIXED_SIZE class CPUInfo(object): hf_abi = False arch_version = 6 + neon = False class AbstractARMCPU(AbstractLLCPU): @@ -48,6 +49,7 @@ def setup_once(self): self.cpuinfo.arch_version = detect_arch_version() self.cpuinfo.hf_abi = detect_hardfloat() + self.cpuinfo.neon = detect_neon() #self.codemap.setup() self.assembler.setup_once() 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 @@ -455,7 +455,7 @@ if box is not frame.current_op: value = frame.env[box] else: - value = box.getvalue() # 0 or 0.0 or NULL + value = 0 # box.getvalue() # 0 or 0.0 or NULL else: value = None values.append(value) @@ -472,6 +472,13 @@ # ------------------------------------------------------------ + def setup_descrs(self): + all_descrs = [] + for k, v in self.descrs.iteritems(): + v.descr_index = len(all_descrs) + all_descrs.append(v) + return all_descrs + def calldescrof(self, FUNC, ARGS, RESULT, effect_info): key = ('call', getkind(RESULT), tuple([getkind(A) for A in ARGS]), 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 @@ -331,7 +331,7 @@ counter = self._register_counter(tp, number, token) c_adr = ConstInt(rffi.cast(lltype.Signed, counter)) operations.append( - ResOperation(rop.INCREMENT_DEBUG_COUNTER, [c_adr], None)) + ResOperation(rop.INCREMENT_DEBUG_COUNTER, [c_adr])) def _register_counter(self, tp, number, token): # YYY very minor leak -- we need the counters to stay alive diff --git a/rpython/jit/backend/llsupport/descr.py b/rpython/jit/backend/llsupport/descr.py --- a/rpython/jit/backend/llsupport/descr.py +++ b/rpython/jit/backend/llsupport/descr.py @@ -21,6 +21,30 @@ self._cache_call = {} self._cache_interiorfield = {} + def setup_descrs(self): + all_descrs = [] + for k, v in self._cache_size.iteritems(): + v.descr_index = len(all_descrs) + all_descrs.append(v) + for k, v in self._cache_field.iteritems(): + for k1, v1 in v.iteritems(): + v1.descr_index = len(all_descrs) + all_descrs.append(v1) + for k, v in self._cache_array.iteritems(): + v.descr_index = len(all_descrs) + all_descrs.append(v) + for k, v in self._cache_arraylen.iteritems(): + v.descr_index = len(all_descrs) + all_descrs.append(v) + for k, v in self._cache_call.iteritems(): + v.descr_index = len(all_descrs) + all_descrs.append(v) + for k, v in self._cache_interiorfield.iteritems(): + v.descr_index = len(all_descrs) + all_descrs.append(v) + assert len(all_descrs) < 2**15 + return all_descrs + def init_size_descr(self, STRUCT, sizedescr): pass diff --git a/rpython/jit/backend/llsupport/llmodel.py b/rpython/jit/backend/llsupport/llmodel.py --- a/rpython/jit/backend/llsupport/llmodel.py +++ b/rpython/jit/backend/llsupport/llmodel.py @@ -316,6 +316,9 @@ return ll_frame return execute_token + def setup_descrs(self): + return self.gc_ll_descr.setup_descrs() + # ------------------- helpers and descriptions -------------------- @staticmethod diff --git a/rpython/jit/backend/llsupport/regalloc.py b/rpython/jit/backend/llsupport/regalloc.py --- a/rpython/jit/backend/llsupport/regalloc.py +++ b/rpython/jit/backend/llsupport/regalloc.py @@ -683,7 +683,7 @@ for i in range(len(operations)-1, -1, -1): op = operations[i] if op.type != 'v': - if op not in last_used and op.has_no_side_effect(): + if op not in last_used and rop.has_no_side_effect(op.opnum): continue opnum = op.getopnum() for j in range(op.numargs()): @@ -695,7 +695,7 @@ if opnum != rop.JUMP and opnum != rop.LABEL: if arg not in last_real_usage: last_real_usage[arg] = i - if op.is_guard(): + if rop.is_guard(op.opnum): for arg in op.getfailargs(): if arg is None: # hole continue @@ -732,14 +732,7 @@ return longevity, last_real_usage def is_comparison_or_ovf_op(opnum): - from rpython.jit.metainterp.resoperation import opclasses - cls = opclasses[opnum] - # hack hack: in theory they are instance method, but they don't use - # any instance field, we can use a fake object - class Fake(cls): - pass - op = Fake() - return op.is_comparison() or op.is_ovf() + return rop.is_comparison(opnum) or rop.is_ovf(opnum) def valid_addressing_size(size): return size == 1 or size == 2 or size == 4 or size == 8 diff --git a/rpython/jit/backend/llsupport/rewrite.py b/rpython/jit/backend/llsupport/rewrite.py --- a/rpython/jit/backend/llsupport/rewrite.py +++ b/rpython/jit/backend/llsupport/rewrite.py @@ -103,7 +103,7 @@ orig_op.set_forwarded(op) replaced = True op.setarg(i, arg) - if op.is_guard(): + if rop.is_guard(op.opnum): if not replaced: op = op.copy_and_change(op.getopnum()) orig_op.set_forwarded(op) @@ -212,7 +212,7 @@ # self._emit_mul_if_factor_offset_not_supported(v_length, scale, 0) # op.setarg(1, ConstInt(scale)) # op.setarg(2, v_length) - if op.is_getarrayitem() or \ + if rop.is_getarrayitem(opnum) or \ opnum in (rop.GETARRAYITEM_RAW_I, rop.GETARRAYITEM_RAW_F): self.handle_getarrayitem(op) @@ -324,13 +324,13 @@ if self.transform_to_gc_load(op): continue # ---------- turn NEWxxx into CALL_MALLOC_xxx ---------- - if op.is_malloc(): + if rop.is_malloc(op.opnum): self.handle_malloc_operation(op) continue - if (op.is_guard() or + if (rop.is_guard(op.opnum) or self.could_merge_with_next_guard(op, i, operations)): self.emit_pending_zeros() - elif op.can_malloc(): + elif rop.can_malloc(op.opnum): self.emitting_an_operation_that_can_collect() elif op.getopnum() == rop.LABEL: self.emitting_an_operation_that_can_collect() @@ -370,8 +370,8 @@ # return True in cases where the operation and the following guard # should likely remain together. Simplified version of # can_merge_with_next_guard() in llsupport/regalloc.py. - if not op.is_comparison(): - return op.is_ovf() # int_xxx_ovf() / guard_no_overflow() + if not rop.is_comparison(op.opnum): + return rop.is_ovf(op.opnum) # int_xxx_ovf() / guard_no_overflow() if i + 1 >= len(operations): return False next_op = operations[i + 1] @@ -400,7 +400,6 @@ # it's hard to test all cases). Rewrite it away. value = int(opnum == rop.GUARD_FALSE) op1 = ResOperation(rop.SAME_AS_I, [ConstInt(value)]) - op1.setint(value) self.emit_op(op1) lst = op.getfailargs()[:] lst[i] = op1 @@ -633,8 +632,7 @@ args = [frame, arglist[jd.index_of_virtualizable]] else: args = [frame] - call_asm = ResOperation(op.getopnum(), args, - op.getdescr()) + call_asm = ResOperation(op.getopnum(), args, descr=op.getdescr()) self.replace_op_with(self.get_box_replacement(op), call_asm) self.emit_op(call_asm) @@ -708,7 +706,7 @@ def _gen_call_malloc_gc(self, args, v_result, descr): """Generate a CALL_MALLOC_GC with the given args.""" self.emitting_an_operation_that_can_collect() - op = ResOperation(rop.CALL_MALLOC_GC, args, descr) + op = ResOperation(rop.CALL_MALLOC_GC, args, descr=descr) self.replace_op_with(v_result, op) self.emit_op(op) # In general, don't add v_result to write_barrier_applied: diff --git a/rpython/jit/backend/ppc/regalloc.py b/rpython/jit/backend/ppc/regalloc.py --- a/rpython/jit/backend/ppc/regalloc.py +++ b/rpython/jit/backend/ppc/regalloc.py @@ -286,7 +286,8 @@ self.assembler.mc.mark_op(op) self.rm.position = i self.fprm.position = i - if op.has_no_side_effect() and op not in self.longevity: + opnum = op.opnum + if rop.has_no_side_effect(opnum) and op not in self.longevity: i += 1 self.possibly_free_vars_for_op(op) continue @@ -298,8 +299,7 @@ else: self.fprm.temp_boxes.append(box) # - opnum = op.getopnum() - if not we_are_translated() and opnum == -127: + if not we_are_translated() and opnum == rop.FORCE_SPILL: self._consider_force_spill(op) else: arglocs = oplist[opnum](self, op) diff --git a/rpython/jit/backend/test/test_ll_random.py b/rpython/jit/backend/test/test_ll_random.py --- a/rpython/jit/backend/test/test_ll_random.py +++ b/rpython/jit/backend/test/test_ll_random.py @@ -2,6 +2,7 @@ from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr from rpython.rtyper import rclass from rpython.jit.backend.test import test_random +from rpython.jit.backend.test.test_random import getint, getref_base, getref from rpython.jit.metainterp.resoperation import ResOperation, rop, optypes from rpython.jit.metainterp.history import ConstInt, ConstPtr, getkind from rpython.jit.codewriter import heaptracker @@ -169,7 +170,7 @@ if length == 0: raise test_random.CannotProduceOperation v_index = r.choice(self.intvars) - if not (0 <= v_index.getint() < length): + if not (0 <= getint(v_index) < length): v_index = ConstInt(r.random_integer() % length) return v_index @@ -311,7 +312,7 @@ def field_descr(self, builder, r): v, A = builder.get_structptr_var(r, type=lltype.Array, array_of_structs=True) - array = v.getref(lltype.Ptr(A)) + array = getref(lltype.Ptr(A), v) v_index = builder.get_index(len(array), r) choice = [] for name in A.OF._names: @@ -344,7 +345,7 @@ w = ConstInt(r.random_integer()) else: w = r.choice(builder.intvars) - value = w.getint() + value = getint(w) if rffi.cast(lltype.Signed, rffi.cast(TYPE, value)) == value: break builder.do(self.opnum, [v, w], descr) @@ -357,7 +358,7 @@ w = ConstInt(r.random_integer()) else: w = r.choice(builder.intvars) - value = w.getint() + value = getint(w) if rffi.cast(lltype.Signed, rffi.cast(TYPE, value)) == value: break builder.do(self.opnum, [v, v_index, w], descr) @@ -389,7 +390,7 @@ class GetArrayItemOperation(ArrayOperation): def field_descr(self, builder, r): v, A = builder.get_arrayptr_var(r) - array = v.getref(lltype.Ptr(A)) + array = getref(lltype.Ptr(A), v) v_index = builder.get_index(len(array), r) descr = self.array_descr(builder, A) return v, A, v_index, descr @@ -411,7 +412,7 @@ w = ConstInt(r.random_integer()) else: w = r.choice(builder.intvars) - value = w.getint() + value = getint(w) if rffi.cast(lltype.Signed, rffi.cast(A.OF, value)) == value: break builder.do(self.opnum, [v, v_index, w], descr) @@ -455,7 +456,7 @@ v_ptr = builder.do(self.opnum, [v_length]) getattr(builder, self.builder_cache).append(v_ptr) # Initialize the string. Is there a better way to do this? - for i in range(v_length.getint()): + for i in range(getint(v_length)): v_index = ConstInt(i) v_char = ConstInt(r.random_integer() % self.max) builder.do(self.set_char, [v_ptr, v_index, v_char]) @@ -471,9 +472,9 @@ current = getattr(builder, self.builder_cache) if current and r.random() < .8: v_string = r.choice(current) - string = v_string.getref(self.ptr) + string = getref(self.ptr, v_string) else: - string = self.alloc(builder.get_index(500, r).getint()) + string = self.alloc(getint(builder.get_index(500, r))) v_string = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, string)) current.append(v_string) for i in range(len(string.chars)): @@ -484,7 +485,7 @@ class AbstractGetItemOperation(AbstractStringOperation): def produce_into(self, builder, r): v_string = self.get_string(builder, r) - v_index = builder.get_index(len(v_string.getref(self.ptr).chars), r) + v_index = builder.get_index(len(getref(self.ptr, v_string).chars), r) builder.do(self.opnum, [v_string, v_index]) class AbstractSetItemOperation(AbstractStringOperation): @@ -492,7 +493,7 @@ v_string = self.get_string(builder, r) if isinstance(v_string, ConstPtr): raise test_random.CannotProduceOperation # setitem(Const, ...) - v_index = builder.get_index(len(v_string.getref(self.ptr).chars), r) + v_index = builder.get_index(len(getref(self.ptr, v_string).chars), r) v_target = ConstInt(r.random_integer() % self.max) builder.do(self.opnum, [v_string, v_index, v_target]) @@ -505,15 +506,15 @@ def produce_into(self, builder, r): v_srcstring = self.get_string(builder, r) v_dststring = self.get_string(builder, r) - src = v_srcstring.getref(self.ptr) - dst = v_dststring.getref(self.ptr) + src = getref(self.ptr, v_srcstring) + dst = getref(self.ptr, v_dststring) if src == dst: # because it's not a raise test_random.CannotProduceOperation # memmove(), but memcpy() srclen = len(src.chars) dstlen = len(dst.chars) v_length = builder.get_index(min(srclen, dstlen), r) - v_srcstart = builder.get_index(srclen - v_length.getint() + 1, r) - v_dststart = builder.get_index(dstlen - v_length.getint() + 1, r) + v_srcstart = builder.get_index(srclen - getint(v_length) + 1, r) + v_dststart = builder.get_index(dstlen - getint(v_length) + 1, r) builder.do(self.opnum, [v_srcstring, v_dststring, v_srcstart, v_dststart, v_length]) @@ -585,7 +586,7 @@ """ % funcargs).compile() vtableptr = v._hints['vtable']._as_ptr() d = { - 'ptr': S.getref_base(), + 'ptr': getref_base(S), 'vtable' : vtableptr, 'LLException' : LLException, } diff --git a/rpython/jit/backend/test/test_random.py b/rpython/jit/backend/test/test_random.py --- a/rpython/jit/backend/test/test_random.py +++ b/rpython/jit/backend/test/test_random.py @@ -11,11 +11,9 @@ from rpython.jit.metainterp.executor import _execute_arglist, wrap_constant from rpython.jit.metainterp.resoperation import opname from rpython.jit.codewriter import longlong -from rpython.rtyper.lltypesystem import lltype, rstr +from rpython.rtyper.lltypesystem import lltype, llmemory, rstr from rpython.rtyper import rclass -class PleaseRewriteMe(Exception): - pass class DummyLoop(object): def __init__(self, subops): @@ -27,6 +25,41 @@ def execute_raised(self, exc, constant=False): self._got_exc = exc + +def getint(v): + if isinstance(v, (ConstInt, InputArgInt)): + return v.getint() + else: + return v._example_int + +def getfloatstorage(v): + if isinstance(v, (ConstFloat, InputArgFloat)): + return v.getfloatstorage() + else: + return v._example_float + +def getfloat(v): + return longlong.getrealfloat(getfloatstorage(v)) + +def getref_base(v): + if isinstance(v, (ConstPtr, InputArgRef)): + return v.getref_base() + else: + return v._example_ref + +def getref(PTR, v): + return lltype.cast_opaque_ptr(PTR, getref_base(v)) + +def constbox(v): + if v.type == INT: + return ConstInt(getint(v)) + if v.type == FLOAT: + return ConstFloat(getfloatstorage(v)) + if v.type == REF: + return ConstPtr(getref_base(v)) + assert 0, v.type + + class OperationBuilder(object): def __init__(self, cpu, loop, vars): self.cpu = cpu @@ -57,11 +90,21 @@ def do(self, opnum, argboxes, descr=None): self.fakemetainterp._got_exc = None op = ResOperation(opnum, argboxes, descr) + argboxes = map(constbox, argboxes) result = _execute_arglist(self.cpu, self.fakemetainterp, opnum, argboxes, descr) if result is not None: - c_result = wrap_constant(result) - op.copy_value_from(c_result) + if lltype.typeOf(result) == lltype.Signed: + op._example_int = result + elif isinstance(result, bool): + op._example_int = int(result) + elif lltype.typeOf(result) == longlong.FLOATSTORAGE: + op._example_float = result + elif isinstance(result, float): + op._example_float = longlong.getfloatstorage(result) + else: + assert lltype.typeOf(result) == llmemory.GCREF + op._example_ref = result self.loop.operations.append(op) return op @@ -101,7 +144,7 @@ if v in names: args.append(names[v]) elif isinstance(v, ConstPtr): - assert not v.getref_base() # otherwise should be in the names + assert not getref_base(v) # otherwise should be in the names args.append('ConstPtr(lltype.nullptr(llmemory.GCREF.TO))') elif isinstance(v, ConstFloat): args.append('ConstFloat(longlong.getfloatstorage(%r))' @@ -198,10 +241,10 @@ # def writevar(v, nameprefix, init=''): if nameprefix == 'const_ptr': - if not v.getref_base(): + if not getref_base(v): return 'lltype.nullptr(llmemory.GCREF.TO)' - TYPE = v.getref_base()._obj.ORIGTYPE - cont = lltype.cast_opaque_ptr(TYPE, v.getref_base()) + TYPE = getref_base(v)._obj.ORIGTYPE + cont = lltype.cast_opaque_ptr(TYPE, getref_base(v)) if TYPE.TO._is_varsize(): if isinstance(TYPE.TO, lltype.GcStruct): lgt = len(cont.chars) @@ -252,9 +295,9 @@ for i, v in enumerate(self.loop.inputargs): assert not isinstance(v, Const) if v.type == FLOAT: - vals.append("longlong.getfloatstorage(%r)" % v.getfloat()) + vals.append("longlong.getfloatstorage(%r)" % getfloat(v)) else: - vals.append("%r" % v.getint()) + vals.append("%r" % getint(v)) print >>s, ' loop_args = [%s]' % ", ".join(vals) print >>s, ' frame = cpu.execute_token(looptoken, *loop_args)' if self.should_fail_by is None: @@ -264,10 +307,10 @@ for i, v in enumerate(fail_args): if v.type == FLOAT: print >>s, (' assert longlong.getrealfloat(' - 'cpu.get_float_value(frame, %d)) == %r' % (i, v.getfloatstorage())) + 'cpu.get_float_value(frame, %d)) == %r' % (i, getfloatstorage(v))) else: print >>s, (' assert cpu.get_int_value(frame, %d) == %d' - % (i, v.getint())) + % (i, getint(v))) self.names = names s.flush() @@ -295,7 +338,7 @@ builder.intvars.append(v_result) boolres = self.boolres if boolres == 'sometimes': - boolres = v_result.getint() in [0, 1] + boolres = getint(v_result) in [0, 1] if boolres: builder.boolvars.append(v_result) elif v_result.type == FLOAT: @@ -346,10 +389,10 @@ v_second = ConstInt((value & self.and_mask) | self.or_mask) else: v = r.choice(builder.intvars) - v_value = v.getint() + v_value = getint(v) if (v_value & self.and_mask) != v_value: v = builder.do(rop.INT_AND, [v, ConstInt(self.and_mask)]) - v_value = v.getint() + v_value = getint(v) if (v_value | self.or_mask) != v_value: v = builder.do(rop.INT_OR, [v, ConstInt(self.or_mask)]) v_second = v @@ -395,9 +438,9 @@ v_second = ConstFloat(r.random_float_storage()) else: v_second = r.choice(builder.floatvars) - if abs(v_first.getfloat()) > 1E100 or abs(v_second.getfloat()) > 1E100: + if abs(getfloat(v_first)) > 1E100 or abs(getfloat(v_second)) > 1E100: raise CannotProduceOperation # avoid infinities - if abs(v_second.getfloat()) < 1E-100: + if abs(getfloat(v_second)) < 1E-100: raise CannotProduceOperation # e.g. division by zero error self.put(builder, [v_first, v_second]) @@ -432,7 +475,7 @@ if not builder.floatvars: raise CannotProduceOperation box = r.choice(builder.floatvars) - if not (-sys.maxint-1 <= box.getfloat() <= sys.maxint): + if not (-sys.maxint-1 <= getfloat(box) <= sys.maxint): raise CannotProduceOperation # would give an overflow self.put(builder, [box]) @@ -440,8 +483,8 @@ def gen_guard(self, builder, r): v = builder.get_bool_var(r) op = ResOperation(self.opnum, [v]) - passing = ((self.opnum == rop.GUARD_TRUE and v.getint()) or - (self.opnum == rop.GUARD_FALSE and not v.getint())) + passing = ((self.opnum == rop.GUARD_TRUE and getint(v)) or + (self.opnum == rop.GUARD_FALSE and not getint(v))) return op, passing def produce_into(self, builder, r): @@ -459,8 +502,8 @@ raise CannotProduceOperation box = r.choice(builder.ptrvars)[0] op = ResOperation(self.opnum, [box]) - passing = ((self.opnum == rop.GUARD_NONNULL and box.getref_base()) or - (self.opnum == rop.GUARD_ISNULL and not box.getref_base())) + passing = ((self.opnum == rop.GUARD_NONNULL and getref_base(box)) or + (self.opnum == rop.GUARD_ISNULL and not getref_base(box))) return op, passing class GuardValueOperation(GuardOperation): @@ -470,14 +513,14 @@ other = r.choice(builder.intvars) else: if r.random() < 0.75: - value = v.getint() + value = getint(v) elif r.random() < 0.5: - value = v.getint() ^ 1 + value = getint(v) ^ 1 else: value = r.random_integer() other = ConstInt(value) op = ResOperation(self.opnum, [v, other]) - return op, (v.getint() == other.getint()) + return op, (getint(v) == getint(other)) # ____________________________________________________________ @@ -675,7 +718,7 @@ assert not hasattr(loop, '_targettoken') for i in range(position): op = loop.operations[i] - if (not op.has_no_side_effect() + if (not rop.has_no_side_effect(op.opnum) or op.type not in (INT, FLOAT)): position = i break # cannot move the LABEL later @@ -728,9 +771,9 @@ self.expected = {} for v in endvars: if v.type == INT: - self.expected[v] = v.getint() + self.expected[v] = getint(v) elif v.type == FLOAT: - self.expected[v] = v.getfloatstorage() + self.expected[v] = getfloatstorage(v) else: assert 0, v.type @@ -742,7 +785,7 @@ args = [] for box in self.startvars: if box not in self.loop.inputargs: - box = box.constbox() + box = constbox(box) args.append(box) self.cpu.compile_loop(self.loop.inputargs, [ResOperation(rop.JUMP, args, @@ -760,7 +803,7 @@ def clear_state(self): for v, S, fields in self.prebuilt_ptr_consts: - container = v.getref_base()._obj.container + container = getref_base(v)._obj.container for name, value in fields.items(): if isinstance(name, str): setattr(container, name, value) @@ -781,9 +824,9 @@ arguments = [] for box in self.loop.inputargs: if box.type == INT: - arguments.append(box.getint()) + arguments.append(getint(box)) elif box.type == FLOAT: - arguments.append(box.getfloatstorage()) + arguments.append(getfloatstorage(box)) else: assert 0, box.type deadframe = cpu.execute_token(self.runjitcelltoken(), *arguments) @@ -795,7 +838,7 @@ if v not in self.expected: assert v.getopnum() == rop.SAME_AS_I # special case assert isinstance(v.getarg(0), ConstInt) - self.expected[v] = v.getarg(0).getint() + self.expected[v] = getint(v.getarg(0)) if v.type == FLOAT: value = cpu.get_float_value(deadframe, i) else: @@ -807,7 +850,7 @@ ) exc = cpu.grab_exc_value(deadframe) if (self.guard_op is not None and - self.guard_op.is_guard_exception()): + rop.is_guard_exception(self.guard_op.getopnum())): if self.guard_op.getopnum() == rop.GUARD_NO_EXCEPTION: do_assert(exc, "grab_exc_value() should not be %r" % (exc,)) @@ -840,7 +883,7 @@ # generate the branch: a sequence of operations that ends in a FINISH subloop = DummyLoop([]) self.subloops.append(subloop) # keep around for debugging - if guard_op.is_guard_exception(): + if rop.is_guard_exception(guard_op.getopnum()): subloop.operations.append(exc_handling(guard_op)) bridge_builder = self.builder.fork(self.builder.cpu, subloop, op.getfailargs()[:]) @@ -876,9 +919,9 @@ args = [] for x in subset: if x.type == INT: - args.append(InputArgInt(x.getint())) + args.append(InputArgInt(getint(x))) elif x.type == FLOAT: - args.append(InputArgFloat(x.getfloatstorage())) + args.append(InputArgFloat(getfloatstorage(x))) else: assert 0, x.type rl = RandomLoop(self.builder.cpu, self.builder.fork, 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 @@ -358,11 +358,11 @@ assert self.assembler.mc._frame_size == DEFAULT_FRAME_BYTES self.rm.position = i self.xrm.position = i - if op.has_no_side_effect() and op not in self.longevity: + if rop.has_no_side_effect(op.opnum) and op not in self.longevity: i += 1 self.possibly_free_vars_for_op(op) continue - if not we_are_translated() and op.getopnum() == -127: + if not we_are_translated() and op.getopnum() == rop.FORCE_SPILL: self._consider_force_spill(op) else: oplist[op.getopnum()](self, op) diff --git a/rpython/jit/backend/zarch/regalloc.py b/rpython/jit/backend/zarch/regalloc.py --- a/rpython/jit/backend/zarch/regalloc.py +++ b/rpython/jit/backend/zarch/regalloc.py @@ -476,7 +476,8 @@ self.assembler.mc.mark_op(op) self.rm.position = i self.fprm.position = i - if op.has_no_side_effect() and op not in self.longevity: + opnum = op.getopnum() + if rop.has_no_side_effect(opnum) and op not in self.longevity: i += 1 self.possibly_free_vars_for_op(op) continue @@ -488,8 +489,7 @@ else: self.fprm.temp_boxes.append(box) # - opnum = op.getopnum() - if not we_are_translated() and opnum == -127: + if not we_are_translated() and opnum == rop.FORCE_SPILL: self._consider_force_spill(op) else: arglocs = prepare_oplist[opnum](self, op) diff --git a/rpython/jit/metainterp/blackhole.py b/rpython/jit/metainterp/blackhole.py --- a/rpython/jit/metainterp/blackhole.py +++ b/rpython/jit/metainterp/blackhole.py @@ -1585,7 +1585,6 @@ def _done_with_this_frame(self): # rare case: we only get there if the blackhole interps all returned # normally (in general we get a ContinueRunningNormally exception). - sd = self.builder.metainterp_sd kind = self._return_type if kind == 'v': raise jitexc.DoneWithThisFrameVoid() diff --git a/rpython/jit/metainterp/compile.py b/rpython/jit/metainterp/compile.py --- a/rpython/jit/metainterp/compile.py +++ b/rpython/jit/metainterp/compile.py @@ -27,12 +27,11 @@ class CompileData(object): memo = None + log_noopt = True def forget_optimization_info(self): - for arg in self.start_label.getarglist(): + for arg in self.trace.inputargs: arg.set_forwarded(None) - for op in self.operations: - op.set_forwarded(None) class LoopCompileData(CompileData): """ An object that accumulates all of the necessary info for @@ -40,15 +39,13 @@ This is the case of label() ops label() """ - def __init__(self, start_label, end_label, operations, - call_pure_results=None, enable_opts=None): - self.start_label = start_label - self.end_label = end_label + def __init__(self, trace, runtime_boxes, call_pure_results=None, + enable_opts=None): self.enable_opts = enable_opts - assert start_label.getopnum() == rop.LABEL - assert end_label.getopnum() == rop.LABEL - self.operations = operations + self.trace = trace self.call_pure_results = call_pure_results + assert runtime_boxes is not None + self.runtime_boxes = runtime_boxes def optimize(self, metainterp_sd, jitdriver_sd, optimizations, unroll): from rpython.jit.metainterp.optimizeopt.unroll import (UnrollOptimizer, @@ -56,23 +53,21 @@ if unroll: opt = UnrollOptimizer(metainterp_sd, jitdriver_sd, optimizations) - return opt.optimize_preamble(self.start_label, self.end_label, - self.operations, + return opt.optimize_preamble(self.trace, + self.runtime_boxes, self.call_pure_results, self.box_names_memo) else: opt = Optimizer(metainterp_sd, jitdriver_sd, optimizations) - return opt.propagate_all_forward(self.start_label.getarglist(), - self.operations, self.call_pure_results) + return opt.propagate_all_forward(self.trace, self.call_pure_results) class SimpleCompileData(CompileData): """ This represents label() ops jump with no extra info associated with the label """ - def __init__(self, start_label, operations, call_pure_results=None, + def __init__(self, trace, call_pure_results=None, enable_opts=None): - self.start_label = start_label - self.operations = operations + self.trace = trace self.call_pure_results = call_pure_results self.enable_opts = enable_opts @@ -81,17 +76,17 @@ #assert not unroll opt = Optimizer(metainterp_sd, jitdriver_sd, optimizations) - return opt.propagate_all_forward(self.start_label.getarglist(), - self.operations, self.call_pure_results) + return opt.propagate_all_forward(self.trace.get_iter(), + self.call_pure_results) class BridgeCompileData(CompileData): """ This represents ops() with a jump at the end that goes to some loop, we need to deal with virtual state and inlining of short preamble """ - def __init__(self, start_label, operations, call_pure_results=None, + def __init__(self, trace, runtime_boxes, call_pure_results=None, enable_opts=None, inline_short_preamble=False): - self.start_label = start_label - self.operations = operations + self.trace = trace + self.runtime_boxes = runtime_boxes self.call_pure_results = call_pure_results self.enable_opts = enable_opts self.inline_short_preamble = inline_short_preamble @@ -100,7 +95,7 @@ from rpython.jit.metainterp.optimizeopt.unroll import UnrollOptimizer opt = UnrollOptimizer(metainterp_sd, jitdriver_sd, optimizations) - return opt.optimize_bridge(self.start_label, self.operations, + return opt.optimize_bridge(self.trace, self.runtime_boxes, self.call_pure_results, self.inline_short_preamble, self.box_names_memo) @@ -109,12 +104,13 @@ """ This represents label() ops jump with extra info that's from the run of LoopCompileData. Jump goes to the same label """ - def __init__(self, start_label, end_jump, operations, state, + log_noopt = False + + def __init__(self, trace, celltoken, state, call_pure_results=None, enable_opts=None, inline_short_preamble=True): - self.start_label = start_label - self.end_jump = end_jump - self.operations = operations + self.trace = trace + self.celltoken = celltoken self.enable_opts = enable_opts self.state = state self.call_pure_results = call_pure_results @@ -125,9 +121,8 @@ assert unroll # we should not be here if it's disabled opt = UnrollOptimizer(metainterp_sd, jitdriver_sd, optimizations) - return opt.optimize_peeled_loop(self.start_label, self.end_jump, - self.operations, self.state, self.call_pure_results, - self.inline_short_preamble) + return opt.optimize_peeled_loop(self.trace, self.celltoken, self.state, + self.call_pure_results, self.inline_short_preamble) def show_procedures(metainterp_sd, procedure=None, error=None): # debugging @@ -208,23 +203,21 @@ # ____________________________________________________________ -def compile_simple_loop(metainterp, greenkey, start, inputargs, ops, jumpargs, - enable_opts): +def compile_simple_loop(metainterp, greenkey, trace, runtime_args, enable_opts, + cut_at): from rpython.jit.metainterp.optimizeopt import optimize_trace jitdriver_sd = metainterp.jitdriver_sd metainterp_sd = metainterp.staticdata jitcell_token = make_jitcell_token(jitdriver_sd) - label = ResOperation(rop.LABEL, inputargs[:], descr=jitcell_token) - jump_op = ResOperation(rop.JUMP, jumpargs[:], descr=jitcell_token) call_pure_results = metainterp.call_pure_results - data = SimpleCompileData(label, ops + [jump_op], - call_pure_results=call_pure_results, - enable_opts=enable_opts) + data = SimpleCompileData(trace, call_pure_results=call_pure_results, + enable_opts=enable_opts) try: loop_info, ops = optimize_trace(metainterp_sd, jitdriver_sd, data, metainterp.box_names_memo) except InvalidLoop: + trace.cut_at(cut_at) return None loop = create_empty_loop(metainterp) loop.original_jitcell_token = jitcell_token @@ -241,7 +234,7 @@ loop.check_consistency() jitcell_token.target_tokens = [target_token] send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd, loop, "loop", - inputargs, metainterp.box_names_memo) + runtime_args, metainterp.box_names_memo) record_loop_or_bridge(metainterp_sd, loop) return target_token @@ -255,6 +248,7 @@ metainterp_sd = metainterp.staticdata jitdriver_sd = metainterp.jitdriver_sd history = metainterp.history + trace = history.trace warmstate = jitdriver_sd.warmstate enable_opts = jitdriver_sd.warmstate.enable_opts @@ -264,16 +258,16 @@ enable_opts = enable_opts.copy() del enable_opts['unroll'] - ops = history.operations[start:] + jitcell_token = make_jitcell_token(jitdriver_sd) + cut_at = history.get_trace_position() + history.record(rop.JUMP, jumpargs, None, descr=jitcell_token) + if start != (0, 0, 0): + trace = trace.cut_trace_from(start, inputargs) if 'unroll' not in enable_opts or not metainterp.cpu.supports_guard_gc_type: - return compile_simple_loop(metainterp, greenkey, start, inputargs, ops, - jumpargs, enable_opts) - jitcell_token = make_jitcell_token(jitdriver_sd) - label = ResOperation(rop.LABEL, inputargs, - descr=TargetToken(jitcell_token)) - end_label = ResOperation(rop.LABEL, jumpargs, descr=jitcell_token) + return compile_simple_loop(metainterp, greenkey, trace, jumpargs, + enable_opts, cut_at) call_pure_results = metainterp.call_pure_results - preamble_data = LoopCompileData(label, end_label, ops, + preamble_data = LoopCompileData(trace, jumpargs, call_pure_results=call_pure_results, enable_opts=enable_opts) try: @@ -281,17 +275,15 @@ preamble_data, metainterp.box_names_memo) except InvalidLoop: + history.cut(cut_at) return None metainterp_sd = metainterp.staticdata jitdriver_sd = metainterp.jitdriver_sd - end_label = ResOperation(rop.LABEL, inputargs, - descr=jitcell_token) - jump_op = ResOperation(rop.JUMP, jumpargs, descr=jitcell_token) start_descr = TargetToken(jitcell_token, original_jitcell_token=jitcell_token) jitcell_token.target_tokens = [start_descr] - loop_data = UnrolledLoopData(end_label, jump_op, ops, start_state, + loop_data = UnrolledLoopData(trace, jitcell_token, start_state, call_pure_results=call_pure_results, enable_opts=enable_opts) try: @@ -299,11 +291,12 @@ loop_data, metainterp.box_names_memo) except InvalidLoop: + history.cut(cut_at) return None if ((warmstate.vec and jitdriver_sd.vec) or warmstate.vec_all): from rpython.jit.metainterp.optimizeopt.vector import optimize_vector - loop_info, loop_ops = optimize_vector(metainterp_sd, + loop_info, loop_ops = optimize_vector(trace, metainterp_sd, jitdriver_sd, warmstate, loop_info, loop_ops, jitcell_token) @@ -342,22 +335,20 @@ to the first operation. """ from rpython.jit.metainterp.optimizeopt import optimize_trace - from rpython.jit.metainterp.optimizeopt.optimizer import BasicLoopInfo - history = metainterp.history + trace = metainterp.history.trace.cut_trace_from(start, inputargs) metainterp_sd = metainterp.staticdata jitdriver_sd = metainterp.jitdriver_sd + history = metainterp.history loop_jitcell_token = metainterp.get_procedure_token(greenkey) assert loop_jitcell_token - end_label = ResOperation(rop.LABEL, inputargs[:], - descr=loop_jitcell_token) - jump_op = ResOperation(rop.JUMP, jumpargs[:], descr=loop_jitcell_token) + cut = history.get_trace_position() + history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token) enable_opts = jitdriver_sd.warmstate.enable_opts - ops = history.operations[start:] call_pure_results = metainterp.call_pure_results - loop_data = UnrolledLoopData(end_label, jump_op, ops, start_state, + loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state, call_pure_results=call_pure_results, enable_opts=enable_opts) try: @@ -366,8 +357,9 @@ metainterp.box_names_memo) except InvalidLoop: # Fall back on jumping directly to preamble - jump_op = ResOperation(rop.JUMP, inputargs[:], descr=loop_jitcell_token) - loop_data = UnrolledLoopData(end_label, jump_op, [jump_op], start_state, + history.cut(cut) + history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token) + loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state, call_pure_results=call_pure_results, enable_opts=enable_opts, inline_short_preamble=False) @@ -376,9 +368,13 @@ loop_data, metainterp.box_names_memo) except InvalidLoop: + history.cut(cut) return None - label_token = loop_info.label_op.getdescr() + label_op = loop_info.label_op + if label_op is None: + assert False, "unreachable code" # hint for some strange tests + label_token = label_op.getdescr() assert isinstance(label_token, TargetToken) if label_token.short_preamble: metainterp_sd.logger_ops.log_short_preamble([], @@ -445,13 +441,13 @@ box = inputargs[i] opnum = OpHelpers.getfield_for_descr(descr) emit_op(extra_ops, - ResOperation(opnum, [vable_box], descr)) + ResOperation(opnum, [vable_box], descr=descr)) box.set_forwarded(extra_ops[-1]) i += 1 arrayindex = 0 for descr in vinfo.array_field_descrs: arraylen = vinfo.get_array_length(vable, arrayindex) - arrayop = ResOperation(rop.GETFIELD_GC_R, [vable_box], descr) + arrayop = ResOperation(rop.GETFIELD_GC_R, [vable_box], descr=descr) emit_op(extra_ops, arrayop) arraydescr = vinfo.array_descrs[arrayindex] assert i + arraylen <= len(inputargs) @@ -1017,9 +1013,9 @@ metainterp_sd.stats.add_jitcell_token(jitcell_token) -def compile_trace(metainterp, resumekey): +def compile_trace(metainterp, resumekey, runtime_boxes): """Try to compile a new bridge leading from the beginning of the history - to some existing place. + to some existging place. """ from rpython.jit.metainterp.optimizeopt import optimize_trace @@ -1037,20 +1033,19 @@ else: inline_short_preamble = True inputargs = metainterp.history.inputargs[:] - operations = metainterp.history.operations - label = ResOperation(rop.LABEL, inputargs) + trace = metainterp.history.trace jitdriver_sd = metainterp.jitdriver_sd enable_opts = jitdriver_sd.warmstate.enable_opts call_pure_results = metainterp.call_pure_results - if operations[-1].getopnum() == rop.JUMP: - data = BridgeCompileData(label, operations[:], + if metainterp.history.ends_with_jump: + data = BridgeCompileData(trace, runtime_boxes, call_pure_results=call_pure_results, enable_opts=enable_opts, inline_short_preamble=inline_short_preamble) else: - data = SimpleCompileData(label, operations[:], + data = SimpleCompileData(trace, call_pure_results=call_pure_results, enable_opts=enable_opts) try: 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 @@ -9,7 +9,7 @@ from rpython.jit.metainterp.history import INT, REF, FLOAT, VOID, AbstractDescr from rpython.jit.metainterp.history import ConstInt, ConstFloat, ConstPtr from rpython.jit.metainterp import resoperation -from rpython.jit.metainterp.resoperation import rop +from rpython.jit.metainterp.resoperation import rop, opname from rpython.jit.metainterp.blackhole import BlackholeInterpreter, NULL from rpython.jit.codewriter import longlong @@ -314,7 +314,8 @@ def _make_execute_list(): execute_by_num_args = {} - for key, value in rop.__dict__.items(): + for key in opname.values(): + value = getattr(rop, key) if not key.startswith('_'): if (rop._FINAL_FIRST <= value <= rop._FINAL_LAST or rop._GUARD_FIRST <= value <= rop._GUARD_LAST): @@ -384,6 +385,11 @@ rop.CALL_MALLOC_NURSERY_VARSIZE_FRAME, rop.NURSERY_PTR_INCREMENT, rop.LABEL, + rop.ESCAPE_I, + rop.ESCAPE_N, + rop.ESCAPE_R, + rop.ESCAPE_F, + rop.FORCE_SPILL, rop.SAVE_EXC_CLASS, rop.SAVE_EXCEPTION, rop.RESTORE_EXCEPTION, diff --git a/rpython/jit/metainterp/graphpage.py b/rpython/jit/metainterp/graphpage.py --- a/rpython/jit/metainterp/graphpage.py +++ b/rpython/jit/metainterp/graphpage.py @@ -170,7 +170,8 @@ while True: op = operations[opindex] op_repr = op.repr(self.memo, graytext=True) - if op.getopnum() == rop.DEBUG_MERGE_POINT: + if (op.getopnum() == rop.DEBUG_MERGE_POINT and + self.metainterp_sd is not None): jd_sd = self.metainterp_sd.jitdrivers_sd[op.getarg(0).getint()] if jd_sd._get_printable_location_ptr: s = jd_sd.warmstate.get_location_str(op.getarglist()[3:]) diff --git a/rpython/jit/metainterp/heapcache.py b/rpython/jit/metainterp/heapcache.py --- a/rpython/jit/metainterp/heapcache.py +++ b/rpython/jit/metainterp/heapcache.py @@ -1,33 +1,59 @@ -from rpython.jit.metainterp.history import ConstInt +from rpython.jit.metainterp.history import Const, ConstInt +from rpython.jit.metainterp.history import FrontendOp, RefFrontendOp from rpython.jit.metainterp.resoperation import rop, OpHelpers +from rpython.jit.metainterp.executor import constant_from_op +from rpython.rlib.rarithmetic import r_uint32, r_uint +from rpython.rlib.objectmodel import always_inline -class HeapCacheValue(object): - def __init__(self, box): - self.box = box - self.likely_virtual = False - self.reset_keep_likely_virtual() +""" A big note: we don't do heap caches on Consts, because it used +to be done with the identity of the Const instance. This gives very wonky +results at best, so we decided to not do it at all. Can be fixed with +interning of Consts (already done on trace anyway) +""" - def reset_keep_likely_virtual(self): - self.known_class = False - self.known_nullity = False - # did we see the allocation during tracing? - self.seen_allocation = False - self.is_unescaped = False - self.nonstandard_virtualizable = False - self.length = None - self.dependencies = None +# RefFrontendOp._heapc_flags: +HF_LIKELY_VIRTUAL = 0x01 +HF_KNOWN_CLASS = 0x02 +HF_KNOWN_NULLITY = 0x04 +HF_SEEN_ALLOCATION = 0x08 # did we see the allocation during tracing? +HF_IS_UNESCAPED = 0x10 +HF_NONSTD_VABLE = 0x20 - def __repr__(self): - return 'HeapCacheValue(%s)' % (self.box, ) +_HF_VERSION_INC = 0x40 # must be last +_HF_VERSION_MAX = r_uint(2 ** 32 - _HF_VERSION_INC) + +@always_inline +def add_flags(ref_frontend_op, flags): + f = ref_frontend_op._get_heapc_flags() + f |= r_uint(flags) + ref_frontend_op._set_heapc_flags(f) + +@always_inline +def remove_flags(ref_frontend_op, flags): + f = ref_frontend_op._get_heapc_flags() + f &= r_uint(~flags) + ref_frontend_op._set_heapc_flags(f) + +@always_inline +def test_flags(ref_frontend_op, flags): + f = ref_frontend_op._get_heapc_flags() + return bool(f & r_uint(flags)) + +def maybe_replace_with_const(box): + if not isinstance(box, Const) and box.is_replaced_with_const(): + return constant_from_op(box) + else: + return box class CacheEntry(object): - def __init__(self): - # both are {from_value: to_value} dicts + def __init__(self, heapcache): + # both are {from_ref_box: to_field_box} dicts # the first is for boxes where we did not see the allocation, the # second for anything else. the reason that distinction makes sense is # because if we saw the allocation, we know it cannot alias with # anything else where we saw the allocation. + self.heapcache = heapcache self.cache_anything = {} self.cache_seen_allocation = {} @@ -36,112 +62,137 @@ self.cache_seen_allocation.clear() self.cache_anything.clear() - def _getdict(self, value): - if value.seen_allocation: + def _seen_alloc(self, ref_box): + if not isinstance(ref_box, RefFrontendOp): + return False + return self.heapcache._check_flag(ref_box, HF_SEEN_ALLOCATION) + + def _getdict(self, seen_alloc): + if seen_alloc: return self.cache_seen_allocation else: return self.cache_anything - def do_write_with_aliasing(self, value, fieldvalue): - self._clear_cache_on_write(value.seen_allocation) - self._getdict(value)[value] = fieldvalue + def do_write_with_aliasing(self, ref_box, fieldbox): + seen_alloc = self._seen_alloc(ref_box) + self._clear_cache_on_write(seen_alloc) + self._getdict(seen_alloc)[ref_box] = fieldbox - def read(self, value): - return self._getdict(value).get(value, None) + def read(self, ref_box): + dict = self._getdict(self._seen_alloc(ref_box)) + try: + res_box = dict[ref_box] + except KeyError: + return None + return maybe_replace_with_const(res_box) - def read_now_known(self, value, fieldvalue): - self._getdict(value)[value] = fieldvalue + def read_now_known(self, ref_box, fieldbox): + self._getdict(self._seen_alloc(ref_box))[ref_box] = fieldbox def invalidate_unescaped(self): self._invalidate_unescaped(self.cache_anything) self._invalidate_unescaped(self.cache_seen_allocation) def _invalidate_unescaped(self, d): - for value in d.keys(): - if not value.is_unescaped: - del d[value] + for ref_box in d.keys(): + if not self.heapcache.is_unescaped(ref_box): + del d[ref_box] class FieldUpdater(object): - def __init__(self, heapcache, value, cache, fieldvalue): - self.heapcache = heapcache - self.value = value + def __init__(self, ref_box, cache, fieldbox): + self.ref_box = ref_box self.cache = cache - if fieldvalue is not None: - self.currfieldbox = fieldvalue.box - else: - self.currfieldbox = None + self.currfieldbox = fieldbox # <= read directly from pyjitpl.py def getfield_now_known(self, fieldbox): - fieldvalue = self.heapcache.getvalue(fieldbox) - self.cache.read_now_known(self.value, fieldvalue) + self.cache.read_now_known(self.ref_box, fieldbox) def setfield(self, fieldbox): - fieldvalue = self.heapcache.getvalue(fieldbox) - self.cache.do_write_with_aliasing(self.value, fieldvalue) + self.cache.do_write_with_aliasing(self.ref_box, fieldbox) + +class DummyFieldUpdater(FieldUpdater): + def __init__(self): + self.currfieldbox = None + + def getfield_now_known(self, fieldbox): + pass + + def setfield(self, fieldbox): + pass + +dummy_field_updater = DummyFieldUpdater() class HeapCache(object): def __init__(self): + # Works with flags stored on RefFrontendOp._heapc_flags. + # There are two ways to do a global resetting of these flags: + # reset() and reset_keep_likely_virtual(). The basic idea is + # to use a version number in each RefFrontendOp, and in order + # to reset the flags globally, we increment the global version + # number in this class. Then when we read '_heapc_flags' we + # also check if the associated version number is up-to-date + # or not. More precisely, we have two global version numbers + # here: 'head_version' and 'likely_virtual_version'. Normally + # we use 'head_version'. For is_likely_virtual() though, we + # use the other, older version number. + self.head_version = r_uint(0) + self.likely_virtual_version = r_uint(0) self.reset() def reset(self): - # maps boxes to values - self.values = {} - # store the boxes that contain newly allocated objects, this maps the - # boxes to a bool, the bool indicates whether or not the object has - # escaped the trace or not (True means the box never escaped, False - # means it did escape), its presences in the mapping shows that it was - # allocated inside the trace - #if trace_branch: - #self.new_boxes = {} - # pass - #else: - #for box in self.new_boxes: - # self.new_boxes[box] = False - # pass - #if reset_virtuals: - # self.likely_virtuals = {} # only for jit.isvirtual() - # Tracks which boxes should be marked as escaped when the key box - # escapes. - #self.dependencies = {} - + # Global reset of all flags. Update both version numbers so + # that any access to '_heapc_flags' will be marked as outdated. + assert self.head_version < _HF_VERSION_MAX + self.head_version += _HF_VERSION_INC + self.likely_virtual_version = self.head_version + # # heap cache # maps descrs to CacheEntry self.heap_cache = {} # heap array cache - # maps descrs to {index: {from_value: to_value}} dicts + # maps descrs to {index: CacheEntry} dicts self.heap_array_cache = {} def reset_keep_likely_virtuals(self): - for value in self.values.itervalues(): - value.reset_keep_likely_virtual() + # Update only 'head_version', but 'likely_virtual_version' remains + # at its older value. + assert self.head_version < _HF_VERSION_MAX + self.head_version += _HF_VERSION_INC self.heap_cache = {} self.heap_array_cache = {} - def getvalue(self, box, create=True): - value = self.values.get(box, None) - if not value and create: - value = self.values[box] = HeapCacheValue(box) - return value + @always_inline + def test_head_version(self, ref_frontend_op): + return ref_frontend_op._get_heapc_flags() >= self.head_version - def getvalues(self, boxes): - return [self.getvalue(box) for box in boxes] + @always_inline + def test_likely_virtual_version(self, ref_frontend_op): + return ref_frontend_op._get_heapc_flags() >= self.likely_virtual_version + + def update_version(self, ref_frontend_op): + """Ensure the version of 'ref_frontend_op' is current. If not, + it will update 'ref_frontend_op' (removing most flags currently set). + """ + if not self.test_head_version(ref_frontend_op): + f = self.head_version + if (self.test_likely_virtual_version(ref_frontend_op) and + test_flags(ref_frontend_op, HF_LIKELY_VIRTUAL)): + f |= HF_LIKELY_VIRTUAL + ref_frontend_op._set_heapc_flags(f) + ref_frontend_op._heapc_deps = None def invalidate_caches(self, opnum, descr, argboxes): self.mark_escaped(opnum, descr, argboxes) self.clear_caches(opnum, descr, argboxes) def _escape_from_write(self, box, fieldbox): - value = self.getvalue(box, create=False) - fieldvalue = self.getvalue(fieldbox, create=False) - if (value is not None and value.is_unescaped and - fieldvalue is not None and fieldvalue.is_unescaped): - if value.dependencies is None: - value.dependencies = [] - value.dependencies.append(fieldvalue) - elif fieldvalue is not None: - self._escape(fieldvalue) + if self.is_unescaped(box) and self.is_unescaped(fieldbox): + deps = self._get_deps(box) + deps.append(fieldbox) + elif fieldbox is not None: + self._escape_box(fieldbox) def mark_escaped(self, opnum, descr, argboxes): if opnum == rop.SETFIELD_GC: @@ -176,19 +227,20 @@ self._escape_box(box) def _escape_box(self, box): - value = self.getvalue(box, create=False) - if not value: - return - self._escape(value) - - def _escape(self, value): - value.is_unescaped = False - value.likely_virtual = False - deps = value.dependencies - value.dependencies = None - if deps is not None: - for dep in deps: - self._escape(dep) + if isinstance(box, RefFrontendOp): + remove_flags(box, HF_LIKELY_VIRTUAL | HF_IS_UNESCAPED) + deps = box._heapc_deps + if deps is not None: + if not self.test_head_version(box): + box._heapc_deps = None + else: + # 'deps[0]' is abused to store the array length, keep it + if deps[0] is None: + box._heapc_deps = None + else: + box._heapc_deps = [deps[0]] + for i in range(1, len(deps)): + self._escape_box(deps[i]) def clear_caches(self, opnum, descr, argboxes): if (opnum == rop.SETFIELD_GC or @@ -241,7 +293,8 @@ self.reset_keep_likely_virtuals() def _clear_caches_arraycopy(self, opnum, desrc, argboxes, effectinfo): - seen_allocation_of_target = self.getvalue(argboxes[2]).seen_allocation + seen_allocation_of_target = self._check_flag( + argboxes[2], HF_SEEN_ALLOCATION) if ( isinstance(argboxes[3], ConstInt) and isinstance(argboxes[4], ConstInt) and @@ -285,74 +338,82 @@ return self.reset_keep_likely_virtuals() + def _get_deps(self, box): + if not isinstance(box, RefFrontendOp): + return None + self.update_version(box) + if box._heapc_deps is None: + box._heapc_deps = [None] + return box._heapc_deps + + def _check_flag(self, box, flag): + return (isinstance(box, RefFrontendOp) and + self.test_head_version(box) and + test_flags(box, flag)) + + def _set_flag(self, box, flag): + assert isinstance(box, RefFrontendOp) + self.update_version(box) + add_flags(box, flag) + def is_class_known(self, box): - value = self.getvalue(box, create=False) - if value: - return value.known_class - return False + return self._check_flag(box, HF_KNOWN_CLASS) def class_now_known(self, box): - self.getvalue(box).known_class = True + if isinstance(box, Const): + return + self._set_flag(box, HF_KNOWN_CLASS) def is_nullity_known(self, box): - value = self.getvalue(box, create=False) - if value: - return value.known_nullity - return False + if isinstance(box, Const): + return bool(box.getref_base()) + return self._check_flag(box, HF_KNOWN_NULLITY) def nullity_now_known(self, box): - self.getvalue(box).known_nullity = True + if isinstance(box, Const): + return + self._set_flag(box, HF_KNOWN_NULLITY) def is_nonstandard_virtualizable(self, box): - value = self.getvalue(box, create=False) - if value: - return value.nonstandard_virtualizable - return False + return self._check_flag(box, HF_NONSTD_VABLE) def nonstandard_virtualizables_now_known(self, box): - self.getvalue(box).nonstandard_virtualizable = True + self._set_flag(box, HF_NONSTD_VABLE) def is_unescaped(self, box): - value = self.getvalue(box, create=False) - if value: - return value.is_unescaped - return False + return self._check_flag(box, HF_IS_UNESCAPED) def is_likely_virtual(self, box): - value = self.getvalue(box, create=False) - if value: - return value.likely_virtual - return False + # note: this is different from _check_flag() + return (isinstance(box, RefFrontendOp) and + self.test_likely_virtual_version(box) and + test_flags(box, HF_LIKELY_VIRTUAL)) def new(self, box): - value = self.getvalue(box) - value.is_unescaped = True - value.likely_virtual = True - value.seen_allocation = True + assert isinstance(box, RefFrontendOp) + self.update_version(box) + add_flags(box, HF_LIKELY_VIRTUAL | HF_SEEN_ALLOCATION | HF_IS_UNESCAPED) def new_array(self, box, lengthbox): self.new(box) self.arraylen_now_known(box, lengthbox) def getfield(self, box, descr): - value = self.getvalue(box, create=False) - if value: - cache = self.heap_cache.get(descr, None) - if cache: - tovalue = cache.read(value) - if tovalue: - return tovalue.box + cache = self.heap_cache.get(descr, None) + if cache: + return cache.read(box) return None def get_field_updater(self, box, descr): - value = self.getvalue(box) + if not isinstance(box, RefFrontendOp): + return dummy_field_updater cache = self.heap_cache.get(descr, None) if cache is None: - cache = self.heap_cache[descr] = CacheEntry() - fieldvalue = None + cache = self.heap_cache[descr] = CacheEntry(self) + fieldbox = None else: - fieldvalue = cache.read(value) - return FieldUpdater(self, value, cache, fieldvalue) + fieldbox = cache.read(box) + return FieldUpdater(box, cache, fieldbox) def getfield_now_known(self, box, descr, fieldbox): upd = self.get_field_updater(box, descr) @@ -365,17 +426,12 @@ def getarrayitem(self, box, indexbox, descr): if not isinstance(indexbox, ConstInt): return None - value = self.getvalue(box, create=False) - if value is None: - return None index = indexbox.getint() cache = self.heap_array_cache.get(descr, None) if cache: indexcache = cache.get(index, None) if indexcache is not None: - resvalue = indexcache.read(value) - if resvalue: - return resvalue.box + return indexcache.read(box) return None def _get_or_make_array_cache_entry(self, indexbox, descr): @@ -385,16 +441,14 @@ cache = self.heap_array_cache.setdefault(descr, {}) indexcache = cache.get(index, None) if indexcache is None: - cache[index] = indexcache = CacheEntry() + cache[index] = indexcache = CacheEntry(self) return indexcache def getarrayitem_now_known(self, box, indexbox, fieldbox, descr): - value = self.getvalue(box) - fieldvalue = self.getvalue(fieldbox) indexcache = self._get_or_make_array_cache_entry(indexbox, descr) if indexcache: - indexcache.read_now_known(value, fieldvalue) + indexcache.read_now_known(box, fieldbox) def setarrayitem(self, box, indexbox, fieldbox, descr): if not isinstance(indexbox, ConstInt): @@ -402,25 +456,31 @@ if cache is not None: cache.clear() return - value = self.getvalue(box) - fieldvalue = self.getvalue(fieldbox) indexcache = self._get_or_make_array_cache_entry(indexbox, descr) if indexcache: - indexcache.do_write_with_aliasing(value, fieldvalue) + indexcache.do_write_with_aliasing(box, fieldbox) def arraylen(self, box): - value = self.getvalue(box, create=False) - if value and value.length: - return value.length.box + if (isinstance(box, RefFrontendOp) and + self.test_head_version(box) and + box._heapc_deps is not None): + res_box = box._heapc_deps[0] + if res_box is not None: + return maybe_replace_with_const(res_box) return None def arraylen_now_known(self, box, lengthbox): - value = self.getvalue(box) - value.length = self.getvalue(lengthbox) + # we store in '_heapc_deps' a list of boxes: the *first* box is + # the known length or None, and the remaining boxes are the + # regular dependencies. + if isinstance(box, Const): + return + deps = self._get_deps(box) + assert deps is not None + deps[0] = lengthbox def replace_box(self, oldbox, newbox): - value = self.getvalue(oldbox, create=False) - if value is None: - return - value.box = newbox - self.values[newbox] = value + # here, only for replacing a box with a const + if isinstance(oldbox, FrontendOp) and isinstance(newbox, Const): + assert newbox.same_constant(constant_from_op(oldbox)) + oldbox.set_replaced_with_const() diff --git a/rpython/jit/metainterp/history.py b/rpython/jit/metainterp/history.py --- a/rpython/jit/metainterp/history.py +++ b/rpython/jit/metainterp/history.py @@ -3,12 +3,17 @@ from rpython.rlib.objectmodel import we_are_translated, Symbolic from rpython.rlib.objectmodel import compute_unique_id, specialize from rpython.rlib.rarithmetic import r_int64, is_valid_int +from rpython.rlib.rarithmetic import LONG_BIT, intmask, r_uint +from rpython.rlib.jit import Counters from rpython.conftest import option -from rpython.jit.metainterp.resoperation import ResOperation, rop, AbstractValue +from rpython.jit.metainterp.resoperation import ResOperation, rop,\ + AbstractValue, oparity, AbstractResOp, IntOp, RefOp, FloatOp,\ + opclasses from rpython.jit.codewriter import heaptracker, longlong import weakref +from rpython.jit.metainterp import jitexc # ____________________________________________________________ @@ -22,6 +27,15 @@ FAILARGS_LIMIT = 1000 +class SwitchToBlackhole(jitexc.JitException): + def __init__(self, reason, raising_exception=False): + self.reason = reason + self.raising_exception = raising_exception + # ^^^ must be set to True if the SwitchToBlackhole is raised at a + # point where the exception on metainterp.last_exc_value + # is supposed to be raised. The default False means that it + # should just be copied into the blackhole interp, but not raised. + def getkind(TYPE, supports_floats=True, supports_longlong=True, supports_singlefloats=True): @@ -72,57 +86,10 @@ ) #compute_unique_id(box)) -class XxxAbstractValue(object): - __slots__ = () - - def getint(self): - raise NotImplementedError - - def getfloatstorage(self): - raise NotImplementedError - - def getfloat(self): - return longlong.getrealfloat(self.getfloatstorage()) - - def getref_base(self): - raise NotImplementedError - - def getref(self, TYPE): - raise NotImplementedError - getref._annspecialcase_ = 'specialize:arg(1)' - - def constbox(self): - raise NotImplementedError - - def getaddr(self): - "Only for raw addresses (BoxInt & ConstInt), not for GC addresses" - raise NotImplementedError - - def sort_key(self): - raise NotImplementedError - - def nonnull(self): - raise NotImplementedError - - def repr_rpython(self): - return '%s' % self - - def _get_str(self): - raise NotImplementedError - - def same_box(self, other): - return self is other - - def same_shape(self, other): - # only structured containers can compare their shape (vector box) - return True - - def getaccum(self): - return None - class AbstractDescr(AbstractValue): - __slots__ = () + __slots__ = ('descr_index',) llopaque = True + descr_index = -1 def repr_of_descr(self): return '%r' % (self,) @@ -204,7 +171,7 @@ class Const(AbstractValue): - __slots__ = () + _attrs_ = () @staticmethod def _new(x): @@ -638,43 +605,174 @@ # ____________________________________________________________ +FO_REPLACED_WITH_CONST = r_uint(1) +FO_POSITION_SHIFT = 1 +FO_POSITION_MASK = r_uint(0xFFFFFFFE) + + +class FrontendOp(AbstractResOp): + type = 'v' + _attrs_ = ('position_and_flags',) + + def __init__(self, pos): + # p is the 32-bit position shifted left by one (might be negative, + # but casted to the 32-bit UINT type) + p = rffi.cast(rffi.UINT, pos << FO_POSITION_SHIFT) + self.position_and_flags = r_uint(p) # zero-extended to a full word + + def get_position(self): + # p is the signed 32-bit position, from self.position_and_flags + p = rffi.cast(rffi.INT, self.position_and_flags) + return intmask(p) >> FO_POSITION_SHIFT + + def set_position(self, new_pos): + assert new_pos >= 0 + self.position_and_flags &= ~FO_POSITION_MASK + self.position_and_flags |= r_uint(new_pos << FO_POSITION_SHIFT) + + def is_replaced_with_const(self): + return bool(self.position_and_flags & FO_REPLACED_WITH_CONST) + + def set_replaced_with_const(self): + self.position_and_flags |= FO_REPLACED_WITH_CONST + + def __repr__(self): + return '%s(0x%x)' % (self.__class__.__name__, self.position_and_flags) + +class IntFrontendOp(IntOp, FrontendOp): + _attrs_ = ('position_and_flags', '_resint') + + def copy_value_from(self, other): + self._resint = other.getint() + +class FloatFrontendOp(FloatOp, FrontendOp): + _attrs_ = ('position_and_flags', '_resfloat') + + def copy_value_from(self, other): + self._resfloat = other.getfloatstorage() + +class RefFrontendOp(RefOp, FrontendOp): + _attrs_ = ('position_and_flags', '_resref', '_heapc_deps') + if LONG_BIT == 32: + _attrs_ += ('_heapc_flags',) # on 64 bit, this gets stored into the + _heapc_flags = r_uint(0) # high 32 bits of 'position_and_flags' + _heapc_deps = None + + def copy_value_from(self, other): + self._resref = other.getref_base() + + if LONG_BIT == 32: + def _get_heapc_flags(self): + return self._heapc_flags + def _set_heapc_flags(self, value): + self._heapc_flags = value + else: + def _get_heapc_flags(self): + return self.position_and_flags >> 32 + def _set_heapc_flags(self, value): + self.position_and_flags = ( + (self.position_and_flags & 0xFFFFFFFF) | + (value << 32)) + + class History(object): + ends_with_jump = False + trace = None + def __init__(self): - self.inputargs = None - self.operations = [] + self.descr_cache = {} + self.descrs = {} + self.consts = [] + self._cache = [] + + def set_inputargs(self, inpargs, metainterp_sd): + from rpython.jit.metainterp.opencoder import Trace + + self.trace = Trace(inpargs, metainterp_sd) + self.inputargs = inpargs + if self._cache: _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit