Author: Richard Plangger <[email protected]>
Branch: vecopt2
Changeset: r77111:53e935368706
Date: 2015-04-10 16:14 +0200
http://bitbucket.org/pypy/pypy/changeset/53e935368706/
Log: the dependency graph now wraps each operation in a Node object. This
makes the arch. much cleaner and separates concerns
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
@@ -2,7 +2,7 @@
from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
from rpython.jit.metainterp.resoperation import rop
from rpython.jit.codewriter.effectinfo import EffectInfo
-from rpython.jit.metainterp.history import BoxPtr, ConstPtr, ConstInt, BoxInt,
Box
+from rpython.jit.metainterp.history import BoxPtr, ConstPtr, ConstInt, BoxInt,
Box, Const
from rpython.rtyper.lltypesystem import llmemory
from rpython.rlib.unroll import unrolling_iterable
from rpython.rlib.objectmodel import we_are_translated
@@ -38,16 +38,190 @@
def clone(self):
return Path(self.path[:])
-class OpWrapper(object):
+class Node(object):
def __init__(self, op, opidx):
self.op = op
self.opidx = opidx
+ self.adjacent_list = []
+ self.adjacent_list_back = []
+ self.memory_ref = None
+ self.pack = None
+
+ def getoperation(self):
+ return self.op
+ def getindex(self):
+ return self.opidx
+
+ def dependency_count(self):
+ return len(self.adjacent_list)
def getopnum(self):
return self.op.getopnum()
+ def getopname(self):
+ return self.op.getopname()
+
+ def edge_to(self, to, arg, label=None):
+ assert self != to
+ dep = self.depends_on(to)
+ if not dep:
+ #if force or self.independent(idx_from, idx_to):
+ dep = Dependency(self, to, arg)
+ self.adjacent_list.append(dep)
+ dep_back = Dependency(to, self, arg)
+ to.adjacent_list_back.append(dep_back)
+ if not we_are_translated():
+ if label is None:
+ label = ''
+ dep.label = label
+ else:
+ if not dep.because_of(arg):
+ dep.add_dependency(self,to,arg)
+ if not we_are_translated() and label is not None:
+ _label = getattr(dep, 'label', '')
+ dep.label = _label + ", " + label
+
+ def depends_on(self, to):
+ """ 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.
+ """
+ for edge in self.adjacent_list:
+ if edge.to == to:
+ return edge
+ return None
+
+ def clear_dependencies(self):
+ self.adjacent_list.clear()
+ self.adjacent_list_back.clear()
def is_guard_early_exit(self):
- return self.op.getopnum() == rop.GUARD_NO_EARLY_EXIT:
+ return self.op.getopnum() == rop.GUARD_NO_EARLY_EXIT
+
+ def loads_from_complex_object(self):
+ return rop._ALWAYS_PURE_LAST <= self.op.getopnum() <= rop._MALLOC_FIRST
+
+ def modifies_complex_object(self):
+ return rop.SETARRAYITEM_GC <= self.op.getopnum() <= rop.UNICODESETITEM
+
+ def side_effect_arguments(self):
+ # if an item in array p0 is modified or a call contains an argument
+ # it can modify it is returned in the destroyed list.
+ args = []
+ op = self.op
+ if self.modifies_complex_object():
+ for opnum, i, j in unrolling_iterable(MODIFY_COMPLEX_OBJ):
+ if op.getopnum() == opnum:
+ op_args = op.getarglist()
+ if j == -1:
+ args.append((op.getarg(i), None, True))
+ for j in range(i+1,len(op_args)):
+ args.append((op.getarg(j), None, False))
+ else:
+ args.append((op.getarg(i), op.getarg(j), True))
+ for x in range(j+1,len(op_args)):
+ args.append((op.getarg(x), None, False))
+ break
+ else:
+ # assume this destroys every argument... can be enhanced by looking
+ # at the effect info of a call for instance
+ for arg in op.getarglist():
+ args.append((arg,None,True))
+ return args
+
+ def provides_count(self):
+ return len(self.adjacent_list)
+
+ def provides(self):
+ return self.adjacent_list
+
+ def depends_count(self, idx):
+ return len(self.adjacent_list_back)
+
+ def depends(self):
+ return self.adjacent_list_back
+
+ def dependencies(self):
+ return self.adjacent_list[:] + self.adjacent_list_back[:]
+
+ def is_after(self, other):
+ return self.opidx > other.opidx
+
+ def is_before(self, other):
+ return self.opidx < other.opidx
+
+ def independent(self, other):
+ """ An instruction depends on another if there is a path from
+ self to other. """
+ if self == other:
+ return True
+ # forward
+ worklist = [self]
+ while len(worklist) > 0:
+ node = worklist.pop()
+ for dep in node.provides():
+ if dep.points_to(other):
+ # dependent. There is a path from self to other
+ return False
+ worklist.append(dep.to)
+ # backward
+ worklist = [self]
+ while len(worklist) > 0:
+ node = worklist.pop()
+ for dep in node.depends():
+ if dep.points_to(other):
+ # dependent. There is a path from self to other
+ return False
+ worklist.append(dep.to)
+ return True
+
+ 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 getedge_to(self, other):
+ for dep in self.adjacent_list:
+ if dep.to == other:
+ return dep
+ return None
+
+ def i_remove_dependency(self, idx_at, idx_to):
+ at = self.nodes[idx_at]
+ to = self.nodes[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]
+
+ return args
+ def __repr__(self):
+ return "Node(opidx: %d)"%self.opidx
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __eq__(self, other):
+ if isinstance(other, Node):
+ return self.opidx == other.opidx
+ return False
+
class Dependency(object):
def __init__(self, at, to, arg):
@@ -58,7 +232,33 @@
self.at = at
self.to = to
- def add_dependency(self, at, arg):
+ def because_of(self, var):
+ for arg in self.args:
+ if arg[1] == var:
+ return True
+ return False
+
+ def to_index(self):
+ return self.to.getindex()
+ def at_index(self):
+ return self.at.getindex()
+
+ def points_after_to(self, to):
+ return self.to.opidx < to.opidx
+ def points_above_at(self, at):
+ return self.at.opidx < at.opidx
+ def i_points_above_at(self, idx):
+ return self.at.opidx < idx
+
+ def points_to(self, to):
+ return self.to == to
+ def points_at(self, at):
+ return self.at == at
+ def i_points_at(self, idx):
+ # REM
+ return self.at.opidx == idx
+
+ def add_dependency(self, at, to, arg):
self.args.append((at,arg))
def reverse_direction(self, ref):
@@ -76,17 +276,17 @@
self.graph = graph
self.defs = {}
- def define(self, arg, index, argcell=None):
+ def define(self, arg, node, argcell=None):
if arg in self.defs:
- self.defs[arg].append((index,argcell))
+ self.defs[arg].append((node,argcell))
else:
- self.defs[arg] = [(index,argcell)]
+ self.defs[arg] = [(node,argcell)]
def redefintions(self, arg):
for _def in self.defs[arg]:
yield _def[0]
- def definition_index(self, arg, index = -1, argcell=None):
+ def definition(self, arg, node=None, argcell=None):
def_chain = self.defs[arg]
if len(def_chain) == 1:
return def_chain[0][0]
@@ -94,17 +294,17 @@
if argcell == None:
return def_chain[-1][0]
else:
- assert index != -1
+ assert node is not None
i = len(def_chain)-1
try:
- mref = self.graph.memory_refs[index]
+ mref = node.memory_ref
while i >= 0:
- def_index = def_chain[i][0]
- oref = self.graph.memory_refs.get(def_index)
+ def_node = def_chain[i][0]
+ oref = def_node.memory_ref
if oref is not None and mref.indices_can_alias(oref):
- return def_index
+ return def_node
elif oref is None:
- return def_index
+ return def_node
i -= 1
except KeyError:
# when a key error is raised, this means
@@ -114,11 +314,12 @@
def depends_on_arg(self, arg, to, argcell=None):
try:
- idx_at = self.definition_index(arg, to.opidx, argcell)
- at = self.graph.operations[idx_at]
- graph.edge(at, to, arg)
+ at = self.definition(arg, to, argcell)
+ at.edge_to(to, arg)
except KeyError:
- assert False, "arg %s must be defined" % arg
+ if not we_are_translated():
+ if not isinstance(arg, Const):
+ assert False, "arg %s must be defined" % arg
class DependencyGraph(object):
@@ -141,14 +342,16 @@
the same element.
"""
def __init__(self, operations):
- self.operations = [OpWrapper(op) for op in operations]
+ self.nodes = [ Node(op,i) for i,op in enumerate(operations) ]
self.memory_refs = {}
- self.adjacent_list = { op: [] for op in operations }
self.schedulable_nodes = [0] # label is always scheduleable
self.index_vars = {}
self.guards = []
self.build_dependencies()
+ def getnode(self, i):
+ return self.nodes[i]
+
def build_dependencies(self):
""" This is basically building the definition-use chain and saving this
information in a graph structure. This is the same as calculating
@@ -161,54 +364,49 @@
#
intformod = IntegralForwardModification(self.memory_refs,
self.index_vars)
# pass 1
- for i,opw in enumerate(self.operations):
- op = opw.op
+ for i,node in enumerate(self.nodes):
+ op = node.op
# the label operation defines all operations at the
# 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')
+ ee_node = self.nodes[i+1]
+ if ee_node.is_guard_early_exit():
+ node.edge_to(ee_node,None,label='L->EE')
+ node = ee_node
for arg in op.getarglist():
- tracker.define(arg, s)
- #if isinstance(arg, BoxInt):
- # assert arg not in self.index_vars
- # self.index_vars[arg] = IndexVar(arg)
+ tracker.define(arg, node)
continue # prevent adding edge to the label itself
- intformod.inspect_operation(op, i)
+ intformod.inspect_operation(node)
# definition of a new variable
if op.result is not None:
# In SSA form. Modifications get a new variable
- tracker.define(op.result, i)
+ tracker.define(op.result, node)
# usage of defined variables
if op.is_always_pure() or op.is_final():
# normal case every arguments definition is set
for arg in op.getarglist():
- tracker.depends_on_arg(arg, opw)
+ tracker.depends_on_arg(arg, node)
elif op.is_guard():
- self.guards.append(i)
+ self.guards.append(node)
else:
- self._build_non_pure_dependencies(op, i, tracker)
+ self._build_non_pure_dependencies(node, tracker)
# pass 2 correct guard dependencies
- for guard_idx in self.guards:
- self._build_guard_dependencies(guard_idx, op.getopnum(), tracker)
+ for guard_node in self.guards:
+ self._build_guard_dependencies(guard_node, op.getopnum(), tracker)
# pass 3 find schedulable nodes
- jump_pos = len(self.operations)-1
- for i,op in enumerate(self.operations):
- if len(self.adjacent_list[i]) == 0:
- self.schedulable_nodes.append(i)
+ jump_pos = len(self.nodes)-1
+ jump_node = self.nodes[jump_pos]
+ for node in self.nodes:
+ if node.dependency_count() == 0:
+ self.schedulable_nodes.append(node.opidx)
# every leaf instruction points to the jump_op. in theory every
instruction
# points to jump_op. this forces the jump/finish op to be the last
operation
- if i != jump_pos:
- for dep in self.adjacent_list[i]:
- if dep.idx_to > i:
- break
- else:
- self._put_edge(jump_pos, i, jump_pos, None)
+ if node != jump_node:
+ if node.provides_count() == 0:
+ node.edge_to(jump_node, None, label='jump')
- def _build_guard_dependencies(self, guard_idx, guard_opnum, tracker):
+ def _build_guard_dependencies(self, guard_node, guard_opnum, tracker):
if guard_opnum >= rop.GUARD_NOT_INVALIDATED:
# ignure invalidated & future condition guard
return
@@ -219,14 +417,13 @@
# 'GUARD_NONNULL/1d',
# 'GUARD_ISNULL/1d',
# 'GUARD_NONNULL_CLASS/2d',
- guard_opw = self.operations[guard_idx]
- guard_op = guard_opw.op
+ guard_op = guard_node.op
for arg in guard_op.getarglist():
- tracker.depends_on_arg(arg, guard_opw)
+ tracker.depends_on_arg(arg, guard_node)
variables = []
- for dep in self.depends(guard_opw):
- op = dep.at.op
+ for dep in guard_node.depends():
+ op = dep.to.op
for arg in op.getarglist():
if isinstance(arg, Box):
variables.append(arg)
@@ -235,66 +432,67 @@
#
for var in variables:
try:
- 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.edge(guard_opw, dep.to, var,
label='prev('+str(var)+')')
+ def_node = tracker.definition(var)
+ for dep in def_node.provides():
+ if guard_node.is_before(dep.to) and dep.because_of(var):
+ guard_node.edge_to(dep.to, var,
label='prev('+str(var)+')')
except KeyError:
pass
# handle fail args
if guard_op.getfailargs():
for arg in guard_op.getfailargs():
try:
- for def_idx in tracker.redefintions(arg):
- at = self.operations[def_idx]
- dep = self.edge(at, guard_opw, arg, label="fail")
+ for at in tracker.redefintions(arg):
+ # later redefinitions are prohibited
+ if at.is_before(guard_node):
+ at.edge_to(guard_node, arg, label="fail")
except KeyError:
assert False
#
# guards check overflow or raise are directly dependent
# find the first non guard operation
- prev_op_idx = guard_idx - 1
+ prev_op_idx = guard_node.opidx - 1
while prev_op_idx > 0:
- prev_op = self.operations[prev_op_idx].op
- if prev_op.is_guard():
+ prev_node = self.nodes[prev_op_idx]
+ if prev_node.op.is_guard():
prev_op_idx -= 1
else:
break
- prev_op = self.operations[prev_op_idx].op
- #
- if op.is_guard_exception() and prev_op.can_raise():
- self.i_guard_inhert(prev_op_idx, guard_idx)
- elif op.is_guard_overflow() and prev_op.is_ovf():
- self.i_guard_inhert(prev_op_idx, guard_idx)
- elif op.getopnum() == rop.GUARD_NOT_FORCED and prev_op.can_raise():
- self.i_guard_inhert(prev_op_idx, guard_idx)
- elif op.getopnum() == rop.GUARD_NOT_FORCED_2 and prev_op.can_raise():
- self.i_guard_inhert(prev_op_idx, guard_idx)
+ prev_node = self.nodes[prev_op_idx]
+ guard_op = guard_node.getoperation()
+ prev_op = prev_node.getoperation()
+ if guard_op.is_guard_exception() and prev_op.can_raise():
+ self.guard_inhert(prev_node, guard_node)
+ elif guard_op.is_guard_overflow() and prev_op.is_ovf():
+ self.guard_inhert(prev_node, guard_node)
+ elif guard_op.getopnum() == rop.GUARD_NOT_FORCED and
prev_op.can_raise():
+ self.guard_inhert(prev_node, guard_node)
+ elif guard_op.getopnum() == rop.GUARD_NOT_FORCED_2 and
prev_op.can_raise():
+ self.guard_inhert(prev_node, guard_node)
- def i_guard_inhert(self, idx, guard_idx):
- at = self.operation[idx]
- dep = self.i_edge(idx, guard_idx, None, label='inhert')
- for dep in self.provides(at):
- if dep.to.opidx > guard_idx:
- self.i_edge(guard_idx, dep.to.opidx, None, label='inhert')
+ def guard_inhert(self, at, to):
+ at.edge_to(to, None, label='inhert')
+ for dep in at.provides():
+ if to.is_before(dep.to):
+ to.edge_to(dep.to, None, label='inhert')
- def _build_non_pure_dependencies(self, op, index, tracker):
- # self._update_memory_ref(op, index, tracker)
- if self.loads_from_complex_object(op):
+ def _build_non_pure_dependencies(self, node, tracker):
+ op = node.op
+ if node.loads_from_complex_object():
# If this complex object load operation loads an index that has
been
# modified, the last modification should be used to put a def-use
edge.
for opnum, i, j in unrolling_iterable(LOAD_COMPLEX_OBJ):
if opnum == op.getopnum():
cobj = op.getarg(i)
index_var = op.getarg(j)
- self._def_use(cobj, index, tracker, argcell=index_var)
- self._def_use(index_var, index, tracker)
+ tracker.depends_on_arg(cobj, node, index_var)
+ tracker.depends_on_arg(index_var, node)
else:
- for arg, argcell, destroyed in self._side_effect_argument(op):
+ for arg, argcell, destroyed in node.side_effect_arguments():
if argcell is not None:
# tracks the exact cell that is modified
- self._def_use(arg, index, tracker, argcell=argcell)
- self._def_use(argcell, index, tracker)
+ tracker.depends_on_arg(arg, node, argcell)
+ tracker.depends_on_arg(argcell, node)
else:
if destroyed:
# cannot be sure that only a one cell is modified
@@ -302,232 +500,47 @@
try:
# A trace is not entirely in SSA form. complex
object
# modification introduces WAR/WAW dependencies
- def_idx = tracker.definition_index(arg)
- for dep in self.provides(def_idx):
- if dep.idx_to >= index:
- break
- self._put_edge(index, dep.idx_to, index,
argcell, label='war')
- self._put_edge(index, def_idx, index, argcell)
+ def_node = tracker.definition(arg)
+ for dep in def_node.provides():
+ if dep.to != node:
+ dep.to.edge_to(node, argcell, label='war')
+ def_node.edge_to(node, argcell)
except KeyError:
pass
else:
# not destroyed, just a normal use of arg
- self._def_use(arg, index, tracker)
+ tracker.depends_on_arg(arg, node)
if destroyed:
- tracker.define(arg, index, argcell=argcell)
-
- def _side_effect_argument(self, op):
- # if an item in array p0 is modified or a call contains an argument
- # it can modify it is returned in the destroyed list.
- args = []
- if self.modifies_complex_object(op):
- for opnum, i, j in unrolling_iterable(MODIFY_COMPLEX_OBJ):
- if op.getopnum() == opnum:
- op_args = op.getarglist()
- if j == -1:
- args.append((op.getarg(i), None, True))
- for j in range(i+1,len(op_args)):
- args.append((op.getarg(j), None, False))
- else:
- args.append((op.getarg(i), op.getarg(j), True))
- for x in range(j+1,len(op_args)):
- args.append((op.getarg(x), None, False))
- break
- else:
- # assume this destroys every argument... can be enhanced by looking
- # at the effect info of a call for instance
- for arg in op.getarglist():
- args.append((arg,None,True))
-
- return args
-
- def _update_memory_ref(self, op, index, tracker):
- # deprecated
- if index not in self.memory_refs:
- return
- memref = self.memory_refs[index]
- self.integral_mod.reset()
- try:
- curidx = tracker.definition_index(memref.origin)
- except KeyError:
- return
- curop = self.operations[curidx]
- while True:
- self.integral_mod.inspect_operation(curop)
- if self.integral_mod.is_const_mod:
- self.integral_mod.update_memory_ref(memref)
- else:
- break # an operation that is not tractable
- for dep in self.depends(curidx):
- curop = self.operations[dep.idx_from]
- if curop.result == memref.origin:
- curidx = dep.idx_from
- break
- else:
- break # cannot go further, this might be the label, or a
constant
-
- 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.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, opw):
- for dep in self.adjacent_list[opw]:
- if opw.opidx < dep.to.opidx:
- yield dep
-
- def depends_count(self, idx):
- i = 0
- for _ in self.depends(idx):
- i += 1
- return i
-
- def i_depends(self, idx):
- opw = self.operations[idx]
- return self.depends(opw)
- def depends(self, opw):
- for dep in self.adjacent_list[opw]:
- if opw.opidx > dep.at.opidx:
- yield dep
-
- def dependencies(self, idx):
- return self.adjacent_list[idx]
-
- def independent(self, ai, bi):
- """ An instruction depends on another if there is a dependency path
from
- A to B. It is not enough to check only if A depends on B, because
- due to transitive relations.
- """
- if ai == bi:
- return True
- if ai > bi:
- ai, bi = bi, ai
- stmt_indices = [bi]
- while len(stmt_indices) > 0:
- idx = stmt_indices.pop()
- for dep in self.depends(idx):
- if ai > dep.idx_from:
- # this points above ai (thus unrelevant)
- continue
-
- if dep.idx_from == ai:
- # dependent. There is a path from ai to bi
- return False
- stmt_indices.append(dep.idx_from)
- return True
-
- 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)
- """ 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.
- """
- if from_instr_idx > to_instr_idx:
- to_instr_idx, from_instr_idx = from_instr_idx, to_instr_idx
- for edge in self.instr_dependencies(from_instr_idx):
- if edge.idx_to == to_instr_idx:
- return edge
- return None
-
- 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]
+ tracker.define(arg, node, argcell=argcell)
def __repr__(self):
graph = "graph([\n"
- for i,l in enumerate(self.adjacent_list):
- graph += " " + str(i) + ": "
- for d in l:
- if i == d.idx_from:
- graph += str(d.idx_to) + ","
- else:
- graph += str(d.idx_from) + ","
+ for node in self.nodes:
+ graph += " " + str(node.opidx) + ": "
+ for dep in node.provides():
+ graph += "=>" + str(dep.to.opidx) + ","
+ graph += " | "
+ for dep in node.depends():
+ graph += "<=" + str(dep.to.opidx) + ","
graph += "\n"
return graph + " ])"
- def loads_from_complex_object(self, op):
- return rop._ALWAYS_PURE_LAST <= op.getopnum() <= rop._MALLOC_FIRST
-
- def modifies_complex_object(self, op):
- return rop.SETARRAYITEM_GC <= op.getopnum() <= rop.UNICODESETITEM
-
- def as_dot(self, operations):
+ def as_dot(self):
if not we_are_translated():
dot = "digraph dep_graph {\n"
- for i in range(len(self.adjacent_list)):
- op = operations[i]
+ for node in self.nodes:
+ op = node.getoperation()
op_str = str(op)
if op.is_guard():
op_str += " " + str(op.getfailargs())
- dot += " n%d [label=\"[%d]: %s\"];\n" % (i,i,op_str)
+ dot += " n%d [label=\"[%d]: %s\"];\n" %
(node.getindex(),node.getindex(),op_str)
dot += "\n"
- for i,alist in enumerate(self.adjacent_list):
- for dep in alist:
- if dep.idx_to > i:
- label = ''
- 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)
+ for node in self.nodes:
+ for dep in node.provides():
+ label = ''
+ if getattr(dep, 'label', None):
+ label = '[label="%s"]' % dep.label
+ dot += " n%d -> n%d %s;\n" %
(node.getindex(),dep.to_index(),label)
dot += "\n}\n"
return dot
raise NotImplementedError("dot cannot built at runtime")
@@ -562,6 +575,7 @@
def schedule_all(self, opindices):
while len(opindices) > 0:
+ print "sched"
opidx = opindices.pop()
for i,node in enumerate(self.schedulable_nodes):
if node == opidx:
@@ -586,7 +600,7 @@
#
# TODO for dep in self.graph.provides(node):
# candidate = dep.idx_to
- self.graph.adjacent_list[node] = []
+ node.clear_dependencies()
def is_schedulable(self, idx):
print "is sched", idx, "count:", self.graph.depends_count(idx),
self.graph.adjacent_list[idx]
@@ -610,7 +624,8 @@
return var
additive_func_source = """
- def operation_{name}(self, op, index):
+ def operation_{name}(self, node):
+ op = node.op
box_r = op.result
if not box_r:
return
@@ -638,7 +653,8 @@
del additive_func_source
multiplicative_func_source = """
- def operation_{name}(self, op, index):
+ def operation_{name}(self, node):
+ op = node.op
box_r = op.result
if not box_r:
return
@@ -670,10 +686,12 @@
del multiplicative_func_source
array_access_source = """
- def operation_{name}(self, op, index):
+ def operation_{name}(self, node):
+ op = node.getoperation()
descr = op.getdescr()
idx_ref = self.get_or_create(op.getarg(1))
- self.memory_refs[index] = MemoryRef(op, idx_ref, {raw_access})
+ node.memory_ref = MemoryRef(op, idx_ref, {raw_access})
+ self.memory_refs[node] = node.memory_ref
"""
exec py.code.Source(array_access_source
.format(name='RAW_LOAD',raw_access=True)).compile()
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_dependency.py
b/rpython/jit/metainterp/optimizeopt/test/test_dependency.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_dependency.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_dependency.py
@@ -8,29 +8,17 @@
IndexVar, MemoryRef)
from rpython.jit.metainterp.resoperation import rop, ResOperation
-class IntWrapper(object):
- def __init__(self,number):
- self.transitive = False
- number_s = str(number)
- if number_s.endswith("?"):
- self.transitive = True
- self.number = int(number_s[:-1])
- else:
- self.number = int(number_s)
- def clone(self):
- iw = IntWrapper(self.number)
- iw.transitive = self.transitive
- return iw
- def __str__(self):
- return str(self.number)
-
class DependencyBaseTest(BaseTest):
- def build_dependency(self, ops, refs = False):
+ def setup_method(self, method):
+ self.test_name = method.__name__
+
+ def build_dependency(self, ops):
loop = self.parse_loop(ops)
self.last_graph = DependencyGraph(loop.operations)
- for i in range(len(self.last_graph.adjacent_list)):
- self.assert_independent(i,i)
+ self._write_dot_and_convert_to_svg(self.last_graph, self.test_name)
+ for node in self.last_graph.nodes:
+ assert node.independent(node)
return self.last_graph
def parse_loop(self, ops):
@@ -42,77 +30,70 @@
loop.operations[-1].setdescr(token)
return loop
- def assert_edges(self, graph, edge_list):
+ def assert_edges(self, graph, edge_list, exceptions):
""" Check if all dependencies are met. for complex cases
adding None instead of a list of integers skips the test.
This checks both if a dependency forward and backward exists.
"""
- assert len(edge_list) == len(graph.adjacent_list)
+ assert len(edge_list) == len(graph.nodes)
for idx,edges in enumerate(edge_list):
if edges is None:
continue
- dependencies = graph.adjacent_list[idx][:]
- for edge in edges:
- if isinstance(edge,int):
- edge = IntWrapper(edge)
- dependency = graph.instr_dependency(idx,edge.number)
- if edge < idx:
- dependency = graph.instr_dependency(edge.number, idx)
- if dependency is None and not edge.transitive:
- self._write_dot_and_convert_to_svg(graph,
graph.operations, 'except')
+ node_a = graph.getnode(idx)
+ dependencies = node_a.provides()[:]
+ for idx_b in edges:
+ node_b = graph.getnode(idx_b)
+ dependency = node_a.getedge_to(node_b)
+ if dependency is None and idx_b not in
exceptions.setdefault(idx,[]):
+ #self._write_dot_and_convert_to_svg(graph, graph.nodes,
'except')
assert dependency is not None, \
" it is expected that instruction at index" + \
" %s depends on instr on index %s but it does not.\n%s"
\
- % (idx, edge, graph)
+ % (node_a, node_b, graph)
elif dependency is not None:
dependencies.remove(dependency)
assert dependencies == [], \
"dependencies unexpected %s.\n%s" \
% (dependencies,graph)
- def assert_graph_equal(self, ga, gb):
- assert len(ga.adjacent_list) == len(gb.adjacent_list)
- for i in range(len(ga.adjacent_list)):
- la = ga.adjacent_list[i]
- lb = gb.adjacent_list[i]
- assert len(la) == len(lb)
- assert sorted([l.idx_to for l in la]) == \
- sorted([l.idx_to for l in lb])
- assert sorted([l.idx_from for l in la]) == \
- sorted([l.idx_from for l in lb])
- def assert_dependencies(self, ops, memref=False, full_check=True):
- graph = self.build_dependency(ops, memref)
+ def assert_dependencies(self, ops, full_check=True):
+ graph = self.build_dependency(ops)
import re
deps = {}
+ exceptions = {}
for i,line in enumerate(ops.splitlines()):
dep_pattern = re.compile("#\s*(\d+):")
dep_match = dep_pattern.search(line)
if dep_match:
label = int(dep_match.group(1))
deps_list = []
- deps[label] = [IntWrapper(d) for d in
line[dep_match.end():].split(',') if len(d) > 0]
+ deps[label] = []
+ for to in [d for d in line[dep_match.end():].split(',') if
len(d) > 0]:
+ exception = to.endswith("?")
+ if exception:
+ to = to[:-1]
+ exceptions.setdefault(label,[]).append(int(to))
+ deps[label].append(int(to))
if full_check:
edges = [ None ] * len(deps)
for k,l in deps.items():
edges[k] = l
- for k,l in deps.items():
- for rk in l:
- if rk.number > k:
- iw = IntWrapper(k)
- iw.transitive = rk.transitive
- edges[rk.number].append(iw)
- self.assert_edges(graph, edges)
+ self.assert_edges(graph, edges, exceptions)
return graph
def assert_independent(self, a, b):
- assert self.last_graph.independent(a,b), "{a} and {b} are
dependent!".format(a=a,b=b)
+ a = self.last_graph.getnode(a)
+ b = self.last_graph.getnode(b)
+ assert a.independent(b), "{a} and {b} are dependent!".format(a=a,b=b)
def assert_dependent(self, a, b):
- assert not self.last_graph.independent(a,b), "{a} and {b} are
independent!".format(a=a,b=b)
+ a = self.last_graph.getnode(a)
+ b = self.last_graph.getnode(b)
+ assert not a.independent(b), "{a} and {b} are
independent!".format(a=a,b=b)
- def _write_dot_and_convert_to_svg(self, graph, ops, filename):
- dot = graph.as_dot(ops)
+ def _write_dot_and_convert_to_svg(self, graph, filename):
+ dot = graph.as_dot()
with open('/tmp/_'+filename+'.dot', 'w') as fd:
fd.write(dot)
with open('/tmp/'+filename+'.svg', 'w') as fd:
@@ -136,6 +117,10 @@
assert not m1.is_adjacent_to(m2)
assert not m2.is_adjacent_to(m1)
+ def getmemref(self, idx):
+ node = self.last_graph.getnode(idx)
+ return self.last_graph.memory_refs[node]
+
class BaseTestDependencyGraph(DependencyBaseTest):
def test_dependency_empty(self):
@@ -333,8 +318,8 @@
setarrayitem_raw(p0, i2, 2, descr=chararraydescr) # 3: 4
jump(p0, i1) # 4:
"""
- self.assert_dependencies(ops, memref=True, full_check=True)
- assert len(self.last_graph.adjacent_list[1]) > 1
+ self.assert_dependencies(ops, full_check=True)
+ assert self.last_graph.getnode(1).provides_count() == 1
self.assert_independent(1,2)
self.assert_independent(1,3) # they modify 2 different cells
@@ -363,7 +348,7 @@
guard_true(i25) [i7, i22, i5, i4, i3, i21, i19, i24] # 20:
jump(i24, i19, i21, i3, i4, i5, i22, i7) # 21:
"""
- self.assert_dependencies(ops, memref=True, full_check=False)
+ self.assert_dependencies(ops, full_check=False)
self.assert_dependent(2,12)
class TestLLtype(BaseTestDependencyGraph, LLtypeMixin):
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
@@ -28,23 +28,12 @@
jitdriver_sd = FakeJitDriverStaticData()
- def setup_method(self, method):
- self.test_name = method.__name__
-
- def build_dependency(self, ops):
- loop = self.parse_loop(ops)
- graph = DependencyGraph(loop)
- self.assert_acyclic(graph)
- return graph
-
- def assert_acyclic(self, graph):
- pass
-
def parse_loop(self, ops):
loop = self.parse(ops, postprocess=self.postprocess)
token = JitCellToken()
- loop.operations = [ResOperation(rop.LABEL, loop.inputargs, None,
- descr=TargetToken(token))] + loop.operations
+ loop.operations = \
+ [ResOperation(rop.LABEL, loop.inputargs, None,
descr=TargetToken(token))] + \
+ loop.operations
if loop.operations[-1].getopnum() == rop.JUMP:
loop.operations[-1].setdescr(token)
return loop
@@ -71,6 +60,7 @@
opt.clear_newoperations()
opt.build_dependency_graph()
self.last_graph = opt.dependency_graph
+ self._write_dot_and_convert_to_svg(self.last_graph, self.test_name)
return opt
def init_packset(self, loop, unroll_factor = -1):
@@ -80,7 +70,6 @@
def extend_packset(self, loop, unroll_factor = -1):
opt = self.vectoroptimizer_unrolled(loop, unroll_factor)
- self._write_dot_and_convert_to_svg(opt.dependency_graph,
opt.loop.operations, 'extend_packset')
opt.find_adjacent_memory_refs()
opt.extend_packset()
return opt
@@ -95,7 +84,6 @@
def schedule(self, loop, unroll_factor = -1):
opt = self.vectoroptimizer_unrolled(loop, unroll_factor)
opt.find_adjacent_memory_refs()
- self._write_dot_and_convert_to_svg(opt.dependency_graph,
opt.loop.operations, self.test_name)
opt.extend_packset()
opt.combine_packset()
opt.schedule()
@@ -148,6 +136,11 @@
else:
pytest.fail("can't find a pack set for indices {x},{y}" \
.format(x=x,y=y))
+ def assert_has_memory_ref_at(self, idx):
+ node = self.last_graph.nodes[idx]
+ assert node in self.last_graph.memory_refs, \
+ "operation %s at pos %d has no memory ref!" % \
+ (node.getoperation(), node.getindex())
class BaseTestVectorize(VecTestHelper):
@@ -193,11 +186,13 @@
it is unrolled 16 times. (it is the smallest type in the trace) """
ops = """
[p0,i0]
+ guard_no_early_exit() []
raw_load(p0,i0,descr=chararraydescr)
jump(p0,i0)
"""
opt_ops = """
[p0,i0]
+ guard_no_early_exit() []
{}
jump(p0,i0)
""".format(('\n' + ' '
*8).join(['raw_load(p0,i0,descr=chararraydescr)'] * 16))
@@ -254,9 +249,8 @@
jump(p0,i0)
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
- vopt.build_dependency_graph()
- assert 1 in vopt.dependency_graph.memory_refs
assert len(vopt.dependency_graph.memory_refs) == 1
+ self.assert_has_memory_ref_at(1)
def test_array_operation_indices_unrolled_1(self):
ops = """
@@ -265,10 +259,9 @@
jump(p0,i0)
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),1)
- vopt.build_dependency_graph()
- assert 1 in vopt.dependency_graph.memory_refs
- assert 2 in vopt.dependency_graph.memory_refs
assert len(vopt.dependency_graph.memory_refs) == 2
+ self.assert_has_memory_ref_at(1)
+ self.assert_has_memory_ref_at(2)
def test_array_operation_indices_unrolled_2(self):
ops = """
@@ -279,17 +272,19 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
vopt.build_dependency_graph()
- assert 1 in vopt.dependency_graph.memory_refs
- assert 2 in vopt.dependency_graph.memory_refs
assert len(vopt.dependency_graph.memory_refs) == 2
+ self.assert_has_memory_ref_at(1)
+ self.assert_has_memory_ref_at(2)
+ #
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),1)
+ assert len(vopt.dependency_graph.memory_refs) == 4
for i in [1,2,3,4]:
- assert i in vopt.dependency_graph.memory_refs
- assert len(vopt.dependency_graph.memory_refs) == 4
+ self.assert_has_memory_ref_at(i)
+ #
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),3)
+ assert len(vopt.dependency_graph.memory_refs) == 8
for i in [1,2,3,4,5,6,7,8]:
- assert i in vopt.dependency_graph.memory_refs
- assert len(vopt.dependency_graph.memory_refs) == 8
+ self.assert_has_memory_ref_at(i)
def test_array_memory_ref_adjacent_1(self):
ops = """
@@ -300,15 +295,15 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),1)
self.assert_edges(vopt.dependency_graph,
- [ [1,2,3,5], [0,5], [0,3,4], [0,2,5], [2,5], [0,4,1,3] ])
+ [ [1,2,3,5], [5], [3,4], [5], [5], [] ], {})
vopt.find_adjacent_memory_refs()
- assert 1 in vopt.dependency_graph.memory_refs
- assert 3 in vopt.dependency_graph.memory_refs
+ self.assert_has_memory_ref_at(1)
+ self.assert_has_memory_ref_at(3)
assert len(vopt.dependency_graph.memory_refs) == 2
- mref1 = vopt.dependency_graph.memory_refs[1]
- mref3 = vopt.dependency_graph.memory_refs[3]
+ mref1 = self.getmemref(1)
+ mref3 = self.getmemref(3)
assert isinstance(mref1, MemoryRef)
assert isinstance(mref3, MemoryRef)
@@ -323,7 +318,7 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
vopt.find_adjacent_memory_refs()
- mref1 = vopt.dependency_graph.memory_refs[1]
+ mref1 = self.getmemref(1)
assert isinstance(mref1, MemoryRef)
assert mref1.index_var.coefficient_mul == 1
assert mref1.index_var.constant == 0
@@ -337,7 +332,7 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
vopt.find_adjacent_memory_refs()
- mref1 = vopt.dependency_graph.memory_refs[2]
+ mref1 = self.getmemref(2)
assert isinstance(mref1, MemoryRef)
assert mref1.index_var.coefficient_mul == 1
assert mref1.index_var.constant == 1
@@ -351,7 +346,7 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
vopt.find_adjacent_memory_refs()
- mref1 = vopt.dependency_graph.memory_refs[2]
+ mref1 = self.getmemref(2)
assert isinstance(mref1, MemoryRef)
assert mref1.index_var.coefficient_mul == 1
assert mref1.index_var.constant == -1
@@ -366,7 +361,7 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
vopt.find_adjacent_memory_refs()
- mref1 = vopt.dependency_graph.memory_refs[3]
+ mref1 = self.getmemref(3)
assert isinstance(mref1, MemoryRef)
assert mref1.index_var.coefficient_mul == 3
assert mref1.index_var.constant == 3
@@ -383,7 +378,7 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
vopt.find_adjacent_memory_refs()
- mref1 = vopt.dependency_graph.memory_refs[5]
+ mref1 = self.getmemref(5)
assert isinstance(mref1, MemoryRef)
assert mref1.index_var.coefficient_mul == 18
assert mref1.index_var.constant == 48
@@ -401,7 +396,7 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
vopt.find_adjacent_memory_refs()
- mref1 = vopt.dependency_graph.memory_refs[7]
+ mref1 = self.getmemref(7)
assert isinstance(mref1, MemoryRef)
assert mref1.index_var.coefficient_mul == 1026
assert mref1.index_var.coefficient_div == 1
@@ -419,7 +414,7 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
vopt.find_adjacent_memory_refs()
- mref1 = vopt.dependency_graph.memory_refs[5]
+ mref1 = self.getmemref(5)
assert isinstance(mref1, MemoryRef)
assert mref1.index_var.coefficient_mul == 6
assert mref1.index_var.coefficient_div == 1
@@ -450,21 +445,21 @@
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),1)
self.assert_edges(vopt.dependency_graph,
[ [1,2,3,4,5,7,9],
- [0,9], [0,5,6], [0,9], [0,7,8],
- [0,2,9], [2,9], [0,4,9], [4,9],
- [0,6,8,1,3,5,7],
- ])
+ [9], [5,6], [9], [7,8],
+ [9], [9], [9], [9],
+ [],
+ ], {})
vopt.find_adjacent_memory_refs()
for i in [1,3,5,7]:
- assert i in vopt.dependency_graph.memory_refs
+ self.assert_has_memory_ref_at(i)
assert len(vopt.dependency_graph.memory_refs) == 4
- mref1 = vopt.dependency_graph.memory_refs[1]
- mref3 = vopt.dependency_graph.memory_refs[3]
- mref5 = vopt.dependency_graph.memory_refs[5]
- mref7 = vopt.dependency_graph.memory_refs[7]
+ mref1 = self.getmemref(1)
+ mref3 = self.getmemref(3)
+ mref5 = self.getmemref(5)
+ mref7 = self.getmemref(7)
assert isinstance(mref1, MemoryRef)
assert isinstance(mref3, MemoryRef)
assert isinstance(mref5, MemoryRef)
@@ -486,7 +481,7 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
vopt.find_adjacent_memory_refs()
- mref = vopt.dependency_graph.memory_refs[3]
+ mref = self.getmemref(3)
assert mref.index_var.coefficient_div == 16
ops = """
[p0,i0]
@@ -497,7 +492,7 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
vopt.find_adjacent_memory_refs()
- mref = vopt.dependency_graph.memory_refs[3]
+ mref = self.getmemref(3)
assert mref.index_var.coefficient_div == 2
assert mref.index_var.constant == 4
ops = """
@@ -512,8 +507,8 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
vopt.find_adjacent_memory_refs()
- mref = vopt.dependency_graph.memory_refs[3]
- mref2 = vopt.dependency_graph.memory_refs[6]
+ mref = self.getmemref(3)
+ mref2 = self.getmemref(6)
self.assert_memory_ref_not_adjacent(mref, mref2)
assert mref != mref2
@@ -532,8 +527,8 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
vopt.find_adjacent_memory_refs()
- mref = vopt.dependency_graph.memory_refs[3]
- mref2 = vopt.dependency_graph.memory_refs[7]
+ mref = self.getmemref(3)
+ mref2 = self.getmemref(7)
self.assert_memory_ref_not_adjacent(mref, mref2)
assert mref == mref2
@@ -552,8 +547,8 @@
"""
vopt = self.vectoroptimizer_unrolled(self.parse_loop(ops),0)
vopt.find_adjacent_memory_refs()
- mref = vopt.dependency_graph.memory_refs[3]
- mref2 = vopt.dependency_graph.memory_refs[7]
+ mref = self.getmemref(3)
+ mref2 = self.getmemref(7)
self.assert_memory_ref_not_adjacent(mref, mref2)
assert mref != mref2
@@ -580,7 +575,7 @@
"""
loop = self.parse_loop(ops)
vopt = self.init_packset(loop,1)
- assert vopt.dependency_graph.independent(1,5)
+ self.assert_independent(1,5)
assert vopt.packset is not None
assert len(vopt.dependency_graph.memory_refs) == 2
assert len(vopt.packset.packs) == 1
@@ -608,7 +603,7 @@
for i in range(3):
x = (i+1)*2
y = x + 2
- assert vopt.dependency_graph.independent(x,y)
+ self.assert_independent(x,y)
self.assert_packset_contains_pair(vopt.packset, x,y)
def test_packset_init_2(self):
@@ -629,19 +624,19 @@
for j in range(15):
try:
if i-4 == j or i+4 == j:
- mref1 = vopt.dependency_graph.memory_refs[i]
- mref2 = vopt.dependency_graph.memory_refs[j]
+ mref1 = self.getmemref(i)
+ mref2 = self.getmemref(j)
assert mref1.is_adjacent_to(mref2)
else:
- mref1 = vopt.dependency_graph.memory_refs[i]
- mref2 = vopt.dependency_graph.memory_refs[j]
+ mref1 = self.getmemref(i)
+ mref2 = self.getmemref(j)
assert not mref1.is_adjacent_to(mref2)
except KeyError:
pass
for i in range(15):
x = (i+1)*4
y = x + 4
- assert vopt.dependency_graph.independent(x,y)
+ self.assert_independent(x,y)
self.assert_packset_contains_pair(vopt.packset, x, y)
def test_isomorphic_operations(self):
@@ -680,7 +675,7 @@
loop = self.parse_loop(ops)
vopt = self.extend_packset(loop,1)
assert len(vopt.dependency_graph.memory_refs) == 2
- assert vopt.dependency_graph.independent(5,10) == True
+ self.assert_independent(5,10)
assert len(vopt.packset.packs) == 2
self.assert_packset_empty(vopt.packset, len(loop.operations),
[(5,10), (4,9)])
@@ -699,9 +694,9 @@
loop = self.parse_loop(ops)
vopt = self.extend_packset(loop,1)
assert len(vopt.dependency_graph.memory_refs) == 4
- assert vopt.dependency_graph.independent(4,10)
- assert vopt.dependency_graph.independent(5,11)
- assert vopt.dependency_graph.independent(6,12)
+ self.assert_independent(4,10)
+ self.assert_independent(5,11)
+ self.assert_independent(6,12)
assert len(vopt.packset.packs) == 3
self.assert_packset_empty(vopt.packset, len(loop.operations),
[(5,11), (4,10), (6,12)])
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
@@ -5,7 +5,7 @@
from rpython.jit.metainterp.optimizeopt.optimizer import Optimizer,
Optimization
from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
from rpython.jit.metainterp.optimizeopt.dependency import (DependencyGraph,
- MemoryRef, Scheduler, SchedulerData)
+ MemoryRef, Scheduler, SchedulerData, Node)
from rpython.jit.metainterp.resoperation import (rop, ResOperation)
from rpython.jit.metainterp.resume import Snapshot
from rpython.rlib.debug import debug_print, debug_start, debug_stop
@@ -61,10 +61,6 @@
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 """
@@ -252,7 +248,7 @@
def build_dependency_graph(self):
self.dependency_graph = DependencyGraph(self.loop.operations)
- self.relax_guard_dependencies()
+ #self.relax_guard_dependencies()
def find_adjacent_memory_refs(self):
""" the pre pass already builds a hash of memory references and the
@@ -269,20 +265,18 @@
self.smallest_type_bytes)
memory_refs = self.dependency_graph.memory_refs.items()
# initialize the pack set
- for a_opidx,a_memref in memory_refs:
- for b_opidx,b_memref in memory_refs:
+ for node_a,memref_a in memory_refs:
+ for node_b,memref_b in memory_refs:
# instead of compare every possible combination and
# exclue a_opidx == b_opidx only consider the ones
# that point forward:
- if a_opidx < b_opidx:
- #print "point forward[", a_opidx, "]", a_memref,
"[",b_opidx,"]", b_memref
- if a_memref.is_adjacent_to(b_memref):
- #print " -> adjacent[", a_opidx, "]", a_memref,
"[",b_opidx,"]", b_memref
- if self.packset.can_be_packed(a_opidx, b_opidx,
- a_memref, b_memref):
- #print " =-=-> can be packed[", a_opidx, "]",
a_memref, "[",b_opidx,"]", b_memref
- self.packset.add_pair(a_opidx, b_opidx,
- a_memref, b_memref)
+ if node_a.is_before(node_b):
+ #print "point forward[", a_opidx, "]", memref_a,
"[",b_opidx,"]", memref_b
+ if memref_a.is_adjacent_to(memref_b):
+ #print " -> adjacent[", a_opidx, "]", memref_a,
"[",b_opidx,"]", memref_b
+ if self.packset.can_be_packed(node_a, node_b):
+ #print " =-=-> can be packed[", a_opidx, "]",
memref_a, "[",b_opidx,"]", memref_b
+ self.packset.add_pair(node_a, node_b)
def extend_packset(self):
pack_count = self.packset.pack_count()
@@ -296,36 +290,30 @@
def follow_use_defs(self, pack):
assert isinstance(pack, Pair)
- lref = pack.left.memref
- rref = pack.right.memref
- for ldef in self.dependency_graph.depends(pack.left.opidx):
- for rdef in self.dependency_graph.depends(pack.right.opidx):
- ldef_idx = ldef.idx_from
- rdef_idx = rdef.idx_from
- if ldef_idx != rdef_idx and \
- self.packset.can_be_packed(ldef_idx, rdef_idx, lref, rref):
- savings = self.packset.estimate_savings(ldef_idx, rdef_idx,
- pack, False)
+ for ldep in pack.left.depends():
+ for rdep in pack.right.depends():
+ lnode = ldep.to
+ rnode = rdep.to
+ if lnode != rnode and self.packset.can_be_packed(lnode, rnode):
+ savings = self.packset.estimate_savings(lnode, rnode,
pack, False)
if savings >= 0:
- self.packset.add_pair(ldef_idx, rdef_idx, lref, rref)
+ self.packset.add_pair(lnode, rnode)
def follow_def_uses(self, pack):
assert isinstance(pack, Pair)
savings = -1
candidate = (-1,-1, None, None)
- lref = pack.left.memref
- rref = pack.right.memref
- for luse in self.dependency_graph.provides(pack.left.opidx):
- for ruse in self.dependency_graph.provides(pack.right.opidx):
- luse_idx = luse.idx_to
- ruse_idx = ruse.idx_to
- if luse_idx != ruse_idx and \
- self.packset.can_be_packed(luse_idx, ruse_idx, lref, rref):
- est_savings = self.packset.estimate_savings(luse_idx,
ruse_idx,
- pack, True)
+ for ldep in pack.left.depends():
+ for rdep in pack.right.depends():
+ lnode = ldep.to
+ rnode = rdep.to
+ if lnode != rnode and \
+ self.packset.can_be_packed(lnode, rnode):
+ est_savings = \
+ self.packset.estimate_savings(lnode, rnode, pack, True)
if est_savings > savings:
savings = est_savings
- candidate = (luse_idx, ruse_idx, lref, rref)
+ candidate = (lnode, rnode)
#
if savings >= 0:
self.packset.add_pair(*candidate)
@@ -360,13 +348,12 @@
self.clear_newoperations()
scheduler = Scheduler(self.dependency_graph, VecScheduleData())
while scheduler.has_more_to_schedule():
- candidate_index = scheduler.next_schedule_index()
- candidate = self.loop.operations[candidate_index]
- pack = self.packset.pack_for_operation(candidate, candidate_index)
+ candidate = scheduler.next_to_schedule()
+ pack = self.packset.pack_for_operation(candidate)
if pack:
self._schedule_pack(scheduler, pack)
else:
- self.emit_operation(candidate)
+ self.emit_operation(candidate.getoperation())
scheduler.schedule(0)
self.loop.operations = self._newoperations[:]
@@ -547,19 +534,16 @@
def pack_count(self):
return len(self.packs)
- def add_pair(self, lidx, ridx, lmemref=None, rmemref=None):
- l = PackOpWrapper(lidx, lmemref)
- r = PackOpWrapper(ridx, rmemref)
+ def add_pair(self, l, r):
self.packs.append(Pair(l,r))
- def can_be_packed(self, lop_idx, rop_idx, lmemref, rmemref):
- l_op = self.operations[lop_idx]
- r_op = self.operations[rop_idx]
- if isomorphic(l_op, r_op):
- if self.dependency_graph.independent(lop_idx, rop_idx):
+ def can_be_packed(self, lnode, rnode):
+ if isomorphic(lnode.getoperation(), rnode.getoperation()):
+ if lnode.independent(rnode):
for pack in self.packs:
- if pack.left.opidx == lop_idx or \
- pack.right.opidx == rop_idx:
+ # TODO save pack on Node
+ if pack.left.opidx == lnode.getindex() or \
+ pack.right.opidx == rnode.getindex():
return False
return True
return False
@@ -612,10 +596,10 @@
del self.packs[last_pos]
return last_pos
- def pack_for_operation(self, op, opidx):
+ def pack_for_operation(self, node):
for pack in self.packs:
- for op in pack.operations:
- if op.getopidx() == opidx:
+ for node2 in pack.operations:
+ if node == node2:
return pack
return None
@@ -640,8 +624,8 @@
class Pair(Pack):
""" A special Pack object with only two statements. """
def __init__(self, left, right):
- assert isinstance(left, PackOpWrapper)
- assert isinstance(right, PackOpWrapper)
+ assert isinstance(left, Node)
+ assert isinstance(right, Node)
self.left = left
self.right = right
Pack.__init__(self, [left, right])
@@ -650,20 +634,3 @@
if isinstance(other, Pair):
return self.left == other.left and \
self.right == other.right
-
-class PackOpWrapper(object):
- def __init__(self, opidx, memref = None):
- self.opidx = opidx
- self.memref = memref
-
- def getopidx(self):
- return self.opidx
-
- def __eq__(self, other):
- if isinstance(other, PackOpWrapper):
- return self.opidx == other.opidx and self.memref == other.memref
- return False
-
- def __repr__(self):
- return "PackOpWrapper(%d, %r)" % (self.opidx, self.memref)
-
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit