Author: Carl Friedrich Bolz <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit