Author: Carl Friedrich Bolz <cfb...@gmx.de> Branch: guard-compatible Changeset: r83003:818cabf99bbf Date: 2016-03-12 23:02 +0100 http://bitbucket.org/pypy/pypy/changeset/818cabf99bbf/
Log: in-progress: tracing support for guard_compatible, also some sketched code for how failures and checking of the conditions would work. needs proper integration with the backends 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 @@ -50,7 +50,7 @@ self.operations = [] for op in operations: opnum = op.getopnum() - if opnum == rop.GUARD_VALUE: + if opnum == rop.GUARD_VALUE or opnum == rop.GUARD_COMPATIBLE: # we don't care about the value 13 here, because we gonna # fish it from the extra slot on frame anyway op.getdescr().make_a_counter_per_value(op, 13) @@ -1271,6 +1271,12 @@ if self.lltrace.invalid: self.fail_guard(descr) + def execute_guard_compatible(self, descr, arg1, arg2): + if arg1 != arg2: + if descr.fake_check_against_list(self.cpu, arg1): + return + self.fail_guard(descr, extra_value=arg1) + def execute_int_add_ovf(self, _, x, y): try: z = ovfcheck(x + y) 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 @@ -582,6 +582,10 @@ def bhimpl_str_guard_value(a, i, d): return a + @arguments("r") + def bhimpl_ref_guard_compatible(a): + pass + @arguments("self", "i") def bhimpl_int_push(self, a): self.tmpreg_i = a diff --git a/rpython/jit/metainterp/compatible.py b/rpython/jit/metainterp/compatible.py --- a/rpython/jit/metainterp/compatible.py +++ b/rpython/jit/metainterp/compatible.py @@ -1,5 +1,51 @@ from rpython.jit.metainterp.history import newconst +def do_call(cpu, argboxes, descr): + from rpython.jit.metainterp.history import INT, REF, FLOAT, VOID + from rpython.jit.metainterp.blackhole import NULL + # XXX XXX almost copy from executor.py + rettype = descr.get_result_type() + # count the number of arguments of the different types + count_i = count_r = count_f = 0 + for i in range(1, len(argboxes)): + type = argboxes[i].type + if type == INT: count_i += 1 + elif type == REF: count_r += 1 + elif type == FLOAT: count_f += 1 + # allocate lists for each type that has at least one argument + if count_i: args_i = [0] * count_i + else: args_i = None + if count_r: args_r = [NULL] * count_r + else: args_r = None + if count_f: args_f = [longlong.ZEROF] * count_f + else: args_f = None + # fill in the lists + count_i = count_r = count_f = 0 + for i in range(1, len(argboxes)): + box = argboxes[i] + if box.type == INT: + args_i[count_i] = box.getint() + count_i += 1 + elif box.type == REF: + args_r[count_r] = box.getref_base() + count_r += 1 + elif box.type == FLOAT: + args_f[count_f] = box.getfloatstorage() + count_f += 1 + # get the function address as an integer + func = argboxes[0].getint() + # do the call using the correct function from the cpu + if rettype == INT: + return newconst(cpu.bh_call_i(func, args_i, args_r, args_f, descr)) + if rettype == REF: + return newconst(cpu.bh_call_r(func, args_i, args_r, args_f, descr)) + if rettype == FLOAT: + return newconst(cpu.bh_call_f(func, args_i, args_r, args_f, descr)) + if rettype == VOID: + # don't even need to call the void function, result will always match + return None + raise AssertionError("bad rettype") + class CompatibilityCondition(object): """ A collections of conditions that an object needs to fulfil. """ def __init__(self, ptr): @@ -8,3 +54,14 @@ def record_pure_call(self, op, res): self.pure_call_conditions.append((op, res)) + + def check_compat(self, cpu, ref): + for op, correct_res in self.pure_call_conditions: + calldescr = op.getdescr() + # change exactly the first argument + arglist = op.getarglist() + arglist[1] = newconst(ref) + res = do_call(cpu, arglist, calldescr) + if not res.same_constant(correct_res): + return False + return True 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 @@ -810,7 +810,7 @@ metainterp.box_names_memo) def make_a_counter_per_value(self, guard_value_op, index): - assert guard_value_op.getopnum() == rop.GUARD_VALUE + assert guard_value_op.getopnum() in (rop.GUARD_VALUE, rop.GUARD_COMPATIBLE) box = guard_value_op.getarg(0) if box.type == history.INT: ty = self.TY_INT @@ -929,6 +929,11 @@ resumedescr = ResumeGuardCopiedExcDescr() else: resumedescr = ResumeGuardExcDescr() + elif opnum == rop.GUARD_COMPATIBLE: + if copied_guard: + import pdb; pdb.set_trace() + else: + resumedescr = GuardCompatibleDescr() else: if copied_guard: resumedescr = ResumeGuardCopiedDescr() @@ -1078,6 +1083,50 @@ metainterp.retrace_needed(new_trace, info) return None +class GuardCompatibleDescr(ResumeGuardDescr): + """ A descr for guard_compatible. All the conditions that a value should + fulfil need to be attached to this descr by optimizeopt. """ + + def __init__(self): + # XXX for now - in the end this would be in assembler + self._checked_ptrs = [] + self._compatibility_conditions = None + + def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd): + index = intmask(self.status >> self.ST_SHIFT) + typetag = intmask(self.status & self.ST_TYPE_MASK) + assert typetag == self.TY_REF # for now + refval = metainterp_sd.cpu.get_value_direct(deadframe, 'r', index) + if self.is_compatible(metainterp_sd.cpu, refval): + from rpython.jit.metainterp.blackhole import resume_in_blackhole + # next time it'll pass XXX use new cpu thingie here + self._checked_ptrs.append(history.newconst(refval)) + resume_in_blackhole(metainterp_sd, jitdriver_sd, self, deadframe) + else: + # a real failure + return ResumeGuardDescr.handle_fail(self, deadframe, metainterp_sd, jitdriver_sd) + + def fake_check_against_list(self, cpu, ref): + # XXX should be in assembler + const = history.newconst(ref) + if self._compatibility_conditions: + for i in range(len(self._checked_ptrs)): + if const.same_constant(self._checked_ptrs[i]): + return True + return False + + def is_compatible(self, cpu, ref): + const = history.newconst(ref) + if self._compatibility_conditions: + for i in range(len(self._checked_ptrs)): + if const.same_constant(self._checked_ptrs[i]): + return True + if self._compatibility_conditions.check_compat(cpu, ref): + self._checked_ptrs.append(const) + return True + return False + return True # no conditions, everything works + # ____________________________________________________________ memory_error = MemoryError() diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py --- a/rpython/jit/metainterp/pyjitpl.py +++ b/rpython/jit/metainterp/pyjitpl.py @@ -1200,6 +1200,16 @@ opimpl_float_guard_value = _opimpl_guard_value @arguments("box", "orgpc") + def opimpl_ref_guard_compatible(self, box, orgpc): + if isinstance(box, Const): + return box # no promotion needed, already a Const + else: + promoted_box = executor.constant_from_op(box) + self.metainterp.generate_guard(rop.GUARD_COMPATIBLE, box, [promoted_box], + resumepc=orgpc) + # importantly, there is no replace_box here! + + @arguments("box", "orgpc") def opimpl_guard_class(self, box, orgpc): clsbox = self.cls_of_box(box) if not self.metainterp.heapcache.is_class_known(box): 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 @@ -1049,6 +1049,7 @@ 'GUARD_NOT_FORCED_2/0d/n', # same as GUARD_NOT_FORCED, but for finish() 'GUARD_NOT_INVALIDATED/0d/n', 'GUARD_FUTURE_CONDITION/0d/n', + 'GUARD_COMPATIBLE/2d/n', # is removable, may be patched by an optimization '_GUARD_LAST', # ----- end of guard operations ----- diff --git a/rpython/jit/metainterp/test/test_compatible.py b/rpython/jit/metainterp/test/test_compatible.py new file mode 100644 --- /dev/null +++ b/rpython/jit/metainterp/test/test_compatible.py @@ -0,0 +1,35 @@ +from rpython.jit.metainterp.test.support import LLJitMixin +from rpython.rlib import jit +from rpython.rtyper.lltypesystem import lltype, rffi + + +class TestCompatible(LLJitMixin): + def test_simple(self): + S = lltype.GcStruct('S', ('x', lltype.Signed)) + p1 = lltype.malloc(S) + p1.x = 5 + + p2 = lltype.malloc(S) + p2.x = 5 + + p3 = lltype.malloc(S) + p3.x = 6 + driver = jit.JitDriver(greens = [], reds = ['n', 'x']) + @jit.elidable_compatible() + def g(s): + return s.x + + def f(n, x): + while n > 0: + driver.can_enter_jit(n=n, x=x) + driver.jit_merge_point(n=n, x=x) + n -= g(x) + + def main(): + f(100, p1) + f(100, p2) + f(100, p3) + + self.meta_interp(main, []) + # XXX check number of bridges + _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit