[pypy-commit] pypy regalloc-playground: use the regular spill heuristics to chose the variables to spill before a call

cfbolz pypy.commits at gmail.com
Wed Aug 23 12:34:34 EDT 2017


Author: Carl Friedrich Bolz-Tereick <cfbolz at gmx.de>
Branch: regalloc-playground
Changeset: r92233:f68f729a80cf
Date: 2017-08-23 18:33 +0200
http://bitbucket.org/pypy/pypy/changeset/f68f729a80cf/

Log:	use the regular spill heuristics to chose the variables to spill
	before a call

diff --git a/rpython/jit/backend/llsupport/regalloc.py b/rpython/jit/backend/llsupport/regalloc.py
--- a/rpython/jit/backend/llsupport/regalloc.py
+++ b/rpython/jit/backend/llsupport/regalloc.py
@@ -418,20 +418,24 @@
         return loc
 
     def _pick_variable_to_spill(self, v, forbidden_vars, selected_reg=None,
-                                need_lower_byte=False):
+                                need_lower_byte=False, vars=None):
+        # YYY v is unused, remove
+
         # try to spill a variable that has no further real usages, ie that only
         # appears in failargs or in a jump
         # if that doesn't exist, spill the variable that has a real_usage that
         # is the furthest away from the current position
 
         # YYY check for fixed variable usages
+        if vars is None:
+            vars = self.reg_bindings.keys()
 
         cur_max_use_distance = -1
         position = self.position
         candidate = None
         cur_max_age_failargs = -1
         candidate_from_failargs = None
-        for next in self.reg_bindings:
+        for next in vars:
             reg = self.reg_bindings[next]
             if next in forbidden_vars:
                 continue
@@ -696,46 +700,31 @@
             else:
                 # this is a register like eax/rax, which needs either
                 # spilling or moving.
-                move_or_spill.append((v, max_age))
+                move_or_spill.append(v)
 
         if len(move_or_spill) > 0:
-            while len(self.free_regs) > 0:
-                # YYY here we need to use the new information to pick stuff
-                new_reg = self.free_regs.pop()
-                if new_reg in self.save_around_call_regs:
-                    new_free_regs.append(new_reg)    # not this register...
-                    continue
-                # This 'new_reg' is suitable for moving a candidate to.
-                # Pick the one with the smallest max_age.  (This
-                # is one step of a naive sorting algo, slow in theory,
-                # but the list should always be very small so it
-                # doesn't matter.)
-                best_i = 0
-                smallest_max_age = move_or_spill[0][1]
-                for i in range(1, len(move_or_spill)):
-                    max_age = move_or_spill[i][1]
-                    if max_age < smallest_max_age:
-                        best_i = i
-                        smallest_max_age = max_age
-                v, max_age = move_or_spill.pop(best_i)
-                # move from 'reg' to 'new_reg'
+            free_regs = [reg for reg in self.free_regs
+                             if reg not in self.save_around_call_regs]
+            # chose which to spill using the usual spill heuristics
+            while len(move_or_spill) > len(free_regs):
+                v = self._pick_variable_to_spill(None, [], vars=move_or_spill)
+                self._bc_spill(v, new_free_regs)
+                move_or_spill.remove(v)
+            assert len(move_or_spill) <= len(free_regs)
+            for v in move_or_spill:
+                # search next good reg
+                new_reg = None
+                while True:
+                    new_reg = self.free_regs.pop()
+                    if new_reg in self.save_around_call_regs:
+                        new_free_regs.append(new_reg)    # not this register...
+                        continue
+                    break
+                assert new_reg is not None # must succeed
                 reg = self.reg_bindings[v]
-                if not we_are_translated():
-                    if move_or_spill:
-                        assert max_age <= min([_a for _, _a in move_or_spill])
-                    assert reg in save_sublist
-                    assert reg in self.save_around_call_regs
-                    assert new_reg not in self.save_around_call_regs
                 self.assembler.regalloc_mov(reg, new_reg)
                 self.reg_bindings[v] = new_reg    # change the binding
                 new_free_regs.append(reg)
-                #
-                if len(move_or_spill) == 0:
-                    break
-            else:
-                # no more free registers to move to, spill the rest
-                for v, max_age in move_or_spill:
-                    self._bc_spill(v, new_free_regs)
 
         # re-add registers in 'new_free_regs', but in reverse order,
         # so that the last ones (added just above, from
diff --git a/rpython/jit/backend/llsupport/test/test_regalloc.py b/rpython/jit/backend/llsupport/test/test_regalloc.py
--- a/rpython/jit/backend/llsupport/test/test_regalloc.py
+++ b/rpython/jit/backend/llsupport/test/test_regalloc.py
@@ -617,6 +617,7 @@
                               assembler=asm)
         for b in boxes[:-1]:
             rm.force_allocate_reg(b)
+        rm.position = 0
         rm.before_call()
         assert len(rm.reg_bindings) == 2
         assert fm.get_frame_depth() == 2
@@ -1236,3 +1237,35 @@
             ('guard_false', r0, [])
         ]
 
+    def test_call_spill_furthest_use(self):
+        # here, i2 should be spilled, because its use is farther away
+        ops = '''
+        [i0, i1, i2, i3, i4, i5, i6]
+        i8 = call_i(ConstClass(f2ptr), i0, i1, descr=f2_calldescr)
+        escape_i(i3)
+        escape_i(i2)
+        guard_false(i8) [i2, i3, i4, i5, i6]
+        '''
+        emitted = self.allocate(ops, [r1, r2, r0, r3, r4, r5, r6])
+        fp0 = FakeFramePos(0, INT)
+        assert emitted == [
+            ('move', fp0, r0),
+            ('move', r7, r3),
+            ('call_i', r0, [r1, r2]),
+            ('escape_i', r1, [r7]),
+            ('escape_i', r1, [fp0]),
+            ('guard_false', r0, [fp0, r7, r4, r5, r6])
+        ]
+
+    def test_call_spill(self):
+        py.test.skip("also messy")
+        # i0 dies, i1 is the argument, the other fight for caller-saved regs
+        # all_regs = [r0, r1, r2, r3, r4, r5, r6, r7]
+        # save_around_call_regs = [r0, r1, r2, r3]
+        ops = '''
+        [i0, i1, i2, i3, i4, i5, i6]
+        i8 = call_i(ConstClass(f2ptr), i1, i0, descr=f2_calldescr)
+        guard_false(i8) [i2, i3, i4, i5, i6]
+        '''
+        emitted = self.allocate(ops, [r5, r1, r0, r2, r3, r6, r7])
+        assert emitted == ["???"]


More information about the pypy-commit mailing list