Author: Maciej Fijalkowski <[email protected]>
Branch: jitframe-on-heap
Changeset: r60130:3fe697670da9
Date: 2013-01-17 15:29 +0200
http://bitbucket.org/pypy/pypy/changeset/3fe697670da9/
Log: rework the frame manager to use freelists
diff --git a/pypy/jit/backend/llsupport/regalloc.py
b/pypy/jit/backend/llsupport/regalloc.py
--- a/pypy/jit/backend/llsupport/regalloc.py
+++ b/pypy/jit/backend/llsupport/regalloc.py
@@ -13,16 +13,117 @@
class NoVariableToSpill(Exception):
pass
+class Node(object):
+ def __init__(self, val, next):
+ self.val = val
+ self.next = next
+
+class LinkedList(object):
+ def __init__(self, fm, lst=None):
+ # assume the list is sorted
+ if lst is not None:
+ node = None
+ for item in range(len(lst) - 1, -1, -1):
+ node = Node(fm.get_loc_index(item), node)
+ self.master_node = node
+ else:
+ self.master_node = None
+ self.fm = fm
+
+ def append(self, size, item):
+ key = self.fm.get_loc_index(item)
+ if size == 2:
+ self._append(key)
+ self._append(key + 1)
+ else:
+ assert size == 1
+ self._append(key)
+
+ def _append(self, key):
+ if self.master_node is None or self.master_node.val > key:
+ self.master_node = Node(key, self.master_node)
+ else:
+ node = self.master_node
+ prev_node = self.master_node
+ while node and node.val < key:
+ prev_node = node
+ node = node.next
+ prev_node.next = Node(key, node)
+
+ def pop(self, size, tp):
+ if size == 2:
+ return self._pop_two(tp)
+ assert size == 1
+ if not self.master_node:
+ return None
+ node = self.master_node
+ self.master_node = node.next
+ return self.fm.frame_pos(node.val, tp)
+
+ def _candidate(self, node):
+ return node.val + 1 == node.next.val
+
+ def _pop_two(self, tp):
+ node = self.master_node
+ if node is None or node.next is None:
+ return None
+ if self._candidate(node):
+ self.master_node = node.next.next
+ return self.fm.frame_pos(node.val, tp)
+ prev_node = node
+ node = node.next
+ while True:
+ if node.next is None:
+ return None
+ if self._candidate(node):
+ # pop two
+ prev_node.next = node.next.next
+ return self.fm.frame_pos(node.val, tp)
+ node = node.next
+
+ def __len__(self):
+ """ For tests only
+ """
+ node = self.master_node
+ c = 0
+ while node:
+ node = node.next
+ c += 1
+ return c
+
+ def __repr__(self):
+ if not self.master_node:
+ return 'LinkedList(<empty>)'
+ node = self.master_node
+ l = []
+ while node:
+ l.append(str(node.val))
+ node = node.next
+ return 'LinkedList(%s)' % '->'.join(l)
+
class FrameManager(object):
""" Manage frame positions
+
+ start_free_depth is the start where we can allocate in whatever order
+ we like.
+
+ freelist_gcrefs and freelist_others are free lists of locations that
+ can be used for gcrefs and others. below stack_free_depth. Note
+ that if floats are occupying more than one spot, in order to allocate
+ the correct size, we need to use more than one from the freelist in
+ the consecutive order.
"""
- def __init__(self):
+ def __init__(self, start_free_depth=0, freelist_gcrefs=None,
+ freelist_others=None):
self.bindings = {}
- self.used = [] # list of bools
- self.hint_frame_locations = {}
+ self.current_frame_depth = start_free_depth
+ # we disable hints for now
+ #self.hint_frame_locations = {}
+ self.freelist_gcrefs = LinkedList(self, freelist_gcrefs)
+ self.freelist_others = LinkedList(self, freelist_others)
def get_frame_depth(self):
- return len(self.used)
+ return self.current_frame_depth
def get(self, box):
return self.bindings.get(box, None)
@@ -35,12 +136,12 @@
except KeyError:
pass
# check if we have a hint for this box
- if box in self.hint_frame_locations:
- # if we do, try to reuse the location for this box
- loc = self.hint_frame_locations[box]
- if self.try_to_reuse_location(box, loc):
- return loc
- # no valid hint. make up a new free location
+ #if box in self.hint_frame_locations:
+ # # if we do, try to reuse the location for this box
+ # loc = self.hint_frame_locations[box]
+ # if self.try_to_reuse_location(box, loc):
+ # return loc
+ ## no valid hint. make up a new free location
return self.get_new_loc(box)
def get_new_loc(self, box):
@@ -49,22 +150,26 @@
# that 'size' is a power of two. The reason for doing so is to
# avoid obscure issues in jump.py with stack locations that try
# to move from position (6,7) to position (7,8).
- while self.get_frame_depth() & (size - 1):
- self.used.append(False)
- #
- index = self.get_frame_depth()
- newloc = self.frame_pos(index, box.type)
- for i in range(size):
- self.used.append(True)
- #
- if not we_are_translated(): # extra testing
- testindex = self.get_loc_index(newloc)
- assert testindex == index
- #
+ if box.type == REF:
+ newloc = self.freelist_gcrefs.pop(1, box.type)
+ else:
+ newloc = self.freelist_others.pop(size, box.type)
+
+ if newloc is None:
+ #
+ index = self.get_frame_depth()
+ newloc = self.frame_pos(index, box.type)
+ self.current_frame_depth += size
+ #
+ if not we_are_translated(): # extra testing
+ testindex = self.get_loc_index(newloc)
+ assert testindex == index
+ #
self.bindings[box] = newloc
return newloc
def set_binding(self, box, loc):
+ xxx
self.bindings[box] = loc
#
index = self.get_loc_index(loc)
@@ -77,29 +182,20 @@
self.used[index] = True
index += 1
- def reserve_location_in_frame(self, size):
- frame_depth = self.get_frame_depth()
- for i in range(size):
- self.used.append(True)
- return frame_depth
-
def mark_as_free(self, box):
try:
loc = self.bindings[box]
except KeyError:
return # already gone
del self.bindings[box]
- #
- size = self.frame_size(box.type)
- baseindex = self.get_loc_index(loc)
- if baseindex < 0:
- return
- for i in range(size):
- index = baseindex + i
- assert 0 <= index < len(self.used)
- self.used[index] = False
+ if box.type == REF:
+ self.freelist_gcrefs.append(1, loc)
+ else:
+ size = self.frame_size(box)
+ self.freelist_others.append(size, loc)
def try_to_reuse_location(self, box, loc):
+ xxx
index = self.get_loc_index(loc)
if index < 0:
return False
@@ -125,7 +221,11 @@
@staticmethod
def get_loc_index(loc):
raise NotImplementedError("Purely abstract")
-
+ @staticmethod
+ def newloc(pos, size, tp):
+ """ Reverse of get_loc_index
+ """
+ raise NotImplementedError("Purely abstract")
class RegisterManager(object):
""" Class that keeps track of register allocations
diff --git a/pypy/jit/backend/llsupport/test/test_regalloc.py
b/pypy/jit/backend/llsupport/test/test_regalloc.py
--- a/pypy/jit/backend/llsupport/test/test_regalloc.py
+++ b/pypy/jit/backend/llsupport/test/test_regalloc.py
@@ -1,6 +1,7 @@
import py
-from pypy.jit.metainterp.history import BoxInt, ConstInt, BoxFloat, INT, FLOAT
-from pypy.jit.backend.llsupport.regalloc import FrameManager
+from pypy.jit.metainterp.history import BoxInt, ConstInt, BoxFloat, INT,
FLOAT,\
+ BoxPtr
+from pypy.jit.backend.llsupport.regalloc import FrameManager, LinkedList
from pypy.jit.backend.llsupport.regalloc import RegisterManager as BaseRegMan
from pypy.jit.tool.oparser import parse
from pypy.jit.backend.detect_cpu import getcpuclass
@@ -8,6 +9,9 @@
def newboxes(*values):
return [BoxInt(v) for v in values]
+def newrefboxes(count):
+ return [BoxPtr() for _ in range(count)]
+
def boxes_and_longevity(num):
res = []
longevity = {}
@@ -35,6 +39,21 @@
def __init__(self, pos, box_type):
self.pos = pos
self.box_type = box_type
+ def __repr__(self):
+ return 'FramePos<%d,%s>' % (self.pos, self.box_type)
+ def __eq__(self, other):
+ return self.pos == other.pos and self.box_type == other.box_type
+ def __ne__(self, other):
+ return not self == other
+
+class TFrameManagerEqual(FrameManager):
+ def frame_pos(self, i, box_type):
+ return FakeFramePos(i, box_type)
+ def frame_size(self, box_type):
+ return 1
+ def get_loc_index(self, loc):
+ assert isinstance(loc, FakeFramePos)
+ return loc.pos
class TFrameManager(FrameManager):
def frame_pos(self, i, box_type):
@@ -42,10 +61,8 @@
def frame_size(self, box_type):
if box_type == FLOAT:
return 2
- elif box_type == INT:
+ else:
return 1
- else:
- raise ValueError(box_type)
def get_loc_index(self, loc):
assert isinstance(loc, FakeFramePos)
return loc.pos
@@ -343,9 +360,7 @@
xrm.loc(f0)
rm.loc(b0)
assert fm.get_frame_depth() == 3
-
-
-
+
def test_spilling(self):
b0, b1, b2, b3, b4, b5 = newboxes(0, 1, 2, 3, 4, 5)
longevity = {b0: (0, 3), b1: (0, 3), b3: (0, 5), b2: (0, 2), b4: (1,
4), b5: (1, 3)}
@@ -366,6 +381,7 @@
def test_hint_frame_locations_1(self):
+ py.test.skip("xxx")
b0, = newboxes(0)
fm = TFrameManager()
loc123 = FakeFramePos(123, INT)
@@ -376,6 +392,7 @@
assert fm.get_frame_depth() == 124
def test_hint_frame_locations_2(self):
+ py.test.skip("xxx")
b0, b1, b2 = newboxes(0, 1, 2)
longevity = {b0: (0, 1), b1: (0, 2), b2: (0, 2)}
fm = TFrameManager()
@@ -407,6 +424,115 @@
#
rm._check_invariants()
+ def test_linkedlist(self):
+ class Loc(object):
+ def __init__(self, pos, size, tp):
+ self.pos = pos
+ self.size = size
+ self.tp = tp
+
+ class FrameManager(object):
+ @staticmethod
+ def get_loc_index(item):
+ return item.pos
+ @staticmethod
+ def frame_pos(pos, tp):
+ if tp == 13:
+ size = 2
+ else:
+ size = 1
+ return Loc(pos, size, tp)
+
+ fm = FrameManager()
+ l = LinkedList(fm)
+ l.append(1, Loc(1, 1, 0))
+ l.append(1, Loc(4, 1, 0))
+ l.append(1, Loc(2, 1, 0))
+ l.append(1, Loc(0, 1, 0))
+ assert l.master_node.val == 0
+ assert l.master_node.next.val == 1
+ assert l.master_node.next.next.val == 2
+ assert l.master_node.next.next.next.val == 4
+ assert l.master_node.next.next.next.next is None
+ item = l.pop(1, 0)
+ assert item.pos == 0
+ item = l.pop(1, 0)
+ assert item.pos == 1
+ item = l.pop(1, 0)
+ assert item.pos == 2
+ item = l.pop(1, 0)
+ assert item.pos == 4
+ assert l.pop(1, 0) is None
+ l.append(1, Loc(1, 1, 0))
+ l.append(1, Loc(5, 1, 0))
+ l.append(1, Loc(2, 1, 0))
+ l.append(1, Loc(0, 1, 0))
+ item = l.pop(2, 13)
+ assert item.tp == 13
+ assert item.pos == 0
+ assert item.size == 2
+ assert l.pop(2, 0) is None # 2 and 4
+ l.append(1, Loc(4, 1, 0))
+ item = l.pop(2, 13)
+ assert item.pos == 4
+ assert item.size == 2
+ assert l.pop(1, 0).pos == 2
+ assert l.pop(1, 0) is None
+ l.append(2, Loc(1, 2, 0))
+ item = l.pop(2, 13)
+ assert item.pos == 1
+ assert item.tp == 13
+ assert item.size == 2
+
+ def test_frame_manager_basic_equal(self):
+ b0, b1 = newboxes(0, 1)
+ fm = TFrameManagerEqual()
+ loc0 = fm.loc(b0)
+ assert fm.get_loc_index(loc0) == 0
+ #
+ assert fm.get(b1) is None
+ loc1 = fm.loc(b1)
+ assert fm.get_loc_index(loc1) == 1
+ assert fm.get(b1) == loc1
+ #
+ loc0b = fm.loc(b0)
+ assert loc0b == loc0
+ #
+ fm.loc(BoxInt())
+ assert fm.get_frame_depth() == 3
+ #
+ f0 = BoxFloat()
+ locf0 = fm.loc(f0)
+ assert fm.get_loc_index(locf0) == 3
+ assert fm.get_frame_depth() == 4
+ #
+ f1 = BoxFloat()
+ locf1 = fm.loc(f1)
+ assert fm.get_loc_index(locf1) == 4
+ assert fm.get_frame_depth() == 5
+ fm.mark_as_free(b1)
+ assert fm.freelist_others
+ b2 = BoxInt()
+ fm.loc(b2) # should be in the same spot as b1 before
+ assert fm.get(b1) is None
+ assert fm.get(b2) == loc1
+ fm.mark_as_free(b0)
+ p0 = BoxPtr()
+ ploc = fm.loc(p0)
+ assert fm.get_loc_index(ploc) == 5
+ assert fm.get_frame_depth() == 6
+ assert ploc != loc1
+ p1 = BoxPtr()
+ p1loc = fm.loc(p1)
+ assert fm.get_loc_index(p1loc) == 6
+ assert fm.get_frame_depth() == 7
+ fm.mark_as_free(p0)
+ p2 = BoxPtr()
+ p2loc = fm.loc(p2)
+ assert p2loc == ploc
+ assert not fm.freelist_gcrefs
+ assert len(fm.freelist_others) == 1
+
def test_frame_manager_basic(self):
b0, b1 = newboxes(0, 1)
fm = TFrameManager()
@@ -426,60 +552,39 @@
#
f0 = BoxFloat()
locf0 = fm.loc(f0)
- assert fm.get_loc_index(locf0) == 4
- assert fm.get_frame_depth() == 6
+ assert fm.get_loc_index(locf0) == 3
+ assert fm.get_frame_depth() == 5
#
f1 = BoxFloat()
locf1 = fm.loc(f1)
- assert fm.get_loc_index(locf1) == 6
+ assert fm.get_loc_index(locf1) == 5
+ assert fm.get_frame_depth() == 7
+ fm.mark_as_free(b1)
+ assert fm.freelist_others
+ b2 = BoxInt()
+ fm.loc(b2) # should be in the same spot as b1 before
+ assert fm.get(b1) is None
+ assert fm.get(b2) == loc1
+ fm.mark_as_free(b0)
+ p0 = BoxPtr()
+ ploc = fm.loc(p0)
+ assert fm.get_loc_index(ploc) == 7
assert fm.get_frame_depth() == 8
- assert fm.used == [True, True, True, False, True, True, True, True]
- #
- fm.mark_as_free(b0)
- assert fm.used == [False, True, True, False, True, True, True, True]
- fm.mark_as_free(b0)
- assert fm.used == [False, True, True, False, True, True, True, True]
- fm.mark_as_free(f1)
- assert fm.used == [False, True, True, False, True, True, False, False]
- #
- fm.reserve_location_in_frame(1)
+ assert ploc != loc1
+ p1 = BoxPtr()
+ p1loc = fm.loc(p1)
+ assert fm.get_loc_index(p1loc) == 8
assert fm.get_frame_depth() == 9
- assert fm.used == [False, True, True, False, True, True, False, False,
True]
- #
- assert b0 not in fm.bindings
- fm.set_binding(b0, loc0)
- assert b0 in fm.bindings
- assert fm.used == [True, True, True, False, True, True, False, False,
True]
- #
- b3 = BoxInt()
- assert not fm.try_to_reuse_location(b3, loc0)
- assert fm.used == [True, True, True, False, True, True, False, False,
True]
- #
- fm.mark_as_free(b0)
- assert fm.used == [False, True, True, False, True, True, False, False,
True]
- assert fm.try_to_reuse_location(b3, loc0)
- assert fm.used == [True, True, True, False, True, True, False, False,
True]
- #
- fm.mark_as_free(b0) # already free
- assert fm.used == [True, True, True, False, True, True, False, False,
True]
- #
- fm.mark_as_free(b3)
- assert fm.used == [False, True, True, False, True, True, False, False,
True]
+ fm.mark_as_free(p0)
+ p2 = BoxPtr()
+ p2loc = fm.loc(p2)
+ assert p2loc == ploc
+ assert not fm.freelist_gcrefs
+ assert len(fm.freelist_others) == 1
+ fm.mark_as_free(b2)
f3 = BoxFloat()
- assert not fm.try_to_reuse_location(f3, fm.frame_pos(0, FLOAT))
- assert not fm.try_to_reuse_location(f3, fm.frame_pos(1, FLOAT))
- assert not fm.try_to_reuse_location(f3, fm.frame_pos(2, FLOAT))
- assert not fm.try_to_reuse_location(f3, fm.frame_pos(3, FLOAT))
- assert not fm.try_to_reuse_location(f3, fm.frame_pos(4, FLOAT))
- assert not fm.try_to_reuse_location(f3, fm.frame_pos(5, FLOAT))
- assert fm.used == [False, True, True, False, True, True, False, False,
True]
- assert fm.try_to_reuse_location(f3, fm.frame_pos(6, FLOAT))
- assert fm.used == [False, True, True, False, True, True, True, True,
True]
- #
- fm.used = [False]
- assert fm.try_to_reuse_location(BoxFloat(), fm.frame_pos(0, FLOAT))
- assert fm.used == [True, True]
- #
- fm.used = [True]
- assert not fm.try_to_reuse_location(BoxFloat(), fm.frame_pos(0, FLOAT))
- assert fm.used == [True]
+ floc = fm.loc(f3)
+ assert fm.get_loc_index(floc) == 0
+
+ def test_frame_manager_2(self):
+ pass
diff --git a/pypy/jit/backend/x86/assembler.py
b/pypy/jit/backend/x86/assembler.py
--- a/pypy/jit/backend/x86/assembler.py
+++ b/pypy/jit/backend/x86/assembler.py
@@ -2072,6 +2072,7 @@
# like %eax that would be destroyed by this call, *and* they are
# used by arglocs for the *next* call, then trouble; for now we
# will just push/pop them.
+ xxx
from pypy.rpython.memory.gctransform import asmgcroot
css = self._regalloc.close_stack_struct
if css == 0:
diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py
--- a/pypy/jit/backend/x86/regalloc.py
+++ b/pypy/jit/backend/x86/regalloc.py
@@ -331,6 +331,7 @@
def _update_bindings(self, locs, inputargs):
# XXX this should probably go to llsupport/regalloc.py
+ xxx
used = {}
i = 0
for loc in locs:
@@ -1286,6 +1287,7 @@
# optimization only: fill in the 'hint_frame_locations' dictionary
# of 'fm' based on the JUMP at the end of the loop, by looking
# at where we would like the boxes to be after the jump.
+ return # XXX disabled for now
op = operations[-1]
if op.getopnum() != rop.JUMP:
return
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit