[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