[pypy-commit] pypy vecopt2: packset combination (need to rewrite for rpython) and tests
plan_rich
noreply at buildbot.pypy.org
Tue May 5 09:45:45 CEST 2015
Author: Richard Plangger <rich at pasra.at>
Branch: vecopt2
Changeset: r77095:c675e35448fb
Date: 2015-03-27 11:34 +0100
http://bitbucket.org/pypy/pypy/changeset/c675e35448fb/
Log: packset combination (need to rewrite for rpython) and tests
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
@@ -289,10 +289,11 @@
dep_graph = self.build_dependency(ops)
self.assert_edges(dep_graph,
[ [1,2,3], [0,2], [0,1,3], [0,2] ])
+ self.assert_dependent(1,2)
+ self.assert_dependent(0,3)
def test_setarrayitem_same_modified_var_not_aliased(self):
- # #1 depends on #2, i1 and i2 might alias, reordering would destroy
- # coorectness
+ # #1 does NOT depend on #2, i1 and i2 are not aliased
ops="""
[p0, i1]
setarrayitem_raw(p0, i1, 1, descr=floatarraydescr) #1
@@ -303,9 +304,13 @@
dep_graph = self.build_dependency(ops, True)
self.assert_edges(dep_graph,
[ [1,2,3,4], [0], [0,3], [0,2,4], [0,3] ])
+ self.assert_independent(1,2)
+ self.assert_independent(1,3)
dep_graph = self.build_dependency(ops)
self.assert_edges(dep_graph,
[ [1,2,4], [0,3], [0,3], [1,2,4], [0,3] ])
+ self.assert_independent(1,2)
+ self.assert_dependent(1,3)
class TestLLtype(BaseTestDependencyGraph, LLtypeMixin):
pass
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
@@ -10,7 +10,8 @@
import rpython.jit.metainterp.optimizeopt.virtualize as virtualize
from rpython.jit.metainterp.optimizeopt.dependency import DependencyGraph
from rpython.jit.metainterp.optimizeopt.unroll import Inliner
-from rpython.jit.metainterp.optimizeopt.vectorize import VectorizingOptimizer, MemoryRef, isomorphic
+from rpython.jit.metainterp.optimizeopt.vectorize import (VectorizingOptimizer, MemoryRef,
+ isomorphic, Pair)
from rpython.jit.metainterp.optimize import InvalidLoop
from rpython.jit.metainterp.history import ConstInt, BoxInt, get_const_ptr_for_string
from rpython.jit.metainterp import executor, compile, resume
@@ -59,17 +60,25 @@
opt.loop.operations = opt.get_newoperations()
return opt
- def init_pack_set(self, loop, unroll_factor = -1):
+ def init_packset(self, loop, unroll_factor = -1):
opt = self.vec_optimizer_unrolled(loop, unroll_factor)
opt.build_dependency_graph()
opt.find_adjacent_memory_refs()
return opt
- def extend_pack_set(self, loop, unroll_factor = -1):
+ def extend_packset(self, loop, unroll_factor = -1):
opt = self.vec_optimizer_unrolled(loop, unroll_factor)
opt.build_dependency_graph()
opt.find_adjacent_memory_refs()
- opt.extend_pack_set()
+ opt.extend_packset()
+ return opt
+
+ def combine_packset(self, loop, unroll_factor = -1):
+ opt = self.vec_optimizer_unrolled(loop, unroll_factor)
+ opt.build_dependency_graph()
+ opt.find_adjacent_memory_refs()
+ opt.extend_packset()
+ opt.combine_packset()
return opt
def assert_unroll_loop_equals(self, loop, expected_loop, \
@@ -91,29 +100,34 @@
for i,op in enumerate(loop.operations):
print(i,op)
+ def assert_pack(self, pack, indices):
+ assert len(pack.operations) == len(indices)
+ for op,i in zip(pack.operations, indices):
+ assert op.opidx == i
+
def assert_packset_empty(self, packset, instr_count, exceptions):
-
for a,b in exceptions:
- self.assert_packset_contains(packset, a, b)
+ self.assert_packset_contains_pair(packset, a, b)
import itertools
combintations = set(itertools.product(range(instr_count),
range(instr_count)))
combintations -= set(exceptions)
for a,b in combintations:
- self.assert_packset_not_contains(packset, a, b)
+ self.assert_packset_not_contains_pair(packset, a, b)
- def assert_packset_not_contains(self, packset, x, y):
+ def assert_packset_not_contains_pair(self, packset, x, y):
for pack in packset.packs:
if pack.left.opidx == x and \
pack.right.opidx == y:
pytest.fail("must not find packset with indices {x},{y}" \
.format(x=x,y=y))
- def assert_packset_contains(self, packset, x, y):
+ def assert_packset_contains_pair(self, packset, x, y):
for pack in packset.packs:
- if pack.left.opidx == x and \
- pack.right.opidx == y:
- break
+ if isinstance(pack, Pair):
+ if pack.left.opidx == x and \
+ pack.right.opidx == y:
+ break
else:
pytest.fail("can't find a pack set for indices {x},{y}" \
.format(x=x,y=y))
@@ -582,11 +596,11 @@
jump(p0,i1)
"""
loop = self.parse_loop(ops)
- vopt = self.init_pack_set(loop,1)
+ vopt = self.init_packset(loop,1)
assert vopt.dependency_graph.independent(1,5)
- assert vopt.pack_set is not None
+ assert vopt.packset is not None
assert len(vopt.vec_info.memory_refs) == 2
- assert len(vopt.pack_set.packs) == 1
+ assert len(vopt.packset.packs) == 1
def test_packset_init_raw_load_not_adjacent_and_adjacent(self):
ops = """
@@ -595,9 +609,9 @@
jump(p0,i0)
"""
loop = self.parse_loop(ops)
- vopt = self.init_pack_set(loop,3)
+ vopt = self.init_packset(loop,3)
assert len(vopt.vec_info.memory_refs) == 4
- assert len(vopt.pack_set.packs) == 0
+ assert len(vopt.packset.packs) == 0
ops = """
[p0,i0]
i2 = int_add(i0,1)
@@ -605,14 +619,14 @@
jump(p0,i2)
"""
loop = self.parse_loop(ops)
- vopt = self.init_pack_set(loop,3)
+ vopt = self.init_packset(loop,3)
assert len(vopt.vec_info.memory_refs) == 4
- assert len(vopt.pack_set.packs) == 3
+ assert len(vopt.packset.packs) == 3
for i in range(3):
x = (i+1)*2
y = x + 2
assert vopt.dependency_graph.independent(x,y)
- self.assert_packset_contains(vopt.pack_set, x,y)
+ self.assert_packset_contains_pair(vopt.packset, x,y)
def test_packset_init_2(self):
ops = """
@@ -624,9 +638,9 @@
jump(p0,i1)
"""
loop = self.parse_loop(ops)
- vopt = self.init_pack_set(loop,15)
+ vopt = self.init_packset(loop,15)
assert len(vopt.vec_info.memory_refs) == 16
- assert len(vopt.pack_set.packs) == 15
+ assert len(vopt.packset.packs) == 15
# assure that memory refs are not adjacent for all
for i in range(15):
for j in range(15):
@@ -645,7 +659,7 @@
x = (i+1)*4
y = x + 4
assert vopt.dependency_graph.independent(x,y)
- self.assert_packset_contains(vopt.pack_set, x, y)
+ self.assert_packset_contains_pair(vopt.packset, x, y)
def test_isomorphic_operations(self):
ops_src = """
@@ -681,12 +695,11 @@
jump(p0,i1)
"""
loop = self.parse_loop(ops)
- vopt = self.extend_pack_set(loop,1)
- self.debug_print_operations(loop)
+ vopt = self.extend_packset(loop,1)
assert len(vopt.vec_info.memory_refs) == 2
assert vopt.dependency_graph.independent(5,10) == True
- assert len(vopt.pack_set.packs) == 2
- self.assert_packset_empty(vopt.pack_set, len(loop.operations),
+ assert len(vopt.packset.packs) == 2
+ self.assert_packset_empty(vopt.packset, len(loop.operations),
[(5,10), (4,9)])
def test_packset_extend_load_modify_store(self):
@@ -701,15 +714,76 @@
jump(p0,i1)
"""
loop = self.parse_loop(ops)
- vopt = self.extend_pack_set(loop,1)
- self.debug_print_operations(loop)
+ vopt = self.extend_packset(loop,1)
assert len(vopt.vec_info.memory_refs) == 4
assert vopt.dependency_graph.independent(4,10)
assert vopt.dependency_graph.independent(5,11)
assert vopt.dependency_graph.independent(6,12)
- assert len(vopt.pack_set.packs) == 3
- self.assert_packset_empty(vopt.pack_set, len(loop.operations),
+ assert len(vopt.packset.packs) == 3
+ self.assert_packset_empty(vopt.packset, len(loop.operations),
[(5,11), (4,10), (6,12)])
+ def test_packset_combine_simple(self):
+ ops = """
+ [p0,i0]
+ i3 = getarrayitem_gc(p0, i0, descr=floatarraydescr)
+ i1 = int_add(i0,1)
+ jump(p0,i1)
+ """
+ loop = self.parse_loop(ops)
+ vopt = self.combine_packset(loop,3)
+ assert len(vopt.vec_info.memory_refs) == 4
+ assert len(vopt.packset.packs) == 1
+ self.assert_pack(vopt.packset.packs[0], (1,3,5,7))
+
+ def test_packset_combine_2_loads_in_trace(self):
+ ops = """
+ [p0,i0]
+ i3 = getarrayitem_gc(p0, i0, descr=floatarraydescr)
+ i1 = int_add(i0,1)
+ i4 = getarrayitem_gc(p0, i1, descr=floatarraydescr)
+ i2 = int_add(i1,1)
+ jump(p0,i2)
+ """
+ loop = self.parse_loop(ops)
+ vopt = self.combine_packset(loop,3)
+ assert len(vopt.vec_info.memory_refs) == 8
+ assert len(vopt.packset.packs) == 1
+ self.assert_pack(vopt.packset.packs[0], (1,3,5,7,9,11,13,15))
+
+ def test_packset_combine_2_loads_one_redundant(self):
+ ops = """
+ [p0,i0]
+ i3 = getarrayitem_gc(p0, i0, descr=floatarraydescr)
+ i1 = int_add(i0,1)
+ i4 = getarrayitem_gc(p0, i1, descr=floatarraydescr)
+ jump(p0,i1)
+ """
+ pytest.skip("loop unrolling must apply redundant loop unrolling")
+ loop = self.parse_loop(ops)
+ vopt = self.combine_packset(loop,3)
+ assert len(vopt.vec_info.memory_refs) == 4
+ assert len(vopt.packset.packs) == 1
+ self.assert_pack(vopt.packset.packs[0], (1,3,5,7))
+
+ def test_packset_combine_no_candidates_packset_empty(self):
+ ops = """
+ []
+ jump()
+ """
+ vopt = self.combine_packset(self.parse_loop(ops),15)
+ assert len(vopt.vec_info.memory_refs) == 0
+ assert len(vopt.packset.packs) == 0
+
+ ops = """
+ [p0,i0]
+ i3 = getarrayitem_gc(p0, i0, descr=floatarraydescr)
+ jump(p0,i3)
+ """
+ loop = self.parse_loop(ops)
+ vopt = self.combine_packset(loop,15)
+ assert len(vopt.vec_info.memory_refs) == 16
+ assert len(vopt.packset.packs) == 0
+
class TestLLtype(BaseTestVectorize, LLtypeMixin):
pass
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
@@ -36,7 +36,8 @@
self.dependency_graph = None
self.first_debug_merge_point = False
self.last_debug_merge_point = None
- self.pack_set = None
+ self.packset = None
+ self.unroll_count = 0
def emit_unrolled_operation(self, op):
if op.getopnum() == rop.DEBUG_MERGE_POINT:
@@ -175,9 +176,9 @@
# stop, there is no chance to vectorize this trace
raise NotAVectorizeableLoop()
- unroll_count = self.get_unroll_count()
+ self.unroll_count = self.get_unroll_count()
- self.unroll_loop_iterations(self.loop, unroll_count)
+ self.unroll_loop_iterations(self.loop, self.unroll_count)
self.loop.operations = self.get_newoperations();
self.clear_newoperations();
@@ -199,7 +200,9 @@
loop = self.loop
operations = loop.operations
- self.pack_set = PackSet(self.dependency_graph, operations)
+ self.packset = PackSet(self.dependency_graph, operations,
+ self.unroll_count,
+ self.vec_info.smallest_type_bytes)
memory_refs = self.vec_info.memory_refs.items()
# initialize the pack set
for a_opidx,a_memref in memory_refs:
@@ -209,50 +212,76 @@
# that point forward:
if a_opidx < b_opidx:
if a_memref.is_adjacent_to(b_memref):
- if self.pack_set.can_be_packed(a_opidx, b_opidx):
- self.pack_set.add_pair(a_opidx, b_opidx,
+ if self.packset.can_be_packed(a_opidx, b_opidx,
+ a_memref, b_memref):
+ self.packset.add_pair(a_opidx, b_opidx,
a_memref, b_memref)
- def extend_pack_set(self):
- pack_count = self.pack_set.pack_count()
+ def extend_packset(self):
+ pack_count = self.packset.pack_count()
while True:
- for pack in self.pack_set.packs:
+ for pack in self.packset.packs:
self.follow_use_defs(pack)
self.follow_def_uses(pack)
- if pack_count == self.pack_set.pack_count():
+ if pack_count == self.packset.pack_count():
break
- pack_count = self.pack_set.pack_count()
+ pack_count = self.packset.pack_count()
def follow_use_defs(self, pack):
assert isinstance(pack, Pair)
+ lref = pack.left.memref
+ rref = pack.right.memref
for ldef in self.dependency_graph.get_defs(pack.left.opidx):
for rdef in self.dependency_graph.get_defs(pack.right.opidx):
ldef_idx = ldef.idx_from
rdef_idx = rdef.idx_from
if ldef_idx != rdef_idx and \
- self.pack_set.can_be_packed(ldef_idx, rdef_idx):
- savings = self.pack_set.estimate_savings(ldef_idx, rdef_idx)
+ self.packset.can_be_packed(ldef_idx, rdef_idx, lref, rref):
+ savings = self.packset.estimate_savings(ldef_idx, rdef_idx)
if savings >= 0:
- self.pack_set.add_pair(ldef_idx, rdef_idx)
+ self.packset.add_pair(ldef_idx, rdef_idx, lref, rref)
def follow_def_uses(self, pack):
assert isinstance(pack, Pair)
savings = -1
- candidate = (-1,-1)
+ candidate = (-1,-1, None, None)
+ lref = pack.left.memref
+ rref = pack.right.memref
for luse in self.dependency_graph.get_uses(pack.left.opidx):
for ruse in self.dependency_graph.get_uses(pack.right.opidx):
luse_idx = luse.idx_to
ruse_idx = ruse.idx_to
if luse_idx != ruse_idx and \
- self.pack_set.can_be_packed(luse_idx, ruse_idx):
- est_savings = self.pack_set.estimate_savings(luse_idx,
+ self.packset.can_be_packed(luse_idx, ruse_idx, lref, rref):
+ est_savings = self.packset.estimate_savings(luse_idx,
ruse_idx)
if est_savings > savings:
savings = est_savings
- candidate = (luse_idx, ruse_idx)
+ candidate = (luse_idx, ruse_idx, lref, rref)
+ #
+ if savings >= 0:
+ self.packset.add_pair(*candidate)
- if savings >= 0:
- self.pack_set.add_pair(*candidate)
+ def combine_packset(self):
+ changed = False
+ while True:
+ changed = False
+ for i,pack1 in enumerate(self.packset.packs):
+ for j,pack2 in enumerate(self.packset.packs):
+ if i == j:
+ continue
+ if pack1.rightmost_match_leftmost(pack2):
+ self.packset.combine(i,j)
+ changed = True
+ break
+ if pack2.rightmost_match_leftmost(pack1):
+ self.packset.combine(j,i)
+ changed = True
+ break
+ if changed:
+ break
+ if not changed:
+ break
def isomorphic(l_op, r_op):
""" Described in the paper ``Instruction-Isomorphism in Program Execution''.
@@ -278,20 +307,23 @@
class PackSet(object):
- def __init__(self, dependency_graph, operations):
+ def __init__(self, dependency_graph, operations, unroll_count,
+ smallest_type_bytes):
self.packs = []
self.dependency_graph = dependency_graph
self.operations = operations
+ self.unroll_count = unroll_count
+ self.smallest_type_bytes = smallest_type_bytes
def pack_count(self):
return len(self.packs)
- def add_pair(self, lidx, ridx, lmemref = None, rmemref = None):
+ def add_pair(self, lidx, ridx, lmemref=None, rmemref=None):
l = PackOpWrapper(lidx, lmemref)
r = PackOpWrapper(ridx, rmemref)
self.packs.append(Pair(l,r))
- def can_be_packed(self, lop_idx, rop_idx):
+ 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):
@@ -311,6 +343,15 @@
"""
return 0
+ def combine(self, i, j):
+ print "combine", i, j
+ pack_i = self.packs[i]
+ pack_j = self.packs[j]
+ operations = pack_i.operations
+ for op in pack_j.operations[1:]:
+ operations.append(op)
+ self.packs[i] = Pack(operations)
+ del self.packs[j]
class Pack(object):
""" A pack is a set of n statements that are:
@@ -321,6 +362,15 @@
def __init__(self, ops):
self.operations = ops
+ def rightmost_match_leftmost(self, other):
+ assert isinstance(other, Pack)
+ rightmost = self.operations[-1]
+ leftmost = other.operations[0]
+ return rightmost == leftmost
+
+ def __repr__(self):
+ return "Pack(%r)" % self.operations
+
class Pair(Pack):
""" A special Pack object with only two statements. """
def __init__(self, left, right):
@@ -345,6 +395,9 @@
return self.opidx == other.opidx and self.memref == other.memref
return False
+ def __repr__(self):
+ return "PackOpWrapper(%d, %r)" % (self.opidx, self.memref)
+
class LoopVectorizeInfo(object):
def __init__(self):
More information about the pypy-commit
mailing list