]> git.saurik.com Git - apple/javascriptcore.git/blame - dfg/DFGRegisterBank.h
JavaScriptCore-1097.13.tar.gz
[apple/javascriptcore.git] / dfg / DFGRegisterBank.h
CommitLineData
14957cd0
A
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
6fe7ccc8 31#include <dfg/DFGCommon.h>
14957cd0
A
32
33namespace 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.
68template<class BankInfo>
69class 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
76public:
77 RegisterBank()
78 : m_lastAllocated(NUM_REGS - 1)
79 {
80 }
81
6fe7ccc8
A
82 // Attempt to allocate a register - this function finds an unlocked
83 // register, locks it, and returns it. If none can be found, this
84 // returns -1 (InvalidGPRReg or InvalidFPRReg).
85 RegID tryAllocate()
86 {
87 VirtualRegister ignored;
88
89 for (uint32_t i = m_lastAllocated + 1; i < NUM_REGS; ++i) {
90 if (!m_data[i].lockCount && m_data[i].name == InvalidVirtualRegister)
91 return allocateInternal(i, ignored);
92 }
93 // Loop over the remaining entries.
94 for (uint32_t i = 0; i <= m_lastAllocated; ++i) {
95 if (!m_data[i].lockCount && m_data[i].name == InvalidVirtualRegister)
96 return allocateInternal(i, ignored);
97 }
98
99 return (RegID)-1;
100 }
101
14957cd0
A
102 // Allocate a register - this function finds an unlocked register,
103 // locks it, and returns it. If any named registers exist, one
104 // of these should be selected to be allocated. If all unlocked
105 // registers are named, then one of the named registers will need
106 // to be spilled. In this case the register selected to be spilled
107 // will be one of the registers that has the lowest 'spillOrder'
108 // cost associated with it.
109 //
110 // This method select the register to be allocated, and calls the
111 // private 'allocateInternal' method to update internal data
112 // structures accordingly.
113 RegID allocate(VirtualRegister &spillMe)
114 {
115 uint32_t currentLowest = NUM_REGS;
116 SpillHint currentSpillOrder = SpillHintInvalid;
117
118 // Scan through all register, starting at the last allocated & looping around.
119 ASSERT(m_lastAllocated < NUM_REGS);
120
121 // This loop is broken into two halves, looping from the last allocated
122 // register (the register returned last time this method was called) to
123 // the maximum register value, then from 0 to the last allocated.
124 // This implements a simple round-robin like approach to try to reduce
125 // thrash, and minimize time spent scanning locked registers in allocation.
126 // If a unlocked and unnamed register is found return it immediately.
127 // Otherwise, find the first unlocked register with the lowest spillOrder.
128 for (uint32_t i = m_lastAllocated + 1; i < NUM_REGS; ++i) {
129 // (1) If the current register is locked, it is not a candidate.
130 if (m_data[i].lockCount)
131 continue;
132 // (2) If the current register's spill order is 0, pick this! – unassigned registers have spill order 0.
133 SpillHint spillOrder = m_data[i].spillOrder;
134 if (spillOrder == SpillHintInvalid)
135 return allocateInternal(i, spillMe);
136 // If this register is better (has a lower spill order value) than any prior
137 // candidate, then record it.
138 if (spillOrder < currentSpillOrder) {
139 currentSpillOrder = spillOrder;
140 currentLowest = i;
141 }
142 }
143 // Loop over the remaining entries.
144 for (uint32_t i = 0; i <= m_lastAllocated; ++i) {
145 if (m_data[i].lockCount)
146 continue;
147 SpillHint spillOrder = m_data[i].spillOrder;
148 if (spillOrder == SpillHintInvalid)
149 return allocateInternal(i, spillMe);
150 if (spillOrder < currentSpillOrder) {
151 currentSpillOrder = spillOrder;
152 currentLowest = i;
153 }
154 }
155
156 // Deadlock check - this could only occur is all registers are locked!
157 ASSERT(currentLowest != NUM_REGS && currentSpillOrder != SpillHintInvalid);
158 // There were no available registers; currentLowest will need to be spilled.
159 return allocateInternal(currentLowest, spillMe);
160 }
161
6fe7ccc8
A
162 // Allocates the given register, even if this will force a spill.
163 VirtualRegister allocateSpecific(RegID reg)
164 {
165 unsigned index = BankInfo::toIndex(reg);
166
167 ++m_data[index].lockCount;
168 VirtualRegister name = nameAtIndex(index);
169 if (name != InvalidVirtualRegister)
170 releaseAtIndex(index);
171
172 return name;
173 }
174
14957cd0
A
175 // retain/release - these methods are used to associate/disassociate names
176 // with values in registers. retain should only be called on locked registers.
177 void retain(RegID reg, VirtualRegister name, SpillHint spillOrder)
178 {
179 unsigned index = BankInfo::toIndex(reg);
180
181 // SpillHint must be valid.
182 ASSERT(spillOrder != SpillHintInvalid);
183 // 'index' must be a valid, locked register.
184 ASSERT(index < NUM_REGS);
185 ASSERT(m_data[index].lockCount);
186 // 'index' should not currently be named, the new name must be valid.
187 ASSERT(m_data[index].name == InvalidVirtualRegister);
188 ASSERT(name != InvalidVirtualRegister);
189 // 'index' should not currently have a spillOrder.
190 ASSERT(m_data[index].spillOrder == SpillHintInvalid);
191
192 m_data[index].name = name;
193 m_data[index].spillOrder = spillOrder;
194 }
195 void release(RegID reg)
196 {
197 releaseAtIndex(BankInfo::toIndex(reg));
198 }
199
200 // lock/unlock register, ensures that they are not spilled.
201 void lock(RegID reg)
202 {
203 unsigned index = BankInfo::toIndex(reg);
204
205 ASSERT(index < NUM_REGS);
206 ++m_data[index].lockCount;
207 ASSERT(m_data[index].lockCount);
208 }
209 void unlock(RegID reg)
210 {
211 unsigned index = BankInfo::toIndex(reg);
212
213 ASSERT(index < NUM_REGS);
214 ASSERT(m_data[index].lockCount);
215 --m_data[index].lockCount;
216 }
217 bool isLocked(RegID reg) const
218 {
219 return isLockedAtIndex(BankInfo::toIndex(reg));
220 }
221
222 // Get the name (VirtualRegister) associated with the
223 // given register (or InvalidVirtualRegister for none).
224 VirtualRegister name(RegID reg) const
225 {
226 return nameAtIndex(BankInfo::toIndex(reg));
227 }
228
229#ifndef NDEBUG
230 void dump()
231 {
232 // For each register, print the VirtualRegister 'name'.
233 for (uint32_t i =0; i < NUM_REGS; ++i) {
234 if (m_data[i].name != InvalidVirtualRegister)
6fe7ccc8 235 dataLog("[%02d]", m_data[i].name);
14957cd0 236 else
6fe7ccc8 237 dataLog("[--]");
14957cd0 238 }
6fe7ccc8 239 dataLog("\n");
14957cd0
A
240 }
241#endif
242
243 class iterator {
244 friend class RegisterBank<BankInfo>;
245 public:
246 VirtualRegister name() const
247 {
248 return m_bank->nameAtIndex(m_index);
249 }
250
251 bool isLocked() const
252 {
253 return m_bank->isLockedAtIndex(m_index);
254 }
255
256 void release() const
257 {
258 m_bank->releaseAtIndex(m_index);
259 }
260
261 RegID regID() const
262 {
263 return BankInfo::toRegister(m_index);
264 }
265
266#ifndef NDEBUG
267 const char* debugName() const
268 {
269 return BankInfo::debugName(regID());
270 }
271#endif
272
273 iterator& operator++()
274 {
275 ++m_index;
276 return *this;
277 }
278
279 bool operator!=(const iterator& other) const
280 {
281 ASSERT(m_bank == other.m_bank);
282 return m_index != other.m_index;
283 }
284
285 unsigned index() const
286 {
287 return m_index;
288 }
289
290 private:
291 iterator(RegisterBank<BankInfo>* bank, unsigned index)
292 : m_bank(bank)
293 , m_index(index)
294 {
295 }
296
297 RegisterBank<BankInfo>* m_bank;
298 unsigned m_index;
299 };
300
301 iterator begin()
302 {
303 return iterator(this, 0);
304 }
305
306 iterator end()
307 {
308 return iterator(this, NUM_REGS);
309 }
310
311private:
312 bool isLockedAtIndex(unsigned index) const
313 {
314 ASSERT(index < NUM_REGS);
315 return m_data[index].lockCount;
316 }
317
318 VirtualRegister nameAtIndex(unsigned index) const
319 {
320 ASSERT(index < NUM_REGS);
321 return m_data[index].name;
322 }
323
324 void releaseAtIndex(unsigned index)
325 {
326 // 'index' must be a valid register.
327 ASSERT(index < NUM_REGS);
328 // 'index' should currently be named.
329 ASSERT(m_data[index].name != InvalidVirtualRegister);
330 // 'index' should currently have a valid spill order.
331 ASSERT(m_data[index].spillOrder != SpillHintInvalid);
332
333 m_data[index].name = InvalidVirtualRegister;
334 m_data[index].spillOrder = SpillHintInvalid;
335 }
336
337 // Used by 'allocate', above, to update inforamtion in the map.
338 RegID allocateInternal(uint32_t i, VirtualRegister &spillMe)
339 {
340 // 'i' must be a valid, unlocked register.
341 ASSERT(i < NUM_REGS && !m_data[i].lockCount);
342
343 // Return the VirtualRegister of the named value currently stored in
344 // the register being returned - or InvalidVirtualRegister if none.
345 spillMe = m_data[i].name;
346
347 // Clear any name/spillOrder currently associated with the register,
348 m_data[i] = MapEntry();
349 // Mark the register as locked (with a lock count of 1).
350 m_data[i].lockCount = 1;
351
352 m_lastAllocated = i;
353 return BankInfo::toRegister(i);
354 }
355
356 // === MapEntry ===
357 //
358 // This structure provides information for an individual machine register
359 // being managed by the RegisterBank. For each register we track a lock
360 // count, name and spillOrder hint.
361 struct MapEntry {
362 MapEntry()
363 : name(InvalidVirtualRegister)
364 , spillOrder(SpillHintInvalid)
365 , lockCount(0)
366 {
367 }
368
369 VirtualRegister name;
370 SpillHint spillOrder;
371 uint32_t lockCount;
372 };
373
374 // Holds the current status of all registers.
375 MapEntry m_data[NUM_REGS];
376 // Used to to implement a simple round-robin like allocation scheme.
377 uint32_t m_lastAllocated;
378};
379
380} } // namespace JSC::DFG
381
382#endif
383#endif