comparison python/registerallocator.py @ 278:9fca39eebe50

First implementation of regalloc with coalsesc
author Windel Bouwman
date Sun, 29 Sep 2013 14:08:15 +0200
parents 046017431c6a
children 2ccd57b1d78c
comparison
equal deleted inserted replaced
277:046017431c6a 278:9fca39eebe50
1 from flowgraph import FlowGraph 1 from flowgraph import FlowGraph
2 from interferencegraph import InterferenceGraph 2 from interferencegraph import InterferenceGraph
3 3
4 # Nifty first function:
5 first = lambda x: next(iter(x))
4 6
5 class RegisterAllocator: 7 class RegisterAllocator:
6 """ 8 """
7 Target independent register allocator. 9 Target independent register allocator.
8 10
9 Algorithm is iterated .. (ITR) by Appel and George 11 Algorithm is iterated register coalescing by Appel and George.
12
13 Chaitin's algorithm: remove all nodes with less than K neighbours.
14 These nodes can be colored when added back.
10 15
11 The process consists of the following steps: 16 The process consists of the following steps:
12 - build interference graph from the instruction list 17 - build interference graph from the instruction list
18 - remove low degree non move related nodes.
13 - (optional) coalesc registers to remove redundant moves 19 - (optional) coalesc registers to remove redundant moves
14 - (optional) spill registers 20 - (optional) spill registers
15 - select registers 21 - select registers
16 """ 22 """
17 def __init__(self): 23
24 def InitData(self, f):
25 self.f = f
26 # Register information:
27 self.regs = set(f.regs)
28 self.K = len(self.regs)
29
30 # Move related sets:
31 self.coalescedMoves = set()
32 self.constrainedMoves = set()
33 self.frozenMoves = set()
34 self.activeMoves = set()
35 self.worklistMoves = set()
36
37 def Build(self):
38 """ 1. Construct interference graph from instruction list """
39 self.f.cfg = FlowGraph(self.f.instructions)
40 self.f.ig = InterferenceGraph(self.f.cfg)
41 self.Node = self.f.ig.getNode
42
43 # Divide nodes into pre-colored and initial:
44 pre_tmp = list(self.f.tempMap.keys())
45 self.precolored = set(self.Node(tmp) for tmp in pre_tmp)
46 self.workSet = set(self.f.ig.nodes - self.precolored)
47
48 # Make degree table:
49 for n in self.precolored:
50 n.addDegree = 100 + len(self.f.ig.nodes) + self.K
51
52 # Initialize color map:
53 self.color = {}
54 for tmp, c in self.f.tempMap.items():
55 self.color[self.Node(tmp)] = c
56 dict(self.f.tempMap) # start with pre-colored
57
58 self.moves = [i for i in self.f.instructions if i.ismove]
59 for mv in self.moves:
60 self.Node(mv.src[0]).moves.add(mv)
61 self.Node(mv.dst[0]).moves.add(mv)
62
63 def NodeMoves(self, n):
64 return n.moves & (self.activeMoves | self.worklistMoves)
65
66 def MoveRelated(self, n):
67 return bool(self.NodeMoves(n))
68
69 @property
70 def SpillWorkSet(self):
71 c = lambda n: n.Degree >= self.K
72 return set(filter(c, self.workSet))
73
74 @property
75 def FreezeWorkSet(self):
76 c = lambda n: n.Degree < self.K and self.MoveRelated(n)
77 return set(filter(c, self.workSet))
78
79 @property
80 def SimplifyWorkSet(self):
81 c = lambda n: n.Degree < self.K and not self.MoveRelated(n)
82 return set(filter(c, self.workSet))
83
84 def makeWorkList(self):
85 """ Divide initial nodes into worklists """
86 self.selectStack = []
87
88 # Fill initial move set:
89 for m in self.moves:
90 self.worklistMoves.add(m)
91
92 def Simplify(self):
93 """ 2. Remove nodes from the graph """
94 n = first(self.SimplifyWorkSet)
95 self.workSet.remove(n)
96 self.selectStack.append(n)
97 # Pop out of graph:
98 self.f.ig.delNode(n)
99
100 def EnableMoves(self, nodes):
101 for n in nodes:
102 for m in self.NodeMoves(n):
103 if m in self.activeMoves:
104 self.activeMoves.remove(m)
105 self.worklistMoves.add(m)
106
107 def Coalesc(self):
108 """ Coalesc conservative. """
109 m = first(self.worklistMoves)
110 x = self.Node(m.dst[0])
111 y = self.Node(m.src[0])
112 u, v = (y, x) if y in self.precolored else (x, y)
113 self.worklistMoves.remove(m)
114 if u is v:
115 self.coalescedMoves.add(m)
116 elif v in self.precolored or self.f.ig.hasEdge(u, v):
117 self.constrainedMoves.add(m)
118 elif u not in self.precolored and self.Conservative(u, v):
119 self.coalescedMoves.add(m)
120 self.workSet.remove(v)
121 self.f.ig.Combine(u, v)
122 else:
123 self.activeMoves.add(m)
124
125 def Conservative(self, u, v):
126 """ Briggs conservative criteria for coalesc """
127 nodes = u.Adjecent | v.Adjecent
128 c = lambda n: n.Degree >= self.K
129 k = len(list(filter(c, nodes)))
130 return k < self.K
131
132 def Freeze(self):
133 """ Give up coalescing on some node """
134 u = first(self.FreezeWorkSet)
135 self.freezeMoves(u)
136
137 def freezeMoves(self, u):
138 """ Freeze moves for node u """
139 for m in self.NodeMoves(u):
140 if m in self.activeMoves:
141 self.activeMoves.remove(m)
142 else:
143 sekf.worklistMoves.remove(m)
144 self.frozenMoves.add(m)
145 # Check other part of the move for still being move related:
146 v = m.src[0] if u is m.dst[0] else m.dst[0]
147
148 def SelectSpill(self):
149 # TODO
18 pass 150 pass
19 151
20 def useUnused(self, inslist): 152 def AssignColors(self):
21 # Use unused temporaries at the end of the list 153 """ Add nodes back to the graph to color it. """
22 defTemps = []
23 useTemps = []
24 for i in inslist:
25 for d in iter(i.dst):
26 defTemps.append(d)
27 for s in iter(i.src):
28 useTemps.append(s)
29 defTemps = set(defTemps)
30 useTemps = set(useTemps)
31 unUsed = defTemps - useTemps
32 assert not unUsed
33
34 def build(self, f):
35 """ 1. Construct interference graph from instruction list """
36 self.useUnused(f.instructions)
37 f.cfg = FlowGraph(f.instructions)
38 f.ig = InterferenceGraph(f.cfg)
39 self.pre_colored = set(n for n in f.ig.nodes if n.varname in f.tempMap.keys())
40 print('pre-colored:', self.pre_colored)
41 self.moves = [i for i in f.instructions if i.ismove]
42 print('move instructions:', self.moves)
43 self.nodestack = []
44
45 def getMoveRelated(self, f):
46 mr = set()
47 for i in self.moves:
48 mr.add(f.ig.getNode(i.src[0]))
49 mr.add(f.ig.getNode(i.dst[0]))
50 return mr
51
52 def simplify(self, f):
53 """ 2. Remove nodes from the graph """
54 # Chaitin's algorithm:
55 # remove all non move related nodes that have less than K neighbours:
56 # Also do not remove pre-colored nodes.
57 # move_related = self.getMoveRelated(f)
58 #print('move related', move_related)
59 # o_nodes = f.ig.nodes - (self.pre_colored | move_related)
60 o_nodes = f.ig.nodes - self.pre_colored
61 #print('o_nodes', o_nodes)
62 while o_nodes:
63 less_k = [n for n in o_nodes if n.Degree < self.K]
64 if not less_k:
65 raise NotImplementedError('Spilling not implemented')
66 n = less_k[0]
67 self.nodestack.append(n)
68 f.ig.delNode(n)
69 o_nodes.remove(n)
70
71 def coalesc(self, f):
72 # for mov in ins:
73 # if mov.src is not connected to mov.dst
74 # and the node being coalesced has less than K nodes
75 # of significant degree, then the node can be coalesced.
76 # This is briggs algorithm.
77 for m in self.moves:
78 src = f.ig.getNode(m.src[0])
79 dst = f.ig.getNode(m.dst[0])
80 assert not f.ig.hasEdge(src, dst)
81 neighbours = src.Adj | dst.Adj
82 # neighbours with significant degree:
83 sd_nb = set(n for n in neighbours if n.Degree >= self.K)
84 print('neighbours with significant degree', sd_nb)
85 if len(sd_nb) < self.K:
86 print('Coalesc:', m)
87 if dst in self.pre_colored:
88 if src in self.pre_colored:
89 break
90 v2 = src
91 for i in f.instructions:
92 rf = lambda x: x
93 i.src = rf(i.src)
94 i.dst = rf(i.dst)
95 f.instructions.remove(m)
96
97 def select(self, f):
98 """ Add nodes of less than K degree back to the graph to color it. """
99 regMap = dict(f.tempMap) # start with pre-colored
100
101 # Add nodes back to the graph: 154 # Add nodes back to the graph:
102 while self.nodestack: 155 while self.selectStack:
103 n = self.nodestack.pop(-1) 156 n = self.selectStack.pop(-1)
104 f.ig.addNode(n) 157 self.f.ig.addNode(n)
105 takenregs = set(regMap[m.varname] for m in n.Adj) 158 takenregs = set(self.color[m] for m in n.Adjecent)
106 possibleregs = set(self.regs) - takenregs 159 okColors = self.regs - takenregs
107 regMap[n.varname] = list(possibleregs)[0] 160 if okColors:
108 # We have a register mapping! 161 self.color[n] = first(okColors)
109 print(regMap) 162 else:
163 raise NotImplementedError('Spill required here!')
164
165 def ApplyColors(self):
166 # Remove coalesced moves:
167 for mv in self.coalescedMoves:
168 self.f.instructions.remove(mv)
110 # Use allocated registers: 169 # Use allocated registers:
111 lookup = lambda t: regMap[t] 170 lookup = lambda t: self.color[self.Node(t)]
112 for i in f.instructions: 171 for i in self.f.instructions:
113 i.src = tuple(map(lookup, i.src)) 172 i.src = tuple(map(lookup, i.src))
114 i.dst = tuple(map(lookup, i.dst)) 173 i.dst = tuple(map(lookup, i.dst))
115 174
116 def allocFrame(self, f): 175 def allocFrame(self, f):
117 """ Do register allocation for a single stack frame. """ 176 """ Do register allocation for a single stack frame. """
118 self.regs = set(f.regs) 177 self.InitData(f)
119 self.K = len(self.regs) 178 self.Build()
120 self.build(f) 179 self.makeWorkList()
121 self.simplify(f) 180 while True:
122 # TODO: implement spilling 181 if self.SimplifyWorkSet:
123 #self.coalesc(f) # Optional! 182 self.Simplify()
124 self.select(f) 183 elif self.worklistMoves:
184 self.Coalesc()
185 elif self.FreezeWorkSet:
186 self.Freeze()
187 elif self.SpillWorkSet:
188 raise NotImplementedError('Spill not implemented')
189 else:
190 break # Done!
191 self.AssignColors()
192 self.ApplyColors()
125 193