[pypy-commit] pypy vecopt2: extended the instruction packing. it now finds adjacent memory references and packs them into pairs in the packset

plan_rich noreply at buildbot.pypy.org
Tue May 5 09:45:35 CEST 2015


Author: Richard Plangger <rich at pasra.at>
Branch: vecopt2
Changeset: r77086:333f5c08c25c
Date: 2015-03-24 13:37 +0100
http://bitbucket.org/pypy/pypy/changeset/333f5c08c25c/

Log:	extended the instruction packing. it now finds adjacent memory
	references and packs them into pairs in the packset

diff --git a/rpython/jit/backend/llgraph/runner.py b/rpython/jit/backend/llgraph/runner.py
--- a/rpython/jit/backend/llgraph/runner.py
+++ b/rpython/jit/backend/llgraph/runner.py
@@ -157,6 +157,10 @@
     def is_array_of_pointers(self):
         return getkind(self.A.OF) == 'ref'
 
+    def getflag(self):
+        from rpython.jit.backend.llsupport.descr import get_type_flag
+        return get_type_flag(self.A.OF)
+
     def is_array_of_floats(self):
         return getkind(self.A.OF) == 'float'
 
diff --git a/rpython/jit/backend/llsupport/descr.py b/rpython/jit/backend/llsupport/descr.py
--- a/rpython/jit/backend/llsupport/descr.py
+++ b/rpython/jit/backend/llsupport/descr.py
@@ -211,6 +211,9 @@
     def get_item_size_in_bytes(self):
         return self.itemsize
 
+    def get_flag(self):
+        return self.flag
+
     def is_array_of_structs(self):
         return self.flag == FLAG_STRUCT
 
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
@@ -171,7 +171,6 @@
 
     def _put_edge(self, idx_from, idx_to, arg):
         assert idx_from != idx_to
-        print("puttin", idx_from, idx_to)
         dep = self.instr_dependency(idx_from, idx_to)
         if dep is None:
             dep = Dependency(idx_from, idx_to, arg)
@@ -199,6 +198,32 @@
         edges = self.adjacent_list[idx]
         return edges
 
+    def independant(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.instr_dependencies(idx):
+                if idx < dep.idx_to:
+                    # this dependency points downwards (thus unrelevant)
+                    continue
+                if ai > dep.idx_from:
+                    # this points above ai (thus unrelevant)
+                    continue
+
+                if dep.idx_from == ai:
+                    # dependant. There is a path from ai to bi
+                    return False
+                stmt_indices.append(dep.idx_from)
+        return True
+
     def definition_dependencies(self, idx):
         deps = []
         for dep in self.adjacent_list[idx]:
@@ -211,6 +236,8 @@
             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
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
@@ -10,7 +10,10 @@
 
     def build_dependency(self, ops):
         loop = self.parse_loop(ops)
-        return DependencyGraph(loop.operations)
+        self.last_graph = DependencyGraph(loop.operations)
+        for i in range(len(self.last_graph.adjacent_list)):
+            self.assert_independent(i,i)
+        return self.last_graph
 
     def parse_loop(self, ops):
         loop = self.parse(ops, postprocess=self.postprocess)
@@ -21,7 +24,7 @@
             loop.operations[-1].setdescr(token)
         return loop
 
-    def assert_dependant(self, graph, edge_list):
+    def assert_edges(self, graph, edge_list):
         """ 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.
@@ -53,6 +56,11 @@
                    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_independent(self, a, b):
+        assert self.last_graph.independant(a,b), "{a} and {b} are dependant!".format(a=a,b=b)
+    def assert_dependent(self, a, b):
+        assert not self.last_graph.independant(a,b), "{a} and {b} are independant!".format(a=a,b=b)
 
 class BaseTestDependencyGraph(DepTestHelper):
     def test_dependency_empty(self):
@@ -61,7 +69,7 @@
         jump()
         """
         dep_graph = self.build_dependency(ops)
-        self.assert_dependant(dep_graph, [ [], [], ])
+        self.assert_edges(dep_graph, [ [], [], ])
 
     def test_dependency_of_constant_not_used(self):
         ops = """
@@ -70,7 +78,7 @@
         jump()
         """
         dep_graph = self.build_dependency(ops)
-        self.assert_dependant(dep_graph, [ [], [], [] ])
+        self.assert_edges(dep_graph, [ [], [], [] ])
 
     def test_dependency_simple(self):
         ops = """
@@ -80,9 +88,16 @@
         guard_value(i2,3) []
         jump()
         """
-        dep_graph = self.build_dependency(ops)
-        self.assert_dependant(dep_graph, 
+        graph = self.build_dependency(ops)
+        self.assert_edges(graph, 
                 [ [], [2], [1,3], [2], [], ])
+        for i in range(0,5):
+            self.assert_independent(0,i)
+        self.assert_dependent(1,2)
+        self.assert_dependent(2,3)
+        self.assert_dependent(1,3)
+        self.assert_independent(2,4)
+        self.assert_independent(3,4)
 
     def test_def_use_jump_use_def(self):
         ops = """
@@ -92,7 +107,7 @@
         jump(i1)
         """
         dep_graph = self.build_dependency(ops)
-        self.assert_dependant(dep_graph, 
+        self.assert_edges(dep_graph, 
                 [ [1], [0,2,3], [1], [1] ])
 
     def test_dependency_guard(self):
@@ -103,7 +118,7 @@
         jump(i3)
         """
         dep_graph = self.build_dependency(ops)
-        self.assert_dependant(dep_graph, 
+        self.assert_edges(dep_graph, 
                 [ [2,3], [2], [1,0], [0] ])
 
     def test_no_edge_duplication(self):
@@ -115,7 +130,7 @@
         jump(i3)
         """
         dep_graph = self.build_dependency(ops)
-        self.assert_dependant(dep_graph, 
+        self.assert_edges(dep_graph, 
                 [ [1,2,3], [0,2], [1,0], [0,4], [3] ])
 
     def test_no_edge_duplication_in_guard_failargs(self):
@@ -126,8 +141,11 @@
         jump(i1)
         """
         dep_graph = self.build_dependency(ops)
-        self.assert_dependant(dep_graph, 
+        self.assert_edges(dep_graph, 
                 [ [1,2,3], [0,2], [1,0], [0] ])
+        self.assert_dependent(0,1)
+        self.assert_dependent(0,2)
+        self.assert_dependent(0,3)
 
     def test_swap_dependencies(self):
         ops = """
@@ -139,7 +157,7 @@
         """
         dep_graph = self.build_dependency(ops)
         dep_graph.swap_instructions(1,2)
-        self.assert_dependant(dep_graph,
+        self.assert_edges(dep_graph,
                 [ [1,2,4], [4,0], [3,0], [2], [0,1] ])
         dep_graph.swap_instructions(1,2)
         self.assert_graph_equal(dep_graph, self.build_dependency(ops))
@@ -171,10 +189,13 @@
         jump(i12, i1, i14) # 11
         """
         dep_graph = self.build_dependency(ops)
-        self.assert_dependant(dep_graph,
+        self.assert_edges(dep_graph,
                 [ [1,3,6,7,11], [0,2], [1], [0,4], [3,5], [4],
                   # next entry is instr 6
                   [0,8], [0,9,11], [6,11], [7,10], [9], [7,0,8] ])
+        self.assert_independent(6, 2)
+        self.assert_independent(6, 1)
+        self.assert_dependent(6, 0)
 
     def test_prevent_double_arg(self):
         ops="""
@@ -184,7 +205,7 @@
         jump(i0, i1, i2)
         """
         dep_graph = self.build_dependency(ops)
-        self.assert_dependant(dep_graph,
+        self.assert_edges(dep_graph,
                 [ [1,3], [0,2], [1], [0] ])
 
     def test_ovf_dep(self):
@@ -195,7 +216,7 @@
         jump(i0, i1, i2)
         """
         dep_graph = self.build_dependency(ops)
-        self.assert_dependant(dep_graph,
+        self.assert_edges(dep_graph,
                 [ [1,2,3], [0,2], [0,1], [0] ])
 
     def test_exception_dep(self):
@@ -206,7 +227,7 @@
         jump(p0, i1, i2)
         """
         dep_graph = self.build_dependency(ops)
-        self.assert_dependant(dep_graph,
+        self.assert_edges(dep_graph,
                 [ [1,3], [0,2], [1], [0] ])
 
     def test_call_dependency_on_ptr_but_not_index_value(self):
@@ -219,7 +240,7 @@
         jump(p2, p1, i3)
         """
         dep_graph = self.build_dependency(ops)
-        self.assert_dependant(dep_graph,
+        self.assert_edges(dep_graph,
                 [ [1,2,3,4,5], [0,2,4,5], [0,1,3], [0,2], [0,1,5], [4,0,1] ])
 
     def test_call_dependency(self):
@@ -232,7 +253,7 @@
         jump(p2, p1, i3)
         """
         dep_graph = self.build_dependency(ops)
-        self.assert_dependant(dep_graph,
+        self.assert_edges(dep_graph,
                 [ [1,2,3,4,5], [0,2,4,5], [0,1,3], [0,2], [0,1,5], [4,0,1] ])
 
 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
@@ -9,7 +9,7 @@
 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
+from rpython.jit.metainterp.optimizeopt.vectorize import VectorizingOptimizer, MemoryRef, isomorphic
 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
@@ -62,7 +62,6 @@
         opt = self.vec_optimizer_unrolled(loop, unroll_factor)
         opt.build_dependency_graph()
         opt.find_adjacent_memory_refs()
-        opt.initialize_pack_set()
         return opt
 
     def assert_unroll_loop_equals(self, loop, expected_loop, \
@@ -84,7 +83,7 @@
         for i,op in enumerate(loop.operations):
             print(i,op)
 
-    def assert_dependant(self, graph, edge_list):
+    def assert_edges(self, graph, edge_list):
         """ 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.
@@ -255,7 +254,7 @@
         """
         vopt = self.vec_optimizer_unrolled(self.parse_loop(ops),1)
         vopt.build_dependency_graph()
-        self.assert_dependant(vopt.dependency_graph,
+        self.assert_edges(vopt.dependency_graph,
                 [ [1,2,3,5], [0], [0,3,4], [0,2], [2,5], [0,4] ])
 
         vopt.find_adjacent_memory_refs()
@@ -397,11 +396,27 @@
         i6 = int_add(i4,1)
         jump(p0,i1,i6)
         """
+        ops2 = """
+        [p0,i0,i4]
+        i3 = raw_load(p0,i0,descr=chararraydescr)
+        i1 = int_add(i0,1)
+        i5 = raw_load(p0,i4,descr=chararraydescr)
+        i6 = int_add(i4,1)
+        i3 = raw_load(p0,i1,descr=chararraydescr)
+        i8 = int_add(i1,1)
+        i9 = raw_load(p0,i6,descr=chararraydescr)
+        i7 = int_add(i6,1)
+        jump(p0,i8,i7)
+        """
+
         vopt = self.vec_optimizer_unrolled(self.parse_loop(ops),1)
         vopt.build_dependency_graph()
-        self.assert_no_edge(vopt.dependency_graph, [(i,i) for i in range(6)])
-        self.assert_def_use(vopt.dependency_graph, [(0,1),(0,2),(0,3),(0,4),(2,5)])
-        self.assert_no_edge(vopt.dependency_graph, [(1,3),(2,4)])
+        self.assert_edges(vopt.dependency_graph,
+                [ [1,2,3,4,5,7,9], 
+                    [0], [0,5,6], [0], [0,7,8],
+                    [0,2],  [2,9], [0,4], [4,9], 
+                  [0,6,8],
+                ])
 
         vopt.find_adjacent_memory_refs()
 
@@ -532,10 +547,33 @@
         jump(p0,i1)
         """
         loop = self.parse_loop(ops)
-        vopt = self.init_pack_set(loop,2)
+        vopt = self.init_pack_set(loop,1)
+        assert vopt.dependency_graph.independant(1,5)
         assert vopt.pack_set is not None
+        assert len(vopt.vec_info.memory_refs) == 2
+        assert len(vopt.pack_set.packs) == 1
 
-
+    def test_isomorphic_operations(self):
+        ops_src = """
+        [p1,p0,i0]
+        i3 = getarrayitem_gc(p0, i0, descr=chararraydescr)
+        i1 = int_add(i0, 1)
+        i2 = int_le(i1, 16)
+        i4 = getarrayitem_gc(p0, i1, descr=chararraydescr)
+        i5 = getarrayitem_gc(p1, i1, descr=floatarraydescr)
+        i6 = getarrayitem_gc(p0, i1, descr=floatarraydescr)
+        guard_true(i2) [p0, i0]
+        jump(p1,p0,i1)
+        """
+        loop = self.parse_loop(ops_src)
+        ops = loop.operations
+        assert isomorphic(ops[1], ops[4])
+        assert not isomorphic(ops[0], ops[1])
+        assert not isomorphic(ops[0], ops[5])
+        assert not isomorphic(ops[4], ops[5])
+        assert not isomorphic(ops[5], ops[6])
+        assert not isomorphic(ops[4], ops[6])
+        assert not isomorphic(ops[1], ops[6])
 
 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
@@ -210,11 +210,11 @@
                     # this is a use, thus if dep is not a defintion
                     # it points back to the definition
                     # if memref.origin == dep.defined_arg and not dep.is_definition:
-                    if memref.origin in dep.args and not dep.is_definition:
+                    if memref.origin in dep.args:
                         # if is_definition is false the params is swapped
-                        # idx_to attributes points to definer
-                        def_op = operations[dep.idx_to]
-                        opidx = dep.idx_to
+                        # idx_to attributes points to define
+                        def_op = operations[dep.idx_from]
+                        opidx = dep.idx_from
                         break
                 else:
                     # this is an error in the dependency graph
@@ -231,8 +231,18 @@
                 else:
                     break
 
-    def init_pack_set(self):
-        self.pack_set = PackSet()
+        self.pack_set = PackSet(self.dependency_graph, operations)
+        memory_refs = self.vec_info.memory_refs.items()
+        # initialize the pack set
+        for a_opidx,a_memref in memory_refs:
+            for b_opidx,b_memref 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:
+                    if a_memref.is_adjacent_to(b_memref):
+                        if self.pack_set.can_be_packed(a_memref, b_memref):
+                            self.pack_set.packs.append(Pair(a_memref, b_memref))
 
     def vectorize_trace(self, loop):
         """ Implementation of the algorithm introduced by Larsen. Refer to
@@ -251,6 +261,116 @@
         # was not able to vectorize
         return False
 
+def isomorphic(l_op, r_op):
+    """ Described in the paper ``Instruction-Isomorphism in Program Execution''.
+    I think this definition is to strict. TODO -> find another reference
+    For now it must have the same instruction type, the array parameter must be equal,
+    and it must be of the same type (both size in bytes and type of array)
+    .
+    """
+    if l_op.getopnum() == r_op.getopnum() and \
+       l_op.getarg(0) == r_op.getarg(0):
+        l_d = l_op.getdescr()
+        r_d = r_op.getdescr()
+        if l_d is not None and r_d is not None:
+            if l_d.get_item_size_in_bytes() == r_d.get_item_size_in_bytes():
+                if l_d.getflag() == r_d.getflag():
+                    return True
+
+        elif l_d is None and r_d is None:
+            return True
+
+    return False
+
+class PackSet(object):
+
+    def __init__(self, dependency_graph, operations):
+        self.packs = []
+        self.dependency_graph = dependency_graph
+        self.operations = operations
+
+    def can_be_packed(self, lh_ref, rh_ref):
+        l_op = self.operations[lh_ref.op_idx]
+        r_op = self.operations[lh_ref.op_idx]
+        if isomorphic(l_op, r_op):
+            if self.dependency_graph.independant(lh_ref.op_idx, rh_ref.op_idx):
+                for pack in self.packs:
+                    if pack.left == lh_ref or pack.right == rh_ref:
+                        return False
+                return True
+        return False
+
+
+class Pack(object):
+    """ A pack is a set of n statements that are:
+        * isomorphic
+        * independant
+        Statements are named operations in the code.
+    """
+    def __init__(self, ops):
+        self.operations = ops
+
+class Pair(Pack):
+    """ A special Pack object with only two statements. """
+    def __init__(self, left, right):
+        assert isinstance(left, MemoryRef)
+        assert isinstance(right, MemoryRef)
+        self.left = left
+        self.right = right
+        Pack.__init__(self, [left, right])
+
+    def __eq__(self, other):
+        if isinstance(other, Pair):
+            return self.left == other.left and \
+                   self.right == other.right
+
+class MemoryRef(object):
+    def __init__(self, op_idx, array, origin, descr):
+        self.op_idx = op_idx
+        self.array = array
+        self.origin = origin
+        self.descr = descr
+        self.coefficient_mul = 1
+        self.coefficient_div = 1
+        self.constant = 0
+
+    def is_adjacent_to(self, other):
+        """ this is a symmetric relation """
+        match, off = self.calc_difference(other)
+        if match:
+            return off == 1 or off == -1
+        return False
+
+    def is_adjacent_after(self, other):
+        """ the asymetric relation to is_adjacent_to """
+        match, off = self.calc_difference(other)
+        if match:
+            return off == 1
+        return False
+
+    def __eq__(self, other):
+        match, off = self.calc_difference(other)
+        if match:
+            return off == 0
+        return False
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+
+    def calc_difference(self, other):
+        if self.array == other.array \
+            and self.origin == other.origin:
+            mycoeff = self.coefficient_mul // self.coefficient_div
+            othercoeff = other.coefficient_mul // other.coefficient_div
+            diff = other.constant - self.constant
+            return mycoeff == othercoeff, diff
+        return False, 0
+
+    def __repr__(self):
+        return 'MemoryRef(%s*(%s/%s)+%s)' % (self.origin, self.coefficient_mul,
+                                            self.coefficient_div, self.constant)
+
 class IntegralMod(object):
     """ Calculates integral modifications on an integer object.
     The operations must be provided in backwards direction and of one
@@ -268,26 +388,9 @@
         self.constant = 0
         self.used_box = None
 
-    def operation_INT_SUB(self, op):
-        box_a0 = op.getarg(0)
-        box_a1 = op.getarg(1)
-        a0 = self.optimizer.getvalue(box_a0)
-        a1 = self.optimizer.getvalue(box_a1)
-        self.is_const_mod = True
-        if a0.is_constant() and a1.is_constant():
-            raise NotImplementedError()
-        elif a0.is_constant():
-            self.constant -= box_a0.getint() * self.coefficient_mul
-            self.used_box = box_a1
-        elif a1.is_constant():
-            self.constant -= box_a1.getint() * self.coefficient_mul
-            self.used_box = box_a0
-        else:
-            self.is_const_mod = False
-
     def _update_additive(self, i):
         return (i * self.coefficient_mul) / self.coefficient_div
-    
+
     additive_func_source = """
     def operation_{name}(self, op):
         box_a0 = op.getarg(0)
@@ -371,17 +474,23 @@
         self.memory_refs = {}
         self.track_memory_refs = False
 
-    def operation_RAW_LOAD(self, op):
+    array_access_source = """
+    def operation_{name}(self, op):
         descr = op.getdescr()
         if self.track_memory_refs:
             idx = len(self.optimizer._newoperations)-1
             self.memory_refs[idx] = \
-                    MemoryRef(op.getarg(0), op.getarg(1), op.getdescr())
+                    MemoryRef(idx, op.getarg(0), op.getarg(1), op.getdescr())
         if not descr.is_array_of_pointers():
             byte_count = descr.get_item_size_in_bytes()
             if self.smallest_type_bytes == 0 \
                or byte_count < self.smallest_type_bytes:
                 self.smallest_type_bytes = byte_count
+    """
+    exec py.code.Source(array_access_source.format(name='RAW_LOAD')).compile()
+    exec py.code.Source(array_access_source.format(name='GETARRAYITEM_GC')).compile()
+    exec py.code.Source(array_access_source.format(name='GETARRAYITEM_RAW')).compile()
+    del array_access_source
 
     def default_operation(self, operation):
         pass
@@ -389,70 +498,3 @@
         default=LoopVectorizeInfo.default_operation)
 LoopVectorizeInfo.inspect_operation = dispatch_opt
 
-class PackSet(object):
-    pass
-
-class Pack(object):
-    """ A pack is a set of n statements that are:
-        * isomorphic
-        * independant
-        Statements are named operations in the code.
-    """
-    def __init__(self, ops):
-        self.operations = ops
-
-class Pair(Pack):
-    """ A special Pack object with only two statements. """
-    def __init__(self, left_op, right_op):
-        assert isinstance(left_op, rop.ResOperation)
-        assert isinstance(right_op, rop.ResOperation)
-        self.left_op = left_op
-        self.right_op = right_op
-        Pack.__init__(self, [left_op, right_op])
-
-
-class MemoryRef(object):
-    def __init__(self, array, origin, descr):
-        self.array = array
-        self.origin = origin
-        self.descr = descr
-        self.coefficient_mul = 1
-        self.coefficient_div = 1
-        self.constant = 0
-
-    def is_adjacent_to(self, other):
-        """ this is a symmetric relation """
-        match, off = self.calc_difference(other)
-        if match:
-            return off == 1 or off == -1
-        return False
-
-    def is_adjacent_after(self, other):
-        """ the asymetric relation to is_adjacent_to """
-        match, off = self.calc_difference(other)
-        if match:
-            return off == 1
-        return False
-
-    def __eq__(self, other):
-        match, off = self.calc_difference(other)
-        if match:
-            return off == 0
-        return False
-
-    def __ne__(self, other):
-        return not self.__eq__(other)
-
-
-    def calc_difference(self, other):
-        if self.array == other.array \
-            and self.origin == other.origin:
-            mycoeff = self.coefficient_mul // self.coefficient_div
-            othercoeff = other.coefficient_mul // other.coefficient_div
-            diff = other.constant - self.constant
-            return mycoeff == othercoeff, diff
-        return False, 0
-
-    def __repr__(self):
-        return 'MemoryRef(%s*(%s/%s)+%s)' % (self.origin, self.coefficient_mul,
-                                            self.coefficient_div, self.constant)


More information about the pypy-commit mailing list