[pypy-commit] pypy backend-vector-ops: a first pass at vectorizing, a simple test passes

fijal noreply at buildbot.pypy.org
Thu Feb 2 13:17:23 CET 2012


Author: Maciej Fijalkowski <fijall at gmail.com>
Branch: backend-vector-ops
Changeset: r52026:dfaccda61176
Date: 2012-02-02 14:16 +0200
http://bitbucket.org/pypy/pypy/changeset/dfaccda61176/

Log:	a first pass at vectorizing, a simple test passes

diff --git a/pypy/jit/metainterp/executor.py b/pypy/jit/metainterp/executor.py
--- a/pypy/jit/metainterp/executor.py
+++ b/pypy/jit/metainterp/executor.py
@@ -274,7 +274,7 @@
 
 
 IGNORED = ['FLOAT_VECTOR_ADD', 'GETARRAYITEM_VECTOR_RAW',
-           'SETARRAYITEM_VECTOR_RAW']
+           'SETARRAYITEM_VECTOR_RAW', 'ASSERT_ALIGNED']
 
 def _make_execute_list():
     if 0:     # enable this to trace calls to do_xxx
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_vectorize.py b/pypy/jit/metainterp/optimizeopt/test/test_vectorize.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/optimizeopt/test/test_vectorize.py
@@ -0,0 +1,37 @@
+
+from pypy.jit.metainterp.optimizeopt.test.test_optimizebasic import BaseTestBasic, LLtypeMixin
+
+class TestVectorize(BaseTestBasic, LLtypeMixin):
+    enable_opts = "intbounds:rewrite:virtualize:string:earlyforce:pure:heap:unroll:vectorize"
+
+    def test_vectorize_basic(self):
+        ops = """
+        [p0, p1, p2, i0, i1, i2]
+        assert_aligned(p0, i0)
+        assert_aligned(p1, i1)
+        assert_aligned(p1, i2)
+        f0 = getarrayitem_raw(p0, i0, descr=arraydescr)
+        f1 = getarrayitem_raw(p1, i1, descr=arraydescr)
+        f2 = float_add(f0, f1)
+        setarrayitem_raw(p2, i2, f2, descr=arraydescr)
+        i0_1 = int_add(i0, 1)
+        i1_1 = int_add(1, i1)
+        i2_1 = int_add(i2, 1)
+        f0_1 = getarrayitem_raw(p0, i0_1, descr=arraydescr)
+        f1_1 = getarrayitem_raw(p1, i1_1, descr=arraydescr)
+        f2_1 = float_add(f0_1, f1_1)
+        setarrayitem_raw(p2, i2_1, f2_1, descr=arraydescr)
+        finish(p0, p1, p2, i0_1, i1_1, i2_1)
+        """
+        expected = """
+        [p0, p1, p2, i0, i1, i2]
+        i0_1 = int_add(i0, 1)
+        i1_1 = int_add(1, i1)
+        i2_1 = int_add(i2, 1)
+        vec0 = getarrayitem_vector_raw(p0, i0, descr=arraydescr)
+        vec1 = getarrayitem_vector_raw(p1, i1, descr=arraydescr)
+        vec2 = float_vector_add(vec0, vec1)
+        setarrayitem_vector_raw(p2, i2, vec2, descr=arraydescr)
+        finish(p0, p1, p2, i0_1, i1_1, i2_1)        
+        """
+        self.optimize_loop(ops, expected)
diff --git a/pypy/jit/metainterp/optimizeopt/vectorize.py b/pypy/jit/metainterp/optimizeopt/vectorize.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/optimizeopt/vectorize.py
@@ -0,0 +1,184 @@
+
+from pypy.jit.metainterp.optimizeopt.optimizer import Optimization
+from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method
+from pypy.jit.metainterp.resoperation import rop, ResOperation
+from pypy.jit.metainterp.history import BoxVector
+
+VECTOR_SIZE = 2
+VEC_MAP = {rop.FLOAT_ADD: rop.FLOAT_VECTOR_ADD}
+
+class BaseTrack(object):
+    pass
+
+class Read(BaseTrack):
+    def __init__(self, arr, index, op):
+        self.arr = arr
+        self.op = op
+        self.index = index
+
+    def match(self, other):
+        if not isinstance(other, Read):
+            return False
+        return self.arr == other.arr
+
+    def emit(self, optimizer):
+        box = BoxVector()
+        op = ResOperation(rop.GETARRAYITEM_VECTOR_RAW, [self.arr.box,
+                                                        self.index.val.box],
+                          box, descr=self.op.getdescr())
+        optimizer.emit_operation(op)
+        return box
+
+class Write(BaseTrack):
+    def __init__(self, arr, index, v, op):
+        self.arr = arr
+        self.index = index
+        self.v = v
+        self.op = op
+
+    def match(self, other):
+        if not isinstance(other, Write):
+            return False
+        return self.v.match(other.v)
+
+    def emit(self, optimizer):
+        arg = self.v.emit(optimizer)
+        op = ResOperation(rop.SETARRAYITEM_VECTOR_RAW, [self.arr.box,
+                                                        self.index.box, arg],
+                          None, descr=self.op.getdescr())
+        optimizer.emit_operation(op)
+
+class BinOp(BaseTrack):
+    def __init__(self, left, right, op):
+        self.op = op
+        self.left = left
+        self.right = right
+
+    def match(self, other):
+        if not isinstance(other, BinOp):
+            return False
+        if self.op.getopnum() != other.op.getopnum():
+            return False
+        return self.left.match(other.left) and self.right.match(other.right)
+
+    def emit(self, optimizer):
+        left_box = self.left.emit(optimizer)
+        right_box = self.right.emit(optimizer)
+        res_box = BoxVector()
+        op = ResOperation(VEC_MAP[self.op.getopnum()], [left_box, right_box],
+                          res_box)
+        optimizer.emit_operation(op)
+        return res_box
+
+class TrackIndex(object):
+    def __init__(self, val, index):
+        self.val = val
+        self.index = index
+
+    def advance(self):
+        return TrackIndex(self.val, self.index + 1)
+
+class OptVectorize(Optimization):
+    def __init__(self):
+        self.ops_so_far = []
+        self.reset()
+
+    def reset(self):
+        # deal with reset
+        for op in self.ops_so_far:
+            self.emit_operation(op)
+        self.ops_so_far = []
+        self.track = {}
+        self.tracked_indexes = {}
+        self.full = {}
+    
+    def new(self):
+        return OptVectorize()
+    
+    def optimize_ASSERT_ALIGNED(self, op):
+        index = self.getvalue(op.getarg(1))
+        self.tracked_indexes[index] = TrackIndex(index, 0)
+
+    def optimize_GETARRAYITEM_RAW(self, op):
+        arr = self.getvalue(op.getarg(0))
+        index = self.getvalue(op.getarg(1))
+        track = self.tracked_indexes.get(index)
+        if track is None:
+            self.emit_operation(op)
+        else:
+            self.ops_so_far.append(op)
+            self.track[self.getvalue(op.result)] = Read(arr, track, op)
+
+    def optimize_INT_ADD(self, op):
+        # only for += 1
+        one = self.getvalue(op.getarg(0))
+        two = self.getvalue(op.getarg(1))
+        self.emit_operation(op)
+        if (one.is_constant() and one.box.getint() == 1 and
+            two in self.tracked_indexes):
+            index = two
+        elif (two.is_constant() and two.box.getint() == 1 and
+              one in self.tracked_indexes):
+            index = one
+        else:
+            return
+        self.tracked_indexes[self.getvalue(op.result)] = self.tracked_indexes[index].advance()
+
+    def optimize_FLOAT_ADD(self, op):
+        left = self.getvalue(op.getarg(0))
+        right = self.getvalue(op.getarg(1))
+        if left not in self.track or right not in self.track:
+            self.emit_operation(op)
+        else:
+            self.ops_so_far.append(op)
+            lt = self.track[left]
+            rt = self.track[right]
+            self.track[self.getvalue(op.result)] = BinOp(lt, rt, op)
+
+    def optimize_SETARRAYITEM_RAW(self, op):
+        index = self.getvalue(op.getarg(1))
+        val = self.getvalue(op.getarg(2))
+        if index not in self.tracked_indexes or val not in self.track:
+            self.emit_operation(op)
+            return
+        v = self.track[val]
+        arr = self.getvalue(op.getarg(0))
+        ti = self.tracked_indexes[index]
+        if arr not in self.full:
+            self.full[arr] = [None] * VECTOR_SIZE
+        self.full[arr][ti.index] = Write(arr, index, v, op)
+
+    def emit_vector_ops(self, forbidden_boxes):
+        for arg in forbidden_boxes:
+            if arg in self.track:
+                self.reset()
+                return
+        if self.full:
+            for arr, items in self.full.iteritems():
+                for item in items[1:]:
+                    if item is None or not items[0].match(item):
+                        self.reset()
+                        return
+                # XXX Right now we blow up on any of the vectorizers not
+                # working. We need something more advanced in terms of ops
+                # tracking
+            for arr, items in self.full.iteritems():
+                items[0].emit(self)
+            self.ops_so_far = []
+
+    def optimize_default(self, op):
+        # list operations that are fine, not that many
+        if op.opnum in [rop.JUMP, rop.FINISH, rop.LABEL]:
+            self.emit_vector_ops(op.getarglist())
+        elif op.is_guard():
+            xxx
+        else:
+            self.reset()
+        self.emit_operation(op)
+
+    def propagate_forward(self, op):
+        dispatch_opt(self, op)
+
+dispatch_opt = make_dispatcher_method(OptVectorize, 'optimize_',
+        default=OptVectorize.optimize_default)
+
diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py
--- a/pypy/jit/metainterp/resoperation.py
+++ b/pypy/jit/metainterp/resoperation.py
@@ -376,6 +376,7 @@
     '_FINAL_LAST',
 
     'LABEL/*d',
+    'ASSERT_ALIGNED/2',
 
     '_GUARD_FIRST',
     '_GUARD_FOLDABLE_FIRST',


More information about the pypy-commit mailing list