]> git.saurik.com Git - apple/javascriptcore.git/blob - dfg/DFGRegisterBank.h
f4fdd50a7197a7a14d7edf9b16e32ebde9147d93
[apple/javascriptcore.git] / dfg / DFGRegisterBank.h
1 /*
2 * Copyright (C) 2011 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #ifndef DFGRegisterBank_h
27 #define DFGRegisterBank_h
28
29 #if ENABLE(DFG_JIT)
30
31 #include <dfg/DFGNode.h>
32
33 namespace JSC { namespace DFG {
34
35 // === RegisterBank ===
36 //
37 // This class is used to implement the GPR and FPR register banks.
38 // All registers have two pieces of state associated with them:
39 // a lock count (used to indicate this register is already in use
40 // in code generation of the current node, and cannot be spilled or
41 // allocated as a temporary), and VirtualRegister 'name', recording
42 // which value (if any) a machine register currently holds.
43 // Either or both of these pieces of information may be valid for a
44 // given register. A register may be:
45 //
46 // - unlocked, and unnamed: Available for allocation.
47 // - locked, but unnamed: Already allocated as a temporary or
48 // result for the current node.
49 // - unlocked, but named: Contains the result of a prior operation,
50 // not yet in use for this node,
51 // - locked, but named: Contains the result of a prior operation,
52 // already allocated as a operand to the
53 // current operation.
54 //
55 // For every named register we also record a hint value indicating
56 // the order in which registers should be selected to be spilled;
57 // registers that can be more cheaply spilled and/or filled should
58 // be selected first.
59 //
60 // Locking register is a strong retention mechanism; a locked register
61 // will never be reallocated (this is used to ensure the operands to
62 // the current node are in registers). Naming, conversely, in a weak
63 // retention mechanism - allocating a register may force a named value
64 // to be spilled.
65 //
66 // All named values must be given a hint that is greater than Min and
67 // less than Max.
68 template<class BankInfo>
69 class RegisterBank {
70 typedef typename BankInfo::RegisterType RegID;
71 static const size_t NUM_REGS = BankInfo::numberOfRegisters;
72
73 typedef uint32_t SpillHint;
74 static const SpillHint SpillHintInvalid = 0xffffffff;
75
76 public:
77 RegisterBank()
78 : m_lastAllocated(NUM_REGS - 1)
79 {
80 }
81
82 // Allocate a register - this function finds an unlocked register,
83 // locks it, and returns it. If any named registers exist, one
84 // of these should be selected to be allocated. If all unlocked
85 // registers are named, then one of the named registers will need
86 // to be spilled. In this case the register selected to be spilled
87 // will be one of the registers that has the lowest 'spillOrder'
88 // cost associated with it.
89 //
90 // This method select the register to be allocated, and calls the
91 // private 'allocateInternal' method to update internal data
92 // structures accordingly.
93 RegID allocate(VirtualRegister &spillMe)
94 {
95 uint32_t currentLowest = NUM_REGS;
96 SpillHint currentSpillOrder = SpillHintInvalid;
97
98 // Scan through all register, starting at the last allocated & looping around.
99 ASSERT(m_lastAllocated < NUM_REGS);
100
101 // This loop is broken into two halves, looping from the last allocated
102 // register (the register returned last time this method was called) to
103 // the maximum register value, then from 0 to the last allocated.
104 // This implements a simple round-robin like approach to try to reduce
105 // thrash, and minimize time spent scanning locked registers in allocation.
106 // If a unlocked and unnamed register is found return it immediately.
107 // Otherwise, find the first unlocked register with the lowest spillOrder.
108 for (uint32_t i = m_lastAllocated + 1; i < NUM_REGS; ++i) {
109 // (1) If the current register is locked, it is not a candidate.
110 if (m_data[i].lockCount)
111 continue;
112 // (2) If the current register's spill order is 0, pick this! – unassigned registers have spill order 0.
113 SpillHint spillOrder = m_data[i].spillOrder;
114 if (spillOrder == SpillHintInvalid)
115 return allocateInternal(i, spillMe);
116 // If this register is better (has a lower spill order value) than any prior
117 // candidate, then record it.
118 if (spillOrder < currentSpillOrder) {
119 currentSpillOrder = spillOrder;
120 currentLowest = i;
121 }
122 }
123 // Loop over the remaining entries.
124 for (uint32_t i = 0; i <= m_lastAllocated; ++i) {
125 if (m_data[i].lockCount)
126 continue;
127 SpillHint spillOrder = m_data[i].spillOrder;
128 if (spillOrder == SpillHintInvalid)
129 return allocateInternal(i, spillMe);
130 if (spillOrder < currentSpillOrder) {
131 currentSpillOrder = spillOrder;
132 currentLowest = i;
133 }
134 }
135
136 // Deadlock check - this could only occur is all registers are locked!
137 ASSERT(currentLowest != NUM_REGS && currentSpillOrder != SpillHintInvalid);
138 // There were no available registers; currentLowest will need to be spilled.
139 return allocateInternal(currentLowest, spillMe);
140 }
141
142 // retain/release - these methods are used to associate/disassociate names
143 // with values in registers. retain should only be called on locked registers.
144 void retain(RegID reg, VirtualRegister name, SpillHint spillOrder)
145 {
146 unsigned index = BankInfo::toIndex(reg);
147
148 // SpillHint must be valid.
149 ASSERT(spillOrder != SpillHintInvalid);
150 // 'index' must be a valid, locked register.
151 ASSERT(index < NUM_REGS);
152 ASSERT(m_data[index].lockCount);
153 // 'index' should not currently be named, the new name must be valid.
154 ASSERT(m_data[index].name == InvalidVirtualRegister);
155 ASSERT(name != InvalidVirtualRegister);
156 // 'index' should not currently have a spillOrder.
157 ASSERT(m_data[index].spillOrder == SpillHintInvalid);
158
159 m_data[index].name = name;
160 m_data[index].spillOrder = spillOrder;
161 }
162 void release(RegID reg)
163 {
164 releaseAtIndex(BankInfo::toIndex(reg));
165 }
166
167 // lock/unlock register, ensures that they are not spilled.
168 void lock(RegID reg)
169 {
170 unsigned index = BankInfo::toIndex(reg);
171
172 ASSERT(index < NUM_REGS);
173 ++m_data[index].lockCount;
174 ASSERT(m_data[index].lockCount);
175 }
176 void unlock(RegID reg)
177 {
178 unsigned index = BankInfo::toIndex(reg);
179
180 ASSERT(index < NUM_REGS);
181 ASSERT(m_data[index].lockCount);
182 --m_data[index].lockCount;
183 }
184 bool isLocked(RegID reg) const
185 {
186 return isLockedAtIndex(BankInfo::toIndex(reg));
187 }
188
189 // Get the name (VirtualRegister) associated with the
190 // given register (or InvalidVirtualRegister for none).
191 VirtualRegister name(RegID reg) const
192 {
193 return nameAtIndex(BankInfo::toIndex(reg));
194 }
195
196 #ifndef NDEBUG
197 void dump()
198 {
199 // For each register, print the VirtualRegister 'name'.
200 for (uint32_t i =0; i < NUM_REGS; ++i) {
201 if (m_data[i].name != InvalidVirtualRegister)
202 fprintf(stderr, "[%02d]", m_data[i].name);
203 else
204 fprintf(stderr, "[--]");
205 }
206 fprintf(stderr, "\n");
207 }
208 #endif
209
210 class iterator {
211 friend class RegisterBank<BankInfo>;
212 public:
213 VirtualRegister name() const
214 {
215 return m_bank->nameAtIndex(m_index);
216 }
217
218 bool isLocked() const
219 {
220 return m_bank->isLockedAtIndex(m_index);
221 }
222
223 void release() const
224 {
225 m_bank->releaseAtIndex(m_index);
226 }
227
228 RegID regID() const
229 {
230 return BankInfo::toRegister(m_index);
231 }
232
233 #ifndef NDEBUG
234 const char* debugName() const
235 {
236 return BankInfo::debugName(regID());
237 }
238 #endif
239
240 iterator& operator++()
241 {
242 ++m_index;
243 return *this;
244 }
245
246 bool operator!=(const iterator& other) const
247 {
248 ASSERT(m_bank == other.m_bank);
249 return m_index != other.m_index;
250 }
251
252 unsigned index() const
253 {
254 return m_index;
255 }
256
257 private:
258 iterator(RegisterBank<BankInfo>* bank, unsigned index)
259 : m_bank(bank)
260 , m_index(index)
261 {
262 }
263
264 RegisterBank<BankInfo>* m_bank;
265 unsigned m_index;
266 };
267
268 iterator begin()
269 {
270 return iterator(this, 0);
271 }
272
273 iterator end()
274 {
275 return iterator(this, NUM_REGS);
276 }
277
278 private:
279 bool isLockedAtIndex(unsigned index) const
280 {
281 ASSERT(index < NUM_REGS);
282 return m_data[index].lockCount;
283 }
284
285 VirtualRegister nameAtIndex(unsigned index) const
286 {
287 ASSERT(index < NUM_REGS);
288 return m_data[index].name;
289 }
290
291 void releaseAtIndex(unsigned index)
292 {
293 // 'index' must be a valid register.
294 ASSERT(index < NUM_REGS);
295 // 'index' should currently be named.
296 ASSERT(m_data[index].name != InvalidVirtualRegister);
297 // 'index' should currently have a valid spill order.
298 ASSERT(m_data[index].spillOrder != SpillHintInvalid);
299
300 m_data[index].name = InvalidVirtualRegister;
301 m_data[index].spillOrder = SpillHintInvalid;
302 }
303
304 // Used by 'allocate', above, to update inforamtion in the map.
305 RegID allocateInternal(uint32_t i, VirtualRegister &spillMe)
306 {
307 // 'i' must be a valid, unlocked register.
308 ASSERT(i < NUM_REGS && !m_data[i].lockCount);
309
310 // Return the VirtualRegister of the named value currently stored in
311 // the register being returned - or InvalidVirtualRegister if none.
312 spillMe = m_data[i].name;
313
314 // Clear any name/spillOrder currently associated with the register,
315 m_data[i] = MapEntry();
316 // Mark the register as locked (with a lock count of 1).
317 m_data[i].lockCount = 1;
318
319 m_lastAllocated = i;
320 return BankInfo::toRegister(i);
321 }
322
323 // === MapEntry ===
324 //
325 // This structure provides information for an individual machine register
326 // being managed by the RegisterBank. For each register we track a lock
327 // count, name and spillOrder hint.
328 struct MapEntry {
329 MapEntry()
330 : name(InvalidVirtualRegister)
331 , spillOrder(SpillHintInvalid)
332 , lockCount(0)
333 {
334 }
335
336 VirtualRegister name;
337 SpillHint spillOrder;
338 uint32_t lockCount;
339 };
340
341 // Holds the current status of all registers.
342 MapEntry m_data[NUM_REGS];
343 // Used to to implement a simple round-robin like allocation scheme.
344 uint32_t m_lastAllocated;
345 };
346
347 } } // namespace JSC::DFG
348
349 #endif
350 #endif