[pypy-commit] pypy vecopt2: work in progress refactoring dependencies to easier remove instructions
plan_rich
noreply at buildbot.pypy.org
Tue May 5 09:46:01 CEST 2015
Author: Richard Plangger <rich at pasra.at>
Branch: vecopt2
Changeset: r77109:c110482d24ca
Date: 2015-04-09 16:34 +0200
http://bitbucket.org/pypy/pypy/changeset/c110482d24ca/
Log: work in progress refactoring dependencies to easier remove
instructions
diff --git a/rpython/jit/metainterp/optimizeopt/dependency.py b/rpython/jit/metainterp/optimizeopt/dependency.py
--- a/rpython/jit/metainterp/optimizeopt/dependency.py
+++ b/rpython/jit/metainterp/optimizeopt/dependency.py
@@ -28,19 +28,48 @@
, (rop.GETFIELD_RAW, 0, 1)
]
+class Path(object):
+ def __init__(self,path):
+ self.path = path
+
+ def walk(self, idx):
+ self.path.append(idx)
+
+ def clone(self):
+ return Path(self.path[:])
+
+class OpWrapper(object):
+ def __init__(self, op, opidx):
+ self.op = op
+ self.opidx = opidx
+
+ def getopnum(self):
+ return self.op.getopnum()
+
+ def is_guard_early_exit(self):
+ return self.op.getopnum() == rop.GUARD_NO_EARLY_EXIT:
+
class Dependency(object):
- def __init__(self, idx_from, idx_to, arg):
- assert idx_from != idx_to
+ def __init__(self, at, to, arg):
+ assert at != to
self.args = []
if arg is not None:
- self.args.append(arg)
+ self.add_dependency(at, to, arg)
+ self.at = at
+ self.to = to
- self.idx_from = idx_from
- self.idx_to = idx_to
+ def add_dependency(self, at, arg):
+ self.args.append((at,arg))
+
+ def reverse_direction(self, ref):
+ """ if the parameter index is the same as idx_to then
+ this edge is in reverse direction.
+ """
+ return self.to == ref
def __repr__(self):
- return 'Dep(trace[%d] -> trace[%d], arg: %s)' \
- % (self.idx_from, self.idx_to, self.args)
+ return 'Dep(T[%d] -> T[%d], arg: %s)' \
+ % (self.at.opidx, self.to.opidx, self.args)
class DefTracker(object):
def __init__(self, memory_refs):
@@ -103,9 +132,9 @@
the same element.
"""
def __init__(self, operations):
- self.operations = operations
+ self.operations = [OpWrapper(op) for op in operations]
self.memory_refs = {}
- self.adjacent_list = [ [] for i in range(len(self.operations)) ]
+ self.adjacent_list = { op: [] for op in operations }
self.schedulable_nodes = [0] # label is always scheduleable
self.index_vars = {}
self.guards = []
@@ -128,8 +157,12 @@
# beginning of the loop
if op.getopnum() == rop.LABEL:
# TODO is it valid that a label occurs at the end of a trace?
+ s = 0
+ if self.operations[s+1].is_guard_early_exit():
+ s = 1
+ self.i_edge(0,1,label='L->EE')
for arg in op.getarglist():
- tracker.define(arg, 0)
+ tracker.define(arg, s)
#if isinstance(arg, BoxInt):
# assert arg not in self.index_vars
# self.index_vars[arg] = IndexVar(arg)
@@ -163,7 +196,7 @@
if dep.idx_to > i:
break
else:
- self._put_edge(i, jump_pos, None)
+ self._put_edge(jump_pos, i, jump_pos, None)
def _build_guard_dependencies(self, guard_idx, guard_opnum, tracker):
if guard_opnum >= rop.GUARD_NOT_INVALIDATED:
@@ -195,7 +228,7 @@
def_idx = tracker.definition_index(var)
for dep in self.provides(def_idx):
if var in dep.args and dep.idx_to > guard_idx:
- self._put_edge(guard_idx, dep.idx_to, var, force=True, label='prev('+str(var)+')')
+ self._put_edge(dep.idx_to, guard_idx, dep.idx_to, var, force=True, label='prev('+str(var)+')')
except KeyError:
pass
# handle fail args
@@ -204,7 +237,7 @@
for arg in op.getfailargs():
try:
for def_idx in tracker.redefintions(arg):
- dep = self._put_edge(def_idx, guard_idx, arg, label="fail")
+ dep = self._put_edge(guard_idx, def_idx, guard_idx, arg, label="fail")
except KeyError:
assert False
#
@@ -229,10 +262,10 @@
self._guard_inhert(prev_op_idx, guard_idx)
def _guard_inhert(self, idx, guard_idx):
- dep = self._put_edge(idx, guard_idx, None, label='inhert')
+ dep = self._put_edge(guard_idx, idx, guard_idx, None, label='inhert')
for dep in self.provides(idx):
if dep.idx_to > guard_idx:
- self._put_edge(guard_idx, dep.idx_to, None, label='inhert')
+ self._put_edge(dep.idx_to, guard_idx, dep.idx_to, None, label='inhert')
def _build_non_pure_dependencies(self, op, index, tracker):
# self._update_memory_ref(op, index, tracker)
@@ -262,8 +295,8 @@
for dep in self.provides(def_idx):
if dep.idx_to >= index:
break
- self._put_edge(dep.idx_to, index, argcell, label='war')
- self._put_edge(def_idx, index, argcell)
+ self._put_edge(index, dep.idx_to, index, argcell, label='war')
+ self._put_edge(index, def_idx, index, argcell)
except KeyError:
pass
else:
@@ -275,7 +308,7 @@
def _def_use(self, arg, index, tracker, argcell=None):
try:
def_idx = tracker.definition_index(arg, index, argcell)
- self._put_edge(def_idx, index, arg)
+ self._put_edge(index, def_idx, index, arg)
except KeyError:
pass
@@ -329,34 +362,41 @@
else:
break # cannot go further, this might be the label, or a constant
- def _put_edge(self, idx_from, idx_to, arg, force=False, label=None):
- assert idx_from != idx_to
- dep = self.directly_depends(idx_from, idx_to)
- if not dep:
- if force or self.independent(idx_from, idx_to):
- dep = Dependency(idx_from, idx_to, arg)
- self.adjacent_list[idx_from].append(dep)
- self.adjacent_list[idx_to].append(dep)
- if not we_are_translated() and label is not None:
- dep.label = label
+ def i_edge(self, idx_at, idx_to, label=None):
+ self._i_edge(idx_at, idx_to, None, label=label)
+
+ def _edge(self, at, to, arg, label=None):
+ assert at != to
+ dep = self.i_directly_depends(idx_from, idx_to)
+ if not dep or dep.at != at:
+ #if force or self.independent(idx_from, idx_to):
+ dep = Dependency(at, to, arg)
+ self.adjacent_list.setdefault(at,[]).append(dep)
+ self.adjacent_list.setdefault(to,[]).append(dep)
+ if not we_are_translated() and label is not None:
+ dep.label = label
else:
if arg not in dep.args:
- dep.args.append(arg)
+ dep.add_dependency(at,to,arg)
if not we_are_translated() and label is not None:
l = getattr(dep,'label',None)
if l is None:
l = ''
dep.label = l + ", " + label
+ def _i_edge(self, idx_at, idx_to, arg, label=None):
+ at = self.operations[idx_at]
+ to = self.operations[idx_to]
+ self._edge(at, to, arg, label)
+
def provides_count(self, idx):
+ # TODO
i = 0
for _ in self.provides(idx):
i += 1
return i
def provides(self, idx):
- return self.get_uses(idx)
- def get_uses(self, idx):
for dep in self.adjacent_list[idx]:
if idx < dep.idx_to:
yield dep
@@ -368,17 +408,12 @@
return i
def depends(self, idx):
- return self.get_defs(idx)
- def get_defs(self, idx):
for dep in self.adjacent_list[idx]:
if idx > dep.idx_from:
yield dep
def dependencies(self, idx):
return self.adjacent_list[idx]
- def instr_dependencies(self, idx):
- edges = self.adjacent_list[idx]
- return edges
def independent(self, ai, bi):
""" An instruction depends on another if there is a dependency path from
@@ -403,19 +438,27 @@
stmt_indices.append(dep.idx_from)
return True
- def definition_dependencies(self, idx):
- # XXX remove
- deps = []
- for dep in self.adjacent_list[idx]:
- for dep_def in self.adjacent_list[dep.idx_from]:
- deps.append(dep_def)
- return deps
+ def iterate_paths_backward(self, ai, bi):
+ if ai == bi:
+ return
+ if ai > bi:
+ ai, bi = bi, ai
+ worklist = [(Path([bi]),bi)]
+ while len(worklist) > 0:
+ path,idx = worklist.pop()
+ for dep in self.depends(idx):
+ if ai > dep.idx_from or dep.points_backward():
+ # this points above ai (thus unrelevant)
+ continue
+ cloned_path = path.clone()
+ cloned_path.walk(dep.idx_from)
+ if dep.idx_from == ai:
+ yield cloned_path
+ else:
+ worklist.append((cloned_path,dep.idx_from))
def directly_depends(self, from_idx, to_idx):
return self.instr_dependency(from_idx, to_idx)
-
- def instr_dependency(self, from_instr_idx, to_instr_idx):
- # XXX
""" Does there exist a dependency from the instruction to another?
Returns None if there is no dependency or the Dependency object in
any other case.
@@ -427,16 +470,16 @@
return edge
return None
- def remove_depencency(self, follow_dep, point_to_idx):
- """ removes a all dependencies that point to the second parameter.
- it is assumed that the adjacent_list[point_to_idx] is not iterated
- when calling this function.
- """
- idx = follow_dep.idx_from
- if idx == point_to_idx:
- idx = follow_dep.idx_to
- self.adjacent_list[idx] = [d for d in self.adjacent_list[idx] \
- if d.idx_to != point_to_idx and d.idx_from != point_to_idx]
+ def i_remove_dependency(self, idx_at, idx_to):
+ at = self.operations[idx_at]
+ to = self.operations[idx_to]
+ self.remove_dependency(at, to)
+ def remove_dependency(self, at, to):
+ """ Removes a all dependencies that point to 'to' """
+ self.adjacent_list[at] = \
+ [d for d in self.adjacent_list[at] if d.to != to]
+ self.adjacent_list[to] = \
+ [d for d in self.adjacent_list[to] if d.at != at]
def __repr__(self):
graph = "graph([\n"
@@ -473,6 +516,11 @@
if getattr(dep, 'label', None):
label = '[label="%s"]' % dep.label
dot += " n%d -> n%d %s;\n" % (i,dep.idx_to,label)
+ elif dep.idx_to == i and dep.idx_from > i:
+ label = ''
+ if getattr(dep, 'label', None):
+ label = '[label="%s"]' % dep.label
+ dot += " n%d -> n%d %s;\n" % (dep.idx_from,dep.idx_to,label)
dot += "\n}\n"
return dot
raise NotImplementedError("dot cannot built at runtime")
@@ -522,15 +570,19 @@
to_del = []
adj_list = self.graph.adjacent_list[node]
for dep in adj_list:
- self.graph.remove_depencency(dep, node)
+ self.graph.remove_dependency_by_index(node, dep.idx_to)
+ self.graph.remove_dependency_by_index(dep.idx_to, node)
+ print "remove", node, "<=>", dep.idx_to
+ if self.is_schedulable(dep.idx_to):
+ print "sched", dep.idx_to
+ self.schedulable_nodes.append(dep.idx_to)
#
for dep in self.graph.provides(node):
candidate = dep.idx_to
- if self.is_schedulable(dep.idx_to):
- self.schedulable_nodes.append(dep.idx_to)
self.graph.adjacent_list[node] = []
def is_schedulable(self, idx):
+ print "is sched", idx, "count:", self.graph.depends_count(idx), self.graph.adjacent_list[idx]
return self.graph.depends_count(idx) == 0
class IntegralForwardModification(object):
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_vectorize.py b/rpython/jit/metainterp/optimizeopt/test/test_vectorize.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_vectorize.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_vectorize.py
@@ -923,6 +923,7 @@
def test_schedule_vectorized_trace_1(self):
ops = """
[i0, i1, i2, i3, i4, i5, i6, i7]
+ guard_no_early_exit() []
i8 = raw_load(i3, i0, descr=intarraydescr)
i9 = raw_load(i4, i0, descr=intarraydescr)
i10 = int_add(i8, i9)
@@ -935,6 +936,7 @@
jump(i12, i8, i9, i3, i4, i5, i10, i7)
"""
vopt = self.schedule(self.parse_loop(ops),1)
+ self.debug_print_operations(vopt.loop)
class TestLLtype(BaseTestVectorize, LLtypeMixin):
diff --git a/rpython/jit/metainterp/optimizeopt/vectorize.py b/rpython/jit/metainterp/optimizeopt/vectorize.py
--- a/rpython/jit/metainterp/optimizeopt/vectorize.py
+++ b/rpython/jit/metainterp/optimizeopt/vectorize.py
@@ -61,6 +61,10 @@
def_opt = Optimizer(metainterp_sd, jitdriver_sd, loop, optimizations)
def_opt.propagate_all_forward()
+class OpWrapper(object):
+ def __init__(self, op, opidx):
+ self.op = op
+
class VectorizingOptimizer(Optimizer):
""" Try to unroll the loop and find instructions to group """
@@ -131,6 +135,8 @@
operations = []
for i in range(1,op_count-1):
+ if loop.operations[i].getopnum() == rop.GUARD_FUTURE_CONDITION:
+ continue
op = loop.operations[i].clone()
operations.append(op)
self.emit_unrolled_operation(op)
@@ -152,6 +158,8 @@
rename_map[la] = ja
#
for op in operations:
+ if op.getopnum() in (rop.GUARD_NO_EARLY_EXIT, rop.GUARD_FUTURE_CONDITION):
+ continue # do not unroll this operation twice
copied_op = op.clone()
if copied_op.result is not None:
# every result assigns a new box, thus creates an entry
@@ -246,15 +254,6 @@
self.dependency_graph = DependencyGraph(self.loop.operations)
self.relax_guard_dependencies()
- def relax_guard_dependencies(self):
- return
- for guard_idx in self.dependency_graph.guards:
- guard = self.operations[guard_idx]
- for dep in self.dependency_graph.depends(idx):
- op = self.operations[dep.idx_from]
- if op.returns_bool_result():
- pass
-
def find_adjacent_memory_refs(self):
""" the pre pass already builds a hash of memory references and the
operations. Since it is in SSA form there are no array indices.
@@ -382,6 +381,59 @@
else:
scheduler.schedule_later(0)
+ def relax_guard_dependencies(self):
+ early_exit_idx = 1
+ operations = self.loop.operations
+ assert operations[early_exit_idx].getopnum() == \
+ rop.GUARD_NO_EARLY_EXIT
+ target_guard = operations[early_exit_idx]
+ for guard_idx in self.dependency_graph.guards:
+ if guard_idx == early_exit_idx:
+ continue
+ guard = operations[guard_idx]
+ if guard.getopnum() not in (rop.GUARD_TRUE,rop.GUARD_FALSE):
+ continue
+ self.dependency_graph.edge(early_exit_idx, guard_idx, early_exit_idx, label='EE')
+ print "put", guard_idx, "=>", early_exit_idx
+ del_deps = []
+ for path in self.dependency_graph.iterate_paths_backward(guard_idx, early_exit_idx):
+ op_idx = path.path[1]
+ print "path", path.path
+ op = operations[op_idx]
+ if fail_args_break_dependency(guard, guard_idx, target_guard, early_exit_idx, op, op_idx):
+ print " +>+>==> break", op_idx, "=>", guard_idx
+ del_deps.append(op_idx)
+ for dep_idx in del_deps:
+ self.dependency_graph.remove_dependency_by_index(dep_idx, guard_idx)
+
+ del_deps = []
+ for dep in self.dependency_graph.provides(early_exit_idx):
+ del_deps.append(dep.idx_to)
+ for dep_idx in del_deps:
+ self.dependency_graph.remove_dependency_by_index(1, dep_idx)
+ self.dependency_graph.edge(dep_idx, 0, dep_idx)
+ last_idx = len(operations) - 1
+ self.dependency_graph.remove_dependency_by_index(0,1)
+ self.dependency_graph.edge(last_idx, early_exit_idx, last_idx)
+
+def fail_args_break_dependency(guard, guard_idx, target_guard, target_guard_idx, op, op_idx):
+ failargs = set(guard.getfailargs())
+ new_failargs = set(target_guard.getfailargs())
+
+ print " args:", [op.result] + op.getarglist()[:], " &&& ", failargs, " !!! ", new_failargs
+ if op.is_array_op():
+ return True
+ if op.result is not None:
+ arg = op.result
+ if arg not in failargs or \
+ arg in failargs and arg in new_failargs:
+ return False
+ for arg in op.getarglist():
+ if arg not in failargs or \
+ arg in failargs and arg in new_failargs:
+ return False
+ return True
+
class VecScheduleData(SchedulerData):
def __init__(self):
self.box_to_vbox = {}
More information about the pypy-commit
mailing list